From 9a53b18ceae2027db07272f6f1ac4ac2031d9a68 Mon Sep 17 00:00:00 2001 From: Jinkun Jang Date: Wed, 13 Mar 2013 01:41:55 +0900 Subject: [PATCH] Tizen 2.1 base --- AUTHORS | 1 + BUGS | 1 + COPYING.LIB | 504 + ChangeLog | 47 + HACKING | 102 + LICENSE | 179 + MAINTAINERS | 1 + Makefile.am | 22 + NEWS | 31 + README | 64 + TODO | 1 + ac-helpers/binreloc.m4 | 77 + ac-helpers/ssize_t.m4 | 21 + autogen.sh | 99 + autopackage/@gtk.org/glib/skeleton.1 | 14 + autopackage/@gtk.org/gmodule/skeleton.1 | 14 + autopackage/default.apspec.in | 53 + compile-resource | 46 + configure.in | 574 + data/Makefile.am | 4 + data/enchant.ordering | 8 + doc/.cvsignore | 2 + doc/Makefile.am | 3 + doc/enchant.1 | 69 + enchant-uninstalled.pc.in | 11 + enchant.manifest | 5 + enchant.pc.in | 11 + enchant.spec.in | 96 + lt-compile-resource | 78 + msvc/Build.win32.readme | 88 + msvc/enchant-lsmod.vcproj | 203 + msvc/enchant.sln | 341 + msvc/enchant.vcproj | 203 + msvc/libenchant.vcproj | 247 + msvc/libenchant_aspell.vcproj | 217 + msvc/libenchant_hspell.vcproj | 215 + msvc/libenchant_ispell.vcproj | 259 + msvc/libenchant_mock_provider.vcproj | 207 + msvc/libenchant_myspell.vcproj | 355 + msvc/libenchant_uspell.vcproj | 215 + msvc/libenchant_voikko.vcproj | 203 + msvc/libenchant_zemberek.vcproj | 203 + msvc/test-enchant.vcproj | 201 + msvc/test-enchantxx.vcproj | 201 + msvc/unittest-enchant.vcproj | 357 + msvc/unittest-providers.vcproj | 255 + packaging/enchant.spec | 105 + src/.cvsignore | 6 + src/Makefile.am | 37 + src/applespell/AppleSpell.config | 13 + src/applespell/Makefile.am | 4 + src/applespell/applespell_checker.h | 52 + src/applespell/applespell_checker.mm | 450 + src/aspell/.cvsignore | 6 + src/aspell/Makefile.am | 15 + src/aspell/README | 4 + src/aspell/aspell_provider.c | 473 + src/bindings/Enchant.Net/App.config | 4 + src/bindings/Enchant.Net/Bindings.cs | 374 + src/bindings/Enchant.Net/Broker.cs | 361 + src/bindings/Enchant.Net/Dictionary.cs | 217 + src/bindings/Enchant.Net/DictionaryInfo.cs | 55 + src/bindings/Enchant.Net/Enchant.Net.csproj | 62 + .../Enchant.Net/Properties/AssemblyInfo.cs.in | 34 + src/bindings/Enchant.Net/ProviderInfo.cs | 52 + src/bindings/Enchant.Net/SafeBrokerHandle.cs | 59 + src/bindings/Enchant.Net/SafeDictionaryHandle.cs | 70 + src/bindings/Enchant.Net/Utf8Marshaller.cs | 121 + src/config.h.win32 | 11 + src/enchant++.h | 292 + src/enchant-provider.h | 145 + src/enchant.c | 2339 +++ src/enchant.h | 183 + src/enchant.i | 8 + src/enchant_cocoa.h | 50 + src/enchant_cocoa.m | 83 + src/hspell/.cvsignore | 6 + src/hspell/Makefile.am | 15 + src/hspell/README | 8 + src/hspell/hspell_provider.c | 308 + src/ispell/.cvsignore | 12 + src/ispell/Makefile.am | 27 + src/ispell/README | 22 + src/ispell/correct.cpp | 917 + src/ispell/good.cpp | 399 + src/ispell/hash.cpp | 172 + src/ispell/ispell.h | 769 + src/ispell/ispell_checker.cpp | 686 + src/ispell/ispell_checker.h | 243 + src/ispell/ispell_def.h | 5 + src/ispell/lookup.cpp | 744 + src/ispell/makedent.cpp | 936 + src/ispell/msgs.h | 297 + src/ispell/sp_spell.h | 32 + src/ispell/tgood.cpp | 778 + src/libenchant.rc.in | 31 + src/myspell/.cvsignore | 6 + src/myspell/Makefile.am | 70 + src/myspell/README | 21 + src/myspell/affentry.cxx | 962 + src/myspell/affentry.hxx | 136 + src/myspell/affixmgr.cxx | 4488 +++++ src/myspell/affixmgr.hxx | 250 + src/myspell/atypes.hxx | 107 + src/myspell/baseaffix.hxx | 28 + src/myspell/csutil.cxx | 5824 ++++++ src/myspell/csutil.hxx | 227 + src/myspell/dictmgr.cxx | 182 + src/myspell/dictmgr.hxx | 36 + src/myspell/filemgr.cxx | 51 + src/myspell/filemgr.hxx | 25 + src/myspell/hashmgr.cxx | 928 + src/myspell/hashmgr.hxx | 69 + src/myspell/htypes.hxx | 32 + src/myspell/hunspell.cxx | 2004 ++ src/myspell/hunspell.dsp | 164 + src/myspell/hunspell.h | 95 + src/myspell/hunspell.hxx | 172 + src/myspell/hunvisapi.h | 18 + src/myspell/hunzip.cxx | 195 + src/myspell/hunzip.hxx | 45 + src/myspell/langnum.hxx | 38 + src/myspell/libenchant_myspell.rc | 37 + src/myspell/license.hunspell | 61 + src/myspell/license.myspell | 61 + src/myspell/license.readme | 61 + src/myspell/makefile.mk | 113 + src/myspell/myspell_checker.cpp | 650 + src/myspell/phonet.cxx | 292 + src/myspell/phonet.hxx | 52 + src/myspell/replist.cxx | 87 + src/myspell/replist.hxx | 27 + src/myspell/suggestmgr.cxx | 1996 ++ src/myspell/suggestmgr.hxx | 111 + src/myspell/utf_info.cxx | 19676 +++++++++++++++++++ src/myspell/w_char.hxx | 21 + src/prefix.c | 696 + src/prefix.h | 68 + src/pwl.c | 1259 ++ src/pwl.h | 59 + src/uspell/.cvsignore | 6 + src/uspell/Makefile.am | 15 + src/uspell/README | 6 + src/uspell/uspell_provider.cpp | 481 + src/voikko/Makefile.am | 15 + src/voikko/voikko_provider.c | 201 + src/zemberek/Makefile.am | 21 + src/zemberek/zemberek.cpp | 124 + src/zemberek/zemberek.h | 51 + src/zemberek/zemberek_provider.cpp | 156 + tests/.cvsignore | 8 + tests/Makefile.am | 35 + tests/enchant-ispell.c | 549 + tests/enchant-lsmod.c | 145 + tests/ispell.in | 25 + tests/test-enchant.c | 157 + tests/test-enchantxx.cpp | 125 + tests/test.pwl | 2 + unittests/Enchant.Net.Tests/BrokerTests.cs | 363 + unittests/Enchant.Net.Tests/DictionaryTests.cs | 219 + .../Enchant.Net.Tests/Enchant.Net.Tests.csproj | 59 + .../Enchant.Net.Tests/Properties/AssemblyInfo.cs | 34 + unittests/EnchantBrokerTestFixture.h | 313 + unittests/EnchantDictionaryTestFixture.h | 352 + unittests/EnchantTestFixture.h | 450 + unittests/Readme.txt | 3 + unittests/broker/enchant_broker_describe_tests.cpp | 328 + .../broker/enchant_broker_dict_exists_tests.cpp | 148 + .../broker/enchant_broker_dict_exists_tests.i | 150 + .../broker/enchant_broker_free_dict_tests.cpp | 143 + unittests/broker/enchant_broker_free_tests.cpp | 128 + .../broker/enchant_broker_get_error_tests.cpp | 65 + unittests/broker/enchant_broker_init_tests.cpp | 40 + .../broker/enchant_broker_list_dicts_tests.cpp | 268 + .../broker/enchant_broker_request_dict_tests.cpp | 225 + .../enchant_broker_request_pwl_dict_tests.cpp | 138 + .../broker/enchant_broker_set_ordering_tests.cpp | 577 + unittests/dictionary/enchant_dict_add_tests.cpp | 234 + .../enchant_dict_add_to_session_tests.cpp | 221 + unittests/dictionary/enchant_dict_check_tests.cpp | 224 + .../dictionary/enchant_dict_describe_tests.cpp | 140 + .../enchant_dict_free_string_list_tests.cpp | 95 + .../dictionary/enchant_dict_get_error_tests.cpp | 70 + .../dictionary/enchant_dict_is_added_tests.cpp | 132 + .../dictionary/enchant_dict_is_removed_tests.cpp | 134 + .../enchant_dict_remove_from_session_tests.cpp | 194 + unittests/dictionary/enchant_dict_remove_tests.cpp | 336 + .../enchant_dict_store_replacement_tests.cpp | 228 + .../dictionary/enchant_dict_suggest_tests.cpp | 463 + .../Dictionary/dictionary_check.cpp | 206 + .../Dictionary/dictionary_suggest.cpp | 173 + .../Provider/provider_describe_dict.cpp | 68 + .../Provider/provider_dictionary_exists.cpp | 61 + .../Provider/provider_identify_dict.cpp | 68 + .../Provider/provider_list_dicts.cpp | 97 + .../Provider/provider_request_dict.cpp | 67 + unittests/enchant_providers/Readme.txt | 9 + unittests/enchant_providers/main.cpp | 183 + .../enchant_providers/unittest_enchant_providers.h | 98 + unittests/main.cpp | 31 + unittests/mock_provider/mock_provider.cpp | 108 + unittests/mock_provider/mock_provider.h | 22 + .../enchant_provider_broker_set_error_tests.cpp | 101 + .../enchant_provider_dict_set_error_tests.cpp | 93 + .../enchant_provider_get_prefix_dir_tests.cpp | 68 + .../enchant_provider_get_registry_value_tests.cpp | 180 + ...enchant_provider_get_user_config_dirs_tests.cpp | 122 + .../enchant_provider_get_user_language_tests.cpp | 88 + unittests/pwl/enchant_pwl_tests.cpp | 1579 ++ 209 files changed, 71718 insertions(+) create mode 100644 AUTHORS create mode 100644 BUGS create mode 100644 COPYING.LIB create mode 100644 ChangeLog create mode 100644 HACKING create mode 100644 LICENSE create mode 100644 MAINTAINERS create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 TODO create mode 100644 ac-helpers/binreloc.m4 create mode 100644 ac-helpers/ssize_t.m4 create mode 100755 autogen.sh create mode 100644 autopackage/@gtk.org/glib/skeleton.1 create mode 100644 autopackage/@gtk.org/gmodule/skeleton.1 create mode 100644 autopackage/default.apspec.in create mode 100755 compile-resource create mode 100644 configure.in create mode 100644 data/Makefile.am create mode 100644 data/enchant.ordering create mode 100644 doc/.cvsignore create mode 100644 doc/Makefile.am create mode 100644 doc/enchant.1 create mode 100644 enchant-uninstalled.pc.in create mode 100644 enchant.manifest create mode 100644 enchant.pc.in create mode 100644 enchant.spec.in create mode 100755 lt-compile-resource create mode 100644 msvc/Build.win32.readme create mode 100644 msvc/enchant-lsmod.vcproj create mode 100644 msvc/enchant.sln create mode 100644 msvc/enchant.vcproj create mode 100644 msvc/libenchant.vcproj create mode 100644 msvc/libenchant_aspell.vcproj create mode 100644 msvc/libenchant_hspell.vcproj create mode 100644 msvc/libenchant_ispell.vcproj create mode 100644 msvc/libenchant_mock_provider.vcproj create mode 100644 msvc/libenchant_myspell.vcproj create mode 100644 msvc/libenchant_uspell.vcproj create mode 100644 msvc/libenchant_voikko.vcproj create mode 100644 msvc/libenchant_zemberek.vcproj create mode 100644 msvc/test-enchant.vcproj create mode 100644 msvc/test-enchantxx.vcproj create mode 100644 msvc/unittest-enchant.vcproj create mode 100644 msvc/unittest-providers.vcproj create mode 100644 packaging/enchant.spec create mode 100644 src/.cvsignore create mode 100644 src/Makefile.am create mode 100644 src/applespell/AppleSpell.config create mode 100644 src/applespell/Makefile.am create mode 100644 src/applespell/applespell_checker.h create mode 100644 src/applespell/applespell_checker.mm create mode 100644 src/aspell/.cvsignore create mode 100644 src/aspell/Makefile.am create mode 100644 src/aspell/README create mode 100644 src/aspell/aspell_provider.c create mode 100644 src/bindings/Enchant.Net/App.config create mode 100644 src/bindings/Enchant.Net/Bindings.cs create mode 100644 src/bindings/Enchant.Net/Broker.cs create mode 100644 src/bindings/Enchant.Net/Dictionary.cs create mode 100644 src/bindings/Enchant.Net/DictionaryInfo.cs create mode 100644 src/bindings/Enchant.Net/Enchant.Net.csproj create mode 100644 src/bindings/Enchant.Net/Properties/AssemblyInfo.cs.in create mode 100644 src/bindings/Enchant.Net/ProviderInfo.cs create mode 100644 src/bindings/Enchant.Net/SafeBrokerHandle.cs create mode 100644 src/bindings/Enchant.Net/SafeDictionaryHandle.cs create mode 100644 src/bindings/Enchant.Net/Utf8Marshaller.cs create mode 100644 src/config.h.win32 create mode 100644 src/enchant++.h create mode 100644 src/enchant-provider.h create mode 100644 src/enchant.c create mode 100644 src/enchant.h create mode 100644 src/enchant.i create mode 100644 src/enchant_cocoa.h create mode 100644 src/enchant_cocoa.m create mode 100644 src/hspell/.cvsignore create mode 100644 src/hspell/Makefile.am create mode 100644 src/hspell/README create mode 100644 src/hspell/hspell_provider.c create mode 100644 src/ispell/.cvsignore create mode 100644 src/ispell/Makefile.am create mode 100644 src/ispell/README create mode 100644 src/ispell/correct.cpp create mode 100644 src/ispell/good.cpp create mode 100644 src/ispell/hash.cpp create mode 100644 src/ispell/ispell.h create mode 100644 src/ispell/ispell_checker.cpp create mode 100644 src/ispell/ispell_checker.h create mode 100644 src/ispell/ispell_def.h create mode 100644 src/ispell/lookup.cpp create mode 100644 src/ispell/makedent.cpp create mode 100644 src/ispell/msgs.h create mode 100644 src/ispell/sp_spell.h create mode 100644 src/ispell/tgood.cpp create mode 100644 src/libenchant.rc.in create mode 100644 src/myspell/.cvsignore create mode 100644 src/myspell/Makefile.am create mode 100644 src/myspell/README create mode 100644 src/myspell/affentry.cxx create mode 100644 src/myspell/affentry.hxx create mode 100644 src/myspell/affixmgr.cxx create mode 100644 src/myspell/affixmgr.hxx create mode 100644 src/myspell/atypes.hxx create mode 100644 src/myspell/baseaffix.hxx create mode 100644 src/myspell/csutil.cxx create mode 100644 src/myspell/csutil.hxx create mode 100644 src/myspell/dictmgr.cxx create mode 100644 src/myspell/dictmgr.hxx create mode 100644 src/myspell/filemgr.cxx create mode 100644 src/myspell/filemgr.hxx create mode 100644 src/myspell/hashmgr.cxx create mode 100644 src/myspell/hashmgr.hxx create mode 100644 src/myspell/htypes.hxx create mode 100644 src/myspell/hunspell.cxx create mode 100644 src/myspell/hunspell.dsp create mode 100644 src/myspell/hunspell.h create mode 100644 src/myspell/hunspell.hxx create mode 100644 src/myspell/hunvisapi.h create mode 100644 src/myspell/hunzip.cxx create mode 100644 src/myspell/hunzip.hxx create mode 100644 src/myspell/langnum.hxx create mode 100644 src/myspell/libenchant_myspell.rc create mode 100644 src/myspell/license.hunspell create mode 100644 src/myspell/license.myspell create mode 100644 src/myspell/license.readme create mode 100644 src/myspell/makefile.mk create mode 100644 src/myspell/myspell_checker.cpp create mode 100644 src/myspell/phonet.cxx create mode 100644 src/myspell/phonet.hxx create mode 100644 src/myspell/replist.cxx create mode 100644 src/myspell/replist.hxx create mode 100644 src/myspell/suggestmgr.cxx create mode 100644 src/myspell/suggestmgr.hxx create mode 100644 src/myspell/utf_info.cxx create mode 100644 src/myspell/w_char.hxx create mode 100644 src/prefix.c create mode 100644 src/prefix.h create mode 100644 src/pwl.c create mode 100644 src/pwl.h create mode 100644 src/uspell/.cvsignore create mode 100644 src/uspell/Makefile.am create mode 100644 src/uspell/README create mode 100644 src/uspell/uspell_provider.cpp create mode 100644 src/voikko/Makefile.am create mode 100644 src/voikko/voikko_provider.c create mode 100644 src/zemberek/Makefile.am create mode 100644 src/zemberek/zemberek.cpp create mode 100644 src/zemberek/zemberek.h create mode 100644 src/zemberek/zemberek_provider.cpp create mode 100644 tests/.cvsignore create mode 100644 tests/Makefile.am create mode 100644 tests/enchant-ispell.c create mode 100644 tests/enchant-lsmod.c create mode 100755 tests/ispell.in create mode 100644 tests/test-enchant.c create mode 100644 tests/test-enchantxx.cpp create mode 100644 tests/test.pwl create mode 100644 unittests/Enchant.Net.Tests/BrokerTests.cs create mode 100644 unittests/Enchant.Net.Tests/DictionaryTests.cs create mode 100644 unittests/Enchant.Net.Tests/Enchant.Net.Tests.csproj create mode 100644 unittests/Enchant.Net.Tests/Properties/AssemblyInfo.cs create mode 100644 unittests/EnchantBrokerTestFixture.h create mode 100644 unittests/EnchantDictionaryTestFixture.h create mode 100644 unittests/EnchantTestFixture.h create mode 100644 unittests/Readme.txt create mode 100644 unittests/broker/enchant_broker_describe_tests.cpp create mode 100644 unittests/broker/enchant_broker_dict_exists_tests.cpp create mode 100644 unittests/broker/enchant_broker_dict_exists_tests.i create mode 100644 unittests/broker/enchant_broker_free_dict_tests.cpp create mode 100644 unittests/broker/enchant_broker_free_tests.cpp create mode 100644 unittests/broker/enchant_broker_get_error_tests.cpp create mode 100644 unittests/broker/enchant_broker_init_tests.cpp create mode 100644 unittests/broker/enchant_broker_list_dicts_tests.cpp create mode 100644 unittests/broker/enchant_broker_request_dict_tests.cpp create mode 100644 unittests/broker/enchant_broker_request_pwl_dict_tests.cpp create mode 100644 unittests/broker/enchant_broker_set_ordering_tests.cpp create mode 100644 unittests/dictionary/enchant_dict_add_tests.cpp create mode 100644 unittests/dictionary/enchant_dict_add_to_session_tests.cpp create mode 100644 unittests/dictionary/enchant_dict_check_tests.cpp create mode 100644 unittests/dictionary/enchant_dict_describe_tests.cpp create mode 100644 unittests/dictionary/enchant_dict_free_string_list_tests.cpp create mode 100644 unittests/dictionary/enchant_dict_get_error_tests.cpp create mode 100644 unittests/dictionary/enchant_dict_is_added_tests.cpp create mode 100644 unittests/dictionary/enchant_dict_is_removed_tests.cpp create mode 100644 unittests/dictionary/enchant_dict_remove_from_session_tests.cpp create mode 100644 unittests/dictionary/enchant_dict_remove_tests.cpp create mode 100644 unittests/dictionary/enchant_dict_store_replacement_tests.cpp create mode 100644 unittests/dictionary/enchant_dict_suggest_tests.cpp create mode 100644 unittests/enchant_providers/Dictionary/dictionary_check.cpp create mode 100644 unittests/enchant_providers/Dictionary/dictionary_suggest.cpp create mode 100644 unittests/enchant_providers/Provider/provider_describe_dict.cpp create mode 100644 unittests/enchant_providers/Provider/provider_dictionary_exists.cpp create mode 100644 unittests/enchant_providers/Provider/provider_identify_dict.cpp create mode 100644 unittests/enchant_providers/Provider/provider_list_dicts.cpp create mode 100644 unittests/enchant_providers/Provider/provider_request_dict.cpp create mode 100644 unittests/enchant_providers/Readme.txt create mode 100644 unittests/enchant_providers/main.cpp create mode 100644 unittests/enchant_providers/unittest_enchant_providers.h create mode 100644 unittests/main.cpp create mode 100644 unittests/mock_provider/mock_provider.cpp create mode 100644 unittests/mock_provider/mock_provider.h create mode 100644 unittests/provider/enchant_provider_broker_set_error_tests.cpp create mode 100644 unittests/provider/enchant_provider_dict_set_error_tests.cpp create mode 100644 unittests/provider/enchant_provider_get_prefix_dir_tests.cpp create mode 100644 unittests/provider/enchant_provider_get_registry_value_tests.cpp create mode 100644 unittests/provider/enchant_provider_get_user_config_dirs_tests.cpp create mode 100644 unittests/provider/enchant_provider_get_user_language_tests.cpp create mode 100644 unittests/pwl/enchant_pwl_tests.cpp diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..dfb7c2c --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Dom Lachowicz diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..02d0425 --- /dev/null +++ b/BUGS @@ -0,0 +1 @@ +none, yet... diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 0000000..d74274e --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, 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 Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + 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 Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +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 other code 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. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + 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, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser 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 combine 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) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) 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. + + d) 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. + + e) 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 materials to be 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 with +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 Lesser 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. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..bcc69d0 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,47 @@ +2007-01-14 Hubert Figuiere + + * src/Makefile.am (libenchant_includedir): removed trailing '/' + that breaks on MacOS X. + +2006-10-22 Dom Lachowicz + + * Added Voikko spell checker backend (AbiWord bug 10450), for better + Finnish support. Credit goes to Harri Pitknen and Anssi Hannula. + + * Added Zemberek spell checker backend, for better Turkish support. + Credit goes to Baris Metin. + +2006-07-22 Jose Da Silva + + * doc/enchant.1: enchant.1 page more similar to other man/info pages + (bug 10375) + +2006-07-22 Dom Lachowicz + + * src/*/Makefile.am: add '-module' to LDFLAGS so that things get + built correctly on Mac OSX (bug 10398) + * src/enchant.c: Fix C99-ism (bug 10387) + * configure.in: Grab the value of `aspell config prefix` and + use that if available (bug 10308) + +2006-05-06 Dom Lachowicz + + * configure.in: Bump version number to indicate an unstable series + * src/pwl.[ch]: New personal wordlist implementation, based on Trie. + Thanks to Ryan Kelly (bug 10212). + * src/enchant.c: Ditto + +2006-04-23 Dom Lachowicz + + * configure.in: --with-aspell-prefix didn't work + +2006-03-20 Dom Lachowicz + + * configure.in: relocatable_library undefined command in configure; + Bug 10134, from Pierre Joye + + * configure.in: configure does not check if aspell/pspell headers exist; + Bug 10135, based on a patch from Pierre Joye + +2003-07-13 Dom Lachowicz + * Initial import diff --git a/HACKING b/HACKING new file mode 100644 index 0000000..9300024 --- /dev/null +++ b/HACKING @@ -0,0 +1,102 @@ +* Don't commit directly, instead send your diff to the authors (use + 'cvs diff -Nu'). + +Working in libenchant +------------------- + + When writing libenchant our priorities are + + 1) Portable + 2) Maintainable & Documented + 3) Modular and well designed + + When you submit code to inclusion in libenchant, or when you modify the sources +directly on the CVS repository, please keep those things in mind. While +performance is important please note that we do not want to hand tune code +to shave milliseconds at this point. Well designed algorithms and data +strucutures are fertile areas for development, obfuscated code to make a +loop 3% faster is not. Specifically, this means: + + - Clarity of design and function are paramount + - Make sure your code does not generate warnings at all. + - Please follow the formatting style + +Formatting style +---------------- + + The formatting style of libenchant is a mix of various styles, make +yourself familiar with the GNU coding standards (shipped with most +GNU/Linux systems as the standards.info file), then read the Linux +kernel coding standards and ignore Linus' jokes. Then look at the +Gtk+ header files to get aquainted on how to write nice header files +that are almost self documenting. + + Remember: Use 8 space tabs for indentation: that will keep your +code honest as you will be forced to split your routines in more +modular chunks (as detailed by Linus). + + Emacs users can get the default indentation style with this: + (set-c-style "K&R") + (setq c-basic-offset 8) + + On top of that, you will have to: + + - Follow the Gtk+ cleanliness conventions for function + prototypes. + + - Follow the Gtk+ namespace convention for function names. + module_submodule_operation + + - Make sure your code does not have a single warning (with the + default strong warnings that Gnumeric compiles with) before + your code is submited. (Although we do not advocate -Werror) + + - Every entry point to a public routine should use the + g_return_if_fail and g_return_val_if_fail macros to verify + that the parameters passed are valid. + + - Under no circunstances use magic variables. Use typedef + enum { ... } type; to create enumerations. Do not use + integers to hold references to enumerations, the compiler + can help catch various errors. + + - Use g_warning to mark spots that need to be reviewed or are + not finished to let me fix it eventually. + + - Do not submit code that is just a temporary workaround for a + full fledged feature. i.e. don't submit a quick hack at + "search text" which is not designed to be expanded upon. We + do not want to maintain limited features. It is better submit an + implementation that has been designed to be expanded and enhanced, + even if it is not completely finished. + + - It is more important to be correct than to be fast. + + - Do not optimize unnecesarly. Do profile, do look for the + weak spots before applying "optimization by feeling". This + is not a Ouija-based project. + + - It is more important to keep the code maintainable and + readable than to be fast. If you have a great idea about + optimizing the code, make sure it is implemented cleanly, + that there is a clean and maintainable way to do it: + + - Fast code that is difficult to maintain has no place in + Gnumeric and will be dropped. + + - Follow the libenchant commenting style, which is not the Gtk + style; + /* ie. use this for + * multi-line comments + */ + + All of this is to ensure the libenchant code will be kept within +reasonable margins of maintainability for the future: Remember, in two years +you will probably be far too busy to maintain your own contributions, and they +might become a burden to the program maintainers. + + libenchant is intented to be a foundation for a various document centric +projects. + + Cleaning code in libenchant is more important than trying not to break +existing code. By all means, code clean ups are always welcome. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d498b27 --- /dev/null +++ b/LICENSE @@ -0,0 +1,179 @@ +Enchant is currently licensed under the LGPL license, with an Exception so that non-free (as in speech and/or cost) plugin backends could be loaded and used by Enchant. This is mainly so that we can use the native spell checkers on various platforms (MacOS X, MS Office, ...), or that our users could "plug in" their favorite commercial product to do the job. + +The author would consider re-licensing it as necessary to help foster broader adoption. This may include MPL or JCA/SISSL license, but the author would also consider alternate licenses as well. + +GNU LIBRARY GENERAL PUBLIC LICENSE, version 2.0 +Version 2, June 1991 + +http://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html + +Copyright (C) 1991 Free Software Foundation, Inc. +51 Franklin St, 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. +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 an 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 St, 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/MAINTAINERS b/MAINTAINERS new file mode 100644 index 0000000..dfb7c2c --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1 @@ +Dom Lachowicz diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..b9e72b2 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,22 @@ +SUBDIRS=src tests doc data + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = enchant.pc + +EXTRA_DIST = \ + autogen.sh \ + enchant.pc.in \ + enchant-uninstalled.pc.in \ + enchant.spec.in \ + enchant.spec \ + lt-compile-resource \ + compile-resource \ + README \ + TODO \ + HACKING \ + NEWS \ + BUGS \ + ChangeLog \ + MAINTAINERS \ + AUTHORS \ + COPYING.LIB diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..1de83a3 --- /dev/null +++ b/NEWS @@ -0,0 +1,31 @@ +0.4.0 +------------------- +Ispell-like command line interface + +0.3.0 +------------------- +Better Aspell detection +Aspell backend sync's dictionary lists to disk +Ispell crash fixes +Myspell crash fixes +Refined C++ API/ABI +Refined C ABI +Implemented some new APIs +Better implemented some internals + +0.2.0 +------------------- +Myspell support +Better Uspell support +Ability to enumerate providers +Win32 support + +0.1.0 +------------------- +Initial Release +Aspell/Pspell support +Ispell support +Uspell support +Ability to order dictionaries/providers + + diff --git a/README b/README new file mode 100644 index 0000000..3676feb --- /dev/null +++ b/README @@ -0,0 +1,64 @@ +libenchant -- Generic spell checking library +Dom Lachowicz + + libenchant is licensed under the terms of the GNU LGPL included in the +file COPYING.LIB. + + The project aims to provide an efficient extensible abstraction for +dealing with different spell checking libraries. + + Enchant is meant to provide a generic interface into various existing +spell checking libaries. These include, but are not limited to: + * Apple Spell (OS X only) + * Aspell/Pspell + * Ispell + * Hspell + * Uspell + * Voikko (Linux only) + + Enchant is also meant to be used in a cross-platform (XP) environment. Part +of this means that Enchant wants to limit its number of external dependencies +to 0, or as close is as humanly possible. Also, any enchant consumer (i.e. a +Word Processor) should not need to know about what backend providers Enchant +knows about. In fact, Enchant shouldn't even need to know this information +itself. To accomplish this, all of Enchant's providers are DLLs. + + Enchant is also meant to be used in a multi-user environment, such as Unix. +It is preferable to have both a $USER and a $GLOBAL location for both +provider DLLs and for dictionaries themselves, when possible. Enchant's DLL +location algorithm takes this into account, and gives preference to the $USER +DLLs, when found. + +To report libenchant bugs, please visit bugzilla.abisource.com. + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public License + as published by the Free Software Foundation. + + 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 Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA + +-- + +This library has taken insight from + +Aspell/Pspell: + - http://aspell.net + +Mailing lists +------------- + + There is NO mailing list used to discuss libenchant specificly as yet. For now +please use the AbiWord-devel list. To subscribe send a mail to: + + abiword-dev-request@abisource.com +And in the body of the message write "subscribe" + +An archive of the mailing lists is available in: + http://www.abisource.com/mailinglists/abiword-dev/ + diff --git a/TODO b/TODO new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/TODO @@ -0,0 +1 @@ + diff --git a/ac-helpers/binreloc.m4 b/ac-helpers/binreloc.m4 new file mode 100644 index 0000000..071e49e --- /dev/null +++ b/ac-helpers/binreloc.m4 @@ -0,0 +1,77 @@ +# Check for binary relocation support. +# Written by Hongli Lai +# http://autopackage.org/ + +AC_DEFUN([AM_BINRELOC], +[ + AC_ARG_ENABLE(binreloc, + [ --enable-binreloc compile with binary relocation support + (default=enable when available)], + enable_binreloc=$enableval,enable_binreloc=auto) + + AC_ARG_ENABLE(binreloc-threads, + [ --enable-binreloc-threads compile binary relocation with threads support + (default=yes)], + enable_binreloc_threads=$enableval,enable_binreloc_threads=yes) + + BINRELOC_CFLAGS= + BINRELOC_LIBS= + if test "x$enable_binreloc" = "xauto"; then + AC_CHECK_FILE([/proc/self/maps]) + AC_CACHE_CHECK([whether everything is installed to the same prefix], + [br_cv_valid_prefixes], [ + if test "$bindir" = '${exec_prefix}/bin' -a "$sbindir" = '${exec_prefix}/sbin' -a \ + "$datadir" = '${prefix}/share' -a "$libdir" = '${exec_prefix}/lib' -a \ + "$libexecdir" = '${exec_prefix}/libexec' -a "$sysconfdir" = '${prefix}/etc' + then + br_cv_valid_prefixes=yes + else + br_cv_valid_prefixes=no + fi + ]) + fi + AC_CACHE_CHECK([whether binary relocation support should be enabled], + [br_cv_binreloc], + [if test "x$enable_binreloc" = "xyes"; then + br_cv_binreloc=yes + elif test "x$enable_binreloc" = "xauto"; then + if test "x$br_cv_valid_prefixes" = "xyes" -a \ + "x$ac_cv_file__proc_self_maps" = "xyes"; then + br_cv_binreloc=yes + else + br_cv_binreloc=no + fi + else + br_cv_binreloc=no + fi]) + + if test "x$br_cv_binreloc" = "xyes"; then + BINRELOC_CFLAGS="-DENABLE_BINRELOC" + AC_DEFINE(ENABLE_BINRELOC,,[Use binary relocation?]) + if test "x$enable_binreloc_threads" = "xyes"; then + AC_CHECK_LIB([pthread], [pthread_getspecific]) + fi + + AC_CACHE_CHECK([whether binary relocation should use threads], + [br_cv_binreloc_threads], + [if test "x$enable_binreloc_threads" = "xyes"; then + if test "x$ac_cv_lib_pthread_pthread_getspecific" = "xyes"; then + br_cv_binreloc_threads=yes + else + br_cv_binreloc_threads=no + fi + else + br_cv_binreloc_threads=no + fi]) + + if test "x$br_cv_binreloc_threads" = "xyes"; then + BINRELOC_LIBS="-lpthread" + AC_DEFINE(BR_PTHREAD,1,[Include pthread support for binary relocation?]) + else + BINRELOC_CFLAGS="$BINRELOC_CFLAGS -DBR_PTHREADS=0" + AC_DEFINE(BR_PTHREAD,0,[Include pthread support for binary relocation?]) + fi + fi + AC_SUBST(BINRELOC_CFLAGS) + AC_SUBST(BINRELOC_LIBS) +]) diff --git a/ac-helpers/ssize_t.m4 b/ac-helpers/ssize_t.m4 new file mode 100644 index 0000000..4eaef93 --- /dev/null +++ b/ac-helpers/ssize_t.m4 @@ -0,0 +1,21 @@ +# ssize_t.m4 serial 4 (gettext-0.15) +dnl Copyright (C) 2001-2003, 2006 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. +dnl Test whether ssize_t is defined. + +AC_DEFUN([gt_TYPE_SSIZE_T], +[ + AC_CACHE_CHECK([for ssize_t], [gt_cv_ssize_t], + [AC_TRY_COMPILE([#include ], + [int x = sizeof (ssize_t *) + sizeof (ssize_t); + return !x;], + [gt_cv_ssize_t=yes], [gt_cv_ssize_t=no])]) + if test $gt_cv_ssize_t = no; then + AC_DEFINE([ssize_t], [int], + [Define as a signed type of the same size as size_t.]) + fi +]) diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..af54c4d --- /dev/null +++ b/autogen.sh @@ -0,0 +1,99 @@ +#!/bin/sh +# +# Run this before configure +# +# This file blatantly ripped off from subversion. +# +# Note: this dependency on Perl is fine: only developers use autogen.sh +# and we can state that dev people need Perl on their machine +# + +rm -f autogen.err + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +olddir=`pwd` +cd $srcdir + +automake --version | perl -ne 'if (/\(GNU automake\) (([0-9]+).([0-9]+))/) {print; if ($2 < 1 || ($2 == 1 && $3 < 4)) {exit 1;}}' + +if [ $? -ne 0 ]; then + echo "Error: you need automake 1.4 or later. Please upgrade." + exit 1 +fi + +if test ! -d `aclocal --print-ac-dir 2>> autogen.err`; then + echo "Bad aclocal (automake) installation" + exit 1 +fi + +libtoolize="" +for found_lib in libtoolize glibtoolize ; do + $found_lib --version > /dev/null 2>&1 + if [ $? = 0 ]; then + libtoolize=$found_lib + break + fi +done + +$libtoolize --force --copy || { + echo "error: libtoolize failed" + exit 1 +} + +# Produce aclocal.m4, so autoconf gets the automake macros it needs +# +echo "Creating aclocal.m4: aclocal -I ac-helpers $ACLOCAL_FLAGS" + +aclocal -I ac-helpers $ACLOCAL_FLAGS 2>> autogen.err + +# Produce all the `GNUmakefile.in's and create neat missing things +# like `install-sh', etc. +# +echo "automake --add-missing --copy --foreign" + +automake --add-missing --copy --foreign 2>> autogen.err || { + echo "" + echo "* * * warning: possible errors while running automake - check autogen.err" + echo "" +} + +# If there's a config.cache file, we may need to delete it. +# If we have an existing configure script, save a copy for comparison. +if [ -f config.cache ] && [ -f configure ]; then + cp configure configure.$$.tmp +fi + +# Produce ./configure +# +echo "Creating configure..." + +autoconf 2>> autogen.err || { + echo "" + echo "* * * warning: possible errors while running automake - check autogen.err" + echo "" +} + +cd $olddir + +run_configure=true +for arg in $*; do + case $arg in + --no-configure) + run_configure=false + ;; + *) + ;; + esac +done + +if $run_configure; then + $srcdir/configure --enable-maintainer-mode "$@" + echo + echo "Now type 'make' to compile enchant." +else + echo + echo "Now run 'configure' and 'make' to compile enchant." +fi + diff --git a/autopackage/@gtk.org/glib/skeleton.1 b/autopackage/@gtk.org/glib/skeleton.1 new file mode 100644 index 0000000..95ba298 --- /dev/null +++ b/autopackage/@gtk.org/glib/skeleton.1 @@ -0,0 +1,14 @@ +# Package skeleton for glib + +[Meta] +RootName: @gtk.org/glib +DisplayName: The Gtk+ utility library +ShortName: glib +Skeleton-Author: Robert Staudinger +Skeleton-Version: 1 + +[Notes] +Does not set SOFTWARE_VERSIONS + +[Test] +INTERFACE_VERSIONS=$( testForLib -i libglib-2.0.so ) diff --git a/autopackage/@gtk.org/gmodule/skeleton.1 b/autopackage/@gtk.org/gmodule/skeleton.1 new file mode 100644 index 0000000..b64d155 --- /dev/null +++ b/autopackage/@gtk.org/gmodule/skeleton.1 @@ -0,0 +1,14 @@ +# Package skeleton for gmodule + +[Meta] +RootName: @gtk.org/gmodule +DisplayName: The Gtk+ dynamic module loader +ShortName: gmodule +Skeleton-Author: Robert Staudinger +Skeleton-Version: 1 + +[Notes] +Does not set SOFTWARE_VERSIONS + +[Test] +INTERFACE_VERSIONS=$( testForLib -i libgmodule-2.0.so ) diff --git a/autopackage/default.apspec.in b/autopackage/default.apspec.in new file mode 100644 index 0000000..e3da4d2 --- /dev/null +++ b/autopackage/default.apspec.in @@ -0,0 +1,53 @@ +# -*-shell-script-*- + +[Meta] +RootName: @abisource.org/enchant:@VERSION@ +DisplayName: An Enchanting Spell Checking Library +ShortName: enchant +Maintainer: Dom Lachowicz - http://www.abisource.org/ +Packager: Robert Staudinger +Summary: A library that wraps other spell checking backends. +SoftwareVersion: @VERSION@ +AutopackageTarget: 1.0 +PackageVersion: 3 + +# Only uncomment InterfaceVersion if your package exposes interfaces to other software, +# for instance if it includes DSOs or python/perl modules. See the developer guide for more info, +# or ask on autopackage-dev if you don't understand interface versioning in autopackage. + +# FIXME set this automatically +# but VERSION has 3 digits +InterfaceVersion: 1.1 + +[Description] +Enchant is meant to provide a generic interface into various existing +spell checking libaries. These include, but are not limited to: +* Aspell/Pspell +* Ispell +* Hspell +* Uspell + +[BuildPrepare] +prepareBuild --prefix=/bail/if/binreloc/bails --enable-ispell --enable-myspell --enable-aspell --enable-uspell --enable-hspell + +[BuildUnprepare] +unprepareBuild + +[Imports] +echo '*' | import + +[Prepare] +require @gtk.org/glib 0 +require @gtk.org/gmodule 0 + +[Install] +# Put your installation script here +installExe ./bin/* +installLib ./lib/*.so.*.*.* +mkdir -p "$PREFIX/lib/enchant" +mv ./lib/enchant/libenchant_*so* "$PREFIX/lib/enchant" +installMan 1 man/man1/* + +[Uninstall] +rm -R -f "$PREFIX/lib/enchant" +uninstallFromLog diff --git a/compile-resource b/compile-resource new file mode 100755 index 0000000..6a37565 --- /dev/null +++ b/compile-resource @@ -0,0 +1,46 @@ +#!/bin/sh + +# Script to compile a resource file for a DLL if there is a .rc file +# for it. The resource source file is supposed to contain a version +# info section, that uses the string BUILDNUMBER as the least +# significant part of the version numbers. This script replaces that +# string with a "build number" before compiling the binary resource +# file. The build number is kept between builds in a "stamp" file, and +# incremented each time. (If there is no stamp file, build number 0 is +# used.) The intention is that only the "official" maintainer of a DLL +# keeps such a stamp file, and thus the DLLs he releases have +# increasing version number resources, which can be used by an +# installer program to decide whether to replace an existing DLL with +# the same name. + +# This is just my (tml@iki.fi) idea, if somebody comes up with a +# better way to generate version number resources, don't hesitate to +# suggest. + +# The command line arguments are: +# $1: the name of the .rc file to check +# $2: the name of the resource object file to produce, if the rc file exists + +# Check if we have a resource file for this DLL. +rcfile=$1 +resfile=$2 +if [ -f $rcfile ]; then + # Check if we have a build number stamp file. + basename=`basename $rcfile .rc` + if [ -f $basename-build.stamp ]; then + read number <$basename-build.stamp + buildnumber=$[number] + echo Build number $buildnumber + rm -rf $basename-build.stamp + else + echo Using zero as build number + buildnumber=0 + fi + + m4 -DBUILDNUMBER=$buildnumber <$rcfile >$$.rc && + ${WINDRES-windres} $$.rc $resfile && + rm $$.rc +else + # Return failure + exit 1 +fi diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..2051360 --- /dev/null +++ b/configure.in @@ -0,0 +1,574 @@ +AC_INIT(src/enchant.h) +AC_CANONICAL_HOST + +dnl Set release number +dnl This is derived from "Versioning" chapter of info libtool documentation. +PACKAGE=enchant +dnl 4a) Increment when removing or changing interfaces. +ENCHANT_MAJOR_VERSION=1 +dnl 4a) 5) Increment when adding interfaces. +dnl 6) Set to zero when removing or changing interfaces. +ENCHANT_MINOR_VERSION=6 +dnl 3) Increment when interfaces not changed at all, +dnl only bug fixes or internal changes made. +dnl 4b) Set to zero when adding, removing or changing interfaces. +ENCHANT_MICRO_VERSION=1 +dnl +dnl Set this too +MAJOR_VERSION_PLUS_MINOR_VERSION=`expr $ENCHANT_MAJOR_VERSION + $ENCHANT_MINOR_VERSION` +dnl +VERSION=$ENCHANT_MAJOR_VERSION.$ENCHANT_MINOR_VERSION.$ENCHANT_MICRO_VERSION +dnl Version info for libraries = CURRENT:REVISION:AGE +VERSION_INFO=$MAJOR_VERSION_PLUS_MINOR_VERSION:$ENCHANT_MICRO_VERSION:$ENCHANT_MINOR_VERSION +AC_SUBST(VERSION_INFO) +AC_SUBST(ENCHANT_MAJOR_VERSION) +AC_SUBST(ENCHANT_MINOR_VERSION) +AC_SUBST(ENCHANT_MICRO_VERSION) + +AM_INIT_AUTOMAKE($PACKAGE, $VERSION) +AM_MAINTAINER_MODE + +dnl Checks for programs. +AC_ISC_POSIX +AC_PROG_CC +AC_PROG_CPP +AC_PROG_OBJC +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +AC_STDC_HEADERS +AC_LIBTOOL_WIN32_DLL +AM_PROG_LIBTOOL +gt_TYPE_SSIZE_T + +AC_C_CONST + +AC_CHECK_FUNCS(flock lockf) + +have_cxx=yes +AC_PROG_CXX(,have_cxx=no) +AM_CONDITIONAL(WITH_CXX, test "x$have_cxx" = "xyes") + +AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + +PKG_CHECK_MODULES(ENCHANT, [glib-2.0 >= 2.6 gmodule-2.0]) + +dnl =========================================================================== +dnl check compiler flags +AC_DEFUN([ENCHANT_CC_TRY_FLAG], [ + AC_MSG_CHECKING([whether $CC supports $1]) + + enchant_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $1" + + AC_COMPILE_IFELSE([ ], [enchant_cc_flag=yes], [enchant_cc_flag=no]) + CFLAGS="$enchant_save_CFLAGS" + + if test "x$enchant_cc_flag" = "xyes"; then + ifelse([$2], , :, [$2]) + else + ifelse([$3], , :, [$3]) + fi + AC_MSG_RESULT([$enchant_cc_flag]) +]) + +dnl check compiler flags +AC_DEFUN([ENCHANT_CXX_TRY_FLAG], [ + AC_MSG_CHECKING([whether $CXX supports $1]) + + AC_LANG_PUSH(C++) + enchant_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $1" + + AC_COMPILE_IFELSE([ ], [enchant_cxx_flag=yes], [enchant_cxx_flag=no]) + CXXFLAGS="$enchant_save_CXXFLAGS" + + if test "x$enchant_cxx_flag" = "xyes"; then + ifelse([$2], , :, [$2]) + else + ifelse([$3], , :, [$3]) + fi + AC_MSG_RESULT([$enchant_cxx_flag]) + AC_LANG_POP(C++) +]) + +dnl Use lots of warning flags with gcc and compatible compilers + +dnl Note: if you change the following variable, the cache is automatically +dnl skipped and all flags rechecked. So there's no need to do anything +dnl else. If for any reason you need to force a recheck, just change +dnl MAYBE_WARN in an ignorable way (like adding whitespace) + +CC_MAYBE_WARN="-Wall -Wextra \ +-Wsign-compare -Werror-implicit-function-declaration \ +-Wpointer-arith -Wwrite-strings -Wstrict-prototypes \ +-Wmissing-prototypes -Wmissing-declarations -Wnested-externs \ +-Wpacked -Wswitch-enum -Wmissing-format-attribute \ +-Wstrict-aliasing=2 -Winit-self -Wunsafe-loop-optimizations \ +-Wdeclaration-after-statement -Wold-style-definition \ +-Wno-missing-field-initializers -Wno-unused-parameter \ +-Wno-attributes -Wno-long-long -Winline -fno-strict-aliasing" + +# invalidate cached value if MAYBE_WARN has changed +if test "x$enchant_cv_warn_maybe" != "x$CC_MAYBE_WARN"; then + unset enchant_cv_warn_cflags +fi +AC_CACHE_CHECK([for supported C warning flags], enchant_cv_warn_cflags, [ + echo + CC_WARN_CFLAGS="" + + # Some warning options are not supported by all versions of + # gcc, so test all desired options against the current + # compiler. + # + # Note that there are some order dependencies + # here. Specifically, an option that disables a warning will + # have no net effect if a later option then enables that + # warnings, (perhaps implicitly). So we put some grouped + # options (-Wall and -Wextra) up front and the -Wno options + # last. + + for W in $CC_MAYBE_WARN; do + ENCHANT_CC_TRY_FLAG([$W], [CC_WARN_CFLAGS="$CC_WARN_CFLAGS $W"]) + done + + enchant_cv_warn_cflags=$CC_WARN_CFLAGS + enchant_cv_warn_maybe=$CC_MAYBE_WARN + + AC_MSG_CHECKING([which C warning flags were supported])]) +CC_WARN_CFLAGS="$enchant_cv_warn_cflags" +AC_SUBST(CC_WARN_CFLAGS) + +CXX_MAYBE_WARN="-Wall -Wextra \ +-Wsign-compare \ +-Wpointer-arith -Wwrite-strings \ +-Wpacked -Wswitch-enum -Wmissing-format-attribute \ +-Wstrict-aliasing=2 -Winit-self -Wunsafe-loop-optimizations \ +-Wno-missing-field-initializers -Wno-unused-parameter \ +-Wno-attributes -Wno-long-long -Winline -fno-strict-aliasing" + +# invalidate cached value if MAYBE_WARN has changed +if test "x$enchant_cxx_cv_warn_maybe" != "x$CXX_MAYBE_WARN"; then + unset enchant_cxx_cv_warn_cflags +fi +AC_CACHE_CHECK([for supported C++ warning flags], enchant_cxx_cv_warn_cflags, [ + echo + CXX_WARN_CFLAGS="" + + # Some warning options are not supported by all versions of + # gcc, so test all desired options against the current + # compiler. + # + # Note that there are some order dependencies + # here. Specifically, an option that disables a warning will + # have no net effect if a later option then enables that + # warnings, (perhaps implicitly). So we put some grouped + # options (-Wall and -Wextra) up front and the -Wno options + # last. + + for W in $CXX_MAYBE_WARN; do + ENCHANT_CXX_TRY_FLAG([$W], [CXX_WARN_CFLAGS="$CXX_WARN_CFLAGS $W"]) + done + + enchant_cxx_cv_warn_cflags=$CXX_WARN_CFLAGS + enchant_cxx_cv_warn_maybe=$CXX_MAYBE_WARN + + AC_MSG_CHECKING([which C++ warning flags were supported])]) +CXX_WARN_CFLAGS="$enchant_cxx_cv_warn_cflags" +AC_SUBST(CXX_WARN_CFLAGS) + +ENCHANT_CFLAGS="$ENCHANT_CFLAGS -DENCHANT_PREFIX_DIR='\"$prefix\"'" + +AC_SUBST(ENCHANT_CFLAGS) +AC_SUBST(ENCHANT_LIBS) + +AC_MSG_CHECKING([for native Win32]) +case "$target" in + *-*-mingw*) + native_win32=yes + SOCKET_LIBS='-lws2_32 -ldnsapi' + ENCHANT_WIN32_RESOURCE=enchant-win32res.lo + ;; + *) + native_win32=no + SOCKET_LIBS='' + ENCHANT_WIN32_RESOURCE= + ;; +esac +AC_MSG_RESULT([$native_win32]) +AM_CONDITIONAL(OS_WIN32, test "x$native_win32" = "xyes") +AC_SUBST([ENCHANT_WIN32_RESOURCE]) + +AC_MSG_CHECKING([for OS X]) +case ${host_os} in + *darwin*) + check_applespell=yes + ;; + *) + check_applespell=no + ;; +esac +AC_MSG_RESULT([$check_applespell]) + +AC_SUBST(SOCKET_LIBS) + +# Courtesy of Glib: Ensure MSVC-compatible struct packing convention +# is used when compiling for Win32 with gcc. +# What flag to depends on gcc version: gcc3 uses "-mms-bitfields", while +# gcc2 uses "-fnative-struct". +if test x"$native_win32" = xyes; then + if test x"$GCC" = xyes; then + msnative_struct='' + AC_MSG_CHECKING([how to get MSVC-compatible struct packing]) + if test -z "$ac_cv_prog_CC"; then + our_gcc="$CC" + else + our_gcc="$ac_cv_prog_CC" + fi + case `$our_gcc --version | sed -e 's,\..*,.,' -e q` in + 2.) + if $our_gcc -v --help 2>/dev/null | grep fnative-struct >/dev/null; then + msnative_struct='-fnative-struct' + fi + ;; + *) + if $our_gcc -v --help 2>/dev/null | grep ms-bitfields >/dev/null; then + msnative_struct='-mms-bitfields' + fi + ;; + esac + if test x"$msnative_struct" = x ; then + AC_MSG_RESULT([no way]) + AC_MSG_WARN([produced libraries might be incompatible with MSVC-compiled code]) + else + CFLAGS="$CFLAGS $msnative_struct" + CXXFLAGS="$CXXFLAGS $msnative_struct" + AC_MSG_RESULT([${msnative_struct}]) + fi + fi +fi + +dnl =========================================================================== + +m4_copy([AC_DEFUN],[glib_DEFUN]) +glib_DEFUN([GLIB_LC_MESSAGES], + [AC_CHECK_HEADERS([locale.h]) + if test $ac_cv_header_locale_h = yes; then + AC_CACHE_CHECK([for LC_MESSAGES], am_cv_val_LC_MESSAGES, + [AC_TRY_LINK([#include ], [return LC_MESSAGES], + am_cv_val_LC_MESSAGES=yes, am_cv_val_LC_MESSAGES=no)]) + if test $am_cv_val_LC_MESSAGES = yes; then + AC_DEFINE(HAVE_LC_MESSAGES, 1, + [Define if your file defines LC_MESSAGES.]) + fi + fi]) + +GLIB_LC_MESSAGES + +dnl =========================================================================== + +dnl binreloc +dnl AM_BINRELOC +AM_CONDITIONAL(WITH_BINRELOC, test "x$br_cv_binreloc" = "xyes") + +build_ispell=yes + +AC_ARG_ENABLE(ispell, AS_HELP_STRING([--disable-ispell],[enable the ispell backend @<:@default=auto@:>@]), build_ispell="$enableval", build_ispell=yes) + +if test "x$have_cxx" = "xno"; then + build_ispell=no +fi +AM_CONDITIONAL(WITH_ISPELL, test "x$build_ispell" = "xyes") + +ispell_dir=${datadir}/enchant/ispell +AC_ARG_WITH(ispell-dir, AS_HELP_STRING([--with-ispell-dir=PATH],[path to installed ispell dicts])) + +if test "x$with_ispell_dir" != "x" ; then + ispell_dir=$with_ispell_dir +fi + +ISPELL_CFLAGS="-DENCHANT_ISPELL_DICT_DIR='\"$ispell_dir\"'" +AC_SUBST(ISPELL_CFLAGS) + +build_myspell=yes + +AC_ARG_ENABLE(myspell, AS_HELP_STRING([--disable-myspell],[enable the myspell backend @<:@default=auto@:>@]), build_myspell="$enableval", build_myspell=yes) + +if test "x$have_cxx" = "xno"; then + build_myspell=no +fi +AM_CONDITIONAL(WITH_MYSPELL, test "x$build_myspell" = "xyes") + +myspell_dir=${datadir}/myspell/dicts +AC_ARG_WITH(myspell-dir, AS_HELP_STRING([--with-myspell-dir=PATH],[path to installed myspell dicts])) + +if test "x$with_myspell_dir" != "x" ; then + myspell_dir=$with_myspell_dir +fi + +MYSPELL_CFLAGS="$MYSPELL_CFLAGS -DENCHANT_MYSPELL_DICT_DIR='\"$myspell_dir\"'" + +AC_ARG_WITH(system-myspell, AS_HELP_STRING([--with-system-myspell=yes/no],[use the system myspell/hunspell @<:@default=auto@:>@])) +if test "x$with_system_myspell" != "xno"; then + PKG_CHECK_MODULES(MYSPELL, [hunspell], have_system_myspell=true, have_system_myspell=false) + AC_SUBST(MYSPELL_CFLAGS) + AC_SUBST(MYSPELL_LIBS) +else + have_system_myspell=false; +fi +AM_CONDITIONAL(WITH_SYSTEM_MYSPELL, test "x$have_system_myspell" = "xtrue") + +check_aspell=yes +build_aspell=no + +AC_ARG_ENABLE(aspell, AS_HELP_STRING([--disable-aspell],[enable the aspell backend @<:@default=auto@:>@]), check_aspell="$enableval", check_aspell=yes) + +AC_ARG_WITH(aspell-prefix, AS_HELP_STRING([--with-aspell-prefix=DIR],[specify under which prefix aspell is installed]), aspell_prefix="$withval", ) + +if test "x$check_aspell" != "xno"; then + saved_LDFLAGS=$LDFLAGS + + ASPELL_CFLAGS= + if test "x$aspell_prefix" != "x"; then + LDFLAGS="-L$aspell_prefix/lib "$LDFLAGS + ASPELL_INC="-I$aspell_prefix/include" + ASPELL_LIBS="-L$aspell_prefix/lib -laspell" + else + aspell_prefix=${prefix} + if test "x$prefix" = "xNONE"; then + aspell_prefix="/usr/local" + fi + + AC_CHECK_PROG(HAVE_ASPELL, aspell, yes, no) + if test "x$HAVE_ASPELL" = "xyes"; then + AC_MSG_CHECKING([For Aspell >= 0.50.0]) + aspell_major=`aspell -v | awk -F. '{print $4;}' | awk -F")" '{print $1;}'` + if test "$aspell_major" -ge "50"; then + aspell_prefix=`aspell config prefix` + LDFLAGS="-L${aspell_prefix}/lib "$LDFLAGS + ASPELL_INC="-I${aspell_prefix}/include" + ASPELL_LIBS="-L${aspell_prefix}/lib -laspell" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([unknown]) + fi + fi + + if test "x$HAVE_ASPELL" != "xyes"; then + AC_MSG_WARN([Didn't find aspell >= 0.50.0 and no explicit path aspell specified. Guessing at $aspell_prefix]) + ASPELL_LIBS="-L$aspell_prefix/lib -laspell" + fi + fi + + AC_MSG_CHECKING([For aspell headers in $aspell_prefix/include]) + if test -f "$aspell_prefix/include/pspell/pspell.h"; then + AC_MSG_RESULT([yes (pspell)]) + + AC_CHECK_LIB(aspell,new_aspell_config,build_aspell=yes, + [AC_CHECK_LIB(pspell, new_pspell_config, build_aspell=yes, AC_MSG_WARN([Enchant building without the aspell library]))] + ,) + AC_CHECK_LIB(aspell,get_aspell_dict_info_list,ASPELL_CFLAGS="-DASPELL_0_50_0=1",) + + ASPELL_CFLAGS+=" -DHAVE_PSPELL_H" + elif test -f "$aspell_prefix/include/aspell.h"; then + AC_MSG_RESULT([yes (aspell)]) + + AC_CHECK_LIB(aspell,new_aspell_config,build_aspell=yes, + [AC_CHECK_LIB(pspell, new_pspell_config, build_aspell=yes, AC_MSG_WARN([Enchant building without the aspell library]))] + ,) + AC_CHECK_LIB(aspell,get_aspell_dict_info_list,ASPELL_CFLAGS="-DASPELL_0_50_0=1",) + else + AC_MSG_RESULT([no]) + if test "x$HAVE_ASPELL" = "xyes"; then + AC_MSG_WARN([Didn't find pspell (aspell) headers]) + fi + fi + LDFLAGS=$saved_LDFLAGS + + AC_SUBST(ASPELL_INC) + AC_SUBST(ASPELL_LIBS) + AC_SUBST(ASPELL_CFLAGS) +fi + +AM_CONDITIONAL(WITH_ASPELL, test "$build_aspell" = yes) + +check_voikko=yes +build_voikko=no + +AC_ARG_ENABLE(voikko, AS_HELP_STRING([--disable-voikko],[enable the voikko backend @<:@default=auto@:>@]), check_voikko="$enableval", check_voikko=yes) + +AC_ARG_WITH(voikko-prefix, AS_HELP_STRING([--with-voikko-prefix=DIR],[specify under which prefix voikko is installed]), voikko_prefix="$withval", ) + +if test "x$check_voikko" != "xno"; then + saved_LDFLAGS=$LDFLAGS + + VOIKKO_INC= + VOIKKO_LIBS="-lvoikko" + if test "x$voikko_prefix" != "x"; then + LDFLAGS="-L$voikko_prefix/lib "$LDFLAGS + VOIKKO_INC="-I$voikko_prefix/include" + VOIKKO_LIBS="-L$voikko_prefix/lib "$VOIKKO_LIBS + fi + + AC_CHECK_LIB(voikko,voikko_init,build_voikko=yes) + + LDFLAGS=$saved_LDFLAGS + + AC_SUBST(VOIKKO_INC) + AC_SUBST(VOIKKO_LIBS) +fi + +AM_CONDITIONAL(WITH_VOIKKO, test "$build_voikko" = yes) + +build_uspell=no + +check_uspell=yes +AC_ARG_ENABLE(uspell, AS_HELP_STRING([--disable-uspell],[enable the uspell backend @<:@default=auto@:>@]), check_uspell="$enableval", check_uspell=yes) + +if test "x$have_cxx" = "xno"; then + check_uspell=no +fi + +if test "x$check_uspell" != "xno"; then + PKG_CHECK_MODULES(USPELL, [libuspell >= 1.1.0], build_uspell=yes, build_uspell=no) +fi + +uspell_dir=${datadir}/uspell +AC_ARG_WITH(uspell-dir, AS_HELP_STRING([--with-uspell-dir=PATH],[path to installed uspell dicts])) + +if test "x$with_uspell_dir" != "x" ; then + uspell_dir=$with_uspell_dir +fi + +USPELL_CFLAGS="$USPELL_CFLAGS -DENCHANT_USPELL_DICT_DIR='\"$uspell_dir\"'" +AC_SUBST(USPELL_CFLAGS) +AC_SUBST(USPELL_LIBS) + +AM_CONDITIONAL(WITH_USPELL, test "$build_uspell" = yes) + +build_hspell=no +check_hspell=yes +AC_ARG_ENABLE(hspell, AS_HELP_STRING([--disable-hspell],[enable the hspell backend @<:@default=auto@:>@]), check_hspell="$enableval", check_hspell=yes) + +AC_ARG_WITH(hspell-prefix, AS_HELP_STRING([--with-hspell-prefix=DIR],[specify under which prefix hspell is installed]), hspell_prefix="$withval", ) + +dnl change to pkg-config when hspell provides a hspell.pc file + +if test "x$check_hspell" != "xno"; then + saved_LDFLAGS=$LDFLAGS + + HSPELL_CFLAGS= + HSPELL_LIBS=" -lhspell -lz" + + if test "x$hspell_prefix" != "x"; then + LDFLAGS="-L$hspell_prefix/lib "$LDFLAGS + HSPELL_CFLAGS="-I$hspell_prefix/include" + HSPELL_LIBS="-L$hspell_prefix/lib "$HSPELL_LIBS + fi + + AC_CHECK_LIB(hspell, hspell_get_dictionary_path, build_hspell=yes, build_hspell=no, -lz) + LDFLAGS=$saved_LDFLAGS + + AC_SUBST(HSPELL_CFLAGS) + AC_SUBST(HSPELL_LIBS) +fi + +AM_CONDITIONAL(WITH_HSPELL, test "$build_hspell" = yes) + +build_zemberek=no + +AC_ARG_ENABLE(zemberek, AS_HELP_STRING([--disable-zemberek],[enable the experimental zemberek (turkish) backend @<:@default=auto@:>@]), build_zemberek="$enableval", build_zemberek=no) + +if test "x$have_cxx" = "xno"; then + build_zemberek=no +fi +AC_MSG_CHECKING([For DBus-Glib >= 0.62]) +if pkg-config --atleast-version=0.62 dbus-glib-1; then + ZEMBEREK_CFLAGS=`pkg-config --cflags dbus-glib-1` + ZEMBEREK_LIBS=`pkg-config --libs dbus-glib-1` +else + build_zemberek=no +fi + +AC_SUBST(ZEMBEREK_CFLAGS) +AC_SUBST(ZEMBEREK_LIBS) + +AM_CONDITIONAL(WITH_ZEMBEREK, test "x$build_zemberek" = "xyes") + +zemberek_dir=${datadir}/enchant/zemberek + +APPLESPELL_CFLAGS="" +APPLESPELL_LIBS="" +APPLESPELL_LDFLAGS="" + +build_applespell=no +if test "x$check_applespell" = "xyes"; then + + AC_ARG_ENABLE(applespell, AS_HELP_STRING([--disable-applespell],[enable the applespell backend @<:@default=no@:>@]), build_applespell="$enableval", build_applespell=no) + + if test "x$build_applespell" != "xno"; then + APPLESPELL_CFLAGS+=" -DXP_TARGET_COCOA -xobjective-c" + APPLESPELL_LIBS+=" -lobjc" + APPLESPELL_LDFLAGS+=" -framework Cocoa" + fi + +fi + +AC_SUBST(APPLESPELL_CFLAGS) +AC_SUBST(APPLESPELL_LIBS) +AC_SUBST(APPLESPELL_LDFLAGS) + +AM_CONDITIONAL(WITH_APPLESPELL, test "x$build_applespell" = "xyes") + +dnl ======================================================================================= + +AC_OUTPUT([ +Makefile +enchant.pc +enchant-uninstalled.pc +enchant.spec +src/libenchant.rc +src/Makefile +src/aspell/Makefile +src/ispell/Makefile +src/uspell/Makefile +src/myspell/Makefile +src/hspell/Makefile +src/applespell/Makefile +src/voikko/Makefile +src/zemberek/Makefile +tests/Makefile +tests/ispell +doc/Makefile +data/Makefile +autopackage/default.apspec +]) + +dnl =========================================================================================== + +relocatable_library="no" +if test "x$native_win32" = "xyes" || test "x$br_cv_binreloc" = "xyes"; then + relocatable_library="yes" +fi + +echo " +$PACKAGE-$VERSION + + prefix: ${prefix} + compiler: ${CC} + + Build Aspell backend: ${build_aspell} + Build Ispell backend: ${build_ispell} + Build Uspell backend: ${build_uspell} + Build Hspell backend: ${build_hspell} + Build Myspell/Hunspell backend: ${build_myspell} + Build Voikko backend (Linux only): ${build_voikko} + Build Zemberek backend: ${build_zemberek} + Build Apple Spell backend (OS X only): ${build_applespell} + Build a relocatable library: ${relocatable_library}" + +if test "x$build_zemberek" = "xyes"; then + echo " +The Zemberek Turkish spell-checking plugin is enabled. It is known +to cause crashes with WebKit. Use at your own discretion." +fi diff --git a/data/Makefile.am b/data/Makefile.am new file mode 100644 index 0000000..e361e02 --- /dev/null +++ b/data/Makefile.am @@ -0,0 +1,4 @@ +orderingdir=$(datadir)/enchant +ordering_DATA=enchant.ordering + +EXTRA_DIST = $(ordering_DATA) diff --git a/data/enchant.ordering b/data/enchant.ordering new file mode 100644 index 0000000..8e380fb --- /dev/null +++ b/data/enchant.ordering @@ -0,0 +1,8 @@ +*:myspell,aspell,ispell +fi:voikko,ispell,myspell,aspell +fi_FI:voikko,ispell,myspell,aspell +he:hspell,myspell +he_IL:hspell,myspell +yi:uspell +tr:zemberek +tr_TR:zemberek diff --git a/doc/.cvsignore b/doc/.cvsignore new file mode 100644 index 0000000..282522d --- /dev/null +++ b/doc/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..4c24bae --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,3 @@ +man_MANS = enchant.1 + +EXTRA_DIST = $(man_MANS) diff --git a/doc/enchant.1 b/doc/enchant.1 new file mode 100644 index 0000000..2b5bed7 --- /dev/null +++ b/doc/enchant.1 @@ -0,0 +1,69 @@ +.PU +.TH ENCHANT 1 "July 2006" enchant "Enchant Mini Help File" +.SH NAME +Enchant \- a spellchecker +.SH SYNOPSIS +.ll +8 +.B enchant +[\fB\-a\fR] [\fB\-l\fR] [\fB\-L\fR] [\fB\-v\fR] +.ll -8 +.br +.SH DESCRIPTION +.B Enchant +is an ispell-compatible spellchecker. +.SS OPTIONS +.TP +.B "\-a" +List alternatives. +.TP +.B "\-l" +List only the misspellings. +.TP +.B "\-L" +Include the line number in the output. +.TP +.B "\-v" +Prints the program's version. +.SH ENCHANT ORDERING FILE +Enchant has a global and a per-user ordering file named \fIenchant.ordering\fR. +It lets the user specify which spelling backend to use for individual +languages in the case when you care which backend gets used. The global +file is located in \fI$(datadir)/enchant\fR and the per-user file is +located in \fI~/.enchant\fR. +The per-user file takes precedence, if found. +.PP +The ordering file takes the form language_tag:. I am currently aware of the following backends: aspell, myspell, ispell, uspell, hspell, voikko, and zemberek. '*' is +used to mean "use this ordering for all languages, unless instructed otherwise." For example: +.PP +*:aspell,myspell,ispell +.br +en:aspell,myspell,ispell +.br +en_UK:myspell,aspell,ispell +.br +fr:myspell,ispell:aspell +.SH DIRECTORIES IMPORTANT TO ENCHANT +Unless configured otherwise, Enchant's Myspell, Ispell, and Uspell +backends will look for dictionaries in directories specific to Enchant, +and will not use your system-wide installed dictionaries. This is for +pragmatic reasons since many distributions install these dictionaries +into different locations. +.PP +Like the \fIenchant.ordering\fR file described above, Enchant looks in +the global directory for these dictionaries and a per-user directory. +The per-user directory takes precedence if it is found. +Enchant looks for Myspell dictionaries in \fI$(datadir)/enchant/myspell\fR and \fI~/.enchant/myspell\fR. +Enchant looks for Ispell dictionaries in \fI$(datadir)/enchant/ispell\fR and \fI~/.enchant/ispell\fR. +Enchant looks for Uspell dictionaries in \fI$(datadir)/enchant/uspell\fR and \fI~/.enchant/uspell\fR. +.PP +Packagers and users may wish to make symbolic links to the system-wide dictionary directories. Or, preferably, use the --with-myspell-dir, --with-ispell-dir, and --with-uspell-dir 'configure' arguments. +.SH MORE INFORMATION +http://www.abisource.com/enchant/ +.SH "SEE ALSO" +.BR aspell(1), +.BR ispell(1), +.SH "AUTHOR" + Dom Lachowicz + WEB: http://www.abisource.com/enchant/ + MAIL: domlachowicz@gmail.com diff --git a/enchant-uninstalled.pc.in b/enchant-uninstalled.pc.in new file mode 100644 index 0000000..a61c2b1 --- /dev/null +++ b/enchant-uninstalled.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libenchant +Description: A spell checking library +Version: @VERSION@ +Requires: glib-2.0 gmodule-2.0 +Libs: ${pc_top_builddir}/${pcfiledir}/src/libenchant.la +Cflags: -I${pc_top_builddir}/${pcfiledir}/src diff --git a/enchant.manifest b/enchant.manifest new file mode 100644 index 0000000..f127585 --- /dev/null +++ b/enchant.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/enchant.pc.in b/enchant.pc.in new file mode 100644 index 0000000..1699ce1 --- /dev/null +++ b/enchant.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libenchant +Description: A spell checking library +Version: @VERSION@ +Requires: glib-2.0 gmodule-no-export-2.0 +Libs: -L${libdir} -lenchant +Cflags: -I${includedir}/enchant diff --git a/enchant.spec.in b/enchant.spec.in new file mode 100644 index 0000000..1b25cc0 --- /dev/null +++ b/enchant.spec.in @@ -0,0 +1,96 @@ +%define name enchant +%define version @VERSION@ +%define release 1 + +Summary: An Enchanting Spell Checking Library + +Name: %{name} +Version: %{version} +Release: %{release} +Group: System Environment/Libraries +License: LGPL + +Source: ftp://ftp.gnome.org/pub/GNOME/unstable/sources/libenchant/%{name}-%{version}.tar.gz +Buildroot: /var/tmp/%{name}-%{version}-%{release}-root +URL: http://www.abisource.com/ + +Requires: glib2 >= 2.0.0 +BuildRequires: glib2-devel >= 2.0.0 + +%description +A library that wraps other spell checking backends. + +%package devel +Summary: Support files necessary to compile applications with libenchant. +Group: Development/Libraries +Requires: enchant + +%description devel +Libraries, headers, and support files necessary to compile applications using libenchant. + +%prep + +%setup + +%build +%ifarch alpha + MYARCH_FLAGS="--host=alpha-redhat-linux" +%endif + +if [ ! -f configure ]; then +CFLAGS="$RPM_OPT_FLAGS" ./autogen.sh --prefix=%{_prefix} +fi +CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{_prefix} \ +%{?_without_ispell:--disable-ispell} \ +%{?_without_myspell:--disable-myspell} \ +%{?_without_aspell:--disable-aspell} \ +%{?_with_uspell:--enable-uspell} + + +if [ "$SMP" != "" ]; then + (%__make "MAKE=%__make -k -j $SMP"; exit 0) + %__make +else +%__make +fi + +%install +if [ -d $RPM_BUILD_ROOT ]; then rm -r $RPM_BUILD_ROOT; fi +%__make DESTDIR=$RPM_BUILD_ROOT install +find $RPM_BUILD_ROOT/%{_libdir} -name \*.la -exec rm -f \{\} \; + +%files +%defattr(644,root,root,755) +%doc AUTHORS COPYING.LIB README +%attr(755,root,root)%{_bindir}/* +%{_libdir}/lib*.so* +%{_prefix}/man/man1/enchant.1.gz +%{!?_without_ispell:%{_libdir}/enchant/*ispell.so*} +%{!?_without_myspell:%{_libdir}/enchant/*myspell.so*} +%{!?_without_aspell:%{_libdir}/enchant/*aspell.so*} +%{?_with_uspell:%{_libdir}/enchant/*uspell.so*} + +%files devel +%defattr(644,root,root,755) +%{_libdir}/*.a +%{!?_without_ispell:%{_libdir}/enchant/*ispell.a} +%{!?_without_myspell:%{_libdir}/enchant/*myspell.a} +%{!?_without_aspell:%{_libdir}/enchant/*aspell.a} +%{?_with_uspell:%{_libdir}/enchant/*uspell.a} +%{_libdir}/pkgconfig/enchant.pc +%{_includedir}/enchant/* + +%clean +%__rm -r $RPM_BUILD_ROOT + +%changelog +* Sun Aug 24 2003 Rui Miguel Seabra +- update spec to current stat of affairs +- building from source rpm is now aware of --with and --without flags: +- --without aspell --without ispell --without myspell --with uspell + +* Wed Jul 16 2003 Rui Miguel Seabra +- take advantage of environment rpm macros + +* Sun Jul 13 2003 Dom Lachowicz +- Initial version diff --git a/lt-compile-resource b/lt-compile-resource new file mode 100755 index 0000000..aeddb44 --- /dev/null +++ b/lt-compile-resource @@ -0,0 +1,78 @@ +#!/bin/sh + +# Script to compile a resource file for a DLL in the same way that +# libtool would, if it knew about .rc files. + +# This kinda sucks, but the alternative would be to teach autoconf, +# automake, and libtool about compiling .rc files. That would be +# doable, but waiting for those changes to propagate to official +# versions of those tools would take some time. + +# The command line arguments are: +# $1: the name of the .rc file to compile if it exists +# $2: the name of the resource libtool object file to produce + +rcfile=$1 +lo=$2 +case "$lo" in +*.lo) + resfile=.libs/`basename $lo .lo`.o + ;; +*) + echo libtool object name should end with .lo + exit 1 + ;; +esac +d=`dirname $0` + +# Create .libs if not there already +[ ! -d .libs ] && mkdir .libs + +# Super-ugly hack: libtool can work in two ways on Win32: Either it +# uses .lo files which are the real object files in "this" directory, +# or it creates .o files in the .libs subdirectory, and the .lo file +# is a small text file. We try to deduce which case this is by +# checking if there are any .o files in .libs. This requires that the +# resource file gets built last in the Makefile. + +o_files_in_dotlibs=`echo .libs/*.o` +case "$o_files_in_dotlibs" in + .libs/\*.o) + use_script=false + ;; + *) use_script=true + ;; +esac + +# Another way of working of libtool: When compiling with --enable-static and +# --disable-shared options, the .lo file can be still a small text file, and +# the .o files are created in the same directory as the .lo files. + +o_files_in_dot=`echo ./*.o` +case "$o_files_in_dot" in + ./\*.o) + use_script=$use_script + ;; + *) use_script=true + ;; +esac + +# Try to compile resource file +$d/compile-resource $rcfile $resfile && { + if [ $use_script = true ]; then + # Handcraft a libtool object + # libtool checks for a second line matching "Generated by .* libtool"! + (echo "# $lo" + echo "# Generated by lt-compile-resource, compatible with libtool" + echo "pic_object=$resfile" + echo "non_pic_object=none") >$lo + else + mv $resfile $lo + fi + # Success + exit 0 +} + +# If unsuccessful (no .rc file, or some error in it) return failure + +exit 1 diff --git a/msvc/Build.win32.readme b/msvc/Build.win32.readme new file mode 100644 index 0000000..6e223ea --- /dev/null +++ b/msvc/Build.win32.readme @@ -0,0 +1,88 @@ +The following are notes on building enchant and its dependencies using MSVC 2005. + +Enchant depends on glib (http://www.gtk.org/) +Glib depends on libiconv (http://www.gnu.org/software/libiconv/) + and libintl (http://www.gnu.org/software/gettext/) + +Glib passes file handles to clients. Libiconv takes a file pointer (in fprintf) from clients. Because + these are CRT resources, it is very important that the version of the C runtime library be the same + for glib and any dlls that use it. See http://msdn2.microsoft.com/en-us/library/abx4dbyh(vs.80).aspx + and http://msdn2.microsoft.com/en-us/library/ms235460(VS.80).aspx for more information. + + If you see a Debug Assertion Failed (_osfile(fh) & FOPEN) this is because a file is opened using + one runtime and read using another. + + Because libenchant does not expose any CRT boundaries, (no file handles, locales, or environment + variables passed to clients, and no CRT allocated pointers that will be free'd by clients, except + when the free method is provided in the library) it can be linked with applications that do not + use the same CRT. The same is true of libenchant and the providers. Due to the fact that a + provider may also link to glib, we will explicitly name our glib with a new CRT so that others + can coexist. + +This means you need to compile glib and libintl yourself instead of using Tor Lillqvist's prebuilt libraries + (http://www.gimp.org/~tml/gimp/win32/downloads.html) since they use MSVCRT.DLL. (You can use + the prebuilt ones for libiconv, but I've included instructions for that as well.) + +For people concerned that the use of any runtime library other than MSVCRT.DLL would invalidate + the GPL see http://www.gnu.org/licenses/gpl-faq.html#WindowsRuntimeAndGPL + +I successfully built using libiconv-1.9.1, gettext-0.14.5, and glib-2.14.2 + +libiconv and libintl are codependent so you need to follow the instructions in libiconv's README.woe32, +in summary: + +For release + - build libiconv with "nmake -f Makefile.msvc NO_NLS=1 DLL=1 MFLAGS=-MD" + (I got errors: 6 unresolved externals like unresolved external symbol + __imp____libiconv_version referenced in function _print_version. But + those were errors with building iconv_no_i18n.exe which isn't required + for the next steps so can safely be ignored.) + - install libiconv with "nmake -f Makefile.msvc DLL=1 MFLAGS=-MD install" + (This will produce a set of files in c:\usr.) + - build libintl with "nmake -f Makefile.msvc DLL=1 MFLAGS=-MD" + - install libintl with "nmake -f Makefile.msvc DLL=1 MFLAGS=-MD install" + - build libiconv again with "nmake -f Makefile.msvc DLL=1 MFLAGS=-MD check" + - install libiconv again with "nmake -f Makefile.msvc DLL=1 MFLAGS=-MD install" + +For debug: + - build libiconv with "nmake -f Makefile.msvc NO_NLS=1 DLL=1 MFLAGS=-MDd PREFIX=c:\usr-debug" + - install libiconv with "nmake -f Makefile.msvc DLL=1 MFLAGS=-MDd PREFIX=c:\usr-debug install" + (This will produce a set of files in c:\usr.) + - build libintl with "nmake -f Makefile.msvc DLL=1 MFLAGS=-MDd PREFIX=c:\usr-debug" + - install libintl with "nmake -f Makefile.msvc DLL=1 MFLAGS=-MDd PREFIX=c:\usr-debug install" + - build libiconv again with "nmake -f Makefile.msvc DLL=1 MFLAGS=-MDd PREFIX=c:\usr-debug check" + - install libiconv again with "nmake -f Makefile.msvc DLL=1 MFLAGS=-MDd PREFIX=c:\usr-debug install" + +add a manifest to iconv.dll and intl.dll + mt iconv.dll.manifest -outputresource:iconv.dll + +glib has a VC2005 solution (build\win32\vs8) that can be used to build it + currently it has issues so we will do the bare minimum to build glib.dll and gmodule.dll + copy libintl.h and iconv.h from c:\usr\include to glib-2.14.2\ (base directory) + copy iconv.lib and intl.lib from c:\usr\lib to glib-2.14.2\build\win32\vs8\lib\release + run the glib makefile in the glib directory to generate the glib.def file + "nmake -f makefile.msc glib.def" + build the pcre library from the makefile.msc (after removing the -DEBCDIC=0 line and adding -I . ) + add glib\gsequence.c, glib\gregex.c to the glib project + add ..\..\..\glib\pcre to the libpath and add pcre.lib to the list of libraries + with debug version, I was getting two copies of the runtime (debug and release) so had to + explicitly tell it to ignore the release version of the library (msvcrt.lib) + +copy iconv.dll and intl.dll from c:\usr\bin to enchant\lib\glib\release +copy libglib-2.0-0-vs8.dll and libgmodule-2.0-0.dll to enchant\lib\glib\release +copy glib-2.0-vs8.lib and gmodule-2.0-vs8.lib to enchant\lib\glib\lib\release + +do the same as above for debug + + +When distributing, be sure to include: + msvcm80.dll + msvcp80.dll + msvcr80.dll + Microsoft.VC80.CRT.manifest (these are included in vcredist_x86.exe and Microsoft_VC80_CRT_x86.msm) + intl.dll + iconv.dll + libglib-2.0-0-vs8.dll + libgmodule-2.0-0-vs8.dll + libenchant.dll + (any providers and their dependencies) diff --git a/msvc/enchant-lsmod.vcproj b/msvc/enchant-lsmod.vcproj new file mode 100644 index 0000000..0cb88ae --- /dev/null +++ b/msvc/enchant-lsmod.vcproj @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/enchant.sln b/msvc/enchant.sln new file mode 100644 index 0000000..d575cdc --- /dev/null +++ b/msvc/enchant.sln @@ -0,0 +1,341 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libenchant", "libenchant.vcproj", "{E0DB6274-F44C-48E6-AE30-C00D59864569}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libenchant_aspell", "libenchant_aspell.vcproj", "{E948850F-C9DA-4824-876D-4FE97B88E79F}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {E0DB6274-F44C-48E6-AE30-C00D59864569} = {E0DB6274-F44C-48E6-AE30-C00D59864569} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libenchant_hspell", "libenchant_hspell.vcproj", "{AA6839DF-19CB-49DD-8258-ABB86B5B0428}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {E0DB6274-F44C-48E6-AE30-C00D59864569} = {E0DB6274-F44C-48E6-AE30-C00D59864569} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libenchant_uspell", "libenchant_uspell.vcproj", "{6FA58A42-075E-4671-BBB4-F7AA8061F839}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {E0DB6274-F44C-48E6-AE30-C00D59864569} = {E0DB6274-F44C-48E6-AE30-C00D59864569} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libenchant_ispell", "libenchant_ispell.vcproj", "{5DC9EE15-B080-4964-96CC-1BDC90BB873A}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {E0DB6274-F44C-48E6-AE30-C00D59864569} = {E0DB6274-F44C-48E6-AE30-C00D59864569} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libenchant_myspell", "libenchant_myspell.vcproj", "{70927F4C-0446-4EF3-9F65-FA78AC210506}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {E0DB6274-F44C-48E6-AE30-C00D59864569} = {E0DB6274-F44C-48E6-AE30-C00D59864569} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libenchant_voikko", "libenchant_voikko.vcproj", "{5612C360-309B-4E73-8512-6A1265590B90}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {E0DB6274-F44C-48E6-AE30-C00D59864569} = {E0DB6274-F44C-48E6-AE30-C00D59864569} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libenchant_zemberek", "libenchant_zemberek.vcproj", "{B35929B8-374C-442B-A0F5-494AD69EDE1A}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {E0DB6274-F44C-48E6-AE30-C00D59864569} = {E0DB6274-F44C-48E6-AE30-C00D59864569} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test-enchant", "test-enchant.vcproj", "{3CC7AE6D-FEA3-4EB3-98FA-8AB038541411}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {E0DB6274-F44C-48E6-AE30-C00D59864569} = {E0DB6274-F44C-48E6-AE30-C00D59864569} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test-enchantxx", "test-enchantxx.vcproj", "{001DB089-3C83-4384-BB2B-1FE37D091B04}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {E0DB6274-F44C-48E6-AE30-C00D59864569} = {E0DB6274-F44C-48E6-AE30-C00D59864569} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "enchant", "enchant.vcproj", "{FE3CD71B-7742-46DD-AAE9-3CCAA110B4DB}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {E0DB6274-F44C-48E6-AE30-C00D59864569} = {E0DB6274-F44C-48E6-AE30-C00D59864569} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "enchant-lsmod", "enchant-lsmod.vcproj", "{8B0F0A17-CEB4-42DF-B97E-375C60DB31E7}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {E0DB6274-F44C-48E6-AE30-C00D59864569} = {E0DB6274-F44C-48E6-AE30-C00D59864569} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittest-enchant", "unittest-enchant.vcproj", "{44BF74D0-5462-4A3B-8768-61E0DEF81B7B}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {E0DB6274-F44C-48E6-AE30-C00D59864569} = {E0DB6274-F44C-48E6-AE30-C00D59864569} + {A8F1C09A-A244-441B-804C-635106305056} = {A8F1C09A-A244-441B-804C-635106305056} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libenchant_mock_provider", "libenchant_mock_provider.vcproj", "{A8F1C09A-A244-441B-804C-635106305056}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Readme", "Readme", "{A8947D89-B8C1-479C-8C2B-E0444D624E0E}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(SolutionItems) = preProject + Build.win32.readme = Build.win32.readme + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Enchant.Net", "..\src\bindings\Enchant.Net\Enchant.Net.csproj", "{249A514E-7212-485B-86D0-E0ACB8967A4C}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {E0DB6274-F44C-48E6-AE30-C00D59864569} = {E0DB6274-F44C-48E6-AE30-C00D59864569} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Enchant.Net.Tests", "..\unittests\Enchant.Net.Tests\Enchant.Net.Tests.csproj", "{518F02B2-A542-44B3-A10D-CA8144268077}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {5DC9EE15-B080-4964-96CC-1BDC90BB873A} = {5DC9EE15-B080-4964-96CC-1BDC90BB873A} + {70927F4C-0446-4EF3-9F65-FA78AC210506} = {70927F4C-0446-4EF3-9F65-FA78AC210506} + {E0DB6274-F44C-48E6-AE30-C00D59864569} = {E0DB6274-F44C-48E6-AE30-C00D59864569} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittest-providers", "unittest-providers.vcproj", "{B38EE40E-078C-427E-82BB-AC517B720F97}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {E948850F-C9DA-4824-876D-4FE97B88E79F} = {E948850F-C9DA-4824-876D-4FE97B88E79F} + {5DC9EE15-B080-4964-96CC-1BDC90BB873A} = {5DC9EE15-B080-4964-96CC-1BDC90BB873A} + {6FA58A42-075E-4671-BBB4-F7AA8061F839} = {6FA58A42-075E-4671-BBB4-F7AA8061F839} + {70927F4C-0446-4EF3-9F65-FA78AC210506} = {70927F4C-0446-4EF3-9F65-FA78AC210506} + {5612C360-309B-4E73-8512-6A1265590B90} = {5612C360-309B-4E73-8512-6A1265590B90} + {B35929B8-374C-442B-A0F5-494AD69EDE1A} = {B35929B8-374C-442B-A0F5-494AD69EDE1A} + {AA6839DF-19CB-49DD-8258-ABB86B5B0428} = {AA6839DF-19CB-49DD-8258-ABB86B5B0428} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|Win32 = Debug|Win32 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E0DB6274-F44C-48E6-AE30-C00D59864569}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {E0DB6274-F44C-48E6-AE30-C00D59864569}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {E0DB6274-F44C-48E6-AE30-C00D59864569}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {E0DB6274-F44C-48E6-AE30-C00D59864569}.Debug|Win32.ActiveCfg = Debug|Win32 + {E0DB6274-F44C-48E6-AE30-C00D59864569}.Debug|Win32.Build.0 = Debug|Win32 + {E0DB6274-F44C-48E6-AE30-C00D59864569}.Release|Any CPU.ActiveCfg = Release|Win32 + {E0DB6274-F44C-48E6-AE30-C00D59864569}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {E0DB6274-F44C-48E6-AE30-C00D59864569}.Release|Mixed Platforms.Build.0 = Release|Win32 + {E0DB6274-F44C-48E6-AE30-C00D59864569}.Release|Win32.ActiveCfg = Release|Win32 + {E0DB6274-F44C-48E6-AE30-C00D59864569}.Release|Win32.Build.0 = Release|Win32 + {E948850F-C9DA-4824-876D-4FE97B88E79F}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {E948850F-C9DA-4824-876D-4FE97B88E79F}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {E948850F-C9DA-4824-876D-4FE97B88E79F}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {E948850F-C9DA-4824-876D-4FE97B88E79F}.Debug|Win32.ActiveCfg = Debug|Win32 + {E948850F-C9DA-4824-876D-4FE97B88E79F}.Debug|Win32.Build.0 = Debug|Win32 + {E948850F-C9DA-4824-876D-4FE97B88E79F}.Release|Any CPU.ActiveCfg = Release|Win32 + {E948850F-C9DA-4824-876D-4FE97B88E79F}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {E948850F-C9DA-4824-876D-4FE97B88E79F}.Release|Mixed Platforms.Build.0 = Release|Win32 + {E948850F-C9DA-4824-876D-4FE97B88E79F}.Release|Win32.ActiveCfg = Release|Win32 + {E948850F-C9DA-4824-876D-4FE97B88E79F}.Release|Win32.Build.0 = Release|Win32 + {AA6839DF-19CB-49DD-8258-ABB86B5B0428}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {AA6839DF-19CB-49DD-8258-ABB86B5B0428}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {AA6839DF-19CB-49DD-8258-ABB86B5B0428}.Debug|Win32.ActiveCfg = Debug|Win32 + {AA6839DF-19CB-49DD-8258-ABB86B5B0428}.Release|Any CPU.ActiveCfg = Release|Win32 + {AA6839DF-19CB-49DD-8258-ABB86B5B0428}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {AA6839DF-19CB-49DD-8258-ABB86B5B0428}.Release|Win32.ActiveCfg = Release|Win32 + {6FA58A42-075E-4671-BBB4-F7AA8061F839}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {6FA58A42-075E-4671-BBB4-F7AA8061F839}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {6FA58A42-075E-4671-BBB4-F7AA8061F839}.Debug|Win32.ActiveCfg = Debug|Win32 + {6FA58A42-075E-4671-BBB4-F7AA8061F839}.Release|Any CPU.ActiveCfg = Release|Win32 + {6FA58A42-075E-4671-BBB4-F7AA8061F839}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {6FA58A42-075E-4671-BBB4-F7AA8061F839}.Release|Win32.ActiveCfg = Release|Win32 + {5DC9EE15-B080-4964-96CC-1BDC90BB873A}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {5DC9EE15-B080-4964-96CC-1BDC90BB873A}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {5DC9EE15-B080-4964-96CC-1BDC90BB873A}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {5DC9EE15-B080-4964-96CC-1BDC90BB873A}.Debug|Win32.ActiveCfg = Debug|Win32 + {5DC9EE15-B080-4964-96CC-1BDC90BB873A}.Debug|Win32.Build.0 = Debug|Win32 + {5DC9EE15-B080-4964-96CC-1BDC90BB873A}.Release|Any CPU.ActiveCfg = Release|Win32 + {5DC9EE15-B080-4964-96CC-1BDC90BB873A}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {5DC9EE15-B080-4964-96CC-1BDC90BB873A}.Release|Mixed Platforms.Build.0 = Release|Win32 + {5DC9EE15-B080-4964-96CC-1BDC90BB873A}.Release|Win32.ActiveCfg = Release|Win32 + {5DC9EE15-B080-4964-96CC-1BDC90BB873A}.Release|Win32.Build.0 = Release|Win32 + {70927F4C-0446-4EF3-9F65-FA78AC210506}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {70927F4C-0446-4EF3-9F65-FA78AC210506}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {70927F4C-0446-4EF3-9F65-FA78AC210506}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {70927F4C-0446-4EF3-9F65-FA78AC210506}.Debug|Win32.ActiveCfg = Debug|Win32 + {70927F4C-0446-4EF3-9F65-FA78AC210506}.Debug|Win32.Build.0 = Debug|Win32 + {70927F4C-0446-4EF3-9F65-FA78AC210506}.Release|Any CPU.ActiveCfg = Release|Win32 + {70927F4C-0446-4EF3-9F65-FA78AC210506}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {70927F4C-0446-4EF3-9F65-FA78AC210506}.Release|Mixed Platforms.Build.0 = Release|Win32 + {70927F4C-0446-4EF3-9F65-FA78AC210506}.Release|Win32.ActiveCfg = Release|Win32 + {70927F4C-0446-4EF3-9F65-FA78AC210506}.Release|Win32.Build.0 = Release|Win32 + {5612C360-309B-4E73-8512-6A1265590B90}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {5612C360-309B-4E73-8512-6A1265590B90}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {5612C360-309B-4E73-8512-6A1265590B90}.Debug|Win32.ActiveCfg = Debug|Win32 + {5612C360-309B-4E73-8512-6A1265590B90}.Release|Any CPU.ActiveCfg = Release|Win32 + {5612C360-309B-4E73-8512-6A1265590B90}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {5612C360-309B-4E73-8512-6A1265590B90}.Release|Win32.ActiveCfg = Release|Win32 + {B35929B8-374C-442B-A0F5-494AD69EDE1A}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {B35929B8-374C-442B-A0F5-494AD69EDE1A}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {B35929B8-374C-442B-A0F5-494AD69EDE1A}.Debug|Win32.ActiveCfg = Debug|Win32 + {B35929B8-374C-442B-A0F5-494AD69EDE1A}.Release|Any CPU.ActiveCfg = Release|Win32 + {B35929B8-374C-442B-A0F5-494AD69EDE1A}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {B35929B8-374C-442B-A0F5-494AD69EDE1A}.Release|Mixed Platforms.Build.0 = Release|Win32 + {B35929B8-374C-442B-A0F5-494AD69EDE1A}.Release|Win32.ActiveCfg = Release|Win32 + {3CC7AE6D-FEA3-4EB3-98FA-8AB038541411}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {3CC7AE6D-FEA3-4EB3-98FA-8AB038541411}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {3CC7AE6D-FEA3-4EB3-98FA-8AB038541411}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {3CC7AE6D-FEA3-4EB3-98FA-8AB038541411}.Debug|Win32.ActiveCfg = Debug|Win32 + {3CC7AE6D-FEA3-4EB3-98FA-8AB038541411}.Debug|Win32.Build.0 = Debug|Win32 + {3CC7AE6D-FEA3-4EB3-98FA-8AB038541411}.Release|Any CPU.ActiveCfg = Release|Win32 + {3CC7AE6D-FEA3-4EB3-98FA-8AB038541411}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {3CC7AE6D-FEA3-4EB3-98FA-8AB038541411}.Release|Mixed Platforms.Build.0 = Release|Win32 + {3CC7AE6D-FEA3-4EB3-98FA-8AB038541411}.Release|Win32.ActiveCfg = Release|Win32 + {3CC7AE6D-FEA3-4EB3-98FA-8AB038541411}.Release|Win32.Build.0 = Release|Win32 + {001DB089-3C83-4384-BB2B-1FE37D091B04}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {001DB089-3C83-4384-BB2B-1FE37D091B04}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {001DB089-3C83-4384-BB2B-1FE37D091B04}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {001DB089-3C83-4384-BB2B-1FE37D091B04}.Debug|Win32.ActiveCfg = Debug|Win32 + {001DB089-3C83-4384-BB2B-1FE37D091B04}.Debug|Win32.Build.0 = Debug|Win32 + {001DB089-3C83-4384-BB2B-1FE37D091B04}.Release|Any CPU.ActiveCfg = Release|Win32 + {001DB089-3C83-4384-BB2B-1FE37D091B04}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {001DB089-3C83-4384-BB2B-1FE37D091B04}.Release|Mixed Platforms.Build.0 = Release|Win32 + {001DB089-3C83-4384-BB2B-1FE37D091B04}.Release|Win32.ActiveCfg = Release|Win32 + {001DB089-3C83-4384-BB2B-1FE37D091B04}.Release|Win32.Build.0 = Release|Win32 + {FE3CD71B-7742-46DD-AAE9-3CCAA110B4DB}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {FE3CD71B-7742-46DD-AAE9-3CCAA110B4DB}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {FE3CD71B-7742-46DD-AAE9-3CCAA110B4DB}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {FE3CD71B-7742-46DD-AAE9-3CCAA110B4DB}.Debug|Win32.ActiveCfg = Debug|Win32 + {FE3CD71B-7742-46DD-AAE9-3CCAA110B4DB}.Debug|Win32.Build.0 = Debug|Win32 + {FE3CD71B-7742-46DD-AAE9-3CCAA110B4DB}.Release|Any CPU.ActiveCfg = Release|Win32 + {FE3CD71B-7742-46DD-AAE9-3CCAA110B4DB}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {FE3CD71B-7742-46DD-AAE9-3CCAA110B4DB}.Release|Mixed Platforms.Build.0 = Release|Win32 + {FE3CD71B-7742-46DD-AAE9-3CCAA110B4DB}.Release|Win32.ActiveCfg = Release|Win32 + {FE3CD71B-7742-46DD-AAE9-3CCAA110B4DB}.Release|Win32.Build.0 = Release|Win32 + {8B0F0A17-CEB4-42DF-B97E-375C60DB31E7}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {8B0F0A17-CEB4-42DF-B97E-375C60DB31E7}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {8B0F0A17-CEB4-42DF-B97E-375C60DB31E7}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {8B0F0A17-CEB4-42DF-B97E-375C60DB31E7}.Debug|Win32.ActiveCfg = Debug|Win32 + {8B0F0A17-CEB4-42DF-B97E-375C60DB31E7}.Debug|Win32.Build.0 = Debug|Win32 + {8B0F0A17-CEB4-42DF-B97E-375C60DB31E7}.Release|Any CPU.ActiveCfg = Release|Win32 + {8B0F0A17-CEB4-42DF-B97E-375C60DB31E7}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {8B0F0A17-CEB4-42DF-B97E-375C60DB31E7}.Release|Mixed Platforms.Build.0 = Release|Win32 + {8B0F0A17-CEB4-42DF-B97E-375C60DB31E7}.Release|Win32.ActiveCfg = Release|Win32 + {8B0F0A17-CEB4-42DF-B97E-375C60DB31E7}.Release|Win32.Build.0 = Release|Win32 + {44BF74D0-5462-4A3B-8768-61E0DEF81B7B}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {44BF74D0-5462-4A3B-8768-61E0DEF81B7B}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {44BF74D0-5462-4A3B-8768-61E0DEF81B7B}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {44BF74D0-5462-4A3B-8768-61E0DEF81B7B}.Debug|Win32.ActiveCfg = Debug|Win32 + {44BF74D0-5462-4A3B-8768-61E0DEF81B7B}.Debug|Win32.Build.0 = Debug|Win32 + {44BF74D0-5462-4A3B-8768-61E0DEF81B7B}.Release|Any CPU.ActiveCfg = Release|Win32 + {44BF74D0-5462-4A3B-8768-61E0DEF81B7B}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {44BF74D0-5462-4A3B-8768-61E0DEF81B7B}.Release|Mixed Platforms.Build.0 = Release|Win32 + {44BF74D0-5462-4A3B-8768-61E0DEF81B7B}.Release|Win32.ActiveCfg = Release|Win32 + {44BF74D0-5462-4A3B-8768-61E0DEF81B7B}.Release|Win32.Build.0 = Release|Win32 + {A8F1C09A-A244-441B-804C-635106305056}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {A8F1C09A-A244-441B-804C-635106305056}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {A8F1C09A-A244-441B-804C-635106305056}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {A8F1C09A-A244-441B-804C-635106305056}.Debug|Win32.ActiveCfg = Debug|Win32 + {A8F1C09A-A244-441B-804C-635106305056}.Debug|Win32.Build.0 = Debug|Win32 + {A8F1C09A-A244-441B-804C-635106305056}.Release|Any CPU.ActiveCfg = Release|Win32 + {A8F1C09A-A244-441B-804C-635106305056}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {A8F1C09A-A244-441B-804C-635106305056}.Release|Mixed Platforms.Build.0 = Release|Win32 + {A8F1C09A-A244-441B-804C-635106305056}.Release|Win32.ActiveCfg = Release|Win32 + {A8F1C09A-A244-441B-804C-635106305056}.Release|Win32.Build.0 = Release|Win32 + {249A514E-7212-485B-86D0-E0ACB8967A4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {249A514E-7212-485B-86D0-E0ACB8967A4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {249A514E-7212-485B-86D0-E0ACB8967A4C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {249A514E-7212-485B-86D0-E0ACB8967A4C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {249A514E-7212-485B-86D0-E0ACB8967A4C}.Debug|Win32.ActiveCfg = Debug|Any CPU + {249A514E-7212-485B-86D0-E0ACB8967A4C}.Debug|Win32.Build.0 = Debug|Any CPU + {249A514E-7212-485B-86D0-E0ACB8967A4C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {249A514E-7212-485B-86D0-E0ACB8967A4C}.Release|Any CPU.Build.0 = Release|Any CPU + {249A514E-7212-485B-86D0-E0ACB8967A4C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {249A514E-7212-485B-86D0-E0ACB8967A4C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {249A514E-7212-485B-86D0-E0ACB8967A4C}.Release|Win32.ActiveCfg = Release|Any CPU + {249A514E-7212-485B-86D0-E0ACB8967A4C}.Release|Win32.Build.0 = Release|Any CPU + {518F02B2-A542-44B3-A10D-CA8144268077}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {518F02B2-A542-44B3-A10D-CA8144268077}.Debug|Any CPU.Build.0 = Debug|Any CPU + {518F02B2-A542-44B3-A10D-CA8144268077}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {518F02B2-A542-44B3-A10D-CA8144268077}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {518F02B2-A542-44B3-A10D-CA8144268077}.Debug|Win32.ActiveCfg = Debug|Any CPU + {518F02B2-A542-44B3-A10D-CA8144268077}.Debug|Win32.Build.0 = Debug|Any CPU + {518F02B2-A542-44B3-A10D-CA8144268077}.Release|Any CPU.ActiveCfg = Release|Any CPU + {518F02B2-A542-44B3-A10D-CA8144268077}.Release|Any CPU.Build.0 = Release|Any CPU + {518F02B2-A542-44B3-A10D-CA8144268077}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {518F02B2-A542-44B3-A10D-CA8144268077}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {518F02B2-A542-44B3-A10D-CA8144268077}.Release|Win32.ActiveCfg = Release|Any CPU + {518F02B2-A542-44B3-A10D-CA8144268077}.Release|Win32.Build.0 = Release|Any CPU + {B38EE40E-078C-427E-82BB-AC517B720F97}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {B38EE40E-078C-427E-82BB-AC517B720F97}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {B38EE40E-078C-427E-82BB-AC517B720F97}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {B38EE40E-078C-427E-82BB-AC517B720F97}.Debug|Win32.ActiveCfg = Debug|Win32 + {B38EE40E-078C-427E-82BB-AC517B720F97}.Debug|Win32.Build.0 = Debug|Win32 + {B38EE40E-078C-427E-82BB-AC517B720F97}.Release|Any CPU.ActiveCfg = Release|Win32 + {B38EE40E-078C-427E-82BB-AC517B720F97}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {B38EE40E-078C-427E-82BB-AC517B720F97}.Release|Mixed Platforms.Build.0 = Release|Win32 + {B38EE40E-078C-427E-82BB-AC517B720F97}.Release|Win32.ActiveCfg = Release|Win32 + {B38EE40E-078C-427E-82BB-AC517B720F97}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/msvc/enchant.vcproj b/msvc/enchant.vcproj new file mode 100644 index 0000000..db22fc7 --- /dev/null +++ b/msvc/enchant.vcproj @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/libenchant.vcproj b/msvc/libenchant.vcproj new file mode 100644 index 0000000..07f950e --- /dev/null +++ b/msvc/libenchant.vcproj @@ -0,0 +1,247 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/libenchant_aspell.vcproj b/msvc/libenchant_aspell.vcproj new file mode 100644 index 0000000..84980a5 --- /dev/null +++ b/msvc/libenchant_aspell.vcproj @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/libenchant_hspell.vcproj b/msvc/libenchant_hspell.vcproj new file mode 100644 index 0000000..a5a611f --- /dev/null +++ b/msvc/libenchant_hspell.vcproj @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/libenchant_ispell.vcproj b/msvc/libenchant_ispell.vcproj new file mode 100644 index 0000000..a29c1fa --- /dev/null +++ b/msvc/libenchant_ispell.vcproj @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/libenchant_mock_provider.vcproj b/msvc/libenchant_mock_provider.vcproj new file mode 100644 index 0000000..4ef8a3e --- /dev/null +++ b/msvc/libenchant_mock_provider.vcproj @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/libenchant_myspell.vcproj b/msvc/libenchant_myspell.vcproj new file mode 100644 index 0000000..d727f29 --- /dev/null +++ b/msvc/libenchant_myspell.vcproj @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/libenchant_uspell.vcproj b/msvc/libenchant_uspell.vcproj new file mode 100644 index 0000000..215836f --- /dev/null +++ b/msvc/libenchant_uspell.vcproj @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/libenchant_voikko.vcproj b/msvc/libenchant_voikko.vcproj new file mode 100644 index 0000000..39413e6 --- /dev/null +++ b/msvc/libenchant_voikko.vcproj @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/libenchant_zemberek.vcproj b/msvc/libenchant_zemberek.vcproj new file mode 100644 index 0000000..7b583dd --- /dev/null +++ b/msvc/libenchant_zemberek.vcproj @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/test-enchant.vcproj b/msvc/test-enchant.vcproj new file mode 100644 index 0000000..d6dacbc --- /dev/null +++ b/msvc/test-enchant.vcproj @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/test-enchantxx.vcproj b/msvc/test-enchantxx.vcproj new file mode 100644 index 0000000..bfec3bb --- /dev/null +++ b/msvc/test-enchantxx.vcproj @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/unittest-enchant.vcproj b/msvc/unittest-enchant.vcproj new file mode 100644 index 0000000..74b392e --- /dev/null +++ b/msvc/unittest-enchant.vcproj @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/unittest-providers.vcproj b/msvc/unittest-providers.vcproj new file mode 100644 index 0000000..c78d7eb --- /dev/null +++ b/msvc/unittest-providers.vcproj @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packaging/enchant.spec b/packaging/enchant.spec new file mode 100644 index 0000000..6d9c4fb --- /dev/null +++ b/packaging/enchant.spec @@ -0,0 +1,105 @@ +%define name enchant +%define version 1.6.1 +%define release 4 + +Summary: An Enchanting Spell Checking Library + +Name: %{name} +Version: %{version} +Release: %{release} +Group: System Environment/Libraries +License: LGPL + +Source: ftp://ftp.gnome.org/pub/GNOME/unstable/sources/libenchant/%{name}-%{version}.tar.gz +Buildroot: /var/tmp/%{name}-%{version}-%{release}-root +URL: http://www.abisource.com/ + +Requires: glib2 >= 2.0.0 +BuildRequires: glib2-devel >= 2.0.0 + +%description +A library that wraps other spellchecking backends. + +%package devel +Summary: Support files necessary to compile applications with libenchant. +Group: Development/Libraries +Requires: enchant + +%description devel +Libraries, headers, and support files necessary to compile applications using libenchant. + +%prep + +%setup + +%build +%ifarch alpha + MYARCH_FLAGS="--host=alpha-redhat-linux" +%endif + +if [ ! -f configure ]; then +CFLAGS="$RPM_OPT_FLAGS" ./autogen.sh --prefix=%{_prefix} +fi +CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{_prefix} \ +%{?_without_ispell:--disable-ispell} \ +%{?_without_myspell:--disable-myspell} \ +%{?_without_aspell:--disable-aspell} \ +%{?_with_uspell:--enable-uspell} + +if [ "$SMP" != "" ]; then + (%__make "MAKE=%__make -k -j $SMP"; exit 0) + %__make +else +%__make +fi + +%install +if [ -d $RPM_BUILD_ROOT ]; then rm -r $RPM_BUILD_ROOT; fi +mkdir -p %{buildroot}/usr/share/license +cp LICENSE %{buildroot}/usr/share/license/%{name} +%__make DESTDIR=$RPM_BUILD_ROOT install +find $RPM_BUILD_ROOT/%{_libdir} -name \*.la -exec rm -f \{\} \; + +%files -n enchant +%manifest enchant.manifest +%defattr(644,root,root,755) +%doc AUTHORS COPYING.LIB README +%attr(755,root,root)%{_bindir}/* +%{_libdir}/lib*.so* +%{_prefix}/share/man/man1/enchant.1.gz +%{_prefix}/share/enchant/enchant.ordering +%{!?_without_ispell:%{_libdir}/enchant/*ispell.so*} +%{!?_without_myspell:%{_libdir}/enchant/*myspell.so*} +# commented by MR +#%{!?_without_aspell:%{_libdir}/enchant/*aspell.so*} +%{?_with_uspell:%{_libdir}/enchant/*uspell.so*} +/usr/share/license/%{name} + +%files devel +%defattr(644,root,root,755) +%{_libdir}/*.a +%{!?_without_ispell:%{_libdir}/enchant/*ispell.a} +%{!?_without_myspell:%{_libdir}/enchant/*myspell.a} +# commented by MR +#%{!?_without_aspell:%{_libdir}/enchant/*aspell.a} +%{?_with_uspell:%{_libdir}/enchant/*uspell.a} +%{_libdir}/pkgconfig/enchant.pc +%{_includedir}/enchant/* + +%clean +%__rm -rf $RPM_BUILD_ROOT + +%changelog +* Fri Sep 14 2012 Michal Roj +- this spec file updated in order to compile successfully with OBS for Tizen + +* Sun Aug 24 2003 Rui Miguel Seabra +- update spec to current stat of affairs +- building from source rpm is now aware of --with and --without flags: +- --without aspell --without ispell --without myspell --with uspell + +* Wed Jul 16 2003 Rui Miguel Seabra +- take advantage of environment rpm macros + +* Sun Jul 13 2003 Dom Lachowicz +- Initial version diff --git a/src/.cvsignore b/src/.cvsignore new file mode 100644 index 0000000..64f8351 --- /dev/null +++ b/src/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +enchant.lo +libenchant.la +.deps +.libs diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..7fa7b1a --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,37 @@ +SUBDIRS=. aspell ispell uspell myspell hspell applespell voikko zemberek + +INCLUDES=-I$(top_srcdir) $(ENCHANT_CFLAGS) $(CC_WARN_CFLAGS) -DENCHANT_GLOBAL_MODULE_DIR=\"$(libdir)/enchant\" -DENCHANT_GLOBAL_ORDERING=\"$(datadir)/enchant\" -D_ENCHANT_BUILD=1 -DENCHANT_VERSION_STRING=\"@ENCHANT_MAJOR_VERSION@.@ENCHANT_MINOR_VERSION@.@ENCHANT_MICRO_VERSION@\" + +lib_LTLIBRARIES = libenchant.la + +libenchant_la_LIBADD= $(ENCHANT_LIBS) @ENCHANT_WIN32_RESOURCE@ + +libenchant_la_LDFLAGS = -no-undefined -export-dynamic +if OS_WIN32 +libenchant_la_LDFLAGS += -avoid-version +else +libenchant_la_LDFLAGS += -version-info $(VERSION_INFO) +endif + +libenchant_la_DEPENDENCIES = @ENCHANT_WIN32_RESOURCE@ +libenchant_la_SOURCES = prefix.c enchant.c pwl.c enchant.h prefix.h pwl.h + +libenchant_includedir = $(includedir)/enchant +libenchant_include_HEADERS = enchant.h enchant-provider.h enchant++.h + +if WITH_APPLESPELL +INCLUDES += @APPLESPELL_CFLAGS@ +libenchant_la_LIBADD += @APPLESPELL_LIBS@ +libenchant_la_LDFLAGS += @APPLESPELL_LDFLAGS@ +libenchant_la_SOURCES += enchant_cocoa.m enchant_cocoa.h +libenchant_include_HEADERS += enchant_cocoa.h +endif + +EXTRA_DIST=enchant.i + +if OS_WIN32 + +@ENCHANT_WIN32_RESOURCE@ : libenchant.rc + $(top_srcdir)/lt-compile-resource libenchant.rc @ENCHANT_WIN32_RESOURCE@ +endif + diff --git a/src/applespell/AppleSpell.config b/src/applespell/AppleSpell.config new file mode 100644 index 0000000..92933b3 --- /dev/null +++ b/src/applespell/AppleSpell.config @@ -0,0 +1,13 @@ +de_DE de Deutsch +en_US en English +en_AU en_AU Australian English +en_CA en_CA Canadian English +en_GB en_GB British English +es_ES es Español +fr_FR fr Français +it_IT it Italiano +nl_NL nl Nederlands +pt_PT pt Português +pt_BR pt_BR Português do Brasil +sv_SE sv Svenska +hu_HU hu Magyar (MySpellX) diff --git a/src/applespell/Makefile.am b/src/applespell/Makefile.am new file mode 100644 index 0000000..e2e0c88 --- /dev/null +++ b/src/applespell/Makefile.am @@ -0,0 +1,4 @@ +EXTRA_DIST= \ + applespell_checker.h \ + applespell_checker.mm \ + AppleSpell.config diff --git a/src/applespell/applespell_checker.h b/src/applespell/applespell_checker.h new file mode 100644 index 0000000..87e09b6 --- /dev/null +++ b/src/applespell/applespell_checker.h @@ -0,0 +1,52 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2004 Remi Payette + * Copyright (C) 2004 Francis James Franklin + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02110-1301, USA. + */ + +#import + +#include "enchant-provider.h" + +ENCHANT_PLUGIN_DECLARE("AppleSpell") + +class AppleSpellChecker +{ + public: + AppleSpellChecker(); + + ~AppleSpellChecker(); + + void parseConfigFile (const char * configFile); + + bool checkWord (const char * word, size_t len, NSString * lang); + char ** suggestWord (const char * const word, size_t len, size_t * nsug, NSString * lang); + + NSString * requestDictionary (const char * const code); + private: + NSString * dictionaryForCode (NSString * code); + + NSSpellChecker * m_checker; + NSMutableDictionary * m_languages; +}; + +typedef struct +{ + AppleSpellChecker * AppleSpell; + NSString * DictionaryName; +} AppleSpellDictionary; diff --git a/src/applespell/applespell_checker.mm b/src/applespell/applespell_checker.mm new file mode 100644 index 0000000..eafe8cb --- /dev/null +++ b/src/applespell/applespell_checker.mm @@ -0,0 +1,450 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2004 Remi Payette + * Copyright (C) 2004 Francis James Franklin + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02110-1301, USA. + */ + +#include +#include + +#include "applespell_checker.h" + +AppleSpellChecker::AppleSpellChecker() +{ + // NSLog (@"AppleSpellChecker::AppleSpellChecker"); + + m_checker = [NSSpellChecker sharedSpellChecker]; + + m_languages = [[NSMutableDictionary alloc] initWithCapacity:16]; +} + +AppleSpellChecker::~AppleSpellChecker() +{ + // NSLog (@"AppleSpellChecker::~AppleSpellChecker"); + + if (m_languages) + { + [m_languages release]; + m_languages = 0; + } +} + +void AppleSpellChecker::parseConfigFile (const char * configFile) +{ + if (!m_languages || !configFile) + return; + + // NSLog (@"AppleSpellChecker::parseConfigFile: file=\"%s\"", configFile); + + if (FILE * in = fopen (configFile, "r")) + { + char line[1024]; + char code[1024]; + char name[1024]; + char lang[1024]; + + while (fgets (line, sizeof(line), in)) + if (sscanf (line, "%s %s %s", code, name, lang) == 3) + { + NSString * key = [[NSString alloc] initWithUTF8String:code]; + NSString * value = [[NSString alloc] initWithUTF8String:name]; + NSString * language = [[NSString alloc] initWithUTF8String:lang]; + + if (key && value) + { + // NSLog (@"AppleSpellChecker: %@ -> %@ (%@)", key, value, language); + [m_languages setObject:value forKey:key]; + } + + if (key) + [key release]; + if (value) + [value release]; + if (language) + [language release]; + } + fclose (in); + } +} + +NSString * AppleSpellChecker::dictionaryForCode (NSString * code) +{ + if (!m_languages) + return 0; + + return [m_languages objectForKey:code]; +} + +bool AppleSpellChecker::checkWord (const char * word, size_t len, NSString * lang) +{ + // NSLog(@"AppleSpellChecker::checkWord: lang=\"%@\"", lang); + + if (!m_checker || !lang) + return false; + + NSString * str = [[NSString alloc] initWithBytes:word length:len encoding:NSUTF8StringEncoding]; + if (!str) + return false; + + // NSLog (@"AppleSpellChecker::checkWord: word=\"%@\"", str); + + [m_checker setLanguage:lang]; + + NSRange result = [m_checker checkSpellingOfString:str startingAt:0]; + + [str release]; + + return (result.length ? true : false); +} + +char ** AppleSpellChecker::suggestWord (const char * const word, size_t len, size_t * nsug, NSString * lang) +{ + // NSLog (@"AppleSpellChecker::suggestWord: lang=\"%@\"", lang); + + if (!m_checker || !word || !len || !nsug || !lang) + return 0; + + *nsug = 0; + + [m_checker setLanguage:lang]; + + NSString * str = [[NSString alloc] initWithBytes:word length:len encoding:NSUTF8StringEncoding]; + if (!str) + return 0; + + // NSLog (@"AppleSpellChecker::suggestWord: word=\"%@\"", str); + + NSArray * result = [m_checker guessesForWord:str]; + + [str release]; + + char ** sug = 0; + + if (unsigned int count = [result count]) + { + sug = g_new0 (char *, static_cast(count) + 1); + if (sug) + { + for (unsigned int i = 0; i < count; i++) + { + str = [result objectAtIndex:i]; + + sug[*nsug] = g_strdup ([str UTF8String]); + + if (sug[*nsug]) + *nsug = *nsug + 1; + } + } + } + return sug; +} + +NSString * AppleSpellChecker::requestDictionary (const char * const code) +{ + // NSLog (@"AppleSpellChecker::requestDictionary: code=\"%s\"", code); + + if (!m_checker || !code) + return 0; + + NSString * dictionary = dictionaryForCode ([NSString stringWithUTF8String:code]); + if (dictionary) + { + NSString * language = [m_checker language]; + // NSLog (@"AppleSpellChecker::requestDictionary: ld language=\"%@\", new language=\"%@\"", language, dictionary); + if (![m_checker setLanguage:dictionary]) + { + // NSLog (@"AppleSpellChecker::requestDictionary: failed to set new language!"); + dictionary = 0; + } + if (language) + [m_checker setLanguage:language]; + } + return dictionary; +} + +/* + * Enchant + */ + +static char ** appleSpell_dict_suggest (EnchantDict * me, const char * const word, size_t len, size_t * out_n_suggs) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + // NSLog (@"appleSpell_dict_suggest word=\"%s\"", word); + + if (!me || !word || !len || !out_n_suggs) + { + if (pool) [pool release]; + return 0; + } + + char ** result = 0; + + if (AppleSpellDictionary * ASD = static_cast(me->user_data)) + { + result = ASD->AppleSpell->suggestWord (word, len, out_n_suggs, ASD->DictionaryName); + } + + if (pool) [pool release]; + return result; +} + +static int appleSpell_dict_check (EnchantDict * me, const char * const word, size_t len) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + // NSLog (@"appleSpell_dict_check"); + + if (!me || !word || !len) + { + if (pool) [pool release]; + return 0; + } + + int result = 0; + + if (AppleSpellDictionary * ASD = static_cast(me->user_data)) + { + result = ASD->AppleSpell->checkWord (word, len, ASD->DictionaryName); + } + + if (pool) [pool release]; + return result; +} + +static EnchantDict * appleSpell_provider_request_dict (EnchantProvider * me, const char * const tag) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + // NSLog (@"appleSpell_provider_request_dict"); + + if (!me || !tag) + { + if (pool) [pool release]; + return 0; + } + + AppleSpellChecker * checker = static_cast(me->user_data); + if (!checker) + { + if (pool) [pool release]; + return 0; + } + + EnchantDict * dict = g_new0 (EnchantDict, 1); + if (!dict) + { + if (pool) [pool release]; + return 0; + } + + dict->check = appleSpell_dict_check; + dict->suggest = appleSpell_dict_suggest; + + AppleSpellDictionary * ASD = g_new0 (AppleSpellDictionary, 1); + if (!ASD) + { + g_free (dict); + if (pool) [pool release]; + return 0; + } + + ASD->AppleSpell = checker; + ASD->DictionaryName = checker->requestDictionary (tag); + + if (ASD->DictionaryName) + { + [ASD->DictionaryName retain]; + // NSLog (@"appleSpell_provider_request_dict: providing dictionary \"%@\"", ASD->DictionaryName); + dict->user_data = (void *) ASD; + } + else + { + g_free (ASD); + g_free (dict); + dict = 0; + } + + if (pool) [pool release]; + return dict; +} + +static void appleSpell_provider_dispose_dict (EnchantProvider * me, EnchantDict * dict) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + // NSLog (@"appleSpell_provider_dispose_dict"); + + if (dict) + { + AppleSpellDictionary * ASD = static_cast(dict->user_data); + if (ASD) + { + [ASD->DictionaryName release]; + g_free (ASD); + } + g_free (dict); + } + if (pool) [pool release]; +} + +static int appleSpell_provider_dictionary_exists (EnchantProvider * me, const char * const tag) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + // NSLog (@"appleSpell_provider_dictionary_exists: tag=\"%s\"", tag); + + int result = 0; + + AppleSpellChecker * checker = static_cast(me->user_data); + if (checker) + result = (checker->requestDictionary (tag) ? 1 : 0); + + if (pool) [pool release]; + return result; +} + +static void appleSpell_provider_dispose (EnchantProvider * me) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + // NSLog (@"appleSpell_provider_dispose"); + + if (me) + { + AppleSpellChecker * checker = static_cast(me->user_data); + if (checker) + delete checker; + + g_free (me); + } + if (pool) [pool release]; +} + +static const char * appleSpell_provider_identify (EnchantProvider * me) +{ + return "AppleSpell"; +} + +static const char * appleSpell_provider_describe (EnchantProvider * me) +{ + return "AppleSpell Provider"; +} + +static void appleSpell_provider_free_string_list (EnchantProvider * me, char **str_list) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + // NSLog (@"appleSpell_provider_free_suggestions"); + + if (str_list) + g_strfreev (str_list); + + if (pool) [pool release]; +} + +extern "C" { + ENCHANT_MODULE_EXPORT (EnchantProvider *) + init_enchant_provider (void) + { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + // NSLog (@"init_enchant_provider"); + + EnchantProvider * provider = g_new0 (EnchantProvider, 1); + if (!provider) + { + if (pool) [pool release]; + return 0; + } + + provider->dispose = appleSpell_provider_dispose; + provider->request_dict = appleSpell_provider_request_dict; + provider->dispose_dict = appleSpell_provider_dispose_dict; + provider->dictionary_exists = appleSpell_provider_dictionary_exists; + provider->identify = appleSpell_provider_identify; + provider->describe = appleSpell_provider_describe; + provider->free_string_list = appleSpell_provider_free_string_list; + + AppleSpellChecker * checker = 0; + try + { + checker = new AppleSpellChecker; + } + catch (...) + { + checker = 0; + } + if (checker) + { + provider->user_data = (void *) checker; + } + else + { + g_free (provider); + provider = 0; + } + + if (pool) [pool release]; + return provider; + } + + static bool s_bReloadSelf = true; + + ENCHANT_MODULE_EXPORT (void) + configure_enchant_provider (EnchantProvider * me, const char * module_dir) + { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + // NSLog (@"configure_enchant_provider"); + + if (!me || !module_dir) + { + if (pool) [pool release]; + return; + } + + AppleSpellChecker * checker = static_cast(me->user_data); + if (checker) + { + if (s_bReloadSelf) + if (gchar * moduleFile = g_build_filename (module_dir, "AppleSpell.so", NULL)) + { + /* ugly hack :-( + * Darwin linker doesn't like unloading Obj-C modules; + * reload once to suppress unloading... + */ + g_module_open (moduleFile, (GModuleFlags) 0); + + g_free (moduleFile); + + s_bReloadSelf = false; + } + + if (gchar * configFile = g_build_filename (module_dir, "AppleSpell.config", NULL)) + { + checker->parseConfigFile (configFile); + + g_free (configFile); + } + } + + if (pool) [pool release]; + return; + } +} diff --git a/src/aspell/.cvsignore b/src/aspell/.cvsignore new file mode 100644 index 0000000..a8c990f --- /dev/null +++ b/src/aspell/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +aspell_provider.lo +libenchant_aspell.la +.deps +.libs diff --git a/src/aspell/Makefile.am b/src/aspell/Makefile.am new file mode 100644 index 0000000..4b5c1a1 --- /dev/null +++ b/src/aspell/Makefile.am @@ -0,0 +1,15 @@ +if WITH_ASPELL +target_lib = libenchant_aspell.la +else +target_lib = +endif + +INCLUDES=-I$(top_srcdir)/src $(ENCHANT_CFLAGS) $(CC_WARN_CFLAGS) @ASPELL_INC@ -D_ENCHANT_BUILD=1 @ASPELL_CFLAGS@ + +aspell_LTLIBRARIES = $(target_lib) +aspelldir= $(libdir)/enchant + +libenchant_aspell_la_LIBADD= $(ENCHANT_LIBS) $(ASPELL_LIBS) $(top_builddir)/src/libenchant.la +libenchant_aspell_la_LDFLAGS = -module -avoid-version -no-undefined +libenchant_aspell_la_SOURCES = aspell_provider.c +libenchant_aspell_lalibdir=$(libdir)/enchant diff --git a/src/aspell/README b/src/aspell/README new file mode 100644 index 0000000..fb786e0 --- /dev/null +++ b/src/aspell/README @@ -0,0 +1,4 @@ +This is the Aspell/Pspell Enchant Backend. + +Aspell/Pspell are by Kevin Atkinson. You can find out more information +about Aspell/Pspell at http://aspell.net/ diff --git a/src/aspell/aspell_provider.c b/src/aspell/aspell_provider.c new file mode 100644 index 0000000..79a221b --- /dev/null +++ b/src/aspell/aspell_provider.c @@ -0,0 +1,473 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003,2004 Dom Lachowicz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#include +#include +#include + +#include + +#ifdef HAVE_PSPELL_H +#include +#else +#include + +/* to allow this to build on places that lack pspell.h */ + +#define PspellManager AspellSpeller +#define PspellWordList AspellWordList +#define PspellStringEmulation AspellStringEnumeration +#define PspellCanHaveError AspellCanHaveError +#define PspellConfig AspellConfig + +#define new_pspell_config new_aspell_config +#define delete_pspell_config delete_aspell_config +#define pspell_config_replace aspell_config_replace + +#define new_pspell_manager new_aspell_speller +#define to_pspell_manager to_aspell_speller +#define delete_pspell_manager delete_aspell_speller +#define pspell_manager_error_number aspell_speller_error_number +#define pspell_manager_error_message aspell_speller_error_message +#define pspell_manager_save_all_word_lists aspell_speller_save_all_word_lists +#define pspell_manager_check aspell_speller_check +#define pspell_manager_add_to_personal aspell_speller_add_to_personal +#define pspell_manager_add_to_session aspell_speller_add_to_session +#define pspell_manager_suggest aspell_speller_suggest +#define pspell_manager_store_replacement aspell_speller_store_replacement + +#define pspell_word_list_elements aspell_word_list_elements +#define pspell_word_list_size aspell_word_list_size + +#define pspell_string_emulation_next aspell_string_enumeration_next +#define delete_pspell_string_emulation delete_aspell_string_enumeration + +#define pspell_error_message aspell_error_message +#define pspell_error_number aspell_error_number + +#endif + +#include "enchant.h" +#include "enchant-provider.h" + +ENCHANT_PLUGIN_DECLARE("Aspell") + +#ifdef __cplusplus +extern "C" { +#endif + +ENCHANT_MODULE_EXPORT(void) + configure_enchant_provider(EnchantProvider * me, const char *dir_name); + +ENCHANT_MODULE_EXPORT (EnchantProvider *) + init_enchant_provider (void); + +#ifdef __cplusplus +} +#endif + +static int +aspell_dict_check (EnchantDict * me, const char *const word, size_t len) +{ + PspellManager *manager; + int val; + char *normalizedWord; + + manager = (PspellManager *) me->user_data; + + normalizedWord = g_utf8_normalize (word, len, G_NORMALIZE_NFC); + val = pspell_manager_check (manager, normalizedWord, strlen(normalizedWord)); + g_free(normalizedWord); + + if (val == 0) + return 1; + else if (val > 0) + return 0; + else { + enchant_dict_set_error (me, pspell_manager_error_message (manager)); + return -1; + } +} + +static char ** +aspell_dict_suggest (EnchantDict * me, const char *const word, + size_t len, size_t * out_n_suggs) +{ + PspellManager *manager; + + const PspellWordList *word_list; + PspellStringEmulation *suggestions; + char *normalizedWord; + + char **sugg_arr = NULL; + size_t n_suggestions, i; + const char *sugg; + + manager = (PspellManager *) me->user_data; + + normalizedWord = g_utf8_normalize (word, len, G_NORMALIZE_NFC); + word_list = pspell_manager_suggest (manager, normalizedWord, strlen(normalizedWord)); + g_free(normalizedWord); + + if (word_list) + { + suggestions = pspell_word_list_elements (word_list); + if (suggestions) + { + n_suggestions = pspell_word_list_size (word_list); + *out_n_suggs = n_suggestions; + + if (n_suggestions) + { + sugg_arr = g_new0 (char *, n_suggestions + 1); + + for (i = 0; i < n_suggestions; i++) + { + sugg = pspell_string_emulation_next (suggestions); + if (sugg) + sugg_arr[i] = g_strdup (sugg); + } + } + delete_pspell_string_emulation (suggestions); + } + } + + return sugg_arr; +} + +static void +aspell_dict_add_to_personal (EnchantDict * me, + const char *const word, size_t len) +{ + PspellManager *manager; + + manager = (PspellManager *) me->user_data; + pspell_manager_add_to_personal (manager, word, len); + pspell_manager_save_all_word_lists (manager); +} + +static void +aspell_dict_add_to_session (EnchantDict * me, + const char *const word, size_t len) +{ + PspellManager *manager; + + manager = (PspellManager *) me->user_data; + pspell_manager_add_to_session (manager, word, len); +} + +static void +aspell_dict_store_replacement (EnchantDict * me, + const char *const mis, size_t mis_len, + const char *const cor, size_t cor_len) +{ + PspellManager *manager; + + manager = (PspellManager *) me->user_data; + pspell_manager_store_replacement (manager, mis, mis_len, + cor, cor_len); + pspell_manager_save_all_word_lists (manager); +} + +static EnchantDict * +aspell_provider_request_dict (EnchantProvider * me, const char *const tag) +{ + EnchantDict *dict; + PspellManager *manager; + PspellConfig *spell_config; + PspellCanHaveError *spell_error; + + spell_config = new_pspell_config (); + pspell_config_replace (spell_config, "language-tag", tag); + pspell_config_replace (spell_config, "encoding", "utf-8"); + + spell_error = new_pspell_manager (spell_config); + delete_pspell_config (spell_config); + + if (pspell_error_number (spell_error) != 0) + { + return NULL; + } + + manager = to_pspell_manager (spell_error); + + dict = g_new0 (EnchantDict, 1); + dict->user_data = (void *) manager; + dict->check = aspell_dict_check; + dict->suggest = aspell_dict_suggest; + dict->add_to_personal = aspell_dict_add_to_personal; + dict->add_to_session = aspell_dict_add_to_session; + dict->store_replacement = aspell_dict_store_replacement; + + return dict; +} + +static void +aspell_provider_dispose_dict (EnchantProvider * me, EnchantDict * dict) +{ + PspellManager *manager; + + manager = (PspellManager *) dict->user_data; + delete_pspell_manager (manager); + + g_free (dict); +} + +#if ASPELL_0_50_0 +static char ** +aspell_provider_list_dicts (EnchantProvider * me, + size_t * out_n_dicts) +{ + PspellConfig * spell_config; + AspellDictInfoList * dlist; + AspellDictInfoEnumeration * dels; + const AspellDictInfo * entry; + char ** out_list = NULL; + + spell_config = new_pspell_config (); + + dlist = get_aspell_dict_info_list (spell_config); + + *out_n_dicts = 0; + dels = aspell_dict_info_list_elements (dlist); + + /* TODO: Use aspell_dict_info_list_size() once it is implemented and returns non-zero. */ + while ( (entry = aspell_dict_info_enumeration_next(dels)) != 0) + (*out_n_dicts)++; + + if (*out_n_dicts) { + size_t i; + + out_list = g_new0 (char *, *out_n_dicts + 1); + dels = aspell_dict_info_list_elements (dlist); + + for (i = 0; i < *out_n_dicts; i++) { + entry = aspell_dict_info_enumeration_next (dels); + /* XXX: should this be entry->code or entry->name ? */ + out_list[i] = g_strdup (entry->code); + } + + delete_aspell_dict_info_enumeration (dels); + } + + delete_pspell_config (spell_config); + + return out_list; +} +#endif + +static void +aspell_provider_free_string_list (EnchantProvider * me, char **str_list) +{ + g_strfreev (str_list); +} + +static void +aspell_provider_dispose (EnchantProvider * me) +{ + g_free (me); +} + +static const char * +aspell_provider_identify (EnchantProvider * me) +{ + return "aspell"; +} + +static const char * +aspell_provider_describe (EnchantProvider * me) +{ + return "Aspell Provider"; +} + +#ifdef __cplusplus +extern "C" { +#endif + +EnchantProvider * +init_enchant_provider (void) +{ + EnchantProvider *provider; + + provider = g_new0 (EnchantProvider, 1); + provider->dispose = aspell_provider_dispose; + provider->request_dict = aspell_provider_request_dict; + provider->dispose_dict = aspell_provider_dispose_dict; + provider->identify = aspell_provider_identify; + provider->describe = aspell_provider_describe; + +#if ASPELL_0_50_0 + provider->list_dicts = aspell_provider_list_dicts; +#else +# ifdef __GNUC__ +# warning "You're using an ancient version of Aspell. Some things won't work properly." +# endif +#endif + provider->free_string_list = aspell_provider_free_string_list; + + return provider; +} + + +#if defined(_WIN32) + +static WCHAR* GetDirectoryOfThisLibrary(void) +{ + WCHAR dll_path[MAX_PATH]; + gchar* utf8_dll_path; + gchar* utf8_prefix; + gunichar2* utf16_prefix; + + if(!GetModuleFileNameW(s_hModule,dll_path,MAX_PATH)) + { /* unable to determine filename of this library */ + return NULL; + } + utf8_dll_path = g_utf16_to_utf8 (dll_path, -1, NULL, NULL, NULL); + utf8_prefix = g_path_get_dirname(utf8_dll_path); + g_free(utf8_dll_path); + + utf16_prefix = g_utf8_to_utf16 (utf8_prefix, -1, NULL, NULL, NULL); + g_free(utf8_prefix); + + return utf16_prefix; +} + +static HMODULE LoadLibraryFromPath(const WCHAR* path, const WCHAR* libraryName) +{ + HMODULE h; + WCHAR* wszFullLibraryPath; + size_t fullLibraryPathLen; + + fullLibraryPathLen = wcslen(path) + 1 /* '\\' */+ wcslen(libraryName); + wszFullLibraryPath = g_new0(WCHAR, fullLibraryPathLen + 1); + + wcscpy(wszFullLibraryPath, path); + wcscat(wszFullLibraryPath, L"\\"); + wcscat(wszFullLibraryPath, libraryName); + + h = LoadLibraryW(wszFullLibraryPath); + + g_free(wszFullLibraryPath); + return h; +} + +static WCHAR* GetRegistryValue(HKEY baseKey, const WCHAR * uKeyName, const WCHAR * uKey) +{ + HKEY hKey; + unsigned long lType; + DWORD dwSize; + WCHAR* wszValue = NULL; + + if(RegOpenKeyExW(baseKey, uKeyName, 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + /* Determine size of string */ + if(RegQueryValueExW( hKey, uKey, NULL, &lType, NULL, &dwSize) == ERROR_SUCCESS) + { + wszValue = g_new0(WCHAR, dwSize + 1); + RegQueryValueExW(hKey, uKey, NULL, &lType, (LPBYTE) wszValue, &dwSize); + } + } + + return wszValue; +} +#endif + +#if defined(_WIN32) +gboolean load_library(EnchantProvider * me, const char *dir_name, const WCHAR *aspell_module_name) +{ + HMODULE aspell_module = NULL; + char* szModule; + + /* first try load from registry path */ + szModule = enchant_get_registry_value ("Aspell", "Module"); + if(szModule) + { + WCHAR* wszModule; + + wszModule = g_utf8_to_utf16 (szModule, -1, NULL, NULL, NULL); + aspell_module = LoadLibraryW(wszModule); + g_free(wszModule); + } + + if (aspell_module == NULL) + { + /* next try load from aspell registry path */ + WCHAR* wszDirectory = GetRegistryValue (HKEY_LOCAL_MACHINE, L"Software\\Aspell", L"Path"); + if(wszDirectory) + { + aspell_module = LoadLibraryFromPath(wszDirectory, aspell_module_name); + g_free(wszDirectory); + } + } + + if (aspell_module == NULL) + { + /* then try from same directory as provider */ + WCHAR* wszDirectory = GetDirectoryOfThisLibrary(); + if(wszDirectory) + { + aspell_module = LoadLibraryFromPath(wszDirectory, aspell_module_name); + g_free(wszDirectory); + } + } + + if (aspell_module == NULL) + { + /* then try default lookup */ + aspell_module = LoadLibraryW(aspell_module_name); + } + + if (aspell_module == NULL) + { + return FALSE; + } + return TRUE; +} +#endif + +void configure_enchant_provider(EnchantProvider * me, const char *dir_name) +{ +#if defined(_WIN32) + if(!load_library(me, dir_name, L"aspell-15.dll") && + !load_library(me, dir_name, L"libaspell-15.dll")) + { + /* we can't seem to load aspell. Avoid late binding problems later */ + g_warning("Unable to load library aspell-15.dll."); + me->request_dict = NULL; + me->dispose_dict = NULL; + me->list_dicts = NULL; + } +#endif +} + + +#ifdef __cplusplus +} +#endif diff --git a/src/bindings/Enchant.Net/App.config b/src/bindings/Enchant.Net/App.config new file mode 100644 index 0000000..4fd10ad --- /dev/null +++ b/src/bindings/Enchant.Net/App.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/bindings/Enchant.Net/Bindings.cs b/src/bindings/Enchant.Net/Bindings.cs new file mode 100644 index 0000000..acc150b --- /dev/null +++ b/src/bindings/Enchant.Net/Bindings.cs @@ -0,0 +1,374 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Enchant +{ + internal static class Bindings + { + #region Delegates + + public delegate void EnchantBrokerDescribeDelegate(ProviderInfo provider_info); + + public delegate void EnchantDictDescribeDelegate(DictionaryInfo dictionary_info); + + #endregion + + private const string ENCHANT_LIBRARY = "libenchant.dll"; + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + public static extern SafeBrokerHandle enchant_broker_init(); + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + public static extern void enchant_broker_free(IntPtr broker); + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr enchant_broker_request_dict(SafeBrokerHandle broker, IntPtr tag); + + public static SafeDictionaryHandle enchant_broker_request_dict(SafeBrokerHandle broker, + string tag) + { + using (Utf8Marshaller utf8Tag = new Utf8Marshaller(tag)) + { + IntPtr handle = enchant_broker_request_dict(broker, utf8Tag.MarshalledValue); + return new SafeDictionaryHandle(broker, handle); + } + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr enchant_broker_request_pwl_dict( + SafeBrokerHandle broker, IntPtr pwl); + + public static SafeDictionaryHandle enchant_broker_request_pwl_dict(SafeBrokerHandle broker, + string pwl) + { + using (Utf8Marshaller utf8Pwl = new Utf8Marshaller(pwl)) + { + IntPtr handle = enchant_broker_request_pwl_dict(broker, utf8Pwl.MarshalledValue); + return new SafeDictionaryHandle(broker, handle); + } + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + public static extern void enchant_broker_free_dict(SafeBrokerHandle broker, IntPtr dict); + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern int enchant_broker_dict_exists(SafeBrokerHandle broker, IntPtr tag); + + public static int enchant_broker_dict_exists(SafeBrokerHandle broker, string tag) + { + using (Utf8Marshaller utf8Tag = new Utf8Marshaller(tag)) + { + return enchant_broker_dict_exists(broker, utf8Tag.MarshalledValue); + } + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern void enchant_broker_set_ordering(SafeBrokerHandle broker, + IntPtr tag, + IntPtr ordering); + + public static void enchant_broker_set_ordering(SafeBrokerHandle broker, + string tag, + string ordering) + { + using ( + Utf8Marshaller utf8Tag = new Utf8Marshaller(tag), + utf8Ordering = new Utf8Marshaller(ordering)) + { + enchant_broker_set_ordering(broker, + utf8Tag.MarshalledValue, + utf8Ordering.MarshalledValue); + } + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + public static extern string enchant_broker_get_error(SafeBrokerHandle broker); + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern void enchant_broker_describe(SafeBrokerHandle broker, + [MarshalAs(UnmanagedType.FunctionPtr)] EnchantBrokerDescribeFn + enchantBrokerDescribeFn, + IntPtr user_data); + + public static void enchant_broker_describe(SafeBrokerHandle broker, + EnchantBrokerDescribeDelegate describe) + { + enchant_broker_describe(broker, + delegate(IntPtr provider_name, + IntPtr provider_desc, + IntPtr provider_dll_file, + IntPtr user_data) + { + ProviderInfo provider = + new ProviderInfo( + Utf8Marshaller.MarshalFromUtf8(provider_name), + Utf8Marshaller.MarshalFromUtf8(provider_desc), + Utf8Marshaller.MarshalFromUtf8( + provider_dll_file)); + describe(provider); + }, + IntPtr.Zero); + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern void enchant_broker_list_dicts(SafeBrokerHandle broker, + [MarshalAs(UnmanagedType.FunctionPtr)] EnchantDictDescribeFn + enchantDictDescribeFn, + IntPtr user_data); + + public static void enchant_broker_list_dicts(SafeBrokerHandle broker, + EnchantDictDescribeDelegate describe) + { + enchant_broker_list_dicts(broker, + delegate(IntPtr lang_tag, + IntPtr provider_name, + IntPtr provider_desc, + IntPtr provider_dll_file, + IntPtr user_data) + { + ProviderInfo provider = + new ProviderInfo( + Utf8Marshaller.MarshalFromUtf8(provider_name), + Utf8Marshaller.MarshalFromUtf8(provider_desc), + Utf8Marshaller.MarshalFromUtf8(provider_dll_file)); + DictionaryInfo dictionary = + new DictionaryInfo( + Utf8Marshaller.MarshalFromUtf8(lang_tag), + provider); + describe(dictionary); + }, + IntPtr.Zero); + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern void enchant_dict_describe(SafeDictionaryHandle dict, + [MarshalAs(UnmanagedType.FunctionPtr)] EnchantDictDescribeFn + enchantDictDescribeFn, + IntPtr user_data); + + public static void enchant_dict_describe(SafeDictionaryHandle dict, + EnchantDictDescribeDelegate describe) + { + enchant_dict_describe(dict, + delegate(IntPtr lang_tag, + IntPtr provider_name, + IntPtr provider_desc, + IntPtr provider_dll_file, + IntPtr user_data) + { + ProviderInfo provider = + new ProviderInfo( + Utf8Marshaller.MarshalFromUtf8(provider_name), + Utf8Marshaller.MarshalFromUtf8(provider_desc), + Utf8Marshaller.MarshalFromUtf8(provider_dll_file)); + DictionaryInfo dictionary = + new DictionaryInfo( + Utf8Marshaller.MarshalFromUtf8(lang_tag), + provider); + describe(dictionary); + }, + IntPtr.Zero); + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern int enchant_dict_check(SafeDictionaryHandle dict, IntPtr word, int len); + + public static int enchant_dict_check(SafeDictionaryHandle dict, string word) + { + using (Utf8Marshaller utf8Word = new Utf8Marshaller(word)) + { + return enchant_dict_check(dict, utf8Word.MarshalledValue, utf8Word.MarshalledSize); + } + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr enchant_dict_suggest(SafeDictionaryHandle dict, + IntPtr word, + int len, + [MarshalAs(UnmanagedType.U4)] out int + out_n_suggs); + + public static List enchant_dict_suggest(SafeDictionaryHandle dict, string word) + { + int suggsCount; + IntPtr suggs; + using (Utf8Marshaller utf8Word = new Utf8Marshaller(word)) + { + suggs = + enchant_dict_suggest(dict, + utf8Word.MarshalledValue, + utf8Word.MarshalledSize, + out suggsCount); + } + List result = Utf8Marshaller.MarshalFromUtf8Array(suggs, suggsCount); + enchant_dict_free_string_list(dict, suggs); + return result; + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern void enchant_dict_free_string_list(SafeDictionaryHandle dict, + IntPtr string_list); + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern void enchant_dict_add(SafeDictionaryHandle dict, IntPtr word, int len); + + public static void enchant_dict_add(SafeDictionaryHandle dict, string word) + { + using (Utf8Marshaller utf8Word = new Utf8Marshaller(word)) + { + enchant_dict_add(dict, utf8Word.MarshalledValue, utf8Word.MarshalledSize); + } + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern void enchant_dict_add_to_session(SafeDictionaryHandle dict, + IntPtr word, + int len); + + public static void enchant_dict_add_to_session(SafeDictionaryHandle dict, string word) + { + using (Utf8Marshaller utf8Word = new Utf8Marshaller(word)) + { + enchant_dict_add_to_session(dict, utf8Word.MarshalledValue, utf8Word.MarshalledSize); + } + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern void enchant_dict_remove(SafeDictionaryHandle dict, + IntPtr word, + int len); + + public static void enchant_dict_remove(SafeDictionaryHandle dict, string word) + { + using (Utf8Marshaller utf8Word = new Utf8Marshaller(word)) + { + enchant_dict_remove(dict, utf8Word.MarshalledValue, utf8Word.MarshalledSize); + } + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern void enchant_dict_remove_from_session(SafeDictionaryHandle dict, + IntPtr word, + int len); + + public static void enchant_dict_remove_from_session(SafeDictionaryHandle dict, string word) + { + using (Utf8Marshaller utf8Word = new Utf8Marshaller(word)) + { + enchant_dict_remove_from_session(dict, + utf8Word.MarshalledValue, + utf8Word.MarshalledSize); + } + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern int enchant_dict_is_added(SafeDictionaryHandle dict, + IntPtr word, + int len); + + public static int enchant_dict_is_added(SafeDictionaryHandle dict, string word) + { + using (Utf8Marshaller utf8Word = new Utf8Marshaller(word)) + { + return + enchant_dict_is_added(dict, + utf8Word.MarshalledValue, + utf8Word.MarshalledSize); + } + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern int enchant_dict_is_removed(SafeDictionaryHandle dict, + IntPtr word, + int len); + + public static int enchant_dict_is_removed(SafeDictionaryHandle dict, string word) + { + using (Utf8Marshaller utf8Word = new Utf8Marshaller(word)) + { + return + enchant_dict_is_removed(dict, + utf8Word.MarshalledValue, + utf8Word.MarshalledSize); + } + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern void enchant_dict_store_replacement(SafeDictionaryHandle dict, + IntPtr mis, + int mis_len, + IntPtr cor, + int cor_len); + + public static void enchant_dict_store_replacement(SafeDictionaryHandle dict, + string mis, + string cor) + { + using ( + Utf8Marshaller utf8Mis = new Utf8Marshaller(mis), + utf8Cor = new Utf8Marshaller(cor)) + { + enchant_dict_store_replacement(dict, + utf8Mis.MarshalledValue, + utf8Mis.MarshalledSize, + utf8Cor.MarshalledValue, + utf8Cor.MarshalledSize); + } + } + + [DllImport(ENCHANT_LIBRARY, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "enchant_dict_get_error")] + private static extern IntPtr enchant_dict_get_error_(SafeDictionaryHandle dict); + + public static string enchant_dict_get_error(SafeDictionaryHandle dict) + { + IntPtr message = enchant_dict_get_error_(dict); + return Utf8Marshaller.MarshalFromUtf8(message); + } + + #region Nested type: EnchantBrokerDescribeFn + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void EnchantBrokerDescribeFn( + IntPtr provider_name, + IntPtr provider_desc, + IntPtr provider_dll_file, + IntPtr user_data); + + #endregion + + #region Nested type: EnchantDictDescribeFn + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void EnchantDictDescribeFn( + IntPtr lang_tag, + IntPtr provider_name, + IntPtr provider_desc, + IntPtr provider_file, + IntPtr user_data); + + #endregion + } +} \ No newline at end of file diff --git a/src/bindings/Enchant.Net/Broker.cs b/src/bindings/Enchant.Net/Broker.cs new file mode 100644 index 0000000..c944777 --- /dev/null +++ b/src/bindings/Enchant.Net/Broker.cs @@ -0,0 +1,361 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +using System; +using System.Collections.Generic; + +namespace Enchant +{ + public sealed class Broker : IDisposable + { + private readonly SafeBrokerHandle _handle; + private IList _dictionaries; + private bool _disposed = false; + private IList _providers; + private readonly Dictionary _dictionaryCache; + private readonly Dictionary _pwlDictionaryCache; + private bool _cacheDictionaries = true; + private static Nullable _isLibEnchantAvailable; + + private static Broker _defaultBroker; + /// + /// Singleton for apps that want to use that pattern + /// + public static Broker Default + { + get + { + if(_defaultBroker == null || _defaultBroker._disposed) + { + _defaultBroker = new Broker(); + } + return _defaultBroker; + } + } + + public static bool IsLibEnchantAvailable + { + get + { + if (_isLibEnchantAvailable == null) + { + // force a broker to be created and thus trying to + // us libenchant and setting our status + Broker broker = Default; + } + return _isLibEnchantAvailable??false; + } + } + + + public Broker() + { + try + { + _handle = Bindings.enchant_broker_init(); + _isLibEnchantAvailable = true; + } + catch (DllNotFoundException) + { + _isLibEnchantAvailable = false; + } + if (_isLibEnchantAvailable == true) + { + VerifyNoErrors(); + if (_handle.IsInvalid) + { + throw new ApplicationException("Unable to initialize broker"); + } + } + _dictionaryCache = new Dictionary(); + _pwlDictionaryCache = new Dictionary(); + } + + + public IEnumerable Providers + { + get + { + VerifyNotDisposed(); + if (_providers == null) + { + InitializeProviderList(); + } + return _providers; + } + } + + private void InitializeProviderList() + { + _providers = new List(); + if (_isLibEnchantAvailable == true) + { + Bindings.enchant_broker_describe(_handle, + delegate(ProviderInfo provider) + { _providers.Add(provider); }); + VerifyNoErrors(); + } + } + + public IEnumerable Dictionaries + { + get + { + VerifyNotDisposed(); + if (_dictionaries == null) + { + InitializeDictionaryList(); + } + return _dictionaries; + } + } + + public bool CacheDictionaries + { + get { return this._cacheDictionaries; } + set { this._cacheDictionaries = value; } + } + + private void InitializeDictionaryList() + { + _dictionaries = new List(); + if (_isLibEnchantAvailable == true) + { + Bindings.enchant_broker_list_dicts(_handle, + delegate(DictionaryInfo dictionary) + { _dictionaries.Add(dictionary); }); + VerifyNoErrors(); + } + } + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + // dispose-only, i.e. non-finalizable logic + if(_handle != null) + { + _handle.Dispose(); + } + DisposeAllDictionariesFromCache(_dictionaryCache); + DisposeAllDictionariesFromCache(_pwlDictionaryCache); + } + + // shared (dispose and finalizable) cleanup logic + _disposed = true; + } + } + + private static void DisposeAllDictionariesFromCache(ICollection> cache) { + List dictionariesToDispose = new List(); + foreach (KeyValuePair pair in cache) + { + if(pair.Value.IsAlive) + { + dictionariesToDispose.Add((Dictionary) pair.Value.Target); + } + } + cache.Clear(); + foreach (Dictionary dictionary in dictionariesToDispose) + { + dictionary.Dispose(); + } + } + + private void VerifyNotDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException("Dictionary"); + } + } + + public Dictionary RequestDictionary(string language_tag) + { + VerifyNotDisposed(); + if (language_tag == null) + { + throw new ArgumentNullException("language_tag"); + } + if(_isLibEnchantAvailable != true) + { + throw new InvalidOperationException("LibEnchant is not available"); + } + + Dictionary dictionary = GetDictionaryFromCache(this._dictionaryCache, language_tag); + if(dictionary != null) + { + return dictionary; + } + return CreateDictionary(language_tag); + } + + private Dictionary CreateDictionary(string language_tag) { + SafeDictionaryHandle handle = Bindings.enchant_broker_request_dict(this._handle, language_tag); + VerifyNoErrors(); + if (handle.IsInvalid) + { + throw new ApplicationException("There is no provider that supplies a dictionary for " + language_tag); + } + + return CreateAndRegisterDictionary(handle, this._dictionaryCache, language_tag); + } + + private Dictionary GetDictionaryFromCache(IDictionary cache, string language_tag) { + if(CacheDictionaries) + { + WeakReference dictionaryReference; + if (cache.TryGetValue(language_tag, out dictionaryReference)) + { + if (dictionaryReference.IsAlive) + { + return (Dictionary) dictionaryReference.Target; + } + } + } + return null; + } + + private Dictionary CreateAndRegisterDictionary(SafeDictionaryHandle handle, IDictionary cache, string language_tag) { + Dictionary dictionary; + dictionary = new Dictionary(handle); + dictionary.Disposed += OnDictionaryDisposed; + // always store the dictionaries we have created + // so that we can dispose of them cleanly and give a + // better error message (ObjectDisposed) instead of a crash + // if someone tries to use a dictionary after the broker + // that created it has been disposed. + cache[language_tag] = new WeakReference(dictionary); + return dictionary; + } + + private void OnDictionaryDisposed(object sender, EventArgs e) + { + Dictionary dictionary = (Dictionary) sender; + + // try to remove from _dictionaryCache + if (!RemoveDictionaryFromCache(this._dictionaryCache, dictionary)) + { + // try to remove from _pwlDictionaryCache + RemoveDictionaryFromCache(this._pwlDictionaryCache, dictionary); + } + } + + private static bool RemoveDictionaryFromCache(IDictionary cache, Dictionary dictionary) { + foreach (KeyValuePair pair in cache) + { + if (pair.Value.IsAlive + && pair.Value.Target == dictionary) + { + cache.Remove(pair.Key); + return true; + } + } + return false; + } + + public bool DictionaryExists(string language_tag) + { + VerifyNotDisposed(); + if (language_tag == null) + { + throw new ArgumentNullException("language_tag"); + } + if (_isLibEnchantAvailable != true) + { + return false; + } + + int result = Bindings.enchant_broker_dict_exists(_handle, language_tag); + VerifyNoErrors(); + if (result != 0 && result != 1) + { + throw new NotImplementedException( + "enchant_broker_dict_exists returned unexpected value that is currently unhandled."); + } + return result == 1; + } + + public Dictionary RequestPwlDictionary(string pwlFile) + { + VerifyNotDisposed(); + if (pwlFile == null) + { + throw new ArgumentNullException("pwlFile"); + } + if (_isLibEnchantAvailable != true) + { + throw new InvalidOperationException("LibEnchant is not available"); + } + Dictionary dictionary = GetDictionaryFromCache(this._pwlDictionaryCache, pwlFile); + if (dictionary != null) + { + return dictionary; + } + + return CreatePwlDictionary(pwlFile); + } + + private Dictionary CreatePwlDictionary(string pwlFile) { + SafeDictionaryHandle handle = Bindings.enchant_broker_request_pwl_dict(this._handle, pwlFile); + VerifyNoErrors(); + if (handle.IsInvalid) + { + throw new ApplicationException("Unable to create pwl file " + pwlFile); + } + return CreateAndRegisterDictionary(handle, this._pwlDictionaryCache, pwlFile); + } + + public void SetOrdering(string language_tag, string ordering) + { + VerifyNotDisposed(); + if (_isLibEnchantAvailable == true) + { + Bindings.enchant_broker_set_ordering(_handle, language_tag, ordering); + } + VerifyNoErrors(); + } + + private void VerifyNoErrors() + { + if (_isLibEnchantAvailable == true) + { + string message = Bindings.enchant_broker_get_error(_handle); + if (!string.IsNullOrEmpty(message)) + { + throw new ApplicationException(message); + } + } + } + } +} diff --git a/src/bindings/Enchant.Net/Dictionary.cs b/src/bindings/Enchant.Net/Dictionary.cs new file mode 100644 index 0000000..e219210 --- /dev/null +++ b/src/bindings/Enchant.Net/Dictionary.cs @@ -0,0 +1,217 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +using System; +using System.Collections.Generic; + +namespace Enchant +{ + public sealed class Dictionary : IDisposable + { + private readonly SafeDictionaryHandle _handle; + private bool _disposed = false; + private DictionaryInfo _info; + + internal Dictionary(SafeDictionaryHandle handle) + { + if (handle == null) + { + throw new ArgumentNullException("handle"); + } + if (handle.IsInvalid) + { + throw new ArgumentException("handle is invalid"); + } + _handle = handle; + } + + public DictionaryInfo Information + { + get + { + VerifyNotDisposed(); + if (_info == null) + { + InitializeDictionaryInformation(); + } + return _info; + } + } + + private void InitializeDictionaryInformation() + { + Bindings.enchant_dict_describe(_handle, + delegate(DictionaryInfo dictionary) + { + _info = dictionary; + }); + } + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + // dispose-only, i.e. non-finalizable logic + _handle.Dispose(); + } + + // shared (dispose and finalizable) cleanup logic + _disposed = true; + if (disposing) + { + OnDisposed(); // call it here so will throw if someone uses + // it because _disposed has been set to true; + } + } + } + + private void VerifyNotDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException("Dictionary"); + } + } + + public bool Check(string word) + { + VerifyNotDisposed(); + if (word == null) + { + throw new ArgumentNullException("word"); + } + // result is 0 if correctly spelled, positive if not, negative if error + int result = Bindings.enchant_dict_check(_handle, word); + if (result < 0) + { + string message = Bindings.enchant_dict_get_error(_handle); + throw new ApplicationException(message); + } + return result == 0; + } + + public ICollection Suggest(string word) + { + VerifyNotDisposed(); + if (word == null) + { + throw new ArgumentNullException("word"); + } + return Bindings.enchant_dict_suggest(_handle, word); + } + + public void Add(string word) + { + VerifyNotDisposed(); + if (word == null) + { + throw new ArgumentNullException("word"); + } + Bindings.enchant_dict_add(_handle, word); + } + + public void AddToSession(string word) + { + VerifyNotDisposed(); + if (word == null) + { + throw new ArgumentNullException("word"); + } + Bindings.enchant_dict_add_to_session(_handle, word); + } + + public bool IsAdded(string word) + { + VerifyNotDisposed(); + if (word == null) + { + throw new ArgumentNullException("word"); + } + return Bindings.enchant_dict_is_added(_handle, word) == 1; + } + + public void Remove(string word) + { + VerifyNotDisposed(); + if (word == null) + { + throw new ArgumentNullException("word"); + } + Bindings.enchant_dict_remove(_handle, word); + } + + public void RemoveFromSession(string word) + { + VerifyNotDisposed(); + if (word == null) + { + throw new ArgumentNullException("word"); + } + Bindings.enchant_dict_remove_from_session(_handle, word); + } + + public bool IsRemoved(string word) + { + VerifyNotDisposed(); + if (word == null) + { + throw new ArgumentNullException("word"); + } + return Bindings.enchant_dict_is_removed(_handle, word) == 1; + } + + public void StoreReplacement(string misspelling, string correction) + { + VerifyNotDisposed(); + if (misspelling == null) + { + throw new ArgumentNullException("misspelling"); + } + if (correction == null) + { + throw new ArgumentNullException("correction"); + } + Bindings.enchant_dict_store_replacement(_handle, misspelling, correction); + } + + /// + /// Occurs when a dictionary is disposed by a call to the Dispose method + /// + public event EventHandler Disposed = delegate { }; + + private void OnDisposed() + { + Disposed.Invoke(this, new EventArgs()); + } + } +} \ No newline at end of file diff --git a/src/bindings/Enchant.Net/DictionaryInfo.cs b/src/bindings/Enchant.Net/DictionaryInfo.cs new file mode 100644 index 0000000..c3dc902 --- /dev/null +++ b/src/bindings/Enchant.Net/DictionaryInfo.cs @@ -0,0 +1,55 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +using System; + +namespace Enchant +{ + public class DictionaryInfo + { + private readonly string _language_tag; + private readonly ProviderInfo _provider_info; + + internal DictionaryInfo(string language_tag, ProviderInfo provider_info) + { + if (language_tag == null) + { + throw new ArgumentNullException("language_tag"); + } + if (provider_info == null) + { + throw new ArgumentNullException("provider_info"); + } + _language_tag = language_tag; + _provider_info = provider_info; + } + + public string Language + { + get { return _language_tag; } + } + + public ProviderInfo Provider + { + get { return _provider_info; } + } + } +} \ No newline at end of file diff --git a/src/bindings/Enchant.Net/Enchant.Net.csproj b/src/bindings/Enchant.Net/Enchant.Net.csproj new file mode 100644 index 0000000..4ea0099 --- /dev/null +++ b/src/bindings/Enchant.Net/Enchant.Net.csproj @@ -0,0 +1,62 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {249A514E-7212-485B-86D0-E0ACB8967A4C} + Library + Properties + Enchant + Enchant.Net + Always + + + true + full + false + ..\..\..\bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/bindings/Enchant.Net/Properties/AssemblyInfo.cs.in b/src/bindings/Enchant.Net/Properties/AssemblyInfo.cs.in new file mode 100644 index 0000000..43c60bd --- /dev/null +++ b/src/bindings/Enchant.Net/Properties/AssemblyInfo.cs.in @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Enchant.Net")] +[assembly: AssemblyDescription("A library that wraps other spell checking backends.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Enchant.Net")] +[assembly: AssemblyCopyright("Copyright © 2007-2008 Eric Scott Albright")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("770fdaf9-6948-4d9d-bcb1-176a9630d99b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("@ENCHANT_MAJOR_VERSION@.@ENCHANT_MINOR_VERSION@.@ENCHANT_MICRO_VERSION@.0")] +[assembly: AssemblyFileVersion("@ENCHANT_MAJOR_VERSION@.@ENCHANT_MINOR_VERSION@.@ENCHANT_MICRO_VERSION@.*")] diff --git a/src/bindings/Enchant.Net/ProviderInfo.cs b/src/bindings/Enchant.Net/ProviderInfo.cs new file mode 100644 index 0000000..183bf08 --- /dev/null +++ b/src/bindings/Enchant.Net/ProviderInfo.cs @@ -0,0 +1,52 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +namespace Enchant +{ + public class ProviderInfo + { + private readonly string _description; + private readonly string _file; + private readonly string _name; + + internal ProviderInfo(string name, string description, string file) + { + _name = name; + _description = description; + _file = file; + } + + public string Description + { + get { return _description; } + } + + public string File + { + get { return _file; } + } + + public string Name + { + get { return _name; } + } + } +} \ No newline at end of file diff --git a/src/bindings/Enchant.Net/SafeBrokerHandle.cs b/src/bindings/Enchant.Net/SafeBrokerHandle.cs new file mode 100644 index 0000000..b07cb9f --- /dev/null +++ b/src/bindings/Enchant.Net/SafeBrokerHandle.cs @@ -0,0 +1,59 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +using System; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; + +namespace Enchant +{ + internal sealed class SafeBrokerHandle : SafeHandle + { + public SafeBrokerHandle() + : base(IntPtr.Zero, true) + { + } + + /// + ///When overridden in a derived class, gets a value indicating whether the handle value is invalid. + /// + /// + ///true if the handle is valid; otherwise, false. + /// + public override bool IsInvalid + { + get { return (handle == IntPtr.Zero); } + } + + /// + ///When overridden in a derived class, executes the code required to free the handle. + /// + /// + ///true if the handle is released successfully; otherwise, in the event of a catastrophic failure, false. In this case, it generates a ReleaseHandleFailed Managed Debugging Assistant. + /// + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + protected override bool ReleaseHandle() + { + Bindings.enchant_broker_free(handle); + return true; + } + } +} \ No newline at end of file diff --git a/src/bindings/Enchant.Net/SafeDictionaryHandle.cs b/src/bindings/Enchant.Net/SafeDictionaryHandle.cs new file mode 100644 index 0000000..cf80857 --- /dev/null +++ b/src/bindings/Enchant.Net/SafeDictionaryHandle.cs @@ -0,0 +1,70 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +using System; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; + +namespace Enchant +{ + internal sealed class SafeDictionaryHandle : SafeHandle + { + private readonly SafeBrokerHandle _broker; + + public SafeDictionaryHandle(SafeBrokerHandle broker, IntPtr handle) + : base(IntPtr.Zero, true) + { + _broker = broker; + SetHandle(handle); + } + + + /// + ///When overridden in a derived class, gets a value indicating whether the handle value is invalid. + /// + /// + ///true if the handle is valid; otherwise, false. + /// + public override bool IsInvalid + { + get { return (handle == IntPtr.Zero); } + } + + /// + ///When overridden in a derived class, executes the code required to free the handle. + /// + /// + ///true if the handle is released successfully; otherwise, in the event of a catastrophic failure, false. In this case, it generates a ReleaseHandleFailed Managed Debugging Assistant. + /// + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + protected override bool ReleaseHandle() + { + // The finalizer may finalize the broker and the dictionaries in any order + // leading to a case where the broker may have already been released and thus + // already cleaned up all its owned dictionaries. If that is the case, we don't + // need to clean up. + if (_broker.IsClosed) + return true; + Bindings.enchant_broker_free_dict(_broker, handle); + return true; + } + } +} \ No newline at end of file diff --git a/src/bindings/Enchant.Net/Utf8Marshaller.cs b/src/bindings/Enchant.Net/Utf8Marshaller.cs new file mode 100644 index 0000000..fc2f583 --- /dev/null +++ b/src/bindings/Enchant.Net/Utf8Marshaller.cs @@ -0,0 +1,121 @@ +/* Copyright (c) 2007 Christopher Wilks and Eric Scott Albright + * + * 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. + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace Enchant +{ + internal sealed class Utf8Marshaller : IDisposable + { + private readonly int _byteCount; + private readonly IntPtr _unmanagedPrt; + private bool _isDisposed; + + public Utf8Marshaller(string value) + { + _byteCount = Encoding.UTF8.GetByteCount(value); + byte[] bytes = new byte[_byteCount + 1]; + int bytesWritten = Encoding.UTF8.GetBytes(value, 0, value.Length, bytes, 0); + bytes[bytesWritten] = 0; // null terminate it + ++bytesWritten; + _unmanagedPrt = Marshal.AllocHGlobal(bytesWritten); + for (int i = 0; i < bytes.Length; i++) + { + Marshal.WriteByte(MarshalledValue, i, bytes[i]); + } + } + + public IntPtr MarshalledValue + { + get { return _unmanagedPrt; } + } + + public int MarshalledSize + { + get { return _byteCount; } + } + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + + private void Dispose(bool disposing) + { + if (_isDisposed) + { + return; + } + _isDisposed = true; + if (disposing) + { + //dump managed resources + } + + Marshal.FreeHGlobal(MarshalledValue); + } + + public static List MarshalFromUtf8Array(IntPtr listAddress, int count) + { + List strings = new List(); + + for (int i = 0; i < count; i++) + { + IntPtr stringAddress = Marshal.ReadIntPtr(listAddress, i*IntPtr.Size); + string s = MarshalFromUtf8(stringAddress); + strings.Add(s); + } + return strings; + } + + public static string MarshalFromUtf8(IntPtr intPtr) + { + if (intPtr.ToInt64() == 0) + { + return null; + } + List bytes = new List(); + for (int i = 0; true; i++) + { + byte b = Marshal.ReadByte(intPtr, i); + if (b == 0) + { + break; + } + bytes.Add(b); + } + return Encoding.UTF8.GetString(bytes.ToArray()); + } + + ~Utf8Marshaller() + { + Dispose(false); + } + } +} \ No newline at end of file diff --git a/src/config.h.win32 b/src/config.h.win32 new file mode 100644 index 0000000..a893d5e --- /dev/null +++ b/src/config.h.win32 @@ -0,0 +1,11 @@ +/* + * Hand tailored config.h for windows. + */ + +/* define ssize_t to int if doesn't define.*/ +typedef int ssize_t; +/* #undef ssize_t */ + +#if defined(_MSC_VER) +#pragma warning(disable: 4996) /* The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name. */ +#endif diff --git a/src/enchant++.h b/src/enchant++.h new file mode 100644 index 0000000..9d05825 --- /dev/null +++ b/src/enchant++.h @@ -0,0 +1,292 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003 Dom Lachowicz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#ifndef ENCHANT_PLUS_PLUS_H +#define ENCHANT_PLUS_PLUS_H + +#include +#include +#include +#include + +namespace enchant +{ + class Broker; + + class Exception : public std::exception + { + public: + explicit Exception (const char * ex) + : std::exception (), m_ex ("") { + if (ex) + m_ex = ex; + } + + virtual ~Exception () throw() { + } + + virtual const char * what () throw() { + return m_ex.c_str(); + } + + private: + Exception (); + + std::string m_ex; + }; + + class Dict + { + friend class enchant::Broker; + + public: + + ~Dict () { + enchant_broker_free_dict (m_broker, m_dict); + } + + bool check (const std::string & utf8word) { + int val; + + val = enchant_dict_check (m_dict, utf8word.c_str(), + utf8word.size()); + if (val == 0) + return true; + else if (val > 0) + return false; + else { + throw enchant::Exception (enchant_dict_get_error (m_dict)); + } + + return false; // never reached + } + + void suggest (const std::string & utf8word, + std::vector & out_suggestions) { + size_t n_suggs; + char ** suggs; + + out_suggestions.clear (); + + suggs = enchant_dict_suggest (m_dict, utf8word.c_str(), + utf8word.size(), &n_suggs); + + if (suggs && n_suggs) { + for (size_t i = 0; i < n_suggs; i++) { + out_suggestions.push_back (suggs[i]); + } + + enchant_dict_free_string_list (m_dict, suggs); + } + } + + std::vector suggest (const std::string & utf8word) { + std::vector result; + suggest (utf8word, result); + return result; + } + + void add (const std::string & utf8word) { + enchant_dict_add (m_dict, utf8word.c_str(), + utf8word.size()); + } + + void add_to_session (const std::string & utf8word) { + enchant_dict_add_to_session (m_dict, utf8word.c_str(), + utf8word.size()); + } + + void is_added (const std::string & utf8word) { + enchant_dict_is_added (m_dict, utf8word.c_str(), + utf8word.size()); + } + + void remove (const std::string & utf8word) { + enchant_dict_remove (m_dict, utf8word.c_str(), + utf8word.size()); + } + + void remove_from_session (const std::string & utf8word) { + enchant_dict_remove_from_session (m_dict, utf8word.c_str(), + utf8word.size()); + } + + void is_removed (const std::string & utf8word) { + enchant_dict_is_removed (m_dict, utf8word.c_str(), + utf8word.size()); + } + + void store_replacement (const std::string & utf8bad, + const std::string & utf8good) { + enchant_dict_store_replacement (m_dict, + utf8bad.c_str(), utf8bad.size(), + utf8good.c_str(), utf8good.size()); + } + + const std::string & get_lang () const { + return m_lang; + } + + const std::string & get_provider_name () const { + return m_provider_name; + } + + const std::string & get_provider_desc () const { + return m_provider_desc; + } + + const std::string & get_provider_file () const { + return m_provider_file; + } + + /* deprecated */ + void add_to_personal (const std::string & utf8word) { + return add (utf8word); + } + + /* deprecated */ + void add_to_pwl (const std::string & utf8word) { + return add (utf8word); + } + private: + + // space reserved for API/ABI expansion + void * _private[5]; + + static void s_describe_fn (const char * const lang, + const char * const provider_name, + const char * const provider_desc, + const char * const provider_file, + void * user_data) { + enchant::Dict * dict = static_cast (user_data); + + dict->m_lang = lang; + dict->m_provider_name = provider_name; + dict->m_provider_desc = provider_desc; + dict->m_provider_file = provider_file; + } + + Dict (EnchantDict * dict, EnchantBroker * broker) + : m_dict (dict), m_broker (broker) { + enchant_dict_describe (m_dict, s_describe_fn, this); + } + + // private, unimplemented + Dict (); + Dict (const Dict & rhs); + Dict& operator=(const Dict & rhs); + + EnchantDict * m_dict; + EnchantBroker * m_broker; + + std::string m_lang; + std::string m_provider_name; + std::string m_provider_desc; + std::string m_provider_file; + }; // class enchant::Dict + + class Broker + { + + public: + + static Broker * instance () { + return &m_instance; + } + + Dict * request_dict (const std::string & lang) { + EnchantDict * dict = enchant_broker_request_dict (m_broker, lang.c_str()); + + if (!dict) { + throw enchant::Exception (enchant_broker_get_error (m_broker)); + return 0; // never reached + } + + return new Dict (dict, m_broker); + } + + Dict * request_pwl_dict (const std::string & pwl) { + EnchantDict * dict = enchant_broker_request_pwl_dict (m_broker, pwl.c_str()); + + if (!dict) { + throw enchant::Exception (enchant_broker_get_error (m_broker)); + return 0; // never reached + } + + return new Dict (dict, m_broker); + } + + bool dict_exists (const std::string & lang) { + if (enchant_broker_dict_exists (m_broker, lang.c_str())) + return true; + return false; + } + + void set_ordering (const std::string & tag, const std::string & ordering) { + enchant_broker_set_ordering (m_broker, tag.c_str(), ordering.c_str()); + } + + void describe (EnchantBrokerDescribeFn fn, void * user_data = NULL) { + enchant_broker_describe (m_broker, fn, user_data); + } + + void list_dicts (EnchantDictDescribeFn fn, void * user_data = NULL) { + enchant_broker_list_dicts (m_broker, fn, user_data); + } + + private: + + // space reserved for API/ABI expansion + void * _private[5]; + + Broker () + : m_broker (enchant_broker_init ()) + { + } + + ~Broker () { + enchant_broker_free (m_broker); + } + + // not implemented + Broker (const Broker & rhs); + Broker& operator=(const Broker & rhs); + + static Broker m_instance; + + EnchantBroker * m_broker; + }; // class enchant::Broker + + // define the broker instance + Broker Broker::m_instance; + +} // enchant namespace + +#endif /* ENCHANT_PLUS_PLUS_H */ diff --git a/src/enchant-provider.h b/src/enchant-provider.h new file mode 100644 index 0000000..64eb814 --- /dev/null +++ b/src/enchant-provider.h @@ -0,0 +1,145 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003 Dom Lachowicz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#ifndef ENCHANT_PROVIDER_H +#define ENCHANT_PROVIDER_H + +#include +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* private */ +ENCHANT_MODULE_EXPORT(char *) + enchant_get_user_language(void); + +#ifdef _WIN32 +#define ENCHANT_PLUGIN_DECLARE(name) static HANDLE s_hModule = (HANDLE)(NULL); BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { s_hModule = hModule; return TRUE; } +#else +#define ENCHANT_PLUGIN_DECLARE(name) +#endif + +typedef struct str_enchant_provider EnchantProvider; + +ENCHANT_MODULE_EXPORT (GSList *) + enchant_get_user_config_dirs (void); + +ENCHANT_MODULE_EXPORT (char *) + enchant_get_registry_value (const char * const prefix, const char * const key); + +ENCHANT_MODULE_EXPORT(char *) + enchant_get_prefix_dir(void); + +ENCHANT_MODULE_EXPORT(void) + enchant_dict_set_error (EnchantDict * dict, const char * const err); + +ENCHANT_MODULE_EXPORT(void) + enchant_provider_set_error (EnchantProvider * provider, const char * const err); + +ENCHANT_MODULE_EXPORT(FILE *) + enchant_fopen (const gchar *filename, const gchar *mode); + +ENCHANT_MODULE_EXPORT (GSList *) + enchant_get_dirs_from_param (EnchantBroker * broker, const char * const param_name); + +struct str_enchant_dict +{ + void *user_data; + void *enchant_private_data; + + int (*check) (struct str_enchant_dict * me, const char *const word, + size_t len); + + /* returns utf8*/ + char **(*suggest) (struct str_enchant_dict * me, + const char *const word, size_t len, + size_t * out_n_suggs); + + void (*add_to_personal) (struct str_enchant_dict * me, + const char *const word, size_t len); + + void (*add_to_session) (struct str_enchant_dict * me, + const char *const word, size_t len); + + void (*store_replacement) (struct str_enchant_dict * me, + const char *const mis, size_t mis_len, + const char *const cor, size_t cor_len); + + void (*add_to_exclude) (struct str_enchant_dict * me, + const char *const word, size_t len); + + void * _reserved[5]; +}; + +struct str_enchant_provider +{ + void *user_data; + void *enchant_private_data; + EnchantBroker * owner; + + void (*dispose) (struct str_enchant_provider * me); + + EnchantDict *(*request_dict) (struct str_enchant_provider * me, + const char *const tag); + + void (*dispose_dict) (struct str_enchant_provider * me, + EnchantDict * dict); + + int (*dictionary_exists) (struct str_enchant_provider * me, + const char *const tag); + + /* returns utf8*/ + const char * (*identify) (struct str_enchant_provider * me); + /* returns utf8*/ + const char * (*describe) (struct str_enchant_provider * me); + + /* frees string lists returned by list_dicts and dict->suggest */ + void (*free_string_list) (struct str_enchant_provider * me, + char **str_list); + + char ** (*list_dicts) (struct str_enchant_provider * me, + size_t * out_n_dicts); + + void * _reserved[5]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ENCHANT_H */ diff --git a/src/enchant.c b/src/enchant.c new file mode 100644 index 0000000..e1f3245 --- /dev/null +++ b/src/enchant.c @@ -0,0 +1,2339 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003, 2004 Dom Lachowicz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "enchant.h" +#include "enchant-provider.h" +#include "pwl.h" + +#ifdef XP_TARGET_COCOA +#import "enchant_cocoa.h" +#endif + +#ifdef XP_TARGET_COCOA +#define ENCHANT_USER_PATH_EXTENSION "Library", "Application Support", "Enchant" +#elif defined(_WIN32) +#define ENCHANT_USER_PATH_EXTENSION "enchant" +#else +#define ENCHANT_USER_PATH_EXTENSION ".enchant" +#endif + +#ifdef ENABLE_BINRELOC +#include "prefix.h" +#endif + +ENCHANT_PLUGIN_DECLARE("Enchant") + +static char * +enchant_get_registry_value_ex (int current_user, const char * const prefix, const char * const key); + +/********************************************************************************/ +/********************************************************************************/ + +struct str_enchant_broker +{ + GSList *provider_list; /* list of all of the spelling backend providers */ + GHashTable *dict_map; /* map of language tag -> dictionary */ + GHashTable *provider_ordering; /* map of language tag -> provider order */ + GHashTable *params; + + gchar * error; +}; + +typedef struct str_enchant_session +{ + GHashTable *session_include; + GHashTable *session_exclude; + EnchantPWL *personal; + EnchantPWL *exclude; + + char * personal_filename; + char * exclude_filename; + char * language_tag; + + char * error; + + gboolean is_pwl; + + EnchantProvider * provider; +} EnchantSession; + +typedef struct str_enchant_dict_private_data +{ + unsigned int reference_count; + EnchantSession* session; +} EnchantDictPrivateData; + +typedef EnchantProvider *(*EnchantProviderInitFunc) (void); +typedef void (*EnchantPreConfigureFunc) (EnchantProvider * provider, const char * module_dir); + +/********************************************************************************/ +/********************************************************************************/ + +#ifdef _WIN32 +#define path_cmp g_utf8_collate +#else +#define path_cmp strcmp +#endif + +static GSList* enchant_slist_prepend_unique_path (GSList *slist, gchar* data) +{ + if (NULL == g_slist_find_custom (slist, data, (GCompareFunc)path_cmp)) + { + return g_slist_prepend (slist, data); + } + else + { + g_free (data); + return slist; + } +} + +static GSList* enchant_slist_append_unique_path (GSList *slist, gchar* data) +{ + if (NULL == g_slist_find_custom (slist, data, (GCompareFunc)path_cmp)) + { + return g_slist_append (slist, data); + } + else + { + g_free (data); + return slist; + } +} + +static GSList * +_enchant_get_user_home_dirs (void) +{ + GSList *dirs = NULL; + const char* home_dir; + char *tmp; + + tmp = enchant_get_registry_value_ex (1, "Config", "Home_Dir"); + if (tmp) + dirs = enchant_slist_append_unique_path (dirs, tmp); + + home_dir = g_get_home_dir (); + if (home_dir) + dirs = enchant_slist_append_unique_path (dirs, g_strdup (home_dir)); + + return dirs; +} + +static void +_enchant_ensure_dir_exists (const char* dir) +{ + if (dir && !g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) + { + (void)g_remove (dir); + g_mkdir_with_parents (dir, 0700); + } +} + +static GSList * +enchant_get_user_dirs (void) +{ + GSList *user_dirs = NULL; + + { + const char * user_config_dir; + + user_config_dir = g_get_user_config_dir(); + + if (user_config_dir) + user_dirs = enchant_slist_append_unique_path (user_dirs, g_build_filename (user_config_dir, + "enchant", + NULL)); + } + + { + GSList *home_dirs = NULL, *dir; + home_dirs = _enchant_get_user_home_dirs (); + + for (dir = home_dirs; dir; dir = dir->next) + { + user_dirs = enchant_slist_append_unique_path (user_dirs, + g_build_filename (dir->data, + ENCHANT_USER_PATH_EXTENSION, + NULL)); + } + + g_slist_foreach (home_dirs, (GFunc)g_free, NULL); + g_slist_free (home_dirs); + } + + return user_dirs; +} + +/* place to look for system level providers */ +static GSList * +enchant_get_module_dirs (void) +{ + GSList *module_dirs = NULL; + + char * module_dir = NULL; + char * prefix = NULL; + + { + char* user_module_dir; + + user_module_dir = enchant_get_registry_value_ex (1, "Config", "Module_Dir"); + if (user_module_dir) + module_dirs = enchant_slist_append_unique_path (module_dirs, user_module_dir); + } + +#ifdef XP_TARGET_COCOA + module_dirs = enchant_slist_append_unique_path (module_dirs, g_strdup ([[EnchantResourceProvider instance] moduleFolder])); +#endif + + { + GSList *user_dirs, *iter; + + user_dirs = enchant_get_user_dirs(); + + for (iter = user_dirs; iter; iter = iter->next) + module_dirs = enchant_slist_append_unique_path (module_dirs, iter->data); + + g_slist_free (user_dirs); + } + + /* Look for explicitly set registry values */ + module_dir = enchant_get_registry_value_ex (0, "Config", "Module_Dir"); + if (module_dir) + module_dirs = enchant_slist_append_unique_path (module_dirs, module_dir); + +#if defined(ENCHANT_GLOBAL_MODULE_DIR) + module_dirs = enchant_slist_append_unique_path (module_dirs, g_strdup (ENCHANT_GLOBAL_MODULE_DIR)); +#else + /* Dynamically locate library and search for modules relative to it. */ + prefix = enchant_get_prefix_dir(); + if(prefix) + { + module_dir = g_build_filename(prefix,"lib","enchant",NULL); + g_free(prefix); + module_dirs = enchant_slist_append_unique_path (module_dirs, module_dir); + } +#endif + + return module_dirs; +} + +static GSList * +enchant_get_conf_dirs (void) +{ + GSList *conf_dirs = NULL, *user_conf_dirs, *iter; + char * ordering_dir = NULL, * prefix = NULL; + + user_conf_dirs = enchant_get_user_config_dirs(); + + for (iter = user_conf_dirs; iter != NULL; iter = iter->next) + { + conf_dirs = enchant_slist_append_unique_path (conf_dirs, iter->data); + } + + g_slist_free (user_conf_dirs); + +#ifdef XP_TARGET_COCOA + conf_dirs = enchant_slist_append_unique_path (conf_dirs, g_strdup ([[EnchantResourceProvider instance] configFolder])); +#endif + + /* Look for explicitly set registry values */ + ordering_dir = enchant_get_registry_value_ex (0, "Config", "Data_Dir"); + if (ordering_dir) + conf_dirs = enchant_slist_append_unique_path (conf_dirs, ordering_dir); + + /* Dynamically locate library and search for files relative to it. */ + prefix = enchant_get_prefix_dir(); + if(prefix) + { + ordering_dir = g_build_filename(prefix,"share","enchant",NULL); + g_free(prefix); + conf_dirs = enchant_slist_append_unique_path (conf_dirs, ordering_dir); + } + +#if defined(ENCHANT_GLOBAL_ORDERING) + conf_dirs = enchant_slist_append_unique_path (conf_dirs, g_strdup (ENCHANT_GLOBAL_ORDERING)); +#endif + + return conf_dirs; +} + +ENCHANT_MODULE_EXPORT(FILE *) +enchant_fopen (const gchar *filename, const gchar *mode) +{ +#ifdef G_OS_WIN32 + wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL); + wchar_t *wmode; + FILE *retval; + int save_errno; + + if (wfilename == NULL) + { + errno = EINVAL; + return NULL; + } + + wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL); + + if (wmode == NULL) + { + g_free (wfilename); + errno = EINVAL; + return NULL; + } + + retval = _wfopen (wfilename, wmode); + save_errno = errno; + + g_free (wfilename); + g_free (wmode); + + errno = save_errno; + return retval; +#else + return fopen (filename, mode); +#endif +} + +/** + * enchant_get_user_config_dir + * + * Returns: the user's enchant directory, or %null. Returned value + * must be free'd. + * + * The enchant directory is the place where enchant finds user + * dictionaries and settings related to enchant + * + * This API is private to the providers. + */ +ENCHANT_MODULE_EXPORT (GSList *) +enchant_get_user_config_dirs (void) +{ + GSList *dirs; + char* user_config; + + dirs = enchant_get_user_dirs(); + + user_config = enchant_get_registry_value_ex (1, "Config", "Data_Dir"); + if (user_config) + dirs = enchant_slist_prepend_unique_path (dirs, user_config); + + return dirs; +} + +/* + * Returns: the value if it exists and is not an empty string ("") or %null otherwise. Must be free'd. + */ +static char * +enchant_get_registry_value_ex (int current_user, const char * const prefix, const char * const key) +{ +#ifndef _WIN32 + /* TODO: GConf? KConfig? */ + return NULL; +#else + HKEY hKey; + HKEY baseKey; + unsigned long lType; + DWORD dwSize; + char* keyName; + WCHAR* wszValue = NULL; + char* szValue = NULL; + gunichar2 * uKeyName; + gunichar2 * uKey; + + if (current_user) + baseKey = HKEY_CURRENT_USER; + else + baseKey = HKEY_LOCAL_MACHINE; + + keyName = g_strdup_printf("Software\\Enchant\\%s", prefix); + uKeyName = g_utf8_to_utf16 (keyName, -1, NULL, NULL, NULL); + uKey = g_utf8_to_utf16 (key, -1, NULL, NULL, NULL); + + if(RegOpenKeyExW(baseKey, uKeyName, 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + /* Determine size of string */ + if(RegQueryValueExW( hKey, uKey, NULL, &lType, NULL, &dwSize) == ERROR_SUCCESS) + { + wszValue = g_new0(WCHAR, dwSize + 1); + RegQueryValueExW(hKey, uKey, NULL, &lType, (LPBYTE) wszValue, &dwSize); + } + RegCloseKey(hKey); + } + + if(wszValue && *wszValue) + szValue = g_utf16_to_utf8 (wszValue, -1, NULL, NULL, NULL); + + g_free(keyName); + g_free(uKeyName); + g_free(uKey); + g_free(wszValue); + + return szValue; +#endif +} + +/** + * enchant_get_registry_value + * @prefix: Your category, such as "Ispell" or "Myspell" + * @key: The tag within your category that you're interested in + * + * Returns: the value if it exists and is not an empty string ("") or %null otherwise. Must be free'd. + * + * This API is private to the providers. + */ +ENCHANT_MODULE_EXPORT (char *) +enchant_get_registry_value (const char * const prefix, const char * const key) +{ + char *val; + + g_return_val_if_fail (prefix, NULL); + g_return_val_if_fail (key, NULL); + + val = enchant_get_registry_value_ex(1, prefix, key); + if(val == NULL) { + val = enchant_get_registry_value_ex (0, prefix, key); + } + return val; +} + +/********************************************************************************/ +/********************************************************************************/ + +static gchar* +enchant_modify_string_chars (gchar *str, + gssize len, + gchar (*function)(gchar)) +{ + gchar* it, *end; + + g_return_val_if_fail (str != NULL, NULL); + + if (len < 0) + len = strlen (str); + + end = str + len; + + for (it = str; it != end; ++it) + *it = function (*it); + + return str; +} + +static gchar* +enchant_ascii_strup (gchar *str, + gssize len) +{ + return enchant_modify_string_chars(str, len, g_ascii_toupper); +} + +static gchar* +enchant_ascii_strdown (gchar *str, + gssize len) +{ + return enchant_modify_string_chars(str, len, g_ascii_tolower); +} + +/* returns TRUE if tag is valid + * for requires alphanumeric ASCII or underscore + */ +static int +enchant_is_valid_dictionary_tag(const char * const tag) +{ + const char * it; + for (it = tag; *it; ++it) + { + if(!g_ascii_isalnum(*it) && *it != '_') + return 0; + } + + return it != tag; /*empty tag invalid*/ +} + +static char * +enchant_normalize_dictionary_tag (const char * const dict_tag) +{ + char * new_tag = g_strdup (dict_tag); + char * needle; + + new_tag = g_strstrip (new_tag); + + /* strip off en_GB@euro */ + if ((needle = strchr (new_tag, '@')) != NULL) + *needle = '\0'; + + /* strip off en_GB.UTF-8 */ + if ((needle = strchr (new_tag, '.')) != NULL) + *needle = '\0'; + + /* turn en-GB into en_GB */ + if ((needle = strchr (new_tag, '-')) != NULL) + *needle = '_'; + + /* everything before first '_' is converted to lower case */ + if ((needle = strchr (new_tag, '_')) != NULL) { + enchant_ascii_strdown(new_tag, needle - new_tag); + ++needle; + /* everything after first '_' is converted to upper case */ + enchant_ascii_strup(needle, -1); + } + else { + enchant_ascii_strdown(new_tag, -1); + } + + return new_tag; +} + +static char * +enchant_iso_639_from_tag (const char * const dict_tag) +{ + char * new_tag = g_strdup (dict_tag); + char * needle; + + if ((needle = strchr (new_tag, '_')) != NULL) + *needle = '\0'; + + return new_tag; +} + +static void +enchant_session_destroy (EnchantSession * session) +{ + g_hash_table_destroy (session->session_include); + g_hash_table_destroy (session->session_exclude); + enchant_pwl_free (session->personal); + enchant_pwl_free (session->exclude); + g_free (session->personal_filename); + g_free (session->exclude_filename); + g_free (session->language_tag); + + if (session->error) + g_free (session->error); + + g_free (session); +} + +static EnchantSession * +enchant_session_new_with_pwl (EnchantProvider * provider, + const char * const pwl, + const char * const excl, + const char * const lang, + gboolean fail_if_no_pwl) +{ + EnchantSession * session; + EnchantPWL *personal = NULL; + EnchantPWL *exclude = NULL; + + if (pwl) + personal = enchant_pwl_init_with_file (pwl); + + if (personal == NULL) { + if (fail_if_no_pwl) + return NULL; + else + personal = enchant_pwl_init (); + } + + if (excl) + exclude = enchant_pwl_init_with_file (excl); + if (exclude == NULL) + exclude = enchant_pwl_init (); + + session = g_new0 (EnchantSession, 1); + session->session_include = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + session->session_exclude = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + session->personal = personal; + session->exclude = exclude; + session->provider = provider; + session->language_tag = g_strdup (lang); + session->personal_filename = g_strdup (pwl); + session->exclude_filename = g_strdup (excl); + + return session; +} + +static EnchantSession * +_enchant_session_new (EnchantProvider *provider, const char * const user_config_dir, + const char * const lang, gboolean fail_if_no_pwl) +{ + char *filename, *dic, *excl; + EnchantSession * session; + + if (!user_config_dir || !lang) + return NULL; + + filename = g_strdup_printf ("%s.dic", lang); + dic = g_build_filename (user_config_dir, filename, NULL); + g_free (filename); + + filename = g_strdup_printf ("%s.exc", lang); + excl = g_build_filename (user_config_dir, filename, NULL); + g_free (filename); + + session = enchant_session_new_with_pwl (provider, dic, excl, lang, fail_if_no_pwl); + + g_free (dic); + g_free (excl); + + return session; +} + +static EnchantSession * +enchant_session_new (EnchantProvider *provider, const char * const lang) +{ + EnchantSession * session = NULL; + GSList *user_config_dirs, *iter; + + user_config_dirs = enchant_get_user_config_dirs (); + for (iter = user_config_dirs; iter != NULL && session == NULL; iter = iter->next) + { + session =_enchant_session_new (provider, iter->data, lang, TRUE); + } + + if (session == NULL && user_config_dirs != NULL) + { + _enchant_ensure_dir_exists (user_config_dirs->data); + + session =_enchant_session_new (provider, user_config_dirs->data, lang, FALSE); + } + + g_slist_foreach (user_config_dirs, (GFunc)g_free, NULL); + g_slist_free (user_config_dirs); + + + return session; +} + +static void +enchant_session_add (EnchantSession * session, const char * const word, size_t len) +{ + char* key = g_strndup (word, len); + g_hash_table_remove (session->session_exclude, key); + g_hash_table_insert (session->session_include, key, GINT_TO_POINTER(TRUE)); +} + +static void +enchant_session_remove (EnchantSession * session, const char * const word, size_t len) +{ + char* key = g_strndup (word, len); + g_hash_table_remove (session->session_include, key); + g_hash_table_insert (session->session_exclude, key, GINT_TO_POINTER(TRUE)); +} + +static void +enchant_session_add_personal (EnchantSession * session, const char * const word, size_t len) +{ + enchant_pwl_add(session->personal, word, len); +} + +static void +enchant_session_remove_personal (EnchantSession * session, const char * const word, size_t len) +{ + enchant_pwl_remove(session->personal, word, len); +} + +static void +enchant_session_add_exclude (EnchantSession * session, const char * const word, size_t len) +{ + enchant_pwl_add(session->exclude, word, len); +} + +static void +enchant_session_remove_exclude (EnchantSession * session, const char * const word, size_t len) +{ + enchant_pwl_remove(session->exclude, word, len); +} + +/* a word is excluded if it is in the exclude dictionary or in the session exclude list + * AND the word has not been added to the session include list + */ +static gboolean +enchant_session_exclude (EnchantSession * session, const char * const word, size_t len) +{ + gboolean result = FALSE; + + char * utf = g_strndup (word, len); + + if (!g_hash_table_lookup (session->session_include, utf) && + (g_hash_table_lookup (session->session_exclude, utf)|| + enchant_pwl_check (session->exclude, word, len) == 0 )) + result = TRUE; + g_free (utf); + + return result; +} + +static gboolean +enchant_session_contains (EnchantSession * session, const char * const word, size_t len) +{ + gboolean result = FALSE; + + char * utf = g_strndup (word, len); + + if (g_hash_table_lookup (session->session_include, utf) || + (enchant_pwl_check (session->personal, word, len) == 0 && + !enchant_pwl_check (session->exclude, word, len) == 0)) + result = TRUE; + + g_free (utf); + + return result; +} + +static void +enchant_session_clear_error (EnchantSession * session) +{ + if (session->error) + { + g_free (session->error); + session->error = NULL; + } +} + +/********************************************************************************/ +/********************************************************************************/ + +static void +enchant_provider_free_string_list (EnchantProvider * provider, char ** string_list) +{ + if (provider && provider->free_string_list) + (*provider->free_string_list) (provider, string_list); +} + +/** + * enchant_dict_set_error + * @dict: A non-null dictionary + * @err: A non-null error message + * + * Sets the current runtime error to @err. This API is private to the + * providers. + */ +ENCHANT_MODULE_EXPORT(void) +enchant_dict_set_error (EnchantDict * dict, const char * const err) +{ + EnchantSession * session; + + g_return_if_fail (dict); + g_return_if_fail (err); + g_return_if_fail (g_utf8_validate(err, -1, NULL)); + + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + + enchant_session_clear_error (session); + session->error = g_strdup (err); +} + +/** + * enchant_dict_get_error + * @dict: A non-null dictionary + * + * Returns a const char string or NULL describing the last exception in UTF8 encoding. + * WARNING: error is transient. It will likely be cleared as soon as + * the next dictionary operation is called + * + * Returns: an error message + */ +ENCHANT_MODULE_EXPORT(char *) +enchant_dict_get_error (EnchantDict * dict) +{ + EnchantSession * session; + + g_return_val_if_fail (dict, NULL); + + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + return session->error; +} + +/** + * enchant_dict_check + * @dict: A non-null #EnchantDict + * @word: The non-null word you wish to check, in UTF-8 encoding + * @len: The byte length of @word, or -1 for strlen (@word) + * + * Will return an "incorrect" value if any of those pre-conditions + * are not met. + * + * Returns: 0 if the word is correctly spelled, positive if not, negative if error + */ +ENCHANT_MODULE_EXPORT (int) +enchant_dict_check (EnchantDict * dict, const char *const word, ssize_t len) +{ + EnchantSession * session; + + g_return_val_if_fail (dict, -1); + g_return_val_if_fail (word, -1); + + if (len < 0) + len = strlen (word); + + g_return_val_if_fail (len, -1); + g_return_val_if_fail (g_utf8_validate(word, len, NULL),-1); + + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + enchant_session_clear_error (session); + + /* first, see if it's to be excluded*/ + if (enchant_session_exclude (session, word, len)) + return 1; + + /* then, see if it's in our pwl or session*/ + if (enchant_session_contains(session, word, len)) + return 0; + + if (dict->check) + return (*dict->check) (dict, word, len); + else if (session->is_pwl) + return 1; + + return -1; +} + +/* @suggs must have at least n_suggs + n_new_suggs space allocated + * @n_suggs is the number if items currently appearing in @suggs + * + * returns the number of items in @suggs after merge is complete + */ +static int +enchant_dict_merge_suggestions(EnchantDict * dict, + const char ** suggs, + size_t n_suggs, + const char * const* const new_suggs, + size_t n_new_suggs) +{ + EnchantSession * session; + size_t i, j; + + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + + for(i = 0; i < n_new_suggs; i++) + { + int is_duplicate = 0; + char * normalized_new_sugg; + + normalized_new_sugg = g_utf8_normalize (new_suggs[i], -1, G_NORMALIZE_NFD); + + for(j = 0; j < n_suggs; j++) + { + char* normalized_sugg; + normalized_sugg = g_utf8_normalize (suggs[j], -1, G_NORMALIZE_NFD); + + if(strcmp(normalized_sugg,normalized_new_sugg)==0) + { + is_duplicate = 1; + g_free(normalized_sugg); + break; + } + g_free(normalized_sugg); + } + g_free(normalized_new_sugg); + + if(!is_duplicate) + { + suggs[n_suggs] = g_strdup (new_suggs[i]); + ++n_suggs; + } + } + + return n_suggs; +} + +static char ** +enchant_dict_get_good_suggestions(EnchantDict * dict, + const char * const* const suggs, + size_t n_suggs, + size_t* out_n_filtered_suggs) +{ + EnchantSession * session; + size_t i, n_filtered_suggs; + char ** filtered_suggs; + + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + + filtered_suggs = g_new0 (char *, n_suggs + 1); + n_filtered_suggs = 0; + + for(i = 0; i < n_suggs; i++) + { + size_t sugg_len = strlen(suggs[i]); + + if (sugg_len == 0) continue; + + if(g_utf8_validate(suggs[i], sugg_len, NULL) && + !enchant_session_exclude(session, suggs[i], sugg_len) ) + { + filtered_suggs[n_filtered_suggs] = g_strdup (suggs[i]); + ++n_filtered_suggs; + } + } + + if(out_n_filtered_suggs) + *out_n_filtered_suggs = n_filtered_suggs; + + return filtered_suggs; +} + +/** + * enchant_dict_suggest + * @dict: A non-null #EnchantDict + * @word: The non-null word you wish to find suggestions for, in UTF-8 encoding + * @len: The byte length of @word, or -1 for strlen (@word) + * @out_n_suggs: The location to store the # of suggestions returned, or %null + * + * Will return an %null value if any of those pre-conditions + * are not met. + * + * Returns: A %null terminated list of UTF-8 encoded suggestions, or %null + */ +ENCHANT_MODULE_EXPORT (char **) +enchant_dict_suggest (EnchantDict * dict, const char *const word, + ssize_t len, size_t * out_n_suggs) +{ + EnchantSession * session; + size_t n_suggs = 0, n_dict_suggs = 0, n_pwl_suggs = 0, n_suggsT = 0; + char **suggs, **dict_suggs = NULL, **pwl_suggs = NULL, **suggsT; + + g_return_val_if_fail (dict, NULL); + g_return_val_if_fail (word, NULL); + + if (len < 0) + len = strlen (word); + + g_return_val_if_fail (len, NULL); + g_return_val_if_fail (g_utf8_validate(word, len, NULL), NULL); + + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + enchant_session_clear_error (session); + /* Check for suggestions from provider dictionary */ + if (dict->suggest) + { + dict_suggs = (*dict->suggest) (dict, word, len, + &n_dict_suggs); + if(dict_suggs) + { + suggsT = enchant_dict_get_good_suggestions(dict, dict_suggs, n_dict_suggs, &n_suggsT); + enchant_provider_free_string_list (session->provider, dict_suggs); + dict_suggs = suggsT; + n_dict_suggs = n_suggsT; + } + } + + /* Check for suggestions from personal dictionary */ + if(session->personal) + { + pwl_suggs = enchant_pwl_suggest(session->personal, word, len, dict_suggs, &n_pwl_suggs); + if(pwl_suggs) + { + suggsT = enchant_dict_get_good_suggestions(dict, pwl_suggs, n_pwl_suggs, &n_suggsT); + enchant_pwl_free_string_list (session->personal, pwl_suggs); + pwl_suggs = suggsT; + n_pwl_suggs = n_suggsT; + } + } + /* Clone suggestions if there are any */ + n_suggs = n_pwl_suggs + n_dict_suggs; + if (n_suggs > 0) + { + suggs = g_new0 (char *, n_suggs + 1); + + /* Copy over suggestions from dict, if no dupes */ + n_suggs = enchant_dict_merge_suggestions(dict, + suggs, 0, + dict_suggs, n_dict_suggs); + + /* Copy over suggestions from pwl, if no dupes */ + n_suggs = enchant_dict_merge_suggestions(dict, + suggs, n_suggs, + pwl_suggs, n_pwl_suggs); + if(n_suggs == 0) + { + g_free(suggs); + suggs = NULL; + } + } + else + { + suggs = NULL; + } + + g_strfreev(dict_suggs); + g_strfreev(pwl_suggs); + + if (out_n_suggs) + *out_n_suggs = n_suggs; + + return suggs; +} + +/** + * enchant_dict_add + * @dict: A non-null #EnchantDict + * @word: The non-null word you wish to add to your personal dictionary, in UTF-8 encoding + * @len: The byte length of @word, or -1 for strlen (@word) + * + * Remarks: if the word exists in the exclude dictionary, it will be removed from the + * exclude dictionary + */ +ENCHANT_MODULE_EXPORT (void) +enchant_dict_add (EnchantDict * dict, const char *const word, + ssize_t len) +{ + EnchantSession * session; + + g_return_if_fail (dict); + g_return_if_fail (word); + + if (len < 0) + len = strlen (word); + + g_return_if_fail (len); + g_return_if_fail (g_utf8_validate(word, len, NULL)); + + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + enchant_session_clear_error (session); + enchant_session_add_personal (session, word, len); + enchant_session_remove_exclude (session, word, len); + + if (dict->add_to_personal) + (*dict->add_to_personal) (dict, word, len); +} + +/** + * enchant_dict_add_to_pwl + * @dict: A non-null #EnchantDict + * @word: The non-null word you wish to add to your personal dictionary, in UTF-8 encoding + * @len: The byte length of @word, or -1 for strlen (@word) + * + * DEPRECATED. Please use enchant_dict_add() instead. + */ +ENCHANT_MODULE_EXPORT (void) +enchant_dict_add_to_pwl (EnchantDict * dict, const char *const word, + ssize_t len) +{ + enchant_dict_add(dict,word,len); +} + +/** + * enchant_dict_add_to_personal + * @dict: A non-null #EnchantDict + * @word: The non-null word you wish to add to your personal dictionary, in UTF-8 encoding + * @len: The byte length of @word, or -1 for strlen (@word) + * + * DEPRECATED. Please use enchant_dict_add() instead. + */ +ENCHANT_MODULE_EXPORT (void) +enchant_dict_add_to_personal (EnchantDict * dict, const char *const word, + ssize_t len) +{ + enchant_dict_add(dict, word, len); +} + +/** + * enchant_dict_add_to_session + * @dict: A non-null #EnchantDict + * @word: The non-null word you wish to add to this spell-checking session, in UTF-8 encoding + * @len: The byte length of @word, or -1 for strlen (@word) + * + */ +ENCHANT_MODULE_EXPORT (void) +enchant_dict_add_to_session (EnchantDict * dict, const char *const word, + ssize_t len) +{ + EnchantSession * session; + + g_return_if_fail (dict); + g_return_if_fail (word); + + if (len < 0) + len = strlen (word); + + g_return_if_fail (len); + g_return_if_fail (g_utf8_validate(word, len, NULL)); + + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + enchant_session_clear_error (session); + + enchant_session_add (session, word, len); + if (dict->add_to_session) + (*dict->add_to_session) (dict, word, len); +} + +/** + * enchant_dict_is_added + * @dict: A non-null #EnchantDict + * @word: The word you wish to see if it has been added (to your session or dict) in UTF8 encoding + * @len: the byte length of @word, or -1 for strlen (@word) + */ +ENCHANT_MODULE_EXPORT (int) +enchant_dict_is_added (EnchantDict * dict, const char *const word, + ssize_t len) +{ + EnchantSession * session; + + g_return_val_if_fail (dict, 0); + g_return_val_if_fail (word, 0); + + if (len < 0) + len = strlen (word); + + g_return_val_if_fail (len, 0); + g_return_val_if_fail (g_utf8_validate(word, len, NULL), 0); + + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + enchant_session_clear_error (session); + + return enchant_session_contains (session, word, len); +} + +/** + * enchant_dict_is_in_session + * @dict: A non-null #EnchantDict + * @word: The word you wish to see if it's in your session in UTF8 encoding + * @len: the byte length of @word, or -1 for strlen (@word) + * + * DEPRECATED. Please use enchant_dict_is_added() instead. +*/ +ENCHANT_MODULE_EXPORT (int) +enchant_dict_is_in_session (EnchantDict * dict, const char *const word, + ssize_t len) +{ + return enchant_dict_is_added(dict, word, len); +} + +/** + * enchant_dict_remove + * @dict: A non-null #EnchantDict + * @word: The non-null word you wish to add to your exclude dictionary and + * remove from the personal dictionary, in UTF-8 encoding + * @len: The byte length of @word, or -1 for strlen (@word) + * + */ +ENCHANT_MODULE_EXPORT (void) +enchant_dict_remove (EnchantDict * dict, const char *const word, + ssize_t len) +{ + EnchantSession * session; + + g_return_if_fail (dict); + g_return_if_fail (word); + + if (len < 0) + len = strlen (word); + + g_return_if_fail (len); + g_return_if_fail (g_utf8_validate(word, len, NULL)); + + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + enchant_session_clear_error (session); + + enchant_session_remove_personal (session, word, len); + enchant_session_add_exclude(session, word, len); + + if (dict->add_to_exclude) + (*dict->add_to_exclude) (dict, word, len); +} + +/** + * enchant_dict_remove_from_session + * @dict: A non-null #EnchantDict + * @word: The non-null word you wish to exclude from this spell-checking session, in UTF-8 encoding + * @len: The byte length of @word, or -1 for strlen (@word) + * + */ +ENCHANT_MODULE_EXPORT (void) +enchant_dict_remove_from_session (EnchantDict * dict, const char *const word, + ssize_t len) +{ + EnchantSession * session; + + g_return_if_fail (dict); + g_return_if_fail (word); + + if (len < 0) + len = strlen (word); + + g_return_if_fail (len); + g_return_if_fail (g_utf8_validate(word, len, NULL)); + + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + enchant_session_clear_error (session); + + enchant_session_remove (session, word, len); +} + +/** + * enchant_dict_is_removed + * @dict: A non-null #EnchantDict + * @word: The word you wish to see if it has been removed (from your session or dict) in UTF8 encoding + * @len: the byte length of @word, or -1 for strlen (@word) + */ +ENCHANT_MODULE_EXPORT (int) +enchant_dict_is_removed (EnchantDict * dict, const char *const word, + ssize_t len) +{ + EnchantSession * session; + + g_return_val_if_fail (dict, 0); + g_return_val_if_fail (word, 0); + + if (len < 0) + len = strlen (word); + + g_return_val_if_fail (len, 0); + g_return_val_if_fail (g_utf8_validate(word, len, NULL), 0); + + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + enchant_session_clear_error (session); + + return enchant_session_exclude (session, word, len); +} + +/** + * enchant_dict_store_replacement + * @dict: A non-null #EnchantDict + * @mis: The non-null word you wish to add a correction for, in UTF-8 encoding + * @mis_len: The byte length of @mis, or -1 for strlen (@mis) + * @cor: The non-null correction word, in UTF-8 encoding + * @cor_len: The byte length of @cor, or -1 for strlen (@cor) + * + * Notes that you replaced @mis with @cor, so it's possibly more likely + * that future occurrences of @mis will be replaced with @cor. So it might + * bump @cor up in the suggestion list. + */ +ENCHANT_MODULE_EXPORT (void) +enchant_dict_store_replacement (EnchantDict * dict, + const char *const mis, ssize_t mis_len, + const char *const cor, ssize_t cor_len) +{ + EnchantSession * session; + + g_return_if_fail (dict); + g_return_if_fail (mis); + g_return_if_fail (cor); + + if (mis_len < 0) + mis_len = strlen (mis); + + if (cor_len < 0) + cor_len = strlen (cor); + + g_return_if_fail (mis_len); + g_return_if_fail (cor_len); + + g_return_if_fail (g_utf8_validate(mis, mis_len, NULL)); + g_return_if_fail (g_utf8_validate(cor, cor_len, NULL)); + + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + enchant_session_clear_error (session); + + /* if it's not implemented, it's not worth emulating */ + if (dict->store_replacement) + (*dict->store_replacement) (dict, mis, mis_len, cor, cor_len); +} + +/** + * enchant_dict_free_string_list + * @dict: A non-null #EnchantDict + * @string_list: A non-null string list returned from enchant_dict_suggest + * + * Releases the string list + */ +ENCHANT_MODULE_EXPORT (void) +enchant_dict_free_string_list (EnchantDict * dict, char **string_list) +{ + EnchantSession * session; + + g_return_if_fail (dict); + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + enchant_session_clear_error (session); + g_strfreev(string_list); +} + +/** + * enchant_dict_free_suggestions + * @dict: A non-null #EnchantDict + * @suggestions: The non-null suggestion list returned by + * 'enchant_dict_suggest' + * + * Releases the suggestions + * This function is DEPRECATED. Please use enchant_dict_free_string_list() instead. + */ +ENCHANT_MODULE_EXPORT (void) +enchant_dict_free_suggestions (EnchantDict * dict, char **suggestions) +{ + enchant_dict_free_string_list (dict, suggestions); +} + +/** + * enchant_dict_describe + * @broker: A non-null #EnchantDict + * @dict: A non-null #EnchantDictDescribeFn + * @user_data: Optional user-data + * + * Describes an individual dictionary + */ +ENCHANT_MODULE_EXPORT (void) +enchant_dict_describe (EnchantDict * dict, + EnchantDictDescribeFn fn, + void * user_data) +{ + EnchantSession * session; + EnchantProvider * provider; + GModule *module; + + const char * tag, * name, * desc, * file; + + g_return_if_fail (dict); + g_return_if_fail (fn); + + session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session; + enchant_session_clear_error (session); + provider = session->provider; + + if (provider) + { + module = (GModule *) provider->enchant_private_data; + file = g_module_name (module); + name = (*provider->identify) (provider); + desc = (*provider->describe) (provider); + } + else + { + file = session->personal_filename; + name = "Personal Wordlist"; + desc = "Personal Wordlist"; + } + + tag = session->language_tag; + (*fn) (tag, name, desc, file, user_data); +} + +/***********************************************************************************/ +/***********************************************************************************/ + +static void +enchant_broker_clear_error (EnchantBroker * broker) +{ + if (broker->error) + { + g_free (broker->error); + broker->error = NULL; + } +} + +static void +enchant_broker_set_error (EnchantBroker * broker, const char * const err) +{ + enchant_broker_clear_error (broker); + broker->error = g_strdup (err); +} + +static int +enchant_provider_is_valid(EnchantProvider * provider) +{ + if(provider == NULL) + { + g_warning ("EnchantProvider cannot be NULL\n"); + return 0; + } + + if(provider->identify == NULL) + { + g_warning ("EnchantProvider's identify method cannot be NULL\n"); + return 0; + } + else if(!g_utf8_validate((*provider->identify)(provider), -1, NULL)) + { + g_warning ("EnchantProvider's identify method does not return valid utf8.\n"); + return 0; + } + + if(provider->describe == NULL) + { + g_warning ("EnchantProvider's describe method cannot be NULL\n"); + return 0; + } + else if(!g_utf8_validate((*provider->describe)(provider), -1, NULL)) + { + g_warning ("EnchantProvider's describe method does not return valid utf8.\n"); + return 0; + } + + return 1; +} + +static void +enchant_load_providers_in_dir (EnchantBroker * broker, const char *dir_name) +{ + GModule *module = NULL; + GDir *dir; + G_CONST_RETURN char *dir_entry; + size_t entry_len, g_module_suffix_len; + + char * filename; + + EnchantProvider *provider; + EnchantProviderInitFunc init_func; + EnchantPreConfigureFunc conf_func; + + dir = g_dir_open (dir_name, 0, NULL); + if (!dir) + return; + + g_module_suffix_len = strlen (G_MODULE_SUFFIX); + + while ((dir_entry = g_dir_read_name (dir)) != NULL) + { + provider = 0; + + entry_len = strlen (dir_entry); + if ((entry_len > g_module_suffix_len) && + !strcmp(dir_entry+(entry_len-g_module_suffix_len), G_MODULE_SUFFIX)) + { +#ifdef _WIN32 + /* Suppress error popups for failing to load plugins */ + UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS); +#endif + filename = g_build_filename (dir_name, dir_entry, NULL); + + module = g_module_open (filename, (GModuleFlags) 0); + if (module) + { + if (g_module_symbol + (module, "init_enchant_provider", (gpointer *) (&init_func)) + && init_func) + { + provider = init_func (); + if (!enchant_provider_is_valid(provider)) + { + g_warning ("Error loading plugin: %s's init_enchant_provider returned invalid provider.\n", dir_entry); + if(provider) + { + if(provider->dispose) + provider->dispose(provider); + + provider = NULL; + } + g_module_close (module); + } + } + else + { + g_module_close (module); + } + } + else + { + g_warning ("Error loading plugin: %s\n", g_module_error()); + } + + g_free (filename); +#ifdef _WIN32 + /* Restore the original error mode */ + SetErrorMode(old_error_mode); +#endif + } + if (provider) + { + /* optional entry point to allow modules to look for associated files + */ + if (g_module_symbol + (module, "configure_enchant_provider", (gpointer *) (&conf_func)) + && conf_func) + { + conf_func (provider, dir_name); + if (!enchant_provider_is_valid(provider)) + { + g_warning ("Error loading plugin: %s's configure_enchant_provider modified provider and it is now invalid.\n", dir_entry); + if(provider->dispose) + provider->dispose(provider); + + provider = NULL; + g_module_close (module); + } + } + } + if (provider) + { + provider->enchant_private_data = (void *) module; + provider->owner = broker; + broker->provider_list = g_slist_append (broker->provider_list, (gpointer)provider); + } + } + + g_dir_close (dir); +} + +static void +enchant_load_providers (EnchantBroker * broker) +{ + GSList *module_dirs, *iter; + + module_dirs = enchant_get_module_dirs(); + + for (iter = module_dirs; iter; iter = iter->next) + { + enchant_load_providers_in_dir (broker, iter->data); + } + + g_slist_foreach (module_dirs, (GFunc)g_free, NULL); + g_slist_free (module_dirs); +} + +static void +enchant_load_ordering_from_file (EnchantBroker * broker, const char * file) +{ + char line [1024]; + char * tag, * ordering; + + size_t i, len; + + FILE * f; + + f = enchant_fopen (file, "r"); + if (!f) + return; + + while (NULL != fgets (line, sizeof(line), f)) { + for (i = 0, len = strlen(line); i < len && line[i] != ':'; i++) + ; + + if (i < len) + { + tag = g_strndup (line, i); + ordering = g_strndup (line+(i+1), len - i); + + enchant_broker_set_ordering (broker, tag, ordering); + + g_free (tag); + g_free (ordering); + } + } + + fclose (f); +} + +static void +enchant_load_provider_ordering (EnchantBroker * broker) +{ + GSList *conf_dirs, *iter; + + broker->provider_ordering = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + /* we want the user's dirs to show up last, so they override system dirs */ + conf_dirs = g_slist_reverse (enchant_get_conf_dirs ()); + for (iter = conf_dirs; iter; iter = iter->next) + { + char *ordering_file; + ordering_file = g_build_filename (iter->data, "enchant.ordering", NULL); + enchant_load_ordering_from_file (broker, ordering_file); + g_free (ordering_file); + } + + g_slist_foreach (conf_dirs, (GFunc)g_free, NULL); + g_slist_free (conf_dirs); +} + +static GSList * +enchant_get_ordered_providers (EnchantBroker * broker, + const char * const tag) +{ + EnchantProvider *provider; + GSList * list = NULL, * iter = NULL; + + char * ordering = NULL, ** tokens, *token; + size_t i; + + ordering = (char *)g_hash_table_lookup (broker->provider_ordering, (gpointer)tag); + if (!ordering) + ordering = (char *)g_hash_table_lookup (broker->provider_ordering, (gpointer)"*"); + + if (!ordering) + { + /* return an unordered copy of the list */ + for (iter = broker->provider_list; iter != NULL; iter = g_slist_next (iter)) + list = g_slist_append (list, iter->data); + return list; + } + + tokens = g_strsplit (ordering, ",", 0); + if (tokens) + { + for (i = 0; tokens[i]; i++) + { + token = g_strstrip(tokens[i]); + + for (iter = broker->provider_list; iter != NULL; iter = g_slist_next (iter)) + { + provider = (EnchantProvider*)iter->data; + + if (provider && !strcmp (token, (*provider->identify)(provider))) + list = g_slist_append (list, (gpointer)provider); + } + } + + g_strfreev (tokens); + } + + /* providers not in the list need to be appended at the end */ + for (iter = broker->provider_list; iter != NULL; iter = g_slist_next (iter)) + { + if (!g_slist_find (list, iter->data)) + list = g_slist_append (list, iter->data); + } + + return list; +} + +static void +enchant_dict_destroyed (gpointer data) +{ + EnchantDict *dict; + EnchantProvider *owner; + EnchantSession *session; + EnchantDictPrivateData *enchant_dict_private_data; + + g_return_if_fail (data); + + dict = (EnchantDict *) data; + enchant_dict_private_data = (EnchantDictPrivateData*)dict->enchant_private_data; + session = enchant_dict_private_data->session; + owner = session->provider; + + if (owner && owner->dispose_dict) + (*owner->dispose_dict) (owner, dict); + else if(session->is_pwl) + g_free (dict); + + g_free(enchant_dict_private_data); + + enchant_session_destroy (session); +} + +static void +enchant_provider_free (gpointer data, gpointer user_data) +{ + EnchantProvider *provider; + GModule *module; + + g_return_if_fail (data); + + provider = (EnchantProvider *) data; + module = (GModule *) provider->enchant_private_data; + + if (provider->dispose) + (*provider->dispose) (provider); + + /* close module only after invoking dispose */ + g_module_close (module); +} + +/** + * enchant_broker_init + * + * Returns: A new broker object capable of requesting + * dictionaries from + */ +ENCHANT_MODULE_EXPORT (EnchantBroker *) +enchant_broker_init (void) +{ + EnchantBroker *broker = NULL; + + g_return_val_if_fail (g_module_supported (), NULL); + +#ifdef ENABLE_BINRELOC + { + static gboolean binreloc_initialized = FALSE; + + if (!binreloc_initialized) + { + (void)gbr_init_lib (NULL); + binreloc_initialized = TRUE; + } + } +#endif + + broker = g_new0 (EnchantBroker, 1); + + broker->dict_map = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, enchant_dict_destroyed); + broker->params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + enchant_load_providers (broker); + enchant_load_provider_ordering (broker); + + return broker; +} + +/** + * enchant_broker_free + * @broker: A non-null #EnchantBroker + * + * Destroys the broker object. Must only be called once per broker init + */ +ENCHANT_MODULE_EXPORT (void) +enchant_broker_free (EnchantBroker * broker) +{ + guint n_remaining; + + g_return_if_fail (broker); + + n_remaining = g_hash_table_size (broker->dict_map); + if (n_remaining) + { + g_warning ("%u dictionaries weren't free'd.\n", n_remaining); + } + + /* will destroy any remaining dictionaries for us */ + g_hash_table_destroy (broker->dict_map); + g_hash_table_destroy (broker->provider_ordering); + g_hash_table_destroy (broker->params); + + g_slist_foreach (broker->provider_list, enchant_provider_free, NULL); + g_slist_free (broker->provider_list); + + enchant_broker_clear_error (broker); + + g_free (broker); +} + +/** + * enchant_broker_request_pwl_dict + * + * PWL is a personal wordlist file, 1 entry per line + * + * @pwl: A non-null pathname in the GLib file name encoding (UTF-8 on Windows) + * to the personal wordlist file + * + * Returns: An EnchantDict. This dictionary is reference counted. + */ +ENCHANT_MODULE_EXPORT (EnchantDict *) +enchant_broker_request_pwl_dict (EnchantBroker * broker, const char *const pwl) +{ + EnchantSession *session; + EnchantDictPrivateData *enchant_dict_private_data; + EnchantDict *dict = NULL; + + g_return_val_if_fail (broker, NULL); + g_return_val_if_fail (pwl && strlen(pwl), NULL); + + enchant_broker_clear_error (broker); + + dict = (EnchantDict*)g_hash_table_lookup (broker->dict_map, (gpointer) pwl); + if (dict) { + ((EnchantDictPrivateData*)dict->enchant_private_data)->reference_count++; + return dict; + } + + /* since the broker pwl file is a read/write file (there is no readonly dictionary associated) + * there is no need for complementary exclude file to add a word to. The word just needs to be + * removed from the broker pwl file + */ + session = enchant_session_new_with_pwl (NULL, pwl, NULL, "Personal Wordlist", TRUE); + if (!session) + { + broker->error = g_strdup_printf ("Couldn't open personal wordlist '%s'", pwl); + return NULL; + } + + session->is_pwl = 1; + + dict = g_new0 (EnchantDict, 1); + enchant_dict_private_data = g_new0 (EnchantDictPrivateData, 1); + enchant_dict_private_data->reference_count = 1; + enchant_dict_private_data->session = session; + dict->enchant_private_data = (void *)enchant_dict_private_data; + + + g_hash_table_insert (broker->dict_map, (gpointer)g_strdup (pwl), dict); + + return dict; +} + +static EnchantDict * +_enchant_broker_request_dict (EnchantBroker * broker, const char *const tag) +{ + EnchantDict * dict; + GSList * list; + GSList * listIter; + + dict = (EnchantDict*)g_hash_table_lookup (broker->dict_map, (gpointer) tag); + if (dict) { + ((EnchantDictPrivateData*)dict->enchant_private_data)->reference_count++; + return dict; + } + + list = enchant_get_ordered_providers (broker, tag); + for (listIter = list; listIter != NULL; listIter = g_slist_next (listIter)) + { + EnchantProvider * provider; + + provider = (EnchantProvider *) listIter->data; + + if (provider->request_dict) + { + dict = (*provider->request_dict) (provider, tag); + + if (dict) + { + EnchantSession *session; + EnchantDictPrivateData *enchant_dict_private_data; + + session = enchant_session_new (provider, tag); + enchant_dict_private_data = g_new0 (EnchantDictPrivateData, 1); + enchant_dict_private_data->reference_count = 1; + enchant_dict_private_data->session = session; + dict->enchant_private_data = (void *)enchant_dict_private_data; + g_hash_table_insert (broker->dict_map, (gpointer)g_strdup (tag), dict); + break; + } + } + } + + g_slist_free (list); + + return dict; +} + +/** + * enchant_broker_request_dict + * @broker: A non-null #EnchantBroker + * @tag: The non-null language tag you wish to request a dictionary for ("en_US", "de_DE", ...) + * + * Returns: An #EnchantDict, or %null if no suitable dictionary could be found. This dictionary is reference counted. + */ +ENCHANT_MODULE_EXPORT (EnchantDict *) +enchant_broker_request_dict (EnchantBroker * broker, const char *const tag) +{ + EnchantDict *dict = NULL; + char * normalized_tag; + + g_return_val_if_fail (broker, NULL); + g_return_val_if_fail (tag && strlen(tag), NULL); + + enchant_broker_clear_error (broker); + + normalized_tag = enchant_normalize_dictionary_tag (tag); + if(!enchant_is_valid_dictionary_tag(normalized_tag)) + { + enchant_broker_set_error (broker, "invalid tag character found"); + } + else if ((dict = _enchant_broker_request_dict (broker, normalized_tag)) == NULL) + { + char * iso_639_only_tag; + + iso_639_only_tag = enchant_iso_639_from_tag (normalized_tag); + + dict = _enchant_broker_request_dict (broker, iso_639_only_tag); + + g_free (iso_639_only_tag); + } + + g_free (normalized_tag); + + return dict; +} + +/** + * enchant_broker_describe + * @broker: A non-null #EnchantBroker + * @fn: A non-null #EnchantBrokerDescribeFn + * @user_data: Optional user-data + * + * Enumerates the Enchant providers and tells + * you some rudimentary information about them. + */ +ENCHANT_MODULE_EXPORT (void) +enchant_broker_describe (EnchantBroker * broker, + EnchantBrokerDescribeFn fn, + void * user_data) +{ + GSList *list; + EnchantProvider *provider; + GModule *module; + + const char * name, * desc, * file; + + g_return_if_fail (broker); + g_return_if_fail (fn); + + enchant_broker_clear_error (broker); + + for (list = broker->provider_list; list != NULL; list = g_slist_next (list)) + { + provider = (EnchantProvider *) list->data; + module = (GModule *) provider->enchant_private_data; + + name = (*provider->identify) (provider); + desc = (*provider->describe) (provider); + file = g_module_name (module); + + (*fn) (name, desc, file, user_data); + } +} + +/** + * enchant_broker_list_dicts + * @broker: A non-null #EnchantBroker + * @fn: A non-null #EnchantDictDescribeFn + * @user_data: Optional user-data + * + * Enumerates the dictionaries available from + * all Enchant providers. + */ +ENCHANT_MODULE_EXPORT (void) +enchant_broker_list_dicts (EnchantBroker * broker, + EnchantDictDescribeFn fn, + void * user_data) +{ + GSList *list; + GHashTable *tags; + + g_return_if_fail (broker); + g_return_if_fail (fn); + + tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + enchant_broker_clear_error (broker); + + for (list = broker->provider_list; list != NULL; list = g_slist_next (list)) + { + EnchantProvider *provider; + GModule *module; + + provider = (EnchantProvider *) list->data; + module = (GModule *) provider->enchant_private_data; + + if (provider->list_dicts) + { + const char * tag, * name, * desc, * file; + size_t n_dicts, i; + char ** dicts; + + dicts = (*provider->list_dicts) (provider, &n_dicts); + name = (*provider->identify) (provider); + desc = (*provider->describe) (provider); + file = g_module_name (module); + + for (i = 0; i < n_dicts; i++) + { + tag = dicts[i]; + if(enchant_is_valid_dictionary_tag(tag) && + !g_hash_table_lookup (tags, tag)) + { + g_hash_table_insert (tags, g_strdup (tag), GINT_TO_POINTER(TRUE)); + (*fn) (tag, name, desc, file, user_data); + } + } + + enchant_provider_free_string_list (provider, dicts); + } + } + + g_hash_table_destroy (tags); +} + +/** + * enchant_broker_free_dict + * @broker: A non-null #EnchantBroker + * @dict: A non-null #EnchantDict + * + * Releases the dictionary when you are done using it. Must only be called once per dictionary request + */ +ENCHANT_MODULE_EXPORT (void) +enchant_broker_free_dict (EnchantBroker * broker, EnchantDict * dict) +{ + EnchantSession * session; + EnchantDictPrivateData * dict_private_data; + + g_return_if_fail (broker); + g_return_if_fail (dict); + + enchant_broker_clear_error (broker); + + dict_private_data = (EnchantDictPrivateData*)dict->enchant_private_data; + dict_private_data->reference_count--; + if(dict_private_data->reference_count == 0) + { + session = dict_private_data->session; + + if (session->provider) + g_hash_table_remove (broker->dict_map, session->language_tag); + else + g_hash_table_remove (broker->dict_map, session->personal_filename); + } +} + +static int +_enchant_provider_dictionary_exists (EnchantProvider * provider, + const char * const tag) +{ + int exists = 0; + + if (provider->dictionary_exists) + { + exists = (*provider->dictionary_exists) (provider, tag); + } + else if (provider->list_dicts) + { + size_t n_dicts, i; + char ** dicts; + + dicts = (*provider->list_dicts) (provider, &n_dicts); + + for (i = 0; (i < n_dicts) && !exists; i++) + { + if (!strcmp(dicts[i], tag)) + exists = 1; + } + + enchant_provider_free_string_list (provider, dicts); + } + else if (provider->request_dict) + { + EnchantDict *dict; + + dict = (*provider->request_dict) (provider, tag); + if (dict) + { + if (provider->dispose_dict) + (*provider->dispose_dict) (provider, dict); + exists = 1; + } + } + + return exists; +} + +static int +_enchant_broker_dict_exists (EnchantBroker * broker, + const char * const tag) +{ + GSList * list; + + /* don't query the providers if it is an empty string */ + if (tag == NULL || *tag == '\0') { + return 0; + } + + /* don't query the providers if we can just do a quick map lookup */ + if (g_hash_table_lookup (broker->dict_map, (gpointer) tag) != NULL) { + return 1; + } + + for (list = broker->provider_list; list != NULL; list = g_slist_next (list)) + { + EnchantProvider * provider; + + provider = (EnchantProvider *) list->data; + + if (_enchant_provider_dictionary_exists (provider, tag)) + { + return 1; + } + } + + return 0; +} + +/** + * enchant_broker_dict_exists + * @broker: A non-null #EnchantBroker + * @tag: The non-null language tag you wish to request a dictionary for ("en_US", "de_DE", ...) + * + * Return existance of the requested dictionary (1 == true, 0 == false) + */ +ENCHANT_MODULE_EXPORT (int) +enchant_broker_dict_exists (EnchantBroker * broker, + const char * const tag) +{ + char * normalized_tag; + int exists = 0; + + g_return_val_if_fail (broker, 0); + g_return_val_if_fail (tag && strlen(tag), 0); + + enchant_broker_clear_error (broker); + + normalized_tag = enchant_normalize_dictionary_tag (tag); + + if(!enchant_is_valid_dictionary_tag(normalized_tag)) + { + enchant_broker_set_error (broker, "invalid tag character found"); + } + else if ((exists = _enchant_broker_dict_exists (broker, normalized_tag)) == 0) + { + char * iso_639_only_tag; + + iso_639_only_tag = enchant_iso_639_from_tag (normalized_tag); + + if (strcmp (normalized_tag, iso_639_only_tag) != 0) + { + exists = _enchant_broker_dict_exists (broker, iso_639_only_tag); + } + + g_free (iso_639_only_tag); + } + + g_free (normalized_tag); + return exists; +} + +/** + * enchant_broker_set_ordering + * @broker: A non-null #EnchantBroker + * @tag: A non-null language tag (en_US) + * @ordering: A non-null ordering (aspell,myspell,ispell,uspell,hspell) + * + * Declares a preference of dictionaries to use for the language + * described/referred to by @tag. The ordering is a comma delimited + * list of provider names. As a special exception, the "*" tag can + * be used as a language tag to declare a default ordering for any + * language that does not explictly declare an ordering. + */ +ENCHANT_MODULE_EXPORT (void) +enchant_broker_set_ordering (EnchantBroker * broker, + const char * const tag, + const char * const ordering) +{ + char * tag_dupl; + char * ordering_dupl; + + g_return_if_fail (broker); + g_return_if_fail (tag && strlen(tag)); + g_return_if_fail (ordering && strlen(ordering)); + + enchant_broker_clear_error (broker); + + tag_dupl = enchant_normalize_dictionary_tag (tag); + + ordering_dupl = g_strdup (ordering); + ordering_dupl = g_strstrip (ordering_dupl); + + if (tag_dupl && strlen(tag_dupl) && + ordering_dupl && strlen(ordering_dupl)) + { + /* we will free ordering_dupl && tag_dupl when the hash is destroyed */ + g_hash_table_insert (broker->provider_ordering, (gpointer)tag_dupl, + (gpointer)(ordering_dupl)); + } + else + { + g_free (tag_dupl); + g_free (ordering_dupl); + } +} + +/** + * enchant_provider_set_error + * @provider: A non-null provider + * @err: A non-null error message + * + * Sets the current runtime error to @err. This API is private to + * the providers. + */ +ENCHANT_MODULE_EXPORT(void) +enchant_provider_set_error (EnchantProvider * provider, const char * const err) +{ + EnchantBroker * broker; + + g_return_if_fail (provider); + g_return_if_fail (err); + g_return_if_fail (g_utf8_validate(err, -1, NULL)); + + broker = provider->owner; + g_return_if_fail (broker); + + enchant_broker_set_error (broker, err); +} + +/** + * enchant_broker_get_error + * @broker: A non-null broker + * + * Returns a const char string or NULL describing the last exception in UTF8 encoding. + * WARNING: error is transient and is likely cleared as soon as the + * next broker operation happens + */ +ENCHANT_MODULE_EXPORT(char *) +enchant_broker_get_error (EnchantBroker * broker) +{ + g_return_val_if_fail (broker, NULL); + + return broker->error; +} + +/* private. returned string should be free'd with g_free */ +ENCHANT_MODULE_EXPORT(char *) +enchant_get_user_language(void) +{ + char * locale = NULL; + +#if defined(G_OS_WIN32) + if(!locale) + locale = g_win32_getlocale (); +#endif + + if(!locale) + locale = g_strdup (g_getenv ("LANG")); + +#if defined(HAVE_LC_MESSAGES) + if(!locale) + locale = g_strdup (setlocale (LC_MESSAGES, NULL)); +#endif + + if(!locale) + locale = g_strdup (setlocale (LC_ALL, NULL)); + + if(!locale || strcmp(locale, "C") == 0) { + g_free(locale); + locale = g_strdup("en"); + } + + return locale; +} + + +/** + * enchant_get_prefix_dir + * + * Returns a string giving the location of the base directory + * of the enchant installation. This corresponds roughly to + * the --prefix option given to ./configure when enchant is + * compiled, except it is determined at runtime based on the location + * of the enchant library. + * + * Returns: the prefix dir if it can be determined, or %null otherwise. Must be free'd. + * + * This API is private to the providers. + * + */ +ENCHANT_MODULE_EXPORT (char *) +enchant_get_prefix_dir(void) +{ + char * prefix = NULL; + +#ifdef _WIN32 + if (!prefix) { + /* Dynamically locate library and return containing directory */ + WCHAR dll_path[MAX_PATH]; + + if(GetModuleFileNameW(s_hModule,dll_path,MAX_PATH)) + { + gchar* utf8_dll_path = g_utf16_to_utf8 (dll_path, -1, NULL, NULL, NULL); + prefix = g_path_get_dirname(utf8_dll_path); + g_free(utf8_dll_path); + /* Strip off "bin" subfolder if present */ + if (strlen(prefix) >=6 && + G_IS_DIR_SEPARATOR(prefix[strlen(prefix)-4]) && + g_ascii_strcasecmp(prefix+strlen(prefix)-3, "bin") == 0) + prefix[strlen(prefix)-4] = '\0'; + } + } +#endif + +#if defined(ENABLE_BINRELOC) + if (!prefix) { + /* Use standard binreloc PREFIX macro */ + prefix = gbr_find_prefix(NULL); + } +#endif + +#if defined(ENCHANT_PREFIX_DIR) + if (!prefix) { + prefix = g_strdup (ENCHANT_PREFIX_DIR); + } +#endif + + return prefix; +} + +ENCHANT_MODULE_EXPORT(char *) +enchant_broker_get_param (EnchantBroker * broker, const char * const param_name) +{ + g_return_val_if_fail (broker, NULL); + g_return_val_if_fail (param_name && *param_name, NULL); + + return g_hash_table_lookup (broker->params, param_name); +} + +ENCHANT_MODULE_EXPORT(void) +enchant_broker_set_param (EnchantBroker * broker, const char * const param_name, const char * const param_value) +{ + g_return_if_fail (broker); + g_return_if_fail (param_name && *param_name); + + if (param_value == NULL || *param_value == '\0') + g_hash_table_remove (broker->params, param_name); + else + g_hash_table_insert (broker->params, g_strdup (param_name), g_strdup (param_value)); +} + +ENCHANT_MODULE_EXPORT (GSList *) +enchant_get_dirs_from_param (EnchantBroker * broker, const char * const param_name) +{ + const char *param_value; + char **tokens; + GSList *dirs = NULL; + + param_value = enchant_broker_get_param (broker, param_name); + if (param_value == NULL) + return NULL; + +#ifdef _WIN32 + tokens = g_strsplit (param_value, ";", 0); +#else + tokens = g_strsplit (param_value, ":", 0); +#endif + if (tokens != NULL) { + int i; + for (i = 0; tokens[i]; i++) + { + char *token = g_strstrip(tokens[i]); + dirs = g_slist_append (dirs, g_strdup (token)); + } + + g_strfreev (tokens); + } + + return dirs; +} + +ENCHANT_MODULE_EXPORT(char *) +enchant_get_version (void) { + return ENCHANT_VERSION_STRING; +} diff --git a/src/enchant.h b/src/enchant.h new file mode 100644 index 0000000..bcc2fa4 --- /dev/null +++ b/src/enchant.h @@ -0,0 +1,183 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003 Dom Lachowicz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#ifndef ENCHANT_H +#define ENCHANT_H + +/* for size_t, ssize_t */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#ifdef _ENCHANT_BUILD +#define ENCHANT_MODULE_EXPORT(x) __declspec(dllexport) x +#else +#define ENCHANT_MODULE_EXPORT(x) __declspec(dllimport) x +#endif +#else +#define ENCHANT_MODULE_EXPORT(x) x +#endif + +typedef struct str_enchant_broker EnchantBroker; +typedef struct str_enchant_dict EnchantDict; + +/* const */ +ENCHANT_MODULE_EXPORT (char *) + enchant_get_version (void); + +ENCHANT_MODULE_EXPORT (EnchantBroker *) + enchant_broker_init (void); +ENCHANT_MODULE_EXPORT (void) + enchant_broker_free (EnchantBroker * broker); + +ENCHANT_MODULE_EXPORT (EnchantDict *) + enchant_broker_request_dict (EnchantBroker * broker, const char *const tag); +ENCHANT_MODULE_EXPORT (EnchantDict *) + enchant_broker_request_pwl_dict (EnchantBroker * broker, const char *const pwl); +ENCHANT_MODULE_EXPORT (void) + enchant_broker_free_dict (EnchantBroker * broker, EnchantDict * dict); +ENCHANT_MODULE_EXPORT (int) + enchant_broker_dict_exists (EnchantBroker * broker, + const char * const tag); +ENCHANT_MODULE_EXPORT (void) + enchant_broker_set_ordering (EnchantBroker * broker, + const char * const tag, + const char * const ordering); +/* const */ +ENCHANT_MODULE_EXPORT(char *) + enchant_broker_get_error (EnchantBroker * broker); + +/* const */ +ENCHANT_MODULE_EXPORT(char *) + enchant_broker_get_param (EnchantBroker * broker, const char * const param_name); +ENCHANT_MODULE_EXPORT(void) + enchant_broker_set_param (EnchantBroker * broker, const char * const param_name, const char * const param_value); + +/** + * EnchantBrokerDescribeFn + * @provider_name: The provider's identifier, such as "ispell" or "aspell" in UTF8 encoding + * @provider_desc: A description of the provider, such as "Aspell 0.53" in UTF8 encoding + * @provider_dll_file: The provider's DLL filename in Glib file encoding (UTF8 on Windows) + * @user_data: Supplied user data, or %null if you don't care + * + * Callback used to enumerate and describe Enchant's various providers + */ +typedef void (*EnchantBrokerDescribeFn) (const char * const provider_name, + const char * const provider_desc, + const char * const provider_dll_file, + void * user_data); + +ENCHANT_MODULE_EXPORT (void) + enchant_broker_describe (EnchantBroker * broker, + EnchantBrokerDescribeFn fn, + void * user_data); + +ENCHANT_MODULE_EXPORT (int) + enchant_dict_check (EnchantDict * dict, const char *const word, ssize_t len); +ENCHANT_MODULE_EXPORT (char **) + enchant_dict_suggest (EnchantDict * dict, const char *const word, + ssize_t len, size_t * out_n_suggs); +ENCHANT_MODULE_EXPORT (void) + enchant_dict_add (EnchantDict * dict, const char *const word, + ssize_t len); +ENCHANT_MODULE_EXPORT (void) + enchant_dict_add_to_session (EnchantDict * dict, const char *const word, + ssize_t len); +ENCHANT_MODULE_EXPORT (void) + enchant_dict_remove (EnchantDict * dict, const char *const word, + ssize_t len); +ENCHANT_MODULE_EXPORT (void) + enchant_dict_remove_from_session (EnchantDict * dict, const char *const word, + ssize_t len); +ENCHANT_MODULE_EXPORT (int) + enchant_dict_is_added (EnchantDict * dict, const char *const word, + ssize_t len); +ENCHANT_MODULE_EXPORT (int) + enchant_dict_is_removed (EnchantDict * dict, const char *const word, + ssize_t len); +ENCHANT_MODULE_EXPORT (void) + enchant_dict_store_replacement (EnchantDict * dict, + const char *const mis, ssize_t mis_len, + const char *const cor, ssize_t cor_len); +ENCHANT_MODULE_EXPORT (void) + enchant_dict_free_string_list (EnchantDict * dict, char **string_list); + +#ifndef ENCHANT_DISABLE_DEPRECATED +ENCHANT_MODULE_EXPORT (void) + enchant_dict_free_suggestions (EnchantDict * dict, char **suggestions); +ENCHANT_MODULE_EXPORT (void) + enchant_dict_add_to_personal (EnchantDict * dict, const char *const word, + ssize_t len); +ENCHANT_MODULE_EXPORT (void) + enchant_dict_add_to_pwl (EnchantDict * dict, const char *const word, + ssize_t len); +ENCHANT_MODULE_EXPORT (int) + enchant_dict_is_in_session (EnchantDict * dict, const char *const word, + ssize_t len); +#endif /* ENCHANT_DISABLE_DEPRECATED */ + +/* const */ +ENCHANT_MODULE_EXPORT(char *) + enchant_dict_get_error (EnchantDict * dict); + +/** + * EnchantDictDescribeFn + * @lang_tag: The dictionary's language tag (eg: en_US, de_AT, ...) + * @provider_name: The provider's name (eg: Aspell) in UTF8 encoding + * @provider_desc: The provider's description (eg: Aspell 0.50.3) in UTF8 encoding + * @provider_file: The DLL/SO where this dict's provider was loaded from in Glib file encoding (UTF8 on Windows) + * @user_data: Supplied user data, or %null if you don't care + * + * Callback used to describe an individual dictionary + */ +typedef void (*EnchantDictDescribeFn) (const char * const lang_tag, + const char * const provider_name, + const char * const provider_desc, + const char * const provider_file, + void * user_data); + +ENCHANT_MODULE_EXPORT (void) + enchant_dict_describe (EnchantDict * dict, + EnchantDictDescribeFn fn, + void * user_data); + +ENCHANT_MODULE_EXPORT (void) + enchant_broker_list_dicts (EnchantBroker * broker, + EnchantDictDescribeFn fn, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif /* ENCHANT_H */ diff --git a/src/enchant.i b/src/enchant.i new file mode 100644 index 0000000..263092a --- /dev/null +++ b/src/enchant.i @@ -0,0 +1,8 @@ +%module enchant +%{ +#include "enchant.h" +%} + +typedef unsigned long size_t; + +%include "enchant.h" diff --git a/src/enchant_cocoa.h b/src/enchant_cocoa.h new file mode 100644 index 0000000..1b86417 --- /dev/null +++ b/src/enchant_cocoa.h @@ -0,0 +1,50 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2004 Francis James Franklin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#ifndef ENCHANT_COCOA_H +#define ENCHANT_COCOA_H + +#import + +@interface EnchantResourceProvider : NSObject +{ + NSString * ModuleFolder; + NSString * ConfigFolder; +} ++ (EnchantResourceProvider *)instance; + +- (id)init; +- (void)dealloc; + +- (const char *)moduleFolder; +- (const char *)configFolder; +@end + +#endif /* ENCHANT_COCOA_H */ diff --git a/src/enchant_cocoa.m b/src/enchant_cocoa.m new file mode 100644 index 0000000..31dba02 --- /dev/null +++ b/src/enchant_cocoa.m @@ -0,0 +1,83 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2004 Francis James Franklin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#import "enchant_cocoa.h" + +static EnchantResourceProvider * s_instance = 0; + +@implementation EnchantResourceProvider + ++ (EnchantResourceProvider *)instance +{ + if (!s_instance) + { + s_instance = [[EnchantResourceProvider alloc] init]; + } + return s_instance; +} + +- (id)init +{ + if (self = [super init]) + { + ModuleFolder = [[NSBundle bundleForClass:[self class]] resourcePath]; + [ModuleFolder retain]; + + ConfigFolder = [[NSString alloc] initWithUTF8String:"/Library/Application Support/Enchant"]; + } + return self; +} + +- (void)dealloc +{ + if (ModuleFolder) + { + [ModuleFolder release]; + ModuleFolder = 0; + } + if (ConfigFolder) + { + [ConfigFolder release]; + ConfigFolder = 0; + } + [super dealloc]; +} + +- (const char *)moduleFolder +{ + return [ModuleFolder UTF8String]; +} + +- (const char *)configFolder +{ + return [ConfigFolder UTF8String]; +} + +@end diff --git a/src/hspell/.cvsignore b/src/hspell/.cvsignore new file mode 100644 index 0000000..913f7fb --- /dev/null +++ b/src/hspell/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +hspell_provider.lo +libenchant_hspell.la +.deps +.libs diff --git a/src/hspell/Makefile.am b/src/hspell/Makefile.am new file mode 100644 index 0000000..28560c3 --- /dev/null +++ b/src/hspell/Makefile.am @@ -0,0 +1,15 @@ +if WITH_HSPELL +target_lib = libenchant_hspell.la +else +target_lib = +endif + +AM_CPPFLAGS=-I$(top_srcdir)/src $(ENCHANT_CFLAGS) $(CC_WARN_CFLAGS) $(HSPELL_CFLAGS) -D_ENCHANT_BUILD=1 + +hspell_LTLIBRARIES = $(target_lib) +hspelldir= $(libdir)/enchant + +libenchant_hspell_la_LIBADD= $(ENCHANT_LIBS) $(HSPELL_LIBS) $(top_builddir)/src/libenchant.la +libenchant_hspell_la_LDFLAGS = -module -avoid-version -no-undefined +libenchant_hspell_la_SOURCES = hspell_provider.c +libenchant_hspell_lalibdir=$(libdir)/enchant diff --git a/src/hspell/README b/src/hspell/README new file mode 100644 index 0000000..80aa332 --- /dev/null +++ b/src/hspell/README @@ -0,0 +1,8 @@ +This is an Hspell Enchant Backend. + By Yaacov Zamir Friday, December 05 2003 + +You can get Hspell from: + http://www.ivrix.org.il/projects/spell-checker/ + +Hspell was written by Nadav Har'El and Dan Kenigsberg. + diff --git a/src/hspell/hspell_provider.c b/src/hspell/hspell_provider.c new file mode 100644 index 0000000..a4a439e --- /dev/null +++ b/src/hspell/hspell_provider.c @@ -0,0 +1,308 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003 Yaacov Zamir + * Copyright (C) 2004 Dom Lachowicz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz and Yaacov Zamir + * give permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#include +#include +#include + +#include +#include + +#include "enchant.h" +#include "enchant-provider.h" + +ENCHANT_PLUGIN_DECLARE ("Hspell") + +/** + * hspell helper functions + */ + +/** + * is hebrew + * return TRUE if iso hebrew ( must be null terminated ) + */ +static int is_hebrew (const char *const iso_word, gsize len) +{ + int i; + + for ( i = 0; (i < len) && (iso_word[i]); i++ ) + { + /* if not a hebrew alphabet or " ` ' using iso-8859-8 encoding */ + if ( (iso_word[i] < (char)224 || iso_word[i] > (char)250 ) && /* alef to tav */ + (iso_word[i] < (char)146 || iso_word[i] > (char)148 ) && /* ` etc... */ + ( iso_word[i] !=(char)34 ) && /* " */ + ( iso_word[i] !=(char)39 ) ) /* ' */ + return FALSE; + } + + return TRUE; +} + +/** + * convert struct corlist to **char + * the **char must be g_freed + */ +static gchar ** +corlist2strv (struct corlist *cl, size_t nb_sugg) +{ + int i; + gsize len; + char **sugg_arr = NULL; + const char *sugg; + + if (nb_sugg > 0) + { + sugg_arr = g_new0 (char *, nb_sugg + 1); + for (i = 0; i < nb_sugg; i++) + { + sugg = corlist_str (cl, i); + if (sugg) + sugg_arr[i] = g_convert (sugg, + strlen (sugg), + "utf-8", "iso8859-8", NULL, &len, NULL); + } + } + + return sugg_arr; +} + +/** + * end of helper functions + */ + +static int +hspell_dict_check (EnchantDict * me, const char *const word, size_t len) +{ + int res; + char *iso_word; + gsize length; + int preflen; + struct dict_radix *hspell_dict; + + hspell_dict = (struct dict_radix *)me->user_data; + + /* convert to iso 8859-8 */ + iso_word = g_convert (word, len, "iso8859-8", "utf-8", NULL, &length, NULL); + + /* check if hebrew ( if not hebrew give it the benefit of a doubt ) */ + if (iso_word == NULL || !is_hebrew (iso_word, length)) + { + if (iso_word) + g_free (iso_word); + return FALSE; + } + + /* check */ + res = hspell_check_word (hspell_dict, iso_word, &preflen); + + /* if not correct try gimatria */ + if (res != 1) + { + res = hspell_is_canonic_gimatria (iso_word); + if (res != 0) + res = 1; + } + + /* free the word */ + g_free (iso_word); + + return (res != 1); +} + +static char ** +hspell_dict_suggest (EnchantDict * me, const char *const word, + size_t len, size_t * out_n_suggs) +{ + + int res; + gsize length; + char *iso_word; + char **sugg_arr = NULL; + struct corlist cl; + struct dict_radix *hspell_dict; + + hspell_dict = (struct dict_radix *)me->user_data; + + /* convert to iso 8859-8 */ + iso_word = g_convert (word, len, "iso8859-8", "utf-8", NULL, &length, NULL); + + /* check if hebrew ( if not hebrew cant do anything ) */ + if (iso_word == NULL || !is_hebrew (iso_word, length)) + { + if (iso_word != NULL) + g_free (iso_word); + return NULL; + } + + /* get suggestions */ + corlist_init (&cl); + hspell_trycorrect (hspell_dict, iso_word, &cl); + + /* set size of list */ + *out_n_suggs = corlist_n (&cl); + + /* convert suggestion list to strv list */ + sugg_arr = corlist2strv (&cl, *out_n_suggs); + + /* free the list */ + corlist_free (&cl); + + /* free the word */ + g_free (iso_word); + + return sugg_arr; +} + +static EnchantDict * +hspell_provider_request_dict (EnchantProvider * me, const char *const tag) +{ + EnchantDict *dict; + int dict_flag = 0; + struct dict_radix *hspell_dict = NULL; + + if(!((strlen(tag) >= 2) && tag[0] == 'h' && tag[1] == 'e')) + return NULL; + + /* try to set a new session */ + dict_flag = hspell_init (&hspell_dict, HSPELL_OPT_DEFAULT); + + if (dict_flag != 0 || !hspell_dict) + { + enchant_provider_set_error (me, "can't create new dict."); + return NULL; + } + + dict = g_new0 (EnchantDict, 1); + dict->user_data = (void *) hspell_dict; + dict->check = hspell_dict_check; + dict->suggest = hspell_dict_suggest; + + return dict; +} + +static void +hspell_provider_dispose_dict (EnchantProvider * me, EnchantDict * dict) +{ + struct dict_radix *hspell_dict; + + hspell_dict = (struct dict_radix *)dict->user_data; + + /* deleting the dict is not posible on hspell ver. < v.0.8 */ +#if (HSPELL_VERSION_MAJOR > 0) || (HSPELL_VERSION_MINOR >= 8) + hspell_uninit (hspell_dict); +#endif + g_free (dict); +} + +/* test for the existence of, then return $prefix/share/hspell/hebrew.wgz */ + +static char ** +hspell_provider_list_dicts (EnchantProvider * me, + size_t * out_n_dicts) +{ + const char * dictionary_path; + char ** out_list = NULL; + *out_n_dicts = 0; + + dictionary_path = hspell_get_dictionary_path(); + + if(dictionary_path && *dictionary_path && g_file_test (dictionary_path, G_FILE_TEST_EXISTS)) { + *out_n_dicts = 1; + + out_list = g_new0 (char *, 2); + + out_list[0] = g_strdup ("he"); + } + + return out_list; +} + +static int +hspell_provider_dictionary_exists (struct str_enchant_provider * me, + const char *const tag) +{ + (void)me; + return (!strcmp ("he", tag) || !strcmp ("he_IL", tag)); +} + +static void +hspell_provider_free_string_list (EnchantProvider * me, char **str_list) +{ + g_strfreev (str_list); +} + +static void +hspell_provider_dispose (EnchantProvider * me) +{ + g_free (me); +} + +static const char * +hspell_provider_identify (EnchantProvider * me) +{ + return "hspell"; +} + +static const char * +hspell_provider_describe (EnchantProvider * me) +{ + return "Hspell Provider"; +} + +#ifdef __cplusplus +extern "C" +{ +#endif + +ENCHANT_MODULE_EXPORT (EnchantProvider *) + init_enchant_provider (void); + +EnchantProvider * +init_enchant_provider (void) +{ + EnchantProvider *provider; + + provider = g_new0 (EnchantProvider, 1); + provider->dispose = hspell_provider_dispose; + provider->request_dict = hspell_provider_request_dict; + provider->dispose_dict = hspell_provider_dispose_dict; + provider->dictionary_exists = hspell_provider_dictionary_exists; + provider->identify = hspell_provider_identify; + provider->describe = hspell_provider_describe; + provider->list_dicts = hspell_provider_list_dicts; + provider->free_string_list = hspell_provider_free_string_list; + + return provider; +} + +#ifdef __cplusplus +} +#endif diff --git a/src/ispell/.cvsignore b/src/ispell/.cvsignore new file mode 100644 index 0000000..1158401 --- /dev/null +++ b/src/ispell/.cvsignore @@ -0,0 +1,12 @@ +Makefile.in +Makefile +correct.lo +good.lo +hash.lo +ispell_checker.lo +libenchant_ispell.la +lookup.lo +makedent.lo +tgood.lo +.deps +.libs diff --git a/src/ispell/Makefile.am b/src/ispell/Makefile.am new file mode 100644 index 0000000..48b3c62 --- /dev/null +++ b/src/ispell/Makefile.am @@ -0,0 +1,27 @@ +if WITH_ISPELL +target_lib = libenchant_ispell.la +else +target_lib = +endif + +INCLUDES=-I$(top_srcdir)/src $(ENCHANT_CFLAGS) $(CXX_WARN_CFLAGS) $(ISPELL_CFLAGS) -D_ENCHANT_BUILD=1 + +ispell_LTLIBRARIES = $(target_lib) +ispelldir= $(libdir)/enchant + +libenchant_ispell_lalibdir=$(libdir)/enchant +libenchant_ispell_la_LIBADD= $(ENCHANT_LIBS) $(top_builddir)/src/libenchant.la +libenchant_ispell_la_LDFLAGS = -module -avoid-version -no-undefined +libenchant_ispell_la_SOURCES = \ + correct.cpp \ + good.cpp \ + hash.cpp \ + ispell_checker.cpp \ + ispell_checker.h \ + ispell_def.h \ + ispell.h \ + lookup.cpp \ + makedent.cpp \ + msgs.h \ + sp_spell.h \ + tgood.cpp diff --git a/src/ispell/README b/src/ispell/README new file mode 100644 index 0000000..9eca2a6 --- /dev/null +++ b/src/ispell/README @@ -0,0 +1,22 @@ +Ispell Enchant Backend + +Ispell is as old as time. I'll bore you with the history lesson - let it +suffice to say that there are many forks of Ispell floating around there. + +This is based off of work done by myself (Dom) and others inside of AbiWord +(http://www.abisource.com). It is a port of Ispell to C++, some other general +modifications, plus it's stateful (no static/global data). These are all good +things. + +The current version of enchant looks for dictionary files in 2 places: + +1) Global directory - this is $prefix/share/enchant/ispell/ +2) User-local directory - this is $home/.enchant/ispell/ + +The user's dictionaries, if present, always take precedence over the global +ones. + +You can grab dictionaries compatible with this version of Ispell from: + +http://www.abisource.com/~fjf/ +http://sourceforge.net/project/showfiles.php?group_id=15518&release_id=84373 diff --git a/src/ispell/correct.cpp b/src/ispell/correct.cpp new file mode 100644 index 0000000..2530ddd --- /dev/null +++ b/src/ispell/correct.cpp @@ -0,0 +1,917 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * correct.c - Routines to manage the higher-level aspects of spell-checking + * + * This code originally resided in ispell.c, but was moved here to keep + * file sizes smaller. + * + * Copyright (c), 1983, by Pace Willisson + * + * Copyright 1992, 1993, Geoff Kuenning, Granada Hills, CA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All modifications to the source code must be clearly marked as + * such. Binary redistributions based on modified source code + * must be clearly marked as modified versions in the documentation + * and/or other materials provided with the distribution. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by Geoff Kuenning and + * other unpaid contributors. + * 5. The name of Geoff Kuenning may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING 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 GEOFF KUENNING 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. + */ + +/* + * $Log$ + * Revision 1.4 2003/08/14 17:51:26 dom + * update license - exception clause should be Lesser GPL + * + * Revision 1.3 2003/07/28 20:40:25 dom + * fix up the license clause, further win32-registry proof some directory getting functions + * + * Revision 1.2 2003/07/16 22:52:35 dom + * LGPL + exception license + * + * Revision 1.1 2003/07/15 01:15:04 dom + * ispell enchant backend + * + * Revision 1.2 2003/01/29 05:50:11 hippietrail + * + * Fixed my mess in EncodingManager. + * Changed many C casts to C++ casts. + * + * Revision 1.1 2003/01/24 05:52:31 hippietrail + * + * Refactored ispell code. Old ispell global variables had been put into + * an allocated structure, a pointer to which was passed to many functions. + * I have now made all such functions and variables private members of the + * ISpellChecker class. It was C OO, now it's C++ OO. + * + * I've fixed the makefiles and tested compilation but am unable to test + * operation. Please back out my changes if they cause problems which + * are not obvious or easy to fix. + * + * Revision 1.7 2002/09/19 05:31:15 hippietrail + * + * More Ispell cleanup. Conditional globals and DEREF macros are removed. + * K&R function declarations removed, converted to Doxygen style comments + * where possible. No code has been changed (I hope). Compiles for me but + * unable to test. + * + * Revision 1.6 2002/09/17 03:03:28 hippietrail + * + * After seeking permission on the developer list I've reformatted all the + * spelling source which seemed to have parts which used 2, 3, 4, and 8 + * spaces for tabs. It should all look good with our standard 4-space + * tabs now. + * I've concentrated just on indentation in the actual code. More prettying + * could be done. + * * NO code changes were made * + * + * Revision 1.5 2002/09/13 17:20:12 mpritchett + * Fix more warnings for Linux build + * + * Revision 1.4 2002/03/06 08:27:16 fjfranklin + * o Only activate compound handling when the hash file says so (Per Larsson) + * + * Revision 1.3 2001/05/14 09:52:50 hub + * Removed newMain.c from GNUmakefile.am + * + * C++ comments are not C comment. Changed to C comments + * + * Revision 1.2 2001/05/12 16:05:42 thomasf + * Big pseudo changes to ispell to make it pass around a structure rather + * than rely on all sorts of gloabals willy nilly here and there. Also + * fixed our spelling class to work with accepting suggestions once more. + * This code is dirty, gross and ugly (not to mention still not supporting + * multiple hash sized just yet) but it works on my machine and will no + * doubt break other machines. + * + * Revision 1.1 2001/04/15 16:01:24 tomas_f + * moving to spell/xp + * + * Revision 1.2 1999/10/05 16:17:28 paul + * Fixed build, and other tidyness. + * Spell dialog enabled by default, with keyboard binding of F7. + * + * Revision 1.1 1999/09/29 23:33:32 justin + * Updates to the underlying ispell-based code to support suggested corrections. + * + * Revision 1.59 1995/08/05 23:19:43 geoff + * Fix a bug that caused offsets for long lines to be confused if the + * line started with a quoting uparrow. + * + * Revision 1.58 1994/11/02 06:56:00 geoff + * Remove the anyword feature, which I've decided is a bad idea. + * + * Revision 1.57 1994/10/26 05:12:39 geoff + * Try boundary characters when inserting or substituting letters, except + * (naturally) at word boundaries. + * + * Revision 1.56 1994/10/25 05:46:30 geoff + * Fix an assignment inside a conditional that could generate spurious + * warnings (as well as being bad style). Add support for the FF_ANYWORD + * option. + * + * Revision 1.55 1994/09/16 04:48:24 geoff + * Don't pass newlines from the input to various other routines, and + * don't assume that those routines leave the input unchanged. + * + * Revision 1.54 1994/09/01 06:06:41 geoff + * Change erasechar/killchar to uerasechar/ukillchar to avoid + * shared-library problems on HP systems. + * + * Revision 1.53 1994/08/31 05:58:38 geoff + * Add code to handle extremely long lines in -a mode without splitting + * words or reporting incorrect offsets. + * + * Revision 1.52 1994/05/25 04:29:24 geoff + * Fix a bug that caused line widths to be calculated incorrectly when + * displaying lines containing tabs. Fix a couple of places where + * characters were sign-extended incorrectly, which could cause 8-bit + * characters to be displayed wrong. + * + * Revision 1.51 1994/05/17 06:44:05 geoff + * Add support for controlled compound formation and the COMPOUNDONLY + * option to affix flags. + * + * Revision 1.50 1994/04/27 05:20:14 geoff + * Allow compound words to be formed from more than two components + * + * Revision 1.49 1994/04/27 01:50:31 geoff + * Add support to correctly capitalize words generated as a result of a + * missing-space suggestion. + * + * Revision 1.48 1994/04/03 23:23:02 geoff + * Clean up the code in missingspace() to be a bit simpler and more + * efficient. + * + * Revision 1.47 1994/03/15 06:24:23 geoff + * Fix the +/-/~ commands to be independent. Allow the + command to + * receive a suffix which is a deformatter type (currently hardwired to + * be either tex or nroff/troff). + * + * Revision 1.46 1994/02/21 00:20:03 geoff + * Fix some bugs that could cause bad displays in the interaction between + * TeX parsing and string characters. Show_char now will not overrun + * the inverse-video display area by accident. + * + * Revision 1.45 1994/02/14 00:34:51 geoff + * Fix correct to accept length parameters for ctok and itok, so that it + * can pass them to the to/from ichar routines. + * + * Revision 1.44 1994/01/25 07:11:22 geoff + * Get rid of all old RCS log lines in preparation for the 3.1 release. + * + */ + +#include +#include +#include +#include "ispell_checker.h" +#include "msgs.h" + +/* +extern void upcase P ((ichar_t * string)); +extern void lowcase P ((ichar_t * string)); +extern ichar_t * strtosichar P ((char * in, int canonical)); + +int compoundflag = COMPOUND_CONTROLLED; +*/ + +/* + * \param a + * \param b + * \param canonical NZ for canonical string chars + * + * \return + */ +int +ISpellChecker::casecmp (char *a, char *b, int canonical) +{ + register ichar_t * ap; + register ichar_t * bp; + ichar_t inta[INPUTWORDLEN + 4 * MAXAFFIXLEN + 4]; + ichar_t intb[INPUTWORDLEN + 4 * MAXAFFIXLEN + 4]; + + strtoichar (inta, a, sizeof inta, canonical); + strtoichar (intb, b, sizeof intb, canonical); + for (ap = inta, bp = intb; *ap != 0; ap++, bp++) + { + if (*ap != *bp) + { + if (*bp == '\0') + return m_hashheader.sortorder[*ap]; + else if (mylower (*ap)) + { + if (mylower (*bp) || mytoupper (*ap) != *bp) + return static_cast(m_hashheader.sortorder[*ap]) + - static_cast(m_hashheader.sortorder[*bp]); + } + else + { + if (myupper (*bp) || mytolower (*ap) != *bp) + return static_cast(m_hashheader.sortorder[*ap]) + - static_cast(m_hashheader.sortorder[*bp]); + } + } + } + if (*bp != '\0') + return -static_cast(m_hashheader.sortorder[*bp]); + for (ap = inta, bp = intb; *ap; ap++, bp++) + { + if (*ap != *bp) + { + return static_cast(m_hashheader.sortorder[*ap]) + - static_cast(m_hashheader.sortorder[*bp]); + } + } + return 0; +} + +/* + * \param word + */ +void +ISpellChecker::makepossibilities (ichar_t *word) +{ + register int i; + + for (i = 0; i < MAXPOSSIBLE; i++) + m_possibilities[i][0] = 0; + m_pcount = 0; + m_maxposslen = 0; + m_easypossibilities = 0; + +#ifndef NO_CAPITALIZATION_SUPPORT + wrongcapital (word); +#endif + +/* + * according to Pollock and Zamora, CACM April 1984 (V. 27, No. 4), + * page 363, the correct order for this is: + * OMISSION = TRANSPOSITION > INSERTION > SUBSTITUTION + * thus, it was exactly backwards in the old version. -- PWP + */ + + if (m_pcount < MAXPOSSIBLE) + missingletter (word); /* omission */ + if (m_pcount < MAXPOSSIBLE) + transposedletter (word); /* transposition */ + if (m_pcount < MAXPOSSIBLE) + extraletter (word); /* insertion */ + if (m_pcount < MAXPOSSIBLE) + wrongletter (word); /* substitution */ + + if ((m_hashheader.compoundflag != COMPOUND_ANYTIME) && + m_pcount < MAXPOSSIBLE) + missingspace (word); /* two words */ + +} + +/* + * \param word + * + * \return + */ +int +ISpellChecker::insert (ichar_t *word) +{ + register int i; + register char * realword; + + realword = ichartosstr (word, 0); + for (i = 0; i < m_pcount; i++) + { + if (strcmp (m_possibilities[i], realword) == 0) + return (0); + } + + strcpy (m_possibilities[m_pcount++], realword); + i = strlen (realword); + if (i > m_maxposslen) + m_maxposslen = i; + if (m_pcount >= MAXPOSSIBLE) + return (-1); + else + return (0); +} + +#ifndef NO_CAPITALIZATION_SUPPORT +/* + * \param word + */ +void +ISpellChecker::wrongcapital (ichar_t *word) +{ + ichar_t newword[INPUTWORDLEN + MAXAFFIXLEN]; + + /* + ** When the third parameter to "good" is nonzero, it ignores + ** case. If the word matches this way, "ins_cap" will recapitalize + ** it correctly. + */ + if (good (word, 0, 1, 0, 0)) + { + icharcpy (newword, word); + upcase (newword); + ins_cap (newword, word); + } +} +#endif + +/* + * \param word + */ +void +ISpellChecker::wrongletter (ichar_t *word) +{ + register int i; + register int j; + register int n; + ichar_t savechar; + ichar_t newword[INPUTWORDLEN + MAXAFFIXLEN]; + + n = icharlen (word); + icharcpy (newword, word); +#ifndef NO_CAPITALIZATION_SUPPORT + upcase (newword); +#endif + + for (i = 0; i < n; i++) + { + savechar = newword[i]; + for (j=0; j < m_Trynum; ++j) + { + if (m_Try[j] == savechar) + continue; + else if (isboundarych (m_Try[j]) && (i == 0 || i == n - 1)) + continue; + newword[i] = m_Try[j]; + if (good (newword, 0, 1, 0, 0)) + { + if (ins_cap (newword, word) < 0) + return; + } + } + newword[i] = savechar; + } +} + +/* + * \param word + */ +void +ISpellChecker::extraletter (ichar_t *word) +{ + ichar_t newword[INPUTWORDLEN + MAXAFFIXLEN]; + register ichar_t * p; + register ichar_t * r; + + if (icharlen (word) < 2) + return; + + icharcpy (newword, word + 1); + for (p = word, r = newword; *p != 0; ) + { + if (good (newword, 0, 1, 0, 0)) + { + if (ins_cap (newword, word) < 0) + return; + } + *r++ = *p++; + } +} + +/* + * \param word + */ +void +ISpellChecker::missingletter (ichar_t *word) +{ + ichar_t newword[INPUTWORDLEN + MAXAFFIXLEN + 1]; + register ichar_t * p; + register ichar_t * r; + register int i; + + icharcpy (newword + 1, word); + for (p = word, r = newword; *p != 0; ) + { + for (i = 0; i < m_Trynum; i++) + { + if (isboundarych (m_Try[i]) && r == newword) + continue; + *r = m_Try[i]; + if (good (newword, 0, 1, 0, 0)) + { + if (ins_cap (newword, word) < 0) + return; + } + } + *r++ = *p++; + } + for (i = 0; i < m_Trynum; i++) + { + if (isboundarych (m_Try[i])) + continue; + *r = m_Try[i]; + if (good (newword, 0, 1, 0, 0)) + { + if (ins_cap (newword, word) < 0) + return; + } + } +} + +/* + * \param word + */ +void ISpellChecker::missingspace (ichar_t *word) +{ + ichar_t firsthalf[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN]; + int firstno; /* Index into first */ + ichar_t * firstp; /* Ptr into current firsthalf word */ + ichar_t newword[INPUTWORDLEN + MAXAFFIXLEN + 1]; + int nfirsthalf; /* No. words saved in 1st half */ + int nsecondhalf; /* No. words saved in 2nd half */ + register ichar_t * p; + ichar_t secondhalf[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN]; + int secondno; /* Index into second */ + + /* + ** We don't do words of length less than 3; this keeps us from + ** splitting all two-letter words into two single letters. We + ** also don't do maximum-length words, since adding the space + ** would exceed the size of the "possibilities" array. + */ + nfirsthalf = icharlen (word); + if (nfirsthalf < 3 || nfirsthalf >= INPUTWORDLEN + MAXAFFIXLEN - 1) + return; + icharcpy (newword + 1, word); + for (p = newword + 1; p[1] != '\0'; p++) + { + p[-1] = *p; + *p = '\0'; + if (good (newword, 0, 1, 0, 0)) + { + /* + * Save_cap must be called before good() is called on the + * second half, because it uses state left around by + * good(). This is unfortunate because it wastes a bit of + * time, but I don't think it's a significant performance + * problem. + */ + nfirsthalf = save_cap (newword, word, firsthalf); + if (good (p + 1, 0, 1, 0, 0)) + { + nsecondhalf = save_cap (p + 1, p + 1, secondhalf); + for (firstno = 0; firstno < nfirsthalf; firstno++) + { + firstp = &firsthalf[firstno][p - newword]; + for (secondno = 0; secondno < nsecondhalf; secondno++) + { + *firstp = ' '; + icharcpy (firstp + 1, secondhalf[secondno]); + if (insert (firsthalf[firstno]) < 0) + return; + *firstp = '-'; + if (insert (firsthalf[firstno]) < 0) + return; + } + } + } + } + } +} + +/* + * \param word + * \param pfxopts Options to apply to prefixes + */ +int +ISpellChecker::compoundgood (ichar_t *word, int pfxopts) +{ + ichar_t newword[INPUTWORDLEN + MAXAFFIXLEN]; + register ichar_t * p; + register ichar_t savech; + long secondcap; /* Capitalization of 2nd half */ + + /* + ** If compoundflag is COMPOUND_NEVER, compound words are never ok. + */ + if (m_hashheader.compoundflag == COMPOUND_NEVER) + return 0; + /* + ** Test for a possible compound word (for languages like German that + ** form lots of compounds). + ** + ** This is similar to missingspace, except we quit on the first hit, + ** and we won't allow either member of the compound to be a single + ** letter. + ** + ** We don't do words of length less than 2 * compoundmin, since + ** both halves must at least compoundmin letters. + */ + if (icharlen (word) < 2 * m_hashheader.compoundmin) + return 0; + icharcpy (newword, word); + p = newword + m_hashheader.compoundmin; + for ( ; p[m_hashheader.compoundmin - 1] != 0; p++) + { + savech = *p; + *p = 0; + if (good (newword, 0, 0, pfxopts, FF_COMPOUNDONLY)) + { + *p = savech; + if (good (p, 0, 1, FF_COMPOUNDONLY, 0) + || compoundgood (p, FF_COMPOUNDONLY)) + { + secondcap = whatcap (p); + switch (whatcap (newword)) + { + case ANYCASE: + case CAPITALIZED: + case FOLLOWCASE: /* Followcase can have l.c. suffix */ + return secondcap == ANYCASE; + case ALLCAPS: + return secondcap == ALLCAPS; + } + } + } + else + *p = savech; + } + return 0; +} + +/* + * \param word + */ +void +ISpellChecker::transposedletter (ichar_t *word) +{ + ichar_t newword[INPUTWORDLEN + MAXAFFIXLEN]; + register ichar_t * p; + register ichar_t temp; + + icharcpy (newword, word); + for (p = newword; p[1] != 0; p++) + { + temp = *p; + *p = p[1]; + p[1] = temp; + if (good (newword, 0, 1, 0, 0)) + { + if (ins_cap (newword, word) < 0) + return; + } + temp = *p; + *p = p[1]; + p[1] = temp; + } +} + +/*! + * Insert one or more correctly capitalized versions of word + * + * \param word + * \param pattern + * + * \return + */ +int +ISpellChecker::ins_cap (ichar_t *word, ichar_t *pattern) +{ + int i; /* Index into savearea */ + int nsaved; /* No. of words saved */ + ichar_t savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN]; + + nsaved = save_cap (word, pattern, savearea); + for (i = 0; i < nsaved; i++) + { + if (insert (savearea[i]) < 0) + return -1; + } + return 0; +} + +/*! + * Save one or more correctly capitalized versions of word + * + * \param word Word to save + * \param pattern Prototype capitalization pattern + * \param savearea Room to save words + * + * \return + */ +int +ISpellChecker::save_cap (ichar_t *word, ichar_t *pattern, + ichar_t savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN]) +{ + int hitno; /* Index into hits array */ + int nsaved; /* Number of words saved */ + int preadd; /* No. chars added to front of root */ + int prestrip; /* No. chars stripped from front */ + int sufadd; /* No. chars added to back of root */ + int sufstrip; /* No. chars stripped from back */ + + if (*word == 0) + return 0; + + for (hitno = m_numhits, nsaved = 0; --hitno >= 0 && nsaved < MAX_CAPS; ) + { + if (m_hits[hitno].prefix) + { + prestrip = m_hits[hitno].prefix->stripl; + preadd = m_hits[hitno].prefix->affl; + } + else + prestrip = preadd = 0; + if (m_hits[hitno].suffix) + { + sufstrip = m_hits[hitno].suffix->stripl; + sufadd = m_hits[hitno].suffix->affl; + } + else + sufadd = sufstrip = 0; + save_root_cap (word, pattern, prestrip, preadd, + sufstrip, sufadd, + m_hits[hitno].dictent, m_hits[hitno].prefix, m_hits[hitno].suffix, + savearea, &nsaved); + } + return nsaved; +} + +/* + * \param word + * \param pattern + * \param prestrip + * \param preadd + * \param sufstrip + * \param sufadd + * \param firstdent + * \param pfxent + * \param sufent + * + * \return + */ +int +ISpellChecker::ins_root_cap (ichar_t *word, ichar_t *pattern, + int prestrip, int preadd, int sufstrip, int sufadd, + struct dent *firstdent, struct flagent *pfxent, struct flagent *sufent) +{ + int i; /* Index into savearea */ + ichar_t savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN]; + int nsaved; /* Number of words saved */ + + nsaved = 0; + save_root_cap (word, pattern, prestrip, preadd, sufstrip, sufadd, + firstdent, pfxent, sufent, savearea, &nsaved); + for (i = 0; i < nsaved; i++) + { + if (insert (savearea[i]) < 0) + return -1; + } + return 0; +} + +/* ARGSUSED */ +/*! + * \param word Word to be saved + * \param pattern Capitalization pattern + * \param prestrip No. chars stripped from front + * \param preadd No. chars added to front of root + * \param sufstrip No. chars stripped from back + * \param sufadd No. chars added to back of root + * \param firstdent First dent for root + * \param pfxent Pfx-flag entry for word + * \param sufent Sfx-flag entry for word + * \param savearea Room to save words + * \param nsaved Number saved so far (updated) + */ +void +ISpellChecker::save_root_cap (ichar_t *word, ichar_t *pattern, + int prestrip, int preadd, int sufstrip, int sufadd, + struct dent *firstdent, struct flagent *pfxent, struct flagent *sufent, + ichar_t savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN], + int * nsaved) +{ +#ifndef NO_CAPITALIZATION_SUPPORT + register struct dent * dent; +#endif /* NO_CAPITALIZATION_SUPPORT */ + int firstisupper; + ichar_t newword[INPUTWORDLEN + 4 * MAXAFFIXLEN + 4]; +#ifndef NO_CAPITALIZATION_SUPPORT + register ichar_t * p; + int len; + int i; + int limit; +#endif /* NO_CAPITALIZATION_SUPPORT */ + + if (*nsaved >= MAX_CAPS) + return; + icharcpy (newword, word); + firstisupper = myupper (pattern[0]); +#ifdef NO_CAPITALIZATION_SUPPORT + /* + ** Apply the old, simple-minded capitalization rules. + */ + if (firstisupper) + { + if (myupper (pattern[1])) + upcase (newword); + else + { + lowcase (newword); + newword[0] = mytoupper (newword[0]); + } + } + else + lowcase (newword); + icharcpy (savearea[*nsaved], newword); + (*nsaved)++; + return; +#else /* NO_CAPITALIZATION_SUPPORT */ +#define flagsareok(dent) \ + ((pfxent == NULL \ + || TSTMASKBIT (dent->mask, pfxent->flagbit)) \ + && (sufent == NULL \ + || TSTMASKBIT (dent->mask, sufent->flagbit))) + + dent = firstdent; + if ((dent->flagfield & (CAPTYPEMASK | MOREVARIANTS)) == ALLCAPS) + { + upcase (newword); /* Uppercase required */ + icharcpy (savearea[*nsaved], newword); + (*nsaved)++; + return; + } + for (p = pattern; *p; p++) + { + if (mylower (*p)) + break; + } + if (*p == 0) + { + upcase (newword); /* Pattern was all caps */ + icharcpy (savearea[*nsaved], newword); + (*nsaved)++; + return; + } + for (p = pattern + 1; *p; p++) + { + if (myupper (*p)) + break; + } + if (*p == 0) + { + /* + ** The pattern was all-lower or capitalized. If that's + ** legal, insert only that version. + */ + if (firstisupper) + { + if (captype (dent->flagfield) == CAPITALIZED + || captype (dent->flagfield) == ANYCASE) + { + lowcase (newword); + newword[0] = mytoupper (newword[0]); + icharcpy (savearea[*nsaved], newword); + (*nsaved)++; + return; + } + } + else + { + if (captype (dent->flagfield) == ANYCASE) + { + lowcase (newword); + icharcpy (savearea[*nsaved], newword); + (*nsaved)++; + return; + } + } + while (dent->flagfield & MOREVARIANTS) + { + dent = dent->next; + if (captype (dent->flagfield) == FOLLOWCASE + || !flagsareok (dent)) + continue; + if (firstisupper) + { + if (captype (dent->flagfield) == CAPITALIZED) + { + lowcase (newword); + newword[0] = mytoupper (newword[0]); + icharcpy (savearea[*nsaved], newword); + (*nsaved)++; + return; + } + } + else + { + if (captype (dent->flagfield) == ANYCASE) + { + lowcase (newword); + icharcpy (savearea[*nsaved], newword); + (*nsaved)++; + return; + } + } + } + } + /* + ** Either the sample had complex capitalization, or the simple + ** capitalizations (all-lower or capitalized) are illegal. + ** Insert all legal capitalizations, including those that are + ** all-lower or capitalized. If the prototype is capitalized, + ** capitalized all-lower samples. Watch out for affixes. + */ + dent = firstdent; + p = strtosichar (dent->word, 1); + len = icharlen (p); + if (dent->flagfield & MOREVARIANTS) + dent = dent->next; /* Skip place-holder entry */ + for ( ; ; ) + { + if (flagsareok (dent)) + { + if (captype (dent->flagfield) != FOLLOWCASE) + { + lowcase (newword); + if (firstisupper || captype (dent->flagfield) == CAPITALIZED) + newword[0] = mytoupper (newword[0]); + icharcpy (savearea[*nsaved], newword); + (*nsaved)++; + if (*nsaved >= MAX_CAPS) + return; + } + else + { + /* Followcase is the tough one. */ + p = strtosichar (dent->word, 1); + memmove ( + reinterpret_cast(newword + preadd), + reinterpret_cast(p + prestrip), + (len - prestrip - sufstrip) * sizeof (ichar_t)); + if (myupper (p[prestrip])) + { + for (i = 0; i < preadd; i++) + newword[i] = mytoupper (newword[i]); + } + else + { + for (i = 0; i < preadd; i++) + newword[i] = mytolower (newword[i]); + } + limit = len + preadd + sufadd - prestrip - sufstrip; + i = len + preadd - prestrip - sufstrip; + p += len - sufstrip - 1; + if (myupper (*p)) + { + for (p = newword + i; i < limit; i++, p++) + *p = mytoupper (*p); + } + else + { + for (p = newword + i; i < limit; i++, p++) + *p = mytolower (*p); + } + icharcpy (savearea[*nsaved], newword); + (*nsaved)++; + if (*nsaved >= MAX_CAPS) + return; + } + } + if ((dent->flagfield & MOREVARIANTS) == 0) + break; /* End of the line */ + dent = dent->next; + } + return; +#endif /* NO_CAPITALIZATION_SUPPORT */ +} + + diff --git a/src/ispell/good.cpp b/src/ispell/good.cpp new file mode 100644 index 0000000..ba3b1d3 --- /dev/null +++ b/src/ispell/good.cpp @@ -0,0 +1,399 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * good.c - see if a word or its root word + * is in the dictionary. + * + * Pace Willisson, 1983 + * + * Copyright 1992, 1993, Geoff Kuenning, Granada Hills, CA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All modifications to the source code must be clearly marked as + * such. Binary redistributions based on modified source code + * must be clearly marked as modified versions in the documentation + * and/or other materials provided with the distribution. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by Geoff Kuenning and + * other unpaid contributors. + * 5. The name of Geoff Kuenning may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING 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 GEOFF KUENNING 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. + */ + +/* + * $Log$ + * Revision 1.4 2003/08/14 17:51:26 dom + * update license - exception clause should be Lesser GPL + * + * Revision 1.3 2003/07/28 20:40:25 dom + * fix up the license clause, further win32-registry proof some directory getting functions + * + * Revision 1.2 2003/07/16 22:52:37 dom + * LGPL + exception license + * + * Revision 1.1 2003/07/15 01:15:04 dom + * ispell enchant backend + * + * Revision 1.2 2003/01/29 05:50:11 hippietrail + * + * Fixed my mess in EncodingManager. + * Changed many C casts to C++ casts. + * + * Revision 1.1 2003/01/24 05:52:32 hippietrail + * + * Refactored ispell code. Old ispell global variables had been put into + * an allocated structure, a pointer to which was passed to many functions. + * I have now made all such functions and variables private members of the + * ISpellChecker class. It was C OO, now it's C++ OO. + * + * I've fixed the makefiles and tested compilation but am unable to test + * operation. Please back out my changes if they cause problems which + * are not obvious or easy to fix. + * + * Revision 1.6 2003/01/06 18:48:38 dom + * ispell cleanup, start of using new 'add' save features + * + * Revision 1.5 2002/09/19 05:31:15 hippietrail + * + * More Ispell cleanup. Conditional globals and DEREF macros are removed. + * K&R function declarations removed, converted to Doxygen style comments + * where possible. No code has been changed (I hope). Compiles for me but + * unable to test. + * + * Revision 1.4 2002/09/17 03:03:29 hippietrail + * + * After seeking permission on the developer list I've reformatted all the + * spelling source which seemed to have parts which used 2, 3, 4, and 8 + * spaces for tabs. It should all look good with our standard 4-space + * tabs now. + * I've concentrated just on indentation in the actual code. More prettying + * could be done. + * * NO code changes were made * + * + * Revision 1.3 2002/09/13 17:20:12 mpritchett + * Fix more warnings for Linux build + * + * Revision 1.2 2001/05/12 16:05:42 thomasf + * Big pseudo changes to ispell to make it pass around a structure rather + * than rely on all sorts of gloabals willy nilly here and there. Also + * fixed our spelling class to work with accepting suggestions once more. + * This code is dirty, gross and ugly (not to mention still not supporting + * multiple hash sized just yet) but it works on my machine and will no + * doubt break other machines. + * + * Revision 1.1 2001/04/15 16:01:24 tomas_f + * moving to spell/xp + * + * Revision 1.5 2000/02/09 22:35:25 sterwill + * Clean up some warnings + * + * Revision 1.4 1998/12/29 14:55:32 eric + * + * I've doctored the ispell code pretty extensively here. It is now + * warning-free on Win32. It also *works* on Win32 now, since I + * replaced all the I/O calls with ANSI standard ones. + * + * Revision 1.3 1998/12/28 23:11:30 eric + * + * modified spell code and integration to build on Windows. + * This is still a hack. + * + * Actually, it doesn't yet WORK on Windows. It just builds. + * SpellCheckInit is failing for some reason. + * + * Revision 1.2 1998/12/28 22:16:22 eric + * + * These changes begin to incorporate the spell checker into AbiWord. Most + * of this is a hack. + * + * 1. added other/spell to the -I list in config/abi_defs + * 2. replaced other/spell/Makefile with one which is more like + * our build system. + * 3. added other/spell to other/Makefile so that the build will now + * dive down and build the spell check library. + * 4. added the AbiSpell library to the Makefiles in wp/main + * 5. added a call to SpellCheckInit in wp/main/unix/UnixMain.cpp. + * This call is a HACK and should be replaced with something + * proper later. + * 6. added code to fv_View.cpp as follows: + * whenever you double-click on a word, the spell checker + * verifies that word and prints its status to stdout. + * + * Caveats: + * 1. This will break the Windows build. I'm going to work on fixing it + * now. + * 2. This only works if your dictionary is in /usr/lib/ispell/american.hash. + * The dictionary location is currently hard-coded. This will be + * fixed as well. + * + * Anyway, such as it is, it works. + * + * Revision 1.1 1998/12/28 18:04:43 davet + * Spell checker code stripped from ispell. At this point, there are + * two external routines... the Init routine, and a check-a-word routine + * which returns a boolean value, and takes a 16 bit char string. + * The code resembles the ispell code as much as possible still. + * + * Revision 1.43 1994/11/02 06:56:05 geoff + * Remove the anyword feature, which I've decided is a bad idea. + * + * Revision 1.42 1994/10/25 05:45:59 geoff + * Add support for an affix that will work with any word, even if there's + * no explicit flag. + * + * Revision 1.41 1994/05/24 06:23:06 geoff + * Let tgood decide capitalization questions, rather than doing it ourselves. + * + * Revision 1.40 1994/05/17 06:44:10 geoff + * Add support for controlled compound formation and the COMPOUNDONLY + * option to affix flags. + * + * Revision 1.39 1994/01/25 07:11:31 geoff + * Get rid of all old RCS log lines in preparation for the 3.1 release. + * + */ + +#include +#include +#include +#include + +#include "ispell_checker.h" + + +int good P ((ichar_t * word, int ignoreflagbits, int allhits, + int pfxopts, int sfxopts)); + +#ifndef NO_CAPITALIZATION_SUPPORT + +/*! +** See if this particular capitalization (dent) is legal with these +** particular affixes. +** +** \param dent +** \param hit +** +** \return +*/ +static int entryhasaffixes (struct dent *dent, struct success *hit) +{ + if (hit->prefix && !TSTMASKBIT (dent->mask, hit->prefix->flagbit)) + return 0; + if (hit->suffix && !TSTMASKBIT (dent->mask, hit->suffix->flagbit)) + return 0; + return 1; /* Yes, these affixes are legal */ +} + +/* + * \param word + * \param hit + * \param len + * + * \return + */ +int ISpellChecker::cap_ok (ichar_t *word, struct success *hit, int len) +{ + register ichar_t * dword; + register ichar_t * w; + register struct dent * dent; + ichar_t dentword[INPUTWORDLEN + MAXAFFIXLEN]; + int preadd; + int prestrip; + int sufadd; + ichar_t * limit; + long thiscap; + long dentcap; + + thiscap = whatcap (word); + /* + ** All caps is always legal, regardless of affixes. + */ + preadd = prestrip = sufadd = 0; + if (thiscap == ALLCAPS) + return 1; + else if (thiscap == FOLLOWCASE) + { + /* Set up some constants for the while(1) loop below */ + if (hit->prefix) + { + preadd = hit->prefix->affl; + prestrip = hit->prefix->stripl; + } + else + preadd = prestrip = 0; + sufadd = hit->suffix ? hit->suffix->affl : 0; + } + /* + ** Search the variants for one that matches what we have. Note + ** that thiscap can't be ALLCAPS, since we already returned + ** for that case. + */ + dent = hit->dictent; + for ( ; ; ) + { + dentcap = captype (dent->flagfield); + if (dentcap != thiscap) + { + if (dentcap == ANYCASE && thiscap == CAPITALIZED + && entryhasaffixes (dent, hit)) + return 1; + } + else /* captypes match */ + { + if (thiscap != FOLLOWCASE) + { + if (entryhasaffixes (dent, hit)) + return 1; + } + else + { + /* + ** Make sure followcase matches exactly. + ** Life is made more difficult by the + ** possibility of affixes. Start with + ** the prefix. + */ + strtoichar (dentword, dent->word, INPUTWORDLEN, 1); + dword = dentword; + limit = word + preadd; + if (myupper (dword[prestrip])) + { + for (w = word; w < limit; w++) + { + if (mylower (*w)) + goto doublecontinue; + } + } + else + { + for (w = word; w < limit; w++) + { + if (myupper (*w)) + goto doublecontinue; + } + } + dword += prestrip; + /* Do root part of word */ + limit = dword + len - preadd - sufadd; + while (dword < limit) + { + if (*dword++ != *w++) + goto doublecontinue; + } + /* Do suffix */ + dword = limit - 1; + if (myupper (*dword)) + { + for ( ; *w; w++) + { + if (mylower (*w)) + goto doublecontinue; + } + } + else + { + for ( ; *w; w++) + { + if (myupper (*w)) + goto doublecontinue; + } + } + /* + ** All failure paths go to "doublecontinue," + ** so if we get here it must match. + */ + if (entryhasaffixes (dent, hit)) + return 1; + doublecontinue: ; + } + } + if ((dent->flagfield & MOREVARIANTS) == 0) + break; + dent = dent->next; + } + + /* No matches found */ + return 0; +} +#endif + +#ifndef NO_CAPITALIZATION_SUPPORT +/*! + * \param w Word to look up + * \param ignoreflagbits NZ to ignore affix flags in dict + * \param allhits NZ to ignore case, get every hit + * \param pfxopts Options to apply to prefixes + * \param sfxopts Options to apply to suffixes + * + * \return + */ +int ISpellChecker::good (ichar_t *w, int ignoreflagbits, int allhits, int pfxopts, int sfxopts) +#else +/* ARGSUSED */ +int ISpellChecker::good (ichar_t *w, int ignoreflagbits, int dummy, int pfxopts, int sfxopts) +#endif +{ + ichar_t nword[INPUTWORDLEN + MAXAFFIXLEN]; + register ichar_t * p; + register ichar_t * q; + register int n; + register struct dent * dp; + + /* + ** Make an uppercase copy of the word we are checking. + */ + for (p = w, q = nword; *p; ) + *q++ = mytoupper (*p++); + *q = 0; + n = q - nword; + + m_numhits = 0; + + if ((dp = ispell_lookup (nword, 1)) != NULL) + { + m_hits[0].dictent = dp; + m_hits[0].prefix = NULL; + m_hits[0].suffix = NULL; +#ifndef NO_CAPITALIZATION_SUPPORT + if (allhits || cap_ok (w, &m_hits[0], n)) + m_numhits = 1; +#else + m_numhits = 1; +#endif + } + + if (m_numhits && !allhits) + return 1; + + /* try stripping off affixes */ + + chk_aff (w, nword, n, ignoreflagbits, allhits, pfxopts, sfxopts); + + return m_numhits; +} + + + + diff --git a/src/ispell/hash.cpp b/src/ispell/hash.cpp new file mode 100644 index 0000000..3ec6ce2 --- /dev/null +++ b/src/ispell/hash.cpp @@ -0,0 +1,172 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * hash.c - a simple hash function for ispell + * + * Pace Willisson, 1983 + * + * Copyright 1992, 1993, Geoff Kuenning, Granada Hills, CA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All modifications to the source code must be clearly marked as + * such. Binary redistributions based on modified source code + * must be clearly marked as modified versions in the documentation + * and/or other materials provided with the distribution. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by Geoff Kuenning and + * other unpaid contributors. + * 5. The name of Geoff Kuenning may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING 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 GEOFF KUENNING 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. + */ + +/* + * $Log$ + * Revision 1.4 2003/08/14 17:51:27 dom + * update license - exception clause should be Lesser GPL + * + * Revision 1.3 2003/07/28 20:40:26 dom + * fix up the license clause, further win32-registry proof some directory getting functions + * + * Revision 1.2 2003/07/16 22:52:39 dom + * LGPL + exception license + * + * Revision 1.1 2003/07/15 01:15:05 dom + * ispell enchant backend + * + * Revision 1.2 2003/01/29 05:50:11 hippietrail + * + * Fixed my mess in EncodingManager. + * Changed many C casts to C++ casts. + * + * Revision 1.1 2003/01/24 05:52:33 hippietrail + * + * Refactored ispell code. Old ispell global variables had been put into + * an allocated structure, a pointer to which was passed to many functions. + * I have now made all such functions and variables private members of the + * ISpellChecker class. It was C OO, now it's C++ OO. + * + * I've fixed the makefiles and tested compilation but am unable to test + * operation. Please back out my changes if they cause problems which + * are not obvious or easy to fix. + * + * Revision 1.5 2002/09/19 05:31:15 hippietrail + * + * More Ispell cleanup. Conditional globals and DEREF macros are removed. + * K&R function declarations removed, converted to Doxygen style comments + * where possible. No code has been changed (I hope). Compiles for me but + * unable to test. + * + * Revision 1.4 2002/09/17 03:03:29 hippietrail + * + * After seeking permission on the developer list I've reformatted all the + * spelling source which seemed to have parts which used 2, 3, 4, and 8 + * spaces for tabs. It should all look good with our standard 4-space + * tabs now. + * I've concentrated just on indentation in the actual code. More prettying + * could be done. + * * NO code changes were made * + * + * Revision 1.3 2002/09/13 17:20:13 mpritchett + * Fix more warnings for Linux build + * + * Revision 1.2 2001/05/12 16:05:42 thomasf + * Big pseudo changes to ispell to make it pass around a structure rather + * than rely on all sorts of gloabals willy nilly here and there. Also + * fixed our spelling class to work with accepting suggestions once more. + * This code is dirty, gross and ugly (not to mention still not supporting + * multiple hash sized just yet) but it works on my machine and will no + * doubt break other machines. + * + * Revision 1.1 2001/04/15 16:01:24 tomas_f + * moving to spell/xp + * + * Revision 1.3 1998/12/29 14:55:33 eric + * + * I've doctored the ispell code pretty extensively here. It is now + * warning-free on Win32. It also *works* on Win32 now, since I + * replaced all the I/O calls with ANSI standard ones. + * + * Revision 1.2 1998/12/28 23:11:30 eric + * + * modified spell code and integration to build on Windows. + * This is still a hack. + * + * Actually, it doesn't yet WORK on Windows. It just builds. + * SpellCheckInit is failing for some reason. + * + * Revision 1.1 1998/12/28 18:04:43 davet + * Spell checker code stripped from ispell. At this point, there are + * two external routines... the Init routine, and a check-a-word routine + * which returns a boolean value, and takes a 16 bit char string. + * The code resembles the ispell code as much as possible still. + * + * Revision 1.20 1994/01/25 07:11:34 geoff + * Get rid of all old RCS log lines in preparation for the 3.1 release. + * + */ + +#include "ispell_checker.h" + +/* + * The following hash algorithm is due to Ian Dall, with slight modifications + * by Geoff Kuenning to reflect the results of testing with the English + * dictionaries actually distributed with ispell. + */ +#define HASHSHIFT 5 + +#ifdef NO_CAPITALIZATION_SUPPORT +#define HASHUPPER(c) c +#else /* NO_CAPITALIZATION_SUPPORT */ +#define HASHUPPER(c) mytoupper(c) +#endif /* NO_CAPITALIZATION_SUPPORT */ + +/* + * \param s + * \param hashtblsize + */ +int ISpellChecker::hash (ichar_t *s, int hashtblsize) +{ + register long h = 0; + register int i; + +#ifdef ICHAR_IS_CHAR + for (i = 4; i-- && *s != 0; ) + h = (h << 8) | HASHUPPER (*s++); +#else /* ICHAR_IS_CHAR */ + for (i = 2; i-- && *s != 0; ) + h = (h << 16) | HASHUPPER (*s++); +#endif /* ICHAR_IS_CHAR */ + while (*s != 0) + { + /* + * We have to do circular shifts the hard way, since C doesn't + * have them even though the hardware probably does. Oh, well. + */ + h = (h << HASHSHIFT) + | ((h >> (32 - HASHSHIFT)) & ((1 << HASHSHIFT) - 1)); + h ^= HASHUPPER (*s++); + } + return static_cast(h) % hashtblsize; +} diff --git a/src/ispell/ispell.h b/src/ispell/ispell.h new file mode 100644 index 0000000..13ae6d6 --- /dev/null +++ b/src/ispell/ispell.h @@ -0,0 +1,769 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +#ifndef ISPELL_H +#define ISPELL_H + +#include + +/* + * $Id: ispell.h 28601 2010-01-11 12:40:00Z dom $ + */ + +/* + * Copyright 1992, 1993, Geoff Kuenning, Granada Hills, CA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All modifications to the source code must be clearly marked as + * such. Binary redistributions based on modified source code + * must be clearly marked as modified versions in the documentation + * and/or other materials provided with the distribution. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by Geoff Kuenning and + * other unpaid contributors. + * 5. The name of Geoff Kuenning may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING 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 GEOFF KUENNING 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. + */ + +/* + * $Log$ + * Revision 1.4 2003/08/14 17:51:27 dom + * update license - exception clause should be Lesser GPL + * + * Revision 1.3 2003/07/28 20:40:26 dom + * fix up the license clause, further win32-registry proof some directory getting functions + * + * Revision 1.2 2003/07/16 22:52:40 dom + * LGPL + exception license + * + * Revision 1.1 2003/07/15 01:15:06 dom + * ispell enchant backend + * + * Revision 1.10 2003/01/24 05:52:33 hippietrail + * + * Refactored ispell code. Old ispell global variables had been put into + * an allocated structure, a pointer to which was passed to many functions. + * I have now made all such functions and variables private members of the + * ISpellChecker class. It was C OO, now it's C++ OO. + * + * I've fixed the makefiles and tested compilation but am unable to test + * operation. Please back out my changes if they cause problems which + * are not obvious or easy to fix. + * + * Revision 1.9 2002/09/19 05:31:15 hippietrail + * + * More Ispell cleanup. Conditional globals and DEREF macros are removed. + * K&R function declarations removed, converted to Doxygen style comments + * where possible. No code has been changed (I hope). Compiles for me but + * unable to test. + * + * Revision 1.8 2002/09/17 03:03:29 hippietrail + * + * After seeking permission on the developer list I've reformatted all the + * spelling source which seemed to have parts which used 2, 3, 4, and 8 + * spaces for tabs. It should all look good with our standard 4-space + * tabs now. + * I've concentrated just on indentation in the actual code. More prettying + * could be done. + * * NO code changes were made * + * + * Revision 1.7 2002/03/22 14:31:57 dom + * fix mg's compile problem + * + * Revision 1.6 2002/03/05 16:55:52 dom + * compound word support, tested against swedish + * + * Revision 1.5 2001/08/10 18:32:40 dom + * Spelling and iconv updates. god, i hate iconv + * + * Revision 1.4 2001/06/26 16:33:27 dom + * 128 StringChars and some other stuff + * + * Revision 1.3 2001/05/12 16:05:42 thomasf + * Big pseudo changes to ispell to make it pass around a structure rather + * than rely on all sorts of gloabals willy nilly here and there. Also + * fixed our spelling class to work with accepting suggestions once more. + * This code is dirty, gross and ugly (not to mention still not supporting + * multiple hash sized just yet) but it works on my machine and will no + * doubt break other machines. + * + * Revision 1.2 2001/04/18 00:59:36 thomasf + * Removed the duplicate declarations of variables that was causing build + * to bail. This new ispell stuff is a total mess. + * + * Revision 1.1 2001/04/15 16:01:24 tomas_f + * moving to spell/xp + * + * Revision 1.13 2001/04/13 12:33:12 tamlin + * ispell can now be used from C++ + * + * Revision 1.12 2001/03/25 01:30:02 tomb + * 1. Fixed ispell #define problems on Win32 + * 2. Changed the way that togglable toolbars are tracked so that Full + * Screen mode works right on Windows + * 3. Fixed SET_GATHER macro in ap_Win32Dialog_Options.h + * 4. Fixed Toggle Case dialog to default to Sentence Case when loaded + * 5. Added #define for Auto Save checkbox (though I haven't updated the + * Prefs dialog yet) + * + * Revision 1.11 2001/03/24 23:28:41 dom + * Make C++ aware and watch out for VOID on Win32 + * + * Revision 1.10 1999/12/21 18:46:29 sterwill + * ispell patch for non-English dictionaries by Henrik Berg + * + * Revision 1.9 1999/10/20 03:19:35 paul + * Hacked ispell code to ignore any characters that don't fit in the lookup tables loaded from the dictionary. It ain't pretty, but at least we don't crash there any more. + * + * Revision 1.8 1999/09/29 23:33:32 justin + * Updates to the underlying ispell-based code to support suggested corrections. + * + * Revision 1.7 1999/04/13 17:12:51 jeff + * Applied "Darren O. Benham" spell check changes. + * Fixed crash on Win32 with the new code. + * + * Revision 1.6 1999/01/07 05:14:22 sterwill + * So it builds on Unix... it might break win32 in ispell, since ut_types + * is no longer included. This is a temporary solution to a larger problem + * of including C++ headers in C source files. + * + * Revision 1.6 1999/01/07 05:14:22 sterwill + * So it builds on Unix... it might break win32 in ispell, since ut_types + * is no longer included. This is a temporary solution to a larger problem + * of including C++ headers in C source files. + * + * Revision 1.5 1999/01/07 05:02:25 sterwill + * Checking in half-broken to avoid tree lossage + * + * Revision 1.4 1999/01/07 01:07:48 paul + * Fixed spell leaks. + * + * Revision 1.3 1998/12/29 15:03:54 eric + * + * minor fix to ispell.h to get things to compile on Linux again. + * + * Revision 1.2 1998/12/29 14:55:33 eric + * + * I've doctored the ispell code pretty extensively here. It is now + * warning-free on Win32. It also *works* on Win32 now, since I + * replaced all the I/O calls with ANSI standard ones. + * + * Revision 1.1 1998/12/28 18:04:43 davet + * Spell checker code stripped from ispell. At this point, there are + * two external routines... the Init routine, and a check-a-word routine + * which returns a boolean value, and takes a 16 bit char string. + * The code resembles the ispell code as much as possible still. + * + * Revision 1.68 1995/03/06 02:42:41 geoff + * Be vastly more paranoid about parenthesizing macro arguments. This + * fixes a bug in defmt.c where a complex argument was passed to + * isstringch. + * + * Revision 1.67 1995/01/03 19:24:12 geoff + * Get rid of a non-global declaration. + * + * Revision 1.66 1994/12/27 23:08:49 geoff + * Fix a lot of subtly bad assumptions about the widths of ints and longs + * which only show up on 64-bit machines like the Cray and the DEC Alpha. + * + * Revision 1.65 1994/11/02 06:56:10 geoff + * Remove the anyword feature, which I've decided is a bad idea. + * + * Revision 1.64 1994/10/25 05:46:18 geoff + * Add the FF_ANYWORD flag for defining an affix that will apply to any + * word, even if not explicitly specified. (Good for French.) + * + * Revision 1.63 1994/09/16 04:48:28 geoff + * Make stringdups and laststringch unsigned ints, and dupnos a plain + * int, so that we can handle more than 128 stringchars and stringchar + * types. + * + * Revision 1.62 1994/09/01 06:06:39 geoff + * Change erasechar/killchar to uerasechar/ukillchar to avoid + * shared-library problems on HP systems. + * + * Revision 1.61 1994/08/31 05:58:35 geoff + * Add contextoffset, used in -a mode to handle extremely long lines. + * + * Revision 1.60 1994/05/17 06:44:15 geoff + * Add support for controlled compound formation and the COMPOUNDONLY + * option to affix flags. + * + * Revision 1.59 1994/03/15 06:25:16 geoff + * Change deftflag's initialization so we can tell if -t/-n appeared. + * + * Revision 1.58 1994/02/07 05:53:28 geoff + * Add typecasts to the the 7-bit versions of ichar* routines + * + * Revision 1.57 1994/01/25 07:11:48 geoff + * Get rid of all old RCS log lines in preparation for the 3.1 release. + * + */ + +#include +/* #include "ut_types.h" */ + +#include "ispell_def.h" + +#ifdef __cplusplus +extern "C" { +#endif /* c++ */ + +/* largest amount that a word might be extended by adding affixes */ +#ifndef MAXAFFIXLEN +#define MAXAFFIXLEN 20 +#endif + +/* +** Number of mask bits (affix flags) supported. Must be 32, 64, 128, or +** 256. If MASKBITS is 32 or 64, there are really only 26 or 58 flags +** available, respectively. If it is 32, the flags are named with the +** 26 English uppercase letters; lowercase will be converted to uppercase. +** If MASKBITS is 64, the 58 flags are named 'A' through 'z' in ASCII +** order, including the 6 special characters from 'Z' to 'a': "[\]^_`". +** If MASKBITS is 128 or 256, all the 7-bit or 8-bit characters, +** respectively, are theoretically available, though a few (newline, slash, +** null byte) are pretty hard to actually use successfully. +** +** Note that a number of non-English affix files depend on having a +** larger value for MASKBITS. See the affix files for more +** information. +*/ + +#ifndef MASKBITS +#define MASKBITS 64 +#endif + +extern int gnMaskBits; + +/* +** C type to use for masks. This should be a type that the processor +** accesses efficiently. +** +** MASKTYPE_WIDTH must correctly reflect the number of bits in a +** MASKTYPE. Unfortunately, it is also required to be a constant at +** preprocessor time, which means you can't use the sizeof operator to +** define it. +** +** Note that MASKTYPE *must* match MASKTYPE_WIDTH or you may get +** division-by-zero errors! +*/ +#ifndef MASKTYPE +#define MASKTYPE long +#endif +#ifndef MASKTYPE_WIDTH +#define MASKTYPE_WIDTH 32 +#endif + + /* program: this should be coded now in init */ + +#if MASKBITS < MASKTYPE_WIDTH +#undef MASKBITS +#define MASKBITS MASKTYPE_WIDTH +#endif /* MASKBITS < MASKTYPE_WIDTH */ + +/* +** Maximum hash table fullness percentage. Larger numbers trade space +** for time. +**/ +#ifndef MAXPCT +#define MAXPCT 70 /* Expand table when 70% full */ +#endif + +/* +** Maximum number of "string" characters that can be defined in a +** language (affix) file. Don't forget that an upper/lower string +** character counts as two! +*/ +#ifndef MAXSTRINGCHARS +#define MAXSTRINGCHARS 128 +#endif /* MAXSTRINGCHARS */ + +/* +** Maximum length of a "string" character. The default is appropriate for +** nroff-style characters starting with a backslash. +*/ +#ifndef MAXSTRINGCHARLEN +#define MAXSTRINGCHARLEN 10 +#endif /* MAXSTRINGCHARLEN */ + +/* +** Maximum number of "hits" expected on a word. This is basically the +** number of different ways different affixes can produce the same word. +** For example, with "english.aff", "brothers" can be produced 3 ways: +** "brothers," "brother+s", or "broth+ers". If this is too low, no major +** harm will be done, but ispell may occasionally forget a capitalization. +*/ +#ifndef MAX_HITS +#define MAX_HITS 10 +#endif + +/* +** Maximum number of capitalization variations expected in any word. +** Besides the obvious all-lower, all-upper, and capitalized versions, +** this includes followcase variants. If this is too low, no real +** harm will be done, but ispell may occasionally fail to suggest a +** correct capitalization. +*/ +#ifndef MAX_CAPS +#define MAX_CAPS 10 +#endif /* MAX_CAPS */ + +/* buffer size to use for file names if not in sys/param.h */ +#ifndef MAXPATHLEN +#define MAXPATHLEN 512 +#endif + +/* +** Maximum language-table search size. Smaller numbers make ispell +** run faster, at the expense of more memory (the lowest reasonable value +** is 2). If a given character appears in a significant position in +** more than MAXSEARCH suffixes, it will be given its own index table. +** If you change this, define INDEXDUMP in lookup.c to be sure your +** index table looks reasonable. +*/ +#ifndef MAXSEARCH +#define MAXSEARCH 4 +#endif + +#if defined(__STDC__) || defined(__cplusplus) +#define P(x) x + #ifndef VOID + #define VOID void + #endif +#else /* __STDC__ */ +#define P(x) () + #ifndef VOID + #define VOID char + #endif +#define const +#endif /* __STDC__ */ + +#ifdef NO8BIT +#define SET_SIZE 128 +#else +#define SET_SIZE 256 +#endif + +#define MASKSIZE (gnMaskBits / MASKTYPE_WIDTH) + +#ifdef lint +extern int TSTMASKBIT P ((MASKTYPE * mask, int bit)); +#else /* lint */ +/* The following is really testing for MASKSIZE <= 1, but cpp can't do that */ +#define TSTMASKBIT(mask, bit) \ + ((mask)[(bit) / MASKTYPE_WIDTH] & \ + ((MASKTYPE) 1 << ((bit) & (MASKTYPE_WIDTH - 1)))) +#endif /* lint */ + +#if MASKBITS > 64 +#define FULLMASKSET +#endif + +#if MASKBITS <= 32 + #define FLAGBASE ((MASKTYPE_WIDTH) - 6) +#else + # if MASKBITS <= 64 + #define FLAGBASE ((MASKTYPE_WIDTH) - 6) + # else + #define FLAGBASE 0 + # endif +#endif + +/* +** Data type for internal word storage. If necessary, we use shorts rather +** than chars so that string characters can be encoded as a single unit. +*/ +#if (SET_SIZE + MAXSTRINGCHARS) <= 256 +#ifndef lint +#define ICHAR_IS_CHAR +#endif /* lint */ +#endif + +#ifdef ICHAR_IS_CHAR +typedef unsigned char ichar_t; /* Internal character */ +#define icharlen(s) strlen ((char *) (s)) +#define icharcpy(a, b) strcpy ((char *) (a), (char *) (b)) +#define icharcmp(a, b) strcmp ((char *) (a), (char *) (b)) +#define icharncmp(a, b, n) strncmp ((char *) (a), (char *) (b), (n)) +#define chartoichar(x) ((ichar_t) (x)) +#else +typedef unsigned short ichar_t; /* Internal character */ +#define chartoichar(x) ((ichar_t) (unsigned char) (x)) + +/* + * Structure used to record data about successful lookups; these values + * are used in the ins_root_cap routine to produce correct capitalizations. + */ +struct success +{ + struct dent * dictent; /* Header of dict entry chain for wd */ + struct flagent * prefix; /* Prefix flag used, or NULL */ + struct flagent * suffix; /* Suffix flag used, or NULL */ +}; + +ichar_t* icharcpy (ichar_t* out, ichar_t* in); +int icharlen (ichar_t* in); +int icharcmp (ichar_t* s1, ichar_t* s2); +int icharncmp (ichar_t* s1, ichar_t* s2, int n); + +#endif + +struct dent +{ + struct dent * next; + char * word; + MASKTYPE mask[2]; +#ifdef FULLMASKSET + char flags; +#endif +}; + +/* +** Flags in the directory entry. If FULLMASKSET is undefined, these are +** stored in the highest bits of the last longword of the mask field. If +** FULLMASKSET is defined, they are stored in the extra "flags" field. +#ifndef NO_CAPITALIZATION_SUPPORT +** +** If a word has only one capitalization form, and that form is not +** FOLLOWCASE, it will have exactly one entry in the dictionary. The +** legal capitalizations will be indicated by the 2-bit capitalization +** field, as follows: +** +** ALLCAPS The word must appear in all capitals. +** CAPITALIZED The word must be capitalized (e.g., London). +** It will also be accepted in all capitals. +** ANYCASE The word may appear in lowercase, capitalized, +** or all-capitals. +** +** Regardless of the capitalization flags, the "word" field of the entry +** will point to an all-uppercase copy of the word. This is to simplify +** the large portion of the code that doesn't care about capitalization. +** Ispell will generate the correct version when needed. +** +** If a word has more than one capitalization, there will be multiple +** entries for it, linked together by the "next" field. The initial +** entry for such words will be a dummy entry, primarily for use by code +** that ignores capitalization. The "word" field of this entry will +** again point to an all-uppercase copy of the word. The "mask" field +** will contain the logical OR of the mask fields of all variants. +** A header entry is indicated by a capitalization type of ALLCAPS, +** with the MOREVARIANTS bit set. +** +** The following entries will define the individual variants. Each +** entry except the last has the MOREVARIANTS flag set, and each +** contains one of the following capitalization options: +** +** ALLCAPS The word must appear in all capitals. +** CAPITALIZED The word must be capitalized (e.g., London). +** It will also be accepted in all capitals. +** FOLLOWCASE The word must be capitalized exactly like the +** sample in the entry. Prefix (suffix) characters +** must be rendered in the case of the first (last) +** "alphabetic" character. It will also be accepted +** in all capitals. ("Alphabetic" means "mentioned +** in a 'casechars' statement".) +** ANYCASE The word may appear in lowercase, capitalized, +** or all-capitals. +** +** The "mask" field for the entry contains only the affix flag bits that +** are legal for that capitalization. The "word" field will be null +** except for FOLLOWCASE entries, where it will point to the +** correctly-capitalized spelling of the root word. +** +** It is worth discussing why the ALLCAPS option is used in +** the header entry. The header entry accepts an all-capitals +** version of the root plus every affix (this is always legal, since +** words get capitalized in headers and so forth). Further, all of +** the following variant entries will reject any all-capitals form +** that is illegal due to an affix. +** +** Finally, note that variations in the KEEP flag can cause a multiple-variant +** entry as well. For example, if the personal dictionary contains "ALPHA", +** (KEEP flag set) and the user adds "alpha" with the KEEP flag clear, a +** multiple-variant entry will be created so that "alpha" will be accepted +** but only "ALPHA" will actually be kept. +#endif +*/ +#ifdef FULLMASKSET +#define flagfield flags +#else +#define flagfield mask[1] +#endif +#define USED ((MASKTYPE) 1 << (FLAGBASE + 0)) +#define KEEP ((MASKTYPE) 1 << (FLAGBASE + 1)) +#ifdef NO_CAPITALIZATION_SUPPORT +#define ALLFLAGS (USED | KEEP) +#else /* NO_CAPITALIZATION_SUPPORT */ +#define ANYCASE ((MASKTYPE) 0 << (FLAGBASE + 2)) +#define ALLCAPS ((MASKTYPE) 1 << (FLAGBASE + 2)) +#define CAPITALIZED ((MASKTYPE) 2 << (FLAGBASE + 2)) +#define FOLLOWCASE ((MASKTYPE) 3 << (FLAGBASE + 2)) +#define CAPTYPEMASK ((MASKTYPE) 3 << (FLAGBASE + 2)) +#define MOREVARIANTS ((MASKTYPE) 1 << (FLAGBASE + 4)) +#define ALLFLAGS (USED | KEEP | CAPTYPEMASK | MOREVARIANTS) +#define captype(x) ((x) & CAPTYPEMASK) +#endif /* NO_CAPITALIZATION_SUPPORT */ + +/* + * Language tables used to encode prefix and suffix information. + */ +struct flagent +{ + ichar_t * strip; /* String to strip off */ + ichar_t * affix; /* Affix to append */ + short flagbit; /* Flag bit this ent matches */ + short stripl; /* Length of strip */ + short affl; /* Length of affix */ + short numconds; /* Number of char conditions */ + short flagflags; /* Modifiers on this flag */ + char conds[SET_SIZE + MAXSTRINGCHARS]; /* Adj. char conds */ +}; + +/* + * Bits in flagflags + */ +#define FF_CROSSPRODUCT (1 << 0) /* Affix does cross-products */ +#define FF_COMPOUNDONLY (1 << 1) /* Afx works in compounds */ + +union ptr_union /* Aid for building flg ptrs */ +{ + struct flagptr * fp; /* Pointer to more indexing */ + struct flagent * ent; /* First of a list of ents */ +}; + +struct flagptr +{ + union ptr_union pu; /* Ent list or more indexes */ + int numents; /* If zero, pu.fp is valid */ +}; + +/* + * Description of a single string character type. + */ +struct strchartype +{ + char * name; /* Name of the type */ + char * deformatter; /* Deformatter to use */ + char * suffixes; /* File suffixes, null seps */ +}; + +/* + * Header placed at the beginning of the hash file. + */ +struct hashheader +{ + unsigned short magic; /* Magic number for ID */ + unsigned short compileoptions; /* How we were compiled */ + short maxstringchars; /* Max # strchrs we support */ + short maxstringcharlen; /* Max strchr len supported */ + short compoundmin; /* Min lth of compound parts */ + short compoundbit; /* Flag 4 compounding roots */ + int stringsize; /* Size of string table */ + int lstringsize; /* Size of lang. str tbl */ + int tblsize; /* No. entries in hash tbl */ + int stblsize; /* No. entries in sfx tbl */ + int ptblsize; /* No. entries in pfx tbl */ + int sortval; /* Largest sort ID assigned */ + int nstrchars; /* No. strchars defined */ + int nstrchartype; /* No. strchar types */ + int strtypestart; /* Start of strtype table */ + char nrchars[5]; /* Nroff special characters */ + char texchars[13]; /* TeX special characters */ + char compoundflag; /* Compund-word handling */ + char defhardflag; /* Default tryveryhard flag */ + char flagmarker; /* "Start-of-flags" char */ + unsigned short sortorder[SET_SIZE + MAXSTRINGCHARS]; /* Sort ordering */ + ichar_t lowerconv[SET_SIZE + MAXSTRINGCHARS]; /* Lower-conversion table */ + ichar_t upperconv[SET_SIZE + MAXSTRINGCHARS]; /* Upper-conversion table */ + char wordchars[SET_SIZE + MAXSTRINGCHARS]; /* NZ for chars found in wrds */ + char upperchars[SET_SIZE + MAXSTRINGCHARS]; /* NZ for uppercase chars */ + char lowerchars[SET_SIZE + MAXSTRINGCHARS]; /* NZ for lowercase chars */ + char boundarychars[SET_SIZE + MAXSTRINGCHARS]; /* NZ for boundary chars */ + char stringstarts[SET_SIZE]; /* NZ if char can start str */ + char stringchars[MAXSTRINGCHARS][MAXSTRINGCHARLEN + 1]; /* String chars */ + unsigned int stringdups[MAXSTRINGCHARS]; /* No. of "base" char */ + int dupnos[MAXSTRINGCHARS]; /* Dup char ID # */ + unsigned short magic2; /* Second magic for dbl chk */ +}; + +/* hash table magic number */ +#define MAGIC 0x9602 + +/* compile options, put in the hash header for consistency checking */ +#ifdef NO8BIT +# define MAGIC8BIT 0x01 +#else +# define MAGIC8BIT 0x00 +#endif +#ifdef NO_CAPITALIZATION_SUPPORT +# define MAGICCAPITALIZATION 0x00 +#else +# define MAGICCAPITALIZATION 0x02 +#endif +# define MAGICMASKSET 0x04 + +#if MASKBITS <= 32 +# define MAGICMASKSET 0x00 +#else +# if MASKBITS <= 64 +# else +# if MASKBITS <= 128 +# define MAGICMASKSET 0x08 +# else +# define MAGICMASKSET 0x0C +# endif +# endif +#endif + +#define COMPILEOPTIONS (MAGIC8BIT | MAGICCAPITALIZATION | MAGICMASKSET) + +/* +** Offsets into the nroff special-character array +*/ +#define NRLEFTPAREN hashheader.nrchars[0] +#define NRRIGHTPAREN hashheader.nrchars[1] +#define NRDOT hashheader.nrchars[2] +#define NRBACKSLASH hashheader.nrchars[3] +#define NRSTAR hashheader.nrchars[4] + +/* +** Offsets into the TeX special-character array +*/ +#define TEXLEFTPAREN hashheader.texchars[0] +#define TEXRIGHTPAREN hashheader.texchars[1] +#define TEXLEFTSQUARE hashheader.texchars[2] +#define TEXRIGHTSQUARE hashheader.texchars[3] +#define TEXLEFTCURLY hashheader.texchars[4] +#define TEXRIGHTCURLY hashheader.texchars[5] +#define TEXLEFTANGLE hashheader.texchars[6] +#define TEXRIGHTANGLE hashheader.texchars[7] +#define TEXBACKSLASH hashheader.texchars[8] +#define TEXDOLLAR hashheader.texchars[9] +#define TEXSTAR hashheader.texchars[10] +#define TEXDOT hashheader.texchars[11] +#define TEXPERCENT hashheader.texchars[12] + +/* +** Values for compoundflag +*/ +#define COMPOUND_NEVER 0 /* Compound words are never good */ +#define COMPOUND_ANYTIME 1 /* Accept run-together words */ +#define COMPOUND_CONTROLLED 2 /* Compounds controlled by afx flags */ +/* +** These macros are similar to the ones above, but they take into account +** the possibility of string characters. Note well that they take a POINTER, +** not a character. +** +** The "l_" versions set "len" to the length of the string character as a +** handy side effect. (Note that the global "laststringch" is also set, +** and sometimes used, by these macros.) +** +** The "l1_" versions go one step further and guarantee that the "len" +** field is valid for *all* characters, being set to 1 even if the macro +** returns false. This macro is a great example of how NOT to write +** readable C. +*/ +/*TF NOTE: This is actually defined in code (makedent) now */ +#if 0 +#define isstringch(ptr, canon) (isstringstart (*(ptr)) \ + && stringcharlen ((ptr), (canon)) > 0) +#define l_isstringch(ptr, len, canon) \ + (isstringstart (*(ptr)) \ + && (len = stringcharlen ((ptr), (canon))) \ + > 0) +#define l1_isstringch(ptr, len, canon) \ + (len = 1, \ + isstringstart ((unsigned char)(*(ptr))) \ + && ((len = \ + stringcharlen ((ptr), (canon))) \ + > 0 \ + ? 1 : (len = 1, 0))) +#endif + +/* + * Sizes of buffers returned by ichartosstr/strtosichar. + */ +#define ICHARTOSSTR_SIZE (INPUTWORDLEN + 4 * MAXAFFIXLEN + 4) +#define STRTOSICHAR_SIZE ((INPUTWORDLEN + 4 * MAXAFFIXLEN + 4) \ + * sizeof (ichar_t)) +/* TF CHANGE: We should fill this as a structure + and then use it throughout. +*/ + +/* + * Initialized variables. These are generated using macros so that they + * may be consistently declared in all programs. Numerous examples of + * usage are given below. + */ +#ifdef MAIN +#define INIT(decl, init) decl = init +#else +#define INIT(decl, init) extern decl +#endif + +#ifdef MINIMENU +INIT (int minimenusize, 2); /* MUST be either 2 or zero */ +#else /* MINIMENU */ +INIT (int minimenusize, 0); /* MUST be either 2 or zero */ +#endif /* MINIMENU */ + +INIT (int eflag, 0); /* NZ for expand mode */ +INIT (int dumpflag, 0); /* NZ to do dump mode */ +INIT (int fflag, 0); /* NZ if -f specified */ +#ifndef USG +INIT (int sflag, 0); /* NZ to stop self after EOF */ +#endif +INIT (int vflag, 0); /* NZ to display characters as M-xxx */ +INIT (int xflag, DEFNOBACKUPFLAG); /* NZ to suppress backups */ +INIT (int deftflag, -1); /* NZ for TeX mode by default */ +INIT (int tflag, DEFTEXFLAG); /* NZ for TeX mode in current file */ +INIT (int prefstringchar, -1); /* Preferred string character type */ + +INIT (int terse, 0); /* NZ for "terse" mode */ + +INIT (char tempfile[MAXPATHLEN], ""); /* Name of file we're spelling into */ + +INIT (int minword, MINWORD); /* Longest always-legal word */ +INIT (int sortit, 1); /* Sort suggestions alphabetically */ +INIT (int compoundflag, -1); /* How to treat compounds: see above */ +INIT (int tryhardflag, -1); /* Always call tryveryhard */ + +INIT (char * currentfile, NULL); /* Name of current input file */ + +/* Odd numbers for math mode in LaTeX; even for LR or paragraph mode */ +INIT (int math_mode, 0); +/* P -- paragraph or LR mode + * b -- parsing a \begin statement + * e -- parsing an \end statement + * r -- parsing a \ref type of argument. + * m -- looking for a \begin{minipage} argument. + */ +INIT (char LaTeX_Mode, 'P'); + +#ifdef __cplusplus +} +#endif /* c++ */ + +#endif /* ISPELL_H */ diff --git a/src/ispell/ispell_checker.cpp b/src/ispell/ispell_checker.cpp new file mode 100644 index 0000000..62c967d --- /dev/null +++ b/src/ispell/ispell_checker.cpp @@ -0,0 +1,686 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +#include +#include +#include + +#include +#include + +#include "enchant-provider.h" +#include "sp_spell.h" +#include "ispell_checker.h" +#include "enchant.h" + +#ifndef ENCHANT_ISPELL_HOME_DIR +#define ENCHANT_ISPELL_HOME_DIR "ispell" +#endif + +ENCHANT_PLUGIN_DECLARE("Ispell") + +#define G_ICONV_INVALID (GIConv)-1 + +/***************************************************************************/ + +typedef struct str_ispell_map +{ + const char * lang; + const char * dict; + const char * enc; +} IspellMap; + +static const IspellMap ispell_map [] = { + {"ca" ,"catala.hash" ,"iso-8859-1" }, + {"cs" ,"czech.hash" ,"iso-8859-2" }, + {"da" ,"dansk.hash" ,"iso-8859-1" }, + {"de" ,"deutsch.hash" ,"iso-8859-1" }, + {"de_CH" ,"swiss.hash" ,"iso-8859-1" }, + {"el" ,"ellhnika.hash" ,"iso-8859-7" }, + {"en" ,"british.hash" ,"iso-8859-1" }, + {"en_PH" ,"american.hash" ,"iso-8859-1" }, + {"en_US" ,"american.hash" ,"iso-8859-1" }, + {"eo" ,"esperanto.hash" ,"iso-8859-3" }, + {"es" ,"espanol.hash" ,"iso-8859-1" }, + {"fi" ,"finnish.hash" ,"iso-8859-1" }, + {"fr" ,"francais.hash" ,"iso-8859-1" }, + {"hu" ,"hungarian.hash" ,"iso-8859-2" }, + {"ga" ,"irish.hash" ,"iso-8859-1" }, + {"gl" ,"galician.hash" ,"iso-8859-1" }, + {"ia" ,"interlingua.hash" ,"iso-8859-1" }, + {"it" ,"italian.hash" ,"iso-8859-1" }, + {"la" ,"mlatin.hash" ,"iso-8859-1" }, + {"lt" ,"lietuviu.hash" ,"iso-8859-13" }, + {"nl" ,"nederlands.hash" ,"iso-8859-1" }, + {"nb" ,"norsk.hash" ,"iso-8859-1" }, + {"nn" ,"nynorsk.hash" ,"iso-8859-1" }, + {"no" ,"norsk.hash" ,"iso-8859-1" }, + {"pl" ,"polish.hash" ,"iso-8859-2" }, + {"pt" ,"brazilian.hash" ,"iso-8859-1" }, + {"pt_PT" ,"portugues.hash" ,"iso-8859-1" }, + {"ru" ,"russian.hash" ,"koi8-r" }, + {"sc" ,"sardinian.hash" ,"iso-8859-1" }, + {"sk" ,"slovak.hash" ,"iso-8859-2" }, + {"sl" ,"slovensko.hash" ,"iso-8859-2" }, + {"sv" ,"svenska.hash" ,"iso-8859-1" }, + {"uk" ,"ukrainian.hash" ,"koi8-u" }, + {"yi" ,"yiddish-yivo.hash" ,"utf-8" } +}; + +static const size_t size_ispell_map = G_N_ELEMENTS(ispell_map); + +static bool +g_iconv_is_valid(GIConv i) +{ + return (i != G_ICONV_INVALID); +} + +void +ISpellChecker::try_autodetect_charset(const char * const inEncoding) +{ + if (inEncoding && strlen(inEncoding)) + { + m_translate_in = g_iconv_open(inEncoding, "UTF-8"); + m_translate_out = g_iconv_open("UTF-8", inEncoding); + } +} + +/***************************************************************************/ +/***************************************************************************/ + +ISpellChecker::ISpellChecker(EnchantBroker * broker) +: m_broker(broker), + deftflag(-1), + prefstringchar(-1), + m_bSuccessfulInit(false), + m_BC(NULL), + m_cd(NULL), + m_cl(NULL), + m_cm(NULL), + m_ho(NULL), + m_nd(NULL), + m_so(NULL), + m_se(NULL), + m_ti(NULL), + m_te(NULL), + m_hashstrings(NULL), + m_hashtbl(NULL), + m_pflaglist(NULL), + m_sflaglist(NULL), + m_chartypes(NULL), + m_infile(NULL), + m_outfile(NULL), + m_askfilename(NULL), + m_Trynum(0), + m_translate_in(G_ICONV_INVALID), + m_translate_out(G_ICONV_INVALID) +{ + memset(m_sflagindex,0,sizeof(m_sflagindex)); + memset(m_pflagindex,0,sizeof(m_pflagindex)); +} + +#ifndef FREEP +#define FREEP(p) do { if (p) free(p); } while (0) +#endif + +ISpellChecker::~ISpellChecker() +{ + if (m_bSuccessfulInit) { + // only cleanup our mess if we were successfully initialized + + clearindex (m_pflagindex); + clearindex (m_sflagindex); + } + + FREEP(m_hashtbl); + FREEP(m_hashstrings); + FREEP(m_sflaglist); + FREEP(m_chartypes); + + if (g_iconv_is_valid (m_translate_in )) + g_iconv_close(m_translate_in); + m_translate_in = G_ICONV_INVALID; + if (g_iconv_is_valid(m_translate_out)) + g_iconv_close(m_translate_out); + m_translate_out = G_ICONV_INVALID; +} + +bool +ISpellChecker::checkWord(const char * const utf8Word, size_t length) +{ + ichar_t iWord[INPUTWORDLEN + MAXAFFIXLEN]; + char szWord[INPUTWORDLEN + MAXAFFIXLEN]; + + if (!m_bSuccessfulInit) + return false; + + if (!utf8Word || length >= (INPUTWORDLEN + MAXAFFIXLEN) || length == 0) + return false; + + bool retVal = false; + + if (!g_iconv_is_valid(m_translate_in)) + return false; + else + { + /* convert to 8bit string and null terminate */ + size_t len_in, len_out, result; + // the 8bit encodings use precomposed forms + char *normalizedWord = g_utf8_normalize (utf8Word, length, G_NORMALIZE_NFC); + char *In = normalizedWord; + char *Out = szWord; + + len_in = strlen(In); + len_out = sizeof( szWord ) - 1; + result = g_iconv(m_translate_in, &In, &len_in, &Out, &len_out); + g_free(normalizedWord); + if ((size_t)-1 == result) + return false; + *Out = '\0'; + } + + if (!strtoichar(iWord, szWord, sizeof(iWord), 0)) + { + if (good(iWord, 0, 0, 1, 0) >= 1 || + compoundgood(iWord, 1)) + { + retVal = true; + } + } + + return retVal; +} + +char ** +ISpellChecker::suggestWord(const char * const utf8Word, size_t length, + size_t * out_n_suggestions) +{ + ichar_t iWord[INPUTWORDLEN + MAXAFFIXLEN]; + char word8[INPUTWORDLEN + MAXAFFIXLEN]; + int c; + + *out_n_suggestions = 0; + + if (!m_bSuccessfulInit) + return NULL; + if (!utf8Word || length >= (INPUTWORDLEN + MAXAFFIXLEN) || length == 0) + return NULL; + + if (!g_iconv_is_valid(m_translate_in)) + return NULL; + else + { + /* convert to 8bit string and null terminate */ + + size_t len_in, len_out, result; + // the 8bit encodings use precomposed forms + char *normalizedWord = g_utf8_normalize (utf8Word, length, G_NORMALIZE_NFC); + char *In = normalizedWord; + char *Out = word8; + len_in = strlen(In); + len_out = sizeof( word8 ) - 1; + result = g_iconv(m_translate_in, &In, &len_in, &Out, &len_out); + g_free(normalizedWord); + if ((size_t)-1 == result) + return NULL; + *Out = '\0'; + } + + if (!strtoichar(iWord, word8, sizeof(iWord), 0)) + makepossibilities(iWord); + else + return NULL; + + char **sugg_arr = NULL; + *out_n_suggestions = m_pcount; + + { + sugg_arr = g_new0 (char *, *out_n_suggestions + 1); + + for (c = 0; c < m_pcount; c++) + { + int l = strlen(m_possibilities[c]); + + char *utf8Sugg = g_new0(char, INPUTWORDLEN + MAXAFFIXLEN + 1); + + if (!g_iconv_is_valid(m_translate_out)) + { + /* copy to 8bit string and null terminate */ + for (int x = 0; x < l; x++) + utf8Sugg[x] = static_cast(m_possibilities[c][x]); + utf8Sugg[l] = 0; + } + else + { + /* convert to 32bit string and null terminate */ + + size_t len_in, len_out; + char *In = m_possibilities[c]; + char *Out = reinterpret_cast(utf8Sugg); + + len_in = l; + len_out = INPUTWORDLEN + MAXAFFIXLEN; + if ((size_t)-1 == g_iconv(m_translate_out, &In, &len_in, &Out, &len_out)) { + *out_n_suggestions = c; + return sugg_arr; + } + *(Out) = 0; + } + + sugg_arr[c] = utf8Sugg; + } + } + + return sugg_arr; +} + +static GSList * +ispell_checker_get_dictionary_dirs (EnchantBroker * broker) +{ + GSList *dirs = NULL; + + { + GSList *config_dirs, *iter; + + config_dirs = enchant_get_user_config_dirs (); + + for (iter = config_dirs; iter; iter = iter->next) + { + dirs = g_slist_append (dirs, g_build_filename ((const gchar *)iter->data, + ENCHANT_ISPELL_HOME_DIR, NULL)); + } + + g_slist_foreach (config_dirs, (GFunc)g_free, NULL); + g_slist_free (config_dirs); + } + +#if 0 + { + const gchar* const * system_data_dirs = g_get_system_data_dirs (); + const gchar* const * iter; + + for (iter = system_data_dirs; *iter; iter++) + { + dirs = g_slist_append (dirs, g_build_filename (*iter, "ispell", "dicts", NULL)); + } + } +#endif + + /* until I work out how to link the modules against enchant in MacOSX - fjf + */ +#ifndef XP_TARGET_COCOA + char * ispell_prefix = NULL; + + /* Look for explicitly set registry values */ + ispell_prefix = enchant_get_registry_value ("Ispell", "Data_Dir"); + if (ispell_prefix) + dirs = g_slist_append (dirs, ispell_prefix); + + /* Dynamically locate library and search for modules relative to it. */ + char * enchant_prefix = enchant_get_prefix_dir(); + if(enchant_prefix) + { + ispell_prefix = g_build_filename(enchant_prefix, "share", "enchant", "ispell", NULL); + g_free(enchant_prefix); + dirs = g_slist_append (dirs, ispell_prefix); + } +#endif + +#ifdef ENCHANT_ISPELL_DICT_DIR + dirs = g_slist_append (dirs, g_strdup (ENCHANT_ISPELL_DICT_DIR)); +#endif + + { + GSList *config_dirs, *iter; + + config_dirs = enchant_get_dirs_from_param (broker, "enchant.ispell.dictionary.path"); + + for (iter = config_dirs; iter; iter = iter->next) + { + dirs = g_slist_append (dirs, g_strdup ((const gchar *)iter->data)); + } + + g_slist_foreach (config_dirs, (GFunc)g_free, NULL); + g_slist_free (config_dirs); + } + + return dirs; +} + +static void +s_buildHashNames (std::vector & names, EnchantBroker * broker, const char * dict) +{ + names.clear (); + + GSList *dirs, *iter; + + dirs = ispell_checker_get_dictionary_dirs(broker); + for (iter = dirs; iter; iter = iter->next) + { + char *tmp; + + tmp = g_build_filename ((const gchar *)iter->data, dict, NULL); + names.push_back (tmp); + g_free (tmp); + } + + g_slist_foreach (dirs, (GFunc)g_free, NULL); + g_slist_free (dirs); +} + +char * +ISpellChecker::loadDictionary (const char * szdict) +{ + std::vector dict_names; + + s_buildHashNames (dict_names, m_broker, szdict); + + for (size_t i = 0; i < dict_names.size(); i++) + { + if (linit(const_cast(dict_names[i].c_str())) >= 0) + return g_strdup (dict_names[i].c_str()); + } + + return NULL; +} + +/*! + * Load ispell dictionary hash file for given language. + * + * \param szLang - The language tag ("en-US") we want to use + * \return The name of the dictionary file + */ +bool +ISpellChecker::loadDictionaryForLanguage ( const char * szLang ) +{ + char *hashname = NULL; + + const char * encoding = NULL; + const char * szFile = NULL; + + for (size_t i = 0; i < size_ispell_map; i++) + { + const IspellMap * mapping = (const IspellMap *)(&(ispell_map[i])); + if (!strcmp (szLang, mapping->lang)) + { + szFile = mapping->dict; + encoding = mapping->enc; + break; + } + } + + if (!szFile || !strlen(szFile)) + return false; + + alloc_ispell_struct(); + + if (!(hashname = loadDictionary(szFile))) + return false; + + // one of the two above calls succeeded + setDictionaryEncoding (hashname, encoding); + g_free (hashname); + + return true; +} + +void +ISpellChecker::setDictionaryEncoding( const char * hashname, const char * encoding ) +{ + /* Get Hash encoding from XML file. This should always work! */ + try_autodetect_charset(encoding); + + if (g_iconv_is_valid(m_translate_in) && g_iconv_is_valid(m_translate_out)) + { + /* We still have to setup prefstringchar*/ + prefstringchar = findfiletype("utf8", 1, deftflag < 0 ? &deftflag + : static_cast(NULL)); + + if (prefstringchar < 0) + { + char teststring[64]; + for(int n1 = 1; n1 <= 15; n1++) + { + sprintf(teststring, "latin%d", n1); + prefstringchar = findfiletype(teststring, 1, + deftflag < 0 ? &deftflag : static_cast(NULL)); + if (prefstringchar >= 0) + break; + } + } + + return; /* success */ + } + + /* Test for UTF-8 first */ + prefstringchar = findfiletype("utf8", 1, deftflag < 0 ? &deftflag : static_cast(NULL)); + if (prefstringchar >= 0) + { + m_translate_in = g_iconv_open("UTF-8", "UTF-8"); + m_translate_out = g_iconv_open("UTF-8", "UTF-8"); + } + + if (g_iconv_is_valid(m_translate_in) && g_iconv_is_valid(m_translate_out)) + return; /* success */ + + /* Test for "latinN" */ + if (!g_iconv_is_valid(m_translate_in)) + { + /* Look for "altstringtype" names from latin1 to latin15 */ + for(int n1 = 1; n1 <= 15; n1++) + { + char * teststring = g_strdup_printf("latin%u", n1); + prefstringchar = findfiletype(teststring, 1, + deftflag < 0 ? &deftflag : static_cast(NULL)); + if (prefstringchar >= 0) + { + m_translate_in = g_iconv_open(teststring, "UTF-8"); + m_translate_out = g_iconv_open("UTF-8", teststring); + g_free (teststring); + break; + } + else + { + g_free (teststring); + } + } + } + + /* If nothing found, use latin1 */ + if (!g_iconv_is_valid(m_translate_in)) + { + m_translate_in = g_iconv_open("latin1", "UTF-8"); + m_translate_out = g_iconv_open("UTF-8", "latin1"); + } +} + +bool +ISpellChecker::requestDictionary(const char *szLang) +{ + if (!loadDictionaryForLanguage (szLang)) + { + // handle a shortened version of the language tag: en_US => en + std::string shortened_dict (szLang); + size_t uscore_pos; + + if ((uscore_pos = shortened_dict.rfind ('_')) != ((size_t)-1)) { + shortened_dict = shortened_dict.substr(0, uscore_pos); + if (!loadDictionaryForLanguage (shortened_dict.c_str())) + return false; + } else + return false; + } + + m_bSuccessfulInit = true; + + if (prefstringchar < 0) + m_defdupchar = 0; + else + m_defdupchar = prefstringchar; + + return true; +} + +static char ** +ispell_dict_suggest (EnchantDict * me, const char *const word, + size_t len, size_t * out_n_suggs) +{ + ISpellChecker * checker; + + checker = (ISpellChecker *) me->user_data; + return checker->suggestWord (word, len, out_n_suggs); +} + +static int +ispell_dict_check (EnchantDict * me, const char *const word, size_t len) +{ + ISpellChecker * checker; + + checker = (ISpellChecker *) me->user_data; + + if (checker->checkWord(word, len)) + return 0; + + return 1; +} + +static EnchantDict * +ispell_provider_request_dict (EnchantProvider * me, const char *const tag) +{ + EnchantDict *dict; + ISpellChecker * checker; + + checker = new ISpellChecker (me->owner); + + if (!checker) + { + return NULL; + } + + if (!checker->requestDictionary(tag)) { + delete checker; + return NULL; + } + + dict = g_new0 (EnchantDict, 1); + dict->user_data = (void *) checker; + dict->check = ispell_dict_check; + dict->suggest = ispell_dict_suggest; + // don't implement session or personal + + return dict; +} + +static void +ispell_provider_dispose_dict (EnchantProvider * me, EnchantDict * dict) +{ + ISpellChecker * checker; + + checker = (ISpellChecker *) dict->user_data; + delete checker; + + g_free (dict); +} + +static int +_ispell_provider_dictionary_exists (EnchantBroker * broker, const char *const szFile) +{ + std::vector names; + + s_buildHashNames (names, broker, szFile); + for (size_t i = 0; i < names.size(); i++) { + if (g_file_test (names[i].c_str(), G_FILE_TEST_EXISTS)) + return 1; + } + + return 0; +} + +extern "C" { + +ENCHANT_MODULE_EXPORT (EnchantProvider *) + init_enchant_provider (void); + +static char ** +ispell_provider_list_dictionaries (EnchantProvider * me, + size_t * out_n_dicts) +{ + size_t i, nb; + char ** out_dicts = g_new0 (char *, size_ispell_map + 1); + + (void)me; + + nb = 0; + for (i = 0; i < size_ispell_map; i++) + if (_ispell_provider_dictionary_exists (me->owner, ispell_map[i].dict)) + out_dicts[nb++] = g_strdup (ispell_map[i].lang); + + *out_n_dicts = nb; + if (nb == 0) { + g_free (out_dicts); + out_dicts = NULL; + } + + return out_dicts; +} + +static int +ispell_provider_dictionary_exists (struct str_enchant_provider * me, + const char *const tag) +{ + std::string shortened_dict (tag); + size_t uscore_pos; + if ((uscore_pos = shortened_dict.rfind ('_')) != ((size_t)-1)) + shortened_dict = shortened_dict.substr(0, uscore_pos); + + for (size_t i = 0; i < size_ispell_map; i++) + { + const IspellMap * mapping = (const IspellMap *)(&(ispell_map[i])); + if (!strcmp (tag, mapping->lang) || !strcmp (shortened_dict.c_str(), mapping->lang)) + return _ispell_provider_dictionary_exists(me->owner, mapping->dict); + } + + return 0; +} + +static void +ispell_provider_free_string_list (EnchantProvider * me, char **str_list) +{ + g_strfreev (str_list); +} + +static void +ispell_provider_dispose (EnchantProvider * me) +{ + g_free (me); +} + +static const char * +ispell_provider_identify (EnchantProvider * me) +{ + return "ispell"; +} + +static const char * +ispell_provider_describe (EnchantProvider * me) +{ + return "Ispell Provider"; +} + +EnchantProvider * +init_enchant_provider (void) +{ + EnchantProvider *provider; + + provider = g_new0 (EnchantProvider, 1); + provider->dispose = ispell_provider_dispose; + provider->request_dict = ispell_provider_request_dict; + provider->dispose_dict = ispell_provider_dispose_dict; + provider->dictionary_exists = ispell_provider_dictionary_exists; + provider->identify = ispell_provider_identify; + provider->describe = ispell_provider_describe; + provider->list_dicts = ispell_provider_list_dictionaries; + provider->free_string_list = ispell_provider_free_string_list; + + return provider; +} + +} // extern C linkage diff --git a/src/ispell/ispell_checker.h b/src/ispell/ispell_checker.h new file mode 100644 index 0000000..8ffcc31 --- /dev/null +++ b/src/ispell/ispell_checker.h @@ -0,0 +1,243 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +#ifndef ISPELL_CHECKER_H +#define ISPELL_CHECKER_H + +#include // give glib a chance to override MAXPATHLEN first before it is set in ispell.h +#include "ispell.h" +#include "enchant.h" + +class ISpellChecker +{ +public: + ISpellChecker(EnchantBroker * broker); + ~ISpellChecker(); + + bool checkWord(const char * const word, size_t len); + char ** suggestWord(const char * const word, size_t len, size_t * out_n_suggs); + + bool requestDictionary (const char * szLang); + +private: + EnchantBroker* m_broker; + + ISpellChecker(); + ISpellChecker(const ISpellChecker&); // no impl + void operator=(const ISpellChecker&); // no impl + + char * loadDictionary (const char * szLang ); + bool loadDictionaryForLanguage ( const char * szLang ); + void setDictionaryEncoding ( const char * hashname, const char * enc ); + + // + // The member functions after this point were formerly global functions + // passed a context structure pointer... + // + + void try_autodetect_charset(const char * inEncoding); + + // + // From ispell correct.c + // + + int casecmp P ((char * a, char * b, int canonical)); + void makepossibilities P ((ichar_t * word)); + int insert P ((ichar_t * word)); +#ifndef NO_CAPITALIZATION_SUPPORT + void wrongcapital P ((ichar_t * word)); +#endif /* NO_CAPITALIZATION_SUPPORT */ + void wrongletter P ((ichar_t * word)); + void extraletter P ((ichar_t * word)); + void missingletter P ((ichar_t * word)); + void missingspace P ((ichar_t * word)); + int compoundgood P ((ichar_t * word, int pfxopts)); + void transposedletter P ((ichar_t * word)); + int ins_cap P ((ichar_t * word, ichar_t * pattern)); + int save_cap P ((ichar_t * word, ichar_t * pattern, + ichar_t savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN])); + int ins_root_cap P ((ichar_t * word, ichar_t * pattern, + int prestrip, int preadd, int sufstrip, int sufadd, + struct dent * firstdent, struct flagent * pfxent, + struct flagent * sufent)); + void save_root_cap P ((ichar_t * word, ichar_t * pattern, + int prestrip, int preadd, int sufstrip, int sufadd, + struct dent * firstdent, struct flagent * pfxent, + struct flagent * sufent, + ichar_t savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN], + int * nsaved)); + + // + // From ispell good.c + // + + int good (ichar_t* w, int ignoreflagbits, int allhits, int pfxopts, int sfxopts); + void chk_aff (ichar_t* word, ichar_t* ucword, int len, int ignoreflagbits, int allhits, int pfxopts, int sfxopts); + int linit(char*); + struct dent * ispell_lookup (ichar_t* s, int dotree); + int strtoichar (ichar_t* out, char* in, int outlen, int canonical); + int ichartostr (char* out, ichar_t* in, int outlen, int canonical); + char * ichartosstr (ichar_t* in, int canonical); + int findfiletype (const char * name, int searchnames, int * deformatter); + long whatcap (ichar_t* word); + + /* + HACK: macros replaced with function implementations + so we could do a side-effect-free check for unicode + characters which aren't in hashheader + */ + char myupper(ichar_t c); + char mylower(ichar_t c); + int myspace(ichar_t c); + char iswordch(ichar_t c); + char isboundarych(ichar_t c); + char isstringstart(ichar_t c); + ichar_t mytolower(ichar_t c); + ichar_t mytoupper(ichar_t c); + +#ifndef ICHAR_IS_CHAR + int cap_ok (ichar_t* word, struct success* hit, int len); + + int hash (ichar_t* s, int hashtblsize); +#endif + + // + // From ispell lookup.c + // + + void clearindex P ((struct flagptr * indexp)); + void initckch P ((char *)); + + void alloc_ispell_struct(); + void free_ispell_struct(); + + // + // From ispell makedent.c + // + + int addvheader P ((struct dent * ent)); + void upcase P ((ichar_t * string)); + void lowcase P ((ichar_t * string)); + void chupcase P ((char * s)); + + int stringcharlen P ((char * bufp, int canonical)); + ichar_t * strtosichar P ((char * in, int canonical)); + char * printichar P ((int in)); + + // + // From ispell tgood.c + // + + void pfx_list_chk P ((ichar_t * word, ichar_t * ucword, + int len, int optflags, int sfxopts, struct flagptr * ind, + int ignoreflagbits, int allhits)); + void chk_suf P ((ichar_t * word, ichar_t * ucword, int len, + int optflags, struct flagent * pfxent, int ignoreflagbits, + int allhits)); + void suf_list_chk P ((ichar_t * word, ichar_t * ucword, int len, + struct flagptr * ind, int optflags, struct flagent * pfxent, + int ignoreflagbits, int allhits)); + int expand_pre P ((char * croot, ichar_t * rootword, + MASKTYPE mask[], int option, char * extra)); + int pr_pre_expansion P ((char * croot, ichar_t * rootword, + struct flagent * flent, MASKTYPE mask[], int option, + char * extra)); + int expand_suf P ((char * croot, ichar_t * rootword, + MASKTYPE mask[], int optflags, int option, char * extra)); + int pr_suf_expansion P ((char * croot, ichar_t * rootword, + struct flagent * flent, int option, char * extra)); + void forcelc P ((ichar_t * dst, int len)); + + /* this is used for converting form unsigned short to UCS-4 */ + + int deftflag; /* NZ for TeX mode by default */ + int prefstringchar; /* Preferred string character type */ + bool m_bSuccessfulInit; + + // + // The members after this point were formerly global variables + // in the original ispell code + // + + char * m_BC; /* backspace if not ^H */ + char * m_cd; /* clear to end of display */ + char * m_cl; /* clear display */ + char * m_cm; /* cursor movement */ + char * m_ho; /* home */ + char * m_nd; /* non-destructive space */ + char * m_so; /* standout */ + char * m_se; /* standout end */ + int m_sg; /* space taken by so/se */ + char * m_ti; /* terminal initialization sequence */ + char * m_te; /* terminal termination sequence */ + int m_li; /* lines */ + int m_co; /* columns */ + + char m_ctoken[INPUTWORDLEN + MAXAFFIXLEN]; /* Current token as char */ + ichar_t m_itoken[INPUTWORDLEN + MAXAFFIXLEN]; /* Ctoken as ichar_t str */ + + int m_numhits; /* number of hits in dictionary lookups */ + struct success + m_hits[MAX_HITS]; /* table of hits gotten in lookup */ + + char * m_hashstrings; /* Strings in hash table */ + struct hashheader + m_hashheader; /* Header of hash table */ + struct dent * + m_hashtbl; /* Main hash table, for dictionary */ + int m_hashsize; /* Size of main hash table */ + + char m_hashname[MAXPATHLEN]; /* Name of hash table file */ + + int m_aflag; /* NZ if -a or -A option specified */ + int m_cflag; /* NZ if -c (crunch) option */ + int m_lflag; /* NZ if -l (list) option */ + int m_incfileflag; /* whether xgets() acts exactly like gets() */ + int m_nodictflag; /* NZ if dictionary not needed */ + + int m_uerasechar; /* User's erase character, from stty */ + int m_ukillchar; /* User's kill character */ + + unsigned int m_laststringch; /* Number of last string character */ + int m_defdupchar; /* Default duplicate string type */ + + int m_numpflags; /* Number of prefix flags in table */ + int m_numsflags; /* Number of suffix flags in table */ + struct flagptr m_pflagindex[SET_SIZE + MAXSTRINGCHARS]; + /* Fast index to pflaglist */ + struct flagent * m_pflaglist; /* Prefix flag control list */ + struct flagptr m_sflagindex[SET_SIZE + MAXSTRINGCHARS]; + /* Fast index to sflaglist */ + struct flagent * m_sflaglist; /* Suffix flag control list */ + + struct strchartype * /* String character type collection */ + m_chartypes; + + FILE * m_infile; /* File being corrected */ + FILE * m_outfile; /* Corrected copy of infile */ + + char * m_askfilename; /* File specified in -f option */ + + int m_changes; /* NZ if changes made to cur. file */ + int m_readonly; /* NZ if current file is readonly */ + int m_quit; /* NZ if we're done with this file */ + +#define MAXPOSSIBLE 100 /* Max no. of possibilities to generate */ + + char m_possibilities[MAXPOSSIBLE][INPUTWORDLEN + MAXAFFIXLEN]; + /* Table of possible corrections */ + int m_pcount; /* Count of possibilities generated */ + int m_maxposslen; /* Length of longest possibility */ + int m_easypossibilities; /* Number of "easy" corrections found */ + /* ..(defined as those using legal affixes) */ + + /* + * The following array contains a list of characters that should be tried + * in "missingletter." Note that lowercase characters are omitted. + */ + int m_Trynum; /* Size of "Try" array */ + ichar_t m_Try[SET_SIZE + MAXSTRINGCHARS]; + + GIConv m_translate_in; /* Selected translation from/to Unicode */ + GIConv m_translate_out; +}; + +#endif /* ISPELL_CHECKER_H */ diff --git a/src/ispell/ispell_def.h b/src/ispell/ispell_def.h new file mode 100644 index 0000000..5710197 --- /dev/null +++ b/src/ispell/ispell_def.h @@ -0,0 +1,5 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* largest word accepted from a file by any input routine, plus one */ +#ifndef INPUTWORDLEN +#define INPUTWORDLEN 100 +#endif diff --git a/src/ispell/lookup.cpp b/src/ispell/lookup.cpp new file mode 100644 index 0000000..28e0244 --- /dev/null +++ b/src/ispell/lookup.cpp @@ -0,0 +1,744 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * lookup.c - see if a word appears in the dictionary + * + * Pace Willisson, 1983 + * + * Copyright 1987, 1988, 1989, 1992, 1993, Geoff Kuenning, Granada Hills, CA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All modifications to the source code must be clearly marked as + * such. Binary redistributions based on modified source code + * must be clearly marked as modified versions in the documentation + * and/or other materials provided with the distribution. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by Geoff Kuenning and + * other unpaid contributors. + * 5. The name of Geoff Kuenning may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING 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 GEOFF KUENNING 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. + */ + +/* + * $Log$ + * Revision 1.7 2003/09/25 02:44:48 dom + * bug 5813 + * + * Revision 1.6 2003/08/26 13:20:40 dom + * ispell crasher fix, implement enchant_dictionary_release + * + * Revision 1.5 2003/08/26 13:08:03 uwog + * Fix segfault when the requested dictionary couldn't be found. + * + * Revision 1.4 2003/08/14 16:27:36 dom + * update some documentation + * + * Revision 1.3 2003/07/28 20:40:27 dom + * fix up the license clause, further win32-registry proof some directory getting functions + * + * Revision 1.2 2003/07/16 22:52:47 dom + * LGPL + exception license + * + * Revision 1.1 2003/07/15 01:15:07 dom + * ispell enchant backend + * + * Revision 1.3 2003/01/29 05:50:12 hippietrail + * + * Fixed my mess in EncodingManager. + * Changed many C casts to C++ casts. + * + * Revision 1.2 2003/01/25 03:16:05 hippietrail + * + * An UT_ICONV_INVALID fix which escaped the last commit. + * + * Revision 1.1 2003/01/24 05:52:34 hippietrail + * + * Refactored ispell code. Old ispell global variables had been put into + * an allocated structure, a pointer to which was passed to many functions. + * I have now made all such functions and variables private members of the + * ISpellChecker class. It was C OO, now it's C++ OO. + * + * I've fixed the makefiles and tested compilation but am unable to test + * operation. Please back out my changes if they cause problems which + * are not obvious or easy to fix. + * + * Revision 1.12 2003/01/06 18:48:39 dom + * ispell cleanup, start of using new 'add' save features + * + * Revision 1.11 2002/09/19 05:31:17 hippietrail + * + * More Ispell cleanup. Conditional globals and DEREF macros are removed. + * K&R function declarations removed, converted to Doxygen style comments + * where possible. No code has been changed (I hope). Compiles for me but + * unable to test. + * + * Revision 1.10 2002/09/17 03:03:30 hippietrail + * + * After seeking permission on the developer list I've reformatted all the + * spelling source which seemed to have parts which used 2, 3, 4, and 8 + * spaces for tabs. It should all look good with our standard 4-space + * tabs now. + * I've concentrated just on indentation in the actual code. More prettying + * could be done. + * * NO code changes were made * + * + * Revision 1.9 2002/09/13 17:20:13 mpritchett + * Fix more warnings for Linux build + * + * Revision 1.8 2002/05/03 09:49:43 fjfranklin + * o hash downloader update (Gabriel Gerhardsson) + * - Comment out the "Can't open " printf. + * - Make the progressbar more clean at the begining of the download. + * - Add support for tarballs that doesn't have the full path included + * - Fix copyright headers on the newly added files (*HashDownloader.*) + * + * Revision 1.7 2001/08/27 19:06:30 dom + * Lots of compilation fixes + * + * Revision 1.6 2001/08/10 18:32:40 dom + * Spelling and iconv updates. god, i hate iconv + * + * Revision 1.5 2001/08/10 09:57:49 hub + * Patch by sobomax@FreeBSD.org + * #include "iconv.h" directive is missed from src/other/spell/xp/lookup.c and + * src/wp/impexp/xp/ie_imp_RTF.cpp. + * See bug 1823 + * + * Revision 1.4 2001/07/18 17:46:01 dom + * Module changes, and fix compiler warnings + * + * Revision 1.3 2001/06/12 21:32:49 dom + * More ispell work... + * + * Revision 1.2 2001/05/12 16:05:42 thomasf + * Big pseudo changes to ispell to make it pass around a structure rather + * than rely on all sorts of gloabals willy nilly here and there. Also + * fixed our spelling class to work with accepting suggestions once more. + * This code is dirty, gross and ugly (not to mention still not supporting + * multiple hash sized just yet) but it works on my machine and will no + * doubt break other machines. + * + * Revision 1.1 2001/04/15 16:01:24 tomas_f + * moving to spell/xp + * + * Revision 1.7 1999/09/29 23:33:32 justin + * Updates to the underlying ispell-based code to support suggested corrections. + * + * Revision 1.6 1999/04/13 17:12:51 jeff + * Applied "Darren O. Benham" spell check changes. + * Fixed crash on Win32 with the new code. + * + * Revision 1.5 1999/01/07 01:07:48 paul + * Fixed spell leaks. + * + * Revision 1.5 1999/01/07 01:07:48 paul + * Fixed spell leaks. + * + * Revision 1.4 1998/12/29 14:55:33 eric + * + * I've doctored the ispell code pretty extensively here. It is now + * warning-free on Win32. It also *works* on Win32 now, since I + * replaced all the I/O calls with ANSI standard ones. + * + * Revision 1.3 1998/12/28 23:11:30 eric + * + * modified spell code and integration to build on Windows. + * This is still a hack. + * + * Actually, it doesn't yet WORK on Windows. It just builds. + * SpellCheckInit is failing for some reason. + * + * Revision 1.2 1998/12/28 22:16:22 eric + * + * These changes begin to incorporate the spell checker into AbiWord. Most + * of this is a hack. + * + * 1. added other/spell to the -I list in config/abi_defs + * 2. replaced other/spell/Makefile with one which is more like + * our build system. + * 3. added other/spell to other/Makefile so that the build will now + * dive down and build the spell check library. + * 4. added the AbiSpell library to the Makefiles in wp/main + * 5. added a call to SpellCheckInit in wp/main/unix/UnixMain.cpp. + * This call is a HACK and should be replaced with something + * proper later. + * 6. added code to fv_View.cpp as follows: + * whenever you double-click on a word, the spell checker + * verifies that word and prints its status to stdout. + * + * Caveats: + * 1. This will break the Windows build. I'm going to work on fixing it + * now. + * 2. This only works if your dictionary is in /usr/lib/ispell/american.hash. + * The dictionary location is currently hard-coded. This will be + * fixed as well. + * + * Anyway, such as it is, it works. + * + * Revision 1.1 1998/12/28 18:04:43 davet + * Spell checker code stripped from ispell. At this point, there are + * two external routines... the Init routine, and a check-a-word routine + * which returns a boolean value, and takes a 16 bit char string. + * The code resembles the ispell code as much as possible still. + * + * Revision 1.42 1995/01/08 23:23:42 geoff + * Support MSDOS_BINARY_OPEN when opening the hash file to read it in. + * + * Revision 1.41 1994/01/25 07:11:51 geoff + * Get rid of all old RCS log lines in preparation for the 3.1 release. + * + */ + +#include +#include +#include + +#include "enchant-provider.h" +#include "ispell_checker.h" +#include "msgs.h" + +#define G_ICONV_INVALID (GIConv)-1 + +static bool g_iconv_is_valid(GIConv i) +{ + return (i != G_ICONV_INVALID); +} + +#ifdef INDEXDUMP +static void dumpindex P ((struct flagptr * indexp, int depth)); +#endif /* INDEXDUMP */ + +int gnMaskBits = 64; + +/*! + * \param hashname name of the hash file (dictionary) + * + * \return + */ +int ISpellChecker::linit (char *hashname) +{ + FILE* fpHash; + + register int i; + register struct dent * dp; + struct flagent * entry; + struct flagptr * ind; + int nextchar, x; + int viazero; + register ichar_t * cp; + + if ((fpHash = enchant_fopen (hashname, "rb")) == NULL) + { + return (-1); + } + + m_hashsize = fread (reinterpret_cast(&m_hashheader), 1, sizeof m_hashheader, fpHash); + if (m_hashsize < static_cast(sizeof(m_hashheader))) + { + if (m_hashsize < 0) + fprintf (stderr, LOOKUP_C_CANT_READ, hashname); + else if (m_hashsize == 0) + fprintf (stderr, LOOKUP_C_NULL_HASH, hashname); + else + fprintf (stderr, + LOOKUP_C_SHORT_HASH (m_hashname, m_hashsize, + static_cast(sizeof m_hashheader))); + return (-1); + } + else if (m_hashheader.magic != MAGIC) + { + fprintf (stderr, + LOOKUP_C_BAD_MAGIC (hashname, static_cast(MAGIC), + static_cast(m_hashheader.magic))); + return (-1); + } + else if (m_hashheader.magic2 != MAGIC) + { + fprintf (stderr, + LOOKUP_C_BAD_MAGIC2 (hashname, static_cast(MAGIC), + static_cast(m_hashheader.magic2))); + return (-1); + } +/* else if (hashheader.compileoptions != COMPILEOPTIONS*/ + else if ( 1 != 1 + || m_hashheader.maxstringchars != MAXSTRINGCHARS + || m_hashheader.maxstringcharlen != MAXSTRINGCHARLEN) + { + fprintf (stderr, + LOOKUP_C_BAD_OPTIONS (static_cast(m_hashheader.compileoptions), + m_hashheader.maxstringchars, m_hashheader.maxstringcharlen, + static_cast(COMPILEOPTIONS), MAXSTRINGCHARS, MAXSTRINGCHARLEN)); + return (-1); + } + + { + m_hashtbl = + (struct dent *) + calloc (static_cast(m_hashheader.tblsize), sizeof (struct dent)); + m_hashsize = m_hashheader.tblsize; + m_hashstrings = static_cast(malloc(static_cast(m_hashheader.stringsize))); + } + m_numsflags = m_hashheader.stblsize; + m_numpflags = m_hashheader.ptblsize; + m_sflaglist = (struct flagent *) + malloc ((m_numsflags + m_numpflags) * sizeof (struct flagent)); + if (m_hashtbl == NULL || m_hashstrings == NULL || m_sflaglist == NULL) + { + fprintf (stderr, LOOKUP_C_NO_HASH_SPACE); + return (-1); + } + m_pflaglist = m_sflaglist + m_numsflags; + + { + if( fread ( m_hashstrings, 1, static_cast(m_hashheader.stringsize), fpHash) + != static_cast(m_hashheader.stringsize) ) + { + fprintf (stderr, LOOKUP_C_BAD_FORMAT); + fprintf (stderr, "stringsize err\n" ); + return (-1); + } + if ( m_hashheader.compileoptions & 0x04 ) + { + if( fread (reinterpret_cast(m_hashtbl), 1, static_cast(m_hashheader.tblsize) * sizeof(struct dent), fpHash) + != (static_cast(m_hashheader.tblsize * sizeof (struct dent)))) + { + fprintf (stderr, LOOKUP_C_BAD_FORMAT); + return (-1); + } + } + else + { + for( x=0; x(m_hashtbl+x), sizeof( struct dent)-sizeof( MASKTYPE ), 1, fpHash) + != 1) + { + fprintf (stderr, LOOKUP_C_BAD_FORMAT); + return (-1); + } + } /*for*/ + } /*else*/ + } + if (fread (reinterpret_cast(m_sflaglist), 1, + static_cast(m_numsflags+ m_numpflags) * sizeof (struct flagent), fpHash) + != (m_numsflags + m_numpflags) * sizeof (struct flagent)) + { + fprintf (stderr, LOOKUP_C_BAD_FORMAT); + return (-1); + } + fclose (fpHash); + + { + for (i = m_hashsize, dp = m_hashtbl; --i >= 0; dp++) + { + if (dp->word == (char *) -1) + dp->word = NULL; + else + dp->word = &m_hashstrings [ reinterpret_cast(dp->word) ]; + if (dp->next == (struct dent *) -1) + dp->next = NULL; + else + dp->next = &m_hashtbl [ reinterpret_cast(dp->next) ]; + } + } + + for (i = m_numsflags + m_numpflags, entry = m_sflaglist; --i >= 0; entry++) + { + if (entry->stripl) + entry->strip = reinterpret_cast(&m_hashstrings[reinterpret_cast(entry->strip)]); + else + entry->strip = NULL; + if (entry->affl) + entry->affix = reinterpret_cast(&m_hashstrings[reinterpret_cast(entry->affix)]); + else + entry->affix = NULL; + } + /* + ** Warning - 'entry' and 'i' are reset in the body of the loop + ** below. Don't try to optimize it by (e.g.) moving the decrement + ** of i into the loop condition. + */ + for (i = m_numsflags, entry = m_sflaglist; i > 0; i--, entry++) + { + if (entry->affl == 0) + { + cp = NULL; + ind = &m_sflagindex[0]; + viazero = 1; + } + else + { + cp = entry->affix + entry->affl - 1; + ind = &m_sflagindex[*cp]; + viazero = 0; + while (ind->numents == 0 && ind->pu.fp != NULL) + { + if (cp == entry->affix) + { + ind = &ind->pu.fp[0]; + viazero = 1; + } + else + { + ind = &ind->pu.fp[*--cp]; + viazero = 0; + } + } + } + if (ind->numents == 0) + ind->pu.ent = entry; + ind->numents++; + /* + ** If this index entry has more than MAXSEARCH flags in + ** it, we will split it into subentries to reduce the + ** searching. However, the split doesn't make sense in + ** two cases: (a) if we are already at the end of the + ** current affix, or (b) if all the entries in the list + ** have identical affixes. Since the list is sorted, (b) + ** is true if the first and last affixes in the list + ** are identical. + */ + if (!viazero && ind->numents >= MAXSEARCH + && icharcmp (entry->affix, ind->pu.ent->affix) != 0) + { + /* Sneaky trick: back up and reprocess */ + entry = ind->pu.ent - 1; /* -1 is for entry++ in loop */ + i = m_numsflags - (entry - m_sflaglist); + ind->pu.fp = + (struct flagptr *) + calloc (static_cast(SET_SIZE + m_hashheader.nstrchars), + sizeof (struct flagptr)); + if (ind->pu.fp == NULL) + { + fprintf (stderr, LOOKUP_C_NO_LANG_SPACE); + return (-1); + } + ind->numents = 0; + } + } + /* + ** Warning - 'entry' and 'i' are reset in the body of the loop + ** below. Don't try to optimize it by (e.g.) moving the decrement + ** of i into the loop condition. + */ + for (i = m_numpflags, entry = m_pflaglist; i > 0; i--, entry++) + { + if (entry->affl == 0) + { + cp = NULL; + ind = &m_pflagindex[0]; + viazero = 1; + } + else + { + cp = entry->affix; + ind = &m_pflagindex[*cp++]; + viazero = 0; + while (ind->numents == 0 && ind->pu.fp != NULL) + { + if (*cp == 0) + { + ind = &ind->pu.fp[0]; + viazero = 1; + } + else + { + ind = &ind->pu.fp[*cp++]; + viazero = 0; + } + } + } + if (ind->numents == 0) + ind->pu.ent = entry; + ind->numents++; + /* + ** If this index entry has more than MAXSEARCH flags in + ** it, we will split it into subentries to reduce the + ** searching. However, the split doesn't make sense in + ** two cases: (a) if we are already at the end of the + ** current affix, or (b) if all the entries in the list + ** have identical affixes. Since the list is sorted, (b) + ** is true if the first and last affixes in the list + ** are identical. + */ + if (!viazero && ind->numents >= MAXSEARCH + && icharcmp (entry->affix, ind->pu.ent->affix) != 0) + { + /* Sneaky trick: back up and reprocess */ + entry = ind->pu.ent - 1; /* -1 is for entry++ in loop */ + i = m_numpflags - (entry - m_pflaglist); + ind->pu.fp = + static_cast(calloc(SET_SIZE + m_hashheader.nstrchars, + sizeof (struct flagptr))); + if (ind->pu.fp == NULL) + { + fprintf (stderr, LOOKUP_C_NO_LANG_SPACE); + return (-1); + } + ind->numents = 0; + } + } +#ifdef INDEXDUMP + fprintf (stderr, "Prefix index table:\n"); + dumpindex (m_pflagindex, 0); + fprintf (stderr, "Suffix index table:\n"); + dumpindex (m_sflagindex, 0); +#endif + if (m_hashheader.nstrchartype == 0) + m_chartypes = NULL; + else + { + m_chartypes = (struct strchartype *) + malloc (m_hashheader.nstrchartype * sizeof (struct strchartype)); + if (m_chartypes == NULL) + { + fprintf (stderr, LOOKUP_C_NO_LANG_SPACE); + return (-1); + } + for (i = 0, nextchar = m_hashheader.strtypestart; + i < m_hashheader.nstrchartype; + i++) + { + m_chartypes[i].name = &m_hashstrings[nextchar]; + nextchar += strlen (m_chartypes[i].name) + 1; + m_chartypes[i].deformatter = &m_hashstrings[nextchar]; + nextchar += strlen (m_chartypes[i].deformatter) + 1; + m_chartypes[i].suffixes = &m_hashstrings[nextchar]; + while (m_hashstrings[nextchar] != '\0') + nextchar += strlen (&m_hashstrings[nextchar]) + 1; + nextchar++; + } + } + + initckch(NULL); + + return (0); +} + +#ifndef FREEP +#define FREEP(p) do { if (p) free(p); } while (0) +#endif + +/*! + * \param wchars Characters in -w option, if any + */ +void ISpellChecker::initckch (char *wchars) +{ + register ichar_t c; + char num[4]; + + for (c = 0; c < static_cast(SET_SIZE+ m_hashheader.nstrchars); ++c) + { + if (iswordch (c)) + { + if (!mylower (c)) + { + m_Try[m_Trynum] = c; + ++m_Trynum; + } + } + else if (isboundarych (c)) + { + m_Try[m_Trynum] = c; + ++m_Trynum; + } + } + if (wchars != NULL) + { + while (m_Trynum < SET_SIZE && *wchars != '\0') + { + if (*wchars != 'n' && *wchars != '\\') + { + c = *wchars; + ++wchars; + } + else + { + ++wchars; + num[0] = '\0'; + num[1] = '\0'; + num[2] = '\0'; + num[3] = '\0'; + if (isdigit (wchars[0])) + { + num[0] = wchars[0]; + if (isdigit (wchars[1])) + { + num[1] = wchars[1]; + if (isdigit (wchars[2])) + num[2] = wchars[2]; + } + } + if (wchars[-1] == 'n') + { + wchars += strlen (num); + c = atoi (num); + } + else + { + wchars += strlen (num); + c = 0; + if (num[0]) + c = num[0] - '0'; + if (num[1]) + { + c <<= 3; + c += num[1] - '0'; + } + if (num[2]) + { + c <<= 3; + c += num[2] - '0'; + } + } + } +/* c &= NOPARITY;*/ + if (!m_hashheader.wordchars[c]) + { + m_hashheader.wordchars[c] = 1; + m_hashheader.sortorder[c] = m_hashheader.sortval++; + m_Try[m_Trynum] = c; + ++m_Trynum; + } + } + } +} + +/* + * \param indexp + */ +void ISpellChecker::clearindex (struct flagptr *indexp) +{ + register int i; + for (i = 0; i < SET_SIZE + m_hashheader.nstrchars; i++, indexp++) + { + if (indexp->numents == 0 && indexp->pu.fp != NULL) + { + clearindex(indexp->pu.fp); + free(indexp->pu.fp); + } + } +} + +#ifdef INDEXDUMP +static void dumpindex (indexp, depth) + register struct flagptr * indexp; + register int depth; +{ + register int i; + int j; + int k; + char stripbuf[INPUTWORDLEN + 4 * MAXAFFIXLEN + 4]; + + for (i = 0; i < SET_SIZE + hashheader.nstrchars; i++, indexp++) + { + if (indexp->numents == 0 && indexp->pu.fp != NULL) + { + for (j = depth; --j >= 0; ) + putc (' ', stderr); + if (i >= ' ' && i <= '~') + putc (i, stderr); + else + fprintf (stderr, "0x%x", i); + putc ('\n', stderr); + dumpindex (indexp->pu.fp, depth + 1); + } + else if (indexp->numents) + { + for (j = depth; --j >= 0; ) + putc (' ', stderr); + if (i >= ' ' && i <= '~') + putc (i, stderr); + else + fprintf (stderr, "0x%x", i); + fprintf (stderr, " -> %d entries\n", indexp->numents); + for (k = 0; k < indexp->numents; k++) + { + for (j = depth; --j >= 0; ) + putc (' ', stderr); + if (indexp->pu.ent[k].stripl) + { + ichartostr (stripbuf, indexp->pu.ent[k].strip, + sizeof stripbuf, 1); + fprintf (stderr, " entry %d (-%s,%s)\n", + &indexp->pu.ent[k] - sflaglist, + stripbuf, + indexp->pu.ent[k].affl + ? ichartosstr (indexp->pu.ent[k].affix, 1) : "-"); + } + else + fprintf (stderr, " entry %d (%s)\n", + &indexp->pu.ent[k] - sflaglist, + ichartosstr (indexp->pu.ent[k].affix, 1)); + } + } + } +} +#endif + +/* n is length of s */ + +/* + * \param s + * \param dotree + * + * \return + */ +struct dent * ISpellChecker::ispell_lookup (ichar_t *s, int dotree) +{ + register struct dent * dp; + register char * s1; + char schar[INPUTWORDLEN + MAXAFFIXLEN]; + + dp = &m_hashtbl[hash (s, m_hashsize)]; + if (ichartostr (schar, s, sizeof schar, 1)) + fprintf (stderr, WORD_TOO_LONG (schar)); + for ( ; dp != NULL; dp = dp->next) + { + /* quick strcmp, but only for equality */ + s1 = dp->word; + if (s1 && s1[0] == schar[0] && strcmp (s1 + 1, schar + 1) == 0) + return dp; +#ifndef NO_CAPITALIZATION_SUPPORT + while (dp->flagfield & MOREVARIANTS) /* Skip variations */ + dp = dp->next; +#endif + } + return NULL; +} + +void ISpellChecker::alloc_ispell_struct() +{ + m_translate_in = + m_translate_out = G_ICONV_INVALID; +} + +void ISpellChecker::free_ispell_struct() +{ + if (g_iconv_is_valid(m_translate_in)) + g_iconv_close (m_translate_in); + if (g_iconv_is_valid(m_translate_out)) + g_iconv_close (m_translate_out); +} diff --git a/src/ispell/makedent.cpp b/src/ispell/makedent.cpp new file mode 100644 index 0000000..2739c08 --- /dev/null +++ b/src/ispell/makedent.cpp @@ -0,0 +1,936 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright 1988, 1989, 1992, 1993, Geoff Kuenning, Granada Hills, CA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All modifications to the source code must be clearly marked as + * such. Binary redistributions based on modified source code + * must be clearly marked as modified versions in the documentation + * and/or other materials provided with the distribution. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by Geoff Kuenning and + * other unpaid contributors. + * 5. The name of Geoff Kuenning may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING 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 GEOFF KUENNING 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. + */ + +/* + * $Log$ + * Revision 1.4 2003/08/14 17:51:28 dom + * update license - exception clause should be Lesser GPL + * + * Revision 1.3 2003/07/28 20:40:27 dom + * fix up the license clause, further win32-registry proof some directory getting functions + * + * Revision 1.2 2003/07/16 22:52:49 dom + * LGPL + exception license + * + * Revision 1.1 2003/07/15 01:15:08 dom + * ispell enchant backend + * + * Revision 1.3 2003/02/12 02:10:38 hippietrail + * + * C casts -> C++ casts + * Improved const-correctness due to changing casts + * Fixed some warnings + * + * Revision 1.2 2003/01/29 05:50:12 hippietrail + * + * Fixed my mess in EncodingManager. + * Changed many C casts to C++ casts. + * + * Revision 1.1 2003/01/24 05:52:35 hippietrail + * + * Refactored ispell code. Old ispell global variables had been put into + * an allocated structure, a pointer to which was passed to many functions. + * I have now made all such functions and variables private members of the + * ISpellChecker class. It was C OO, now it's C++ OO. + * + * I've fixed the makefiles and tested compilation but am unable to test + * operation. Please back out my changes if they cause problems which + * are not obvious or easy to fix. + * + * Revision 1.8 2003/01/06 18:48:40 dom + * ispell cleanup, start of using new 'add' save features + * + * Revision 1.7 2003/01/04 19:09:04 dom + * some tidying... bug pissing me off... + * + * Revision 1.6 2002/09/19 05:31:18 hippietrail + * + * More Ispell cleanup. Conditional globals and DEREF macros are removed. + * K&R function declarations removed, converted to Doxygen style comments + * where possible. No code has been changed (I hope). Compiles for me but + * unable to test. + * + * Revision 1.5 2002/09/17 03:03:30 hippietrail + * + * After seeking permission on the developer list I've reformatted all the + * spelling source which seemed to have parts which used 2, 3, 4, and 8 + * spaces for tabs. It should all look good with our standard 4-space + * tabs now. + * I've concentrated just on indentation in the actual code. More prettying + * could be done. + * * NO code changes were made * + * + * Revision 1.4 2002/09/13 17:20:13 mpritchett + * Fix more warnings for Linux build + * + * Revision 1.3 2002/03/22 14:31:57 dom + * fix mg's compile problem + * + * Revision 1.2 2001/05/12 16:05:42 thomasf + * Big pseudo changes to ispell to make it pass around a structure rather + * than rely on all sorts of gloabals willy nilly here and there. Also + * fixed our spelling class to work with accepting suggestions once more. + * This code is dirty, gross and ugly (not to mention still not supporting + * multiple hash sized just yet) but it works on my machine and will no + * doubt break other machines. + * + * Revision 1.1 2001/04/15 16:01:24 tomas_f + * moving to spell/xp + * + * Revision 1.6 1999/12/21 18:46:29 sterwill + * ispell patch for non-English dictionaries by Henrik Berg + * + * Revision 1.5 1999/10/20 03:19:35 paul + * Hacked ispell code to ignore any characters that don't fit in the lookup tables loaded from the dictionary. It ain't pretty, but at least we don't crash there any more. + * + * Revision 1.4 1999/04/13 17:12:51 jeff + * Applied "Darren O. Benham" spell check changes. + * Fixed crash on Win32 with the new code. + * + * Revision 1.3 1998/12/29 14:55:33 eric + * + * I've doctored the ispell code pretty extensively here. It is now + * warning-free on Win32. It also *works* on Win32 now, since I + * replaced all the I/O calls with ANSI standard ones. + * + * Revision 1.3 1998/12/29 14:55:33 eric + * + * I've doctored the ispell code pretty extensively here. It is now + * warning-free on Win32. It also *works* on Win32 now, since I + * replaced all the I/O calls with ANSI standard ones. + * + * Revision 1.2 1998/12/28 23:11:30 eric + * + * modified spell code and integration to build on Windows. + * This is still a hack. + * + * Actually, it doesn't yet WORK on Windows. It just builds. + * SpellCheckInit is failing for some reason. + * + * Revision 1.1 1998/12/28 18:04:43 davet + * Spell checker code stripped from ispell. At this point, there are + * two external routines... the Init routine, and a check-a-word routine + * which returns a boolean value, and takes a 16 bit char string. + * The code resembles the ispell code as much as possible still. + * + * Revision 1.45 1994/12/27 23:08:52 geoff + * Add code to makedent to reject words that contain non-word characters. + * This helps protect people who use ISO 8-bit characters when ispell + * isn't configured for that option. + * + * Revision 1.44 1994/10/25 05:46:20 geoff + * Fix some incorrect declarations in the lint versions of some routines. + * + * Revision 1.43 1994/09/16 03:32:34 geoff + * Issue an error message for bad affix flags + * + * Revision 1.42 1994/02/07 04:23:43 geoff + * Correctly identify the deformatter when changing file types + * + * Revision 1.41 1994/01/25 07:11:55 geoff + * Get rid of all old RCS log lines in preparation for the 3.1 release. + * + */ + +#include +#include +#include + +#include "ispell_checker.h" +#include "msgs.h" + +int makedent P ((char * lbuf, int lbuflen, struct dent * ent)); +/*int combinecaps P ((struct dent * hdr, struct dent * newent)); +#ifndef NO_CAPITALIZATION_SUPPORT +static void forcevheader P ((struct dent * hdrp, struct dent * oldp, + struct dent * newp)); +#endif / * NO_CAPITALIZATION_SUPPORT * / +static int combine_two_entries P ((struct dent * hdrp, + struct dent * oldp, struct dent * newp)); +static int acoversb P ((struct dent * enta, struct dent * entb)); +*/ +/*static int issubset P ((struct dent * ent1, struct dent * ent2)); +static void combineaffixes P ((struct dent * ent1, struct dent * ent2));*/ + +void toutent P ((FILE * outfile, struct dent * hent, + int onlykeep)); +/*static void toutword P ((FILE * outfile, char * word, + struct dent * cent)); +static void flagout P ((FILE * outfile, int flag)); +*/ +#ifndef ICHAR_IS_CHAR +ichar_t * icharcpy P ((ichar_t * out, ichar_t * in)); +int icharlen P ((ichar_t * str)); +int icharcmp P ((ichar_t * s1, ichar_t * s2)); +int icharncmp P ((ichar_t * s1, ichar_t * s2, int n)); +#endif /* ICHAR_IS_CHAR */ + +/*static int has_marker;*/ + +/* + * Fill in a directory entry, including setting the capitalization flags, and + * allocate and initialize memory for the d->word field. Returns -1 + * if there was trouble. The input word must be in canonical form. +int makedent (lbuf, lbuflen, d) +This function is not used by AbiWord. I don't know if it'll be needed for +other abi documents + */ + +#ifndef NO_CAPITALIZATION_SUPPORT +/*! +** Classify the capitalization of a sample entry. Returns one of the +** four capitalization codes ANYCASE, ALLCAPS, CAPITALIZED, or FOLLOWCASE. +** +** \param word +** +** \return +*/ +long +ISpellChecker::whatcap (ichar_t *word) +{ + register ichar_t * p; + + for (p = word; *p; p++) + { + if (mylower (*p)) + break; + } + if (*p == '\0') + return ALLCAPS; + else + { + for ( ; *p; p++) + { + if (myupper (*p)) + break; + } + if (*p == '\0') + { + /* + ** No uppercase letters follow the lowercase ones. + ** If there is more than one uppercase letter, it's + ** "followcase". If only the first one is capitalized, + ** it's "capitalize". If there are no capitals + ** at all, it's ANYCASE. + */ + if (myupper (word[0])) + { + for (p = word + 1; *p != '\0'; p++) + { + if (myupper (*p)) + return FOLLOWCASE; + } + return CAPITALIZED; + } + else + return ANYCASE; + } + else + return FOLLOWCASE; /* .../lower/upper */ + } +} + +/*! +** Add a variant-capitalization header to a word. This routine may be +** called even for a followcase word that doesn't yet have a header. +** +** \param dp Entry to update +** +** \return 0 if all was ok, -1 if allocation error. +*/ +int ISpellChecker::addvheader ( struct dent *dp) +{ + register struct dent * tdent; /* Copy of entry */ + + /* + ** Add a second entry with the correct capitalization, and then make + ** dp into a special dummy entry. + */ + tdent = static_cast(malloc(sizeof (struct dent))); + if (tdent == NULL) + { + fprintf (stderr, MAKEDENT_C_NO_WORD_SPACE, dp->word); + return -1; + } + *tdent = *dp; + if (captype (tdent->flagfield) != FOLLOWCASE) + tdent->word = NULL; + else + { + /* Followcase words need a copy of the capitalization */ + tdent->word = static_cast(malloc (static_cast(strlen(tdent->word)) + 1)); + if (tdent->word == NULL) + { + fprintf (stderr, MAKEDENT_C_NO_WORD_SPACE, dp->word); + free (reinterpret_cast(tdent)); + return -1; + } + strcpy (tdent->word, dp->word); + } + chupcase (dp->word); + dp->next = tdent; + dp->flagfield &= ~CAPTYPEMASK; + dp->flagfield |= (ALLCAPS | MOREVARIANTS); + return 0; +} +#endif /* NO_CAPITALIZATION_SUPPORT */ + +/* +** Combine and resolve the entries describing two capitalizations of the same +** word. This may require allocating yet more entries. +** +** Hdrp is a pointer into a hash table. If the word covered by hdrp has +** variations, hdrp must point to the header. Newp is a pointer to temporary +** storage, and space is malloc'ed if newp is to be kept. The newp->word +** field must have been allocated with mymalloc, so that this routine may free +** the space if it keeps newp but not the word. +** +** Return value: 0 if the word was added, 1 if the word was combined +** with an existing entry, and -1 if trouble occurred (e.g., malloc). +** If 1 is returned, newp->word may have been be freed using myfree. +** +** Life is made much more difficult by the KEEP flag's possibilities. We +** must ensure that a !KEEP word doesn't find its way into the personal +** dictionary as a result of this routine's actions. However, a !KEEP +** word that has affixes must have come from the main dictionary, so it +** is acceptable to combine entries in that case (got that?). +** +** The net result of all this is a set of rules that is a bloody pain +** to figure out. Basically, we want to choose one of the following actions: +** +** (1) Add newp's affixes and KEEP flag to oldp, and discard newp. +** (2) Add oldp's affixes and KEEP flag to newp, replace oldp with +** newp, and discard newp. +#ifndef NO_CAPITALIZATION_SUPPORT +** (3) Insert newp as a new entry in the variants list. If there is +** currently no variant header, this requires adding one. Adding a +** header splits into two sub-cases: +** +** (3a) If oldp is ALLCAPS and the KEEP flags match, just turn it +** into the header. +** (3b) Otherwise, add a new entry to serve as the header. +** To ease list linking, this is done by copying oldp into +** the new entry, and then performing (3a). +** +** After newp has been added as a variant, its affixes and KEEP +** flag are OR-ed into the variant header. +#endif +** +** So how to choose which? The default is always case (3), which adds newp +** as a new entry in the variants list. Cases (1) and (2) are symmetrical +** except for which entry is discarded. We can use case (1) or (2) whenever +** one entry "covers" the other. "Covering" is defined as follows: +** +** (4) For entries with matching capitalization types, A covers B +** if: +** +** (4a) B's affix flags are a subset of A's, or the KEEP flags +** match, and +** (4b) either the KEEP flags match, or A's KEEP flag is set. +** (Since A has more suffixes, combining B with it won't +** cause any extra suffixes to be added to the dictionary.) +** (4c) If the words are FOLLOWCASE, the capitalizations match +** exactly. +** +#ifndef NO_CAPITALIZATION_SUPPORT +** (5) For entries with mismatched capitalization types, A covers B +** if (4a) and (4b) are true, and: +** +** (5a) B is ALLCAPS, or +** (5b) A is ANYCASE, and B is CAPITALIZED. +#endif +** +** For any "hdrp" without variants, oldp is the same as hdrp. Otherwise, +** the above tests are applied using each variant in turn for oldp. +int combinecaps (hdrp, newp) +static void forcevheader (hdrp, oldp, newp) +static int combine_two_entries (hdrp, oldp, newp) +static int acoversb (enta, entb) +*/ + +/* + * \param s + */ +void +ISpellChecker::upcase (ichar_t *s) +{ + + while (*s) + { + *s = mytoupper (*s); + s++; + } +} + +/* + * \param s + */ +void +ISpellChecker::lowcase (ichar_t *s) +{ + + while (*s) + { + *s = mytolower (*s); + s++; + } +} + +/*! + * Upcase variant that works on normal strings. Note that it is a lot + * slower than the normal upcase. The input must be in canonical form. + * + * \param s + */ +void +ISpellChecker::chupcase (char *s) +{ + ichar_t * is; + + is = strtosichar (s, 1); + upcase (is); + ichartostr (s, is, strlen (s) + 1, 1); +} + +/* +** See if one affix field is a subset of another. Returns NZ if ent1 +** is a subset of ent2. The KEEP flag is not taken into consideration. +static int issubset (ent1, ent2) +static void combineaffixes (ent1, ent2) +*/ + +/* +** Write out a dictionary entry, including capitalization variants. +** If onlykeep is true, only those variants with KEEP set will be +** written. +Removed -- not used by Abiword +void toutent_ (toutfile, hent, onlykeep) +static void toutword (toutfile, word, cent) +static void flagout (toutfile, flag) +*/ + +/*! + * If the string under the given pointer begins with a string character, + * return the length of that "character". If not, return 0. + * May be called any time, but it's best if "isstrstart" is first + * used to filter out unnecessary calls. + * + * As a side effect, "laststringch" is set to the number of the string + * found, or to -1 if none was found. This can be useful for such things + * as case conversion. + * + * \param bufp + * \param canonical NZ if input is in canonical form + * + * \return + */ +int +ISpellChecker::stringcharlen (char *bufp, int canonical) +{ +#ifdef SLOWMULTIPLY + static char * sp[MAXSTRINGCHARS]; + static int inited = 0; +#endif /* SLOWMULTIPLY */ + register char * bufcur; + register char * stringcur; + register int stringno; + register int lowstringno; + register int highstringno; + int dupwanted; + +#ifdef SLOWMULTIPLY + if (!inited) + { + inited = 1; + for (stringno = 0; stringno < MAXSTRINGCHARS; stringno++) + sp[stringno] = &hashheader.stringchars[stringno][0]; + } +#endif /* SLOWMULTIPLY */ + lowstringno = 0; + highstringno = m_hashheader.nstrchars - 1; + dupwanted = canonical ? 0 : m_defdupchar; + while (lowstringno <= highstringno) + { + stringno = (lowstringno + highstringno) >> 1; +#ifdef SLOWMULTIPLY + stringcur = sp[stringno]; +#else /* SLOWMULTIPLY */ + stringcur = &m_hashheader.stringchars[stringno][0]; +#endif /* SLOWMULTIPLY */ + bufcur = bufp; + while (*stringcur) + { +#ifdef NO8BIT + if (((*bufcur++ ^ *stringcur) & 0x7F) != 0) +#else /* NO8BIT */ + if (*bufcur++ != *stringcur) +#endif /* NO8BIT */ + break; + /* + ** We can't use autoincrement above because of the + ** test below. + */ + stringcur++; + } + if (*stringcur == '\0') + { + if (m_hashheader.dupnos[stringno] == dupwanted) + { + /* We have a match */ + m_laststringch = m_hashheader.stringdups[stringno]; +#ifdef SLOWMULTIPLY + return stringcur - sp[stringno]; +#else /* SLOWMULTIPLY */ + return stringcur - &m_hashheader.stringchars[stringno][0]; +#endif /* SLOWMULTIPLY */ + } + else + --stringcur; + } + /* No match - choose which side to search on */ +#ifdef NO8BIT + if ((*--bufcur & 0x7F) < (*stringcur & 0x7F)) + highstringno = stringno - 1; + else if ((*bufcur & 0x7F) > (*stringcur & 0x7F)) + lowstringno = stringno + 1; +#else /* NO8BIT */ + if (*--bufcur < *stringcur) + highstringno = stringno - 1; + else if (*bufcur > *stringcur) + lowstringno = stringno + 1; +#endif /* NO8BIT */ + else if (dupwanted < m_hashheader.dupnos[stringno]) + highstringno = stringno - 1; + else + lowstringno = stringno + 1; + } + m_laststringch = static_cast(-1); + return 0; /* Not a string character */ +} + +/* MACROS CONVERTED TO FUNCTIONS +** These macros are similar to the ones above, but they take into account +** the possibility of string characters. Note well that they take a POINTER, +** not a character. +** +** The "l_" versions set "len" to the length of the string character as a +** handy side effect. (Note that the global "laststringch" is also set, +** and sometimes used, by these macros.) +** +** The "l1_" versions go one step further and guarantee that the "len" +** field is valid for *all* characters, being set to 1 even if the macro +** returns false. This macro is a great example of how NOT to write +** readable C. +*/ +#define isstringch(ptr, canon) (isstringstart (*(ptr)) \ + && stringcharlen ((ptr), (canon)) > 0) +/* +int isstringch(char *ptr, int canon) { + return (isstringstart (*(ptr)) && (len = stringcharlen ((ptr), (canon))) > 0); +} +*/ + +#define l_isstringch(ptr, len, canon) \ + (isstringstart (*(ptr)) \ + && (len = stringcharlen ((ptr), (canon))) \ + > 0) +/* +int l_isstringch(char *ptr, int len, int canon) { + return (isstringstart (*(ptr)) && (len = stringcharlen ((ptr), (canon))) > 0); +} +*/ + +#define l1_isstringch(ptr, len, canon) \ + (len = 1, \ + isstringstart ((unsigned char)(*(ptr))) \ + && ((len = \ + stringcharlen ((ptr), (canon))) \ + > 0 \ + ? 1 : (len = 1, 0))) +/* +int l1_isstringch(char *ptr, int len, int canon) { + return (len = 1, isstringstart ((unsigned char)(*(ptr))) && + ((len = stringcharlen ((ptr), (canon))) > 0 ? 1 : (len = 1, 0))); +} +*/ + +/*** END MACRO CONVERSION ***/ + +/*! + * Convert an external string to an ichar_t string. If necessary, the parity + * bit is stripped off as part of the process. + * + * \param out Where to put result + * \param in String to convert + * \param outlen Size of output buffer, *BYTES* + * \param canonical NZ if input is in canonical form + * + * \return NZ if the output string overflowed. + */ +int +ISpellChecker::strtoichar (ichar_t *out, char *in, int outlen, int canonical) +{ + register int len = 1; /* Length of next character */ + + outlen /= sizeof (ichar_t); /* Convert to an ichar_t count */ + for ( ; --outlen > 0 && *in != '\0'; in += len) + { + if (l1_isstringch (in, len , canonical)) + *out++ = SET_SIZE + m_laststringch; + else + *out++ = (unsigned char)( *in ); + } + *out = 0; + return outlen <= 0; +} + +/*! + * Convert an ichar_t string to an external string. + * + * WARNING: the resulting string may wind up being longer than the + * original. In fact, even the sequence strtoichar->ichartostr may + * produce a result longer than the original, because the output form + * may use a different string type set than the original input form. + * + * \param out Where to put result + * \param in String to convert + * \param outlen Size of output buffer, bytes + * \param canonical NZ for canonical form + * + * \return NZ if the output string overflowed. + */ +int +ISpellChecker::ichartostr ( char *out, ichar_t *in, int outlen, int canonical) +{ + register int ch; /* Next character to store */ + register int i; /* Index into duplicates list */ + register char * scharp; /* Pointer into a string char */ + + while (--outlen > 0 && (ch = *in++) != 0) + { + if (ch < SET_SIZE) + *out++ = static_cast(ch); + else + { + ch -= SET_SIZE; + if (!canonical) + { + for (i = m_hashheader.nstrchars; --i >= 0; ) + { + if (m_hashheader.dupnos[i] == m_defdupchar + && (static_cast(m_hashheader.stringdups[i])) == ch) + { + ch = i; + break; + } + } + } + scharp = m_hashheader.stringchars[static_cast(ch)]; + while ((*out++ = *scharp++) != '\0') + ; + out--; + } + } + *out = '\0'; + return outlen <= 0; +} + +/*! + * Convert a string to an ichar_t, storing the result in a static area. + * + * \param in String to convert + * \param canonical NZ if input is in canonical form + * + * \return + */ +ichar_t * +ISpellChecker::strtosichar ( char *in, int canonical) +{ + static ichar_t out[STRTOSICHAR_SIZE / sizeof (ichar_t)]; + + if (strtoichar (out, in, sizeof out, canonical)) + fprintf (stderr, WORD_TOO_LONG (in)); + return out; +} + +/*! + * Convert an ichar_t to a string, storing the result in a static area. + * + * \param in Internal string to convert + * \param canonical NZ for canonical conversion + * + * \return + */ +char * +ISpellChecker::ichartosstr (ichar_t *in, int canonical) +{ + static char out[ICHARTOSSTR_SIZE]; + + if (ichartostr (out, in, sizeof out, canonical)) + fprintf (stderr, WORD_TOO_LONG (out)); + return out; +} + +/*! + * Convert a single ichar to a printable string, storing the result in + * a static area. + * + * \param in + * + * \return + */ +char * +ISpellChecker::printichar (int in) +{ + static char out[MAXSTRINGCHARLEN + 1]; + + if (in < SET_SIZE) + { + out[0] = static_cast(in); + out[1] = '\0'; + } + else + strcpy (out, m_hashheader.stringchars[static_cast(in) - SET_SIZE]); + return out; +} + +#ifndef ICHAR_IS_CHAR +/*! + * Copy an ichar_t. + * + * \param out Destination + * \param in Source + * + * \return + */ +ichar_t * +icharcpy (ichar_t *out, ichar_t *in) +{ + ichar_t * origout; /* Copy of destination for return */ + + origout = out; + while ((*out++ = *in++) != 0) + ; + return origout; +} + +/*! + * Return the length of an ichar_t. + * + * \param in String to count + * + * \return + */ +int +icharlen (ichar_t * in) +{ + register int len; /* Length so far */ + + for (len = 0; *in++ != 0; len++) + ; + return len; +} + +/*! + * Compare two ichar_t's. + * + * \param s1 + * \param s2 + * + * \return + */ +int +icharcmp (ichar_t * s1, ichar_t * s2) +{ + + while (*s1 != 0) + { + if (*s1++ != *s2++) + return *--s1 - *--s2; + } + return *s1 - *s2; +} + +/*! + * Strncmp for two ichar_t's. + * + * \param s1 + * \param s2 + * \param n + * + * \return + */ +int +icharncmp (ichar_t *s1, ichar_t *s2, int n) +{ + + while (--n >= 0 && *s1 != 0) + { + if (*s1++ != *s2++) + return *--s1 - *--s2; + } + if (n < 0) + return 0; + else + return *s1 - *s2; +} + +#endif /* ICHAR_IS_CHAR */ + +/* + * \param istate + * \param name + * \param searchnames + * \param deformatter + * + * \return + */ +int +ISpellChecker::findfiletype (const char *name, int searchnames, int *deformatter) +{ + char * cp; /* Pointer into suffix list */ + int cplen; /* Length of current suffix */ + register int i; /* Index into type table */ + int len; /* Length of the name */ + + /* + * Note: for now, the deformatter is set to 1 for tex, 0 for nroff. + * Further, we assume that it's one or the other, so that a test + * for tex is sufficient. This needs to be generalized. + */ + len = strlen (name); + if (searchnames) + { + for (i = 0; i < m_hashheader.nstrchartype; i++) + { + if (strcmp (name, m_chartypes[i].name) == 0) + { + if (deformatter != NULL) + *deformatter = + (strcmp (m_chartypes[i].deformatter, "tex") == 0); + return i; + } + } + } + for (i = 0; i < m_hashheader.nstrchartype; i++) + { + for (cp = m_chartypes[i].suffixes; *cp != '\0'; cp += cplen + 1) + { + cplen = strlen (cp); + if (len >= cplen && strcmp (&name[len - cplen], cp) == 0) + { + if (deformatter != NULL) + *deformatter = + (strcmp (m_chartypes[i].deformatter, "tex") == 0); + return i; + } + } + } + return -1; +} + +/* + HACK: macros replaced with function implementations + so we could do a side-effect-free check for unicode + characters which aren't in hashheader + + TODO: this is just a workaround to keep us from crashing. + more sophisticated logic needed here. +*/ +char ISpellChecker::myupper(ichar_t c) +{ + if (c < (SET_SIZE + MAXSTRINGCHARS)) + return m_hashheader.upperchars[c]; + else + return 0; +} + +char ISpellChecker::mylower(ichar_t c) +{ + if (c < (SET_SIZE + MAXSTRINGCHARS)) + return m_hashheader.lowerchars[c]; + else + return 0; +} + +int myspace(ichar_t c) +{ + return ((c > 0) && (c < 0x80) && isspace(static_cast(c))); +} + +char ISpellChecker::iswordch(ichar_t c) +{ + if (c < (SET_SIZE + MAXSTRINGCHARS)) + return m_hashheader.wordchars[c]; + else + return 0; +} + +char ISpellChecker::isboundarych(ichar_t c) +{ + if (c < (SET_SIZE + MAXSTRINGCHARS)) + return m_hashheader.boundarychars[c]; + else + return 0; +} + +char ISpellChecker::isstringstart(ichar_t c) +{ + if (c < (SET_SIZE)) + return m_hashheader.stringstarts[static_cast(c)]; + else + return 0; +} + +ichar_t ISpellChecker::mytolower(ichar_t c) +{ + if (c < (SET_SIZE + MAXSTRINGCHARS)) + return m_hashheader.lowerconv[c]; + else + return c; +} + +ichar_t ISpellChecker::mytoupper (ichar_t c) +{ + if (c < (SET_SIZE + MAXSTRINGCHARS)) + return m_hashheader.upperconv[c]; + else + return c; +} + diff --git a/src/ispell/msgs.h b/src/ispell/msgs.h new file mode 100644 index 0000000..00ff1e1 --- /dev/null +++ b/src/ispell/msgs.h @@ -0,0 +1,297 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * $Id: msgs.h 28601 2010-01-11 12:40:00Z dom $ + * + * Copyright 1992, 1993, Geoff Kuenning, Granada Hills, CA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All modifications to the source code must be clearly marked as + * such. Binary redistributions based on modified source code + * must be clearly marked as modified versions in the documentation + * and/or other materials provided with the distribution. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by Geoff Kuenning and + * other unpaid contributors. + * 5. The name of Geoff Kuenning may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING 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 GEOFF KUENNING 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. + * + */ + +/* + * Messages header file. + * + * This file contains all text strings that are written by any of the + * C programs in the ispell package. The strings are collected here so that + * you can have the option of translating them into your local language for + * the benefit of your users. + * + * Anyone who goes to the effort of making a translation may wish to return + * the translated strings to me, geoff@ITcorp.com, so that I can include + * them in a later distribution under #ifdef control. + * + * Besides the strings in this header file, you may also want to translate + * the strings in version.h, which give the version and copyright information. + * However, any translation of these strings MUST accurately preserve the + * legal rights under international law; you may wish to consult a lawyer + * about this since you will be responsible for the results of any + * incorrect translation. + * + * Most of the strings below are simple printf format strings. If the printf + * takes more than one parameter, the string is given as a parameterized + * macro in case your local language needs a different word order. + */ + +/* + * $Log$ + * Revision 1.4 2003/08/14 17:51:28 dom + * update license - exception clause should be Lesser GPL + * + * Revision 1.3 2003/07/28 20:40:27 dom + * fix up the license clause, further win32-registry proof some directory getting functions + * + * Revision 1.2 2003/07/16 22:52:52 dom + * LGPL + exception license + * + * Revision 1.1 2003/07/15 01:15:08 dom + * ispell enchant backend + * + * Revision 1.1 2001/04/15 16:01:24 tomas_f + * moving to spell/xp + * + * Revision 1.1 1998/12/28 18:04:43 davet + * Spell checker code stripped from ispell. At this point, there are + * two external routines... the Init routine, and a check-a-word routine + * which returns a boolean value, and takes a 16 bit char string. + * The code resembles the ispell code as much as possible still. + * + * Revision 1.31 1994/12/27 23:08:57 geoff + * Add a message to be issued if a word contains illegal characters. + * + * Revision 1.30 1994/10/25 05:46:40 geoff + * Improve a couple of error messages relating to affix flags. + * + * Revision 1.29 1994/10/04 03:46:23 geoff + * Add a missing carriage return in the help message + * + * Revision 1.28 1994/09/16 05:07:00 geoff + * Add the BAD_FLAG message, and start a sentence in another message with + * an uppercase letter. + * + * Revision 1.27 1994/07/28 05:11:38 geoff + * Log message for previous revision: add BHASH_C_ZERO_COUNT. + * + * Revision 1.26 1994/07/28 04:53:49 geoff + * + * Revision 1.25 1994/05/24 04:54:36 geoff + * Add error messages for affix-flag checking. + * + * Revision 1.24 1994/01/25 07:12:42 geoff + * Get rid of all old RCS log lines in preparation for the 3.1 release. + * + */ + +/* + * The following strings are used in numerous places: + */ +#define BAD_FLAG "\r\nIllegal affix flag character '%c'\r\n" +#define CANT_OPEN "Can't open %s\r\n" +#define CANT_CREATE "Can't create %s\r\n" +#define WORD_TOO_LONG(w) "\r\nWord '%s' too long at line %d of %s, truncated\r\n", \ + w, __LINE__, __FILE__ + +/* + * The following strings are used in buildhash.c: + */ +#define BHASH_C_NO_DICT "No dictionary (%s)\n" +#define BHASH_C_NO_COUNT "No count file\n" +#define BHASH_C_BAD_COUNT "Bad count file\n" +#define BHASH_C_ZERO_COUNT "No words in dictionary\n" + /* I think this message looks better when it's nearly 80 characters wide, + * thus the ugly formatting in the next two defines. GK 9-87 */ +#define BHASH_C_BAFF_1(max, excess) \ + " Warning: this language table may exceed the maximum total affix length\nof %d by up to %d bytes. You should either increase MAXAFFIXLEN in config.X\nor shorten your largest affix/strip string difference. (This is the\n", \ + max, excess +#define BHASH_C_BAFF_2 \ + "difference between the affix length and the strip length in a given\nreplacement rule, or the affix length if there is no strip string\nin that rule.)\n" +#define BHASH_C_OVERFLOW "Hash table overflowed by %d words\n" +#define BHASH_C_CANT_OPEN_DICT "Can't open dictionary\n" +#define BHASH_C_NO_SPACE "Couldn't allocate hash table\n" +#define BHASH_C_COLLISION_SPACE "\ncouldn't allocate space for collision\n" +#define BHASH_C_COUNTING "Counting words in dictionary ...\n" +#define BHASH_C_WORD_COUNT "\n%d words\n" +#define BHASH_C_USAGE "Usage: buildhash [-s] dict-file aff-file hash-file\n\tbuildhash -c count aff-file\n" + +/* + * The following strings are used in correct.c: + */ +#define CORR_C_HELP_1 "Whenever a word is found that is not in the dictionary,\r\n" +#define CORR_C_HELP_2 "it is printed on the first line of the screen. If the dictionary\r\n" +#define CORR_C_HELP_3 "contains any similar words, they are listed with a number\r\n" +#define CORR_C_HELP_4 "next to each one. You have the option of replacing the word\r\n" +#define CORR_C_HELP_5 "completely, or choosing one of the suggested words.\r\n" + /* You may add HELP_6 through HELP_9 if your language needs more lines */ +#define CORR_C_HELP_6 "" +#define CORR_C_HELP_7 "" +#define CORR_C_HELP_8 "" +#define CORR_C_HELP_9 "" +#define CORR_C_HELP_COMMANDS "\r\nCommands are:\r\n\r\n" +#define CORR_C_HELP_R_CMD "R Replace the misspelled word completely.\r\n" +#define CORR_C_HELP_BLANK "Space Accept the word this time only.\r\n" +#define CORR_C_HELP_A_CMD "A Accept the word for the rest of this session.\r\n" +#define CORR_C_HELP_I_CMD "I Accept the word, and put it in your private dictionary.\r\n" +#define CORR_C_HELP_U_CMD "U Accept and add lowercase version to private dictionary.\r\n" +#define CORR_C_HELP_0_CMD "0-n Replace with one of the suggested words.\r\n" +#define CORR_C_HELP_L_CMD "L Look up words in system dictionary.\r\n" +#define CORR_C_HELP_X_CMD "X Write the rest of this file, ignoring misspellings,\r\n and start next file.\r\n" +#define CORR_C_HELP_Q_CMD "Q Quit immediately. Asks for confirmation.\r\n Leaves file unchanged.\r\n" +#define CORR_C_HELP_BANG "! Shell escape.\r\n" +#define CORR_C_HELP_REDRAW "^L Redraw screen.\r\n" +#define CORR_C_HELP_SUSPEND "^Z Suspend program.\r\n" +#define CORR_C_HELP_HELP "? Show this help screen.\r\n" +#define CORR_C_HELP_TYPE_SPACE "-- Type space to continue --" + +#define CORR_C_FILE_LABEL " File: %s" +#define CORR_C_READONLY "[READONLY]" +#define CORR_C_MINI_MENU "[SP] R)epl A)ccept I)nsert L)ookup U)ncap Q)uit e(X)it or ? for help\r\n" +#define CORR_C_CONFIRM_QUIT "Are you sure you want to throw away your changes? " +#define CORR_C_REPLACE_WITH "Replace with: " +#define CORR_C_LOOKUP_PROMPT "Lookup string ('*' is wildcard): " +#define CORR_C_MORE_PROMPT "-- more --" +#define CORR_C_BLANK_MORE "\r \r" +#define CORR_C_END_LOOK "--end--" + +/* + * The following strings are used in defmt.c: + */ +#define DEFMT_C_TEX_MATH_ERROR "****ERROR in parsing TeX math mode!\r\n" +#define DEFMT_C_LR_MATH_ERROR "***ERROR in LR to math-mode switch.\n" + +/* + * The following strings are used in icombine.c: + */ +#define ICOMBINE_C_BAD_TYPE "icombine: unrecognized formatter type '%s'\n" +#define ICOMBINE_C_USAGE "Usage: icombine [-T suffix] [aff-file] < wordlist\n" + +/* + * The following strings are used in ispell.c: + */ +#define ISPELL_C_USAGE1 "Usage: %s [-dfile | -pfile | -wchars | -Wn | -t | -n | -x | -b | -S | -B | -C | -P | -m | -Lcontext | -M | -N | -Ttype | -V] file .....\n" +#define ISPELL_C_USAGE2 " %s [-dfile | -pfile | -wchars | -Wn | -t | -n | -Ttype] -l\n" +#ifndef USG +#define ISPELL_C_USAGE3 " %s [-dfile | -pfile | -ffile | -Wn | -t | -n | -s | -B | -C | -P | -m | -Ttype] {-a | -A}\n" +#else +#define ISPELL_C_USAGE3 " %s [-dfile | -pfile | -ffile | -Wn | -t | -n | -B | -C | -P | -m | -Ttype] {-a | -A}\n" +#endif +#define ISPELL_C_USAGE4 " %s [-dfile] [-wchars | -Wn] -c\n" +#define ISPELL_C_USAGE5 " %s [-dfile] [-wchars] -e[1-4]\n" +#define ISPELL_C_USAGE6 " %s [-dfile] [-wchars] -D\n" +#define ISPELL_C_USAGE7 " %s -v\n" +#define ISPELL_C_TEMP_DISAPPEARED "temporary file disappeared (%s)\r\n" +#define ISPELL_C_BAD_TYPE "ispell: unrecognized formatter type '%s'\n" +#define ISPELL_C_NO_FILE "ispell: specified file does not exist\n" +#define ISPELL_C_NO_FILES "ispell: specified files do not exist\n" +#define ISPELL_C_CANT_WRITE "Warning: Can't write to %s\r\n" +#define ISPELL_C_OPTIONS_ARE "Compiled-in options:\n" + +/* + * The following strings are used in lookup.c: + */ +#define LOOKUP_C_CANT_READ "Trouble reading hash table %s\r\n" +#define LOOKUP_C_NULL_HASH "Null hash table %s\r\n" +#define LOOKUP_C_SHORT_HASH(name, gotten, wanted) \ + "Truncated hash table %s: got %d bytes, expected %d\r\n", \ + name, gotten, wanted +#define LOOKUP_C_BAD_MAGIC(name, wanted, gotten) \ + "Illegal format hash table %s - expected magic 0x%x, got 0x%x\r\n", \ + name, wanted, gotten +#define LOOKUP_C_BAD_MAGIC2(name, wanted, gotten) \ + "Illegal format hash table %s - expected magic2 0x%x, got 0x%x\r\n", \ + name, wanted, gotten +#define LOOKUP_C_BAD_OPTIONS(gotopts, gotchars, gotlen, wantedopts, wantedchars, wantedlen) \ + "Hash table options don't agree with buildhash - 0x%x/%d/%d vs. 0x%x/%d/%d\r\n", \ + gotopts, gotchars, gotlen, \ + wantedopts, wantedchars, wantedlen +#define LOOKUP_C_NO_HASH_SPACE "Couldn't allocate space for hash table\r\n" +#define LOOKUP_C_BAD_FORMAT "Illegal format hash table\r\n" +#define LOOKUP_C_NO_LANG_SPACE "Couldn't allocate space for language tables\r\n" + +/* + * The following strings are used in makedent.c: + */ +#define MAKEDENT_C_NO_WORD_SPACE "\r\nCouldn't allocate space for word '%s'\r\n" +#define MAKEDENT_C_BAD_WORD_CHAR "\r\nWord '%s' contains illegal characters\r\n" + +/* + * The following strings are used in parse.y: + */ +#define PARSE_Y_8_BIT "Eighth bit ignored (recompile ispell without NO8BIT)" +#define PARSE_Y_NO_WORD_STRINGS "wordchars statement may not specify string characters" +#define PARSE_Y_UNMATCHED "Unmatched charset lengths" +#define PARSE_Y_NO_BOUNDARY_STRINGS "boundarychars statement may not specify string characters" +#define PARSE_Y_LONG_STRING "String character is too long" +#define PARSE_Y_NULL_STRING "String character must have nonzero length" +#define PARSE_Y_MANY_STRINGS "Too many string characters" +#define PARSE_Y_NO_SUCH_STRING "No such string character" +#define PARSE_Y_MULTIPLE_STRINGS "Alternate string character was already defined" +#define PARSE_Y_LENGTH_MISMATCH "Upper and lower versions of string character must be same length" +#define PARSE_Y_WRONG_NROFF "Incorrect character count in nroffchars statement" +#define PARSE_Y_WRONG_TEX "Incorrect character count in TeXchars statement" +#define PARSE_Y_DOUBLE_COMPOUND "Compoundwords option may only appear once" +#define PARSE_Y_LONG_FLAG "Flag must be single character" +#define PARSE_Y_BAD_FLAG "Flag must be alphabetic" +#define PARSE_Y_DUP_FLAG "Duplicate flag" +#define PARSE_Y_NO_SPACE "Out of memory" +#define PARSE_Y_NEED_BLANK "Single characters must be separated by a blank" +#define PARSE_Y_MANY_CONDS "Too many conditions; 8 maximum" +#define PARSE_Y_EOF "Unexpected EOF in quoted string" +#define PARSE_Y_LONG_QUOTE "Quoted string too long, max 256 characters" +#define PARSE_Y_ERROR_FORMAT(file, lineno, error) \ + "%s line %d: %s\n", file, lineno, error +#define PARSE_Y_MALLOC_TROUBLE "yyopen: trouble allocating memory\n" +#define PARSE_Y_UNGRAB_PROBLEM "Internal error: ungrab buffer overflow" +#define PARSE_Y_BAD_DEFORMATTER "Deformatter must be either 'nroff' or 'tex'" +#define PARSE_Y_BAD_NUMBER "Illegal digit in number" + +/* + * The following strings are used in term.c: + */ +#define TERM_C_SMALL_SCREEN "Screen too small: need at least %d lines\n" +#define TERM_C_NO_BATCH "Can't deal with non-interactive use yet.\n" +#define TERM_C_CANT_FORK "Couldn't fork, try later.\r\n" +#define TERM_C_TYPE_SPACE "\n-- Type space to continue --" + +/* + * The following strings are used in tree.c: + */ +#define TREE_C_CANT_UPDATE "Warning: Cannot update personal dictionary (%s)\r\n" +#define TREE_C_NO_SPACE "Ran out of space for personal dictionary\r\n" +#define TREE_C_TRY_ANYWAY "Continuing anyway (with reduced performance).\r\n" + +/* + * The following strings are used in unsq.c: + */ +#define UNSQ_C_BAD_COUNT "Illegal count character 0x%x\n" +#define UNSQ_C_SURPRISE_EOF "Unexpected EOF\n" diff --git a/src/ispell/sp_spell.h b/src/ispell/sp_spell.h new file mode 100644 index 0000000..e854a45 --- /dev/null +++ b/src/ispell/sp_spell.h @@ -0,0 +1,32 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +#ifndef SPELL_H +#define SPELL_H + +/* + TODO stuff we need to do for this spell module: + + eliminate all the stderr fprintfs + rip out the support for ICHAR_IS_CHAR +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct _sp_suggestions { + int count; + short *score; + unsigned short **word; +} sp_suggestions; + +int SpellCheckInit(char *hashname); +void SpellCheckCleanup(void); +int SpellCheckNWord16(const unsigned short *word16, int length); +int SpellCheckSuggestNWord16(const unsigned short *word16, int length, sp_suggestions *sg); + +#ifdef __cplusplus +} +#endif + +#endif /* SPELL_H */ diff --git a/src/ispell/tgood.cpp b/src/ispell/tgood.cpp new file mode 100644 index 0000000..c511611 --- /dev/null +++ b/src/ispell/tgood.cpp @@ -0,0 +1,778 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright 1987, 1988, 1989, 1992, 1993, Geoff Kuenning, Granada Hills, CA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All modifications to the source code must be clearly marked as + * such. Binary redistributions based on modified source code + * must be clearly marked as modified versions in the documentation + * and/or other materials provided with the distribution. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by Geoff Kuenning and + * other unpaid contributors. + * 5. The name of Geoff Kuenning may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING 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 GEOFF KUENNING 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. + */ + +/* + * Table-driven version of good.c. + * + * Geoff Kuenning, July 1987 + */ + +/* + * $Log$ + * Revision 1.4 2003/08/14 17:51:29 dom + * update license - exception clause should be Lesser GPL + * + * Revision 1.3 2003/07/28 20:40:28 dom + * fix up the license clause, further win32-registry proof some directory getting functions + * + * Revision 1.2 2003/07/16 22:52:56 dom + * LGPL + exception license + * + * Revision 1.1 2003/07/15 01:15:09 dom + * ispell enchant backend + * + * Revision 1.2 2003/01/29 05:50:12 hippietrail + * + * Fixed my mess in EncodingManager. + * Changed many C casts to C++ casts. + * + * Revision 1.1 2003/01/24 05:52:36 hippietrail + * + * Refactored ispell code. Old ispell global variables had been put into + * an allocated structure, a pointer to which was passed to many functions. + * I have now made all such functions and variables private members of the + * ISpellChecker class. It was C OO, now it's C++ OO. + * + * I've fixed the makefiles and tested compilation but am unable to test + * operation. Please back out my changes if they cause problems which + * are not obvious or easy to fix. + * + * Revision 1.6 2003/01/06 18:48:42 dom + * ispell cleanup, start of using new 'add' save features + * + * Revision 1.5 2002/09/19 05:31:20 hippietrail + * + * More Ispell cleanup. Conditional globals and DEREF macros are removed. + * K&R function declarations removed, converted to Doxygen style comments + * where possible. No code has been changed (I hope). Compiles for me but + * unable to test. + * + * Revision 1.4 2002/09/17 03:03:31 hippietrail + * + * After seeking permission on the developer list I've reformatted all the + * spelling source which seemed to have parts which used 2, 3, 4, and 8 + * spaces for tabs. It should all look good with our standard 4-space + * tabs now. + * I've concentrated just on indentation in the actual code. More prettying + * could be done. + * * NO code changes were made * + * + * Revision 1.3 2002/09/13 17:20:14 mpritchett + * Fix more warnings for Linux build + * + * Revision 1.2 2001/05/12 16:05:42 thomasf + * Big pseudo changes to ispell to make it pass around a structure rather + * than rely on all sorts of gloabals willy nilly here and there. Also + * fixed our spelling class to work with accepting suggestions once more. + * This code is dirty, gross and ugly (not to mention still not supporting + * multiple hash sized just yet) but it works on my machine and will no + * doubt break other machines. + * + * Revision 1.1 2001/04/15 16:01:24 tomas_f + * moving to spell/xp + * + * Revision 1.7 1999/10/20 06:03:56 sterwill + * Changed C++-style comments to C-style comments in C code. + * + * Revision 1.6 1999/10/20 03:19:35 paul + * Hacked ispell code to ignore any characters that don't fit in the lookup tables loaded from the dictionary. It ain't pretty, but at least we don't crash there any more. + * + * Revision 1.5 1999/04/13 17:12:51 jeff + * Applied "Darren O. Benham" spell check changes. + * Fixed crash on Win32 with the new code. + * + * Revision 1.4 1998/12/29 14:55:33 eric + * + * I've doctored the ispell code pretty extensively here. It is now + * warning-free on Win32. It also *works* on Win32 now, since I + * replaced all the I/O calls with ANSI standard ones. + * + * Revision 1.4 1998/12/29 14:55:33 eric + * + * I've doctored the ispell code pretty extensively here. It is now + * warning-free on Win32. It also *works* on Win32 now, since I + * replaced all the I/O calls with ANSI standard ones. + * + * Revision 1.3 1998/12/28 23:11:30 eric + * + * modified spell code and integration to build on Windows. + * This is still a hack. + * + * Actually, it doesn't yet WORK on Windows. It just builds. + * SpellCheckInit is failing for some reason. + * + * Revision 1.2 1998/12/28 22:16:22 eric + * + * These changes begin to incorporate the spell checker into AbiWord. Most + * of this is a hack. + * + * 1. added other/spell to the -I list in config/abi_defs + * 2. replaced other/spell/Makefile with one which is more like + * our build system. + * 3. added other/spell to other/Makefile so that the build will now + * dive down and build the spell check library. + * 4. added the AbiSpell library to the Makefiles in wp/main + * 5. added a call to SpellCheckInit in wp/main/unix/UnixMain.cpp. + * This call is a HACK and should be replaced with something + * proper later. + * 6. added code to fv_View.cpp as follows: + * whenever you double-click on a word, the spell checker + * verifies that word and prints its status to stdout. + * + * Caveats: + * 1. This will break the Windows build. I'm going to work on fixing it + * now. + * 2. This only works if your dictionary is in /usr/lib/ispell/american.hash. + * The dictionary location is currently hard-coded. This will be + * fixed as well. + * + * Anyway, such as it is, it works. + * + * Revision 1.1 1998/12/28 18:04:43 davet + * Spell checker code stripped from ispell. At this point, there are + * two external routines... the Init routine, and a check-a-word routine + * which returns a boolean value, and takes a 16 bit char string. + * The code resembles the ispell code as much as possible still. + * + * Revision 1.32 1994/11/02 06:56:16 geoff + * Remove the anyword feature, which I've decided is a bad idea. + * + * Revision 1.31 1994/10/25 05:46:25 geoff + * Add support for the FF_ANYWORD (affix applies to all words, even if + * flag bit isn't set) flag option. + * + * Revision 1.30 1994/05/24 06:23:08 geoff + * Don't create a hit if "allhits" is clear and capitalization + * mismatches. This cures a bug where a word could be in the dictionary + * and yet not found. + * + * Revision 1.29 1994/05/17 06:44:21 geoff + * Add support for controlled compound formation and the COMPOUNDONLY + * option to affix flags. + * + * Revision 1.28 1994/01/25 07:12:13 geoff + * Get rid of all old RCS log lines in preparation for the 3.1 release. + * + */ + +#include +#include +#include + +#include "ispell_checker.h" + +/*! + * Check possible affixes + * + * \param word Word to be checked + * \param ucword Upper-case-only copy of word + * \param len The length of word/ucword + * \param ignoreflagbits Ignore whether affix is legal + * \param allhits Keep going after first hit + * \param pfxopts Options to apply to prefixes + * \param sfxopts Options to apply to suffixes + */ +void ISpellChecker::chk_aff (ichar_t *word, ichar_t *ucword, + int len, int ignoreflagbits, int allhits, int pfxopts, int sfxopts) +{ + register ichar_t * cp; /* Pointer to char to index on */ + struct flagptr * ind; /* Flag index table to test */ + + pfx_list_chk (word, ucword, len, pfxopts, sfxopts, &m_pflagindex[0], + ignoreflagbits, allhits); + cp = ucword; + /* HACK: bail on unrecognized chars */ + if (*cp >= (SET_SIZE + MAXSTRINGCHARS)) + return; + ind = &m_pflagindex[*cp++]; + while (ind->numents == 0 && ind->pu.fp != NULL) + { + if (*cp == 0) + return; + if (ind->pu.fp[0].numents) + { + pfx_list_chk (word, ucword, len, pfxopts, sfxopts, &ind->pu.fp[0], + ignoreflagbits, allhits); + if (m_numhits && !allhits && /* !cflag && */ !ignoreflagbits) + return; + } + /* HACK: bail on unrecognized chars */ + if (*cp >= (SET_SIZE + MAXSTRINGCHARS)) + return; + ind = &ind->pu.fp[*cp++]; + } + pfx_list_chk (word, ucword, len, pfxopts, sfxopts, ind, ignoreflagbits, + allhits); + if (m_numhits && !allhits && /* !cflag &&*/ !ignoreflagbits) + return; + chk_suf (word, ucword, len, sfxopts, static_cast(NULL), + ignoreflagbits, allhits); +} + +/*! + * Check some prefix flags + * + * \param word Word to be checked + * \param ucword Upper-case-only word + * \param len The length of ucword + * \param optflags Options to apply + * \param sfxopts Options to apply to suffixes + * \param ind Flag index table + * \param ignoreflagbits Ignore whether affix is legal + * \param allhits Keep going after first hit + * */ +void ISpellChecker::pfx_list_chk (ichar_t *word, ichar_t *ucword, int len, int optflags, + int sfxopts, struct flagptr * ind, int ignoreflagbits, int allhits) +{ + int cond; /* Condition number */ + register ichar_t * cp; /* Pointer into end of ucword */ + struct dent * dent; /* Dictionary entry we found */ + int entcount; /* Number of entries to process */ + register struct flagent * + flent; /* Current table entry */ + int preadd; /* Length added to tword2 as prefix */ + register int tlen; /* Length of tword */ + ichar_t tword[INPUTWORDLEN + 4 * MAXAFFIXLEN + 4]; /* Tmp cpy */ + ichar_t tword2[sizeof tword]; /* 2nd copy for ins_root_cap */ + + for (flent = ind->pu.ent, entcount = ind->numents; + entcount > 0; + flent++, entcount--) + { + /* + * If this is a compound-only affix, ignore it unless we're + * looking for that specific thing. + */ + if ((flent->flagflags & FF_COMPOUNDONLY) != 0 + && (optflags & FF_COMPOUNDONLY) == 0) + continue; + + /* + * See if the prefix matches. + */ + tlen = len - flent->affl; + if (tlen > 0 + && (flent->affl == 0 + || icharncmp (flent->affix, ucword, flent->affl) == 0) + && tlen + flent->stripl >= flent->numconds) + { + /* + * The prefix matches. Remove it, replace it by the "strip" + * string (if any), and check the original conditions. + */ + if (flent->stripl) + icharcpy (tword, flent->strip); + icharcpy (tword + flent->stripl, ucword + flent->affl); + cp = tword; + for (cond = 0; cond < flent->numconds; cond++) + { + if ((flent->conds[*cp++] & (1 << cond)) == 0) + break; + } + if (cond >= flent->numconds) + { + /* + * The conditions match. See if the word is in the + * dictionary. + */ + tlen += flent->stripl; + + if (ignoreflagbits) + { + if ((dent = ispell_lookup (tword, 1)) != NULL) + { + cp = tword2; + if (flent->affl) + { + icharcpy (cp, flent->affix); + cp += flent->affl; + *cp++ = '+'; + } + preadd = cp - tword2; + icharcpy (cp, tword); + cp += tlen; + if (flent->stripl) + { + *cp++ = '-'; + icharcpy (cp, flent->strip); + } + } + } + else if ((dent = ispell_lookup (tword, 1)) != NULL + && TSTMASKBIT (dent->mask, flent->flagbit)) + { + if (m_numhits < MAX_HITS) + { + m_hits[m_numhits].dictent = dent; + m_hits[m_numhits].prefix = flent; + m_hits[m_numhits].suffix = NULL; + m_numhits++; + } + if (!allhits) + { +#ifndef NO_CAPITALIZATION_SUPPORT + if (cap_ok (word, &m_hits[0], len)) + return; + m_numhits = 0; +#else /* NO_CAPITALIZATION_SUPPORT */ + return; +#endif /* NO_CAPITALIZATION_SUPPORT */ + } + } + /* + * Handle cross-products. + */ + if (flent->flagflags & FF_CROSSPRODUCT) + chk_suf (word, tword, tlen, sfxopts | FF_CROSSPRODUCT, + flent, ignoreflagbits, allhits); + } + } + } +} + +/*! + * Check possible suffixes + * + * \param word Word to be checked + * \param ucword Upper-case-only word + * \param len The length of ucword + * \param optflags Affix option flags + * \param pfxent Prefix flag entry if cross-prod + * \param ignoreflagbits Ignore whether affix is legal + * \param allhits Keep going after first hit + */ +void +ISpellChecker::chk_suf (ichar_t *word, ichar_t *ucword, + int len, int optflags, struct flagent *pfxent, + int ignoreflagbits, int allhits) +{ + register ichar_t * cp; /* Pointer to char to index on */ + struct flagptr * ind; /* Flag index table to test */ + + suf_list_chk (word, ucword, len, &m_sflagindex[0], optflags, pfxent, + ignoreflagbits, allhits); + cp = ucword + len - 1; + /* HACK: bail on unrecognized chars */ + if (*cp >= (SET_SIZE + MAXSTRINGCHARS)) + return; + ind = &m_sflagindex[*cp]; + while (ind->numents == 0 && ind->pu.fp != NULL) + { + if (cp == ucword) + return; + if (ind->pu.fp[0].numents) + { + suf_list_chk (word, ucword, len, &ind->pu.fp[0], + optflags, pfxent, ignoreflagbits, allhits); + if (m_numhits != 0 && !allhits && /* !cflag && */ !ignoreflagbits) + return; + } + /* HACK: bail on unrecognized chars */ + if (*(cp-1) >= (SET_SIZE + MAXSTRINGCHARS)) + return; + ind = &ind->pu.fp[*--cp]; + } + suf_list_chk (word, ucword, len, ind, optflags, pfxent, + ignoreflagbits, allhits); +} + +/*! + * \param word Word to be checked + * \param ucword Upper-case-only word + * \param len The length of ucword + * \param ind Flag index table + * \param optflags Affix option flags + * \param pfxent Prefix flag entry if crossonly + * \param ignoreflagbits Ignore whether affix is legal + * \pram allhits Keep going after first hit + */ +void ISpellChecker::suf_list_chk (ichar_t *word, ichar_t *ucword, + int len, struct flagptr *ind, int optflags, + struct flagent *pfxent, int ignoreflagbits, int allhits) +{ + register ichar_t * cp; /* Pointer into end of ucword */ + int cond; /* Condition number */ + struct dent * dent; /* Dictionary entry we found */ + int entcount; /* Number of entries to process */ + register struct flagent * + flent; /* Current table entry */ + int preadd; /* Length added to tword2 as prefix */ + register int tlen; /* Length of tword */ + ichar_t tword[INPUTWORDLEN + 4 * MAXAFFIXLEN + 4]; /* Tmp cpy */ + ichar_t tword2[sizeof tword]; /* 2nd copy for ins_root_cap */ + + icharcpy (tword, ucword); + for (flent = ind->pu.ent, entcount = ind->numents; + entcount > 0; + flent++, entcount--) + { + if ((optflags & FF_CROSSPRODUCT) != 0 + && (flent->flagflags & FF_CROSSPRODUCT) == 0) + continue; + /* + * If this is a compound-only affix, ignore it unless we're + * looking for that specific thing. + */ + if ((flent->flagflags & FF_COMPOUNDONLY) != 0 + && (optflags & FF_COMPOUNDONLY) == 0) + continue; + + /* + * See if the suffix matches. + */ + tlen = len - flent->affl; + if (tlen > 0 + && (flent->affl == 0 + || icharcmp (flent->affix, ucword + tlen) == 0) + && tlen + flent->stripl >= flent->numconds) + { + /* + * The suffix matches. Remove it, replace it by the "strip" + * string (if any), and check the original conditions. + */ + icharcpy (tword, ucword); + cp = tword + tlen; + if (flent->stripl) + { + icharcpy (cp, flent->strip); + tlen += flent->stripl; + cp = tword + tlen; + } + else + *cp = '\0'; + for (cond = flent->numconds; --cond >= 0; ) + { + if ((flent->conds[*--cp] & (1 << cond)) == 0) + break; + } + if (cond < 0) + { + /* + * The conditions match. See if the word is in the + * dictionary. + */ + if (ignoreflagbits) + { + if ((dent = ispell_lookup (tword, 1)) != NULL) + { + cp = tword2; + if ((optflags & FF_CROSSPRODUCT) + && pfxent->affl != 0) + { + icharcpy (cp, pfxent->affix); + cp += pfxent->affl; + *cp++ = '+'; + } + preadd = cp - tword2; + icharcpy (cp, tword); + cp += tlen; + if ((optflags & FF_CROSSPRODUCT) + && pfxent->stripl != 0) + { + *cp++ = '-'; + icharcpy (cp, pfxent->strip); + cp += pfxent->stripl; + } + if (flent->stripl) + { + *cp++ = '-'; + icharcpy (cp, flent->strip); + cp += flent->stripl; + } + if (flent->affl) + { + *cp++ = '+'; + icharcpy (cp, flent->affix); + cp += flent->affl; + } + } + } + else if ((dent = ispell_lookup (tword, 1)) != NULL + && TSTMASKBIT (dent->mask, flent->flagbit) + && ((optflags & FF_CROSSPRODUCT) == 0 + || TSTMASKBIT (dent->mask, pfxent->flagbit))) + { + if (m_numhits < MAX_HITS) + { + m_hits[m_numhits].dictent = dent; + m_hits[m_numhits].prefix = pfxent; + m_hits[m_numhits].suffix = flent; + m_numhits++; + } + if (!allhits) + { +#ifndef NO_CAPITALIZATION_SUPPORT + if (cap_ok (word, &m_hits[0], len)) + return; + m_numhits = 0; +#else /* NO_CAPITALIZATION_SUPPORT */ + return; +#endif /* NO_CAPITALIZATION_SUPPORT */ + } + } + } + } + } +} + +/*! + * Expand a dictionary prefix entry + * + * \param croot Char version of rootword + * \param rootword Root word to expand + * \param mask Mask bits to expand on + * \param option Option, see expandmode + * \param extra Extra info to add to line + * + * \return + */ +int ISpellChecker::expand_pre (char *croot, ichar_t *rootword, MASKTYPE mask[], + int option, char *extra) +{ + int entcount; /* No. of entries to process */ + int explength; /* Length of expansions */ + register struct flagent * + flent; /* Current table entry */ + + for (flent = m_pflaglist, entcount = m_numpflags, explength = 0; + entcount > 0; + flent++, entcount--) + { + if (TSTMASKBIT (mask, flent->flagbit)) + explength += + pr_pre_expansion (croot, rootword, flent, mask, option, extra); + } + return explength; +} + +/*! + * Print a prefix expansion + * + * \param croot Char version of rootword + * \param rootword Root word to expand + * \param flent Current table entry + * \param mask Mask bits to expand on + * \param option Option, see expandmode + * \param extra Extra info to add to line + * + * \return + */ +int ISpellChecker::pr_pre_expansion ( char *croot, ichar_t *rootword, + struct flagent *flent, MASKTYPE mask[], int option, + char *extra) +{ + int cond; /* Current condition number */ + register ichar_t * nextc; /* Next case choice */ + int tlen; /* Length of tword */ + ichar_t tword[INPUTWORDLEN + MAXAFFIXLEN]; /* Temp */ + + tlen = icharlen (rootword); + if (flent->numconds > tlen) + return 0; + tlen -= flent->stripl; + if (tlen <= 0) + return 0; + tlen += flent->affl; + for (cond = 0, nextc = rootword; cond < flent->numconds; cond++) + { + if ((flent->conds[mytoupper (*nextc++)] & (1 << cond)) == 0) + return 0; + } + /* + * The conditions are satisfied. Copy the word, add the prefix, + * and make it the proper case. This code is carefully written + * to match that ins_cap and cap_ok. Note that the affix, as + * inserted, is uppercase. + * + * There is a tricky bit here: if the root is capitalized, we + * want a capitalized result. If the root is followcase, however, + * we want to duplicate the case of the first remaining letter + * of the root. In other words, "Loved/U" should generate "Unloved", + * but "LOved/U" should generate "UNLOved" and "lOved/U" should + * produce "unlOved". + */ + if (flent->affl) + { + icharcpy (tword, flent->affix); + nextc = tword + flent->affl; + } + icharcpy (nextc, rootword + flent->stripl); + if (myupper (rootword[0])) + { + /* We must distinguish followcase from capitalized and all-upper */ + for (nextc = rootword + 1; *nextc; nextc++) + { + if (!myupper (*nextc)) + break; + } + if (*nextc) + { + /* It's a followcase or capitalized word. Figure out which. */ + for ( ; *nextc; nextc++) + { + if (myupper (*nextc)) + break; + } + if (*nextc) + { + /* It's followcase. */ + if (!myupper (tword[flent->affl])) + forcelc (tword, flent->affl); + } + else + { + /* It's capitalized */ + forcelc (tword + 1, tlen - 1); + } + } + } + else + { + /* Followcase or all-lower, we don't care which */ + if (!myupper (*nextc)) + forcelc (tword, flent->affl); + } + if (option == 3) + printf ("\n%s", croot); + if (option != 4) + printf (" %s%s", ichartosstr (tword, 1), extra); + if (flent->flagflags & FF_CROSSPRODUCT) + return tlen + + expand_suf (croot, tword, mask, FF_CROSSPRODUCT, option, extra); + else + return tlen; +} + +/*! + * Expand a dictionary suffix entry + * + * \param croot Char version of rootword + * \param rootword Root word to expand + * \param mask Mask bits to expand on + * \param optflags Affix option flags + * \param option Option, see expandmode + * \param extra Extra info to add to line + * + * \return + */ +int ISpellChecker::expand_suf (char *croot, ichar_t *rootword, MASKTYPE mask[], + int optflags, int option, char *extra) +{ + int entcount; /* No. of entries to process */ + int explength; /* Length of expansions */ + register struct flagent * + flent; /* Current table entry */ + + for (flent = m_sflaglist, entcount = m_numsflags, explength = 0; + entcount > 0; + flent++, entcount--) + { + if (TSTMASKBIT (mask, flent->flagbit)) + { + if ((optflags & FF_CROSSPRODUCT) == 0 + || (flent->flagflags & FF_CROSSPRODUCT)) + explength += + pr_suf_expansion (croot, rootword, flent, option, extra); + } + } + return explength; +} + +/*! + * Print a suffix expansion + * + * \param croot Char version of rootword + * \param rootword Root word to expand + * \param flent Current table entry + * \param option Option, see expandmode + * \param extra Extra info to add to line + * + * \return + */ +int ISpellChecker::pr_suf_expansion (char *croot, ichar_t *rootword, + struct flagent *flent, int option, char *extra) +{ + int cond; /* Current condition number */ + register ichar_t * nextc; /* Next case choice */ + int tlen; /* Length of tword */ + ichar_t tword[INPUTWORDLEN + MAXAFFIXLEN]; /* Temp */ + + tlen = icharlen (rootword); + cond = flent->numconds; + if (cond > tlen) + return 0; + if (tlen - flent->stripl <= 0) + return 0; + for (nextc = rootword + tlen; --cond >= 0; ) + { + if ((flent->conds[mytoupper (*--nextc)] & (1 << cond)) == 0) + return 0; + } + /* + * The conditions are satisfied. Copy the word, add the suffix, + * and make it match the case of the last remaining character of the + * root. Again, this code carefully matches ins_cap and cap_ok. + */ + icharcpy (tword, rootword); + nextc = tword + tlen - flent->stripl; + if (flent->affl) + { + icharcpy (nextc, flent->affix); + if (!myupper (nextc[-1])) + forcelc (nextc, flent->affl); + } + else + *nextc = 0; + if (option == 3) + printf ("\n%s", croot); + if (option != 4) + printf (" %s%s", ichartosstr (tword, 1), extra); + return tlen + flent->affl - flent->stripl; +} + +/*! + * \param dst Destination to modify + * \param len Length to copy + */ +void ISpellChecker::forcelc (ichar_t *dst, int len) /* Force to lowercase */ +{ + + for ( ; --len >= 0; dst++) + *dst = mytolower (*dst); +} diff --git a/src/libenchant.rc.in b/src/libenchant.rc.in new file mode 100644 index 0000000..5ff66de --- /dev/null +++ b/src/libenchant.rc.in @@ -0,0 +1,31 @@ +#include + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @ENCHANT_MAJOR_VERSION@,@ENCHANT_MINOR_VERSION@,@ENCHANT_MICRO_VERSION@,BUILDNUMBER + PRODUCTVERSION @ENCHANT_MAJOR_VERSION@,@ENCHANT_MINOR_VERSION@,@ENCHANT_MICRO_VERSION@,0 + FILEFLAGSMASK 0 + FILEFLAGS 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE VFT2_UNKNOWN + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "none" + VALUE "FileDescription", "libenchant" + VALUE "FileVersion", "@ENCHANT_MAJOR_VERSION@.@ENCHANT_MINOR_VERSION@.@ENCHANT_MICRO_VERSION@.BUILDNUMBER" + VALUE "InternalName", "libenchant-@ENCHANT_MAJOR_VERSION@.@ENCHANT_MINOR_VERSION@" + VALUE "LegalCopyright", "Copyright (C) 2002-2007 Dom Lachowicz" + VALUE "OriginalFilename", "libenchant-@ENCHANT_MAJOR_VERSION@.@ENCHANT_MINOR_VERSION@.dll" + VALUE "ProductName", "libenchant" + VALUE "ProductVersion", "@ENCHANT_MAJOR_VERSION@.@ENCHANT_MINOR_VERSION@.@ENCHANT_MICRO_VERSION@" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END + END + diff --git a/src/myspell/.cvsignore b/src/myspell/.cvsignore new file mode 100644 index 0000000..587e7b3 --- /dev/null +++ b/src/myspell/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +*.lo +*.la +.deps +.libs diff --git a/src/myspell/Makefile.am b/src/myspell/Makefile.am new file mode 100644 index 0000000..7929d21 --- /dev/null +++ b/src/myspell/Makefile.am @@ -0,0 +1,70 @@ +if WITH_MYSPELL +target_lib = libenchant_myspell.la +else +target_lib = +endif + +# copied from hunspell 1.2.15 +COPIED_MYSPELL_FILES= \ + affentry.cxx \ + affentry.hxx \ + affixmgr.cxx \ + affixmgr.hxx \ + atypes.hxx \ + baseaffix.hxx \ + csutil.cxx \ + csutil.hxx \ + dictmgr.cxx \ + dictmgr.hxx \ + filemgr.cxx \ + filemgr.hxx \ + hashmgr.cxx \ + hashmgr.hxx \ + htypes.hxx \ + hunspell.cxx \ + hunspell.h \ + hunspell.hxx \ + hunvisapi.h \ + hunzip.cxx \ + hunzip.hxx \ + langnum.hxx \ + phonet.cxx \ + phonet.hxx \ + replist.cxx \ + replist.hxx \ + suggestmgr.cxx \ + suggestmgr.hxx \ + utf_info.cxx \ + w_char.hxx + +if WITH_SYSTEM_MYSPELL +EXTRA_MYSPELL_FILES= +EXTRA_MYSPELL_CFLAGS=-DWITH_SYSTEM_MYSPELL +else +EXTRA_MYSPELL_FILES=$(COPIED_MYSPELL_FILES) +EXTRA_MYSPELL_CFLAGS=-DHUNSPELL_STATIC +endif + +INCLUDES=-I$(top_srcdir)/src $(ENCHANT_CFLAGS) $(MYSPELL_CFLAGS) $(CXX_WARN_CFLAGS) $(EXTRA_MYSPELL_CFLAGS) -D_ENCHANT_BUILD=1 + +myspell_LTLIBRARIES = $(target_lib) +myspelldir= $(libdir)/enchant + +libenchant_myspell_lalibdir=$(libdir)/enchant +libenchant_myspell_la_LIBADD= $(MYSPELL_LIBS) $(ENCHANT_LIBS) $(top_builddir)/src/libenchant.la +libenchant_myspell_la_LDFLAGS = -module -avoid-version -no-undefined + +libenchant_myspell_la_SOURCES = \ + $(EXTRA_MYSPELL_FILES) \ + myspell_checker.cpp + +EXTRA_DIST= \ + $(COPIED_MYSPELL_FILES) \ + license.readme \ + utf_info.cxx \ + license.hunspell \ + license.myspell \ + license.readme \ + libenchant_myspell.rc \ + hunspell.dsp + diff --git a/src/myspell/README b/src/myspell/README new file mode 100644 index 0000000..b452096 --- /dev/null +++ b/src/myspell/README @@ -0,0 +1,21 @@ +Hunspell spell checker and morphological analyser library + +Documentation, tests, examples: http://hunspell.sourceforge.net + +Author of Hunspell: +László Németh (nemethl (at) gyorsposta.hu) + +Hunspell based on OpenOffice.org's Myspell. MySpell's author: +Kevin Hendricks (kevin.hendricks (at) sympatico.ca) + +License: GPL 2.0/LGPL 2.1/MPL 1.1 tri-license + +The contents of this library may be used under the terms of +the GNU General Public License Version 2 or later (the "GPL"), or +the GNU Lesser General Public License Version 2.1 or later (the "LGPL", +see http://gnu.org/copyleft/lesser.html) or the Mozilla Public License +Version 1.1 or later (the "MPL", see http://mozilla.org/MPL/MPL-1.1.html). + +Software distributed under these licenses is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the licences +for the specific language governing rights and limitations under the licenses. diff --git a/src/myspell/affentry.cxx b/src/myspell/affentry.cxx new file mode 100644 index 0000000..fef0cca --- /dev/null +++ b/src/myspell/affentry.cxx @@ -0,0 +1,962 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include +#include + +#include "affentry.hxx" +#include "csutil.hxx" + +PfxEntry::PfxEntry(AffixMgr* pmgr, affentry* dp) +{ + // register affix manager + pmyMgr = pmgr; + + // set up its initial values + + aflag = dp->aflag; // flag + strip = dp->strip; // string to strip + appnd = dp->appnd; // string to append + stripl = dp->stripl; // length of strip string + appndl = dp->appndl; // length of append string + numconds = dp->numconds; // length of the condition + opts = dp->opts; // cross product flag + // then copy over all of the conditions + if (opts & aeLONGCOND) { + memcpy(c.conds, dp->c.l.conds1, MAXCONDLEN_1); + c.l.conds2 = dp->c.l.conds2; + } else memcpy(c.conds, dp->c.conds, MAXCONDLEN); + next = NULL; + nextne = NULL; + nexteq = NULL; + morphcode = dp->morphcode; + contclass = dp->contclass; + contclasslen = dp->contclasslen; +} + + +PfxEntry::~PfxEntry() +{ + aflag = 0; + if (appnd) free(appnd); + if (strip) free(strip); + pmyMgr = NULL; + appnd = NULL; + strip = NULL; + if (opts & aeLONGCOND) free(c.l.conds2); + if (morphcode && !(opts & aeALIASM)) free(morphcode); + if (contclass && !(opts & aeALIASF)) free(contclass); +} + +// add prefix to this word assuming conditions hold +char * PfxEntry::add(const char * word, int len) +{ + char tword[MAXWORDUTF8LEN + 4]; + + if ((len > stripl || (len == 0 && pmyMgr->get_fullstrip())) && + (len >= numconds) && test_condition(word) && + (!stripl || (strncmp(word, strip, stripl) == 0)) && + ((MAXWORDUTF8LEN + 4) > (len + appndl - stripl))) { + /* we have a match so add prefix */ + char * pp = tword; + if (appndl) { + strcpy(tword,appnd); + pp += appndl; + } + strcpy(pp, (word + stripl)); + return mystrdup(tword); + } + return NULL; +} + +inline char * PfxEntry::nextchar(char * p) { + if (p) { + p++; + if (opts & aeLONGCOND) { + // jump to the 2nd part of the condition + if (p == c.conds + MAXCONDLEN_1) return c.l.conds2; + // end of the MAXCONDLEN length condition + } else if (p == c.conds + MAXCONDLEN) return NULL; + return *p ? p : NULL; + } + return NULL; +} + +inline int PfxEntry::test_condition(const char * st) +{ + const char * pos = NULL; // group with pos input position + bool neg = false; // complementer + bool ingroup = false; // character in the group + if (numconds == 0) return 1; + char * p = c.conds; + while (1) { + switch (*p) { + case '\0': return 1; + case '[': { + neg = false; + ingroup = false; + p = nextchar(p); + pos = st; break; + } + case '^': { p = nextchar(p); neg = true; break; } + case ']': { + if ((neg && ingroup) || (!neg && !ingroup)) return 0; + pos = NULL; + p = nextchar(p); + // skip the next character + if (!ingroup && *st) for (st++; (opts & aeUTF8) && (*st & 0xc0) == 0x80; st++); + if (*st == '\0' && p) return 0; // word <= condition + break; + } + case '.': if (!pos) { // dots are not metacharacters in groups: [.] + p = nextchar(p); + // skip the next character + for (st++; (opts & aeUTF8) && (*st & 0xc0) == 0x80; st++); + if (*st == '\0' && p) return 0; // word <= condition + break; + } + default: { + if (*st == *p) { + st++; + p = nextchar(p); + if ((opts & aeUTF8) && (*(st - 1) & 0x80)) { // multibyte + while (p && (*p & 0xc0) == 0x80) { // character + if (*p != *st) { + if (!pos) return 0; + st = pos; + break; + } + p = nextchar(p); + st++; + } + if (pos && st != pos) { + ingroup = true; + while (p && *p != ']' && (p = nextchar(p))); + } + } else if (pos) { + ingroup = true; + while (p && *p != ']' && (p = nextchar(p))); + } + } else if (pos) { // group + p = nextchar(p); + } else return 0; + } + } + if (!p) return 1; + } +} + +// check if this prefix entry matches +struct hentry * PfxEntry::checkword(const char * word, int len, char in_compound, const FLAG needflag) +{ + int tmpl; // length of tmpword + struct hentry * he; // hash entry of root word or NULL + char tmpword[MAXWORDUTF8LEN + 4]; + + // on entry prefix is 0 length or already matches the beginning of the word. + // So if the remaining root word has positive length + // and if there are enough chars in root word and added back strip chars + // to meet the number of characters conditions, then test it + + tmpl = len - appndl; + + if (tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) { + + // generate new root word by removing prefix and adding + // back any characters that would have been stripped + + if (stripl) strcpy (tmpword, strip); + strcpy ((tmpword + stripl), (word + appndl)); + + // now make sure all of the conditions on characters + // are met. Please see the appendix at the end of + // this file for more info on exactly what is being + // tested + + // if all conditions are met then check if resulting + // root word in the dictionary + + if (test_condition(tmpword)) { + tmpl += stripl; + if ((he = pmyMgr->lookup(tmpword)) != NULL) { + do { + if (TESTAFF(he->astr, aflag, he->alen) && + // forbid single prefixes with needaffix flag + ! TESTAFF(contclass, pmyMgr->get_needaffix(), contclasslen) && + // needflag + ((!needflag) || TESTAFF(he->astr, needflag, he->alen) || + (contclass && TESTAFF(contclass, needflag, contclasslen)))) + return he; + he = he->next_homonym; // check homonyms + } while (he); + } + + // prefix matched but no root word was found + // if aeXPRODUCT is allowed, try again but now + // ross checked combined with a suffix + + //if ((opts & aeXPRODUCT) && in_compound) { + if ((opts & aeXPRODUCT)) { + he = pmyMgr->suffix_check(tmpword, tmpl, aeXPRODUCT, this, NULL, + 0, NULL, FLAG_NULL, needflag, in_compound); + if (he) return he; + } + } + } + return NULL; +} + +// check if this prefix entry matches +struct hentry * PfxEntry::check_twosfx(const char * word, int len, + char in_compound, const FLAG needflag) +{ + int tmpl; // length of tmpword + struct hentry * he; // hash entry of root word or NULL + char tmpword[MAXWORDUTF8LEN + 4]; + + // on entry prefix is 0 length or already matches the beginning of the word. + // So if the remaining root word has positive length + // and if there are enough chars in root word and added back strip chars + // to meet the number of characters conditions, then test it + + tmpl = len - appndl; + + if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) && + (tmpl + stripl >= numconds)) { + + // generate new root word by removing prefix and adding + // back any characters that would have been stripped + + if (stripl) strcpy (tmpword, strip); + strcpy ((tmpword + stripl), (word + appndl)); + + // now make sure all of the conditions on characters + // are met. Please see the appendix at the end of + // this file for more info on exactly what is being + // tested + + // if all conditions are met then check if resulting + // root word in the dictionary + + if (test_condition(tmpword)) { + tmpl += stripl; + + // prefix matched but no root word was found + // if aeXPRODUCT is allowed, try again but now + // cross checked combined with a suffix + + if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) { + he = pmyMgr->suffix_check_twosfx(tmpword, tmpl, aeXPRODUCT, this, needflag); + if (he) return he; + } + } + } + return NULL; +} + +// check if this prefix entry matches +char * PfxEntry::check_twosfx_morph(const char * word, int len, + char in_compound, const FLAG needflag) +{ + int tmpl; // length of tmpword + char tmpword[MAXWORDUTF8LEN + 4]; + + // on entry prefix is 0 length or already matches the beginning of the word. + // So if the remaining root word has positive length + // and if there are enough chars in root word and added back strip chars + // to meet the number of characters conditions, then test it + + tmpl = len - appndl; + + if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) && + (tmpl + stripl >= numconds)) { + + // generate new root word by removing prefix and adding + // back any characters that would have been stripped + + if (stripl) strcpy (tmpword, strip); + strcpy ((tmpword + stripl), (word + appndl)); + + // now make sure all of the conditions on characters + // are met. Please see the appendix at the end of + // this file for more info on exactly what is being + // tested + + // if all conditions are met then check if resulting + // root word in the dictionary + + if (test_condition(tmpword)) { + tmpl += stripl; + + // prefix matched but no root word was found + // if aeXPRODUCT is allowed, try again but now + // ross checked combined with a suffix + + if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) { + return pmyMgr->suffix_check_twosfx_morph(tmpword, tmpl, + aeXPRODUCT, this, needflag); + } + } + } + return NULL; +} + +// check if this prefix entry matches +char * PfxEntry::check_morph(const char * word, int len, char in_compound, const FLAG needflag) +{ + int tmpl; // length of tmpword + struct hentry * he; // hash entry of root word or NULL + char tmpword[MAXWORDUTF8LEN + 4]; + char result[MAXLNLEN]; + char * st; + + *result = '\0'; + + // on entry prefix is 0 length or already matches the beginning of the word. + // So if the remaining root word has positive length + // and if there are enough chars in root word and added back strip chars + // to meet the number of characters conditions, then test it + + tmpl = len - appndl; + + if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) && + (tmpl + stripl >= numconds)) { + + // generate new root word by removing prefix and adding + // back any characters that would have been stripped + + if (stripl) strcpy (tmpword, strip); + strcpy ((tmpword + stripl), (word + appndl)); + + // now make sure all of the conditions on characters + // are met. Please see the appendix at the end of + // this file for more info on exactly what is being + // tested + + // if all conditions are met then check if resulting + // root word in the dictionary + + if (test_condition(tmpword)) { + tmpl += stripl; + if ((he = pmyMgr->lookup(tmpword)) != NULL) { + do { + if (TESTAFF(he->astr, aflag, he->alen) && + // forbid single prefixes with needaffix flag + ! TESTAFF(contclass, pmyMgr->get_needaffix(), contclasslen) && + // needflag + ((!needflag) || TESTAFF(he->astr, needflag, he->alen) || + (contclass && TESTAFF(contclass, needflag, contclasslen)))) { + if (morphcode) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, morphcode, MAXLNLEN); + } else mystrcat(result,getKey(), MAXLNLEN); + if (!HENTRY_FIND(he, MORPH_STEM)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, MORPH_STEM, MAXLNLEN); + mystrcat(result, HENTRY_WORD(he), MAXLNLEN); + } + // store the pointer of the hash entry + if (HENTRY_DATA(he)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, HENTRY_DATA2(he), MAXLNLEN); + } else { + // return with debug information + char * flag = pmyMgr->encode_flag(getFlag()); + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, MORPH_FLAG, MAXLNLEN); + mystrcat(result, flag, MAXLNLEN); + free(flag); + } + mystrcat(result, "\n", MAXLNLEN); + } + he = he->next_homonym; + } while (he); + } + + // prefix matched but no root word was found + // if aeXPRODUCT is allowed, try again but now + // ross checked combined with a suffix + + if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) { + st = pmyMgr->suffix_check_morph(tmpword, tmpl, aeXPRODUCT, this, + FLAG_NULL, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + } + } + } + + if (*result) return mystrdup(result); + return NULL; +} + +SfxEntry::SfxEntry(AffixMgr * pmgr, affentry* dp) +{ + // register affix manager + pmyMgr = pmgr; + + // set up its initial values + aflag = dp->aflag; // char flag + strip = dp->strip; // string to strip + appnd = dp->appnd; // string to append + stripl = dp->stripl; // length of strip string + appndl = dp->appndl; // length of append string + numconds = dp->numconds; // length of the condition + opts = dp->opts; // cross product flag + + // then copy over all of the conditions + if (opts & aeLONGCOND) { + memcpy(c.l.conds1, dp->c.l.conds1, MAXCONDLEN_1); + c.l.conds2 = dp->c.l.conds2; + } else memcpy(c.conds, dp->c.conds, MAXCONDLEN); + + rappnd = myrevstrdup(appnd); + morphcode = dp->morphcode; + contclass = dp->contclass; + contclasslen = dp->contclasslen; +} + + +SfxEntry::~SfxEntry() +{ + aflag = 0; + if (appnd) free(appnd); + if (rappnd) free(rappnd); + if (strip) free(strip); + pmyMgr = NULL; + appnd = NULL; + strip = NULL; + if (opts & aeLONGCOND) free(c.l.conds2); + if (morphcode && !(opts & aeALIASM)) free(morphcode); + if (contclass && !(opts & aeALIASF)) free(contclass); +} + +// add suffix to this word assuming conditions hold +char * SfxEntry::add(const char * word, int len) +{ + char tword[MAXWORDUTF8LEN + 4]; + + /* make sure all conditions match */ + if ((len > stripl || (len == 0 && pmyMgr->get_fullstrip())) && + (len >= numconds) && test_condition(word + len, word) && + (!stripl || (strcmp(word + len - stripl, strip) == 0)) && + ((MAXWORDUTF8LEN + 4) > (len + appndl - stripl))) { + /* we have a match so add suffix */ + strcpy(tword,word); + if (appndl) { + strcpy(tword + len - stripl, appnd); + } else { + *(tword + len - stripl) = '\0'; + } + return mystrdup(tword); + } + return NULL; +} + +inline char * SfxEntry::nextchar(char * p) { + if (p) { + p++; + if (opts & aeLONGCOND) { + // jump to the 2nd part of the condition + if (p == c.l.conds1 + MAXCONDLEN_1) return c.l.conds2; + // end of the MAXCONDLEN length condition + } else if (p == c.conds + MAXCONDLEN) return NULL; + return *p ? p : NULL; + } + return NULL; +} + +inline int SfxEntry::test_condition(const char * st, const char * beg) +{ + const char * pos = NULL; // group with pos input position + bool neg = false; // complementer + bool ingroup = false; // character in the group + if (numconds == 0) return 1; + char * p = c.conds; + st--; + int i = 1; + while (1) { + switch (*p) { + case '\0': return 1; + case '[': { p = nextchar(p); pos = st; break; } + case '^': { p = nextchar(p); neg = true; break; } + case ']': { if (!neg && !ingroup) return 0; + i++; + // skip the next character + if (!ingroup) { + for (; (opts & aeUTF8) && (st >= beg) && (*st & 0xc0) == 0x80; st--); + st--; + } + pos = NULL; + neg = false; + ingroup = false; + p = nextchar(p); + if (st < beg && p) return 0; // word <= condition + break; + } + case '.': if (!pos) { // dots are not metacharacters in groups: [.] + p = nextchar(p); + // skip the next character + for (st--; (opts & aeUTF8) && (st >= beg) && (*st & 0xc0) == 0x80; st--); + if (st < beg) { // word <= condition + if (p) return 0; else return 1; + } + if ((opts & aeUTF8) && (*st & 0x80)) { // head of the UTF-8 character + st--; + if (st < beg) { // word <= condition + if (p) return 0; else return 1; + } + } + break; + } + default: { + if (*st == *p) { + p = nextchar(p); + if ((opts & aeUTF8) && (*st & 0x80)) { + st--; + while (p && (st >= beg)) { + if (*p != *st) { + if (!pos) return 0; + st = pos; + break; + } + // first byte of the UTF-8 multibyte character + if ((*p & 0xc0) != 0x80) break; + p = nextchar(p); + st--; + } + if (pos && st != pos) { + if (neg) return 0; + else if (i == numconds) return 1; + ingroup = true; + while (p && *p != ']' && (p = nextchar(p))); + st--; + } + if (p && *p != ']') p = nextchar(p); + } else if (pos) { + if (neg) return 0; + else if (i == numconds) return 1; + ingroup = true; + while (p && *p != ']' && (p = nextchar(p))); +// if (p && *p != ']') p = nextchar(p); + st--; + } + if (!pos) { + i++; + st--; + } + if (st < beg && p && *p != ']') return 0; // word <= condition + } else if (pos) { // group + p = nextchar(p); + } else return 0; + } + } + if (!p) return 1; + } +} + +// see if this suffix is present in the word +struct hentry * SfxEntry::checkword(const char * word, int len, int optflags, + PfxEntry* ppfx, char ** wlst, int maxSug, int * ns, const FLAG cclass, const FLAG needflag, + const FLAG badflag) +{ + int tmpl; // length of tmpword + struct hentry * he; // hash entry pointer + unsigned char * cp; + char tmpword[MAXWORDUTF8LEN + 4]; + PfxEntry* ep = ppfx; + + // if this suffix is being cross checked with a prefix + // but it does not support cross products skip it + + if (((optflags & aeXPRODUCT) != 0) && ((opts & aeXPRODUCT) == 0)) + return NULL; + + // upon entry suffix is 0 length or already matches the end of the word. + // So if the remaining root word has positive length + // and if there are enough chars in root word and added back strip chars + // to meet the number of characters conditions, then test it + + tmpl = len - appndl; + // the second condition is not enough for UTF-8 strings + // it checked in test_condition() + + if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) && + (tmpl + stripl >= numconds)) { + + // generate new root word by removing suffix and adding + // back any characters that would have been stripped or + // or null terminating the shorter string + + strcpy (tmpword, word); + cp = (unsigned char *)(tmpword + tmpl); + if (stripl) { + strcpy ((char *)cp, strip); + tmpl += stripl; + cp = (unsigned char *)(tmpword + tmpl); + } else *cp = '\0'; + + // now make sure all of the conditions on characters + // are met. Please see the appendix at the end of + // this file for more info on exactly what is being + // tested + + // if all conditions are met then check if resulting + // root word in the dictionary + + if (test_condition((char *) cp, (char *) tmpword)) { + +#ifdef SZOSZABLYA_POSSIBLE_ROOTS + fprintf(stdout,"%s %s %c\n", word, tmpword, aflag); +#endif + if ((he = pmyMgr->lookup(tmpword)) != NULL) { + do { + // check conditional suffix (enabled by prefix) + if ((TESTAFF(he->astr, aflag, he->alen) || (ep && ep->getCont() && + TESTAFF(ep->getCont(), aflag, ep->getContLen()))) && + (((optflags & aeXPRODUCT) == 0) || + (ep && TESTAFF(he->astr, ep->getFlag(), he->alen)) || + // enabled by prefix + ((contclass) && (ep && TESTAFF(contclass, ep->getFlag(), contclasslen))) + ) && + // handle cont. class + ((!cclass) || + ((contclass) && TESTAFF(contclass, cclass, contclasslen)) + ) && + // check only in compound homonyms (bad flags) + (!badflag || !TESTAFF(he->astr, badflag, he->alen) + ) && + // handle required flag + ((!needflag) || + (TESTAFF(he->astr, needflag, he->alen) || + ((contclass) && TESTAFF(contclass, needflag, contclasslen))) + ) + ) return he; + he = he->next_homonym; // check homonyms + } while (he); + + // obsolote stemming code (used only by the + // experimental SuffixMgr:suggest_pos_stems) + // store resulting root in wlst + } else if (wlst && (*ns < maxSug)) { + int cwrd = 1; + for (int k=0; k < *ns; k++) + if (strcmp(tmpword, wlst[k]) == 0) cwrd = 0; + if (cwrd) { + wlst[*ns] = mystrdup(tmpword); + if (wlst[*ns] == NULL) { + for (int j=0; j<*ns; j++) free(wlst[j]); + *ns = -1; + return NULL; + } + (*ns)++; + } + } + } + } + return NULL; +} + +// see if two-level suffix is present in the word +struct hentry * SfxEntry::check_twosfx(const char * word, int len, int optflags, + PfxEntry* ppfx, const FLAG needflag) +{ + int tmpl; // length of tmpword + struct hentry * he; // hash entry pointer + unsigned char * cp; + char tmpword[MAXWORDUTF8LEN + 4]; + PfxEntry* ep = ppfx; + + + // if this suffix is being cross checked with a prefix + // but it does not support cross products skip it + + if ((optflags & aeXPRODUCT) != 0 && (opts & aeXPRODUCT) == 0) + return NULL; + + // upon entry suffix is 0 length or already matches the end of the word. + // So if the remaining root word has positive length + // and if there are enough chars in root word and added back strip chars + // to meet the number of characters conditions, then test it + + tmpl = len - appndl; + + if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) && + (tmpl + stripl >= numconds)) { + + // generate new root word by removing suffix and adding + // back any characters that would have been stripped or + // or null terminating the shorter string + + strcpy (tmpword, word); + cp = (unsigned char *)(tmpword + tmpl); + if (stripl) { + strcpy ((char *)cp, strip); + tmpl += stripl; + cp = (unsigned char *)(tmpword + tmpl); + } else *cp = '\0'; + + // now make sure all of the conditions on characters + // are met. Please see the appendix at the end of + // this file for more info on exactly what is being + // tested + + // if all conditions are met then recall suffix_check + + if (test_condition((char *) cp, (char *) tmpword)) { + if (ppfx) { + // handle conditional suffix + if ((contclass) && TESTAFF(contclass, ep->getFlag(), contclasslen)) + he = pmyMgr->suffix_check(tmpword, tmpl, 0, NULL, NULL, 0, NULL, (FLAG) aflag, needflag); + else + he = pmyMgr->suffix_check(tmpword, tmpl, optflags, ppfx, NULL, 0, NULL, (FLAG) aflag, needflag); + } else { + he = pmyMgr->suffix_check(tmpword, tmpl, 0, NULL, NULL, 0, NULL, (FLAG) aflag, needflag); + } + if (he) return he; + } + } + return NULL; +} + +// see if two-level suffix is present in the word +char * SfxEntry::check_twosfx_morph(const char * word, int len, int optflags, + PfxEntry* ppfx, const FLAG needflag) +{ + int tmpl; // length of tmpword + unsigned char * cp; + char tmpword[MAXWORDUTF8LEN + 4]; + PfxEntry* ep = ppfx; + char * st; + + char result[MAXLNLEN]; + + *result = '\0'; + + // if this suffix is being cross checked with a prefix + // but it does not support cross products skip it + + if ((optflags & aeXPRODUCT) != 0 && (opts & aeXPRODUCT) == 0) + return NULL; + + // upon entry suffix is 0 length or already matches the end of the word. + // So if the remaining root word has positive length + // and if there are enough chars in root word and added back strip chars + // to meet the number of characters conditions, then test it + + tmpl = len - appndl; + + if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) && + (tmpl + stripl >= numconds)) { + + // generate new root word by removing suffix and adding + // back any characters that would have been stripped or + // or null terminating the shorter string + + strcpy (tmpword, word); + cp = (unsigned char *)(tmpword + tmpl); + if (stripl) { + strcpy ((char *)cp, strip); + tmpl += stripl; + cp = (unsigned char *)(tmpword + tmpl); + } else *cp = '\0'; + + // now make sure all of the conditions on characters + // are met. Please see the appendix at the end of + // this file for more info on exactly what is being + // tested + + // if all conditions are met then recall suffix_check + + if (test_condition((char *) cp, (char *) tmpword)) { + if (ppfx) { + // handle conditional suffix + if ((contclass) && TESTAFF(contclass, ep->getFlag(), contclasslen)) { + st = pmyMgr->suffix_check_morph(tmpword, tmpl, 0, NULL, aflag, needflag); + if (st) { + if (ppfx->getMorph()) { + mystrcat(result, ppfx->getMorph(), MAXLNLEN); + mystrcat(result, " ", MAXLNLEN); + } + mystrcat(result,st, MAXLNLEN); + free(st); + mychomp(result); + } + } else { + st = pmyMgr->suffix_check_morph(tmpword, tmpl, optflags, ppfx, aflag, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + mychomp(result); + } + } + } else { + st = pmyMgr->suffix_check_morph(tmpword, tmpl, 0, NULL, aflag, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + mychomp(result); + } + } + if (*result) return mystrdup(result); + } + } + return NULL; +} + +// get next homonym with same affix +struct hentry * SfxEntry::get_next_homonym(struct hentry * he, int optflags, PfxEntry* ppfx, + const FLAG cclass, const FLAG needflag) +{ + PfxEntry* ep = ppfx; + FLAG eFlag = ep ? ep->getFlag() : FLAG_NULL; + + while (he->next_homonym) { + he = he->next_homonym; + if ((TESTAFF(he->astr, aflag, he->alen) || (ep && ep->getCont() && TESTAFF(ep->getCont(), aflag, ep->getContLen()))) && + ((optflags & aeXPRODUCT) == 0 || + TESTAFF(he->astr, eFlag, he->alen) || + // handle conditional suffix + ((contclass) && TESTAFF(contclass, eFlag, contclasslen)) + ) && + // handle cont. class + ((!cclass) || + ((contclass) && TESTAFF(contclass, cclass, contclasslen)) + ) && + // handle required flag + ((!needflag) || + (TESTAFF(he->astr, needflag, he->alen) || + ((contclass) && TESTAFF(contclass, needflag, contclasslen))) + ) + ) return he; + } + return NULL; +} + + +#if 0 + +Appendix: Understanding Affix Code + + +An affix is either a prefix or a suffix attached to root words to make +other words. + +Basically a Prefix or a Suffix is set of AffEntry objects +which store information about the prefix or suffix along +with supporting routines to check if a word has a particular +prefix or suffix or a combination. + +The structure affentry is defined as follows: + +struct affentry +{ + unsigned short aflag; // ID used to represent the affix + char * strip; // string to strip before adding affix + char * appnd; // the affix string to add + unsigned char stripl; // length of the strip string + unsigned char appndl; // length of the affix string + char numconds; // the number of conditions that must be met + char opts; // flag: aeXPRODUCT- combine both prefix and suffix + char conds[SETSIZE]; // array which encodes the conditions to be met +}; + + +Here is a suffix borrowed from the en_US.aff file. This file +is whitespace delimited. + +SFX D Y 4 +SFX D 0 e d +SFX D y ied [^aeiou]y +SFX D 0 ed [^ey] +SFX D 0 ed [aeiou]y + +This information can be interpreted as follows: + +In the first line has 4 fields + +Field +----- +1 SFX - indicates this is a suffix +2 D - is the name of the character flag which represents this suffix +3 Y - indicates it can be combined with prefixes (cross product) +4 4 - indicates that sequence of 4 affentry structures are needed to + properly store the affix information + +The remaining lines describe the unique information for the 4 SfxEntry +objects that make up this affix. Each line can be interpreted +as follows: (note fields 1 and 2 are as a check against line 1 info) + +Field +----- +1 SFX - indicates this is a suffix +2 D - is the name of the character flag for this affix +3 y - the string of chars to strip off before adding affix + (a 0 here indicates the NULL string) +4 ied - the string of affix characters to add +5 [^aeiou]y - the conditions which must be met before the affix + can be applied + +Field 5 is interesting. Since this is a suffix, field 5 tells us that +there are 2 conditions that must be met. The first condition is that +the next to the last character in the word must *NOT* be any of the +following "a", "e", "i", "o" or "u". The second condition is that +the last character of the word must end in "y". + +So how can we encode this information concisely and be able to +test for both conditions in a fast manner? The answer is found +but studying the wonderful ispell code of Geoff Kuenning, et.al. +(now available under a normal BSD license). + +If we set up a conds array of 256 bytes indexed (0 to 255) and access it +using a character (cast to an unsigned char) of a string, we have 8 bits +of information we can store about that character. Specifically we +could use each bit to say if that character is allowed in any of the +last (or first for prefixes) 8 characters of the word. + +Basically, each character at one end of the word (up to the number +of conditions) is used to index into the conds array and the resulting +value found there says whether the that character is valid for a +specific character position in the word. + +For prefixes, it does this by setting bit 0 if that char is valid +in the first position, bit 1 if valid in the second position, and so on. + +If a bit is not set, then that char is not valid for that postion in the +word. + +If working with suffixes bit 0 is used for the character closest +to the front, bit 1 for the next character towards the end, ..., +with bit numconds-1 representing the last char at the end of the string. + +Note: since entries in the conds[] are 8 bits, only 8 conditions +(read that only 8 character positions) can be examined at one +end of a word (the beginning for prefixes and the end for suffixes. + +So to make this clearer, lets encode the conds array values for the +first two affentries for the suffix D described earlier. + + + For the first affentry: + numconds = 1 (only examine the last character) + + conds['e'] = (1 << 0) (the word must end in an E) + all others are all 0 + + For the second affentry: + numconds = 2 (only examine the last two characters) + + conds[X] = conds[X] | (1 << 0) (aeiou are not allowed) + where X is all characters *but* a, e, i, o, or u + + + conds['y'] = (1 << 1) (the last char must be a y) + all other bits for all other entries in the conds array are zero + + +#endif + diff --git a/src/myspell/affentry.hxx b/src/myspell/affentry.hxx new file mode 100644 index 0000000..eaf361f --- /dev/null +++ b/src/myspell/affentry.hxx @@ -0,0 +1,136 @@ +#ifndef _AFFIX_HXX_ +#define _AFFIX_HXX_ + +#include "hunvisapi.h" + +#include "atypes.hxx" +#include "baseaffix.hxx" +#include "affixmgr.hxx" + +/* A Prefix Entry */ + +class LIBHUNSPELL_DLL_EXPORTED PfxEntry : protected AffEntry +{ + AffixMgr* pmyMgr; + + PfxEntry * next; + PfxEntry * nexteq; + PfxEntry * nextne; + PfxEntry * flgnxt; + +public: + + PfxEntry(AffixMgr* pmgr, affentry* dp ); + ~PfxEntry(); + + inline bool allowCross() { return ((opts & aeXPRODUCT) != 0); } + struct hentry * checkword(const char * word, int len, char in_compound, + const FLAG needflag = FLAG_NULL); + + struct hentry * check_twosfx(const char * word, int len, char in_compound, const FLAG needflag = NULL); + + char * check_morph(const char * word, int len, char in_compound, + const FLAG needflag = FLAG_NULL); + + char * check_twosfx_morph(const char * word, int len, + char in_compound, const FLAG needflag = FLAG_NULL); + + inline FLAG getFlag() { return aflag; } + inline const char * getKey() { return appnd; } + char * add(const char * word, int len); + + inline short getKeyLen() { return appndl; } + + inline const char * getMorph() { return morphcode; } + + inline const unsigned short * getCont() { return contclass; } + inline short getContLen() { return contclasslen; } + + inline PfxEntry * getNext() { return next; } + inline PfxEntry * getNextNE() { return nextne; } + inline PfxEntry * getNextEQ() { return nexteq; } + inline PfxEntry * getFlgNxt() { return flgnxt; } + + inline void setNext(PfxEntry * ptr) { next = ptr; } + inline void setNextNE(PfxEntry * ptr) { nextne = ptr; } + inline void setNextEQ(PfxEntry * ptr) { nexteq = ptr; } + inline void setFlgNxt(PfxEntry * ptr) { flgnxt = ptr; } + + inline char * nextchar(char * p); + inline int test_condition(const char * st); +}; + + + + +/* A Suffix Entry */ + +class LIBHUNSPELL_DLL_EXPORTED SfxEntry : protected AffEntry +{ + AffixMgr* pmyMgr; + char * rappnd; + + SfxEntry * next; + SfxEntry * nexteq; + SfxEntry * nextne; + SfxEntry * flgnxt; + + SfxEntry * l_morph; + SfxEntry * r_morph; + SfxEntry * eq_morph; + +public: + + SfxEntry(AffixMgr* pmgr, affentry* dp ); + ~SfxEntry(); + + inline bool allowCross() { return ((opts & aeXPRODUCT) != 0); } + struct hentry * checkword(const char * word, int len, int optflags, + PfxEntry* ppfx, char ** wlst, int maxSug, int * ns, +// const FLAG cclass = FLAG_NULL, const FLAG needflag = FLAG_NULL, char in_compound=IN_CPD_NOT); + const FLAG cclass = FLAG_NULL, const FLAG needflag = FLAG_NULL, const FLAG badflag = 0); + + struct hentry * check_twosfx(const char * word, int len, int optflags, PfxEntry* ppfx, const FLAG needflag = NULL); + + char * check_twosfx_morph(const char * word, int len, int optflags, + PfxEntry* ppfx, const FLAG needflag = FLAG_NULL); + struct hentry * get_next_homonym(struct hentry * he); + struct hentry * get_next_homonym(struct hentry * word, int optflags, PfxEntry* ppfx, + const FLAG cclass, const FLAG needflag); + + + inline FLAG getFlag() { return aflag; } + inline const char * getKey() { return rappnd; } + char * add(const char * word, int len); + + + inline const char * getMorph() { return morphcode; } + + inline const unsigned short * getCont() { return contclass; } + inline short getContLen() { return contclasslen; } + inline const char * getAffix() { return appnd; } + + inline short getKeyLen() { return appndl; } + + inline SfxEntry * getNext() { return next; } + inline SfxEntry * getNextNE() { return nextne; } + inline SfxEntry * getNextEQ() { return nexteq; } + + inline SfxEntry * getLM() { return l_morph; } + inline SfxEntry * getRM() { return r_morph; } + inline SfxEntry * getEQM() { return eq_morph; } + inline SfxEntry * getFlgNxt() { return flgnxt; } + + inline void setNext(SfxEntry * ptr) { next = ptr; } + inline void setNextNE(SfxEntry * ptr) { nextne = ptr; } + inline void setNextEQ(SfxEntry * ptr) { nexteq = ptr; } + inline void setFlgNxt(SfxEntry * ptr) { flgnxt = ptr; } + + inline char * nextchar(char * p); + inline int test_condition(const char * st, const char * begin); + +}; + +#endif + + diff --git a/src/myspell/affixmgr.cxx b/src/myspell/affixmgr.cxx new file mode 100644 index 0000000..492904d --- /dev/null +++ b/src/myspell/affixmgr.cxx @@ -0,0 +1,4488 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include +#include + +#include + +#include "affixmgr.hxx" +#include "affentry.hxx" +#include "langnum.hxx" + +#include "csutil.hxx" + +AffixMgr::AffixMgr(const char * affpath, HashMgr** ptr, int * md, const char * key) +{ + // register hash manager and load affix data from aff file + pHMgr = ptr[0]; + alldic = ptr; + maxdic = md; + keystring = NULL; + trystring = NULL; + encoding=NULL; + csconv=NULL; + utf8 = 0; + complexprefixes = 0; + maptable = NULL; + nummap = 0; + breaktable = NULL; + numbreak = -1; + reptable = NULL; + numrep = 0; + iconvtable = NULL; + oconvtable = NULL; + checkcpdtable = NULL; + // allow simplified compound forms (see 3rd field of CHECKCOMPOUNDPATTERN) + simplifiedcpd = 0; + numcheckcpd = 0; + defcpdtable = NULL; + numdefcpd = 0; + phone = NULL; + compoundflag = FLAG_NULL; // permits word in compound forms + compoundbegin = FLAG_NULL; // may be first word in compound forms + compoundmiddle = FLAG_NULL; // may be middle word in compound forms + compoundend = FLAG_NULL; // may be last word in compound forms + compoundroot = FLAG_NULL; // compound word signing flag + compoundpermitflag = FLAG_NULL; // compound permitting flag for suffixed word + compoundforbidflag = FLAG_NULL; // compound fordidden flag for suffixed word + checkcompounddup = 0; // forbid double words in compounds + checkcompoundrep = 0; // forbid bad compounds (may be non compound word with a REP substitution) + checkcompoundcase = 0; // forbid upper and lowercase combinations at word bounds + checkcompoundtriple = 0; // forbid compounds with triple letters + simplifiedtriple = 0; // allow simplified triple letters in compounds (Schiff+fahrt -> Schiffahrt) + forbiddenword = FORBIDDENWORD; // forbidden word signing flag + nosuggest = FLAG_NULL; // don't suggest words signed with NOSUGGEST flag + nongramsuggest = FLAG_NULL; + lang = NULL; // language + langnum = 0; // language code (see http://l10n.openoffice.org/languages.html) + needaffix = FLAG_NULL; // forbidden root, allowed only with suffixes + cpdwordmax = -1; // default: unlimited wordcount in compound words + cpdmin = -1; // undefined + cpdmaxsyllable = 0; // default: unlimited syllablecount in compound words + cpdvowels=NULL; // vowels (for calculating of Hungarian compounding limit, O(n) search! XXX) + cpdvowels_utf16=NULL; // vowels for UTF-8 encoding (bsearch instead of O(n) search) + cpdvowels_utf16_len=0; // vowels + pfxappnd=NULL; // previous prefix for counting the syllables of prefix BUG + sfxappnd=NULL; // previous suffix for counting a special syllables BUG + cpdsyllablenum=NULL; // syllable count incrementing flag + checknum=0; // checking numbers, and word with numbers + wordchars=NULL; // letters + spec. word characters + wordchars_utf16=NULL; // letters + spec. word characters + wordchars_utf16_len=0; // letters + spec. word characters + ignorechars=NULL; // letters + spec. word characters + ignorechars_utf16=NULL; // letters + spec. word characters + ignorechars_utf16_len=0; // letters + spec. word characters + version=NULL; // affix and dictionary file version string + havecontclass=0; // flags of possible continuing classes (double affix) + // LEMMA_PRESENT: not put root into the morphological output. Lemma presents + // in morhological description in dictionary file. It's often combined with PSEUDOROOT. + lemma_present = FLAG_NULL; + circumfix = FLAG_NULL; + onlyincompound = FLAG_NULL; + maxngramsugs = -1; // undefined + maxdiff = -1; // undefined + onlymaxdiff = 0; + maxcpdsugs = -1; // undefined + nosplitsugs = 0; + sugswithdots = 0; + keepcase = 0; + forceucase = 0; + warn = 0; + forbidwarn = 0; + checksharps = 0; + substandard = FLAG_NULL; + fullstrip = 0; + + sfx = NULL; + pfx = NULL; + + for (int i=0; i < SETSIZE; i++) { + pStart[i] = NULL; + sStart[i] = NULL; + pFlag[i] = NULL; + sFlag[i] = NULL; + } + + for (int j=0; j < CONTSIZE; j++) { + contclasses[j] = 0; + } + + if (parse_file(affpath, key)) { + HUNSPELL_WARNING(stderr, "Failure loading aff file %s\n",affpath); + } + + if (cpdmin == -1) cpdmin = MINCPDLEN; + +} + + +AffixMgr::~AffixMgr() +{ + // pass through linked prefix entries and clean up + for (int i=0; i < SETSIZE ;i++) { + pFlag[i] = NULL; + PfxEntry * ptr = pStart[i]; + PfxEntry * nptr = NULL; + while (ptr) { + nptr = ptr->getNext(); + delete(ptr); + ptr = nptr; + nptr = NULL; + } + } + + // pass through linked suffix entries and clean up + for (int j=0; j < SETSIZE ; j++) { + sFlag[j] = NULL; + SfxEntry * ptr = sStart[j]; + SfxEntry * nptr = NULL; + while (ptr) { + nptr = ptr->getNext(); + delete(ptr); + ptr = nptr; + nptr = NULL; + } + sStart[j] = NULL; + } + + if (keystring) free(keystring); + keystring=NULL; + if (trystring) free(trystring); + trystring=NULL; + if (encoding) free(encoding); + encoding=NULL; + if (maptable) { + for (int j=0; j < nummap; j++) { + for (int k=0; k < maptable[j].len; k++) { + if (maptable[j].set[k]) free(maptable[j].set[k]); + } + free(maptable[j].set); + maptable[j].set = NULL; + maptable[j].len = 0; + } + free(maptable); + maptable = NULL; + } + nummap = 0; + if (breaktable) { + for (int j=0; j < numbreak; j++) { + if (breaktable[j]) free(breaktable[j]); + breaktable[j] = NULL; + } + free(breaktable); + breaktable = NULL; + } + numbreak = 0; + if (reptable) { + for (int j=0; j < numrep; j++) { + free(reptable[j].pattern); + free(reptable[j].pattern2); + } + free(reptable); + reptable = NULL; + } + if (iconvtable) delete iconvtable; + if (oconvtable) delete oconvtable; + if (phone && phone->rules) { + for (int j=0; j < phone->num + 1; j++) { + free(phone->rules[j * 2]); + free(phone->rules[j * 2 + 1]); + } + free(phone->rules); + free(phone); + phone = NULL; + } + + if (defcpdtable) { + for (int j=0; j < numdefcpd; j++) { + free(defcpdtable[j].def); + defcpdtable[j].def = NULL; + } + free(defcpdtable); + defcpdtable = NULL; + } + numrep = 0; + if (checkcpdtable) { + for (int j=0; j < numcheckcpd; j++) { + free(checkcpdtable[j].pattern); + free(checkcpdtable[j].pattern2); + free(checkcpdtable[j].pattern3); + checkcpdtable[j].pattern = NULL; + checkcpdtable[j].pattern2 = NULL; + checkcpdtable[j].pattern3 = NULL; + } + free(checkcpdtable); + checkcpdtable = NULL; + } + numcheckcpd = 0; + FREE_FLAG(compoundflag); + FREE_FLAG(compoundbegin); + FREE_FLAG(compoundmiddle); + FREE_FLAG(compoundend); + FREE_FLAG(compoundpermitflag); + FREE_FLAG(compoundforbidflag); + FREE_FLAG(compoundroot); + FREE_FLAG(forbiddenword); + FREE_FLAG(nosuggest); + FREE_FLAG(nongramsuggest); + FREE_FLAG(needaffix); + FREE_FLAG(lemma_present); + FREE_FLAG(circumfix); + FREE_FLAG(onlyincompound); + + cpdwordmax = 0; + pHMgr = NULL; + cpdmin = 0; + cpdmaxsyllable = 0; + if (cpdvowels) free(cpdvowels); + if (cpdvowels_utf16) free(cpdvowels_utf16); + if (cpdsyllablenum) free(cpdsyllablenum); + free_utf_tbl(); + if (lang) free(lang); + if (wordchars) free(wordchars); + if (wordchars_utf16) free(wordchars_utf16); + if (ignorechars) free(ignorechars); + if (ignorechars_utf16) free(ignorechars_utf16); + if (version) free(version); + checknum=0; +#ifdef MOZILLA_CLIENT + delete [] csconv; +#endif +} + + +// read in aff file and build up prefix and suffix entry objects +int AffixMgr::parse_file(const char * affpath, const char * key) +{ + char * line; // io buffers + char ft; // affix type + + // checking flag duplication + char dupflags[CONTSIZE]; + char dupflags_ini = 1; + + // first line indicator for removing byte order mark + int firstline = 1; + + // open the affix file + FileMgr * afflst = new FileMgr(affpath, key); + if (!afflst) { + HUNSPELL_WARNING(stderr, "error: could not open affix description file %s\n",affpath); + return 1; + } + + // step one is to parse the affix file building up the internal + // affix data structures + + // read in each line ignoring any that do not + // start with a known line type indicator + while ((line = afflst->getline())) { + mychomp(line); + + /* remove byte order mark */ + if (firstline) { + firstline = 0; + // Affix file begins with byte order mark: possible incompatibility with old Hunspell versions + if (strncmp(line,"\xEF\xBB\xBF",3) == 0) { + memmove(line, line+3, strlen(line+3)+1); + } + } + + /* parse in the keyboard string */ + if (strncmp(line,"KEY",3) == 0) { + if (parse_string(line, &keystring, afflst->getlinenum())) { + delete afflst; + return 1; + } + } + + /* parse in the try string */ + if (strncmp(line,"TRY",3) == 0) { + if (parse_string(line, &trystring, afflst->getlinenum())) { + delete afflst; + return 1; + } + } + + /* parse in the name of the character set used by the .dict and .aff */ + if (strncmp(line,"SET",3) == 0) { + if (parse_string(line, &encoding, afflst->getlinenum())) { + delete afflst; + return 1; + } + if (strcmp(encoding, "UTF-8") == 0) { + utf8 = 1; +#ifndef OPENOFFICEORG +#ifndef MOZILLA_CLIENT + if (initialize_utf_tbl()) return 1; +#endif +#endif + } + } + + /* parse COMPLEXPREFIXES for agglutinative languages with right-to-left writing system */ + if (strncmp(line,"COMPLEXPREFIXES",15) == 0) + complexprefixes = 1; + + /* parse in the flag used by the controlled compound words */ + if (strncmp(line,"COMPOUNDFLAG",12) == 0) { + if (parse_flag(line, &compoundflag, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by compound words */ + if (strncmp(line,"COMPOUNDBEGIN",13) == 0) { + if (complexprefixes) { + if (parse_flag(line, &compoundend, afflst)) { + delete afflst; + return 1; + } + } else { + if (parse_flag(line, &compoundbegin, afflst)) { + delete afflst; + return 1; + } + } + } + + /* parse in the flag used by compound words */ + if (strncmp(line,"COMPOUNDMIDDLE",14) == 0) { + if (parse_flag(line, &compoundmiddle, afflst)) { + delete afflst; + return 1; + } + } + /* parse in the flag used by compound words */ + if (strncmp(line,"COMPOUNDEND",11) == 0) { + if (complexprefixes) { + if (parse_flag(line, &compoundbegin, afflst)) { + delete afflst; + return 1; + } + } else { + if (parse_flag(line, &compoundend, afflst)) { + delete afflst; + return 1; + } + } + } + + /* parse in the data used by compound_check() method */ + if (strncmp(line,"COMPOUNDWORDMAX",15) == 0) { + if (parse_num(line, &cpdwordmax, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag sign compounds in dictionary */ + if (strncmp(line,"COMPOUNDROOT",12) == 0) { + if (parse_flag(line, &compoundroot, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by compound_check() method */ + if (strncmp(line,"COMPOUNDPERMITFLAG",18) == 0) { + if (parse_flag(line, &compoundpermitflag, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by compound_check() method */ + if (strncmp(line,"COMPOUNDFORBIDFLAG",18) == 0) { + if (parse_flag(line, &compoundforbidflag, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"CHECKCOMPOUNDDUP",16) == 0) { + checkcompounddup = 1; + } + + if (strncmp(line,"CHECKCOMPOUNDREP",16) == 0) { + checkcompoundrep = 1; + } + + if (strncmp(line,"CHECKCOMPOUNDTRIPLE",19) == 0) { + checkcompoundtriple = 1; + } + + if (strncmp(line,"SIMPLIFIEDTRIPLE",16) == 0) { + simplifiedtriple = 1; + } + + if (strncmp(line,"CHECKCOMPOUNDCASE",17) == 0) { + checkcompoundcase = 1; + } + + if (strncmp(line,"NOSUGGEST",9) == 0) { + if (parse_flag(line, &nosuggest, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"NONGRAMSUGGEST",14) == 0) { + if (parse_flag(line, &nongramsuggest, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by forbidden words */ + if (strncmp(line,"FORBIDDENWORD",13) == 0) { + if (parse_flag(line, &forbiddenword, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by forbidden words */ + if (strncmp(line,"LEMMA_PRESENT",13) == 0) { + if (parse_flag(line, &lemma_present, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by circumfixes */ + if (strncmp(line,"CIRCUMFIX",9) == 0) { + if (parse_flag(line, &circumfix, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by fogemorphemes */ + if (strncmp(line,"ONLYINCOMPOUND",14) == 0) { + if (parse_flag(line, &onlyincompound, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by `needaffixs' */ + if (strncmp(line,"PSEUDOROOT",10) == 0) { + if (parse_flag(line, &needaffix, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by `needaffixs' */ + if (strncmp(line,"NEEDAFFIX",9) == 0) { + if (parse_flag(line, &needaffix, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the minimal length for words in compounds */ + if (strncmp(line,"COMPOUNDMIN",11) == 0) { + if (parse_num(line, &cpdmin, afflst)) { + delete afflst; + return 1; + } + if (cpdmin < 1) cpdmin = 1; + } + + /* parse in the max. words and syllables in compounds */ + if (strncmp(line,"COMPOUNDSYLLABLE",16) == 0) { + if (parse_cpdsyllable(line, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by compound_check() method */ + if (strncmp(line,"SYLLABLENUM",11) == 0) { + if (parse_string(line, &cpdsyllablenum, afflst->getlinenum())) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by the controlled compound words */ + if (strncmp(line,"CHECKNUM",8) == 0) { + checknum=1; + } + + /* parse in the extra word characters */ + if (strncmp(line,"WORDCHARS",9) == 0) { + if (parse_array(line, &wordchars, &wordchars_utf16, &wordchars_utf16_len, utf8, afflst->getlinenum())) { + delete afflst; + return 1; + } + } + + /* parse in the ignored characters (for example, Arabic optional diacretics charachters */ + if (strncmp(line,"IGNORE",6) == 0) { + if (parse_array(line, &ignorechars, &ignorechars_utf16, &ignorechars_utf16_len, utf8, afflst->getlinenum())) { + delete afflst; + return 1; + } + } + + /* parse in the typical fault correcting table */ + if (strncmp(line,"REP",3) == 0) { + if (parse_reptable(line, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the input conversion table */ + if (strncmp(line,"ICONV",5) == 0) { + if (parse_convtable(line, afflst, &iconvtable, "ICONV")) { + delete afflst; + return 1; + } + } + + /* parse in the input conversion table */ + if (strncmp(line,"OCONV",5) == 0) { + if (parse_convtable(line, afflst, &oconvtable, "OCONV")) { + delete afflst; + return 1; + } + } + + /* parse in the phonetic translation table */ + if (strncmp(line,"PHONE",5) == 0) { + if (parse_phonetable(line, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the checkcompoundpattern table */ + if (strncmp(line,"CHECKCOMPOUNDPATTERN",20) == 0) { + if (parse_checkcpdtable(line, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the defcompound table */ + if (strncmp(line,"COMPOUNDRULE",12) == 0) { + if (parse_defcpdtable(line, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the related character map table */ + if (strncmp(line,"MAP",3) == 0) { + if (parse_maptable(line, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the word breakpoints table */ + if (strncmp(line,"BREAK",5) == 0) { + if (parse_breaktable(line, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the language for language specific codes */ + if (strncmp(line,"LANG",4) == 0) { + if (parse_string(line, &lang, afflst->getlinenum())) { + delete afflst; + return 1; + } + langnum = get_lang_num(lang); + } + + if (strncmp(line,"VERSION",7) == 0) { + for(line = line + 7; *line == ' ' || *line == '\t'; line++); + version = mystrdup(line); + } + + if (strncmp(line,"MAXNGRAMSUGS",12) == 0) { + if (parse_num(line, &maxngramsugs, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"ONLYMAXDIFF", 11) == 0) + onlymaxdiff = 1; + + if (strncmp(line,"MAXDIFF",7) == 0) { + if (parse_num(line, &maxdiff, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"MAXCPDSUGS",10) == 0) { + if (parse_num(line, &maxcpdsugs, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"NOSPLITSUGS",11) == 0) { + nosplitsugs=1; + } + + if (strncmp(line,"FULLSTRIP",9) == 0) { + fullstrip=1; + } + + if (strncmp(line,"SUGSWITHDOTS",12) == 0) { + sugswithdots=1; + } + + /* parse in the flag used by forbidden words */ + if (strncmp(line,"KEEPCASE",8) == 0) { + if (parse_flag(line, &keepcase, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by `forceucase' */ + if (strncmp(line,"FORCEUCASE",10) == 0) { + if (parse_flag(line, &forceucase, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by `warn' */ + if (strncmp(line,"WARN",4) == 0) { + if (parse_flag(line, &warn, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"FORBIDWARN",10) == 0) { + forbidwarn=1; + } + + /* parse in the flag used by the affix generator */ + if (strncmp(line,"SUBSTANDARD",11) == 0) { + if (parse_flag(line, &substandard, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"CHECKSHARPS",11) == 0) { + checksharps=1; + } + + /* parse this affix: P - prefix, S - suffix */ + ft = ' '; + if (strncmp(line,"PFX",3) == 0) ft = complexprefixes ? 'S' : 'P'; + if (strncmp(line,"SFX",3) == 0) ft = complexprefixes ? 'P' : 'S'; + if (ft != ' ') { + if (dupflags_ini) { + memset(dupflags, 0, sizeof(dupflags)); + dupflags_ini = 0; + } + if (parse_affix(line, ft, afflst, dupflags)) { + delete afflst; + process_pfx_tree_to_list(); + process_sfx_tree_to_list(); + return 1; + } + } + + } + delete afflst; + + // convert affix trees to sorted list + process_pfx_tree_to_list(); + process_sfx_tree_to_list(); + + // now we can speed up performance greatly taking advantage of the + // relationship between the affixes and the idea of "subsets". + + // View each prefix as a potential leading subset of another and view + // each suffix (reversed) as a potential trailing subset of another. + + // To illustrate this relationship if we know the prefix "ab" is found in the + // word to examine, only prefixes that "ab" is a leading subset of need be examined. + // Furthermore is "ab" is not present then none of the prefixes that "ab" is + // is a subset need be examined. + // The same argument goes for suffix string that are reversed. + + // Then to top this off why not examine the first char of the word to quickly + // limit the set of prefixes to examine (i.e. the prefixes to examine must + // be leading supersets of the first character of the word (if they exist) + + // To take advantage of this "subset" relationship, we need to add two links + // from entry. One to take next if the current prefix is found (call it nexteq) + // and one to take next if the current prefix is not found (call it nextne). + + // Since we have built ordered lists, all that remains is to properly initialize + // the nextne and nexteq pointers that relate them + + process_pfx_order(); + process_sfx_order(); + + /* get encoding for CHECKCOMPOUNDCASE */ + if (!utf8) { + char * enc = get_encoding(); + csconv = get_current_cs(enc); + free(enc); + enc = NULL; + + char expw[MAXLNLEN]; + if (wordchars) { + strcpy(expw, wordchars); + free(wordchars); + } else *expw = '\0'; + + for (int i = 0; i <= 255; i++) { + if ( (csconv[i].cupper != csconv[i].clower) && + (! strchr(expw, (char) i))) { + *(expw + strlen(expw) + 1) = '\0'; + *(expw + strlen(expw)) = (char) i; + } + } + + wordchars = mystrdup(expw); + } + + // default BREAK definition + if (numbreak == -1) { + breaktable = (char **) malloc(sizeof(char *) * 3); + if (!breaktable) return 1; + breaktable[0] = mystrdup("-"); + breaktable[1] = mystrdup("^-"); + breaktable[2] = mystrdup("-$"); + if (breaktable[0] && breaktable[1] && breaktable[2]) numbreak = 3; + } + return 0; +} + + +// we want to be able to quickly access prefix information +// both by prefix flag, and sorted by prefix string itself +// so we need to set up two indexes + +int AffixMgr::build_pfxtree(PfxEntry* pfxptr) +{ + PfxEntry * ptr; + PfxEntry * pptr; + PfxEntry * ep = pfxptr; + + // get the right starting points + const char * key = ep->getKey(); + const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF); + + // first index by flag which must exist + ptr = pFlag[flg]; + ep->setFlgNxt(ptr); + pFlag[flg] = ep; + + + // handle the special case of null affix string + if (strlen(key) == 0) { + // always inset them at head of list at element 0 + ptr = pStart[0]; + ep->setNext(ptr); + pStart[0] = ep; + return 0; + } + + // now handle the normal case + ep->setNextEQ(NULL); + ep->setNextNE(NULL); + + unsigned char sp = *((const unsigned char *)key); + ptr = pStart[sp]; + + // handle the first insert + if (!ptr) { + pStart[sp] = ep; + return 0; + } + + + // otherwise use binary tree insertion so that a sorted + // list can easily be generated later + pptr = NULL; + for (;;) { + pptr = ptr; + if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) { + ptr = ptr->getNextEQ(); + if (!ptr) { + pptr->setNextEQ(ep); + break; + } + } else { + ptr = ptr->getNextNE(); + if (!ptr) { + pptr->setNextNE(ep); + break; + } + } + } + return 0; +} + +// we want to be able to quickly access suffix information +// both by suffix flag, and sorted by the reverse of the +// suffix string itself; so we need to set up two indexes +int AffixMgr::build_sfxtree(SfxEntry* sfxptr) +{ + SfxEntry * ptr; + SfxEntry * pptr; + SfxEntry * ep = sfxptr; + + /* get the right starting point */ + const char * key = ep->getKey(); + const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF); + + // first index by flag which must exist + ptr = sFlag[flg]; + ep->setFlgNxt(ptr); + sFlag[flg] = ep; + + // next index by affix string + + // handle the special case of null affix string + if (strlen(key) == 0) { + // always inset them at head of list at element 0 + ptr = sStart[0]; + ep->setNext(ptr); + sStart[0] = ep; + return 0; + } + + // now handle the normal case + ep->setNextEQ(NULL); + ep->setNextNE(NULL); + + unsigned char sp = *((const unsigned char *)key); + ptr = sStart[sp]; + + // handle the first insert + if (!ptr) { + sStart[sp] = ep; + return 0; + } + + // otherwise use binary tree insertion so that a sorted + // list can easily be generated later + pptr = NULL; + for (;;) { + pptr = ptr; + if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) { + ptr = ptr->getNextEQ(); + if (!ptr) { + pptr->setNextEQ(ep); + break; + } + } else { + ptr = ptr->getNextNE(); + if (!ptr) { + pptr->setNextNE(ep); + break; + } + } + } + return 0; +} + +// convert from binary tree to sorted list +int AffixMgr::process_pfx_tree_to_list() +{ + for (int i=1; i< SETSIZE; i++) { + pStart[i] = process_pfx_in_order(pStart[i],NULL); + } + return 0; +} + + +PfxEntry* AffixMgr::process_pfx_in_order(PfxEntry* ptr, PfxEntry* nptr) +{ + if (ptr) { + nptr = process_pfx_in_order(ptr->getNextNE(), nptr); + ptr->setNext(nptr); + nptr = process_pfx_in_order(ptr->getNextEQ(), ptr); + } + return nptr; +} + + +// convert from binary tree to sorted list +int AffixMgr:: process_sfx_tree_to_list() +{ + for (int i=1; i< SETSIZE; i++) { + sStart[i] = process_sfx_in_order(sStart[i],NULL); + } + return 0; +} + +SfxEntry* AffixMgr::process_sfx_in_order(SfxEntry* ptr, SfxEntry* nptr) +{ + if (ptr) { + nptr = process_sfx_in_order(ptr->getNextNE(), nptr); + ptr->setNext(nptr); + nptr = process_sfx_in_order(ptr->getNextEQ(), ptr); + } + return nptr; +} + + +// reinitialize the PfxEntry links NextEQ and NextNE to speed searching +// using the idea of leading subsets this time +int AffixMgr::process_pfx_order() +{ + PfxEntry* ptr; + + // loop through each prefix list starting point + for (int i=1; i < SETSIZE; i++) { + + ptr = pStart[i]; + + // look through the remainder of the list + // and find next entry with affix that + // the current one is not a subset of + // mark that as destination for NextNE + // use next in list that you are a subset + // of as NextEQ + + for (; ptr != NULL; ptr = ptr->getNext()) { + + PfxEntry * nptr = ptr->getNext(); + for (; nptr != NULL; nptr = nptr->getNext()) { + if (! isSubset( ptr->getKey() , nptr->getKey() )) break; + } + ptr->setNextNE(nptr); + ptr->setNextEQ(NULL); + if ((ptr->getNext()) && isSubset(ptr->getKey() , (ptr->getNext())->getKey())) + ptr->setNextEQ(ptr->getNext()); + } + + // now clean up by adding smart search termination strings: + // if you are already a superset of the previous prefix + // but not a subset of the next, search can end here + // so set NextNE properly + + ptr = pStart[i]; + for (; ptr != NULL; ptr = ptr->getNext()) { + PfxEntry * nptr = ptr->getNext(); + PfxEntry * mptr = NULL; + for (; nptr != NULL; nptr = nptr->getNext()) { + if (! isSubset(ptr->getKey(),nptr->getKey())) break; + mptr = nptr; + } + if (mptr) mptr->setNextNE(NULL); + } + } + return 0; +} + +// initialize the SfxEntry links NextEQ and NextNE to speed searching +// using the idea of leading subsets this time +int AffixMgr::process_sfx_order() +{ + SfxEntry* ptr; + + // loop through each prefix list starting point + for (int i=1; i < SETSIZE; i++) { + + ptr = sStart[i]; + + // look through the remainder of the list + // and find next entry with affix that + // the current one is not a subset of + // mark that as destination for NextNE + // use next in list that you are a subset + // of as NextEQ + + for (; ptr != NULL; ptr = ptr->getNext()) { + SfxEntry * nptr = ptr->getNext(); + for (; nptr != NULL; nptr = nptr->getNext()) { + if (! isSubset(ptr->getKey(),nptr->getKey())) break; + } + ptr->setNextNE(nptr); + ptr->setNextEQ(NULL); + if ((ptr->getNext()) && isSubset(ptr->getKey(),(ptr->getNext())->getKey())) + ptr->setNextEQ(ptr->getNext()); + } + + + // now clean up by adding smart search termination strings: + // if you are already a superset of the previous suffix + // but not a subset of the next, search can end here + // so set NextNE properly + + ptr = sStart[i]; + for (; ptr != NULL; ptr = ptr->getNext()) { + SfxEntry * nptr = ptr->getNext(); + SfxEntry * mptr = NULL; + for (; nptr != NULL; nptr = nptr->getNext()) { + if (! isSubset(ptr->getKey(),nptr->getKey())) break; + mptr = nptr; + } + if (mptr) mptr->setNextNE(NULL); + } + } + return 0; +} + +// add flags to the result for dictionary debugging +void AffixMgr::debugflag(char * result, unsigned short flag) { + char * st = encode_flag(flag); + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, MORPH_FLAG, MAXLNLEN); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } +} + +// calculate the character length of the condition +int AffixMgr::condlen(char * st) +{ + int l = 0; + bool group = false; + for(; *st; st++) { + if (*st == '[') { + group = true; + l++; + } else if (*st == ']') group = false; + else if (!group && (!utf8 || + (!(*st & 0x80) || ((*st & 0xc0) == 0x80)))) l++; + } + return l; +} + +int AffixMgr::encodeit(affentry &entry, char * cs) +{ + if (strcmp(cs,".") != 0) { + entry.numconds = (char) condlen(cs); + strncpy(entry.c.conds, cs, MAXCONDLEN); + // long condition (end of conds padded by strncpy) + if (entry.c.conds[MAXCONDLEN - 1] && cs[MAXCONDLEN]) { + entry.opts += aeLONGCOND; + entry.c.l.conds2 = mystrdup(cs + MAXCONDLEN_1); + if (!entry.c.l.conds2) return 1; + } + } else { + entry.numconds = 0; + entry.c.conds[0] = '\0'; + } + return 0; +} + +// return 1 if s1 is a leading subset of s2 (dots are for infixes) +inline int AffixMgr::isSubset(const char * s1, const char * s2) + { + while (((*s1 == *s2) || (*s1 == '.')) && (*s1 != '\0')) { + s1++; + s2++; + } + return (*s1 == '\0'); + } + + +// check word for prefixes +struct hentry * AffixMgr::prefix_check(const char * word, int len, char in_compound, + const FLAG needflag) +{ + struct hentry * rv= NULL; + + pfx = NULL; + pfxappnd = NULL; + sfxappnd = NULL; + + // first handle the special case of 0 length prefixes + PfxEntry * pe = pStart[0]; + while (pe) { + if ( + // fogemorpheme + ((in_compound != IN_CPD_NOT) || !(pe->getCont() && + (TESTAFF(pe->getCont(), onlyincompound, pe->getContLen())))) && + // permit prefixes in compounds + ((in_compound != IN_CPD_END) || (pe->getCont() && + (TESTAFF(pe->getCont(), compoundpermitflag, pe->getContLen())))) + ) { + // check prefix + rv = pe->checkword(word, len, in_compound, needflag); + if (rv) { + pfx=pe; // BUG: pfx not stateless + return rv; + } + } + pe = pe->getNext(); + } + + // now handle the general case + unsigned char sp = *((const unsigned char *)word); + PfxEntry * pptr = pStart[sp]; + + while (pptr) { + if (isSubset(pptr->getKey(),word)) { + if ( + // fogemorpheme + ((in_compound != IN_CPD_NOT) || !(pptr->getCont() && + (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen())))) && + // permit prefixes in compounds + ((in_compound != IN_CPD_END) || (pptr->getCont() && + (TESTAFF(pptr->getCont(), compoundpermitflag, pptr->getContLen())))) + ) { + // check prefix + rv = pptr->checkword(word, len, in_compound, needflag); + if (rv) { + pfx=pptr; // BUG: pfx not stateless + return rv; + } + } + pptr = pptr->getNextEQ(); + } else { + pptr = pptr->getNextNE(); + } + } + + return NULL; +} + +// check word for prefixes +struct hentry * AffixMgr::prefix_check_twosfx(const char * word, int len, + char in_compound, const FLAG needflag) +{ + struct hentry * rv= NULL; + + pfx = NULL; + sfxappnd = NULL; + + // first handle the special case of 0 length prefixes + PfxEntry * pe = pStart[0]; + + while (pe) { + rv = pe->check_twosfx(word, len, in_compound, needflag); + if (rv) return rv; + pe = pe->getNext(); + } + + // now handle the general case + unsigned char sp = *((const unsigned char *)word); + PfxEntry * pptr = pStart[sp]; + + while (pptr) { + if (isSubset(pptr->getKey(),word)) { + rv = pptr->check_twosfx(word, len, in_compound, needflag); + if (rv) { + pfx = pptr; + return rv; + } + pptr = pptr->getNextEQ(); + } else { + pptr = pptr->getNextNE(); + } + } + + return NULL; +} + +// check word for prefixes +char * AffixMgr::prefix_check_morph(const char * word, int len, char in_compound, + const FLAG needflag) +{ + char * st; + + char result[MAXLNLEN]; + result[0] = '\0'; + + pfx = NULL; + sfxappnd = NULL; + + // first handle the special case of 0 length prefixes + PfxEntry * pe = pStart[0]; + while (pe) { + st = pe->check_morph(word,len,in_compound, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + // if (rv) return rv; + pe = pe->getNext(); + } + + // now handle the general case + unsigned char sp = *((const unsigned char *)word); + PfxEntry * pptr = pStart[sp]; + + while (pptr) { + if (isSubset(pptr->getKey(),word)) { + st = pptr->check_morph(word,len,in_compound, needflag); + if (st) { + // fogemorpheme + if ((in_compound != IN_CPD_NOT) || !((pptr->getCont() && + (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen()))))) { + mystrcat(result, st, MAXLNLEN); + pfx = pptr; + } + free(st); + } + pptr = pptr->getNextEQ(); + } else { + pptr = pptr->getNextNE(); + } + } + + if (*result) return mystrdup(result); + return NULL; +} + + +// check word for prefixes +char * AffixMgr::prefix_check_twosfx_morph(const char * word, int len, + char in_compound, const FLAG needflag) +{ + char * st; + + char result[MAXLNLEN]; + result[0] = '\0'; + + pfx = NULL; + sfxappnd = NULL; + + // first handle the special case of 0 length prefixes + PfxEntry * pe = pStart[0]; + while (pe) { + st = pe->check_twosfx_morph(word,len,in_compound, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + pe = pe->getNext(); + } + + // now handle the general case + unsigned char sp = *((const unsigned char *)word); + PfxEntry * pptr = pStart[sp]; + + while (pptr) { + if (isSubset(pptr->getKey(),word)) { + st = pptr->check_twosfx_morph(word, len, in_compound, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + pfx = pptr; + } + pptr = pptr->getNextEQ(); + } else { + pptr = pptr->getNextNE(); + } + } + + if (*result) return mystrdup(result); + return NULL; +} + +// Is word a non compound with a REP substitution (see checkcompoundrep)? +int AffixMgr::cpdrep_check(const char * word, int wl) +{ + char candidate[MAXLNLEN]; + const char * r; + int lenr, lenp; + + if ((wl < 2) || !numrep) return 0; + + for (int i=0; i < numrep; i++ ) { + r = word; + lenr = strlen(reptable[i].pattern2); + lenp = strlen(reptable[i].pattern); + // search every occurence of the pattern in the word + while ((r=strstr(r, reptable[i].pattern)) != NULL) { + strcpy(candidate, word); + if (r-word + lenr + strlen(r+lenp) >= MAXLNLEN) break; + strcpy(candidate+(r-word),reptable[i].pattern2); + strcpy(candidate+(r-word)+lenr, r+lenp); + if (candidate_check(candidate,strlen(candidate))) return 1; + r++; // search for the next letter + } + } + return 0; +} + +// forbid compoundings when there are special patterns at word bound +int AffixMgr::cpdpat_check(const char * word, int pos, hentry * r1, hentry * r2, const char affixed) +{ + int len; + for (int i = 0; i < numcheckcpd; i++) { + if (isSubset(checkcpdtable[i].pattern2, word + pos) && + (!r1 || !checkcpdtable[i].cond || + (r1->astr && TESTAFF(r1->astr, checkcpdtable[i].cond, r1->alen))) && + (!r2 || !checkcpdtable[i].cond2 || + (r2->astr && TESTAFF(r2->astr, checkcpdtable[i].cond2, r2->alen))) && + // zero length pattern => only TESTAFF + // zero pattern (0/flag) => unmodified stem (zero affixes allowed) + (!*(checkcpdtable[i].pattern) || ( + (*(checkcpdtable[i].pattern)=='0' && r1->blen <= pos && strncmp(word + pos - r1->blen, r1->word, r1->blen) == 0) || + (*(checkcpdtable[i].pattern)!='0' && (len = strlen(checkcpdtable[i].pattern)) && + strncmp(word + pos - len, checkcpdtable[i].pattern, len) == 0)))) { + return 1; + } + } + return 0; +} + +// forbid compounding with neighbouring upper and lower case characters at word bounds +int AffixMgr::cpdcase_check(const char * word, int pos) +{ + if (utf8) { + w_char u, w; + const char * p; + u8_u16(&u, 1, word + pos); + for (p = word + pos - 1; (*p & 0xc0) == 0x80; p--); + u8_u16(&w, 1, p); + unsigned short a = (u.h << 8) + u.l; + unsigned short b = (w.h << 8) + w.l; + if (((unicodetoupper(a, langnum) == a) || (unicodetoupper(b, langnum) == b)) && + (a != '-') && (b != '-')) return 1; + } else { + unsigned char a = *(word + pos - 1); + unsigned char b = *(word + pos); + if ((csconv[a].ccase || csconv[b].ccase) && (a != '-') && (b != '-')) return 1; + } + return 0; +} + +// check compound patterns +int AffixMgr::defcpd_check(hentry *** words, short wnum, hentry * rv, hentry ** def, char all) +{ + signed short btpp[MAXWORDLEN]; // metacharacter (*, ?) positions for backtracking + signed short btwp[MAXWORDLEN]; // word positions for metacharacters + int btnum[MAXWORDLEN]; // number of matched characters in metacharacter positions + short bt = 0; + int i, j; + int ok; + int w = 0; + + if (!*words) { + w = 1; + *words = def; + } + + if (!*words) { + return 0; + } + + (*words)[wnum] = rv; + + // has the last word COMPOUNDRULE flag? + if (rv->alen == 0) { + (*words)[wnum] = NULL; + if (w) *words = NULL; + return 0; + } + ok = 0; + for (i = 0; i < numdefcpd; i++) { + for (j = 0; j < defcpdtable[i].len; j++) { + if (defcpdtable[i].def[j] != '*' && defcpdtable[i].def[j] != '?' && + TESTAFF(rv->astr, defcpdtable[i].def[j], rv->alen)) ok = 1; + } + } + if (ok == 0) { + (*words)[wnum] = NULL; + if (w) *words = NULL; + return 0; + } + + for (i = 0; i < numdefcpd; i++) { + signed short pp = 0; // pattern position + signed short wp = 0; // "words" position + int ok2; + ok = 1; + ok2 = 1; + do { + while ((pp < defcpdtable[i].len) && (wp <= wnum)) { + if (((pp+1) < defcpdtable[i].len) && + ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) { + int wend = (defcpdtable[i].def[pp+1] == '?') ? wp : wnum; + ok2 = 1; + pp+=2; + btpp[bt] = pp; + btwp[bt] = wp; + while (wp <= wend) { + if (!(*words)[wp]->alen || + !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp-2], (*words)[wp]->alen)) { + ok2 = 0; + break; + } + wp++; + } + if (wp <= wnum) ok2 = 0; + btnum[bt] = wp - btwp[bt]; + if (btnum[bt] > 0) bt++; + if (ok2) break; + } else { + ok2 = 1; + if (!(*words)[wp] || !(*words)[wp]->alen || + !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp], (*words)[wp]->alen)) { + ok = 0; + break; + } + pp++; + wp++; + if ((defcpdtable[i].len == pp) && !(wp > wnum)) ok = 0; + } + } + if (ok && ok2) { + int r = pp; + while ((defcpdtable[i].len > r) && ((r+1) < defcpdtable[i].len) && + ((defcpdtable[i].def[r+1] == '*') || (defcpdtable[i].def[r+1] == '?'))) r+=2; + if (defcpdtable[i].len <= r) return 1; + } + // backtrack + if (bt) do { + ok = 1; + btnum[bt - 1]--; + pp = btpp[bt - 1]; + wp = btwp[bt - 1] + (signed short) btnum[bt - 1]; + } while ((btnum[bt - 1] < 0) && --bt); + } while (bt); + + if (ok && ok2 && (!all || (defcpdtable[i].len <= pp))) return 1; + + // check zero ending + while (ok && ok2 && (defcpdtable[i].len > pp) && ((pp+1) < defcpdtable[i].len) && + ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) pp+=2; + if (ok && ok2 && (defcpdtable[i].len <= pp)) return 1; + } + (*words)[wnum] = NULL; + if (w) *words = NULL; + return 0; +} + +inline int AffixMgr::candidate_check(const char * word, int len) +{ + struct hentry * rv=NULL; + + rv = lookup(word); + if (rv) return 1; + +// rv = prefix_check(word,len,1); +// if (rv) return 1; + + rv = affix_check(word,len); + if (rv) return 1; + return 0; +} + +// calculate number of syllable for compound-checking +short AffixMgr::get_syllable(const char * word, int wlen) +{ + if (cpdmaxsyllable==0) return 0; + + short num=0; + + if (!utf8) { + for (int i=0; i 0; i--) { + if (flag_bsearch((unsigned short *) cpdvowels_utf16, + ((unsigned short *) w)[i - 1], cpdvowels_utf16_len)) num++; + } + } + return num; +} + +void AffixMgr::setcminmax(int * cmin, int * cmax, const char * word, int len) { + if (utf8) { + int i; + for (*cmin = 0, i = 0; (i < cpdmin) && word[*cmin]; i++) { + for ((*cmin)++; (word[*cmin] & 0xc0) == 0x80; (*cmin)++); + } + for (*cmax = len, i = 0; (i < (cpdmin - 1)) && *cmax; i++) { + for ((*cmax)--; (word[*cmax] & 0xc0) == 0x80; (*cmax)--); + } + } else { + *cmin = cpdmin; + *cmax = len - cpdmin + 1; + } +} + + +// check if compound word is correctly spelled +// hu_mov_rule = spec. Hungarian rule (XXX) +struct hentry * AffixMgr::compound_check(const char * word, int len, + short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words = NULL, + char hu_mov_rule = 0, char is_sug = 0, int info = 0) +{ + int i; + short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2; + struct hentry * rv = NULL; + struct hentry * rv_first; + struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking + char st [MAXWORDUTF8LEN + 4]; + char ch = '\0'; + int cmin; + int cmax; + int striple = 0; + int scpd = 0; + int soldi = 0; + int oldcmin = 0; + int oldcmax = 0; + int oldlen = 0; + int checkedstriple = 0; + int onlycpdrule; + int affixed = 0; + + int checked_prefix; + + setcminmax(&cmin, &cmax, word, len); + + strcpy(st, word); + + for (i = cmin; i < cmax; i++) { + // go to end of the UTF-8 character + if (utf8) { + for (; (st[i] & 0xc0) == 0x80; i++); + if (i >= cmax) return NULL; + } + + onlycpdrule = 0; + + do { // onlycpdrule loop + + oldnumsyllable = numsyllable; + oldwordnum = wordnum; + checked_prefix = 0; + + do { // simplified checkcompoundpattern loop + + if (scpd > 0) { + for (; scpd <= numcheckcpd && (!checkcpdtable[scpd-1].pattern3 || + strncmp(word + i, checkcpdtable[scpd-1].pattern3, strlen(checkcpdtable[scpd-1].pattern3)) != 0); scpd++); + + if (scpd > numcheckcpd) break; // break simplified checkcompoundpattern loop + strcpy(st + i, checkcpdtable[scpd-1].pattern); + soldi = i; + i += strlen(checkcpdtable[scpd-1].pattern); + strcpy(st + i, checkcpdtable[scpd-1].pattern2); + strcpy(st + i + strlen(checkcpdtable[scpd-1].pattern2), word + soldi + strlen(checkcpdtable[scpd-1].pattern3)); + + oldlen = len; + len += strlen(checkcpdtable[scpd-1].pattern) + strlen(checkcpdtable[scpd-1].pattern2) - strlen(checkcpdtable[scpd-1].pattern3); + oldcmin = cmin; + oldcmax = cmax; + setcminmax(&cmin, &cmax, st, len); + + cmax = len - cpdmin + 1; + } + + ch = st[i]; + st[i] = '\0'; + + sfx = NULL; + pfx = NULL; + + // FIRST WORD + + affixed = 1; + rv = lookup(st); // perhaps without prefix + + // search homonym with compound flag + while ((rv) && !hu_mov_rule && + ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) || + !((compoundflag && !words && !onlycpdrule && TESTAFF(rv->astr, compoundflag, rv->alen)) || + (compoundbegin && !wordnum && !onlycpdrule && + TESTAFF(rv->astr, compoundbegin, rv->alen)) || + (compoundmiddle && wordnum && !words && !onlycpdrule && + TESTAFF(rv->astr, compoundmiddle, rv->alen)) || + (numdefcpd && + ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) || + (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))) && (onlycpdrule = 1))) || // switch onlycpdrule on + (scpd != 0 && checkcpdtable[scpd-1].cond != FLAG_NULL && + !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen))) + ) { + rv = rv->next_homonym; + } + + if (rv) affixed = 0; + + if (!rv) { + if (onlycpdrule) break; + if (compoundflag && !words && + !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) { + if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, + FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule && + sfx->getCont() && + ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag, + sfx->getContLen())) || (compoundend && + TESTAFF(sfx->getCont(), compoundend, + sfx->getContLen())))) { + rv = NULL; + } + } + + if (rv || + (((wordnum == 0) && compoundbegin && + ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || + (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) || + ((wordnum > 0) && compoundmiddle && + ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || + (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle))))) + ) checked_prefix = 1; + // else check forbiddenwords and needaffix + } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) || + TESTAFF(rv->astr, needaffix, rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || + (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)) + )) { + st[i] = ch; + //continue; + break; + } + + // check non_compound flag in suffix and prefix + if ((rv) && !hu_mov_rule && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundforbidflag, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundforbidflag, + sfx->getContLen())))) { + rv = NULL; + } + + // check compoundend flag in suffix and prefix + if ((rv) && !checked_prefix && compoundend && !hu_mov_rule && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundend, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundend, + sfx->getContLen())))) { + rv = NULL; + } + + // check compoundmiddle flag in suffix and prefix + if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundmiddle, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundmiddle, + sfx->getContLen())))) { + rv = NULL; + } + + // check forbiddenwords + if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || + (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) { + return NULL; + } + + // increment word number, if the second root has a compoundroot flag + if ((rv) && compoundroot && + (TESTAFF(rv->astr, compoundroot, rv->alen))) { + wordnum++; + } + + // first word is acceptable in compound words? + if (((rv) && + ( checked_prefix || (words && words[wnum]) || + (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) || + ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) || + ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen))// || +// (numdefcpd && ) + +// LANG_hu section: spec. Hungarian rule + || ((langnum == LANG_hu) && hu_mov_rule && ( + TESTAFF(rv->astr, 'F', rv->alen) || // XXX hardwired Hungarian dictionary codes + TESTAFF(rv->astr, 'G', rv->alen) || + TESTAFF(rv->astr, 'H', rv->alen) + ) + ) +// END of LANG_hu section + ) && + ( + // test CHECKCOMPOUNDPATTERN conditions + scpd == 0 || checkcpdtable[scpd-1].cond == FLAG_NULL || + TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen) + ) + && ! (( checkcompoundtriple && scpd == 0 && !words && // test triple letters + (word[i-1]==word[i]) && ( + ((i>1) && (word[i-1]==word[i-2])) || + ((word[i-1]==word[i+1])) // may be word[i+1] == '\0' + ) + ) || + ( + checkcompoundcase && scpd == 0 && !words && cpdcase_check(word, i) + )) + ) +// LANG_hu section: spec. Hungarian rule + || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) && + (sfx && sfx->getCont() && ( // XXX hardwired Hungarian dic. codes + TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) || + TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen()) + ) + ) + ) + ) { // first word is ok condition + +// LANG_hu section: spec. Hungarian rule + if (langnum == LANG_hu) { + // calculate syllable number of the word + numsyllable += get_syllable(st, i); + + // + 1 word, if syllable number of the prefix > 1 (hungarian convention) + if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++; + } +// END of LANG_hu section + + // NEXT WORD(S) + rv_first = rv; + st[i] = ch; + + do { // striple loop + + // check simplifiedtriple + if (simplifiedtriple) { + if (striple) { + checkedstriple = 1; + i--; // check "fahrt" instead of "ahrt" in "Schiffahrt" + } else if (i > 2 && *(word+i - 1) == *(word + i - 2)) striple = 1; + } + + rv = lookup((st+i)); // perhaps without prefix + + // search homonym with compound flag + while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) || + !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) || + (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) || + (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))) || + (scpd != 0 && checkcpdtable[scpd-1].cond2 != FLAG_NULL && + !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen)) + )) { + rv = rv->next_homonym; + } + + // check FORCEUCASE + if (rv && forceucase && (rv) && + (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info & SPELL_ORIGCAP)) rv = NULL; + + if (rv && words && words[wnum + 1]) return rv_first; + + oldnumsyllable2 = numsyllable; + oldwordnum2 = wordnum; + + +// LANG_hu section: spec. Hungarian rule, XXX hardwired dictionary code + if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) { + numsyllable--; + } +// END of LANG_hu section + + // increment word number, if the second root has a compoundroot flag + if ((rv) && (compoundroot) && + (TESTAFF(rv->astr, compoundroot, rv->alen))) { + wordnum++; + } + + // check forbiddenwords + if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || + (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL; + + // second word is acceptable, as a root? + // hungarian conventions: compounding is acceptable, + // when compound forms consist of 2 words, or if more, + // then the syllable number of root words must be 6, or lesser. + + if ((rv) && ( + (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) || + (compoundend && TESTAFF(rv->astr, compoundend, rv->alen)) + ) + && ( + ((cpdwordmax==-1) || (wordnum+1clen)<=cpdmaxsyllable)) + ) && + ( + // test CHECKCOMPOUNDPATTERN + !numcheckcpd || scpd != 0 || !cpdpat_check(word, i, rv_first, rv, 0) + ) && + ( + (!checkcompounddup || (rv != rv_first)) + ) + // test CHECKCOMPOUNDPATTERN conditions + && (scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL || + TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen)) + ) + { + // forbid compound word, if it is a non compound word with typical fault + if (checkcompoundrep && cpdrep_check(word,len)) return NULL; + return rv_first; + } + + numsyllable = oldnumsyllable2; + wordnum = oldwordnum2; + + // perhaps second word has prefix or/and suffix + sfx = NULL; + sfxflag = FLAG_NULL; + rv = (compoundflag && !onlycpdrule) ? affix_check((word+i),strlen(word+i), compoundflag, IN_CPD_END) : NULL; + if (!rv && compoundend && !onlycpdrule) { + sfx = NULL; + pfx = NULL; + rv = affix_check((word+i),strlen(word+i), compoundend, IN_CPD_END); + } + + if (!rv && numdefcpd && words) { + rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END); + if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1)) return rv_first; + rv = NULL; + } + + // test CHECKCOMPOUNDPATTERN conditions (allowed forms) + if (rv && !(scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL || + TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))) rv = NULL; + + // test CHECKCOMPOUNDPATTERN conditions (forbidden compounds) + if (rv && numcheckcpd && scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) rv = NULL; + + // check non_compound flag in suffix and prefix + if ((rv) && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundforbidflag, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundforbidflag, + sfx->getContLen())))) { + rv = NULL; + } + + // check FORCEUCASE + if (rv && forceucase && (rv) && + (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info & SPELL_ORIGCAP)) rv = NULL; + + // check forbiddenwords + if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || + (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL; + + // pfxappnd = prefix of word+i, or NULL + // calculate syllable number of prefix. + // hungarian convention: when syllable number of prefix is more, + // than 1, the prefix+word counts as two words. + + if (langnum == LANG_hu) { + // calculate syllable number of the word + numsyllable += get_syllable(word + i, strlen(word + i)); + + // - affix syllable num. + // XXX only second suffix (inflections, not derivations) + if (sfxappnd) { + char * tmp = myrevstrdup(sfxappnd); + numsyllable -= get_syllable(tmp, strlen(tmp)); + free(tmp); + } + + // + 1 word, if syllable number of the prefix > 1 (hungarian convention) + if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++; + + // increment syllable num, if last word has a SYLLABLENUM flag + // and the suffix is beginning `s' + + if (cpdsyllablenum) { + switch (sfxflag) { + case 'c': { numsyllable+=2; break; } + case 'J': { numsyllable += 1; break; } + case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; } + } + } + } + + // increment word number, if the second word has a compoundroot flag + if ((rv) && (compoundroot) && + (TESTAFF(rv->astr, compoundroot, rv->alen))) { + wordnum++; + } + + // second word is acceptable, as a word with prefix or/and suffix? + // hungarian conventions: compounding is acceptable, + // when compound forms consist 2 word, otherwise + // the syllable number of root words is 6, or lesser. + if ((rv) && + ( + ((cpdwordmax == -1) || (wordnum + 1 < cpdwordmax)) || + ((cpdmaxsyllable != 0) && + (numsyllable <= cpdmaxsyllable)) + ) + && ( + (!checkcompounddup || (rv != rv_first)) + )) { + // forbid compound word, if it is a non compound word with typical fault + if (checkcompoundrep && cpdrep_check(word, len)) return NULL; + return rv_first; + } + + numsyllable = oldnumsyllable2; + wordnum = oldwordnum2; + + // perhaps second word is a compound word (recursive call) + if (wordnum < maxwordnum) { + rv = compound_check((st+i),strlen(st+i), wordnum+1, + numsyllable, maxwordnum, wnum + 1, words, 0, is_sug, info); + if (rv && numcheckcpd && ((scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) || + (scpd != 0 && !cpdpat_check(word, i, rv_first, rv, affixed)))) rv = NULL; + } else { + rv=NULL; + } + if (rv) { + // forbid compound word, if it is a non compound word with typical fault + if (checkcompoundrep || forbiddenword) { + struct hentry * rv2 = NULL; + if (checkcompoundrep && cpdrep_check(word, len)) return NULL; + // check first part + if (strncmp(rv->word, word + i, rv->blen) == 0) { + char r = *(st + i + rv->blen); + *(st + i + rv->blen) = '\0'; + if (checkcompoundrep && cpdrep_check(st, i + rv->blen)) return NULL; + if (forbiddenword) { + rv2 = lookup(word); + if (!rv2) rv2 = affix_check(word, len); + if (rv2 && rv2->astr && TESTAFF(rv2->astr, forbiddenword, rv2->alen) && + (strncmp(rv2->word, st, i + rv->blen) == 0)) { + return NULL; + } + } + *(st + i + rv->blen) = r; + } + } + + return rv_first; + } + } while (striple && !checkedstriple); // end of striple loop + + if (checkedstriple) { + i++; + checkedstriple = 0; + striple = 0; + } + + } // first word is ok condition + + if (soldi != 0) { + i = soldi; + soldi = 0; + len = oldlen; + cmin = oldcmin; + cmax = oldcmax; + } + scpd++; + + } while (!onlycpdrule && simplifiedcpd && scpd <= numcheckcpd); // end of simplifiedcpd loop + + scpd = 0; + wordnum = oldwordnum; + numsyllable = oldnumsyllable; + + if (soldi != 0) { + i = soldi; + strcpy(st, word); // XXX add more optim. + soldi = 0; + } else st[i] = ch; + + } while (numdefcpd && oldwordnum == 0 && !onlycpdrule && (onlycpdrule = 1)); // end of onlycpd loop + + } + + return NULL; +} + +// check if compound word is correctly spelled +// hu_mov_rule = spec. Hungarian rule (XXX) +int AffixMgr::compound_check_morph(const char * word, int len, + short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words, + char hu_mov_rule = 0, char ** result = NULL, char * partresult = NULL) +{ + int i; + short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2; + int ok = 0; + + struct hentry * rv = NULL; + struct hentry * rv_first; + struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking + char st [MAXWORDUTF8LEN + 4]; + char ch; + + int checked_prefix; + char presult[MAXLNLEN]; + + int cmin; + int cmax; + + setcminmax(&cmin, &cmax, word, len); + + strcpy(st, word); + + for (i = cmin; i < cmax; i++) { + oldnumsyllable = numsyllable; + oldwordnum = wordnum; + checked_prefix = 0; + + // go to end of the UTF-8 character + if (utf8) { + for (; (st[i] & 0xc0) == 0x80; i++); + if (i >= cmax) return 0; + } + + ch = st[i]; + st[i] = '\0'; + sfx = NULL; + + // FIRST WORD + *presult = '\0'; + if (partresult) mystrcat(presult, partresult, MAXLNLEN); + + rv = lookup(st); // perhaps without prefix + + // search homonym with compound flag + while ((rv) && !hu_mov_rule && + ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) || + !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) || + (compoundbegin && !wordnum && + TESTAFF(rv->astr, compoundbegin, rv->alen)) || + (compoundmiddle && wordnum && !words && + TESTAFF(rv->astr, compoundmiddle, rv->alen)) || + (numdefcpd && + ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) || + (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)))) + ))) { + rv = rv->next_homonym; + } + + if (rv) { + sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_PART, st); + if (!HENTRY_FIND(rv, MORPH_STEM)) { + sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_STEM, st); + } + // store the pointer of the hash entry +// sprintf(presult + strlen(presult), "%c%s%p", MSEP_FLD, MORPH_HENTRY, rv); + if (HENTRY_DATA(rv)) { + sprintf(presult + strlen(presult), "%c%s", MSEP_FLD, HENTRY_DATA2(rv)); + } + } + if (!rv) { + if (compoundflag && !words && + !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) { + if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, + FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule && + sfx->getCont() && + ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag, + sfx->getContLen())) || (compoundend && + TESTAFF(sfx->getCont(), compoundend, + sfx->getContLen())))) { + rv = NULL; + } + } + + if (rv || + (((wordnum == 0) && compoundbegin && + ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || + (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) || + ((wordnum > 0) && compoundmiddle && + ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || + (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle))))) + ) { + // char * p = prefix_check_morph(st, i, 0, compound); + char * p = NULL; + if (compoundflag) p = affix_check_morph(st, i, compoundflag); + if (!p || (*p == '\0')) { + if (p) free(p); + p = NULL; + if ((wordnum == 0) && compoundbegin) { + p = affix_check_morph(st, i, compoundbegin); + } else if ((wordnum > 0) && compoundmiddle) { + p = affix_check_morph(st, i, compoundmiddle); + } + } + if (p && (*p != '\0')) { + sprintf(presult + strlen(presult), "%c%s%s%s", MSEP_FLD, + MORPH_PART, st, line_uniq_app(&p, MSEP_REC)); + } + if (p) free(p); + checked_prefix = 1; + } + // else check forbiddenwords + } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || + TESTAFF(rv->astr, needaffix, rv->alen))) { + st[i] = ch; + continue; + } + + // check non_compound flag in suffix and prefix + if ((rv) && !hu_mov_rule && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundforbidflag, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundforbidflag, + sfx->getContLen())))) { + continue; + } + + // check compoundend flag in suffix and prefix + if ((rv) && !checked_prefix && compoundend && !hu_mov_rule && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundend, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundend, + sfx->getContLen())))) { + continue; + } + + // check compoundmiddle flag in suffix and prefix + if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundmiddle, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundmiddle, + sfx->getContLen())))) { + rv = NULL; + } + + // check forbiddenwords + if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) + || TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) continue; + + // increment word number, if the second root has a compoundroot flag + if ((rv) && (compoundroot) && + (TESTAFF(rv->astr, compoundroot, rv->alen))) { + wordnum++; + } + + // first word is acceptable in compound words? + if (((rv) && + ( checked_prefix || (words && words[wnum]) || + (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) || + ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) || + ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen)) +// LANG_hu section: spec. Hungarian rule + || ((langnum == LANG_hu) && // hu_mov_rule + hu_mov_rule && ( + TESTAFF(rv->astr, 'F', rv->alen) || + TESTAFF(rv->astr, 'G', rv->alen) || + TESTAFF(rv->astr, 'H', rv->alen) + ) + ) +// END of LANG_hu section + ) + && ! (( checkcompoundtriple && !words && // test triple letters + (word[i-1]==word[i]) && ( + ((i>1) && (word[i-1]==word[i-2])) || + ((word[i-1]==word[i+1])) // may be word[i+1] == '\0' + ) + ) || + ( + // test CHECKCOMPOUNDPATTERN + numcheckcpd && !words && cpdpat_check(word, i, rv, NULL, 0) + ) || + ( + checkcompoundcase && !words && cpdcase_check(word, i) + )) + ) +// LANG_hu section: spec. Hungarian rule + || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) && + (sfx && sfx->getCont() && ( + TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) || + TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen()) + ) + ) + ) +// END of LANG_hu section + ) { + +// LANG_hu section: spec. Hungarian rule + if (langnum == LANG_hu) { + // calculate syllable number of the word + numsyllable += get_syllable(st, i); + + // + 1 word, if syllable number of the prefix > 1 (hungarian convention) + if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++; + } +// END of LANG_hu section + + // NEXT WORD(S) + rv_first = rv; + rv = lookup((word+i)); // perhaps without prefix + + // search homonym with compound flag + while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) || + !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) || + (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) || + (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))))) { + rv = rv->next_homonym; + } + + if (rv && words && words[wnum + 1]) { + mystrcat(*result, presult, MAXLNLEN); + mystrcat(*result, " ", MAXLNLEN); + mystrcat(*result, MORPH_PART, MAXLNLEN); + mystrcat(*result, word+i, MAXLNLEN); + if (complexprefixes && HENTRY_DATA(rv)) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); + if (!HENTRY_FIND(rv, MORPH_STEM)) { + mystrcat(*result, " ", MAXLNLEN); + mystrcat(*result, MORPH_STEM, MAXLNLEN); + mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN); + } + // store the pointer of the hash entry +// sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv); + if (!complexprefixes && HENTRY_DATA(rv)) { + mystrcat(*result, " ", MAXLNLEN); + mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); + } + mystrcat(*result, "\n", MAXLNLEN); + ok = 1; + return 0; + } + + oldnumsyllable2 = numsyllable; + oldwordnum2 = wordnum; + +// LANG_hu section: spec. Hungarian rule + if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) { + numsyllable--; + } +// END of LANG_hu section + // increment word number, if the second root has a compoundroot flag + if ((rv) && (compoundroot) && + (TESTAFF(rv->astr, compoundroot, rv->alen))) { + wordnum++; + } + + // check forbiddenwords + if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) { + st[i] = ch; + continue; + } + + // second word is acceptable, as a root? + // hungarian conventions: compounding is acceptable, + // when compound forms consist of 2 words, or if more, + // then the syllable number of root words must be 6, or lesser. + if ((rv) && ( + (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) || + (compoundend && TESTAFF(rv->astr, compoundend, rv->alen)) + ) + && ( + ((cpdwordmax==-1) || (wordnum+1blen)<=cpdmaxsyllable)) + ) + && ( + (!checkcompounddup || (rv != rv_first)) + ) + ) + { + // bad compound word + mystrcat(*result, presult, MAXLNLEN); + mystrcat(*result, " ", MAXLNLEN); + mystrcat(*result, MORPH_PART, MAXLNLEN); + mystrcat(*result, word+i, MAXLNLEN); + + if (HENTRY_DATA(rv)) { + if (complexprefixes) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); + if (! HENTRY_FIND(rv, MORPH_STEM)) { + mystrcat(*result, " ", MAXLNLEN); + mystrcat(*result, MORPH_STEM, MAXLNLEN); + mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN); + } + // store the pointer of the hash entry +// sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv); + if (!complexprefixes) { + mystrcat(*result, " ", MAXLNLEN); + mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); + } + } + mystrcat(*result, "\n", MAXLNLEN); + ok = 1; + } + + numsyllable = oldnumsyllable2 ; + wordnum = oldwordnum2; + + // perhaps second word has prefix or/and suffix + sfx = NULL; + sfxflag = FLAG_NULL; + + if (compoundflag) rv = affix_check((word+i),strlen(word+i), compoundflag); else rv = NULL; + + if (!rv && compoundend) { + sfx = NULL; + pfx = NULL; + rv = affix_check((word+i),strlen(word+i), compoundend); + } + + if (!rv && numdefcpd && words) { + rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END); + if (rv && words && defcpd_check(&words, wnum + 1, rv, NULL, 1)) { + char * m = NULL; + if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag); + if ((!m || *m == '\0') && compoundend) { + if (m) free(m); + m = affix_check_morph((word+i),strlen(word+i), compoundend); + } + mystrcat(*result, presult, MAXLNLEN); + if (m || (*m != '\0')) { + sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD, + MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC)); + } + if (m) free(m); + mystrcat(*result, "\n", MAXLNLEN); + ok = 1; + } + } + + // check non_compound flag in suffix and prefix + if ((rv) && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundforbidflag, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundforbidflag, + sfx->getContLen())))) { + rv = NULL; + } + + // check forbiddenwords + if ((rv) && (rv->astr) && (TESTAFF(rv->astr,forbiddenword,rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen)) + && (! TESTAFF(rv->astr, needaffix, rv->alen))) { + st[i] = ch; + continue; + } + + if (langnum == LANG_hu) { + // calculate syllable number of the word + numsyllable += get_syllable(word + i, strlen(word + i)); + + // - affix syllable num. + // XXX only second suffix (inflections, not derivations) + if (sfxappnd) { + char * tmp = myrevstrdup(sfxappnd); + numsyllable -= get_syllable(tmp, strlen(tmp)); + free(tmp); + } + + // + 1 word, if syllable number of the prefix > 1 (hungarian convention) + if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++; + + // increment syllable num, if last word has a SYLLABLENUM flag + // and the suffix is beginning `s' + + if (cpdsyllablenum) { + switch (sfxflag) { + case 'c': { numsyllable+=2; break; } + case 'J': { numsyllable += 1; break; } + case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; } + } + } + } + + // increment word number, if the second word has a compoundroot flag + if ((rv) && (compoundroot) && + (TESTAFF(rv->astr, compoundroot, rv->alen))) { + wordnum++; + } + // second word is acceptable, as a word with prefix or/and suffix? + // hungarian conventions: compounding is acceptable, + // when compound forms consist 2 word, otherwise + // the syllable number of root words is 6, or lesser. + if ((rv) && + ( + ((cpdwordmax==-1) || (wordnum+1 0) && *s1 && (*s1 == *end_of_s2)) { + s1++; + end_of_s2--; + len--; + } + return (*s1 == '\0'); + } + */ + +inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len) + { + while ((len > 0) && (*s1 != '\0') && ((*s1 == *end_of_s2) || (*s1 == '.'))) { + s1++; + end_of_s2--; + len--; + } + return (*s1 == '\0'); + } + +// check word for suffixes + +struct hentry * AffixMgr::suffix_check (const char * word, int len, + int sfxopts, PfxEntry * ppfx, char ** wlst, int maxSug, int * ns, + const FLAG cclass, const FLAG needflag, char in_compound) +{ + struct hentry * rv = NULL; + PfxEntry* ep = ppfx; + + // first handle the special case of 0 length suffixes + SfxEntry * se = sStart[0]; + + while (se) { + if (!cclass || se->getCont()) { + // suffixes are not allowed in beginning of compounds + if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass + // except when signed with compoundpermitflag flag + (se->getCont() && compoundpermitflag && + TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix || + // no circumfix flag in prefix and suffix + ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) || + // circumfix flag in prefix AND suffix + ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen()))))) && + // fogemorpheme + (in_compound || + !(se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen())))) && + // needaffix on prefix or first suffix + (cclass || + !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) || + (ppfx && !((ep->getCont()) && + TESTAFF(ep->getCont(), needaffix, + ep->getContLen()))) + )) { + rv = se->checkword(word,len, sfxopts, ppfx, wlst, maxSug, ns, (FLAG) cclass, + needflag, (in_compound ? 0 : onlyincompound)); + if (rv) { + sfx=se; // BUG: sfx not stateless + return rv; + } + } + } + se = se->getNext(); + } + + // now handle the general case + if (len == 0) return NULL; // FULLSTRIP + unsigned char sp= *((const unsigned char *)(word + len - 1)); + SfxEntry * sptr = sStart[sp]; + + while (sptr) { + if (isRevSubset(sptr->getKey(), word + len - 1, len) + ) { + // suffixes are not allowed in beginning of compounds + if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass + // except when signed with compoundpermitflag flag + (sptr->getCont() && compoundpermitflag && + TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix || + // no circumfix flag in prefix and suffix + ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) || + // circumfix flag in prefix AND suffix + ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen()))))) && + // fogemorpheme + (in_compound || + !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) && + // needaffix on prefix or first suffix + (cclass || + !(sptr->getCont() && TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) || + (ppfx && !((ep->getCont()) && + TESTAFF(ep->getCont(), needaffix, + ep->getContLen()))) + ) + ) if (in_compound != IN_CPD_END || ppfx || !(sptr->getCont() && TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))) { + rv = sptr->checkword(word,len, sfxopts, ppfx, wlst, + maxSug, ns, cclass, needflag, (in_compound ? 0 : onlyincompound)); + if (rv) { + sfx=sptr; // BUG: sfx not stateless + sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless + if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless + return rv; + } + } + sptr = sptr->getNextEQ(); + } else { + sptr = sptr->getNextNE(); + } + } + + return NULL; +} + +// check word for two-level suffixes + +struct hentry * AffixMgr::suffix_check_twosfx(const char * word, int len, + int sfxopts, PfxEntry * ppfx, const FLAG needflag) +{ + struct hentry * rv = NULL; + + // first handle the special case of 0 length suffixes + SfxEntry * se = sStart[0]; + while (se) { + if (contclasses[se->getFlag()]) + { + rv = se->check_twosfx(word,len, sfxopts, ppfx, needflag); + if (rv) return rv; + } + se = se->getNext(); + } + + // now handle the general case + if (len == 0) return NULL; // FULLSTRIP + unsigned char sp = *((const unsigned char *)(word + len - 1)); + SfxEntry * sptr = sStart[sp]; + + while (sptr) { + if (isRevSubset(sptr->getKey(), word + len - 1, len)) { + if (contclasses[sptr->getFlag()]) + { + rv = sptr->check_twosfx(word,len, sfxopts, ppfx, needflag); + if (rv) { + sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless + if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless + return rv; + } + } + sptr = sptr->getNextEQ(); + } else { + sptr = sptr->getNextNE(); + } + } + + return NULL; +} + +char * AffixMgr::suffix_check_twosfx_morph(const char * word, int len, + int sfxopts, PfxEntry * ppfx, const FLAG needflag) +{ + char result[MAXLNLEN]; + char result2[MAXLNLEN]; + char result3[MAXLNLEN]; + + char * st; + + result[0] = '\0'; + result2[0] = '\0'; + result3[0] = '\0'; + + // first handle the special case of 0 length suffixes + SfxEntry * se = sStart[0]; + while (se) { + if (contclasses[se->getFlag()]) + { + st = se->check_twosfx_morph(word,len, sfxopts, ppfx, needflag); + if (st) { + if (ppfx) { + if (ppfx->getMorph()) { + mystrcat(result, ppfx->getMorph(), MAXLNLEN); + mystrcat(result, " ", MAXLNLEN); + } else debugflag(result, ppfx->getFlag()); + } + mystrcat(result, st, MAXLNLEN); + free(st); + if (se->getMorph()) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, se->getMorph(), MAXLNLEN); + } else debugflag(result, se->getFlag()); + mystrcat(result, "\n", MAXLNLEN); + } + } + se = se->getNext(); + } + + // now handle the general case + if (len == 0) return NULL; // FULLSTRIP + unsigned char sp = *((const unsigned char *)(word + len - 1)); + SfxEntry * sptr = sStart[sp]; + + while (sptr) { + if (isRevSubset(sptr->getKey(), word + len - 1, len)) { + if (contclasses[sptr->getFlag()]) + { + st = sptr->check_twosfx_morph(word,len, sfxopts, ppfx, needflag); + if (st) { + sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless + if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless + strcpy(result2, st); + free(st); + + result3[0] = '\0'; + + if (sptr->getMorph()) { + mystrcat(result3, " ", MAXLNLEN); + mystrcat(result3, sptr->getMorph(), MAXLNLEN); + } else debugflag(result3, sptr->getFlag()); + strlinecat(result2, result3); + mystrcat(result2, "\n", MAXLNLEN); + mystrcat(result, result2, MAXLNLEN); + } + } + sptr = sptr->getNextEQ(); + } else { + sptr = sptr->getNextNE(); + } + } + if (*result) return mystrdup(result); + return NULL; +} + +char * AffixMgr::suffix_check_morph(const char * word, int len, + int sfxopts, PfxEntry * ppfx, const FLAG cclass, const FLAG needflag, char in_compound) +{ + char result[MAXLNLEN]; + + struct hentry * rv = NULL; + + result[0] = '\0'; + + PfxEntry* ep = ppfx; + + // first handle the special case of 0 length suffixes + SfxEntry * se = sStart[0]; + while (se) { + if (!cclass || se->getCont()) { + // suffixes are not allowed in beginning of compounds + if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass + // except when signed with compoundpermitflag flag + (se->getCont() && compoundpermitflag && + TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix || + // no circumfix flag in prefix and suffix + ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) || + // circumfix flag in prefix AND suffix + ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen()))))) && + // fogemorpheme + (in_compound || + !((se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen()))))) && + // needaffix on prefix or first suffix + (cclass || + !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) || + (ppfx && !((ep->getCont()) && + TESTAFF(ep->getCont(), needaffix, + ep->getContLen()))) + ) + )) + rv = se->checkword(word, len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag); + while (rv) { + if (ppfx) { + if (ppfx->getMorph()) { + mystrcat(result, ppfx->getMorph(), MAXLNLEN); + mystrcat(result, " ", MAXLNLEN); + } else debugflag(result, ppfx->getFlag()); + } + if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + if (! HENTRY_FIND(rv, MORPH_STEM)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, MORPH_STEM, MAXLNLEN); + mystrcat(result, HENTRY_WORD(rv), MAXLNLEN); + } + // store the pointer of the hash entry +// sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv); + + if (!complexprefixes && HENTRY_DATA(rv)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + } + if (se->getMorph()) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, se->getMorph(), MAXLNLEN); + } else debugflag(result, se->getFlag()); + mystrcat(result, "\n", MAXLNLEN); + rv = se->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag); + } + } + se = se->getNext(); + } + + // now handle the general case + if (len == 0) return NULL; // FULLSTRIP + unsigned char sp = *((const unsigned char *)(word + len - 1)); + SfxEntry * sptr = sStart[sp]; + + while (sptr) { + if (isRevSubset(sptr->getKey(), word + len - 1, len) + ) { + // suffixes are not allowed in beginning of compounds + if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass + // except when signed with compoundpermitflag flag + (sptr->getCont() && compoundpermitflag && + TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix || + // no circumfix flag in prefix and suffix + ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) || + // circumfix flag in prefix AND suffix + ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen()))))) && + // fogemorpheme + (in_compound || + !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) && + // needaffix on first suffix + (cclass || !(sptr->getCont() && + TESTAFF(sptr->getCont(), needaffix, sptr->getContLen()))) + )) rv = sptr->checkword(word,len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag); + while (rv) { + if (ppfx) { + if (ppfx->getMorph()) { + mystrcat(result, ppfx->getMorph(), MAXLNLEN); + mystrcat(result, " ", MAXLNLEN); + } else debugflag(result, ppfx->getFlag()); + } + if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + if (! HENTRY_FIND(rv, MORPH_STEM)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, MORPH_STEM, MAXLNLEN); + mystrcat(result, HENTRY_WORD(rv), MAXLNLEN); + } + // store the pointer of the hash entry +// sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv); + + if (!complexprefixes && HENTRY_DATA(rv)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + } + + if (sptr->getMorph()) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, sptr->getMorph(), MAXLNLEN); + } else debugflag(result, sptr->getFlag()); + mystrcat(result, "\n", MAXLNLEN); + rv = sptr->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag); + } + sptr = sptr->getNextEQ(); + } else { + sptr = sptr->getNextNE(); + } + } + + if (*result) return mystrdup(result); + return NULL; +} + +// check if word with affixes is correctly spelled +struct hentry * AffixMgr::affix_check (const char * word, int len, const FLAG needflag, char in_compound) +{ + struct hentry * rv= NULL; + + // check all prefixes (also crossed with suffixes if allowed) + rv = prefix_check(word, len, in_compound, needflag); + if (rv) return rv; + + // if still not found check all suffixes + rv = suffix_check(word, len, 0, NULL, NULL, 0, NULL, FLAG_NULL, needflag, in_compound); + + if (havecontclass) { + sfx = NULL; + pfx = NULL; + + if (rv) return rv; + // if still not found check all two-level suffixes + rv = suffix_check_twosfx(word, len, 0, NULL, needflag); + + if (rv) return rv; + // if still not found check all two-level suffixes + rv = prefix_check_twosfx(word, len, IN_CPD_NOT, needflag); + } + + return rv; +} + +// check if word with affixes is correctly spelled +char * AffixMgr::affix_check_morph(const char * word, int len, const FLAG needflag, char in_compound) +{ + char result[MAXLNLEN]; + char * st = NULL; + + *result = '\0'; + + // check all prefixes (also crossed with suffixes if allowed) + st = prefix_check_morph(word, len, in_compound); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + + // if still not found check all suffixes + st = suffix_check_morph(word, len, 0, NULL, '\0', needflag, in_compound); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + + if (havecontclass) { + sfx = NULL; + pfx = NULL; + // if still not found check all two-level suffixes + st = suffix_check_twosfx_morph(word, len, 0, NULL, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + + // if still not found check all two-level suffixes + st = prefix_check_twosfx_morph(word, len, IN_CPD_NOT, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + } + + return mystrdup(result); +} + +char * AffixMgr::morphgen(char * ts, int wl, const unsigned short * ap, + unsigned short al, char * morph, char * targetmorph, int level) +{ + // handle suffixes + char * stemmorph; + char * stemmorphcatpos; + char mymorph[MAXLNLEN]; + + if (!morph) return NULL; + + // check substandard flag + if (TESTAFF(ap, substandard, al)) return NULL; + + if (morphcmp(morph, targetmorph) == 0) return mystrdup(ts); + +// int targetcount = get_sfxcount(targetmorph); + + // use input suffix fields, if exist + if (strstr(morph, MORPH_INFL_SFX) || strstr(morph, MORPH_DERI_SFX)) { + stemmorph = mymorph; + strcpy(stemmorph, morph); + mystrcat(stemmorph, " ", MAXLNLEN); + stemmorphcatpos = stemmorph + strlen(stemmorph); + } else { + stemmorph = morph; + stemmorphcatpos = NULL; + } + + for (int i = 0; i < al; i++) { + const unsigned char c = (unsigned char) (ap[i] & 0x00FF); + SfxEntry * sptr = sFlag[c]; + while (sptr) { + if (sptr->getFlag() == ap[i] && sptr->getMorph() && ((sptr->getContLen() == 0) || + // don't generate forms with substandard affixes + !TESTAFF(sptr->getCont(), substandard, sptr->getContLen()))) { + + if (stemmorphcatpos) strcpy(stemmorphcatpos, sptr->getMorph()); + else stemmorph = (char *) sptr->getMorph(); + + int cmp = morphcmp(stemmorph, targetmorph); + + if (cmp == 0) { + char * newword = sptr->add(ts, wl); + if (newword) { + hentry * check = pHMgr->lookup(newword); // XXX extra dic + if (!check || !check->astr || + !(TESTAFF(check->astr, forbiddenword, check->alen) || + TESTAFF(check->astr, ONLYUPCASEFLAG, check->alen))) { + return newword; + } + free(newword); + } + } + + // recursive call for secondary suffixes + if ((level == 0) && (cmp == 1) && (sptr->getContLen() > 0) && +// (get_sfxcount(stemmorph) < targetcount) && + !TESTAFF(sptr->getCont(), substandard, sptr->getContLen())) { + char * newword = sptr->add(ts, wl); + if (newword) { + char * newword2 = morphgen(newword, strlen(newword), sptr->getCont(), + sptr->getContLen(), stemmorph, targetmorph, 1); + + if (newword2) { + free(newword); + return newword2; + } + free(newword); + newword = NULL; + } + } + } + sptr = sptr->getFlgNxt(); + } + } + return NULL; +} + + +int AffixMgr::expand_rootword(struct guessword * wlst, int maxn, const char * ts, + int wl, const unsigned short * ap, unsigned short al, char * bad, int badl, + char * phon) +{ + int nh=0; + // first add root word to list + if ((nh < maxn) && !(al && ((needaffix && TESTAFF(ap, needaffix, al)) || + (onlyincompound && TESTAFF(ap, onlyincompound, al))))) { + wlst[nh].word = mystrdup(ts); + if (!wlst[nh].word) return 0; + wlst[nh].allow = (1 == 0); + wlst[nh].orig = NULL; + nh++; + // add special phonetic version + if (phon && (nh < maxn)) { + wlst[nh].word = mystrdup(phon); + if (!wlst[nh].word) return nh - 1; + wlst[nh].allow = (1 == 0); + wlst[nh].orig = mystrdup(ts); + if (!wlst[nh].orig) return nh - 1; + nh++; + } + } + + // handle suffixes + for (int i = 0; i < al; i++) { + const unsigned char c = (unsigned char) (ap[i] & 0x00FF); + SfxEntry * sptr = sFlag[c]; + while (sptr) { + if ((sptr->getFlag() == ap[i]) && (!sptr->getKeyLen() || ((badl > sptr->getKeyLen()) && + (strcmp(sptr->getAffix(), bad + badl - sptr->getKeyLen()) == 0))) && + // check needaffix flag + !(sptr->getCont() && ((needaffix && + TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) || + (circumfix && + TESTAFF(sptr->getCont(), circumfix, sptr->getContLen())) || + (onlyincompound && + TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen())))) + ) { + char * newword = sptr->add(ts, wl); + if (newword) { + if (nh < maxn) { + wlst[nh].word = newword; + wlst[nh].allow = sptr->allowCross(); + wlst[nh].orig = NULL; + nh++; + // add special phonetic version + if (phon && (nh < maxn)) { + char st[MAXWORDUTF8LEN]; + strcpy(st, phon); + strcat(st, sptr->getKey()); + reverseword(st + strlen(phon)); + wlst[nh].word = mystrdup(st); + if (!wlst[nh].word) return nh - 1; + wlst[nh].allow = (1 == 0); + wlst[nh].orig = mystrdup(newword); + if (!wlst[nh].orig) return nh - 1; + nh++; + } + } else { + free(newword); + } + } + } + sptr = sptr->getFlgNxt(); + } + } + + int n = nh; + + // handle cross products of prefixes and suffixes + for (int j=1;jgetFlag() == ap[k]) && cptr->allowCross() && (!cptr->getKeyLen() || ((badl > cptr->getKeyLen()) && + (strncmp(cptr->getKey(), bad, cptr->getKeyLen()) == 0)))) { + int l1 = strlen(wlst[j].word); + char * newword = cptr->add(wlst[j].word, l1); + if (newword) { + if (nh < maxn) { + wlst[nh].word = newword; + wlst[nh].allow = cptr->allowCross(); + wlst[nh].orig = NULL; + nh++; + } else { + free(newword); + } + } + } + cptr = cptr->getFlgNxt(); + } + } + } + + + // now handle pure prefixes + for (int m = 0; m < al; m ++) { + const unsigned char c = (unsigned char) (ap[m] & 0x00FF); + PfxEntry * ptr = pFlag[c]; + while (ptr) { + if ((ptr->getFlag() == ap[m]) && (!ptr->getKeyLen() || ((badl > ptr->getKeyLen()) && + (strncmp(ptr->getKey(), bad, ptr->getKeyLen()) == 0))) && + // check needaffix flag + !(ptr->getCont() && ((needaffix && + TESTAFF(ptr->getCont(), needaffix, ptr->getContLen())) || + (circumfix && + TESTAFF(ptr->getCont(), circumfix, ptr->getContLen())) || + (onlyincompound && + TESTAFF(ptr->getCont(), onlyincompound, ptr->getContLen())))) + ) { + char * newword = ptr->add(ts, wl); + if (newword) { + if (nh < maxn) { + wlst[nh].word = newword; + wlst[nh].allow = ptr->allowCross(); + wlst[nh].orig = NULL; + nh++; + } else { + free(newword); + } + } + } + ptr = ptr->getFlgNxt(); + } + } + + return nh; +} + +// return length of replacing table +int AffixMgr::get_numrep() const +{ + return numrep; +} + +// return replacing table +struct replentry * AffixMgr::get_reptable() const +{ + if (! reptable ) return NULL; + return reptable; +} + +// return iconv table +RepList * AffixMgr::get_iconvtable() const +{ + if (! iconvtable ) return NULL; + return iconvtable; +} + +// return oconv table +RepList * AffixMgr::get_oconvtable() const +{ + if (! oconvtable ) return NULL; + return oconvtable; +} + +// return replacing table +struct phonetable * AffixMgr::get_phonetable() const +{ + if (! phone ) return NULL; + return phone; +} + +// return length of character map table +int AffixMgr::get_nummap() const +{ + return nummap; +} + +// return character map table +struct mapentry * AffixMgr::get_maptable() const +{ + if (! maptable ) return NULL; + return maptable; +} + +// return length of word break table +int AffixMgr::get_numbreak() const +{ + return numbreak; +} + +// return character map table +char ** AffixMgr::get_breaktable() const +{ + if (! breaktable ) return NULL; + return breaktable; +} + +// return text encoding of dictionary +char * AffixMgr::get_encoding() +{ + if (! encoding ) encoding = mystrdup(SPELL_ENCODING); + return mystrdup(encoding); +} + +// return text encoding of dictionary +int AffixMgr::get_langnum() const +{ + return langnum; +} + +// return double prefix option +int AffixMgr::get_complexprefixes() const +{ + return complexprefixes; +} + +// return FULLSTRIP option +int AffixMgr::get_fullstrip() const +{ + return fullstrip; +} + +FLAG AffixMgr::get_keepcase() const +{ + return keepcase; +} + +FLAG AffixMgr::get_forceucase() const +{ + return forceucase; +} + +FLAG AffixMgr::get_warn() const +{ + return warn; +} + +int AffixMgr::get_forbidwarn() const +{ + return forbidwarn; +} + +int AffixMgr::get_checksharps() const +{ + return checksharps; +} + +char * AffixMgr::encode_flag(unsigned short aflag) const +{ + return pHMgr->encode_flag(aflag); +} + + +// return the preferred ignore string for suggestions +char * AffixMgr::get_ignore() const +{ + if (!ignorechars) return NULL; + return ignorechars; +} + +// return the preferred ignore string for suggestions +unsigned short * AffixMgr::get_ignore_utf16(int * len) const +{ + *len = ignorechars_utf16_len; + return ignorechars_utf16; +} + +// return the keyboard string for suggestions +char * AffixMgr::get_key_string() +{ + if (! keystring ) keystring = mystrdup(SPELL_KEYSTRING); + return mystrdup(keystring); +} + +// return the preferred try string for suggestions +char * AffixMgr::get_try_string() const +{ + if (! trystring ) return NULL; + return mystrdup(trystring); +} + +// return the preferred try string for suggestions +const char * AffixMgr::get_wordchars() const +{ + return wordchars; +} + +unsigned short * AffixMgr::get_wordchars_utf16(int * len) const +{ + *len = wordchars_utf16_len; + return wordchars_utf16; +} + +// is there compounding? +int AffixMgr::get_compound() const +{ + return compoundflag || compoundbegin || numdefcpd; +} + +// return the compound words control flag +FLAG AffixMgr::get_compoundflag() const +{ + return compoundflag; +} + +// return the forbidden words control flag +FLAG AffixMgr::get_forbiddenword() const +{ + return forbiddenword; +} + +// return the forbidden words control flag +FLAG AffixMgr::get_nosuggest() const +{ + return nosuggest; +} + +// return the forbidden words control flag +FLAG AffixMgr::get_nongramsuggest() const +{ + return nongramsuggest; +} + +// return the forbidden words flag modify flag +FLAG AffixMgr::get_needaffix() const +{ + return needaffix; +} + +// return the onlyincompound flag +FLAG AffixMgr::get_onlyincompound() const +{ + return onlyincompound; +} + +// return the compound word signal flag +FLAG AffixMgr::get_compoundroot() const +{ + return compoundroot; +} + +// return the compound begin signal flag +FLAG AffixMgr::get_compoundbegin() const +{ + return compoundbegin; +} + +// return the value of checknum +int AffixMgr::get_checknum() const +{ + return checknum; +} + +// return the value of prefix +const char * AffixMgr::get_prefix() const +{ + if (pfx) return pfx->getKey(); + return NULL; +} + +// return the value of suffix +const char * AffixMgr::get_suffix() const +{ + return sfxappnd; +} + +// return the value of suffix +const char * AffixMgr::get_version() const +{ + return version; +} + +// return lemma_present flag +FLAG AffixMgr::get_lemma_present() const +{ + return lemma_present; +} + +// utility method to look up root words in hash table +struct hentry * AffixMgr::lookup(const char * word) +{ + int i; + struct hentry * he = NULL; + for (i = 0; i < *maxdic && !he; i++) { + he = (alldic[i])->lookup(word); + } + return he; +} + +// return the value of suffix +int AffixMgr::have_contclass() const +{ + return havecontclass; +} + +// return utf8 +int AffixMgr::get_utf8() const +{ + return utf8; +} + +int AffixMgr::get_maxngramsugs(void) const +{ + return maxngramsugs; +} + +int AffixMgr::get_maxcpdsugs(void) const +{ + return maxcpdsugs; +} + +int AffixMgr::get_maxdiff(void) const +{ + return maxdiff; +} + +int AffixMgr::get_onlymaxdiff(void) const +{ + return onlymaxdiff; +} + +// return nosplitsugs +int AffixMgr::get_nosplitsugs(void) const +{ + return nosplitsugs; +} + +// return sugswithdots +int AffixMgr::get_sugswithdots(void) const +{ + return sugswithdots; +} + +/* parse flag */ +int AffixMgr::parse_flag(char * line, unsigned short * out, FileMgr * af) { + char * s = NULL; + if (*out != FLAG_NULL && !(*out >= DEFAULTFLAGS)) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum()); + return 1; + } + if (parse_string(line, &s, af->getlinenum())) return 1; + *out = pHMgr->decode_flag(s); + free(s); + return 0; +} + +/* parse num */ +int AffixMgr::parse_num(char * line, int * out, FileMgr * af) { + char * s = NULL; + if (*out != -1) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum()); + return 1; + } + if (parse_string(line, &s, af->getlinenum())) return 1; + *out = atoi(s); + free(s); + return 0; +} + +/* parse in the max syllablecount of compound words and */ +int AffixMgr::parse_cpdsyllable(char * line, FileMgr * af) +{ + char * tp = line; + char * piece; + int i = 0; + int np = 0; + w_char w[MAXWORDLEN]; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { cpdmaxsyllable = atoi(piece); np++; break; } + case 2: { + if (!utf8) { + cpdvowels = mystrdup(piece); + } else { + int n = u8_u16(w, MAXWORDLEN, piece); + if (n > 0) { + flag_qsort((unsigned short *) w, 0, n); + cpdvowels_utf16 = (w_char *) malloc(n * sizeof(w_char)); + if (!cpdvowels_utf16) return 1; + memcpy(cpdvowels_utf16, w, n * sizeof(w_char)); + } + cpdvowels_utf16_len = n; + } + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np < 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing compoundsyllable information\n", af->getlinenum()); + return 1; + } + if (np == 2) cpdvowels = mystrdup("aeiouAEIOU"); + return 0; +} + +/* parse in the typical fault correcting table */ +int AffixMgr::parse_reptable(char * line, FileMgr * af) +{ + if (numrep != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + numrep = atoi(piece); + if (numrep < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum()); + return 1; + } + reptable = (replentry *) malloc(numrep * sizeof(struct replentry)); + if (!reptable) return 1; + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the numrep lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < numrep; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + reptable[j].pattern = NULL; + reptable[j].pattern2 = NULL; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece,"REP",3) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numrep = 0; + return 1; + } + break; + } + case 1: { + if (*piece == '^') reptable[j].start = true; else reptable[j].start = false; + reptable[j].pattern = mystrrep(mystrdup(piece + int(reptable[j].start)),"_"," "); + int lr = strlen(reptable[j].pattern) - 1; + if (reptable[j].pattern[lr] == '$') { + reptable[j].end = true; + reptable[j].pattern[lr] = '\0'; + } else reptable[j].end = false; + break; + } + case 2: { reptable[j].pattern2 = mystrrep(mystrdup(piece),"_"," "); break; } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if ((!(reptable[j].pattern)) || (!(reptable[j].pattern2))) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numrep = 0; + return 1; + } + } + return 0; +} + +/* parse in the typical fault correcting table */ +int AffixMgr::parse_convtable(char * line, FileMgr * af, RepList ** rl, const char * keyword) +{ + if (*rl) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + int numrl = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + numrl = atoi(piece); + if (numrl < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum()); + return 1; + } + *rl = new RepList(numrl); + if (!*rl) return 1; + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the num lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < numrl; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + char * pattern = NULL; + char * pattern2 = NULL; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece, keyword, sizeof(keyword)) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + delete *rl; + *rl = NULL; + return 1; + } + break; + } + case 1: { pattern = mystrrep(mystrdup(piece),"_"," "); break; } + case 2: { + pattern2 = mystrrep(mystrdup(piece),"_"," "); + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (!pattern || !pattern2) { + if (pattern) + free(pattern); + if (pattern2) + free(pattern2); + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + return 1; + } + (*rl)->add(pattern, pattern2); + } + return 0; +} + + +/* parse in the typical fault correcting table */ +int AffixMgr::parse_phonetable(char * line, FileMgr * af) +{ + if (phone) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + phone = (phonetable *) malloc(sizeof(struct phonetable)); + if (!phone) return 1; + phone->num = atoi(piece); + phone->rules = NULL; + phone->utf8 = (char) utf8; + if (phone->num < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); + return 1; + } + phone->rules = (char * *) malloc(2 * (phone->num + 1) * sizeof(char *)); + if (!phone->rules) { + free(phone); + phone = NULL; + return 1; + } + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the phone->num lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < phone->num; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + phone->rules[j * 2] = NULL; + phone->rules[j * 2 + 1] = NULL; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece,"PHONE",5) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + phone->num = 0; + return 1; + } + break; + } + case 1: { phone->rules[j * 2] = mystrrep(mystrdup(piece),"_",""); break; } + case 2: { phone->rules[j * 2 + 1] = mystrrep(mystrdup(piece),"_",""); break; } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if ((!(phone->rules[j * 2])) || (!(phone->rules[j * 2 + 1]))) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + phone->num = 0; + return 1; + } + } + phone->rules[phone->num * 2] = mystrdup(""); + phone->rules[phone->num * 2 + 1] = mystrdup(""); + init_phonet_hash(*phone); + return 0; +} + +/* parse in the checkcompoundpattern table */ +int AffixMgr::parse_checkcpdtable(char * line, FileMgr * af) +{ + if (numcheckcpd != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + numcheckcpd = atoi(piece); + if (numcheckcpd < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); + return 1; + } + checkcpdtable = (patentry *) malloc(numcheckcpd * sizeof(struct patentry)); + if (!checkcpdtable) return 1; + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the numcheckcpd lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < numcheckcpd; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + checkcpdtable[j].pattern = NULL; + checkcpdtable[j].pattern2 = NULL; + checkcpdtable[j].pattern3 = NULL; + checkcpdtable[j].cond = FLAG_NULL; + checkcpdtable[j].cond2 = FLAG_NULL; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece,"CHECKCOMPOUNDPATTERN",20) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numcheckcpd = 0; + return 1; + } + break; + } + case 1: { + checkcpdtable[j].pattern = mystrdup(piece); + char * p = strchr(checkcpdtable[j].pattern, '/'); + if (p) { + *p = '\0'; + checkcpdtable[j].cond = pHMgr->decode_flag(p + 1); + } + break; } + case 2: { + checkcpdtable[j].pattern2 = mystrdup(piece); + char * p = strchr(checkcpdtable[j].pattern2, '/'); + if (p) { + *p = '\0'; + checkcpdtable[j].cond2 = pHMgr->decode_flag(p + 1); + } + break; + } + case 3: { checkcpdtable[j].pattern3 = mystrdup(piece); simplifiedcpd = 1; break; } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if ((!(checkcpdtable[j].pattern)) || (!(checkcpdtable[j].pattern2))) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numcheckcpd = 0; + return 1; + } + } + return 0; +} + +/* parse in the compound rule table */ +int AffixMgr::parse_defcpdtable(char * line, FileMgr * af) +{ + if (numdefcpd != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + numdefcpd = atoi(piece); + if (numdefcpd < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); + return 1; + } + defcpdtable = (flagentry *) malloc(numdefcpd * sizeof(flagentry)); + if (!defcpdtable) return 1; + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the numdefcpd lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < numdefcpd; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + defcpdtable[j].def = NULL; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece, "COMPOUNDRULE", 12) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numdefcpd = 0; + return 1; + } + break; + } + case 1: { // handle parenthesized flags + if (strchr(piece, '(')) { + defcpdtable[j].def = (FLAG *) malloc(strlen(piece) * sizeof(FLAG)); + defcpdtable[j].len = 0; + int end = 0; + FLAG * conv; + while (!end) { + char * par = piece + 1; + while (*par != '(' && *par != ')' && *par != '\0') par++; + if (*par == '\0') end = 1; else *par = '\0'; + if (*piece == '(') piece++; + if (*piece == '*' || *piece == '?') { + defcpdtable[j].def[defcpdtable[j].len++] = (FLAG) *piece; + } else if (*piece != '\0') { + int l = pHMgr->decode_flags(&conv, piece, af); + for (int k = 0; k < l; k++) defcpdtable[j].def[defcpdtable[j].len++] = conv[k]; + free(conv); + } + piece = par + 1; + } + } else { + defcpdtable[j].len = pHMgr->decode_flags(&(defcpdtable[j].def), piece, af); + } + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (!defcpdtable[j].len) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numdefcpd = 0; + return 1; + } + } + return 0; +} + + +/* parse in the character map table */ +int AffixMgr::parse_maptable(char * line, FileMgr * af) +{ + if (nummap != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + nummap = atoi(piece); + if (nummap < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); + return 1; + } + maptable = (mapentry *) malloc(nummap * sizeof(struct mapentry)); + if (!maptable) return 1; + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the nummap lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < nummap; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + maptable[j].set = NULL; + maptable[j].len = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece,"MAP",3) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + nummap = 0; + return 1; + } + break; + } + case 1: { + int setn = 0; + maptable[j].len = strlen(piece); + maptable[j].set = (char **) malloc(maptable[j].len * sizeof(char*)); + if (!maptable[j].set) return 1; + for (int k = 0; k < maptable[j].len; k++) { + int chl = 1; + int chb = k; + if (piece[k] == '(') { + char * parpos = strchr(piece + k, ')'); + if (parpos != NULL) { + chb = k + 1; + chl = (int)(parpos - piece) - k - 1; + k = k + chl + 1; + } + } else { + if (utf8 && (piece[k] & 0xc0) == 0xc0) { + for (k++; utf8 && (piece[k] & 0xc0) == 0x80; k++); + chl = k - chb; + k--; + } + } + maptable[j].set[setn] = (char *) malloc(chl + 1); + if (!maptable[j].set[setn]) return 1; + strncpy(maptable[j].set[setn], piece + chb, chl); + maptable[j].set[setn][chl] = '\0'; + setn++; + } + maptable[j].len = setn; + break; } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (!maptable[j].set || !maptable[j].len) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + nummap = 0; + return 1; + } + } + return 0; +} + +/* parse in the word breakpoint table */ +int AffixMgr::parse_breaktable(char * line, FileMgr * af) +{ + if (numbreak > -1) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + numbreak = atoi(piece); + if (numbreak < 0) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); + return 1; + } + if (numbreak == 0) return 0; + breaktable = (char **) malloc(numbreak * sizeof(char *)); + if (!breaktable) return 1; + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the numbreak lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < numbreak; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece,"BREAK",5) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numbreak = 0; + return 1; + } + break; + } + case 1: { + breaktable[j] = mystrdup(piece); + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (!breaktable) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numbreak = 0; + return 1; + } + } + return 0; +} + +void AffixMgr::reverse_condition(char * piece) { + int neg = 0; + for (char * k = piece + strlen(piece) - 1; k >= piece; k--) { + switch(*k) { + case '[': { + if (neg) *(k+1) = '['; else *k = ']'; + break; + } + case ']': { + *k = '['; + if (neg) *(k+1) = '^'; + neg = 0; + break; + } + case '^': { + if (*(k+1) == ']') neg = 1; else *(k+1) = *k; + break; + } + default: { + if (neg) *(k+1) = *k; + } + } + } +} + +int AffixMgr::parse_affix(char * line, const char at, FileMgr * af, char * dupflags) +{ + int numents = 0; // number of affentry structures to parse + + unsigned short aflag = 0; // affix char identifier + + char ff=0; + std::vector affentries; + + char * tp = line; + char * nl = line; + char * piece; + int i = 0; + + // checking lines with bad syntax +#ifdef DEBUG + int basefieldnum = 0; +#endif + + // split affix header line into pieces + + int np = 0; + + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + // piece 1 - is type of affix + case 0: { np++; break; } + + // piece 2 - is affix char + case 1: { + np++; + aflag = pHMgr->decode_flag(piece); + if (((at == 'S') && (dupflags[aflag] & dupSFX)) || + ((at == 'P') && (dupflags[aflag] & dupPFX))) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix flag\n", + af->getlinenum()); + // return 1; XXX permissive mode for bad dictionaries + } + dupflags[aflag] += (char) ((at == 'S') ? dupSFX : dupPFX); + break; + } + // piece 3 - is cross product indicator + case 2: { np++; if (*piece == 'Y') ff = aeXPRODUCT; break; } + + // piece 4 - is number of affentries + case 3: { + np++; + numents = atoi(piece); + if (numents == 0) { + char * err = pHMgr->encode_flag(aflag); + if (err) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", + af->getlinenum()); + free(err); + } + return 1; + } + affentries.resize(numents); + affentries[0].opts = ff; + if (utf8) affentries[0].opts += aeUTF8; + if (pHMgr->is_aliasf()) affentries[0].opts += aeALIASF; + if (pHMgr->is_aliasm()) affentries[0].opts += aeALIASM; + affentries[0].aflag = aflag; + } + + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + // check to make sure we parsed enough pieces + if (np != 4) { + char * err = pHMgr->encode_flag(aflag); + if (err) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + free(err); + } + return 1; + } + + // now parse numents affentries for this affix + std::vector::iterator start = affentries.begin(); + std::vector::iterator end = affentries.end(); + for (std::vector::iterator entry = start; entry != end; ++entry) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + np = 0; + + // split line into pieces + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + // piece 1 - is type + case 0: { + np++; + if (entry != start) entry->opts = start->opts & + (char) (aeXPRODUCT + aeUTF8 + aeALIASF + aeALIASM); + break; + } + + // piece 2 - is affix char + case 1: { + np++; + if (pHMgr->decode_flag(piece) != aflag) { + char * err = pHMgr->encode_flag(aflag); + if (err) { + HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n", + af->getlinenum(), err); + free(err); + } + return 1; + } + + if (entry != start) entry->aflag = start->aflag; + break; + } + + // piece 3 - is string to strip or 0 for null + case 2: { + np++; + if (complexprefixes) { + if (utf8) reverseword_utf(piece); else reverseword(piece); + } + entry->strip = mystrdup(piece); + entry->stripl = (unsigned char) strlen(entry->strip); + if (strcmp(entry->strip,"0") == 0) { + free(entry->strip); + entry->strip=mystrdup(""); + entry->stripl = 0; + } + break; + } + + // piece 4 - is affix string or 0 for null + case 3: { + char * dash; + entry->morphcode = NULL; + entry->contclass = NULL; + entry->contclasslen = 0; + np++; + dash = strchr(piece, '/'); + if (dash) { + *dash = '\0'; + + if (ignorechars) { + if (utf8) { + remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len); + } else { + remove_ignored_chars(piece,ignorechars); + } + } + + if (complexprefixes) { + if (utf8) reverseword_utf(piece); else reverseword(piece); + } + entry->appnd = mystrdup(piece); + + if (pHMgr->is_aliasf()) { + int index = atoi(dash + 1); + entry->contclasslen = (unsigned short) pHMgr->get_aliasf(index, &(entry->contclass), af); + if (!entry->contclasslen) HUNSPELL_WARNING(stderr, "error: bad affix flag alias: \"%s\"\n", dash+1); + } else { + entry->contclasslen = (unsigned short) pHMgr->decode_flags(&(entry->contclass), dash + 1, af); + flag_qsort(entry->contclass, 0, entry->contclasslen); + } + *dash = '/'; + + havecontclass = 1; + for (unsigned short _i = 0; _i < entry->contclasslen; _i++) { + contclasses[(entry->contclass)[_i]] = 1; + } + } else { + if (ignorechars) { + if (utf8) { + remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len); + } else { + remove_ignored_chars(piece,ignorechars); + } + } + + if (complexprefixes) { + if (utf8) reverseword_utf(piece); else reverseword(piece); + } + entry->appnd = mystrdup(piece); + } + + entry->appndl = (unsigned char) strlen(entry->appnd); + if (strcmp(entry->appnd,"0") == 0) { + free(entry->appnd); + entry->appnd=mystrdup(""); + entry->appndl = 0; + } + break; + } + + // piece 5 - is the conditions descriptions + case 4: { + np++; + if (complexprefixes) { + if (utf8) reverseword_utf(piece); else reverseword(piece); + reverse_condition(piece); + } + if (entry->stripl && (strcmp(piece, ".") != 0) && + redundant_condition(at, entry->strip, entry->stripl, piece, af->getlinenum())) + strcpy(piece, "."); + if (at == 'S') { + reverseword(piece); + reverse_condition(piece); + } + if (encodeit(*entry, piece)) return 1; + break; + } + + case 5: { + np++; + if (pHMgr->is_aliasm()) { + int index = atoi(piece); + entry->morphcode = pHMgr->get_aliasm(index); + } else { + if (complexprefixes) { // XXX - fix me for morph. gen. + if (utf8) reverseword_utf(piece); else reverseword(piece); + } + // add the remaining of the line + if (*tp) { + *(tp - 1) = ' '; + tp = tp + strlen(tp); + } + entry->morphcode = mystrdup(piece); + if (!entry->morphcode) return 1; + } + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + // check to make sure we parsed enough pieces + if (np < 4) { + char * err = pHMgr->encode_flag(aflag); + if (err) { + HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n", + af->getlinenum(), err); + free(err); + } + return 1; + } + +#ifdef DEBUG + // detect unnecessary fields, excepting comments + if (basefieldnum) { + int fieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6); + if (fieldnum != basefieldnum) + HUNSPELL_WARNING(stderr, "warning: line %d: bad field number\n", af->getlinenum()); + } else { + basefieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6); + } +#endif + } + + // now create SfxEntry or PfxEntry objects and use links to + // build an ordered (sorted by affix string) list + for (std::vector::iterator entry = start; entry != end; ++entry) { + if (at == 'P') { + PfxEntry * pfxptr = new PfxEntry(this,&(*entry)); + build_pfxtree(pfxptr); + } else { + SfxEntry * sfxptr = new SfxEntry(this,&(*entry)); + build_sfxtree(sfxptr); + } + } + return 0; +} + +int AffixMgr::redundant_condition(char ft, char * strip, int stripl, const char * cond, int linenum) { + int condl = strlen(cond); + int i; + int j; + int neg; + int in; + if (ft == 'P') { // prefix + if (strncmp(strip, cond, condl) == 0) return 1; + if (utf8) { + } else { + for (i = 0, j = 0; (i < stripl) && (j < condl); i++, j++) { + if (cond[j] != '[') { + if (cond[j] != strip[i]) { + HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum); + return 0; + } + } else { + neg = (cond[j+1] == '^') ? 1 : 0; + in = 0; + do { + j++; + if (strip[i] == cond[j]) in = 1; + } while ((j < (condl - 1)) && (cond[j] != ']')); + if (j == (condl - 1) && (cond[j] != ']')) { + HUNSPELL_WARNING(stderr, "error: line %d: missing ] in condition:\n%s\n", linenum, cond); + return 0; + } + if ((!neg && !in) || (neg && in)) { + HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum); + return 0; + } + } + } + if (j >= condl) return 1; + } + } else { // suffix + if ((stripl >= condl) && strcmp(strip + stripl - condl, cond) == 0) return 1; + if (utf8) { + } else { + for (i = stripl - 1, j = condl - 1; (i >= 0) && (j >= 0); i--, j--) { + if (cond[j] != ']') { + if (cond[j] != strip[i]) { + HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum); + return 0; + } + } else { + in = 0; + do { + j--; + if (strip[i] == cond[j]) in = 1; + } while ((j > 0) && (cond[j] != '[')); + if ((j == 0) && (cond[j] != '[')) { + HUNSPELL_WARNING(stderr, "error: line: %d: missing ] in condition:\n%s\n", linenum, cond); + return 0; + } + neg = (cond[j+1] == '^') ? 1 : 0; + if ((!neg && !in) || (neg && in)) { + HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum); + return 0; + } + } + } + if (j < 0) return 1; + } + } + return 0; +} diff --git a/src/myspell/affixmgr.hxx b/src/myspell/affixmgr.hxx new file mode 100644 index 0000000..989cd21 --- /dev/null +++ b/src/myspell/affixmgr.hxx @@ -0,0 +1,250 @@ +#ifndef _AFFIXMGR_HXX_ +#define _AFFIXMGR_HXX_ + +#include "hunvisapi.h" + +#include + +#include "atypes.hxx" +#include "baseaffix.hxx" +#include "hashmgr.hxx" +#include "phonet.hxx" +#include "replist.hxx" + +// check flag duplication +#define dupSFX (1 << 0) +#define dupPFX (1 << 1) + +class PfxEntry; +class SfxEntry; + +class LIBHUNSPELL_DLL_EXPORTED AffixMgr +{ + + PfxEntry * pStart[SETSIZE]; + SfxEntry * sStart[SETSIZE]; + PfxEntry * pFlag[SETSIZE]; + SfxEntry * sFlag[SETSIZE]; + HashMgr * pHMgr; + HashMgr ** alldic; + int * maxdic; + char * keystring; + char * trystring; + char * encoding; + struct cs_info * csconv; + int utf8; + int complexprefixes; + FLAG compoundflag; + FLAG compoundbegin; + FLAG compoundmiddle; + FLAG compoundend; + FLAG compoundroot; + FLAG compoundforbidflag; + FLAG compoundpermitflag; + int checkcompounddup; + int checkcompoundrep; + int checkcompoundcase; + int checkcompoundtriple; + int simplifiedtriple; + FLAG forbiddenword; + FLAG nosuggest; + FLAG nongramsuggest; + FLAG needaffix; + int cpdmin; + int numrep; + replentry * reptable; + RepList * iconvtable; + RepList * oconvtable; + int nummap; + mapentry * maptable; + int numbreak; + char ** breaktable; + int numcheckcpd; + patentry * checkcpdtable; + int simplifiedcpd; + int numdefcpd; + flagentry * defcpdtable; + phonetable * phone; + int maxngramsugs; + int maxcpdsugs; + int maxdiff; + int onlymaxdiff; + int nosplitsugs; + int sugswithdots; + int cpdwordmax; + int cpdmaxsyllable; + char * cpdvowels; + w_char * cpdvowels_utf16; + int cpdvowels_utf16_len; + char * cpdsyllablenum; + const char * pfxappnd; // BUG: not stateless + const char * sfxappnd; // BUG: not stateless + FLAG sfxflag; // BUG: not stateless + char * derived; // BUG: not stateless + SfxEntry * sfx; // BUG: not stateless + PfxEntry * pfx; // BUG: not stateless + int checknum; + char * wordchars; + unsigned short * wordchars_utf16; + int wordchars_utf16_len; + char * ignorechars; + unsigned short * ignorechars_utf16; + int ignorechars_utf16_len; + char * version; + char * lang; + int langnum; + FLAG lemma_present; + FLAG circumfix; + FLAG onlyincompound; + FLAG keepcase; + FLAG forceucase; + FLAG warn; + int forbidwarn; + FLAG substandard; + int checksharps; + int fullstrip; + + int havecontclass; // boolean variable + char contclasses[CONTSIZE]; // flags of possible continuing classes (twofold affix) + +public: + + AffixMgr(const char * affpath, HashMgr** ptr, int * md, + const char * key = NULL); + ~AffixMgr(); + struct hentry * affix_check(const char * word, int len, + const unsigned short needflag = (unsigned short) 0, + char in_compound = IN_CPD_NOT); + struct hentry * prefix_check(const char * word, int len, + char in_compound, const FLAG needflag = FLAG_NULL); + inline int isSubset(const char * s1, const char * s2); + struct hentry * prefix_check_twosfx(const char * word, int len, + char in_compound, const FLAG needflag = FLAG_NULL); + inline int isRevSubset(const char * s1, const char * end_of_s2, int len); + struct hentry * suffix_check(const char * word, int len, int sfxopts, + PfxEntry* ppfx, char ** wlst, int maxSug, int * ns, + const FLAG cclass = FLAG_NULL, const FLAG needflag = FLAG_NULL, + char in_compound = IN_CPD_NOT); + struct hentry * suffix_check_twosfx(const char * word, int len, + int sfxopts, PfxEntry* ppfx, const FLAG needflag = FLAG_NULL); + + char * affix_check_morph(const char * word, int len, + const FLAG needflag = FLAG_NULL, char in_compound = IN_CPD_NOT); + char * prefix_check_morph(const char * word, int len, + char in_compound, const FLAG needflag = FLAG_NULL); + char * suffix_check_morph (const char * word, int len, int sfxopts, + PfxEntry * ppfx, const FLAG cclass = FLAG_NULL, + const FLAG needflag = FLAG_NULL, char in_compound = IN_CPD_NOT); + + char * prefix_check_twosfx_morph(const char * word, int len, + char in_compound, const FLAG needflag = FLAG_NULL); + char * suffix_check_twosfx_morph(const char * word, int len, + int sfxopts, PfxEntry * ppfx, const FLAG needflag = FLAG_NULL); + + char * morphgen(char * ts, int wl, const unsigned short * ap, + unsigned short al, char * morph, char * targetmorph, int level); + + int expand_rootword(struct guessword * wlst, int maxn, const char * ts, + int wl, const unsigned short * ap, unsigned short al, char * bad, + int, char *); + + short get_syllable (const char * word, int wlen); + int cpdrep_check(const char * word, int len); + int cpdpat_check(const char * word, int len, hentry * r1, hentry * r2, + const char affixed); + int defcpd_check(hentry *** words, short wnum, hentry * rv, + hentry ** rwords, char all); + int cpdcase_check(const char * word, int len); + inline int candidate_check(const char * word, int len); + void setcminmax(int * cmin, int * cmax, const char * word, int len); + struct hentry * compound_check(const char * word, int len, short wordnum, + short numsyllable, short maxwordnum, short wnum, hentry ** words, + char hu_mov_rule, char is_sug, int info); + + int compound_check_morph(const char * word, int len, short wordnum, + short numsyllable, short maxwordnum, short wnum, hentry ** words, + char hu_mov_rule, char ** result, char * partresult); + + struct hentry * lookup(const char * word); + int get_numrep() const; + struct replentry * get_reptable() const; + RepList * get_iconvtable() const; + RepList * get_oconvtable() const; + struct phonetable * get_phonetable() const; + int get_nummap() const; + struct mapentry * get_maptable() const; + int get_numbreak() const; + char ** get_breaktable() const; + char * get_encoding(); + int get_langnum() const; + char * get_key_string(); + char * get_try_string() const; + const char * get_wordchars() const; + unsigned short * get_wordchars_utf16(int * len) const; + char * get_ignore() const; + unsigned short * get_ignore_utf16(int * len) const; + int get_compound() const; + FLAG get_compoundflag() const; + FLAG get_compoundbegin() const; + FLAG get_forbiddenword() const; + FLAG get_nosuggest() const; + FLAG get_nongramsuggest() const; + FLAG get_needaffix() const; + FLAG get_onlyincompound() const; + FLAG get_compoundroot() const; + FLAG get_lemma_present() const; + int get_checknum() const; + const char * get_prefix() const; + const char * get_suffix() const; + const char * get_derived() const; + const char * get_version() const; + int have_contclass() const; + int get_utf8() const; + int get_complexprefixes() const; + char * get_suffixed(char ) const; + int get_maxngramsugs() const; + int get_maxcpdsugs() const; + int get_maxdiff() const; + int get_onlymaxdiff() const; + int get_nosplitsugs() const; + int get_sugswithdots(void) const; + FLAG get_keepcase(void) const; + FLAG get_forceucase(void) const; + FLAG get_warn(void) const; + int get_forbidwarn(void) const; + int get_checksharps(void) const; + char * encode_flag(unsigned short aflag) const; + int get_fullstrip() const; + +private: + int parse_file(const char * affpath, const char * key); + int parse_flag(char * line, unsigned short * out, FileMgr * af); + int parse_num(char * line, int * out, FileMgr * af); + int parse_cpdsyllable(char * line, FileMgr * af); + int parse_reptable(char * line, FileMgr * af); + int parse_convtable(char * line, FileMgr * af, RepList ** rl, const char * keyword); + int parse_phonetable(char * line, FileMgr * af); + int parse_maptable(char * line, FileMgr * af); + int parse_breaktable(char * line, FileMgr * af); + int parse_checkcpdtable(char * line, FileMgr * af); + int parse_defcpdtable(char * line, FileMgr * af); + int parse_affix(char * line, const char at, FileMgr * af, char * dupflags); + + void reverse_condition(char *); + void debugflag(char * result, unsigned short flag); + int condlen(char *); + int encodeit(affentry &entry, char * cs); + int build_pfxtree(PfxEntry* pfxptr); + int build_sfxtree(SfxEntry* sfxptr); + int process_pfx_order(); + int process_sfx_order(); + PfxEntry * process_pfx_in_order(PfxEntry * ptr, PfxEntry * nptr); + SfxEntry * process_sfx_in_order(SfxEntry * ptr, SfxEntry * nptr); + int process_pfx_tree_to_list(); + int process_sfx_tree_to_list(); + int redundant_condition(char, char * strip, int stripl, + const char * cond, int); +}; + +#endif + diff --git a/src/myspell/atypes.hxx b/src/myspell/atypes.hxx new file mode 100644 index 0000000..df27c4d --- /dev/null +++ b/src/myspell/atypes.hxx @@ -0,0 +1,107 @@ +#ifndef _ATYPES_HXX_ +#define _ATYPES_HXX_ + +#ifndef HUNSPELL_WARNING +#include +#ifdef HUNSPELL_WARNING_ON +#define HUNSPELL_WARNING fprintf +#else +// empty inline function to switch off warnings (instead of the C99 standard variadic macros) +static inline void HUNSPELL_WARNING(FILE *, const char *, ...) {} +#endif +#endif + +// HUNSTEM def. +#define HUNSTEM + +#include "hashmgr.hxx" +#include "w_char.hxx" + +#define SETSIZE 256 +#define CONTSIZE 65536 +#define MAXWORDLEN 100 +#define MAXWORDUTF8LEN 256 + +// affentry options +#define aeXPRODUCT (1 << 0) +#define aeUTF8 (1 << 1) +#define aeALIASF (1 << 2) +#define aeALIASM (1 << 3) +#define aeLONGCOND (1 << 4) + +// compound options +#define IN_CPD_NOT 0 +#define IN_CPD_BEGIN 1 +#define IN_CPD_END 2 +#define IN_CPD_OTHER 3 + +// info options +#define SPELL_COMPOUND (1 << 0) +#define SPELL_FORBIDDEN (1 << 1) +#define SPELL_ALLCAP (1 << 2) +#define SPELL_NOCAP (1 << 3) +#define SPELL_INITCAP (1 << 4) +#define SPELL_ORIGCAP (1 << 5) +#define SPELL_WARN (1 << 6) + +#define MAXLNLEN 8192 + +#define MINCPDLEN 3 +#define MAXCOMPOUND 10 +#define MAXCONDLEN 20 +#define MAXCONDLEN_1 (MAXCONDLEN - sizeof(char *)) + +#define MAXACC 1000 + +#define FLAG unsigned short +#define FLAG_NULL 0x00 +#define FREE_FLAG(a) a = 0 + +#define TESTAFF( a, b , c ) flag_bsearch((unsigned short *) a, (unsigned short) b, c) + +struct affentry +{ + char * strip; + char * appnd; + unsigned char stripl; + unsigned char appndl; + char numconds; + char opts; + unsigned short aflag; + unsigned short * contclass; + short contclasslen; + union { + char conds[MAXCONDLEN]; + struct { + char conds1[MAXCONDLEN_1]; + char * conds2; + } l; + } c; + char * morphcode; +}; + +struct guessword { + char * word; + bool allow; + char * orig; +}; + +struct mapentry { + char ** set; + int len; +}; + +struct flagentry { + FLAG * def; + int len; +}; + +struct patentry { + char * pattern; + char * pattern2; + char * pattern3; + FLAG cond; + FLAG cond2; +}; + +#endif diff --git a/src/myspell/baseaffix.hxx b/src/myspell/baseaffix.hxx new file mode 100644 index 0000000..ed64f3d --- /dev/null +++ b/src/myspell/baseaffix.hxx @@ -0,0 +1,28 @@ +#ifndef _BASEAFF_HXX_ +#define _BASEAFF_HXX_ + +#include "hunvisapi.h" + +class LIBHUNSPELL_DLL_EXPORTED AffEntry +{ +protected: + char * appnd; + char * strip; + unsigned char appndl; + unsigned char stripl; + char numconds; + char opts; + unsigned short aflag; + union { + char conds[MAXCONDLEN]; + struct { + char conds1[MAXCONDLEN_1]; + char * conds2; + } l; + } c; + char * morphcode; + unsigned short * contclass; + short contclasslen; +}; + +#endif diff --git a/src/myspell/csutil.cxx b/src/myspell/csutil.cxx new file mode 100644 index 0000000..17134f0 --- /dev/null +++ b/src/myspell/csutil.cxx @@ -0,0 +1,5824 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include +#include + +#include "csutil.hxx" +#include "atypes.hxx" +#include "langnum.hxx" + +#ifdef OPENOFFICEORG +# include +#else +# ifndef MOZILLA_CLIENT +# include "utf_info.cxx" +# define UTF_LST_LEN (sizeof(utf_lst) / (sizeof(unicode_info))) +# endif +#endif + +#ifdef MOZILLA_CLIENT +#include "nsCOMPtr.h" +#include "nsServiceManagerUtils.h" +#include "nsIUnicodeEncoder.h" +#include "nsIUnicodeDecoder.h" +#include "nsUnicharUtils.h" +#include "nsICharsetConverterManager.h" + +static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); +#endif + +struct unicode_info2 { + char cletter; + unsigned short cupper; + unsigned short clower; +}; + +static struct unicode_info2 * utf_tbl = NULL; +static int utf_tbl_count = 0; // utf_tbl can be used by multiple Hunspell instances + +/* only UTF-16 (BMP) implementation */ +char * u16_u8(char * dest, int size, const w_char * src, int srclen) { + signed char * u8 = (signed char *)dest; + signed char * u8_max = (signed char *)(u8 + size); + const w_char * u2 = src; + const w_char * u2_max = src + srclen; + while ((u2 < u2_max) && (u8 < u8_max)) { + if (u2->h) { // > 0xFF + // XXX 4-byte haven't implemented yet. + if (u2->h >= 0x08) { // >= 0x800 (3-byte UTF-8 character) + *u8 = 0xe0 + (u2->h >> 4); + u8++; + if (u8 < u8_max) { + *u8 = 0x80 + ((u2->h & 0xf) << 2) + (u2->l >> 6); + u8++; + if (u8 < u8_max) { + *u8 = 0x80 + (u2->l & 0x3f); + u8++; + } + } + } else { // < 0x800 (2-byte UTF-8 character) + *u8 = 0xc0 + (u2->h << 2) + (u2->l >> 6); + u8++; + if (u8 < u8_max) { + *u8 = 0x80 + (u2->l & 0x3f); + u8++; + } + } + } else { // <= 0xFF + if (u2->l & 0x80) { // >0x80 (2-byte UTF-8 character) + *u8 = 0xc0 + (u2->l >> 6); + u8++; + if (u8 < u8_max) { + *u8 = 0x80 + (u2->l & 0x3f); + u8++; + } + } else { // < 0x80 (1-byte UTF-8 character) + *u8 = u2->l; + u8++; + } + } + u2++; + } + *u8 = '\0'; + return dest; +} + + +/* only UTF-16 (BMP) implementation */ +int u8_u16(w_char * dest, int size, const char * src) { + const signed char * u8 = (const signed char *)src; + w_char * u2 = dest; + w_char * u2_max = u2 + size; + + while ((u2 < u2_max) && *u8) { + switch ((*u8) & 0xf0) { + case 0x00: + case 0x10: + case 0x20: + case 0x30: + case 0x40: + case 0x50: + case 0x60: + case 0x70: { + u2->h = 0; + u2->l = *u8; + break; + } + case 0x80: + case 0x90: + case 0xa0: + case 0xb0: { + HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Unexpected continuation bytes in %ld. character position\n%s\n", static_cast(u8 - (signed char *)src), src); + u2->h = 0xff; + u2->l = 0xfd; + break; + } + case 0xc0: + case 0xd0: { // 2-byte UTF-8 codes + if ((*(u8+1) & 0xc0) == 0x80) { + u2->h = (*u8 & 0x1f) >> 2; + u2->l = (*u8 << 6) + (*(u8+1) & 0x3f); + u8++; + } else { + HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %ld. character position:\n%s\n", static_cast(u8 - (signed char *)src), src); + u2->h = 0xff; + u2->l = 0xfd; + } + break; + } + case 0xe0: { // 3-byte UTF-8 codes + if ((*(u8+1) & 0xc0) == 0x80) { + u2->h = ((*u8 & 0x0f) << 4) + ((*(u8+1) & 0x3f) >> 2); + u8++; + if ((*(u8+1) & 0xc0) == 0x80) { + u2->l = (*u8 << 6) + (*(u8+1) & 0x3f); + u8++; + } else { + HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %ld. character position:\n%s\n", static_cast(u8 - (signed char *)src), src); + u2->h = 0xff; + u2->l = 0xfd; + } + } else { + HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %ld. character position:\n%s\n", static_cast(u8 - (signed char *)src), src); + u2->h = 0xff; + u2->l = 0xfd; + } + break; + } + case 0xf0: { // 4 or more byte UTF-8 codes + HUNSPELL_WARNING(stderr, "This UTF-8 encoding can't convert to UTF-16:\n%s\n", src); + u2->h = 0xff; + u2->l = 0xfd; + return -1; + } + } + u8++; + u2++; + } + return (int)(u2 - dest); +} + +void flag_qsort(unsigned short flags[], int begin, int end) { + unsigned short reg; + if (end > begin) { + unsigned short pivot = flags[begin]; + int l = begin + 1; + int r = end; + while(l < r) { + if (flags[l] <= pivot) { + l++; + } else { + r--; + reg = flags[l]; + flags[l] = flags[r]; + flags[r] = reg; + } + } + l--; + reg = flags[begin]; + flags[begin] = flags[l]; + flags[l] = reg; + + flag_qsort(flags, begin, l); + flag_qsort(flags, r, end); + } + } + +int flag_bsearch(unsigned short flags[], unsigned short flag, int length) { + int mid; + int left = 0; + int right = length - 1; + while (left <= right) { + mid = (left + right) / 2; + if (flags[mid] == flag) return 1; + if (flag < flags[mid]) right = mid - 1; + else left = mid + 1; + } + return 0; +} + + // strip strings into token based on single char delimiter + // acts like strsep() but only uses a delim char and not + // a delim string + // default delimiter: white space characters + + char * mystrsep(char ** stringp, const char delim) + { + char * mp = *stringp; + if (*mp != '\0') { + char * dp; + if (delim) { + dp = strchr(mp, delim); + } else { + // don't use isspace() here, the string can be in some random charset + // that's way different than the locale's + for (dp = mp; (*dp && *dp != ' ' && *dp != '\t'); dp++); + if (!*dp) dp = NULL; + } + if (dp) { + *stringp = dp+1; + *dp = '\0'; + } else { + *stringp = mp + strlen(mp); + } + return mp; + } + return NULL; + } + + // replaces strdup with ansi version + char * mystrdup(const char * s) + { + char * d = NULL; + if (s) { + size_t sl = strlen(s)+1; + d = (char *) malloc(sl); + if (d) { + memcpy(d,s,sl); + } else { + HUNSPELL_WARNING(stderr, "Can't allocate memory.\n"); + } + } + return d; + } + + // strcat for limited length destination string + char * mystrcat(char * dest, const char * st, int max) { + int len; + int len2; + if (dest == NULL || st == NULL) return dest; + len = strlen(dest); + len2 = strlen(st); + if (len + len2 + 1 > max) return dest; + strcpy(dest + len, st); + return dest; + } + + // remove cross-platform text line end characters + void mychomp(char * s) + { + size_t k = strlen(s); + if ((k > 0) && ((*(s+k-1)=='\r') || (*(s+k-1)=='\n'))) *(s+k-1) = '\0'; + if ((k > 1) && (*(s+k-2) == '\r')) *(s+k-2) = '\0'; + } + + + // does an ansi strdup of the reverse of a string + char * myrevstrdup(const char * s) + { + char * d = NULL; + if (s) { + size_t sl = strlen(s); + d = (char *) malloc(sl+1); + if (d) { + const char * p = s + sl - 1; + char * q = d; + while (p >= s) *q++ = *p--; + *q = '\0'; + } else { + HUNSPELL_WARNING(stderr, "Can't allocate memory.\n"); + } + } + return d; + } + +// break text to lines +// return number of lines +int line_tok(const char * text, char *** lines, char breakchar) { + int linenum = 0; + if (!text) { + return linenum; + } + char * dup = mystrdup(text); + char * p = strchr(dup, breakchar); + while (p) { + linenum++; + *p = '\0'; + p++; + p = strchr(p, breakchar); + } + linenum++; + *lines = (char **) malloc(linenum * sizeof(char *)); + if (!(*lines)) { + free(dup); + return 0; + } + + p = dup; + int l = 0; + for (int i = 0; i < linenum; i++) { + if (*p != '\0') { + (*lines)[l] = mystrdup(p); + if (!(*lines)[l]) { + for (i = 0; i < l; i++) free((*lines)[i]); + free(dup); + return 0; + } + l++; + } + p += strlen(p) + 1; + } + free(dup); + if (!l) free(*lines); + return l; +} + +// uniq line in place +char * line_uniq(char * text, char breakchar) { + char ** lines; + int linenum = line_tok(text, &lines, breakchar); + int i; + strcpy(text, lines[0]); + for ( i = 1; i < linenum; i++ ) { + int dup = 0; + for (int j = 0; j < i; j++) { + if (strcmp(lines[i], lines[j]) == 0) dup = 1; + } + if (!dup) { + if ((i > 1) || (*(lines[0]) != '\0')) { + sprintf(text + strlen(text), "%c", breakchar); + } + strcat(text, lines[i]); + } + } + for ( i = 0; i < linenum; i++ ) { + if (lines[i]) free(lines[i]); + } + if (lines) free(lines); + return text; +} + +// uniq and boundary for compound analysis: "1\n\2\n\1" -> " ( \1 | \2 ) " +char * line_uniq_app(char ** text, char breakchar) { + if (!strchr(*text, breakchar)) { + return *text; + } + + char ** lines; + int i; + int linenum = line_tok(*text, &lines, breakchar); + int dup = 0; + for (i = 0; i < linenum; i++) { + for (int j = 0; j < (i - 1); j++) { + if (strcmp(lines[i], lines[j]) == 0) { + *(lines[i]) = '\0'; + dup++; + break; + } + } + } + if ((linenum - dup) == 1) { + strcpy(*text, lines[0]); + freelist(&lines, linenum); + return *text; + } + char * newtext = (char *) malloc(strlen(*text) + 2 * linenum + 3 + 1); + if (newtext) { + free(*text); + *text = newtext; + } else { + freelist(&lines, linenum); + return *text; + } + strcpy(*text," ( "); + for (i = 0; i < linenum; i++) if (*(lines[i])) { + sprintf(*text + strlen(*text), "%s%s", lines[i], " | "); + } + (*text)[strlen(*text) - 2] = ')'; // " ) " + freelist(&lines, linenum); + return *text; +} + + // append s to ends of every lines in text + void strlinecat(char * dest, const char * s) + { + char * dup = mystrdup(dest); + char * source = dup; + int len = strlen(s); + if (dup) { + while (*source) { + if (*source == '\n') { + strncpy(dest, s, len); + dest += len; + } + *dest = *source; + source++; dest++; + } + strcpy(dest, s); + free(dup); + } + } + +// change \n to char c +char * tr(char * text, char oldc, char newc) { + char * p; + for (p = text; *p; p++) if (*p == oldc) *p = newc; + return text; +} + +// morphcmp(): compare MORPH_DERI_SFX, MORPH_INFL_SFX and MORPH_TERM_SFX fields +// in the first line of the inputs +// return 0, if inputs equal +// return 1, if inputs may equal with a secondary suffix +// otherwise return -1 +int morphcmp(const char * s, const char * t) +{ + int se = 0; + int te = 0; + const char * sl; + const char * tl; + const char * olds; + const char * oldt; + if (!s || !t) return 1; + olds = s; + sl = strchr(s, '\n'); + s = strstr(s, MORPH_DERI_SFX); + if (!s || (sl && sl < s)) s = strstr(olds, MORPH_INFL_SFX); + if (!s || (sl && sl < s)) { + s= strstr(olds, MORPH_TERM_SFX); + olds = NULL; + } + oldt = t; + tl = strchr(t, '\n'); + t = strstr(t, MORPH_DERI_SFX); + if (!t || (tl && tl < t)) t = strstr(oldt, MORPH_INFL_SFX); + if (!t || (tl && tl < t)) { + t = strstr(oldt, MORPH_TERM_SFX); + oldt = NULL; + } + while (s && t && (!sl || sl > s) && (!tl || tl > t)) { + s += MORPH_TAG_LEN; + t += MORPH_TAG_LEN; + se = 0; + te = 0; + while ((*s == *t) && !se && !te) { + s++; + t++; + switch(*s) { + case ' ': + case '\n': + case '\t': + case '\0': se = 1; + } + switch(*t) { + case ' ': + case '\n': + case '\t': + case '\0': te = 1; + } + } + if (!se || !te) { + // not terminal suffix difference + if (olds) return -1; + return 1; + } + olds = s; + s = strstr(s, MORPH_DERI_SFX); + if (!s || (sl && sl < s)) s = strstr(olds, MORPH_INFL_SFX); + if (!s || (sl && sl < s)) { + s = strstr(olds, MORPH_TERM_SFX); + olds = NULL; + } + oldt = t; + t = strstr(t, MORPH_DERI_SFX); + if (!t || (tl && tl < t)) t = strstr(oldt, MORPH_INFL_SFX); + if (!t || (tl && tl < t)) { + t = strstr(oldt, MORPH_TERM_SFX); + oldt = NULL; + } + } + if (!s && !t && se && te) return 0; + return 1; +} + +int get_sfxcount(const char * morph) +{ + if (!morph || !*morph) return 0; + int n = 0; + const char * old = morph; + morph = strstr(morph, MORPH_DERI_SFX); + if (!morph) morph = strstr(old, MORPH_INFL_SFX); + if (!morph) morph = strstr(old, MORPH_TERM_SFX); + while (morph) { + n++; + old = morph; + morph = strstr(morph + 1, MORPH_DERI_SFX); + if (!morph) morph = strstr(old + 1, MORPH_INFL_SFX); + if (!morph) morph = strstr(old + 1, MORPH_TERM_SFX); + } + return n; +} + + +int fieldlen(const char * r) +{ + int n = 0; + while (r && *r != ' ' && *r != '\t' && *r != '\0' && *r != '\n') { + r++; + n++; + } + return n; +} + +char * copy_field(char * dest, const char * morph, const char * var) +{ + if (!morph) return NULL; + const char * beg = strstr(morph, var); + if (beg) { + char * d = dest; + for (beg += MORPH_TAG_LEN; *beg != ' ' && *beg != '\t' && + *beg != '\n' && *beg != '\0'; d++, beg++) { + *d = *beg; + } + *d = '\0'; + return dest; + } + return NULL; +} + +char * mystrrep(char * word, const char * pat, const char * rep) { + char * pos = strstr(word, pat); + if (pos) { + int replen = strlen(rep); + int patlen = strlen(pat); + while (pos) { + if (replen < patlen) { + char * end = word + strlen(word); + char * next = pos + replen; + char * prev = pos + strlen(pat); + for (; prev < end; *next = *prev, prev++, next++); + *next = '\0'; + } else if (replen > patlen) { + char * end = pos + patlen; + char * next = word + strlen(word) + replen - patlen; + char * prev = next - replen + patlen; + for (; prev >= end; *next = *prev, prev--, next--); + } + strncpy(pos, rep, replen); + pos = strstr(word, pat); + } + } + return word; +} + + // reverse word + int reverseword(char * word) { + char r; + for (char * dest = word + strlen(word) - 1; word < dest; word++, dest--) { + r=*word; + *word = *dest; + *dest = r; + } + return 0; + } + + // reverse word (error: 1) + int reverseword_utf(char * word) { + w_char w[MAXWORDLEN]; + w_char * p; + w_char r; + int l = u8_u16(w, MAXWORDLEN, word); + if (l == -1) return 1; + p = w; + for (w_char * dest = w + l - 1; p < dest; p++, dest--) { + r=*p; + *p = *dest; + *dest = r; + } + u16_u8(word, MAXWORDUTF8LEN, w, l); + return 0; + } + + int uniqlist(char ** list, int n) { + int i; + if (n < 2) return n; + for (i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + if (list[j] && list[i] && (strcmp(list[j], list[i]) == 0)) { + free(list[i]); + list[i] = NULL; + break; + } + } + } + int m = 1; + for (i = 1; i < n; i++) if (list[i]) { + list[m] = list[i]; + m++; + } + return m; + } + + void freelist(char *** list, int n) { + if (list && *list && n > 0) { + for (int i = 0; i < n; i++) if ((*list)[i]) free((*list)[i]); + free(*list); + *list = NULL; + } + } + + // convert null terminated string to all caps + void mkallcap(char * p, const struct cs_info * csconv) + { + while (*p != '\0') { + *p = csconv[((unsigned char) *p)].cupper; + p++; + } + } + + // convert null terminated string to all little + void mkallsmall(char * p, const struct cs_info * csconv) + { + while (*p != '\0') { + *p = csconv[((unsigned char) *p)].clower; + p++; + } + } + +void mkallsmall_utf(w_char * u, int nc, int langnum) { + for (int i = 0; i < nc; i++) { + unsigned short idx = (u[i].h << 8) + u[i].l; + if (idx != unicodetolower(idx, langnum)) { + u[i].h = (unsigned char) (unicodetolower(idx, langnum) >> 8); + u[i].l = (unsigned char) (unicodetolower(idx, langnum) & 0x00FF); + } + } +} + +void mkallcap_utf(w_char * u, int nc, int langnum) { + for (int i = 0; i < nc; i++) { + unsigned short idx = (u[i].h << 8) + u[i].l; + if (idx != unicodetoupper(idx, langnum)) { + u[i].h = (unsigned char) (unicodetoupper(idx, langnum) >> 8); + u[i].l = (unsigned char) (unicodetoupper(idx, langnum) & 0x00FF); + } + } +} + + // convert null terminated string to have initial capital + void mkinitcap(char * p, const struct cs_info * csconv) + { + if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper; + } + + // conversion function for protected memory + void store_pointer(char * dest, char * source) + { + memcpy(dest, &source, sizeof(char *)); + } + + // conversion function for protected memory + char * get_stored_pointer(const char * s) + { + char * p; + memcpy(&p, s, sizeof(char *)); + return p; + } + +#ifndef MOZILLA_CLIENT + // convert null terminated string to all caps using encoding + void enmkallcap(char * d, const char * p, const char * encoding) + + { + struct cs_info * csconv = get_current_cs(encoding); + while (*p != '\0') { + *d++ = csconv[((unsigned char) *p)].cupper; + p++; + } + *d = '\0'; + } + + // convert null terminated string to all little using encoding + void enmkallsmall(char * d, const char * p, const char * encoding) + { + struct cs_info * csconv = get_current_cs(encoding); + while (*p != '\0') { + *d++ = csconv[((unsigned char) *p)].clower; + p++; + } + *d = '\0'; + } + + // convert null terminated string to have initial capital using encoding + void enmkinitcap(char * d, const char * p, const char * encoding) + { + struct cs_info * csconv = get_current_cs(encoding); + memcpy(d,p,(strlen(p)+1)); + if (*p != '\0') *d= csconv[((unsigned char)*p)].cupper; + } + +// these are simple character mappings for the +// encodings supported +// supplying isupper, tolower, and toupper + +static struct cs_info iso1_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0xfd, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0xdd }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xff } +}; + + +static struct cs_info iso2_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x01, 0xb1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x01, 0xb3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x01, 0xb5, 0xa5 }, +{ 0x01, 0xb6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x01, 0xb9, 0xa9 }, +{ 0x01, 0xba, 0xaa }, +{ 0x01, 0xbb, 0xab }, +{ 0x01, 0xbc, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x01, 0xbe, 0xae }, +{ 0x01, 0xbf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xa1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xa3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xa5 }, +{ 0x00, 0xb6, 0xa6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xa9 }, +{ 0x00, 0xba, 0xaa }, +{ 0x00, 0xbb, 0xab }, +{ 0x00, 0xbc, 0xac }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xae }, +{ 0x00, 0xbf, 0xaf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0xfd, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0xdd }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xff } +}; + + +static struct cs_info iso3_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x01, 0xb1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x01, 0xb6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x01, 0x69, 0xa9 }, +{ 0x01, 0xba, 0xaa }, +{ 0x01, 0xbb, 0xab }, +{ 0x01, 0xbc, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x01, 0xbf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xa1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xa6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0x49 }, +{ 0x00, 0xba, 0xaa }, +{ 0x00, 0xbb, 0xab }, +{ 0x00, 0xbc, 0xac }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xaf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x00, 0xc3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x00, 0xd0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0xfd, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xe3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xf0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0xdd }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info iso4_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x01, 0xb1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x01, 0xb3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x01, 0xb5, 0xa5 }, +{ 0x01, 0xb6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x01, 0xb9, 0xa9 }, +{ 0x01, 0xba, 0xaa }, +{ 0x01, 0xbb, 0xab }, +{ 0x01, 0xbc, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x01, 0xbe, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xa1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xa3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xa5 }, +{ 0x00, 0xb6, 0xa6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xa9 }, +{ 0x00, 0xba, 0xaa }, +{ 0x00, 0xbb, 0xab }, +{ 0x00, 0xbc, 0xac }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xae }, +{ 0x00, 0xbf, 0xbf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0xfd, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0xdd }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info iso5_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x01, 0xf1, 0xa1 }, +{ 0x01, 0xf2, 0xa2 }, +{ 0x01, 0xf3, 0xa3 }, +{ 0x01, 0xf4, 0xa4 }, +{ 0x01, 0xf5, 0xa5 }, +{ 0x01, 0xf6, 0xa6 }, +{ 0x01, 0xf7, 0xa7 }, +{ 0x01, 0xf8, 0xa8 }, +{ 0x01, 0xf9, 0xa9 }, +{ 0x01, 0xfa, 0xaa }, +{ 0x01, 0xfb, 0xab }, +{ 0x01, 0xfc, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x01, 0xfe, 0xae }, +{ 0x01, 0xff, 0xaf }, +{ 0x01, 0xd0, 0xb0 }, +{ 0x01, 0xd1, 0xb1 }, +{ 0x01, 0xd2, 0xb2 }, +{ 0x01, 0xd3, 0xb3 }, +{ 0x01, 0xd4, 0xb4 }, +{ 0x01, 0xd5, 0xb5 }, +{ 0x01, 0xd6, 0xb6 }, +{ 0x01, 0xd7, 0xb7 }, +{ 0x01, 0xd8, 0xb8 }, +{ 0x01, 0xd9, 0xb9 }, +{ 0x01, 0xda, 0xba }, +{ 0x01, 0xdb, 0xbb }, +{ 0x01, 0xdc, 0xbc }, +{ 0x01, 0xdd, 0xbd }, +{ 0x01, 0xde, 0xbe }, +{ 0x01, 0xdf, 0xbf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x00, 0xd0, 0xb0 }, +{ 0x00, 0xd1, 0xb1 }, +{ 0x00, 0xd2, 0xb2 }, +{ 0x00, 0xd3, 0xb3 }, +{ 0x00, 0xd4, 0xb4 }, +{ 0x00, 0xd5, 0xb5 }, +{ 0x00, 0xd6, 0xb6 }, +{ 0x00, 0xd7, 0xb7 }, +{ 0x00, 0xd8, 0xb8 }, +{ 0x00, 0xd9, 0xb9 }, +{ 0x00, 0xda, 0xba }, +{ 0x00, 0xdb, 0xbb }, +{ 0x00, 0xdc, 0xbc }, +{ 0x00, 0xdd, 0xbd }, +{ 0x00, 0xde, 0xbe }, +{ 0x00, 0xdf, 0xbf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xf0 }, +{ 0x00, 0xf1, 0xa1 }, +{ 0x00, 0xf2, 0xa2 }, +{ 0x00, 0xf3, 0xa3 }, +{ 0x00, 0xf4, 0xa4 }, +{ 0x00, 0xf5, 0xa5 }, +{ 0x00, 0xf6, 0xa6 }, +{ 0x00, 0xf7, 0xa7 }, +{ 0x00, 0xf8, 0xa8 }, +{ 0x00, 0xf9, 0xa9 }, +{ 0x00, 0xfa, 0xaa }, +{ 0x00, 0xfb, 0xab }, +{ 0x00, 0xfc, 0xac }, +{ 0x00, 0xfd, 0xfd }, +{ 0x00, 0xfe, 0xae }, +{ 0x00, 0xff, 0xaf } +}; + +static struct cs_info iso6_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x00, 0xc0, 0xc0 }, +{ 0x00, 0xc1, 0xc1 }, +{ 0x00, 0xc2, 0xc2 }, +{ 0x00, 0xc3, 0xc3 }, +{ 0x00, 0xc4, 0xc4 }, +{ 0x00, 0xc5, 0xc5 }, +{ 0x00, 0xc6, 0xc6 }, +{ 0x00, 0xc7, 0xc7 }, +{ 0x00, 0xc8, 0xc8 }, +{ 0x00, 0xc9, 0xc9 }, +{ 0x00, 0xca, 0xca }, +{ 0x00, 0xcb, 0xcb }, +{ 0x00, 0xcc, 0xcc }, +{ 0x00, 0xcd, 0xcd }, +{ 0x00, 0xce, 0xce }, +{ 0x00, 0xcf, 0xcf }, +{ 0x00, 0xd0, 0xd0 }, +{ 0x00, 0xd1, 0xd1 }, +{ 0x00, 0xd2, 0xd2 }, +{ 0x00, 0xd3, 0xd3 }, +{ 0x00, 0xd4, 0xd4 }, +{ 0x00, 0xd5, 0xd5 }, +{ 0x00, 0xd6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x00, 0xd8, 0xd8 }, +{ 0x00, 0xd9, 0xd9 }, +{ 0x00, 0xda, 0xda }, +{ 0x00, 0xdb, 0xdb }, +{ 0x00, 0xdc, 0xdc }, +{ 0x00, 0xdd, 0xdd }, +{ 0x00, 0xde, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xe0 }, +{ 0x00, 0xe1, 0xe1 }, +{ 0x00, 0xe2, 0xe2 }, +{ 0x00, 0xe3, 0xe3 }, +{ 0x00, 0xe4, 0xe4 }, +{ 0x00, 0xe5, 0xe5 }, +{ 0x00, 0xe6, 0xe6 }, +{ 0x00, 0xe7, 0xe7 }, +{ 0x00, 0xe8, 0xe8 }, +{ 0x00, 0xe9, 0xe9 }, +{ 0x00, 0xea, 0xea }, +{ 0x00, 0xeb, 0xeb }, +{ 0x00, 0xec, 0xec }, +{ 0x00, 0xed, 0xed }, +{ 0x00, 0xee, 0xee }, +{ 0x00, 0xef, 0xef }, +{ 0x00, 0xf0, 0xf0 }, +{ 0x00, 0xf1, 0xf1 }, +{ 0x00, 0xf2, 0xf2 }, +{ 0x00, 0xf3, 0xf3 }, +{ 0x00, 0xf4, 0xf4 }, +{ 0x00, 0xf5, 0xf5 }, +{ 0x00, 0xf6, 0xf6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xf8 }, +{ 0x00, 0xf9, 0xf9 }, +{ 0x00, 0xfa, 0xfa }, +{ 0x00, 0xfb, 0xfb }, +{ 0x00, 0xfc, 0xfc }, +{ 0x00, 0xfd, 0xfd }, +{ 0x00, 0xfe, 0xfe }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info iso7_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x01, 0xdc, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x01, 0xdd, 0xb8 }, +{ 0x01, 0xde, 0xb9 }, +{ 0x01, 0xdf, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x01, 0xfc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x01, 0xfd, 0xbe }, +{ 0x01, 0xfe, 0xbf }, +{ 0x00, 0xc0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x00, 0xd2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x01, 0xf7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x00, 0xdc, 0xb6 }, +{ 0x00, 0xdd, 0xb8 }, +{ 0x00, 0xde, 0xb9 }, +{ 0x00, 0xdf, 0xba }, +{ 0x00, 0xe0, 0xe0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd3 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xd7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xbc }, +{ 0x00, 0xfd, 0xbe }, +{ 0x00, 0xfe, 0xbf }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info iso8_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x00, 0xc0, 0xc0 }, +{ 0x00, 0xc1, 0xc1 }, +{ 0x00, 0xc2, 0xc2 }, +{ 0x00, 0xc3, 0xc3 }, +{ 0x00, 0xc4, 0xc4 }, +{ 0x00, 0xc5, 0xc5 }, +{ 0x00, 0xc6, 0xc6 }, +{ 0x00, 0xc7, 0xc7 }, +{ 0x00, 0xc8, 0xc8 }, +{ 0x00, 0xc9, 0xc9 }, +{ 0x00, 0xca, 0xca }, +{ 0x00, 0xcb, 0xcb }, +{ 0x00, 0xcc, 0xcc }, +{ 0x00, 0xcd, 0xcd }, +{ 0x00, 0xce, 0xce }, +{ 0x00, 0xcf, 0xcf }, +{ 0x00, 0xd0, 0xd0 }, +{ 0x00, 0xd1, 0xd1 }, +{ 0x00, 0xd2, 0xd2 }, +{ 0x00, 0xd3, 0xd3 }, +{ 0x00, 0xd4, 0xd4 }, +{ 0x00, 0xd5, 0xd5 }, +{ 0x00, 0xd6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x00, 0xd8, 0xd8 }, +{ 0x00, 0xd9, 0xd9 }, +{ 0x00, 0xda, 0xda }, +{ 0x00, 0xdb, 0xdb }, +{ 0x00, 0xdc, 0xdc }, +{ 0x00, 0xdd, 0xdd }, +{ 0x00, 0xde, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xe0 }, +{ 0x00, 0xe1, 0xe1 }, +{ 0x00, 0xe2, 0xe2 }, +{ 0x00, 0xe3, 0xe3 }, +{ 0x00, 0xe4, 0xe4 }, +{ 0x00, 0xe5, 0xe5 }, +{ 0x00, 0xe6, 0xe6 }, +{ 0x00, 0xe7, 0xe7 }, +{ 0x00, 0xe8, 0xe8 }, +{ 0x00, 0xe9, 0xe9 }, +{ 0x00, 0xea, 0xea }, +{ 0x00, 0xeb, 0xeb }, +{ 0x00, 0xec, 0xec }, +{ 0x00, 0xed, 0xed }, +{ 0x00, 0xee, 0xee }, +{ 0x00, 0xef, 0xef }, +{ 0x00, 0xf0, 0xf0 }, +{ 0x00, 0xf1, 0xf1 }, +{ 0x00, 0xf2, 0xf2 }, +{ 0x00, 0xf3, 0xf3 }, +{ 0x00, 0xf4, 0xf4 }, +{ 0x00, 0xf5, 0xf5 }, +{ 0x00, 0xf6, 0xf6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xf8 }, +{ 0x00, 0xf9, 0xf9 }, +{ 0x00, 0xfa, 0xfa }, +{ 0x00, 0xfb, 0xfb }, +{ 0x00, 0xfc, 0xfc }, +{ 0x00, 0xfd, 0xfd }, +{ 0x00, 0xfe, 0xfe }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info iso9_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0xfd, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0xdd }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0x69, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0x49 }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info iso10_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x00, 0xc0, 0xc0 }, +{ 0x00, 0xc1, 0xc1 }, +{ 0x00, 0xc2, 0xc2 }, +{ 0x00, 0xc3, 0xc3 }, +{ 0x00, 0xc4, 0xc4 }, +{ 0x00, 0xc5, 0xc5 }, +{ 0x00, 0xc6, 0xc6 }, +{ 0x00, 0xc7, 0xc7 }, +{ 0x00, 0xc8, 0xc8 }, +{ 0x00, 0xc9, 0xc9 }, +{ 0x00, 0xca, 0xca }, +{ 0x00, 0xcb, 0xcb }, +{ 0x00, 0xcc, 0xcc }, +{ 0x00, 0xcd, 0xcd }, +{ 0x00, 0xce, 0xce }, +{ 0x00, 0xcf, 0xcf }, +{ 0x00, 0xd0, 0xd0 }, +{ 0x00, 0xd1, 0xd1 }, +{ 0x00, 0xd2, 0xd2 }, +{ 0x00, 0xd3, 0xd3 }, +{ 0x00, 0xd4, 0xd4 }, +{ 0x00, 0xd5, 0xd5 }, +{ 0x00, 0xd6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x00, 0xd8, 0xd8 }, +{ 0x00, 0xd9, 0xd9 }, +{ 0x00, 0xda, 0xda }, +{ 0x00, 0xdb, 0xdb }, +{ 0x00, 0xdc, 0xdc }, +{ 0x00, 0xdd, 0xdd }, +{ 0x00, 0xde, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xe0 }, +{ 0x00, 0xe1, 0xe1 }, +{ 0x00, 0xe2, 0xe2 }, +{ 0x00, 0xe3, 0xe3 }, +{ 0x00, 0xe4, 0xe4 }, +{ 0x00, 0xe5, 0xe5 }, +{ 0x00, 0xe6, 0xe6 }, +{ 0x00, 0xe7, 0xe7 }, +{ 0x00, 0xe8, 0xe8 }, +{ 0x00, 0xe9, 0xe9 }, +{ 0x00, 0xea, 0xea }, +{ 0x00, 0xeb, 0xeb }, +{ 0x00, 0xec, 0xec }, +{ 0x00, 0xed, 0xed }, +{ 0x00, 0xee, 0xee }, +{ 0x00, 0xef, 0xef }, +{ 0x00, 0xf0, 0xf0 }, +{ 0x00, 0xf1, 0xf1 }, +{ 0x00, 0xf2, 0xf2 }, +{ 0x00, 0xf3, 0xf3 }, +{ 0x00, 0xf4, 0xf4 }, +{ 0x00, 0xf5, 0xf5 }, +{ 0x00, 0xf6, 0xf6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xf8 }, +{ 0x00, 0xf9, 0xf9 }, +{ 0x00, 0xfa, 0xfa }, +{ 0x00, 0xfb, 0xfb }, +{ 0x00, 0xfc, 0xfc }, +{ 0x00, 0xfd, 0xfd }, +{ 0x00, 0xfe, 0xfe }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info koi8r_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xb3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x01, 0xa3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x00, 0xc0, 0xe0 }, +{ 0x00, 0xc1, 0xe1 }, +{ 0x00, 0xc2, 0xe2 }, +{ 0x00, 0xc3, 0xe3 }, +{ 0x00, 0xc4, 0xe4 }, +{ 0x00, 0xc5, 0xe5 }, +{ 0x00, 0xc6, 0xe6 }, +{ 0x00, 0xc7, 0xe7 }, +{ 0x00, 0xc8, 0xe8 }, +{ 0x00, 0xc9, 0xe9 }, +{ 0x00, 0xca, 0xea }, +{ 0x00, 0xcb, 0xeb }, +{ 0x00, 0xcc, 0xec }, +{ 0x00, 0xcd, 0xed }, +{ 0x00, 0xce, 0xee }, +{ 0x00, 0xcf, 0xef }, +{ 0x00, 0xd0, 0xf0 }, +{ 0x00, 0xd1, 0xf1 }, +{ 0x00, 0xd2, 0xf2 }, +{ 0x00, 0xd3, 0xf3 }, +{ 0x00, 0xd4, 0xf4 }, +{ 0x00, 0xd5, 0xf5 }, +{ 0x00, 0xd6, 0xf6 }, +{ 0x00, 0xd7, 0xf7 }, +{ 0x00, 0xd8, 0xf8 }, +{ 0x00, 0xd9, 0xf9 }, +{ 0x00, 0xda, 0xfa }, +{ 0x00, 0xdb, 0xfb }, +{ 0x00, 0xdc, 0xfc }, +{ 0x00, 0xdd, 0xfd }, +{ 0x00, 0xde, 0xfe }, +{ 0x00, 0xdf, 0xff }, +{ 0x01, 0xc0, 0xe0 }, +{ 0x01, 0xc1, 0xe1 }, +{ 0x01, 0xc2, 0xe2 }, +{ 0x01, 0xc3, 0xe3 }, +{ 0x01, 0xc4, 0xe4 }, +{ 0x01, 0xc5, 0xe5 }, +{ 0x01, 0xc6, 0xe6 }, +{ 0x01, 0xc7, 0xe7 }, +{ 0x01, 0xc8, 0xe8 }, +{ 0x01, 0xc9, 0xe9 }, +{ 0x01, 0xca, 0xea }, +{ 0x01, 0xcb, 0xeb }, +{ 0x01, 0xcc, 0xec }, +{ 0x01, 0xcd, 0xed }, +{ 0x01, 0xce, 0xee }, +{ 0x01, 0xcf, 0xef }, +{ 0x01, 0xd0, 0xf0 }, +{ 0x01, 0xd1, 0xf1 }, +{ 0x01, 0xd2, 0xf2 }, +{ 0x01, 0xd3, 0xf3 }, +{ 0x01, 0xd4, 0xf4 }, +{ 0x01, 0xd5, 0xf5 }, +{ 0x01, 0xd6, 0xf6 }, +{ 0x01, 0xd7, 0xf7 }, +{ 0x01, 0xd8, 0xf8 }, +{ 0x01, 0xd9, 0xf9 }, +{ 0x01, 0xda, 0xfa }, +{ 0x01, 0xdb, 0xfb }, +{ 0x01, 0xdc, 0xfc }, +{ 0x01, 0xdd, 0xfd }, +{ 0x01, 0xde, 0xfe }, +{ 0x01, 0xdf, 0xff } +}; + +static struct cs_info koi8u_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xb3 }, +{ 0x00, 0xa4, 0xb4 }, /* ie */ +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xb6 }, /* i */ +{ 0x00, 0xa7, 0xb7 }, /* ii */ +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xbd }, /* g'' */ +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x01, 0xa3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, /* IE */ +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, /* I */ +{ 0x00, 0xb7, 0xb7 }, /* II */ +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x00, 0xc0, 0xe0 }, +{ 0x00, 0xc1, 0xe1 }, +{ 0x00, 0xc2, 0xe2 }, +{ 0x00, 0xc3, 0xe3 }, +{ 0x00, 0xc4, 0xe4 }, +{ 0x00, 0xc5, 0xe5 }, +{ 0x00, 0xc6, 0xe6 }, +{ 0x00, 0xc7, 0xe7 }, +{ 0x00, 0xc8, 0xe8 }, +{ 0x00, 0xc9, 0xe9 }, +{ 0x00, 0xca, 0xea }, +{ 0x00, 0xcb, 0xeb }, +{ 0x00, 0xcc, 0xec }, +{ 0x00, 0xcd, 0xed }, +{ 0x00, 0xce, 0xee }, +{ 0x00, 0xcf, 0xef }, +{ 0x00, 0xd0, 0xf0 }, +{ 0x00, 0xd1, 0xf1 }, +{ 0x00, 0xd2, 0xf2 }, +{ 0x00, 0xd3, 0xf3 }, +{ 0x00, 0xd4, 0xf4 }, +{ 0x00, 0xd5, 0xf5 }, +{ 0x00, 0xd6, 0xf6 }, +{ 0x00, 0xd7, 0xf7 }, +{ 0x00, 0xd8, 0xf8 }, +{ 0x00, 0xd9, 0xf9 }, +{ 0x00, 0xda, 0xfa }, +{ 0x00, 0xdb, 0xfb }, +{ 0x00, 0xdc, 0xfc }, +{ 0x00, 0xdd, 0xfd }, +{ 0x00, 0xde, 0xfe }, +{ 0x00, 0xdf, 0xff }, +{ 0x01, 0xc0, 0xe0 }, +{ 0x01, 0xc1, 0xe1 }, +{ 0x01, 0xc2, 0xe2 }, +{ 0x01, 0xc3, 0xe3 }, +{ 0x01, 0xc4, 0xe4 }, +{ 0x01, 0xc5, 0xe5 }, +{ 0x01, 0xc6, 0xe6 }, +{ 0x01, 0xc7, 0xe7 }, +{ 0x01, 0xc8, 0xe8 }, +{ 0x01, 0xc9, 0xe9 }, +{ 0x01, 0xca, 0xea }, +{ 0x01, 0xcb, 0xeb }, +{ 0x01, 0xcc, 0xec }, +{ 0x01, 0xcd, 0xed }, +{ 0x01, 0xce, 0xee }, +{ 0x01, 0xcf, 0xef }, +{ 0x01, 0xd0, 0xf0 }, +{ 0x01, 0xd1, 0xf1 }, +{ 0x01, 0xd2, 0xf2 }, +{ 0x01, 0xd3, 0xf3 }, +{ 0x01, 0xd4, 0xf4 }, +{ 0x01, 0xd5, 0xf5 }, +{ 0x01, 0xd6, 0xf6 }, +{ 0x01, 0xd7, 0xf7 }, +{ 0x01, 0xd8, 0xf8 }, +{ 0x01, 0xd9, 0xf9 }, +{ 0x01, 0xda, 0xfa }, +{ 0x01, 0xdb, 0xfb }, +{ 0x01, 0xdc, 0xfc }, +{ 0x01, 0xdd, 0xfd }, +{ 0x01, 0xde, 0xfe }, +{ 0x01, 0xdf, 0xff } +}; + +static struct cs_info cp1251_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x01, 0x90, 0x80 }, +{ 0x01, 0x83, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x81 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x01, 0x9a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x01, 0x9c, 0x8c }, +{ 0x01, 0x9d, 0x8d }, +{ 0x01, 0x9e, 0x8e }, +{ 0x01, 0x9f, 0x8f }, +{ 0x00, 0x90, 0x80 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x8a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x8c }, +{ 0x00, 0x9d, 0x8d }, +{ 0x00, 0x9e, 0x8e }, +{ 0x00, 0x9f, 0x8f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x01, 0xa2, 0xa1 }, +{ 0x00, 0xa2, 0xa1 }, +{ 0x01, 0xbc, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x01, 0xb4, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x01, 0xb8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x01, 0xba, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x01, 0xbf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x01, 0xb3, 0xb2 }, +{ 0x00, 0xb3, 0xb2 }, +{ 0x00, 0xb4, 0xa5 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xa8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xaa }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xa3 }, +{ 0x01, 0xbe, 0xbd }, +{ 0x00, 0xbe, 0xbd }, +{ 0x00, 0xbf, 0xaf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x01, 0xf7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0xfd, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x01, 0xff, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xd7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0xdd }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xdf } +}; + +static struct cs_info iso13_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0A, 0x0A }, +{ 0x00, 0x0B, 0x0B }, +{ 0x00, 0x0C, 0x0C }, +{ 0x00, 0x0D, 0x0D }, +{ 0x00, 0x0E, 0x0E }, +{ 0x00, 0x0F, 0x0F }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1A, 0x1A }, +{ 0x00, 0x1B, 0x1B }, +{ 0x00, 0x1C, 0x1C }, +{ 0x00, 0x1D, 0x1D }, +{ 0x00, 0x1E, 0x1E }, +{ 0x00, 0x1F, 0x1F }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2A, 0x2A }, +{ 0x00, 0x2B, 0x2B }, +{ 0x00, 0x2C, 0x2C }, +{ 0x00, 0x2D, 0x2D }, +{ 0x00, 0x2E, 0x2E }, +{ 0x00, 0x2F, 0x2F }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3A, 0x3A }, +{ 0x00, 0x3B, 0x3B }, +{ 0x00, 0x3C, 0x3C }, +{ 0x00, 0x3D, 0x3D }, +{ 0x00, 0x3E, 0x3E }, +{ 0x00, 0x3F, 0x3F }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6A, 0x4A }, +{ 0x01, 0x6B, 0x4B }, +{ 0x01, 0x6C, 0x4C }, +{ 0x01, 0x6D, 0x4D }, +{ 0x01, 0x6E, 0x4E }, +{ 0x01, 0x6F, 0x4F }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7A, 0x5A }, +{ 0x00, 0x5B, 0x5B }, +{ 0x00, 0x5C, 0x5C }, +{ 0x00, 0x5D, 0x5D }, +{ 0x00, 0x5E, 0x5E }, +{ 0x00, 0x5F, 0x5F }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6A, 0x4A }, +{ 0x00, 0x6B, 0x4B }, +{ 0x00, 0x6C, 0x4C }, +{ 0x00, 0x6D, 0x4D }, +{ 0x00, 0x6E, 0x4E }, +{ 0x00, 0x6F, 0x4F }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7A, 0x5A }, +{ 0x00, 0x7B, 0x7B }, +{ 0x00, 0x7C, 0x7C }, +{ 0x00, 0x7D, 0x7D }, +{ 0x00, 0x7E, 0x7E }, +{ 0x00, 0x7F, 0x7F }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8A, 0x8A }, +{ 0x00, 0x8B, 0x8B }, +{ 0x00, 0x8C, 0x8C }, +{ 0x00, 0x8D, 0x8D }, +{ 0x00, 0x8E, 0x8E }, +{ 0x00, 0x8F, 0x8F }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9A, 0x9A }, +{ 0x00, 0x9B, 0x9B }, +{ 0x00, 0x9C, 0x9C }, +{ 0x00, 0x9D, 0x9D }, +{ 0x00, 0x9E, 0x9E }, +{ 0x00, 0x9F, 0x9F }, +{ 0x00, 0xA0, 0xA0 }, +{ 0x00, 0xA1, 0xA1 }, +{ 0x00, 0xA2, 0xA2 }, +{ 0x00, 0xA3, 0xA3 }, +{ 0x00, 0xA4, 0xA4 }, +{ 0x00, 0xA5, 0xA5 }, +{ 0x00, 0xA6, 0xA6 }, +{ 0x00, 0xA7, 0xA7 }, +{ 0x01, 0xB8, 0xA8 }, +{ 0x00, 0xA9, 0xA9 }, +{ 0x01, 0xBA, 0xAA }, +{ 0x00, 0xAB, 0xAB }, +{ 0x00, 0xAC, 0xAC }, +{ 0x00, 0xAD, 0xAD }, +{ 0x00, 0xAE, 0xAE }, +{ 0x01, 0xBF, 0xAF }, +{ 0x00, 0xB0, 0xB0 }, +{ 0x00, 0xB1, 0xB1 }, +{ 0x00, 0xB2, 0xB2 }, +{ 0x00, 0xB3, 0xB3 }, +{ 0x00, 0xB4, 0xB4 }, +{ 0x00, 0xB5, 0xB5 }, +{ 0x00, 0xB6, 0xB6 }, +{ 0x00, 0xB7, 0xB7 }, +{ 0x00, 0xB8, 0xA8 }, +{ 0x00, 0xB9, 0xB9 }, +{ 0x00, 0xBA, 0xAA }, +{ 0x00, 0xBB, 0xBB }, +{ 0x00, 0xBC, 0xBC }, +{ 0x00, 0xBD, 0xBD }, +{ 0x00, 0xBE, 0xBE }, +{ 0x00, 0xBF, 0xAF }, +{ 0x01, 0xE0, 0xC0 }, +{ 0x01, 0xE1, 0xC1 }, +{ 0x01, 0xE2, 0xC2 }, +{ 0x01, 0xE3, 0xC3 }, +{ 0x01, 0xE4, 0xC4 }, +{ 0x01, 0xE5, 0xC5 }, +{ 0x01, 0xE6, 0xC6 }, +{ 0x01, 0xE7, 0xC7 }, +{ 0x01, 0xE8, 0xC8 }, +{ 0x01, 0xE9, 0xC9 }, +{ 0x01, 0xEA, 0xCA }, +{ 0x01, 0xEB, 0xCB }, +{ 0x01, 0xEC, 0xCC }, +{ 0x01, 0xED, 0xCD }, +{ 0x01, 0xEE, 0xCE }, +{ 0x01, 0xEF, 0xCF }, +{ 0x01, 0xF0, 0xD0 }, +{ 0x01, 0xF1, 0xD1 }, +{ 0x01, 0xF2, 0xD2 }, +{ 0x01, 0xF3, 0xD3 }, +{ 0x01, 0xF4, 0xD4 }, +{ 0x01, 0xF5, 0xD5 }, +{ 0x01, 0xF6, 0xD6 }, +{ 0x00, 0xD7, 0xD7 }, +{ 0x01, 0xF8, 0xD8 }, +{ 0x01, 0xF9, 0xD9 }, +{ 0x01, 0xFA, 0xDA }, +{ 0x01, 0xFB, 0xDB }, +{ 0x01, 0xFC, 0xDC }, +{ 0x01, 0xFD, 0xDD }, +{ 0x01, 0xFE, 0xDE }, +{ 0x00, 0xDF, 0xDF }, +{ 0x00, 0xE0, 0xC0 }, +{ 0x00, 0xE1, 0xC1 }, +{ 0x00, 0xE2, 0xC2 }, +{ 0x00, 0xE3, 0xC3 }, +{ 0x00, 0xE4, 0xC4 }, +{ 0x00, 0xE5, 0xC5 }, +{ 0x00, 0xE6, 0xC6 }, +{ 0x00, 0xE7, 0xC7 }, +{ 0x00, 0xE8, 0xC8 }, +{ 0x00, 0xE9, 0xC9 }, +{ 0x00, 0xEA, 0xCA }, +{ 0x00, 0xEB, 0xCB }, +{ 0x00, 0xEC, 0xCC }, +{ 0x00, 0xED, 0xCD }, +{ 0x00, 0xEE, 0xCE }, +{ 0x00, 0xEF, 0xCF }, +{ 0x00, 0xF0, 0xD0 }, +{ 0x00, 0xF1, 0xD1 }, +{ 0x00, 0xF2, 0xD2 }, +{ 0x00, 0xF3, 0xD3 }, +{ 0x00, 0xF4, 0xD4 }, +{ 0x00, 0xF5, 0xD5 }, +{ 0x00, 0xF6, 0xD6 }, +{ 0x00, 0xF7, 0xF7 }, +{ 0x00, 0xF8, 0xD8 }, +{ 0x00, 0xF9, 0xD9 }, +{ 0x00, 0xFA, 0xDA }, +{ 0x00, 0xFB, 0xDB }, +{ 0x00, 0xFC, 0xDC }, +{ 0x00, 0xFD, 0xDD }, +{ 0x00, 0xFE, 0xDE }, +{ 0x00, 0xFF, 0xFF } +}; + + +static struct cs_info iso14_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x01, 0xa2, 0xa1 }, +{ 0x00, 0xa2, 0xa1 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x01, 0xa5, 0xa4 }, +{ 0x00, 0xa5, 0xa4 }, +{ 0x01, 0xa6, 0xab }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x01, 0xb8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x01, 0xba, 0xaa }, +{ 0x00, 0xab, 0xa6 }, +{ 0x01, 0xbc, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x01, 0xff, 0xaf }, +{ 0x01, 0xb1, 0xb0 }, +{ 0x00, 0xb1, 0xb0 }, +{ 0x01, 0xb3, 0xb2 }, +{ 0x00, 0xb3, 0xb2 }, +{ 0x01, 0xb5, 0xb4 }, +{ 0x00, 0xb5, 0xb4 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x01, 0xb9, 0xb7 }, +{ 0x00, 0xb8, 0xa8 }, +{ 0x00, 0xb9, 0xb6 }, +{ 0x00, 0xba, 0xaa }, +{ 0x01, 0xbf, 0xbb }, +{ 0x00, 0xbc, 0xac }, +{ 0x01, 0xbe, 0xbd }, +{ 0x00, 0xbe, 0xbd }, +{ 0x00, 0xbf, 0xbb }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x01, 0xf7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0xfd, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xd7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0xdd }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info iso15_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x01, 0xa8, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa6 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x01, 0xb8, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb4 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x01, 0xbd, 0xbc }, +{ 0x00, 0xbd, 0xbc }, +{ 0x01, 0xff, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0xfd, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0xdd }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xbe } +}; + +static struct cs_info iscii_devanagari_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x00, 0xc0, 0xc0 }, +{ 0x00, 0xc1, 0xc1 }, +{ 0x00, 0xc2, 0xc2 }, +{ 0x00, 0xc3, 0xc3 }, +{ 0x00, 0xc4, 0xc4 }, +{ 0x00, 0xc5, 0xc5 }, +{ 0x00, 0xc6, 0xc6 }, +{ 0x00, 0xc7, 0xc7 }, +{ 0x00, 0xc8, 0xc8 }, +{ 0x00, 0xc9, 0xc9 }, +{ 0x00, 0xca, 0xca }, +{ 0x00, 0xcb, 0xcb }, +{ 0x00, 0xcc, 0xcc }, +{ 0x00, 0xcd, 0xcd }, +{ 0x00, 0xce, 0xce }, +{ 0x00, 0xcf, 0xcf }, +{ 0x00, 0xd0, 0xd0 }, +{ 0x00, 0xd1, 0xd1 }, +{ 0x00, 0xd2, 0xd2 }, +{ 0x00, 0xd3, 0xd3 }, +{ 0x00, 0xd4, 0xd4 }, +{ 0x00, 0xd5, 0xd5 }, +{ 0x00, 0xd6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x00, 0xd8, 0xd8 }, +{ 0x00, 0xd9, 0xd9 }, +{ 0x00, 0xda, 0xda }, +{ 0x00, 0xdb, 0xdb }, +{ 0x00, 0xdc, 0xdc }, +{ 0x00, 0xdd, 0xdd }, +{ 0x00, 0xde, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xe0 }, +{ 0x00, 0xe1, 0xe1 }, +{ 0x00, 0xe2, 0xe2 }, +{ 0x00, 0xe3, 0xe3 }, +{ 0x00, 0xe4, 0xe4 }, +{ 0x00, 0xe5, 0xe5 }, +{ 0x00, 0xe6, 0xe6 }, +{ 0x00, 0xe7, 0xe7 }, +{ 0x00, 0xe8, 0xe8 }, +{ 0x00, 0xe9, 0xe9 }, +{ 0x00, 0xea, 0xea }, +{ 0x00, 0xeb, 0xeb }, +{ 0x00, 0xec, 0xec }, +{ 0x00, 0xed, 0xed }, +{ 0x00, 0xee, 0xee }, +{ 0x00, 0xef, 0xef }, +{ 0x00, 0xf0, 0xf0 }, +{ 0x00, 0xf1, 0xf1 }, +{ 0x00, 0xf2, 0xf2 }, +{ 0x00, 0xf3, 0xf3 }, +{ 0x00, 0xf4, 0xf4 }, +{ 0x00, 0xf5, 0xf5 }, +{ 0x00, 0xf6, 0xf6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xf8 }, +{ 0x00, 0xf9, 0xf9 }, +{ 0x00, 0xfa, 0xfa }, +{ 0x00, 0xfb, 0xfb }, +{ 0x00, 0xfc, 0xfc }, +{ 0x00, 0xfd, 0xfd }, +{ 0x00, 0xfe, 0xfe }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info tis620_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x00, 0xc0, 0xc0 }, +{ 0x00, 0xc1, 0xc1 }, +{ 0x00, 0xc2, 0xc2 }, +{ 0x00, 0xc3, 0xc3 }, +{ 0x00, 0xc4, 0xc4 }, +{ 0x00, 0xc5, 0xc5 }, +{ 0x00, 0xc6, 0xc6 }, +{ 0x00, 0xc7, 0xc7 }, +{ 0x00, 0xc8, 0xc8 }, +{ 0x00, 0xc9, 0xc9 }, +{ 0x00, 0xca, 0xca }, +{ 0x00, 0xcb, 0xcb }, +{ 0x00, 0xcc, 0xcc }, +{ 0x00, 0xcd, 0xcd }, +{ 0x00, 0xce, 0xce }, +{ 0x00, 0xcf, 0xcf }, +{ 0x00, 0xd0, 0xd0 }, +{ 0x00, 0xd1, 0xd1 }, +{ 0x00, 0xd2, 0xd2 }, +{ 0x00, 0xd3, 0xd3 }, +{ 0x00, 0xd4, 0xd4 }, +{ 0x00, 0xd5, 0xd5 }, +{ 0x00, 0xd6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x00, 0xd8, 0xd8 }, +{ 0x00, 0xd9, 0xd9 }, +{ 0x00, 0xda, 0xda }, +{ 0x00, 0xdb, 0xdb }, +{ 0x00, 0xdc, 0xdc }, +{ 0x00, 0xdd, 0xdd }, +{ 0x00, 0xde, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xe0 }, +{ 0x00, 0xe1, 0xe1 }, +{ 0x00, 0xe2, 0xe2 }, +{ 0x00, 0xe3, 0xe3 }, +{ 0x00, 0xe4, 0xe4 }, +{ 0x00, 0xe5, 0xe5 }, +{ 0x00, 0xe6, 0xe6 }, +{ 0x00, 0xe7, 0xe7 }, +{ 0x00, 0xe8, 0xe8 }, +{ 0x00, 0xe9, 0xe9 }, +{ 0x00, 0xea, 0xea }, +{ 0x00, 0xeb, 0xeb }, +{ 0x00, 0xec, 0xec }, +{ 0x00, 0xed, 0xed }, +{ 0x00, 0xee, 0xee }, +{ 0x00, 0xef, 0xef }, +{ 0x00, 0xf0, 0xf0 }, +{ 0x00, 0xf1, 0xf1 }, +{ 0x00, 0xf2, 0xf2 }, +{ 0x00, 0xf3, 0xf3 }, +{ 0x00, 0xf4, 0xf4 }, +{ 0x00, 0xf5, 0xf5 }, +{ 0x00, 0xf6, 0xf6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xf8 }, +{ 0x00, 0xf9, 0xf9 }, +{ 0x00, 0xfa, 0xfa }, +{ 0x00, 0xfb, 0xfb }, +{ 0x00, 0xfc, 0xfc }, +{ 0x00, 0xfd, 0xfd }, +{ 0x00, 0xfe, 0xfe }, +{ 0x00, 0xff, 0xff } +}; + +struct enc_entry { + const char * enc_name; + struct cs_info * cs_table; +}; + +static struct enc_entry encds[] = { + {"iso88591",iso1_tbl}, //ISO-8859-1 + {"iso88592",iso2_tbl}, //ISO-8859-2 + {"iso88593",iso3_tbl}, //ISO-8859-3 + {"iso88594",iso4_tbl}, //ISO-8859-4 + {"iso88595",iso5_tbl}, //ISO-8859-5 + {"iso88596",iso6_tbl}, //ISO-8859-6 + {"iso88597",iso7_tbl}, //ISO-8859-7 + {"iso88598",iso8_tbl}, //ISO-8859-8 + {"iso88599",iso9_tbl}, //ISO-8859-9 + {"iso885910",iso10_tbl}, //ISO-8859-10 + {"tis620",tis620_tbl}, //TIS-620/ISO-8859-11 + {"tis6202533",tis620_tbl}, //TIS-620/ISO-8859-11 + {"iso885911",tis620_tbl}, //TIS-620/ISO-8859-11 + {"iso885913", iso13_tbl}, //ISO-8859-13 + {"iso885914", iso14_tbl}, //ISO-8859-14 + {"iso885915", iso15_tbl}, //ISO-8859-15 + {"koi8r",koi8r_tbl}, //KOI8-R + {"koi8u",koi8u_tbl}, //KOI8-U + {"cp1251",cp1251_tbl}, //CP-1251 + {"microsoftcp1251",cp1251_tbl}, //microsoft-cp1251 + {"xisciias", iscii_devanagari_tbl}, //x-iscii-as + {"isciidevanagari", iscii_devanagari_tbl} //ISCII-DEVANAGARI +}; + +/* map to lower case and remove non alphanumeric chars */ +static void toAsciiLowerAndRemoveNonAlphanumeric( const char* pName, char* pBuf ) +{ + while ( *pName ) + { + /* A-Z */ + if ( (*pName >= 0x41) && (*pName <= 0x5A) ) + { + *pBuf = (*pName)+0x20; /* toAsciiLower */ + pBuf++; + } + /* a-z, 0-9 */ + else if ( ((*pName >= 0x61) && (*pName <= 0x7A)) || + ((*pName >= 0x30) && (*pName <= 0x39)) ) + { + *pBuf = *pName; + pBuf++; + } + + pName++; + } + + *pBuf = '\0'; +} + +struct cs_info * get_current_cs(const char * es) { + char *normalized_encoding = new char[strlen(es)+1]; + toAsciiLowerAndRemoveNonAlphanumeric(es, normalized_encoding); + + struct cs_info * ccs = NULL; + int n = sizeof(encds) / sizeof(encds[0]); + for (int i = 0; i < n; i++) { + if (strcmp(normalized_encoding,encds[i].enc_name) == 0) { + ccs = encds[i].cs_table; + break; + } + } + + delete[] normalized_encoding; + + if (!ccs) { + HUNSPELL_WARNING(stderr, "error: unknown encoding %s: using %s as fallback\n", es, encds[0].enc_name); + ccs = encds[0].cs_table; + } + + return ccs; +} +#else +// XXX This function was rewritten for mozilla. Instead of storing the +// conversion tables static in this file, create them when needed +// with help the mozilla backend. +struct cs_info * get_current_cs(const char * es) { + struct cs_info *ccs; + + nsCOMPtr encoder; + nsCOMPtr decoder; + + nsresult rv; + nsCOMPtr ccm = do_GetService(kCharsetConverterManagerCID, &rv); + if (NS_FAILED(rv)) + return nsnull; + + rv = ccm->GetUnicodeEncoder(es, getter_AddRefs(encoder)); + if (NS_FAILED(rv)) + return nsnull; + encoder->SetOutputErrorBehavior(encoder->kOnError_Signal, nsnull, '?'); + rv = ccm->GetUnicodeDecoder(es, getter_AddRefs(decoder)); + if (NS_FAILED(rv)) + return nsnull; + decoder->SetInputErrorBehavior(decoder->kOnError_Signal); + + if (NS_FAILED(rv)) + return nsnull; + + ccs = new cs_info[256]; + + for (unsigned int i = 0; i <= 0xff; ++i) { + PRBool success = PR_FALSE; + // We want to find the upper/lowercase equivalents of each byte + // in this 1-byte character encoding. Call our encoding/decoding + // APIs separately for each byte since they may reject some of the + // bytes, and we want to handle errors separately for each byte. + char lower, upper; + do { + if (i == 0) + break; + const char source = char(i); + PRUnichar uni, uniCased; + PRInt32 charLength = 1, uniLength = 1; + + rv = decoder->Convert(&source, &charLength, &uni, &uniLength); + // Explicitly check NS_OK because we don't want to allow + // NS_OK_UDEC_MOREOUTPUT or NS_OK_UDEC_MOREINPUT. + if (rv != NS_OK || charLength != 1 || uniLength != 1) + break; + uniCased = ToLowerCase(uni); + rv = encoder->Convert(&uniCased, &uniLength, &lower, &charLength); + // Explicitly check NS_OK because we don't want to allow + // NS_OK_UDEC_MOREOUTPUT or NS_OK_UDEC_MOREINPUT. + if (rv != NS_OK || charLength != 1 || uniLength != 1) + break; + + uniCased = ToUpperCase(uni); + rv = encoder->Convert(&uniCased, &uniLength, &upper, &charLength); + // Explicitly check NS_OK because we don't want to allow + // NS_OK_UDEC_MOREOUTPUT or NS_OK_UDEC_MOREINPUT. + if (rv != NS_OK || charLength != 1 || uniLength != 1) + break; + + success = PR_TRUE; + } while (0); + + if (success) { + ccs[i].cupper = upper; + ccs[i].clower = lower; + } else { + ccs[i].cupper = i; + ccs[i].clower = i; + } + + if (ccs[i].clower != (unsigned char)i) + ccs[i].ccase = true; + else + ccs[i].ccase = false; + } + + return ccs; +} +#endif + +// primitive isalpha() replacement for tokenization +char * get_casechars(const char * enc) { + struct cs_info * csconv = get_current_cs(enc); + char expw[MAXLNLEN]; + char * p = expw; + for (int i = 0; i <= 255; i++) { + if ((csconv[i].cupper != csconv[i].clower)) { + *p = (char) i; + p++; + } + } + *p = '\0'; +#ifdef MOZILLA_CLIENT + delete [] csconv; +#endif + return mystrdup(expw); +} + +// language to encoding default map + +struct lang_map { + const char * lang; + int num; +}; + +static struct lang_map lang2enc[] = { +{"ar", LANG_ar}, +{"az", LANG_az}, +{"bg", LANG_bg}, +{"ca", LANG_ca}, +{"cs", LANG_cs}, +{"da", LANG_da}, +{"de", LANG_de}, +{"el", LANG_el}, +{"en", LANG_en}, +{"es", LANG_es}, +{"eu", LANG_eu}, +{"gl", LANG_gl}, +{"fr", LANG_fr}, +{"hr", LANG_hr}, +{"hu", LANG_hu}, +{"it", LANG_it}, +{"la", LANG_la}, +{"lv", LANG_lv}, +{"nl", LANG_nl}, +{"pl", LANG_pl}, +{"pt", LANG_pt}, +{"sv", LANG_sv}, +{"tr", LANG_tr}, +{"ru", LANG_ru}, +{"uk", LANG_uk} +}; + + +int get_lang_num(const char * lang) { + int n = sizeof(lang2enc) / sizeof(lang2enc[0]); + for (int i = 0; i < n; i++) { + if (strcmp(lang, lang2enc[i].lang) == 0) { + return lang2enc[i].num; + } + } + return LANG_xx; +} + +#ifndef OPENOFFICEORG +#ifndef MOZILLA_CLIENT +int initialize_utf_tbl() { + utf_tbl_count++; + if (utf_tbl) return 0; + utf_tbl = (unicode_info2 *) malloc(CONTSIZE * sizeof(unicode_info2)); + if (utf_tbl) { + size_t j; + for (j = 0; j < CONTSIZE; j++) { + utf_tbl[j].cletter = 0; + utf_tbl[j].clower = (unsigned short) j; + utf_tbl[j].cupper = (unsigned short) j; + } + for (j = 0; j < UTF_LST_LEN; j++) { + utf_tbl[utf_lst[j].c].cletter = 1; + utf_tbl[utf_lst[j].c].clower = utf_lst[j].clower; + utf_tbl[utf_lst[j].c].cupper = utf_lst[j].cupper; + } + } else return 1; + return 0; +} +#endif +#endif + +void free_utf_tbl() { + if (utf_tbl_count > 0) utf_tbl_count--; + if (utf_tbl && (utf_tbl_count == 0)) { + free(utf_tbl); + utf_tbl = NULL; + } +} + +unsigned short unicodetoupper(unsigned short c, int langnum) +{ + // In Azeri and Turkish, I and i dictinct letters: + // There are a dotless lower case i pair of upper `I', + // and an upper I with dot pair of lower `i'. + if (c == 0x0069 && ((langnum == LANG_az) || (langnum == LANG_tr))) + return 0x0130; +#ifdef OPENOFFICEORG + return u_toupper(c); +#else +#ifdef MOZILLA_CLIENT + return ToUpperCase((PRUnichar) c); +#else + return (utf_tbl) ? utf_tbl[c].cupper : c; +#endif +#endif +} + +unsigned short unicodetolower(unsigned short c, int langnum) +{ + // In Azeri and Turkish, I and i dictinct letters: + // There are a dotless lower case i pair of upper `I', + // and an upper I with dot pair of lower `i'. + if (c == 0x0049 && ((langnum == LANG_az) || (langnum == LANG_tr))) + return 0x0131; +#ifdef OPENOFFICEORG + return u_tolower(c); +#else +#ifdef MOZILLA_CLIENT + return ToLowerCase((PRUnichar) c); +#else + return (utf_tbl) ? utf_tbl[c].clower : c; +#endif +#endif +} + +int unicodeisalpha(unsigned short c) +{ +#ifdef OPENOFFICEORG + return u_isalpha(c); +#else + return (utf_tbl) ? utf_tbl[c].cletter : 0; +#endif +} + +/* get type of capitalization */ +int get_captype(char * word, int nl, cs_info * csconv) { + // now determine the capitalization type of the first nl letters + int ncap = 0; + int nneutral = 0; + int firstcap = 0; + if (csconv == NULL) return NOCAP; + for (char * q = word; *q != '\0'; q++) { + if (csconv[*((unsigned char *)q)].ccase) ncap++; + if (csconv[*((unsigned char *)q)].cupper == csconv[*((unsigned char *)q)].clower) nneutral++; + } + if (ncap) { + firstcap = csconv[*((unsigned char *) word)].ccase; + } + + // now finally set the captype + if (ncap == 0) { + return NOCAP; + } else if ((ncap == 1) && firstcap) { + return INITCAP; + } else if ((ncap == nl) || ((ncap + nneutral) == nl)) { + return ALLCAP; + } else if ((ncap > 1) && firstcap) { + return HUHINITCAP; + } + return HUHCAP; +} + +int get_captype_utf8(w_char * word, int nl, int langnum) { + // now determine the capitalization type of the first nl letters + int ncap = 0; + int nneutral = 0; + int firstcap = 0; + unsigned short idx; + // don't check too long words + if (nl >= MAXWORDLEN) return 0; + // big Unicode character (non BMP area) + if (nl == -1) return NOCAP; + for (int i = 0; i < nl; i++) { + idx = (word[i].h << 8) + word[i].l; + if (idx != unicodetolower(idx, langnum)) ncap++; + if (unicodetoupper(idx, langnum) == unicodetolower(idx, langnum)) nneutral++; + } + if (ncap) { + idx = (word[0].h << 8) + word[0].l; + firstcap = (idx != unicodetolower(idx, langnum)); + } + + // now finally set the captype + if (ncap == 0) { + return NOCAP; + } else if ((ncap == 1) && firstcap) { + return INITCAP; + } else if ((ncap == nl) || ((ncap + nneutral) == nl)) { + return ALLCAP; + } else if ((ncap > 1) && firstcap) { + return HUHINITCAP; + } + return HUHCAP; +} + + +// strip all ignored characters in the string +void remove_ignored_chars_utf(char * word, unsigned short ignored_chars[], int ignored_len) +{ + w_char w[MAXWORDLEN]; + w_char w2[MAXWORDLEN]; + int i; + int j; + int len = u8_u16(w, MAXWORDLEN, word); + for (i = 0, j = 0; i < len; i++) { + if (!flag_bsearch(ignored_chars, ((unsigned short *) w)[i], ignored_len)) { + w2[j] = w[i]; + j++; + } + } + if (j < i) u16_u8(word, MAXWORDUTF8LEN, w2, j); +} + +// strip all ignored characters in the string +void remove_ignored_chars(char * word, char * ignored_chars) +{ + for (char * p = word; *p != '\0'; p++) { + if (!strchr(ignored_chars, *p)) { + *word = *p; + word++; + } + } + *word = '\0'; +} + +int parse_string(char * line, char ** out, int ln) +{ + char * tp = line; + char * piece; + int i = 0; + int np = 0; + if (*out) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions\n", ln); + return 1; + } + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + *out = mystrdup(piece); + if (!*out) return 1; + np++; + break; + } + default: break; + } + i++; + } + // free(piece); + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", ln); + return 1; + } + return 0; +} + +int parse_array(char * line, char ** out, unsigned short ** out_utf16, + int * out_utf16_len, int utf8, int ln) { + if (parse_string(line, out, ln)) return 1; + if (utf8) { + w_char w[MAXWORDLEN]; + int n = u8_u16(w, MAXWORDLEN, *out); + if (n > 0) { + flag_qsort((unsigned short *) w, 0, n); + *out_utf16 = (unsigned short *) malloc(n * sizeof(unsigned short)); + if (!*out_utf16) return 1; + memcpy(*out_utf16, w, n * sizeof(unsigned short)); + } + *out_utf16_len = n; + } + return 0; +} diff --git a/src/myspell/csutil.hxx b/src/myspell/csutil.hxx new file mode 100644 index 0000000..a518b34 --- /dev/null +++ b/src/myspell/csutil.hxx @@ -0,0 +1,227 @@ +#ifndef __CSUTILHXX__ +#define __CSUTILHXX__ + +#include "hunvisapi.h" + +// First some base level utility routines + +#include +#include "w_char.hxx" +#include "htypes.hxx" + +#ifdef MOZILLA_CLIENT +#include "nscore.h" // for mozalloc headers +#endif + +// casing +#define NOCAP 0 +#define INITCAP 1 +#define ALLCAP 2 +#define HUHCAP 3 +#define HUHINITCAP 4 + +// default encoding and keystring +#define SPELL_ENCODING "ISO8859-1" +#define SPELL_KEYSTRING "qwertyuiop|asdfghjkl|zxcvbnm" + +// default morphological fields +#define MORPH_STEM "st:" +#define MORPH_ALLOMORPH "al:" +#define MORPH_POS "po:" +#define MORPH_DERI_PFX "dp:" +#define MORPH_INFL_PFX "ip:" +#define MORPH_TERM_PFX "tp:" +#define MORPH_DERI_SFX "ds:" +#define MORPH_INFL_SFX "is:" +#define MORPH_TERM_SFX "ts:" +#define MORPH_SURF_PFX "sp:" +#define MORPH_FREQ "fr:" +#define MORPH_PHON "ph:" +#define MORPH_HYPH "hy:" +#define MORPH_PART "pa:" +#define MORPH_FLAG "fl:" +#define MORPH_HENTRY "_H:" +#define MORPH_TAG_LEN strlen(MORPH_STEM) + +#define MSEP_FLD ' ' +#define MSEP_REC '\n' +#define MSEP_ALT '\v' + +// default flags +#define DEFAULTFLAGS 65510 +#define FORBIDDENWORD 65510 +#define ONLYUPCASEFLAG 65511 + +// convert UTF-16 characters to UTF-8 +LIBHUNSPELL_DLL_EXPORTED char * u16_u8(char * dest, int size, const w_char * src, int srclen); + +// convert UTF-8 characters to UTF-16 +LIBHUNSPELL_DLL_EXPORTED int u8_u16(w_char * dest, int size, const char * src); + +// sort 2-byte vector +LIBHUNSPELL_DLL_EXPORTED void flag_qsort(unsigned short flags[], int begin, int end); + +// binary search in 2-byte vector +LIBHUNSPELL_DLL_EXPORTED int flag_bsearch(unsigned short flags[], unsigned short flag, int right); + +// remove end of line char(s) +LIBHUNSPELL_DLL_EXPORTED void mychomp(char * s); + +// duplicate string +LIBHUNSPELL_DLL_EXPORTED char * mystrdup(const char * s); + +// strcat for limited length destination string +LIBHUNSPELL_DLL_EXPORTED char * mystrcat(char * dest, const char * st, int max); + +// duplicate reverse of string +LIBHUNSPELL_DLL_EXPORTED char * myrevstrdup(const char * s); + +// parse into tokens with char delimiter +LIBHUNSPELL_DLL_EXPORTED char * mystrsep(char ** sptr, const char delim); +// parse into tokens with char delimiter +LIBHUNSPELL_DLL_EXPORTED char * mystrsep2(char ** sptr, const char delim); + +// parse into tokens with char delimiter +LIBHUNSPELL_DLL_EXPORTED char * mystrrep(char *, const char *, const char *); + +// append s to ends of every lines in text +LIBHUNSPELL_DLL_EXPORTED void strlinecat(char * lines, const char * s); + +// tokenize into lines with new line +LIBHUNSPELL_DLL_EXPORTED int line_tok(const char * text, char *** lines, char breakchar); + +// tokenize into lines with new line and uniq in place +LIBHUNSPELL_DLL_EXPORTED char * line_uniq(char * text, char breakchar); +LIBHUNSPELL_DLL_EXPORTED char * line_uniq_app(char ** text, char breakchar); + +// change oldchar to newchar in place +LIBHUNSPELL_DLL_EXPORTED char * tr(char * text, char oldc, char newc); + +// reverse word +LIBHUNSPELL_DLL_EXPORTED int reverseword(char *); + +// reverse word +LIBHUNSPELL_DLL_EXPORTED int reverseword_utf(char *); + +// remove duplicates +LIBHUNSPELL_DLL_EXPORTED int uniqlist(char ** list, int n); + +// free character array list +LIBHUNSPELL_DLL_EXPORTED void freelist(char *** list, int n); + +// character encoding information +struct cs_info { + unsigned char ccase; + unsigned char clower; + unsigned char cupper; +}; + +// Unicode character encoding information +struct unicode_info { + unsigned short c; + unsigned short cupper; + unsigned short clower; +}; + +LIBHUNSPELL_DLL_EXPORTED int initialize_utf_tbl(); +LIBHUNSPELL_DLL_EXPORTED void free_utf_tbl(); +LIBHUNSPELL_DLL_EXPORTED unsigned short unicodetoupper(unsigned short c, int langnum); +LIBHUNSPELL_DLL_EXPORTED unsigned short unicodetolower(unsigned short c, int langnum); +LIBHUNSPELL_DLL_EXPORTED int unicodeisalpha(unsigned short c); + +LIBHUNSPELL_DLL_EXPORTED struct cs_info * get_current_cs(const char * es); + +// get language identifiers of language codes +LIBHUNSPELL_DLL_EXPORTED int get_lang_num(const char * lang); + +// get characters of the given 8bit encoding with lower- and uppercase forms +LIBHUNSPELL_DLL_EXPORTED char * get_casechars(const char * enc); + +// convert null terminated string to all caps using encoding +LIBHUNSPELL_DLL_EXPORTED void enmkallcap(char * d, const char * p, const char * encoding); + +// convert null terminated string to all little using encoding +LIBHUNSPELL_DLL_EXPORTED void enmkallsmall(char * d, const char * p, const char * encoding); + +// convert null terminated string to have initial capital using encoding +LIBHUNSPELL_DLL_EXPORTED void enmkinitcap(char * d, const char * p, const char * encoding); + +// convert null terminated string to all caps +LIBHUNSPELL_DLL_EXPORTED void mkallcap(char * p, const struct cs_info * csconv); + +// convert null terminated string to all little +LIBHUNSPELL_DLL_EXPORTED void mkallsmall(char * p, const struct cs_info * csconv); + +// convert null terminated string to have initial capital +LIBHUNSPELL_DLL_EXPORTED void mkinitcap(char * p, const struct cs_info * csconv); + +// convert first nc characters of UTF-8 string to little +LIBHUNSPELL_DLL_EXPORTED void mkallsmall_utf(w_char * u, int nc, int langnum); + +// convert first nc characters of UTF-8 string to capital +LIBHUNSPELL_DLL_EXPORTED void mkallcap_utf(w_char * u, int nc, int langnum); + +// get type of capitalization +LIBHUNSPELL_DLL_EXPORTED int get_captype(char * q, int nl, cs_info *); + +// get type of capitalization (UTF-8) +LIBHUNSPELL_DLL_EXPORTED int get_captype_utf8(w_char * q, int nl, int langnum); + +// strip all ignored characters in the string +LIBHUNSPELL_DLL_EXPORTED void remove_ignored_chars_utf(char * word, unsigned short ignored_chars[], int ignored_len); + +// strip all ignored characters in the string +LIBHUNSPELL_DLL_EXPORTED void remove_ignored_chars(char * word, char * ignored_chars); + +LIBHUNSPELL_DLL_EXPORTED int parse_string(char * line, char ** out, int ln); + +LIBHUNSPELL_DLL_EXPORTED int parse_array(char * line, char ** out, unsigned short ** out_utf16, + int * out_utf16_len, int utf8, int ln); + +LIBHUNSPELL_DLL_EXPORTED int fieldlen(const char * r); +LIBHUNSPELL_DLL_EXPORTED char * copy_field(char * dest, const char * morph, const char * var); + +LIBHUNSPELL_DLL_EXPORTED int morphcmp(const char * s, const char * t); + +LIBHUNSPELL_DLL_EXPORTED int get_sfxcount(const char * morph); + +// conversion function for protected memory +LIBHUNSPELL_DLL_EXPORTED void store_pointer(char * dest, char * source); + +// conversion function for protected memory +LIBHUNSPELL_DLL_EXPORTED char * get_stored_pointer(const char * s); + +// hash entry macros +LIBHUNSPELL_DLL_EXPORTED inline char* HENTRY_DATA(struct hentry *h) +{ + char *ret; + if (!h->var) + ret = NULL; + else if (h->var & H_OPT_ALIASM) + ret = get_stored_pointer(HENTRY_WORD(h) + h->blen + 1); + else + ret = HENTRY_WORD(h) + h->blen + 1; + return ret; +} + +// NULL-free version for warning-free OOo build +LIBHUNSPELL_DLL_EXPORTED inline const char* HENTRY_DATA2(const struct hentry *h) +{ + const char *ret; + if (!h->var) + ret = ""; + else if (h->var & H_OPT_ALIASM) + ret = get_stored_pointer(HENTRY_WORD(h) + h->blen + 1); + else + ret = HENTRY_WORD(h) + h->blen + 1; + return ret; +} + +LIBHUNSPELL_DLL_EXPORTED inline char* HENTRY_FIND(struct hentry *h, const char *p) +{ + return (HENTRY_DATA(h) ? strstr(HENTRY_DATA(h), p) : NULL); +} + +#define w_char_eq(a,b) (((a).l == (b).l) && ((a).h == (b).h)) + +#endif diff --git a/src/myspell/dictmgr.cxx b/src/myspell/dictmgr.cxx new file mode 100644 index 0000000..35a179c --- /dev/null +++ b/src/myspell/dictmgr.cxx @@ -0,0 +1,182 @@ + +#include +#include +#include +#include + +#include "dictmgr.hxx" + +#include "enchant-provider.h" + +DictMgr::DictMgr(const char * dictpath, const char * etype) : numdict(0) +{ + // load list of etype entries + pdentry = (dictentry *)malloc(MAXDICTIONARIES*sizeof(struct dictentry)); + if (pdentry) { + if (parse_file(dictpath, etype)) { + numdict = 0; + // no dictionary.lst found is okay + } + } +} + + +DictMgr::~DictMgr() +{ + dictentry * pdict = NULL; + if (pdentry) { + pdict = pdentry; + for (int i=0;ilang) { + free(pdict->lang); + pdict->lang = NULL; + } + if (pdict->region) { + free(pdict->region); + pdict->region=NULL; + } + if (pdict->filename) { + free(pdict->filename); + pdict->filename = NULL; + } + pdict++; + } + free(pdentry); + pdentry = NULL; + pdict = NULL; + } + numdict = 0; +} + + +// read in list of etype entries and build up structure to describe them +int DictMgr::parse_file(const char * dictpath, const char * etype) +{ + + int i; + char line[MAXDICTENTRYLEN+1]; + dictentry * pdict = pdentry; + + // open the dictionary list file + FILE * dictlst; + dictlst = enchant_fopen(dictpath,"r"); + if (!dictlst) { + return 1; + } + + // step one is to parse the dictionary list building up the + // descriptive structures + + // read in each line ignoring any that dont start with etype + while (fgets(line,MAXDICTENTRYLEN,dictlst)) { + mychomp(line); + + /* parse in a dictionary entry */ + if (strncmp(line,etype,4) == 0) { + if (numdict < MAXDICTIONARIES) { + char * tp = line; + char * piece; + i = 0; + while ((piece=mystrsep(&tp,' '))) { + if (*piece != '\0') { + switch(i) { + case 0: break; + case 1: pdict->lang = mystrdup(piece); break; + case 2: if (strcmp (piece, "ANY") == 0) + pdict->region = mystrdup(""); + else + pdict->region = mystrdup(piece); + break; + case 3: pdict->filename = mystrdup(piece); break; + default: break; + } + i++; + } + free(piece); + } + if (i == 4) { + numdict++; + pdict++; + } else { + switch (i) { + case 3: + free(pdict->region); + pdict->region=NULL; + case 2: //deliberate fallthrough + free(pdict->lang); + pdict->lang=NULL; + default: + break; + } + fprintf(stderr,"dictionary list corruption in line \"%s\"\n",line); + fflush(stderr); + } + } + } + } + fclose(dictlst); + return 0; +} + +// return text encoding of dictionary +int DictMgr::get_list(dictentry ** ppentry) +{ + *ppentry = pdentry; + return numdict; +} + + + +// strip strings into token based on single char delimiter +// acts like strsep() but only uses a delim char and not +// a delim string + +char * DictMgr::mystrsep(char ** stringp, const char delim) +{ + char * rv = NULL; + char * mp = *stringp; + size_t n = strlen(mp); + if (n > 0) { + char * dp = (char *)memchr(mp,(int)((unsigned char)delim),n); + if (dp) { + *stringp = dp+1; + size_t nc = dp - mp; + rv = (char *) malloc(nc+1); + if (rv) { + memcpy(rv,mp,nc); + *(rv+nc) = '\0'; + } + } else { + rv = (char *) malloc(n+1); + if (rv) { + memcpy(rv, mp, n); + *(rv+n) = '\0'; + *stringp = mp + n; + } + } + } + return rv; +} + + +// replaces strdup with ansi version +char * DictMgr::mystrdup(const char * s) +{ + char * d = NULL; + if (s) { + int sl = strlen(s)+1; + d = (char *) malloc(sl); + if (d) memcpy(d,s,sl); + } + return d; +} + + +// remove cross-platform text line end characters +void DictMgr:: mychomp(char * s) +{ + int k = strlen(s); + if ((k > 0) && ((*(s+k-1)=='\r') || (*(s+k-1)=='\n'))) *(s+k-1) = '\0'; + if ((k > 1) && (*(s+k-2) == '\r')) *(s+k-2) = '\0'; +} + diff --git a/src/myspell/dictmgr.hxx b/src/myspell/dictmgr.hxx new file mode 100644 index 0000000..bb197f8 --- /dev/null +++ b/src/myspell/dictmgr.hxx @@ -0,0 +1,36 @@ +#ifndef _DICTMGR_HXX_ +#define _DICTMGR_HXX_ + +#include "hunvisapi.h" + +#define MAXDICTIONARIES 100 +#define MAXDICTENTRYLEN 1024 + +struct dictentry { + char * filename; + char * lang; + char * region; +}; + + +class LIBHUNSPELL_DLL_EXPORTED DictMgr +{ + + int numdict; + dictentry * pdentry; + +public: + + DictMgr(const char * dictpath, const char * etype); + ~DictMgr(); + int get_list(dictentry** ppentry); + +private: + int parse_file(const char * dictpath, const char * etype); + char * mystrsep(char ** stringp, const char delim); + char * mystrdup(const char * s); + void mychomp(char * s); + +}; + +#endif diff --git a/src/myspell/filemgr.cxx b/src/myspell/filemgr.cxx new file mode 100644 index 0000000..8d6755d --- /dev/null +++ b/src/myspell/filemgr.cxx @@ -0,0 +1,51 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include + +#include "filemgr.hxx" + +#include "enchant-provider.h" + +int FileMgr::fail(const char * err, const char * par) { + fprintf(stderr, err, par); + return -1; +} + +FileMgr::FileMgr(const char * file, const char * key) { + linenum = 0; + hin = NULL; + fin = enchant_fopen(file, "r"); + if (!fin) { + // check hzipped file + char * st = (char *) malloc(strlen(file) + strlen(HZIP_EXTENSION) + 1); + if (st) { + strcpy(st, file); + strcat(st, HZIP_EXTENSION); + hin = new Hunzip(st, key); + free(st); + } + } + if (!fin && !hin) fail(MSG_OPEN, file); +} + +FileMgr::~FileMgr() +{ + if (fin) fclose(fin); + if (hin) delete hin; +} + +char * FileMgr::getline() { + const char * l; + linenum++; + if (fin) return fgets(in, BUFSIZE - 1, fin); + if (hin && (l = hin->getline())) return strcpy(in, l); + linenum--; + return NULL; +} + +int FileMgr::getlinenum() { + return linenum; +} diff --git a/src/myspell/filemgr.hxx b/src/myspell/filemgr.hxx new file mode 100644 index 0000000..94cb723 --- /dev/null +++ b/src/myspell/filemgr.hxx @@ -0,0 +1,25 @@ +/* file manager class - read lines of files [filename] OR [filename.hz] */ +#ifndef _FILEMGR_HXX_ +#define _FILEMGR_HXX_ + +#include "hunvisapi.h" + +#include "hunzip.hxx" +#include + +class LIBHUNSPELL_DLL_EXPORTED FileMgr +{ +protected: + FILE * fin; + Hunzip * hin; + char in[BUFSIZE + 50]; // input buffer + int fail(const char * err, const char * par); + int linenum; + +public: + FileMgr(const char * filename, const char * key = NULL); + ~FileMgr(); + char * getline(); + int getlinenum(); +}; +#endif diff --git a/src/myspell/hashmgr.cxx b/src/myspell/hashmgr.cxx new file mode 100644 index 0000000..ea93b87 --- /dev/null +++ b/src/myspell/hashmgr.cxx @@ -0,0 +1,928 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include +#include + +#include "hashmgr.hxx" +#include "csutil.hxx" +#include "atypes.hxx" + +// build a hash table from a munched word list + +HashMgr::HashMgr(const char * tpath, const char * apath, const char * key) +{ + tablesize = 0; + tableptr = NULL; + flag_mode = FLAG_CHAR; + complexprefixes = 0; + utf8 = 0; + langnum = 0; + lang = NULL; + enc = NULL; + csconv = 0; + ignorechars = NULL; + ignorechars_utf16 = NULL; + ignorechars_utf16_len = 0; + numaliasf = 0; + aliasf = NULL; + numaliasm = 0; + aliasm = NULL; + forbiddenword = FORBIDDENWORD; // forbidden word signing flag + load_config(apath, key); + int ec = load_tables(tpath, key); + if (ec) { + /* error condition - what should we do here */ + HUNSPELL_WARNING(stderr, "Hash Manager Error : %d\n",ec); + if (tableptr) { + free(tableptr); + tableptr = NULL; + } + tablesize = 0; + } +} + + +HashMgr::~HashMgr() +{ + if (tableptr) { + // now pass through hash table freeing up everything + // go through column by column of the table + for (int i=0; i < tablesize; i++) { + struct hentry * pt = tableptr[i]; + struct hentry * nt = NULL; + while(pt) { + nt = pt->next; + if (pt->astr && (!aliasf || TESTAFF(pt->astr, ONLYUPCASEFLAG, pt->alen))) free(pt->astr); + free(pt); + pt = nt; + } + } + free(tableptr); + } + tablesize = 0; + + if (aliasf) { + for (int j = 0; j < (numaliasf); j++) free(aliasf[j]); + free(aliasf); + aliasf = NULL; + if (aliasflen) { + free(aliasflen); + aliasflen = NULL; + } + } + if (aliasm) { + for (int j = 0; j < (numaliasm); j++) free(aliasm[j]); + free(aliasm); + aliasm = NULL; + } + +#ifndef OPENOFFICEORG +#ifndef MOZILLA_CLIENT + if (utf8) free_utf_tbl(); +#endif +#endif + + if (enc) free(enc); + if (lang) free(lang); + + if (ignorechars) free(ignorechars); + if (ignorechars_utf16) free(ignorechars_utf16); + +#ifdef MOZILLA_CLIENT + delete [] csconv; +#endif +} + +// lookup a root word in the hashtable + +struct hentry * HashMgr::lookup(const char *word) const +{ + struct hentry * dp; + if (tableptr) { + dp = tableptr[hash(word)]; + if (!dp) return NULL; + for ( ; dp != NULL; dp = dp->next) { + if (strcmp(word, dp->word) == 0) return dp; + } + } + return NULL; +} + +// add a word to the hash table (private) +int HashMgr::add_word(const char * word, int wbl, int wcl, unsigned short * aff, + int al, const char * desc, bool onlyupcase) +{ + bool upcasehomonym = false; + int descl = desc ? (aliasm ? sizeof(short) : strlen(desc) + 1) : 0; + // variable-length hash record with word and optional fields + struct hentry* hp = + (struct hentry *) malloc (sizeof(struct hentry) + wbl + descl); + if (!hp) return 1; + char * hpw = hp->word; + strcpy(hpw, word); + if (ignorechars != NULL) { + if (utf8) { + remove_ignored_chars_utf(hpw, ignorechars_utf16, ignorechars_utf16_len); + } else { + remove_ignored_chars(hpw, ignorechars); + } + } + if (complexprefixes) { + if (utf8) reverseword_utf(hpw); else reverseword(hpw); + } + + int i = hash(hpw); + + hp->blen = (unsigned char) wbl; + hp->clen = (unsigned char) wcl; + hp->alen = (short) al; + hp->astr = aff; + hp->next = NULL; + hp->next_homonym = NULL; + + // store the description string or its pointer + if (desc) { + hp->var = H_OPT; + if (aliasm) { + hp->var += H_OPT_ALIASM; + store_pointer(hpw + wbl + 1, get_aliasm(atoi(desc))); + } else { + strcpy(hpw + wbl + 1, desc); + if (complexprefixes) { + if (utf8) reverseword_utf(HENTRY_DATA(hp)); + else reverseword(HENTRY_DATA(hp)); + } + } + if (strstr(HENTRY_DATA(hp), MORPH_PHON)) hp->var += H_OPT_PHON; + } else hp->var = 0; + + struct hentry * dp = tableptr[i]; + if (!dp) { + tableptr[i] = hp; + return 0; + } + while (dp->next != NULL) { + if ((!dp->next_homonym) && (strcmp(hp->word, dp->word) == 0)) { + // remove hidden onlyupcase homonym + if (!onlyupcase) { + if ((dp->astr) && TESTAFF(dp->astr, ONLYUPCASEFLAG, dp->alen)) { + free(dp->astr); + dp->astr = hp->astr; + dp->alen = hp->alen; + free(hp); + return 0; + } else { + dp->next_homonym = hp; + } + } else { + upcasehomonym = true; + } + } + dp=dp->next; + } + if (strcmp(hp->word, dp->word) == 0) { + // remove hidden onlyupcase homonym + if (!onlyupcase) { + if ((dp->astr) && TESTAFF(dp->astr, ONLYUPCASEFLAG, dp->alen)) { + free(dp->astr); + dp->astr = hp->astr; + dp->alen = hp->alen; + free(hp); + return 0; + } else { + dp->next_homonym = hp; + } + } else { + upcasehomonym = true; + } + } + if (!upcasehomonym) { + dp->next = hp; + } else { + // remove hidden onlyupcase homonym + if (hp->astr) free(hp->astr); + free(hp); + } + return 0; +} + +int HashMgr::add_hidden_capitalized_word(char * word, int wbl, int wcl, + unsigned short * flags, int al, char * dp, int captype) +{ + // add inner capitalized forms to handle the following allcap forms: + // Mixed caps: OpenOffice.org -> OPENOFFICE.ORG + // Allcaps with suffixes: CIA's -> CIA'S + if (((captype == HUHCAP) || (captype == HUHINITCAP) || + ((captype == ALLCAP) && (flags != NULL))) && + !((flags != NULL) && TESTAFF(flags, forbiddenword, al))) { + unsigned short * flags2 = (unsigned short *) malloc (sizeof(unsigned short) * (al+1)); + if (!flags2) return 1; + if (al) memcpy(flags2, flags, al * sizeof(unsigned short)); + flags2[al] = ONLYUPCASEFLAG; + if (utf8) { + char st[BUFSIZE]; + w_char w[BUFSIZE]; + int wlen = u8_u16(w, BUFSIZE, word); + mkallsmall_utf(w, wlen, langnum); + mkallcap_utf(w, 1, langnum); + u16_u8(st, BUFSIZE, w, wlen); + return add_word(st,wbl,wcl,flags2,al+1,dp, true); + } else { + mkallsmall(word, csconv); + mkinitcap(word, csconv); + return add_word(word,wbl,wcl,flags2,al+1,dp, true); + } + } + return 0; +} + +// detect captype and modify word length for UTF-8 encoding +int HashMgr::get_clen_and_captype(const char * word, int wbl, int * captype) { + int len; + if (utf8) { + w_char dest_utf[BUFSIZE]; + len = u8_u16(dest_utf, BUFSIZE, word); + *captype = get_captype_utf8(dest_utf, len, langnum); + } else { + len = wbl; + *captype = get_captype((char *) word, len, csconv); + } + return len; +} + +// remove word (personal dictionary function for standalone applications) +int HashMgr::remove(const char * word) +{ + struct hentry * dp = lookup(word); + while (dp) { + if (dp->alen == 0 || !TESTAFF(dp->astr, forbiddenword, dp->alen)) { + unsigned short * flags = + (unsigned short *) malloc(sizeof(short) * (dp->alen + 1)); + if (!flags) return 1; + for (int i = 0; i < dp->alen; i++) flags[i] = dp->astr[i]; + flags[dp->alen] = forbiddenword; + dp->astr = flags; + dp->alen++; + flag_qsort(flags, 0, dp->alen); + } + dp = dp->next_homonym; + } + return 0; +} + +/* remove forbidden flag to add a personal word to the hash */ +int HashMgr::remove_forbidden_flag(const char * word) { + struct hentry * dp = lookup(word); + if (!dp) return 1; + while (dp) { + if (dp->astr && TESTAFF(dp->astr, forbiddenword, dp->alen)) { + if (dp->alen == 1) dp->alen = 0; // XXX forbidden words of personal dic. + else { + unsigned short * flags2 = + (unsigned short *) malloc(sizeof(short) * (dp->alen - 1)); + if (!flags2) return 1; + int i, j = 0; + for (i = 0; i < dp->alen; i++) { + if (dp->astr[i] != forbiddenword) flags2[j++] = dp->astr[i]; + } + dp->alen--; + dp->astr = flags2; // XXX allowed forbidden words + } + } + dp = dp->next_homonym; + } + return 0; +} + +// add a custom dic. word to the hash table (public) +int HashMgr::add(const char * word) +{ + unsigned short * flags = NULL; + int al = 0; + if (remove_forbidden_flag(word)) { + int captype; + int wbl = strlen(word); + int wcl = get_clen_and_captype(word, wbl, &captype); + add_word(word, wbl, wcl, flags, al, NULL, false); + return add_hidden_capitalized_word((char *) word, wbl, wcl, flags, al, NULL, captype); + } + return 0; +} + +int HashMgr::add_with_affix(const char * word, const char * example) +{ + // detect captype and modify word length for UTF-8 encoding + struct hentry * dp = lookup(example); + remove_forbidden_flag(word); + if (dp && dp->astr) { + int captype; + int wbl = strlen(word); + int wcl = get_clen_and_captype(word, wbl, &captype); + if (aliasf) { + add_word(word, wbl, wcl, dp->astr, dp->alen, NULL, false); + } else { + unsigned short * flags = (unsigned short *) malloc (dp->alen * sizeof(short)); + if (flags) { + memcpy((void *) flags, (void *) dp->astr, dp->alen * sizeof(short)); + add_word(word, wbl, wcl, flags, dp->alen, NULL, false); + } else return 1; + } + return add_hidden_capitalized_word((char *) word, wbl, wcl, dp->astr, dp->alen, NULL, captype); + } + return 1; +} + +// walk the hash table entry by entry - null at end +// initialize: col=-1; hp = NULL; hp = walk_hashtable(&col, hp); +struct hentry * HashMgr::walk_hashtable(int &col, struct hentry * hp) const +{ + if (hp && hp->next != NULL) return hp->next; + for (col++; col < tablesize; col++) { + if (tableptr[col]) return tableptr[col]; + } + // null at end and reset to start + col = -1; + return NULL; +} + +// load a munched word list and build a hash table on the fly +int HashMgr::load_tables(const char * tpath, const char * key) +{ + int al; + char * ap; + char * dp; + char * dp2; + unsigned short * flags; + char * ts; + + // open dictionary file + FileMgr * dict = new FileMgr(tpath, key); + if (dict == NULL) return 1; + + // first read the first line of file to get hash table size */ + if (!(ts = dict->getline())) { + HUNSPELL_WARNING(stderr, "error: empty dic file\n"); + delete dict; + return 2; + } + mychomp(ts); + + /* remove byte order mark */ + if (strncmp(ts,"\xEF\xBB\xBF",3) == 0) { + memmove(ts, ts+3, strlen(ts+3)+1); + // warning: dic file begins with byte order mark: possible incompatibility with old Hunspell versions + } + + tablesize = atoi(ts); + if (tablesize == 0) { + HUNSPELL_WARNING(stderr, "error: line 1: missing or bad word count in the dic file\n"); + delete dict; + return 4; + } + tablesize = tablesize + 5 + USERWORD; + if ((tablesize %2) == 0) tablesize++; + + // allocate the hash table + tableptr = (struct hentry **) malloc(tablesize * sizeof(struct hentry *)); + if (! tableptr) { + delete dict; + return 3; + } + for (int i=0; igetline())) { + mychomp(ts); + // split each line into word and morphological description + dp = ts; + while ((dp = strchr(dp, ':'))) { + if ((dp > ts + 3) && (*(dp - 3) == ' ' || *(dp - 3) == '\t')) { + for (dp -= 4; dp >= ts && (*dp == ' ' || *dp == '\t'); dp--); + if (dp < ts) { // missing word + dp = NULL; + } else { + *(dp + 1) = '\0'; + dp = dp + 2; + } + break; + } + dp++; + } + + // tabulator is the old morphological field separator + dp2 = strchr(ts, '\t'); + if (dp2 && (!dp || dp2 < dp)) { + *dp2 = '\0'; + dp = dp2 + 1; + } + + // split each line into word and affix char strings + // "\/" signs slash in words (not affix separator) + // "/" at beginning of the line is word character (not affix separator) + ap = strchr(ts,'/'); + while (ap) { + if (ap == ts) { + ap++; + continue; + } else if (*(ap - 1) != '\\') break; + // replace "\/" with "/" + for (char * sp = ap - 1; *sp; *sp = *(sp + 1), sp++); + ap = strchr(ap,'/'); + } + + if (ap) { + *ap = '\0'; + if (aliasf) { + int index = atoi(ap + 1); + al = get_aliasf(index, &flags, dict); + if (!al) { + HUNSPELL_WARNING(stderr, "error: line %d: bad flag vector alias\n", dict->getlinenum()); + *ap = '\0'; + } + } else { + al = decode_flags(&flags, ap + 1, dict); + if (al == -1) { + HUNSPELL_WARNING(stderr, "Can't allocate memory.\n"); + delete dict; + return 6; + } + flag_qsort(flags, 0, al); + } + } else { + al = 0; + ap = NULL; + flags = NULL; + } + + int captype; + int wbl = strlen(ts); + int wcl = get_clen_and_captype(ts, wbl, &captype); + // add the word and its index plus its capitalized form optionally + if (add_word(ts,wbl,wcl,flags,al,dp, false) || + add_hidden_capitalized_word(ts, wbl, wcl, flags, al, dp, captype)) { + delete dict; + return 5; + } + } + + delete dict; + return 0; +} + +// the hash function is a simple load and rotate +// algorithm borrowed + +int HashMgr::hash(const char * word) const +{ + long hv = 0; + for (int i=0; i < 4 && *word != 0; i++) + hv = (hv << 8) | (*word++); + while (*word != 0) { + ROTATE(hv,ROTATE_LEN); + hv ^= (*word++); + } + return (unsigned long) hv % tablesize; +} + +int HashMgr::decode_flags(unsigned short ** result, char * flags, FileMgr * af) { + int len; + if (*flags == '\0') { + *result = NULL; + return 0; + } + switch (flag_mode) { + case FLAG_LONG: { // two-character flags (1x2yZz -> 1x 2y Zz) + len = strlen(flags); + if (len%2 == 1) HUNSPELL_WARNING(stderr, "error: line %d: bad flagvector\n", af->getlinenum()); + len /= 2; + *result = (unsigned short *) malloc(len * sizeof(short)); + if (!*result) return -1; + for (int i = 0; i < len; i++) { + (*result)[i] = (((unsigned short) flags[i * 2]) << 8) + (unsigned short) flags[i * 2 + 1]; + } + break; + } + case FLAG_NUM: { // decimal numbers separated by comma (4521,23,233 -> 4521 23 233) + int i; + len = 1; + char * src = flags; + unsigned short * dest; + char * p; + for (p = flags; *p; p++) { + if (*p == ',') len++; + } + *result = (unsigned short *) malloc(len * sizeof(short)); + if (!*result) return -1; + dest = *result; + for (p = flags; *p; p++) { + if (*p == ',') { + i = atoi(src); + if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: line %d: flag id %d is too large (max: %d)\n", + af->getlinenum(), i, DEFAULTFLAGS - 1); + *dest = (unsigned short) i; + if (*dest == 0) HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n", af->getlinenum()); + src = p + 1; + dest++; + } + } + i = atoi(src); + if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: line %d: flag id %d is too large (max: %d)\n", + af->getlinenum(), i, DEFAULTFLAGS - 1); + *dest = (unsigned short) i; + if (*dest == 0) HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n", af->getlinenum()); + break; + } + case FLAG_UNI: { // UTF-8 characters + w_char w[BUFSIZE/2]; + len = u8_u16(w, BUFSIZE/2, flags); + *result = (unsigned short *) malloc(len * sizeof(short)); + if (!*result) return -1; + memcpy(*result, w, len * sizeof(short)); + break; + } + default: { // Ispell's one-character flags (erfg -> e r f g) + unsigned short * dest; + len = strlen(flags); + *result = (unsigned short *) malloc(len * sizeof(short)); + if (!*result) return -1; + dest = *result; + for (unsigned char * p = (unsigned char *) flags; *p; p++) { + *dest = (unsigned short) *p; + dest++; + } + } + } + return len; +} + +unsigned short HashMgr::decode_flag(const char * f) { + unsigned short s = 0; + int i; + switch (flag_mode) { + case FLAG_LONG: + s = ((unsigned short) f[0] << 8) + (unsigned short) f[1]; + break; + case FLAG_NUM: + i = atoi(f); + if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: flag id %d is too large (max: %d)\n", i, DEFAULTFLAGS - 1); + s = (unsigned short) i; + break; + case FLAG_UNI: + u8_u16((w_char *) &s, 1, f); + break; + default: + s = (unsigned short) *((unsigned char *)f); + } + if (s == 0) HUNSPELL_WARNING(stderr, "error: 0 is wrong flag id\n"); + return s; +} + +char * HashMgr::encode_flag(unsigned short f) { + unsigned char ch[10]; + if (f==0) return mystrdup("(NULL)"); + if (flag_mode == FLAG_LONG) { + ch[0] = (unsigned char) (f >> 8); + ch[1] = (unsigned char) (f - ((f >> 8) << 8)); + ch[2] = '\0'; + } else if (flag_mode == FLAG_NUM) { + sprintf((char *) ch, "%d", f); + } else if (flag_mode == FLAG_UNI) { + u16_u8((char *) &ch, 10, (w_char *) &f, 1); + } else { + ch[0] = (unsigned char) (f); + ch[1] = '\0'; + } + return mystrdup((char *) ch); +} + +// read in aff file and set flag mode +int HashMgr::load_config(const char * affpath, const char * key) +{ + char * line; // io buffers + int firstline = 1; + + // open the affix file + FileMgr * afflst = new FileMgr(affpath, key); + if (!afflst) { + HUNSPELL_WARNING(stderr, "Error - could not open affix description file %s\n",affpath); + return 1; + } + + // read in each line ignoring any that do not + // start with a known line type indicator + + while ((line = afflst->getline())) { + mychomp(line); + + /* remove byte order mark */ + if (firstline) { + firstline = 0; + if (strncmp(line,"\xEF\xBB\xBF",3) == 0) memmove(line, line+3, strlen(line+3)+1); + } + + /* parse in the try string */ + if ((strncmp(line,"FLAG",4) == 0) && isspace(line[4])) { + if (flag_mode != FLAG_CHAR) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of the FLAG affix file parameter\n", afflst->getlinenum()); + } + if (strstr(line, "long")) flag_mode = FLAG_LONG; + if (strstr(line, "num")) flag_mode = FLAG_NUM; + if (strstr(line, "UTF-8")) flag_mode = FLAG_UNI; + if (flag_mode == FLAG_CHAR) { + HUNSPELL_WARNING(stderr, "error: line %d: FLAG needs `num', `long' or `UTF-8' parameter\n", afflst->getlinenum()); + } + } + if (strncmp(line,"FORBIDDENWORD",13) == 0) { + char * st = NULL; + if (parse_string(line, &st, afflst->getlinenum())) { + delete afflst; + return 1; + } + forbiddenword = decode_flag(st); + free(st); + } + if (strncmp(line, "SET", 3) == 0) { + if (parse_string(line, &enc, afflst->getlinenum())) { + delete afflst; + return 1; + } + if (strcmp(enc, "UTF-8") == 0) { + utf8 = 1; +#ifndef OPENOFFICEORG +#ifndef MOZILLA_CLIENT + initialize_utf_tbl(); +#endif +#endif + } else csconv = get_current_cs(enc); + } + if (strncmp(line, "LANG", 4) == 0) { + if (parse_string(line, &lang, afflst->getlinenum())) { + delete afflst; + return 1; + } + langnum = get_lang_num(lang); + } + + /* parse in the ignored characters (for example, Arabic optional diacritics characters */ + if (strncmp(line,"IGNORE",6) == 0) { + if (parse_array(line, &ignorechars, &ignorechars_utf16, + &ignorechars_utf16_len, utf8, afflst->getlinenum())) { + delete afflst; + return 1; + } + } + + if ((strncmp(line,"AF",2) == 0) && isspace(line[2])) { + if (parse_aliasf(line, afflst)) { + delete afflst; + return 1; + } + } + + if ((strncmp(line,"AM",2) == 0) && isspace(line[2])) { + if (parse_aliasm(line, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"COMPLEXPREFIXES",15) == 0) complexprefixes = 1; + if (((strncmp(line,"SFX",3) == 0) || (strncmp(line,"PFX",3) == 0)) && isspace(line[3])) break; + } + if (csconv == NULL) csconv = get_current_cs(SPELL_ENCODING); + delete afflst; + return 0; +} + +/* parse in the ALIAS table */ +int HashMgr::parse_aliasf(char * line, FileMgr * af) +{ + if (numaliasf != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + numaliasf = atoi(piece); + if (numaliasf < 1) { + numaliasf = 0; + aliasf = NULL; + aliasflen = NULL; + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); + return 1; + } + aliasf = (unsigned short **) malloc(numaliasf * sizeof(unsigned short *)); + aliasflen = (unsigned short *) malloc(numaliasf * sizeof(short)); + if (!aliasf || !aliasflen) { + numaliasf = 0; + if (aliasf) free(aliasf); + if (aliasflen) free(aliasflen); + aliasf = NULL; + aliasflen = NULL; + return 1; + } + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + numaliasf = 0; + free(aliasf); + free(aliasflen); + aliasf = NULL; + aliasflen = NULL; + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the numaliasf lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < numaliasf; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + aliasf[j] = NULL; + aliasflen[j] = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece,"AF",2) != 0) { + numaliasf = 0; + free(aliasf); + free(aliasflen); + aliasf = NULL; + aliasflen = NULL; + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + return 1; + } + break; + } + case 1: { + aliasflen[j] = (unsigned short) decode_flags(&(aliasf[j]), piece, af); + flag_qsort(aliasf[j], 0, aliasflen[j]); + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (!aliasf[j]) { + free(aliasf); + free(aliasflen); + aliasf = NULL; + aliasflen = NULL; + numaliasf = 0; + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + return 1; + } + } + return 0; +} + +int HashMgr::is_aliasf() { + return (aliasf != NULL); +} + +int HashMgr::get_aliasf(int index, unsigned short ** fvec, FileMgr * af) { + if ((index > 0) && (index <= numaliasf)) { + *fvec = aliasf[index - 1]; + return aliasflen[index - 1]; + } + HUNSPELL_WARNING(stderr, "error: line %d: bad flag alias index: %d\n", af->getlinenum(), index); + *fvec = NULL; + return 0; +} + +/* parse morph alias definitions */ +int HashMgr::parse_aliasm(char * line, FileMgr * af) +{ + if (numaliasm != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + numaliasm = atoi(piece); + if (numaliasm < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); + return 1; + } + aliasm = (char **) malloc(numaliasm * sizeof(char *)); + if (!aliasm) { + numaliasm = 0; + return 1; + } + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + numaliasm = 0; + free(aliasm); + aliasm = NULL; + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the numaliasm lines to read in the remainder of the table */ + char * nl = line; + for (int j=0; j < numaliasm; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + aliasm[j] = NULL; + piece = mystrsep(&tp, ' '); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece,"AM",2) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numaliasm = 0; + free(aliasm); + aliasm = NULL; + return 1; + } + break; + } + case 1: { + // add the remaining of the line + if (*tp) { + *(tp - 1) = ' '; + tp = tp + strlen(tp); + } + if (complexprefixes) { + if (utf8) reverseword_utf(piece); + else reverseword(piece); + } + aliasm[j] = mystrdup(piece); + if (!aliasm[j]) { + numaliasm = 0; + free(aliasm); + aliasm = NULL; + return 1; + } + break; } + default: break; + } + i++; + } + piece = mystrsep(&tp, ' '); + } + if (!aliasm[j]) { + numaliasm = 0; + free(aliasm); + aliasm = NULL; + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + return 1; + } + } + return 0; +} + +int HashMgr::is_aliasm() { + return (aliasm != NULL); +} + +char * HashMgr::get_aliasm(int index) { + if ((index > 0) && (index <= numaliasm)) return aliasm[index - 1]; + HUNSPELL_WARNING(stderr, "error: bad morph. alias index: %d\n", index); + return NULL; +} diff --git a/src/myspell/hashmgr.hxx b/src/myspell/hashmgr.hxx new file mode 100644 index 0000000..341b081 --- /dev/null +++ b/src/myspell/hashmgr.hxx @@ -0,0 +1,69 @@ +#ifndef _HASHMGR_HXX_ +#define _HASHMGR_HXX_ + +#include "hunvisapi.h" + +#include + +#include "htypes.hxx" +#include "filemgr.hxx" + +enum flag { FLAG_CHAR, FLAG_LONG, FLAG_NUM, FLAG_UNI }; + +class LIBHUNSPELL_DLL_EXPORTED HashMgr +{ + int tablesize; + struct hentry ** tableptr; + int userword; + flag flag_mode; + int complexprefixes; + int utf8; + unsigned short forbiddenword; + int langnum; + char * enc; + char * lang; + struct cs_info * csconv; + char * ignorechars; + unsigned short * ignorechars_utf16; + int ignorechars_utf16_len; + int numaliasf; // flag vector `compression' with aliases + unsigned short ** aliasf; + unsigned short * aliasflen; + int numaliasm; // morphological desciption `compression' with aliases + char ** aliasm; + + +public: + HashMgr(const char * tpath, const char * apath, const char * key = NULL); + ~HashMgr(); + + struct hentry * lookup(const char *) const; + int hash(const char *) const; + struct hentry * walk_hashtable(int & col, struct hentry * hp) const; + + int add(const char * word); + int add_with_affix(const char * word, const char * pattern); + int remove(const char * word); + int decode_flags(unsigned short ** result, char * flags, FileMgr * af); + unsigned short decode_flag(const char * flag); + char * encode_flag(unsigned short flag); + int is_aliasf(); + int get_aliasf(int index, unsigned short ** fvec, FileMgr * af); + int is_aliasm(); + char * get_aliasm(int index); + +private: + int get_clen_and_captype(const char * word, int wbl, int * captype); + int load_tables(const char * tpath, const char * key); + int add_word(const char * word, int wbl, int wcl, unsigned short * ap, + int al, const char * desc, bool onlyupcase); + int load_config(const char * affpath, const char * key); + int parse_aliasf(char * line, FileMgr * af); + int add_hidden_capitalized_word(char * word, int wbl, int wcl, + unsigned short * flags, int al, char * dp, int captype); + int parse_aliasm(char * line, FileMgr * af); + int remove_forbidden_flag(const char * word); + +}; + +#endif diff --git a/src/myspell/htypes.hxx b/src/myspell/htypes.hxx new file mode 100644 index 0000000..5b6c909 --- /dev/null +++ b/src/myspell/htypes.hxx @@ -0,0 +1,32 @@ +#ifndef _HTYPES_HXX_ +#define _HTYPES_HXX_ + +#define ROTATE_LEN 5 + +#define ROTATE(v,q) \ + (v) = ((v) << (q)) | (((v) >> (32 - q)) & ((1 << (q))-1)); + +// hentry options +#define H_OPT (1 << 0) +#define H_OPT_ALIASM (1 << 1) +#define H_OPT_PHON (1 << 2) + +// see also csutil.hxx +#define HENTRY_WORD(h) &(h->word[0]) + +// approx. number of user defined words +#define USERWORD 1000 + +struct hentry +{ + unsigned char blen; // word length in bytes + unsigned char clen; // word length in characters (different for UTF-8 enc.) + short alen; // length of affix flag vector + unsigned short * astr; // affix flag vector + struct hentry * next; // next word with same hash code + struct hentry * next_homonym; // next homonym word (with same hash code) + char var; // variable fields (only for special pronounciation yet) + char word[1]; // variable-length word (8-bit or UTF-8 encoding) +}; + +#endif diff --git a/src/myspell/hunspell.cxx b/src/myspell/hunspell.cxx new file mode 100644 index 0000000..77d4670 --- /dev/null +++ b/src/myspell/hunspell.cxx @@ -0,0 +1,2004 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include + +#include "hunspell.hxx" +#include "hunspell.h" + +#include "csutil.hxx" + +Hunspell::Hunspell(const char * affpath, const char * dpath, const char * key) +{ + encoding = NULL; + csconv = NULL; + utf8 = 0; + complexprefixes = 0; + affixpath = mystrdup(affpath); + maxdic = 0; + + /* first set up the hash manager */ + pHMgr[0] = new HashMgr(dpath, affpath, key); + if (pHMgr[0]) maxdic = 1; + + /* next set up the affix manager */ + /* it needs access to the hash manager lookup methods */ + pAMgr = new AffixMgr(affpath, pHMgr, &maxdic, key); + + /* get the preferred try string and the dictionary */ + /* encoding from the Affix Manager for that dictionary */ + char * try_string = pAMgr->get_try_string(); + encoding = pAMgr->get_encoding(); + langnum = pAMgr->get_langnum(); + utf8 = pAMgr->get_utf8(); + if (!utf8) + csconv = get_current_cs(encoding); + complexprefixes = pAMgr->get_complexprefixes(); + wordbreak = pAMgr->get_breaktable(); + + /* and finally set up the suggestion manager */ + pSMgr = new SuggestMgr(try_string, MAXSUGGESTION, pAMgr); + if (try_string) free(try_string); +} + +Hunspell::~Hunspell() +{ + if (pSMgr) delete pSMgr; + if (pAMgr) delete pAMgr; + for (int i = 0; i < maxdic; i++) delete pHMgr[i]; + maxdic = 0; + pSMgr = NULL; + pAMgr = NULL; +#ifdef MOZILLA_CLIENT + delete [] csconv; +#endif + csconv= NULL; + if (encoding) free(encoding); + encoding = NULL; + if (affixpath) free(affixpath); + affixpath = NULL; +} + +// load extra dictionaries +int Hunspell::add_dic(const char * dpath, const char * key) { + if (maxdic == MAXDIC || !affixpath) return 1; + pHMgr[maxdic] = new HashMgr(dpath, affixpath, key); + if (pHMgr[maxdic]) maxdic++; else return 1; + return 0; +} + +// make a copy of src at destination while removing all leading +// blanks and removing any trailing periods after recording +// their presence with the abbreviation flag +// also since already going through character by character, +// set the capitalization type +// return the length of the "cleaned" (and UTF-8 encoded) word + +int Hunspell::cleanword2(char * dest, const char * src, + w_char * dest_utf, int * nc, int * pcaptype, int * pabbrev) +{ + unsigned char * p = (unsigned char *) dest; + const unsigned char * q = (const unsigned char * ) src; + + // first skip over any leading blanks + while ((*q != '\0') && (*q == ' ')) q++; + + // now strip off any trailing periods (recording their presence) + *pabbrev = 0; + int nl = strlen((const char *)q); + while ((nl > 0) && (*(q+nl-1)=='.')) { + nl--; + (*pabbrev)++; + } + + // if no characters are left it can't be capitalized + if (nl <= 0) { + *pcaptype = NOCAP; + *p = '\0'; + return 0; + } + + strncpy(dest, (char *) q, nl); + *(dest + nl) = '\0'; + nl = strlen(dest); + if (utf8) { + *nc = u8_u16(dest_utf, MAXWORDLEN, dest); + // don't check too long words + if (*nc >= MAXWORDLEN) return 0; + if (*nc == -1) { // big Unicode character (non BMP area) + *pcaptype = NOCAP; + return nl; + } + *pcaptype = get_captype_utf8(dest_utf, *nc, langnum); + } else { + *pcaptype = get_captype(dest, nl, csconv); + *nc = nl; + } + return nl; +} + +int Hunspell::cleanword(char * dest, const char * src, + int * pcaptype, int * pabbrev) +{ + unsigned char * p = (unsigned char *) dest; + const unsigned char * q = (const unsigned char * ) src; + int firstcap = 0; + + // first skip over any leading blanks + while ((*q != '\0') && (*q == ' ')) q++; + + // now strip off any trailing periods (recording their presence) + *pabbrev = 0; + int nl = strlen((const char *)q); + while ((nl > 0) && (*(q+nl-1)=='.')) { + nl--; + (*pabbrev)++; + } + + // if no characters are left it can't be capitalized + if (nl <= 0) { + *pcaptype = NOCAP; + *p = '\0'; + return 0; + } + + // now determine the capitalization type of the first nl letters + int ncap = 0; + int nneutral = 0; + int nc = 0; + + if (!utf8) { + while (nl > 0) { + nc++; + if (csconv[(*q)].ccase) ncap++; + if (csconv[(*q)].cupper == csconv[(*q)].clower) nneutral++; + *p++ = *q++; + nl--; + } + // remember to terminate the destination string + *p = '\0'; + firstcap = csconv[(unsigned char)(*dest)].ccase; + } else { + unsigned short idx; + w_char t[MAXWORDLEN]; + nc = u8_u16(t, MAXWORDLEN, src); + for (int i = 0; i < nc; i++) { + idx = (t[i].h << 8) + t[i].l; + unsigned short low = unicodetolower(idx, langnum); + if (idx != low) ncap++; + if (unicodetoupper(idx, langnum) == low) nneutral++; + } + u16_u8(dest, MAXWORDUTF8LEN, t, nc); + if (ncap) { + idx = (t[0].h << 8) + t[0].l; + firstcap = (idx != unicodetolower(idx, langnum)); + } + } + + // now finally set the captype + if (ncap == 0) { + *pcaptype = NOCAP; + } else if ((ncap == 1) && firstcap) { + *pcaptype = INITCAP; + } else if ((ncap == nc) || ((ncap + nneutral) == nc)){ + *pcaptype = ALLCAP; + } else if ((ncap > 1) && firstcap) { + *pcaptype = HUHINITCAP; + } else { + *pcaptype = HUHCAP; + } + return strlen(dest); +} + +void Hunspell::mkallcap(char * p) +{ + if (utf8) { + w_char u[MAXWORDLEN]; + int nc = u8_u16(u, MAXWORDLEN, p); + unsigned short idx; + for (int i = 0; i < nc; i++) { + idx = (u[i].h << 8) + u[i].l; + if (idx != unicodetoupper(idx, langnum)) { + u[i].h = (unsigned char) (unicodetoupper(idx, langnum) >> 8); + u[i].l = (unsigned char) (unicodetoupper(idx, langnum) & 0x00FF); + } + } + u16_u8(p, MAXWORDUTF8LEN, u, nc); + } else { + while (*p != '\0') { + *p = csconv[((unsigned char) *p)].cupper; + p++; + } + } +} + +int Hunspell::mkallcap2(char * p, w_char * u, int nc) +{ + if (utf8) { + unsigned short idx; + for (int i = 0; i < nc; i++) { + idx = (u[i].h << 8) + u[i].l; + unsigned short up = unicodetoupper(idx, langnum); + if (idx != up) { + u[i].h = (unsigned char) (up >> 8); + u[i].l = (unsigned char) (up & 0x00FF); + } + } + u16_u8(p, MAXWORDUTF8LEN, u, nc); + return strlen(p); + } else { + while (*p != '\0') { + *p = csconv[((unsigned char) *p)].cupper; + p++; + } + } + return nc; +} + + +void Hunspell::mkallsmall(char * p) +{ + while (*p != '\0') { + *p = csconv[((unsigned char) *p)].clower; + p++; + } +} + +int Hunspell::mkallsmall2(char * p, w_char * u, int nc) +{ + if (utf8) { + unsigned short idx; + for (int i = 0; i < nc; i++) { + idx = (u[i].h << 8) + u[i].l; + unsigned short low = unicodetolower(idx, langnum); + if (idx != low) { + u[i].h = (unsigned char) (low >> 8); + u[i].l = (unsigned char) (low & 0x00FF); + } + } + u16_u8(p, MAXWORDUTF8LEN, u, nc); + return strlen(p); + } else { + while (*p != '\0') { + *p = csconv[((unsigned char) *p)].clower; + p++; + } + } + return nc; +} + +// convert UTF-8 sharp S codes to latin 1 +char * Hunspell::sharps_u8_l1(char * dest, char * source) { + char * p = dest; + *p = *source; + for (p++, source++; *(source - 1); p++, source++) { + *p = *source; + if (*source == '\x9F') *--p = '\xDF'; + } + return dest; +} + +// recursive search for right ss - sharp s permutations +hentry * Hunspell::spellsharps(char * base, char * pos, int n, + int repnum, char * tmp, int * info, char **root) { + pos = strstr(pos, "ss"); + if (pos && (n < MAXSHARPS)) { + *pos = '\xC3'; + *(pos + 1) = '\x9F'; + hentry * h = spellsharps(base, pos + 2, n + 1, repnum + 1, tmp, info, root); + if (h) return h; + *pos = 's'; + *(pos + 1) = 's'; + h = spellsharps(base, pos + 2, n + 1, repnum, tmp, info, root); + if (h) return h; + } else if (repnum > 0) { + if (utf8) return checkword(base, info, root); + return checkword(sharps_u8_l1(tmp, base), info, root); + } + return NULL; +} + +int Hunspell::is_keepcase(const hentry * rv) { + return pAMgr && rv->astr && pAMgr->get_keepcase() && + TESTAFF(rv->astr, pAMgr->get_keepcase(), rv->alen); +} + +/* insert a word to the beginning of the suggestion array and return ns */ +int Hunspell::insert_sug(char ***slst, char * word, int ns) { + char * dup = mystrdup(word); + if (!dup) return ns; + if (ns == MAXSUGGESTION) { + ns--; + free((*slst)[ns]); + } + for (int k = ns; k > 0; k--) (*slst)[k] = (*slst)[k - 1]; + (*slst)[0] = dup; + return ns + 1; +} + +int Hunspell::spell(const char * word, int * info, char ** root) +{ + struct hentry * rv=NULL; + // need larger vector. For example, Turkish capital letter I converted a + // 2-byte UTF-8 character (dotless i) by mkallsmall. + char cw[MAXWORDUTF8LEN]; + char wspace[MAXWORDUTF8LEN]; + w_char unicw[MAXWORDLEN]; + // Hunspell supports XML input of the simplified API (see manual) + if (strcmp(word, SPELL_XML) == 0) return 1; + int nc = strlen(word); + int wl2 = 0; + if (utf8) { + if (nc >= MAXWORDUTF8LEN) return 0; + } else { + if (nc >= MAXWORDLEN) return 0; + } + int captype = 0; + int abbv = 0; + int wl = 0; + + // input conversion + RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL; + if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv); + else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv); + + int info2 = 0; + if (wl == 0 || maxdic == 0) return 1; + if (root) *root = NULL; + + // allow numbers with dots, dashes and commas (but forbid double separators: "..", "--" etc.) + enum { NBEGIN, NNUM, NSEP }; + int nstate = NBEGIN; + int i; + + for (i = 0; (i < wl); i++) { + if ((cw[i] <= '9') && (cw[i] >= '0')) { + nstate = NNUM; + } else if ((cw[i] == ',') || (cw[i] == '.') || (cw[i] == '-')) { + if ((nstate == NSEP) || (i == 0)) break; + nstate = NSEP; + } else break; + } + if ((i == wl) && (nstate == NNUM)) return 1; + if (!info) info = &info2; else *info = 0; + + switch(captype) { + case HUHCAP: + case HUHINITCAP: + *info += SPELL_ORIGCAP; + case NOCAP: { + rv = checkword(cw, info, root); + if ((abbv) && !(rv)) { + memcpy(wspace,cw,wl); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + rv = checkword(wspace, info, root); + } + break; + } + case ALLCAP: { + *info += SPELL_ORIGCAP; + rv = checkword(cw, info, root); + if (rv) break; + if (abbv) { + memcpy(wspace,cw,wl); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + rv = checkword(wspace, info, root); + if (rv) break; + } + // Spec. prefix handling for Catalan, French, Italian: + // prefixes separated by apostrophe (SANT'ELIA -> Sant'+Elia). + if (pAMgr && strchr(cw, '\'')) { + wl = mkallsmall2(cw, unicw, nc); + //There are no really sane circumstances where this could fail, + //but anyway... + if (char * apostrophe = strchr(cw, '\'')) { + if (utf8) { + w_char tmpword[MAXWORDLEN]; + *apostrophe = '\0'; + wl2 = u8_u16(tmpword, MAXWORDLEN, cw); + *apostrophe = '\''; + if (wl2 < nc) { + mkinitcap2(apostrophe + 1, unicw + wl2 + 1, nc - wl2 - 1); + rv = checkword(cw, info, root); + if (rv) break; + } + } else { + mkinitcap2(apostrophe + 1, unicw, nc); + rv = checkword(cw, info, root); + if (rv) break; + } + } + mkinitcap2(cw, unicw, nc); + rv = checkword(cw, info, root); + if (rv) break; + } + if (pAMgr && pAMgr->get_checksharps() && strstr(cw, "SS")) { + char tmpword[MAXWORDUTF8LEN]; + wl = mkallsmall2(cw, unicw, nc); + memcpy(wspace,cw,(wl+1)); + rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root); + if (!rv) { + wl2 = mkinitcap2(cw, unicw, nc); + rv = spellsharps(cw, cw, 0, 0, tmpword, info, root); + } + if ((abbv) && !(rv)) { + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root); + if (!rv) { + memcpy(wspace, cw, wl2); + *(wspace+wl2) = '.'; + *(wspace+wl2+1) = '\0'; + rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root); + } + } + if (rv) break; + } + } + case INITCAP: { + *info += SPELL_ORIGCAP; + wl = mkallsmall2(cw, unicw, nc); + memcpy(wspace,cw,(wl+1)); + wl2 = mkinitcap2(cw, unicw, nc); + if (captype == INITCAP) *info += SPELL_INITCAP; + rv = checkword(cw, info, root); + if (captype == INITCAP) *info -= SPELL_INITCAP; + // forbid bad capitalization + // (for example, ijs -> Ijs instead of IJs in Dutch) + // use explicit forms in dic: Ijs/F (F = FORBIDDENWORD flag) + if (*info & SPELL_FORBIDDEN) { + rv = NULL; + break; + } + if (rv && is_keepcase(rv) && (captype == ALLCAP)) rv = NULL; + if (rv) break; + + rv = checkword(wspace, info, root); + if (abbv && !rv) { + + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + rv = checkword(wspace, info, root); + if (!rv) { + memcpy(wspace, cw, wl2); + *(wspace+wl2) = '.'; + *(wspace+wl2+1) = '\0'; + if (captype == INITCAP) *info += SPELL_INITCAP; + rv = checkword(wspace, info, root); + if (captype == INITCAP) *info -= SPELL_INITCAP; + if (rv && is_keepcase(rv) && (captype == ALLCAP)) rv = NULL; + break; + } + } + if (rv && is_keepcase(rv) && + ((captype == ALLCAP) || + // if CHECKSHARPS: KEEPCASE words with \xDF are allowed + // in INITCAP form, too. + !(pAMgr->get_checksharps() && + ((utf8 && strstr(wspace, "\xC3\x9F")) || + (!utf8 && strchr(wspace, '\xDF')))))) rv = NULL; + break; + } + } + + if (rv) { + if (pAMgr && pAMgr->get_warn() && rv->astr && + TESTAFF(rv->astr, pAMgr->get_warn(), rv->alen)) { + *info += SPELL_WARN; + if (pAMgr->get_forbidwarn()) return 0; + return HUNSPELL_OK_WARN; + } + return HUNSPELL_OK; + } + + // recursive breaking at break points + if (wordbreak) { + char * s; + char r; + int nbr = 0; + wl = strlen(cw); + int numbreak = pAMgr ? pAMgr->get_numbreak() : 0; + + // calculate break points for recursion limit + for (int j = 0; j < numbreak; j++) { + s = cw; + do { + s = (char *) strstr(s, wordbreak[j]); + if (s) { + nbr++; + s++; + } + } while (s); + } + if (nbr >= 10) return 0; + + // check boundary patterns (^begin and end$) + for (int j = 0; j < numbreak; j++) { + int plen = strlen(wordbreak[j]); + if (plen == 1 || plen > wl) continue; + if (wordbreak[j][0] == '^' && strncmp(cw, wordbreak[j] + 1, plen - 1) == 0 + && spell(cw + plen - 1)) return 1; + if (wordbreak[j][plen - 1] == '$' && + strncmp(cw + wl - plen + 1, wordbreak[j], plen - 1) == 0) { + r = cw[wl - plen + 1]; + cw[wl - plen + 1] = '\0'; + if (spell(cw)) return 1; + cw[wl - plen + 1] = r; + } + } + + // other patterns + for (int j = 0; j < numbreak; j++) { + int plen = strlen(wordbreak[j]); + s=(char *) strstr(cw, wordbreak[j]); + if (s && (s > cw) && (s < cw + wl - plen)) { + if (!spell(s + plen)) continue; + r = *s; + *s = '\0'; + // examine 2 sides of the break point + if (spell(cw)) return 1; + *s = r; + + // LANG_hu: spec. dash rule + if (langnum == LANG_hu && strcmp(wordbreak[j], "-") == 0) { + r = s[1]; + s[1] = '\0'; + if (spell(cw)) return 1; // check the first part with dash + s[1] = r; + } + // end of LANG speficic region + + } + } + } + + return 0; +} + +struct hentry * Hunspell::checkword(const char * w, int * info, char ** root) +{ + struct hentry * he = NULL; + int len, i; + char w2[MAXWORDUTF8LEN]; + const char * word; + + char * ignoredchars = pAMgr->get_ignore(); + if (ignoredchars != NULL) { + strcpy(w2, w); + if (utf8) { + int ignoredchars_utf16_len; + unsigned short * ignoredchars_utf16 = pAMgr->get_ignore_utf16(&ignoredchars_utf16_len); + remove_ignored_chars_utf(w2, ignoredchars_utf16, ignoredchars_utf16_len); + } else { + remove_ignored_chars(w2,ignoredchars); + } + word = w2; + } else word = w; + + len = strlen(word); + + if (!len) + return NULL; + + // word reversing wrapper for complex prefixes + if (complexprefixes) { + if (word != w2) { + strcpy(w2, word); + word = w2; + } + if (utf8) reverseword_utf(w2); else reverseword(w2); + } + + // look word in hash table + for (i = 0; (i < maxdic) && !he; i ++) { + he = (pHMgr[i])->lookup(word); + + // check forbidden and onlyincompound words + if ((he) && (he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) { + if (info) *info += SPELL_FORBIDDEN; + // LANG_hu section: set dash information for suggestions + if (langnum == LANG_hu) { + if (pAMgr->get_compoundflag() && + TESTAFF(he->astr, pAMgr->get_compoundflag(), he->alen)) { + if (info) *info += SPELL_COMPOUND; + } + } + return NULL; + } + + // he = next not needaffix, onlyincompound homonym or onlyupcase word + while (he && (he->astr) && + ((pAMgr->get_needaffix() && TESTAFF(he->astr, pAMgr->get_needaffix(), he->alen)) || + (pAMgr->get_onlyincompound() && TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) || + (info && (*info & SPELL_INITCAP) && TESTAFF(he->astr, ONLYUPCASEFLAG, he->alen)) + )) he = he->next_homonym; + } + + // check with affixes + if (!he && pAMgr) { + // try stripping off affixes */ + he = pAMgr->affix_check(word, len, 0); + + // check compound restriction and onlyupcase + if (he && he->astr && ( + (pAMgr->get_onlyincompound() && + TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) || + (info && (*info & SPELL_INITCAP) && + TESTAFF(he->astr, ONLYUPCASEFLAG, he->alen)))) { + he = NULL; + } + + if (he) { + if ((he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) { + if (info) *info += SPELL_FORBIDDEN; + return NULL; + } + if (root) { + *root = mystrdup(he->word); + if (*root && complexprefixes) { + if (utf8) reverseword_utf(*root); else reverseword(*root); + } + } + // try check compound word + } else if (pAMgr->get_compound()) { + he = pAMgr->compound_check(word, len, 0, 0, 100, 0, NULL, 0, 0, *info); + // LANG_hu section: `moving rule' with last dash + if ((!he) && (langnum == LANG_hu) && (word[len-1] == '-')) { + char * dup = mystrdup(word); + if (!dup) return NULL; + dup[len-1] = '\0'; + he = pAMgr->compound_check(dup, len-1, -5, 0, 100, 0, NULL, 1, 0, *info); + free(dup); + } + // end of LANG speficic region + if (he) { + if (root) { + *root = mystrdup(he->word); + if (*root && complexprefixes) { + if (utf8) reverseword_utf(*root); else reverseword(*root); + } + } + if (info) *info += SPELL_COMPOUND; + } + } + + } + + return he; +} + +int Hunspell::suggest(char*** slst, const char * word) +{ + int onlycmpdsug = 0; + char cw[MAXWORDUTF8LEN]; + char wspace[MAXWORDUTF8LEN]; + if (!pSMgr || maxdic == 0) return 0; + w_char unicw[MAXWORDLEN]; + *slst = NULL; + // process XML input of the simplified API (see manual) + if (strncmp(word, SPELL_XML, sizeof(SPELL_XML) - 3) == 0) { + return spellml(slst, word); + } + int nc = strlen(word); + if (utf8) { + if (nc >= MAXWORDUTF8LEN) return 0; + } else { + if (nc >= MAXWORDLEN) return 0; + } + int captype = 0; + int abbv = 0; + int wl = 0; + + // input conversion + RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL; + if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv); + else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv); + + if (wl == 0) return 0; + int ns = 0; + int capwords = 0; + + // check capitalized form for FORCEUCASE + if (pAMgr && captype == NOCAP && pAMgr->get_forceucase()) { + int info = SPELL_ORIGCAP; + char ** wlst; + if (checkword(cw, &info, NULL)) { + if (*slst) { + wlst = *slst; + } else { + wlst = (char **) malloc(MAXSUGGESTION * sizeof(char *)); + if (wlst == NULL) return -1; + *slst = wlst; + for (int i = 0; i < MAXSUGGESTION; i++) { + wlst[i] = NULL; + } + } + wlst[0] = mystrdup(cw); + mkinitcap(wlst[0]); + return 1; + } + } + + switch(captype) { + case NOCAP: { + ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug); + break; + } + + case INITCAP: { + capwords = 1; + ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug); + if (ns == -1) break; + memcpy(wspace,cw,(wl+1)); + mkallsmall2(wspace, unicw, nc); + ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug); + break; + } + case HUHINITCAP: + capwords = 1; + case HUHCAP: { + ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug); + if (ns != -1) { + int prevns; + // something.The -> something. The + char * dot = strchr(cw, '.'); + if (dot && (dot > cw)) { + int captype_; + if (utf8) { + w_char w_[MAXWORDLEN]; + int wl_ = u8_u16(w_, MAXWORDLEN, dot + 1); + captype_ = get_captype_utf8(w_, wl_, langnum); + } else captype_ = get_captype(dot+1, strlen(dot+1), csconv); + if (captype_ == INITCAP) { + char * st = mystrdup(cw); + if (st) st = (char *) realloc(st, wl + 2); + if (st) { + st[(dot - cw) + 1] = ' '; + strcpy(st + (dot - cw) + 2, dot + 1); + ns = insert_sug(slst, st, ns); + free(st); + } + } + } + if (captype == HUHINITCAP) { + // TheOpenOffice.org -> The OpenOffice.org + memcpy(wspace,cw,(wl+1)); + mkinitsmall2(wspace, unicw, nc); + ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug); + } + memcpy(wspace,cw,(wl+1)); + mkallsmall2(wspace, unicw, nc); + if (spell(wspace)) ns = insert_sug(slst, wspace, ns); + prevns = ns; + ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug); + if (captype == HUHINITCAP) { + mkinitcap2(wspace, unicw, nc); + if (spell(wspace)) ns = insert_sug(slst, wspace, ns); + ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug); + } + // aNew -> "a New" (instead of "a new") + for (int j = prevns; j < ns; j++) { + char * space = strchr((*slst)[j],' '); + if (space) { + int slen = strlen(space + 1); + // different case after space (need capitalisation) + if ((slen < wl) && strcmp(cw + wl - slen, space + 1)) { + w_char w[MAXWORDLEN]; + int wc = 0; + char * r = (*slst)[j]; + if (utf8) wc = u8_u16(w, MAXWORDLEN, space + 1); + mkinitcap2(space + 1, w, wc); + // set as first suggestion + for (int k = j; k > 0; k--) (*slst)[k] = (*slst)[k - 1]; + (*slst)[0] = r; + } + } + } + } + break; + } + + case ALLCAP: { + memcpy(wspace, cw, (wl+1)); + mkallsmall2(wspace, unicw, nc); + ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug); + if (ns == -1) break; + if (pAMgr && pAMgr->get_keepcase() && spell(wspace)) + ns = insert_sug(slst, wspace, ns); + mkinitcap2(wspace, unicw, nc); + ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug); + for (int j=0; j < ns; j++) { + mkallcap((*slst)[j]); + if (pAMgr && pAMgr->get_checksharps()) { + char * pos; + if (utf8) { + pos = strstr((*slst)[j], "\xC3\x9F"); + while (pos) { + *pos = 'S'; + *(pos+1) = 'S'; + pos = strstr(pos+2, "\xC3\x9F"); + } + } else { + pos = strchr((*slst)[j], '\xDF'); + while (pos) { + (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 2); + mystrrep((*slst)[j], "\xDF", "SS"); + pos = strchr((*slst)[j], '\xDF'); + } + } + } + } + break; + } + } + + // LANG_hu section: replace '-' with ' ' in Hungarian + if (langnum == LANG_hu) { + for (int j=0; j < ns; j++) { + char * pos = strchr((*slst)[j],'-'); + if (pos) { + int info; + char w[MAXWORDUTF8LEN]; + *pos = '\0'; + strcpy(w, (*slst)[j]); + strcat(w, pos + 1); + spell(w, &info, NULL); + if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) { + *pos = ' '; + } else *pos = '-'; + } + } + } + // END OF LANG_hu section + + // try ngram approach since found nothing + if ((ns == 0 || onlycmpdsug) && pAMgr && (pAMgr->get_maxngramsugs() != 0) && (*slst)) { + switch(captype) { + case NOCAP: { + ns = pSMgr->ngsuggest(*slst, cw, ns, pHMgr, maxdic); + break; + } + case HUHINITCAP: + capwords = 1; + case HUHCAP: { + memcpy(wspace,cw,(wl+1)); + mkallsmall2(wspace, unicw, nc); + ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic); + break; + } + case INITCAP: { + capwords = 1; + memcpy(wspace,cw,(wl+1)); + mkallsmall2(wspace, unicw, nc); + ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic); + break; + } + case ALLCAP: { + memcpy(wspace,cw,(wl+1)); + mkallsmall2(wspace, unicw, nc); + int oldns = ns; + ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic); + for (int j = oldns; j < ns; j++) + mkallcap((*slst)[j]); + break; + } + } + } + + // try dash suggestion (Afo-American -> Afro-American) + if (char * pos = strchr(cw, '-')) { + char * ppos = cw; + int nodashsug = 1; + char ** nlst = NULL; + int nn = 0; + int last = 0; + if (*slst) { + for (int j = 0; j < ns && nodashsug == 1; j++) { + if (strchr((*slst)[j], '-')) nodashsug = 0; + } + } + while (nodashsug && !last) { + if (*pos == '\0') last = 1; else *pos = '\0'; + if (!spell(ppos)) { + nn = suggest(&nlst, ppos); + for (int j = nn - 1; j >= 0; j--) { + strncpy(wspace, cw, ppos - cw); + strcpy(wspace + (ppos - cw), nlst[j]); + if (!last) { + strcat(wspace, "-"); + strcat(wspace, pos + 1); + } + ns = insert_sug(slst, wspace, ns); + free(nlst[j]); + } + if (nlst != NULL) free(nlst); + nodashsug = 0; + } + if (!last) { + *pos = '-'; + ppos = pos + 1; + pos = strchr(ppos, '-'); + } + if (!pos) pos = cw + strlen(cw); + } + } + + // word reversing wrapper for complex prefixes + if (complexprefixes) { + for (int j = 0; j < ns; j++) { + if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]); + } + } + + // capitalize + if (capwords) for (int j=0; j < ns; j++) { + mkinitcap((*slst)[j]); + } + + // expand suggestions with dot(s) + if (abbv && pAMgr && pAMgr->get_sugswithdots()) { + for (int j = 0; j < ns; j++) { + (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv); + strcat((*slst)[j], word + strlen(word) - abbv); + } + } + + // remove bad capitalized and forbidden forms + if (pAMgr && (pAMgr->get_keepcase() || pAMgr->get_forbiddenword())) { + switch (captype) { + case INITCAP: + case ALLCAP: { + int l = 0; + for (int j=0; j < ns; j++) { + if (!strchr((*slst)[j],' ') && !spell((*slst)[j])) { + char s[MAXSWUTF8L]; + w_char w[MAXSWL]; + int len; + if (utf8) { + len = u8_u16(w, MAXSWL, (*slst)[j]); + } else { + strcpy(s, (*slst)[j]); + len = strlen(s); + } + mkallsmall2(s, w, len); + free((*slst)[j]); + if (spell(s)) { + (*slst)[l] = mystrdup(s); + if ((*slst)[l]) l++; + } else { + mkinitcap2(s, w, len); + if (spell(s)) { + (*slst)[l] = mystrdup(s); + if ((*slst)[l]) l++; + } + } + } else { + (*slst)[l] = (*slst)[j]; + l++; + } + } + ns = l; + } + } + } + + // remove duplications + int l = 0; + for (int j = 0; j < ns; j++) { + (*slst)[l] = (*slst)[j]; + for (int k = 0; k < l; k++) { + if (strcmp((*slst)[k], (*slst)[j]) == 0) { + free((*slst)[j]); + l--; + break; + } + } + l++; + } + ns = l; + + // output conversion + rl = (pAMgr) ? pAMgr->get_oconvtable() : NULL; + for (int j = 0; rl && j < ns; j++) { + if (rl->conv((*slst)[j], wspace)) { + free((*slst)[j]); + (*slst)[j] = mystrdup(wspace); + } + } + + // if suggestions removed by nosuggest, onlyincompound parameters + if (l == 0 && *slst) { + free(*slst); + *slst = NULL; + } + return l; +} + +void Hunspell::free_list(char *** slst, int n) { + freelist(slst, n); +} + +char * Hunspell::get_dic_encoding() +{ + return encoding; +} + +#ifdef HUNSPELL_EXPERIMENTAL +// XXX need UTF-8 support +int Hunspell::suggest_auto(char*** slst, const char * word) +{ + char cw[MAXWORDUTF8LEN]; + char wspace[MAXWORDUTF8LEN]; + if (!pSMgr || maxdic == 0) return 0; + int wl = strlen(word); + if (utf8) { + if (wl >= MAXWORDUTF8LEN) return 0; + } else { + if (wl >= MAXWORDLEN) return 0; + } + int captype = 0; + int abbv = 0; + wl = cleanword(cw, word, &captype, &abbv); + if (wl == 0) return 0; + int ns = 0; + *slst = NULL; // HU, nsug in pSMgr->suggest + + switch(captype) { + case NOCAP: { + ns = pSMgr->suggest_auto(slst, cw, ns); + if (ns>0) break; + break; + } + + case INITCAP: { + memcpy(wspace,cw,(wl+1)); + mkallsmall(wspace); + ns = pSMgr->suggest_auto(slst, wspace, ns); + for (int j=0; j < ns; j++) + mkinitcap((*slst)[j]); + ns = pSMgr->suggest_auto(slst, cw, ns); + break; + + } + + case HUHINITCAP: + case HUHCAP: { + ns = pSMgr->suggest_auto(slst, cw, ns); + if (ns == 0) { + memcpy(wspace,cw,(wl+1)); + mkallsmall(wspace); + ns = pSMgr->suggest_auto(slst, wspace, ns); + } + break; + } + + case ALLCAP: { + memcpy(wspace,cw,(wl+1)); + mkallsmall(wspace); + ns = pSMgr->suggest_auto(slst, wspace, ns); + + mkinitcap(wspace); + ns = pSMgr->suggest_auto(slst, wspace, ns); + + for (int j=0; j < ns; j++) + mkallcap((*slst)[j]); + break; + } + } + + // word reversing wrapper for complex prefixes + if (complexprefixes) { + for (int j = 0; j < ns; j++) { + if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]); + } + } + + // expand suggestions with dot(s) + if (abbv && pAMgr && pAMgr->get_sugswithdots()) { + for (int j = 0; j < ns; j++) { + (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv); + strcat((*slst)[j], word + strlen(word) - abbv); + } + } + + // LANG_hu section: replace '-' with ' ' in Hungarian + if (langnum == LANG_hu) { + for (int j=0; j < ns; j++) { + char * pos = strchr((*slst)[j],'-'); + if (pos) { + int info; + char w[MAXWORDUTF8LEN]; + *pos = '\0'; + strcpy(w, (*slst)[j]); + strcat(w, pos + 1); + spell(w, &info, NULL); + if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) { + *pos = ' '; + } else *pos = '-'; + } + } + } + // END OF LANG_hu section + return ns; +} +#endif + +int Hunspell::stem(char*** slst, char ** desc, int n) +{ + char result[MAXLNLEN]; + char result2[MAXLNLEN]; + *slst = NULL; + if (n == 0) return 0; + *result2 = '\0'; + for (int i = 0; i < n; i++) { + *result = '\0'; + // add compound word parts (except the last one) + char * s = (char *) desc[i]; + char * part = strstr(s, MORPH_PART); + if (part) { + char * nextpart = strstr(part + 1, MORPH_PART); + while (nextpart) { + copy_field(result + strlen(result), part, MORPH_PART); + part = nextpart; + nextpart = strstr(part + 1, MORPH_PART); + } + s = part; + } + + char **pl; + char tok[MAXLNLEN]; + strcpy(tok, s); + char * alt = strstr(tok, " | "); + while (alt) { + alt[1] = MSEP_ALT; + alt = strstr(alt, " | "); + } + int pln = line_tok(tok, &pl, MSEP_ALT); + for (int k = 0; k < pln; k++) { + // add derivational suffixes + if (strstr(pl[k], MORPH_DERI_SFX)) { + // remove inflectional suffixes + char * is = strstr(pl[k], MORPH_INFL_SFX); + if (is) *is = '\0'; + char * sg = pSMgr->suggest_gen(&(pl[k]), 1, pl[k]); + if (sg) { + char ** gen; + int genl = line_tok(sg, &gen, MSEP_REC); + free(sg); + for (int j = 0; j < genl; j++) { + sprintf(result2 + strlen(result2), "%c%s%s", + MSEP_REC, result, gen[j]); + } + freelist(&gen, genl); + } + } else { + sprintf(result2 + strlen(result2), "%c%s", MSEP_REC, result); + if (strstr(pl[k], MORPH_SURF_PFX)) { + copy_field(result2 + strlen(result2), pl[k], MORPH_SURF_PFX); + } + copy_field(result2 + strlen(result2), pl[k], MORPH_STEM); + } + } + freelist(&pl, pln); + } + int sln = line_tok(result2, slst, MSEP_REC); + return uniqlist(*slst, sln); + +} + +int Hunspell::stem(char*** slst, const char * word) +{ + char ** pl; + int pln = analyze(&pl, word); + int pln2 = stem(slst, pl, pln); + freelist(&pl, pln); + return pln2; +} + +#ifdef HUNSPELL_EXPERIMENTAL +int Hunspell::suggest_pos_stems(char*** slst, const char * word) +{ + char cw[MAXWORDUTF8LEN]; + char wspace[MAXWORDUTF8LEN]; + if (! pSMgr || maxdic == 0) return 0; + int wl = strlen(word); + if (utf8) { + if (wl >= MAXWORDUTF8LEN) return 0; + } else { + if (wl >= MAXWORDLEN) return 0; + } + int captype = 0; + int abbv = 0; + wl = cleanword(cw, word, &captype, &abbv); + if (wl == 0) return 0; + + int ns = 0; // ns=0 = normalized input + + *slst = NULL; // HU, nsug in pSMgr->suggest + + switch(captype) { + case HUHCAP: + case NOCAP: { + ns = pSMgr->suggest_pos_stems(slst, cw, ns); + + if ((abbv) && (ns == 0)) { + memcpy(wspace,cw,wl); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + ns = pSMgr->suggest_pos_stems(slst, wspace, ns); + } + + break; + } + + case INITCAP: { + + ns = pSMgr->suggest_pos_stems(slst, cw, ns); + + if (ns == 0 || ((*slst)[0][0] == '#')) { + memcpy(wspace,cw,(wl+1)); + mkallsmall(wspace); + ns = pSMgr->suggest_pos_stems(slst, wspace, ns); + } + + break; + + } + + case ALLCAP: { + ns = pSMgr->suggest_pos_stems(slst, cw, ns); + if (ns != 0) break; + + memcpy(wspace,cw,(wl+1)); + mkallsmall(wspace); + ns = pSMgr->suggest_pos_stems(slst, wspace, ns); + + if (ns == 0) { + mkinitcap(wspace); + ns = pSMgr->suggest_pos_stems(slst, wspace, ns); + } + break; + } + } + + return ns; +} +#endif // END OF HUNSPELL_EXPERIMENTAL CODE + +const char * Hunspell::get_wordchars() +{ + return pAMgr->get_wordchars(); +} + +unsigned short * Hunspell::get_wordchars_utf16(int * len) +{ + return pAMgr->get_wordchars_utf16(len); +} + +void Hunspell::mkinitcap(char * p) +{ + if (!utf8) { + if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper; + } else { + int len; + w_char u[MAXWORDLEN]; + len = u8_u16(u, MAXWORDLEN, p); + unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum); + u[0].h = (unsigned char) (i >> 8); + u[0].l = (unsigned char) (i & 0x00FF); + u16_u8(p, MAXWORDUTF8LEN, u, len); + } +} + +int Hunspell::mkinitcap2(char * p, w_char * u, int nc) +{ + if (!utf8) { + if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper; + } else if (nc > 0) { + unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum); + u[0].h = (unsigned char) (i >> 8); + u[0].l = (unsigned char) (i & 0x00FF); + u16_u8(p, MAXWORDUTF8LEN, u, nc); + return strlen(p); + } + return nc; +} + +int Hunspell::mkinitsmall2(char * p, w_char * u, int nc) +{ + if (!utf8) { + if (*p != '\0') *p = csconv[((unsigned char)*p)].clower; + } else if (nc > 0) { + unsigned short i = unicodetolower((u[0].h << 8) + u[0].l, langnum); + u[0].h = (unsigned char) (i >> 8); + u[0].l = (unsigned char) (i & 0x00FF); + u16_u8(p, MAXWORDUTF8LEN, u, nc); + return strlen(p); + } + return nc; +} + +int Hunspell::add(const char * word) +{ + if (pHMgr[0]) return (pHMgr[0])->add(word); + return 0; +} + +int Hunspell::add_with_affix(const char * word, const char * example) +{ + if (pHMgr[0]) return (pHMgr[0])->add_with_affix(word, example); + return 0; +} + +int Hunspell::remove(const char * word) +{ + if (pHMgr[0]) return (pHMgr[0])->remove(word); + return 0; +} + +const char * Hunspell::get_version() +{ + return pAMgr->get_version(); +} + +struct cs_info * Hunspell::get_csconv() +{ + return csconv; +} + +void Hunspell::cat_result(char * result, char * st) +{ + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } +} + +int Hunspell::analyze(char*** slst, const char * word) +{ + char cw[MAXWORDUTF8LEN]; + char wspace[MAXWORDUTF8LEN]; + w_char unicw[MAXWORDLEN]; + int wl2 = 0; + *slst = NULL; + if (! pSMgr || maxdic == 0) return 0; + int nc = strlen(word); + if (utf8) { + if (nc >= MAXWORDUTF8LEN) return 0; + } else { + if (nc >= MAXWORDLEN) return 0; + } + int captype = 0; + int abbv = 0; + int wl = 0; + + // input conversion + RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL; + if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv); + else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv); + + if (wl == 0) { + if (abbv) { + for (wl = 0; wl < abbv; wl++) cw[wl] = '.'; + cw[wl] = '\0'; + abbv = 0; + } else return 0; + } + + char result[MAXLNLEN]; + char * st = NULL; + + *result = '\0'; + + int n = 0; + int n2 = 0; + int n3 = 0; + + // test numbers + // LANG_hu section: set dash information for suggestions + if (langnum == LANG_hu) { + while ((n < wl) && + (((cw[n] <= '9') && (cw[n] >= '0')) || (((cw[n] == '.') || (cw[n] == ',')) && (n > 0)))) { + n++; + if ((cw[n] == '.') || (cw[n] == ',')) { + if (((n2 == 0) && (n > 3)) || + ((n2 > 0) && ((cw[n-1] == '.') || (cw[n-1] == ',')))) break; + n2++; + n3 = n; + } + } + + if ((n == wl) && (n3 > 0) && (n - n3 > 3)) return 0; + if ((n == wl) || ((n>0) && ((cw[n]=='%') || (cw[n]=='\xB0')) && checkword(cw+n, NULL, NULL))) { + mystrcat(result, cw, MAXLNLEN); + result[n - 1] = '\0'; + if (n == wl) cat_result(result, pSMgr->suggest_morph(cw + n - 1)); + else { + char sign = cw[n]; + cw[n] = '\0'; + cat_result(result, pSMgr->suggest_morph(cw + n - 1)); + mystrcat(result, "+", MAXLNLEN); // XXX SPEC. MORPHCODE + cw[n] = sign; + cat_result(result, pSMgr->suggest_morph(cw + n)); + } + return line_tok(result, slst, MSEP_REC); + } + } + // END OF LANG_hu section + + switch(captype) { + case HUHCAP: + case HUHINITCAP: + case NOCAP: { + cat_result(result, pSMgr->suggest_morph(cw)); + if (abbv) { + memcpy(wspace,cw,wl); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + cat_result(result, pSMgr->suggest_morph(wspace)); + } + break; + } + case INITCAP: { + wl = mkallsmall2(cw, unicw, nc); + memcpy(wspace,cw,(wl+1)); + wl2 = mkinitcap2(cw, unicw, nc); + cat_result(result, pSMgr->suggest_morph(wspace)); + cat_result(result, pSMgr->suggest_morph(cw)); + if (abbv) { + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + cat_result(result, pSMgr->suggest_morph(wspace)); + + memcpy(wspace, cw, wl2); + *(wspace+wl2) = '.'; + *(wspace+wl2+1) = '\0'; + + cat_result(result, pSMgr->suggest_morph(wspace)); + } + break; + } + case ALLCAP: { + cat_result(result, pSMgr->suggest_morph(cw)); + if (abbv) { + memcpy(wspace,cw,wl); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + cat_result(result, pSMgr->suggest_morph(cw)); + } + wl = mkallsmall2(cw, unicw, nc); + memcpy(wspace,cw,(wl+1)); + wl2 = mkinitcap2(cw, unicw, nc); + + cat_result(result, pSMgr->suggest_morph(wspace)); + cat_result(result, pSMgr->suggest_morph(cw)); + if (abbv) { + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + cat_result(result, pSMgr->suggest_morph(wspace)); + + memcpy(wspace, cw, wl2); + *(wspace+wl2) = '.'; + *(wspace+wl2+1) = '\0'; + + cat_result(result, pSMgr->suggest_morph(wspace)); + } + break; + } + } + + if (*result) { + // word reversing wrapper for complex prefixes + if (complexprefixes) { + if (utf8) reverseword_utf(result); else reverseword(result); + } + return line_tok(result, slst, MSEP_REC); + } + + // compound word with dash (HU) I18n + char * dash = NULL; + int nresult = 0; + // LANG_hu section: set dash information for suggestions + if (langnum == LANG_hu) dash = (char *) strchr(cw,'-'); + if ((langnum == LANG_hu) && dash) { + *dash='\0'; + // examine 2 sides of the dash + if (dash[1] == '\0') { // base word ending with dash + if (spell(cw)) { + char * p = pSMgr->suggest_morph(cw); + if (p) { + int ret = line_tok(p, slst, MSEP_REC); + free(p); + return ret; + } + + } + } else if ((dash[1] == 'e') && (dash[2] == '\0')) { // XXX (HU) -e hat. + if (spell(cw) && (spell("-e"))) { + st = pSMgr->suggest_morph(cw); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + mystrcat(result,"+", MAXLNLEN); // XXX spec. separator in MORPHCODE + st = pSMgr->suggest_morph("-e"); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + return line_tok(result, slst, MSEP_REC); + } + } else { + // first word ending with dash: word- XXX ??? + char r2 = *(dash + 1); + dash[0]='-'; + dash[1]='\0'; + nresult = spell(cw); + dash[1] = r2; + dash[0]='\0'; + if (nresult && spell(dash+1) && ((strlen(dash+1) > 1) || + ((dash[1] > '0') && (dash[1] < '9')))) { + st = pSMgr->suggest_morph(cw); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + mystrcat(result,"+", MAXLNLEN); // XXX spec. separator in MORPHCODE + } + st = pSMgr->suggest_morph(dash+1); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + return line_tok(result, slst, MSEP_REC); + } + } + // affixed number in correct word + if (nresult && (dash > cw) && (((*(dash-1)<='9') && + (*(dash-1)>='0')) || (*(dash-1)=='.'))) { + *dash='-'; + n = 1; + if (*(dash - n) == '.') n++; + // search first not a number character to left from dash + while (((dash - n)>=cw) && ((*(dash - n)=='0') || (n < 3)) && (n < 6)) { + n++; + } + if ((dash - n) < cw) n--; + // numbers: valami1000000-hoz + // examine 100000-hoz, 10000-hoz 1000-hoz, 10-hoz, + // 56-hoz, 6-hoz + for(; n >= 1; n--) { + if ((*(dash - n) >= '0') && (*(dash - n) <= '9') && checkword(dash - n, NULL, NULL)) { + mystrcat(result, cw, MAXLNLEN); + result[dash - cw - n] = '\0'; + st = pSMgr->suggest_morph(dash - n); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + return line_tok(result, slst, MSEP_REC); + } + } + } + } + return 0; +} + +int Hunspell::generate(char*** slst, const char * word, char ** pl, int pln) +{ + *slst = NULL; + if (!pSMgr || !pln) return 0; + char **pl2; + int pl2n = analyze(&pl2, word); + int captype = 0; + int abbv = 0; + char cw[MAXWORDUTF8LEN]; + cleanword(cw, word, &captype, &abbv); + char result[MAXLNLEN]; + *result = '\0'; + + for (int i = 0; i < pln; i++) { + cat_result(result, pSMgr->suggest_gen(pl2, pl2n, pl[i])); + } + freelist(&pl2, pl2n); + + if (*result) { + // allcap + if (captype == ALLCAP) mkallcap(result); + + // line split + int linenum = line_tok(result, slst, MSEP_REC); + + // capitalize + if (captype == INITCAP || captype == HUHINITCAP) { + for (int j=0; j < linenum; j++) mkinitcap((*slst)[j]); + } + + // temporary filtering of prefix related errors (eg. + // generate("undrinkable", "eats") --> "undrinkables" and "*undrinks") + + int r = 0; + for (int j=0; j < linenum; j++) { + if (!spell((*slst)[j])) { + free((*slst)[j]); + (*slst)[j] = NULL; + } else { + if (r < j) (*slst)[r] = (*slst)[j]; + r++; + } + } + if (r > 0) return r; + free(*slst); + *slst = NULL; + } + return 0; +} + +int Hunspell::generate(char*** slst, const char * word, const char * pattern) +{ + char **pl; + int pln = analyze(&pl, pattern); + int n = generate(slst, word, pl, pln); + freelist(&pl, pln); + return uniqlist(*slst, n); +} + +// minimal XML parser functions +int Hunspell::get_xml_par(char * dest, const char * par, int max) +{ + char * d = dest; + if (!par) return 0; + char end = *par; + char * dmax = dest + max; + if (end == '>') end = '<'; + else if (end != '\'' && end != '"') return 0; // bad XML + for (par++; d < dmax && *par != '\0' && *par != end; par++, d++) *d = *par; + *d = '\0'; + mystrrep(dest, "<", "<"); + mystrrep(dest, "&", "&"); + return (int)(d - dest); +} + +int Hunspell::get_langnum() const +{ + return langnum; +} + +// return the beginning of the element (attr == NULL) or the attribute +const char * Hunspell::get_xml_pos(const char * s, const char * attr) +{ + const char * end = strchr(s, '>'); + const char * p = s; + if (attr == NULL) return end; + do { + p = strstr(p, attr); + if (!p || p >= end) return 0; + } while (*(p-1) != ' ' && *(p-1) != '\n'); + return p + strlen(attr); +} + +int Hunspell::check_xml_par(const char * q, const char * attr, const char * value) { + char cw[MAXWORDUTF8LEN]; + if (get_xml_par(cw, get_xml_pos(q, attr), MAXWORDUTF8LEN - 1) && + strcmp(cw, value) == 0) return 1; + return 0; +} + +int Hunspell::get_xml_list(char ***slst, char * list, const char * tag) { + int n = 0; + char * p; + if (!list) return 0; + for (p = list; (p = strstr(p, tag)); p++) n++; + if (n == 0) return 0; + *slst = (char **) malloc(sizeof(char *) * n); + if (!*slst) return 0; + for (p = list, n = 0; (p = strstr(p, tag)); p++, n++) { + int l = strlen(p); + (*slst)[n] = (char *) malloc(l + 1); + if (!(*slst)[n]) return n; + if (!get_xml_par((*slst)[n], p + strlen(tag) - 1, l)) { + free((*slst)[n]); + break; + } + } + return n; +} + +int Hunspell::spellml(char*** slst, const char * word) +{ + char *q, *q2; + char cw[MAXWORDUTF8LEN], cw2[MAXWORDUTF8LEN]; + q = (char *) strstr(word, "'); + if (!q2) return 0; // bad XML input + q2 = strstr(q2, "'), MAXWORDUTF8LEN - 10)) n = analyze(slst, cw); + if (n == 0) return 0; + // convert the result to ana1ana2 format + for (int i = 0; i < n; i++) s+= strlen((*slst)[i]); + char * r = (char *) malloc(6 + 5 * s + 7 * n + 7 + 1); // XXX 5*s->&->& + if (!r) return 0; + strcpy(r, ""); + for (int i = 0; i < n; i++) { + int l = strlen(r); + strcpy(r + l, ""); + strcpy(r + l + 3, (*slst)[i]); + mystrrep(r + l + 3, "\t", " "); + mystrrep(r + l + 3, "<", "<"); + mystrrep(r + l + 3, "&", "&"); + strcat(r, ""); + free((*slst)[i]); + } + strcat(r, ""); + (*slst)[0] = r; + return 1; + } else if (check_xml_par(q, "type=", "stem")) { + if (get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN - 1)) return stem(slst, cw); + } else if (check_xml_par(q, "type=", "generate")) { + int n = get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN - 1); + if (n == 0) return 0; + char * q3 = strstr(q2 + 1, "'), MAXWORDUTF8LEN - 1)) { + return generate(slst, cw, cw2); + } + } else { + if ((q2 = strstr(q2 + 1, "'), ""))) { + int n2 = generate(slst, cw, slst2, n); + freelist(&slst2, n); + return uniqlist(*slst, n2); + } + freelist(&slst2, n); + } + } + } + return 0; +} + + +#ifdef HUNSPELL_EXPERIMENTAL +// XXX need UTF-8 support +char * Hunspell::morph_with_correction(const char * word) +{ + char cw[MAXWORDUTF8LEN]; + char wspace[MAXWORDUTF8LEN]; + if (! pSMgr || maxdic == 0) return NULL; + int wl = strlen(word); + if (utf8) { + if (wl >= MAXWORDUTF8LEN) return NULL; + } else { + if (wl >= MAXWORDLEN) return NULL; + } + int captype = 0; + int abbv = 0; + wl = cleanword(cw, word, &captype, &abbv); + if (wl == 0) return NULL; + + char result[MAXLNLEN]; + char * st = NULL; + + *result = '\0'; + + + switch(captype) { + case NOCAP: { + st = pSMgr->suggest_morph_for_spelling_error(cw); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + if (abbv) { + memcpy(wspace,cw,wl); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + } + break; + } + case INITCAP: { + memcpy(wspace,cw,(wl+1)); + mkallsmall(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + st = pSMgr->suggest_morph_for_spelling_error(cw); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + if (abbv) { + memcpy(wspace,cw,wl); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + mkallsmall(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + mkinitcap(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + } + break; + } + case HUHCAP: { + st = pSMgr->suggest_morph_for_spelling_error(cw); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + memcpy(wspace,cw,(wl+1)); + mkallsmall(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + break; + } + case ALLCAP: { + memcpy(wspace,cw,(wl+1)); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + mkallsmall(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + mkinitcap(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + if (abbv) { + memcpy(wspace,cw,(wl+1)); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + if (*result) mystrcat(result, "\n", MAXLNLEN); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + mkallsmall(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + mkinitcap(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + } + break; + } + } + + if (*result) return mystrdup(result); + return NULL; +} + +#endif // END OF HUNSPELL_EXPERIMENTAL CODE + +Hunhandle *Hunspell_create(const char * affpath, const char * dpath) +{ + return (Hunhandle*)(new Hunspell(affpath, dpath)); +} + +Hunhandle *Hunspell_create_key(const char * affpath, const char * dpath, + const char * key) +{ + return (Hunhandle*)(new Hunspell(affpath, dpath, key)); +} + +void Hunspell_destroy(Hunhandle *pHunspell) +{ + delete (Hunspell*)(pHunspell); +} + +int Hunspell_spell(Hunhandle *pHunspell, const char *word) +{ + return ((Hunspell*)pHunspell)->spell(word); +} + +char *Hunspell_get_dic_encoding(Hunhandle *pHunspell) +{ + return ((Hunspell*)pHunspell)->get_dic_encoding(); +} + +int Hunspell_suggest(Hunhandle *pHunspell, char*** slst, const char * word) +{ + return ((Hunspell*)pHunspell)->suggest(slst, word); +} + +int Hunspell_analyze(Hunhandle *pHunspell, char*** slst, const char * word) +{ + return ((Hunspell*)pHunspell)->analyze(slst, word); +} + +int Hunspell_stem(Hunhandle *pHunspell, char*** slst, const char * word) +{ + return ((Hunspell*)pHunspell)->stem(slst, word); +} + +int Hunspell_stem2(Hunhandle *pHunspell, char*** slst, char** desc, int n) +{ + return ((Hunspell*)pHunspell)->stem(slst, desc, n); +} + +int Hunspell_generate(Hunhandle *pHunspell, char*** slst, const char * word, + const char * word2) +{ + return ((Hunspell*)pHunspell)->generate(slst, word, word2); +} + +int Hunspell_generate2(Hunhandle *pHunspell, char*** slst, const char * word, + char** desc, int n) +{ + return ((Hunspell*)pHunspell)->generate(slst, word, desc, n); +} + + /* functions for run-time modification of the dictionary */ + + /* add word to the run-time dictionary */ + +int Hunspell_add(Hunhandle *pHunspell, const char * word) { + return ((Hunspell*)pHunspell)->add(word); +} + + /* add word to the run-time dictionary with affix flags of + * the example (a dictionary word): Hunspell will recognize + * affixed forms of the new word, too. + */ + +int Hunspell_add_with_affix(Hunhandle *pHunspell, const char * word, + const char * example) { + return ((Hunspell*)pHunspell)->add_with_affix(word, example); +} + + /* remove word from the run-time dictionary */ + +int Hunspell_remove(Hunhandle *pHunspell, const char * word) { + return ((Hunspell*)pHunspell)->remove(word); +} + +void Hunspell_free_list(Hunhandle *, char *** slst, int n) { + freelist(slst, n); +} diff --git a/src/myspell/hunspell.dsp b/src/myspell/hunspell.dsp new file mode 100644 index 0000000..05e072f --- /dev/null +++ b/src/myspell/hunspell.dsp @@ -0,0 +1,164 @@ +# Microsoft Developer Studio Project File - Name="hunspell" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=hunspell - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "hunspell.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "hunspell.mak" CFG="hunspell - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "hunspell - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "hunspell - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "hunspell - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "W32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "W32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD BASE RSC /l 0x40e /d "NDEBUG" +# ADD RSC /l 0x40e /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "hunspell - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "W32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "W32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD BASE RSC /l 0x40e /d "_DEBUG" +# ADD RSC /l 0x40e /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "hunspell - Win32 Release" +# Name "hunspell - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\affentry.cxx +# End Source File +# Begin Source File + +SOURCE=.\affixmgr.cxx +# End Source File +# Begin Source File + +SOURCE=.\csutil.cxx +# End Source File +# Begin Source File + +SOURCE=.\dictmgr.cxx +# End Source File +# Begin Source File + +SOURCE=.\hashmgr.cxx +# End Source File +# Begin Source File + +SOURCE=.\hunspell.cxx +# End Source File +# Begin Source File + +SOURCE=.\suggestmgr.cxx +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\affentry.hxx +# End Source File +# Begin Source File + +SOURCE=.\affixmgr.hxx +# End Source File +# Begin Source File + +SOURCE=.\atypes.hxx +# End Source File +# Begin Source File + +SOURCE=.\baseaffix.hxx +# End Source File +# Begin Source File + +SOURCE=.\csutil.hxx +# End Source File +# Begin Source File + +SOURCE=.\dictmgr.hxx +# End Source File +# Begin Source File + +SOURCE=.\hashmgr.hxx +# End Source File +# Begin Source File + +SOURCE=.\htypes.hxx +# End Source File +# Begin Source File + +SOURCE=.\langnum.hxx +# End Source File +# Begin Source File + +SOURCE=.\hunspell.hxx +# End Source File +# Begin Source File + +SOURCE=.\suggestmgr.hxx +# End Source File +# End Group +# End Target +# End Project diff --git a/src/myspell/hunspell.h b/src/myspell/hunspell.h new file mode 100644 index 0000000..627968a --- /dev/null +++ b/src/myspell/hunspell.h @@ -0,0 +1,95 @@ +#ifndef _MYSPELLMGR_H_ +#define _MYSPELLMGR_H_ + +#include "hunvisapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Hunhandle Hunhandle; + +LIBHUNSPELL_DLL_EXPORTED Hunhandle *Hunspell_create(const char * affpath, const char * dpath); + +LIBHUNSPELL_DLL_EXPORTED Hunhandle *Hunspell_create_key(const char * affpath, const char * dpath, + const char * key); + +LIBHUNSPELL_DLL_EXPORTED void Hunspell_destroy(Hunhandle *pHunspell); + +/* spell(word) - spellcheck word + * output: 0 = bad word, not 0 = good word + */ +LIBHUNSPELL_DLL_EXPORTED int Hunspell_spell(Hunhandle *pHunspell, const char *); + +LIBHUNSPELL_DLL_EXPORTED char *Hunspell_get_dic_encoding(Hunhandle *pHunspell); + +/* suggest(suggestions, word) - search suggestions + * input: pointer to an array of strings pointer and the (bad) word + * array of strings pointer (here *slst) may not be initialized + * output: number of suggestions in string array, and suggestions in + * a newly allocated array of strings (*slts will be NULL when number + * of suggestion equals 0.) + */ +LIBHUNSPELL_DLL_EXPORTED int Hunspell_suggest(Hunhandle *pHunspell, char*** slst, const char * word); + + /* morphological functions */ + + /* analyze(result, word) - morphological analysis of the word */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_analyze(Hunhandle *pHunspell, char*** slst, const char * word); + + /* stem(result, word) - stemmer function */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_stem(Hunhandle *pHunspell, char*** slst, const char * word); + + /* stem(result, analysis, n) - get stems from a morph. analysis + * example: + * char ** result, result2; + * int n1 = Hunspell_analyze(result, "words"); + * int n2 = Hunspell_stem2(result2, result, n1); + */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_stem2(Hunhandle *pHunspell, char*** slst, char** desc, int n); + + /* generate(result, word, word2) - morphological generation by example(s) */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_generate(Hunhandle *pHunspell, char*** slst, const char * word, + const char * word2); + + /* generate(result, word, desc, n) - generation by morph. description(s) + * example: + * char ** result; + * char * affix = "is:plural"; // description depends from dictionaries, too + * int n = Hunspell_generate2(result, "word", &affix, 1); + * for (int i = 0; i < n; i++) printf("%s\n", result[i]); + */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_generate2(Hunhandle *pHunspell, char*** slst, const char * word, + char** desc, int n); + + /* functions for run-time modification of the dictionary */ + + /* add word to the run-time dictionary */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_add(Hunhandle *pHunspell, const char * word); + + /* add word to the run-time dictionary with affix flags of + * the example (a dictionary word): Hunspell will recognize + * affixed forms of the new word, too. + */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_add_with_affix(Hunhandle *pHunspell, const char * word, const char * example); + + /* remove word from the run-time dictionary */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_remove(Hunhandle *pHunspell, const char * word); + + /* free suggestion lists */ + +LIBHUNSPELL_DLL_EXPORTED void Hunspell_free_list(Hunhandle *pHunspell, char *** slst, int n); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/myspell/hunspell.hxx b/src/myspell/hunspell.hxx new file mode 100644 index 0000000..9b6c388 --- /dev/null +++ b/src/myspell/hunspell.hxx @@ -0,0 +1,172 @@ +#include "hunvisapi.h" + +#include "hashmgr.hxx" +#include "affixmgr.hxx" +#include "suggestmgr.hxx" +#include "langnum.hxx" + +#define SPELL_XML "" + +#define MAXDIC 20 +#define MAXSUGGESTION 15 +#define MAXSHARPS 5 + +#define HUNSPELL_OK (1 << 0) +#define HUNSPELL_OK_WARN (1 << 1) + +#ifndef _MYSPELLMGR_HXX_ +#define _MYSPELLMGR_HXX_ + +class LIBHUNSPELL_DLL_EXPORTED Hunspell +{ + AffixMgr* pAMgr; + HashMgr* pHMgr[MAXDIC]; + int maxdic; + SuggestMgr* pSMgr; + char * affixpath; + char * encoding; + struct cs_info * csconv; + int langnum; + int utf8; + int complexprefixes; + char** wordbreak; + +public: + + /* Hunspell(aff, dic) - constructor of Hunspell class + * input: path of affix file and dictionary file + */ + + Hunspell(const char * affpath, const char * dpath, const char * key = NULL); + ~Hunspell(); + + /* load extra dictionaries (only dic files) */ + int add_dic(const char * dpath, const char * key = NULL); + + /* spell(word) - spellcheck word + * output: 0 = bad word, not 0 = good word + * + * plus output: + * info: information bit array, fields: + * SPELL_COMPOUND = a compound word + * SPELL_FORBIDDEN = an explicit forbidden word + * root: root (stem), when input is a word with affix(es) + */ + + int spell(const char * word, int * info = NULL, char ** root = NULL); + + /* suggest(suggestions, word) - search suggestions + * input: pointer to an array of strings pointer and the (bad) word + * array of strings pointer (here *slst) may not be initialized + * output: number of suggestions in string array, and suggestions in + * a newly allocated array of strings (*slts will be NULL when number + * of suggestion equals 0.) + */ + + int suggest(char*** slst, const char * word); + + /* deallocate suggestion lists */ + + void free_list(char *** slst, int n); + + char * get_dic_encoding(); + + /* morphological functions */ + + /* analyze(result, word) - morphological analysis of the word */ + + int analyze(char*** slst, const char * word); + + /* stem(result, word) - stemmer function */ + + int stem(char*** slst, const char * word); + + /* stem(result, analysis, n) - get stems from a morph. analysis + * example: + * char ** result, result2; + * int n1 = analyze(&result, "words"); + * int n2 = stem(&result2, result, n1); + */ + + int stem(char*** slst, char ** morph, int n); + + /* generate(result, word, word2) - morphological generation by example(s) */ + + int generate(char*** slst, const char * word, const char * word2); + + /* generate(result, word, desc, n) - generation by morph. description(s) + * example: + * char ** result; + * char * affix = "is:plural"; // description depends from dictionaries, too + * int n = generate(&result, "word", &affix, 1); + * for (int i = 0; i < n; i++) printf("%s\n", result[i]); + */ + + int generate(char*** slst, const char * word, char ** desc, int n); + + /* functions for run-time modification of the dictionary */ + + /* add word to the run-time dictionary */ + + int add(const char * word); + + /* add word to the run-time dictionary with affix flags of + * the example (a dictionary word): Hunspell will recognize + * affixed forms of the new word, too. + */ + + int add_with_affix(const char * word, const char * example); + + /* remove word from the run-time dictionary */ + + int remove(const char * word); + + /* other */ + + /* get extra word characters definied in affix file for tokenization */ + const char * get_wordchars(); + unsigned short * get_wordchars_utf16(int * len); + + struct cs_info * get_csconv(); + const char * get_version(); + + int get_langnum() const; + + /* experimental and deprecated functions */ + +#ifdef HUNSPELL_EXPERIMENTAL + /* suffix is an affix flag string, similarly in dictionary files */ + int put_word_suffix(const char * word, const char * suffix); + char * morph_with_correction(const char * word); + + /* spec. suggestions */ + int suggest_auto(char*** slst, const char * word); + int suggest_pos_stems(char*** slst, const char * word); +#endif + +private: + int cleanword(char *, const char *, int * pcaptype, int * pabbrev); + int cleanword2(char *, const char *, w_char *, int * w_len, int * pcaptype, int * pabbrev); + void mkinitcap(char *); + int mkinitcap2(char * p, w_char * u, int nc); + int mkinitsmall2(char * p, w_char * u, int nc); + void mkallcap(char *); + int mkallcap2(char * p, w_char * u, int nc); + void mkallsmall(char *); + int mkallsmall2(char * p, w_char * u, int nc); + struct hentry * checkword(const char *, int * info, char **root); + char * sharps_u8_l1(char * dest, char * source); + hentry * spellsharps(char * base, char *, int, int, char * tmp, int * info, char **root); + int is_keepcase(const hentry * rv); + int insert_sug(char ***slst, char * word, int ns); + void cat_result(char * result, char * st); + char * stem_description(const char * desc); + int spellml(char*** slst, const char * word); + int get_xml_par(char * dest, const char * par, int maxl); + const char * get_xml_pos(const char * s, const char * attr); + int get_xml_list(char ***slst, char * list, const char * tag); + int check_xml_par(const char * q, const char * attr, const char * value); + +}; + +#endif diff --git a/src/myspell/hunvisapi.h b/src/myspell/hunvisapi.h new file mode 100644 index 0000000..4712280 --- /dev/null +++ b/src/myspell/hunvisapi.h @@ -0,0 +1,18 @@ +#ifndef _HUNSPELL_VISIBILITY_H_ +#define _HUNSPELL_VISIBILITY_H_ + +#if defined(HUNSPELL_STATIC) +# define LIBHUNSPELL_DLL_EXPORTED +#elif defined(_MSC_VER) +# if defined(BUILDING_LIBHUNSPELL) +# define LIBHUNSPELL_DLL_EXPORTED __declspec(dllexport) +# else +# define LIBHUNSPELL_DLL_EXPORTED __declspec(dllimport) +# endif +#elif BUILDING_LIBHUNSPELL && 1 +# define LIBHUNSPELL_DLL_EXPORTED __attribute__((__visibility__("default"))) +#else +# define LIBHUNSPELL_DLL_EXPORTED +#endif + +#endif diff --git a/src/myspell/hunzip.cxx b/src/myspell/hunzip.cxx new file mode 100644 index 0000000..574df5c --- /dev/null +++ b/src/myspell/hunzip.cxx @@ -0,0 +1,195 @@ +#include +#include +#include + +#include "hunzip.hxx" + +#include "enchant-provider.h" + +#define CODELEN 65536 +#define BASEBITREC 5000 + +#define UNCOMPRESSED '\002' +#define MAGIC "hz0" +#define MAGIC_ENCRYPT "hz1" +#define MAGICLEN (sizeof(MAGIC) - 1) + +int Hunzip::fail(const char * err, const char * par) { + fprintf(stderr, err, par); + return -1; +} + +Hunzip::Hunzip(const char * file, const char * key) { + bufsiz = 0; + lastbit = 0; + inc = 0; + outc = 0; + dec = NULL; + fin = NULL; + filename = (char *) malloc(strlen(file) + 1); + if (filename) strcpy(filename, file); + if (getcode(key) == -1) bufsiz = -1; + else bufsiz = getbuf(); +} + +int Hunzip::getcode(const char * key) { + unsigned char c[2]; + int i, j, n, p; + int allocatedbit = BASEBITREC; + const char * enc = key; + + if (!filename) return -1; + + fin = enchant_fopen(filename, "rb"); + if (!fin) return -1; + + // read magic number + if ((fread(in, 1, 3, fin) < MAGICLEN) + || !(strncmp(MAGIC, in, MAGICLEN) == 0 || + strncmp(MAGIC_ENCRYPT, in, MAGICLEN) == 0)) { + return fail(MSG_FORMAT, filename); + } + + // check encryption + if (strncmp(MAGIC_ENCRYPT, in, MAGICLEN) == 0) { + unsigned char cs; + if (!key) return fail(MSG_KEY, filename); + if (fread(&c, 1, 1, fin) < 1) return fail(MSG_FORMAT, filename); + for (cs = 0; *enc; enc++) cs ^= *enc; + if (cs != c[0]) return fail(MSG_KEY, filename); + enc = key; + } else key = NULL; + + // read record count + if (fread(&c, 1, 2, fin) < 2) return fail(MSG_FORMAT, filename); + + if (key) { + c[0] ^= *enc; + if (*(++enc) == '\0') enc = key; + c[1] ^= *enc; + } + + n = ((int) c[0] << 8) + c[1]; + dec = (struct bit *) malloc(BASEBITREC * sizeof(struct bit)); + if (!dec) return fail(MSG_MEMORY, filename); + dec[0].v[0] = 0; + dec[0].v[1] = 0; + + // read codes + for (i = 0; i < n; i++) { + unsigned char l; + if (fread(c, 1, 2, fin) < 2) return fail(MSG_FORMAT, filename); + if (key) { + if (*(++enc) == '\0') enc = key; + c[0] ^= *enc; + if (*(++enc) == '\0') enc = key; + c[1] ^= *enc; + } + if (fread(&l, 1, 1, fin) < 1) return fail(MSG_FORMAT, filename); + if (key) { + if (*(++enc) == '\0') enc = key; + l ^= *enc; + } + if (fread(in, 1, l/8+1, fin) < (size_t) l/8+1) return fail(MSG_FORMAT, filename); + if (key) for (j = 0; j <= l/8; j++) { + if (*(++enc) == '\0') enc = key; + in[j] ^= *enc; + } + p = 0; + for (j = 0; j < l; j++) { + int b = (in[j/8] & (1 << (7 - (j % 8)))) ? 1 : 0; + int oldp = p; + p = dec[p].v[b]; + if (p == 0) { + lastbit++; + if (lastbit == allocatedbit) { + allocatedbit += BASEBITREC; + dec = (struct bit *) realloc(dec, allocatedbit * sizeof(struct bit)); + } + dec[lastbit].v[0] = 0; + dec[lastbit].v[1] = 0; + dec[oldp].v[b] = lastbit; + p = lastbit; + } + } + dec[p].c[0] = c[0]; + dec[p].c[1] = c[1]; + } + return 0; +} + +Hunzip::~Hunzip() +{ + if (dec) free(dec); + if (fin) fclose(fin); + if (filename) free(filename); +} + +int Hunzip::getbuf() { + int p = 0; + int o = 0; + do { + if (inc == 0) inbits = fread(in, 1, BUFSIZE, fin) * 8; + for (; inc < inbits; inc++) { + int b = (in[inc / 8] & (1 << (7 - (inc % 8)))) ? 1 : 0; + int oldp = p; + p = dec[p].v[b]; + if (p == 0) { + if (oldp == lastbit) { + fclose(fin); + fin = NULL; + // add last odd byte + if (dec[lastbit].c[0]) out[o++] = dec[lastbit].c[1]; + return o; + } + out[o++] = dec[oldp].c[0]; + out[o++] = dec[oldp].c[1]; + if (o == BUFSIZE) return o; + p = dec[p].v[b]; + } + } + inc = 0; + } while (inbits == BUFSIZE * 8); + return fail(MSG_FORMAT, filename); +} + +const char * Hunzip::getline() { + char linebuf[BUFSIZE]; + int l = 0, eol = 0, left = 0, right = 0; + if (bufsiz == -1) return NULL; + while (l < bufsiz && !eol) { + linebuf[l++] = out[outc]; + switch (out[outc]) { + case '\t': break; + case 31: { // escape + if (++outc == bufsiz) { + bufsiz = getbuf(); + outc = 0; + } + linebuf[l - 1] = out[outc]; + break; + } + case ' ': break; + default: if (((unsigned char) out[outc]) < 47) { + if (out[outc] > 32) { + right = out[outc] - 31; + if (++outc == bufsiz) { + bufsiz = getbuf(); + outc = 0; + } + } + if (out[outc] == 30) left = 9; else left = out[outc]; + linebuf[l-1] = '\n'; + eol = 1; + } + } + if (++outc == bufsiz) { + outc = 0; + bufsiz = fin ? getbuf(): -1; + } + } + if (right) strcpy(linebuf + l - 1, line + strlen(line) - right - 1); + else linebuf[l] = '\0'; + strcpy(line + left, linebuf); + return line; +} diff --git a/src/myspell/hunzip.hxx b/src/myspell/hunzip.hxx new file mode 100644 index 0000000..b58e3ab --- /dev/null +++ b/src/myspell/hunzip.hxx @@ -0,0 +1,45 @@ +/* hunzip: file decompression for sorted dictionaries with optional encryption, + * algorithm: prefix-suffix encoding and 16-bit Huffman encoding */ + +#ifndef _HUNZIP_HXX_ +#define _HUNZIP_HXX_ + +#include "hunvisapi.h" + +#include + +#define BUFSIZE 65536 +#define HZIP_EXTENSION ".hz" + +#define MSG_OPEN "error: %s: cannot open\n" +#define MSG_FORMAT "error: %s: not in hzip format\n" +#define MSG_MEMORY "error: %s: missing memory\n" +#define MSG_KEY "error: %s: missing or bad password\n" + +struct bit { + unsigned char c[2]; + int v[2]; +}; + +class LIBHUNSPELL_DLL_EXPORTED Hunzip +{ + +protected: + char * filename; + FILE * fin; + int bufsiz, lastbit, inc, inbits, outc; + struct bit * dec; // code table + char in[BUFSIZE]; // input buffer + char out[BUFSIZE + 1]; // Huffman-decoded buffer + char line[BUFSIZE + 50]; // decoded line + int getcode(const char * key); + int getbuf(); + int fail(const char * err, const char * par); + +public: + Hunzip(const char * filename, const char * key = NULL); + ~Hunzip(); + const char * getline(); +}; + +#endif diff --git a/src/myspell/langnum.hxx b/src/myspell/langnum.hxx new file mode 100644 index 0000000..1d140a7 --- /dev/null +++ b/src/myspell/langnum.hxx @@ -0,0 +1,38 @@ +#ifndef _LANGNUM_HXX_ +#define _LANGNUM_HXX_ + +/* + language numbers for language specific codes + see http://l10n.openoffice.org/languages.html +*/ + +enum { +LANG_ar=96, +LANG_az=100, // custom number +LANG_bg=41, +LANG_ca=37, +LANG_cs=42, +LANG_da=45, +LANG_de=49, +LANG_el=30, +LANG_en=01, +LANG_es=34, +LANG_eu=10, +LANG_fr=02, +LANG_gl=38, +LANG_hr=78, +LANG_hu=36, +LANG_it=39, +LANG_la=99, // custom number +LANG_lv=101, // custom number +LANG_nl=31, +LANG_pl=48, +LANG_pt=03, +LANG_ru=07, +LANG_sv=50, +LANG_tr=90, +LANG_uk=80, +LANG_xx=999 +}; + +#endif diff --git a/src/myspell/libenchant_myspell.rc b/src/myspell/libenchant_myspell.rc new file mode 100644 index 0000000..75384da --- /dev/null +++ b/src/myspell/libenchant_myspell.rc @@ -0,0 +1,37 @@ +//LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +LANGUAGE 0, 0 + +10 VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "Libenchant Myspell module\0" + VALUE "FileVersion", "1, 7, 0, 1\0" + VALUE "InternalName", "libenchant_myspell\0" + VALUE "LegalCopyright", "Copyright \xA9 2003-2011 Joan Moratinos, Dom Lachowicz et al.\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "libenchant_myspell.dll\0" + VALUE "PrivateBuild", "Dev version\0" + VALUE "ProductName", "libenchant\0" + VALUE "ProductVersion", "1, 7, 0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/myspell/license.hunspell b/src/myspell/license.hunspell new file mode 100644 index 0000000..81ffad8 --- /dev/null +++ b/src/myspell/license.hunspell @@ -0,0 +1,61 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Hunspell, based on MySpell. + * + * The Initial Developers of the Original Code are + * Kevin Hendricks (MySpell) and Laszlo Nemeth (Hunspell). + * Portions created by the Initial Developers are Copyright (C) 2002-2005 + * the Initial Developers. All Rights Reserved. + * + * Contributor(s): + * David Einstein + * Davide Prina + * Giuseppe Modugno + * Gianluca Turconi + * Simon Brouwer + * Noll Janos + * Biro Arpad + * Goldman Eleonora + * Sarlos Tamas + * Bencsath Boldizsar + * Halacsy Peter + * Dvornik Laszlo + * Gefferth Andras + * Nagy Viktor + * Varga Daniel + * Chris Halls + * Rene Engelhard + * Bram Moolenaar + * Dafydd Jones + * Harri Pitkanen + * Andras Timar + * Tor Lillqvist + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif diff --git a/src/myspell/license.myspell b/src/myspell/license.myspell new file mode 100644 index 0000000..2da5330 --- /dev/null +++ b/src/myspell/license.myspell @@ -0,0 +1,61 @@ +/* + * Copyright 2002 Kevin B. Hendricks, Stratford, Ontario, Canada + * And Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. All modifications to the source code must be clearly marked as + * such. Binary redistributions based on modified source code + * must be clearly marked as modified versions in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY KEVIN B. HENDRICKS 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 + * KEVIN B. HENDRICKS 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. + * + * + * NOTE: A special thanks and credit goes to Geoff Kuenning + * the creator of ispell. MySpell's affix algorithms were + * based on those of ispell which should be noted is + * copyright Geoff Kuenning et.al. and now available + * under a BSD style license. For more information on ispell + * and affix compression in general, please see: + * http://www.cs.ucla.edu/ficus-members/geoff/ispell.html + * (the home page for ispell) + * + * An almost complete rewrite of MySpell for use by + * the Mozilla project has been developed by David Einstein + * (Deinst@world.std.com). David and I are now + * working on parallel development tracks to help + * our respective projects (Mozilla and OpenOffice.org + * and we will maintain full affix file and dictionary + * file compatibility and work on merging our versions + * of MySpell back into a single tree. David has been + * a significant help in improving MySpell. + * + * Special thanks also go to La'szlo' Ne'meth + * who is the author of the + * Hungarian dictionary and who developed and contributed + * the code to support compound words in MySpell + * and fixed numerous problems with the encoding + * case conversion tables. + * + */ diff --git a/src/myspell/license.readme b/src/myspell/license.readme new file mode 100644 index 0000000..2da5330 --- /dev/null +++ b/src/myspell/license.readme @@ -0,0 +1,61 @@ +/* + * Copyright 2002 Kevin B. Hendricks, Stratford, Ontario, Canada + * And Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. All modifications to the source code must be clearly marked as + * such. Binary redistributions based on modified source code + * must be clearly marked as modified versions in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY KEVIN B. HENDRICKS 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 + * KEVIN B. HENDRICKS 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. + * + * + * NOTE: A special thanks and credit goes to Geoff Kuenning + * the creator of ispell. MySpell's affix algorithms were + * based on those of ispell which should be noted is + * copyright Geoff Kuenning et.al. and now available + * under a BSD style license. For more information on ispell + * and affix compression in general, please see: + * http://www.cs.ucla.edu/ficus-members/geoff/ispell.html + * (the home page for ispell) + * + * An almost complete rewrite of MySpell for use by + * the Mozilla project has been developed by David Einstein + * (Deinst@world.std.com). David and I are now + * working on parallel development tracks to help + * our respective projects (Mozilla and OpenOffice.org + * and we will maintain full affix file and dictionary + * file compatibility and work on merging our versions + * of MySpell back into a single tree. David has been + * a significant help in improving MySpell. + * + * Special thanks also go to La'szlo' Ne'meth + * who is the author of the + * Hungarian dictionary and who developed and contributed + * the code to support compound words in MySpell + * and fixed numerous problems with the encoding + * case conversion tables. + * + */ diff --git a/src/myspell/makefile.mk b/src/myspell/makefile.mk new file mode 100644 index 0000000..cf7d389 --- /dev/null +++ b/src/myspell/makefile.mk @@ -0,0 +1,113 @@ +#************************************************************************* +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.7 $ +# +# last change: $Author: vg $ $Date: 2003/06/12 10:38:24 $ +# +# The Contents of this file are made available subject to the terms of +# either of the following licenses +# +# - GNU Lesser General Public License Version 2.1 +# - Sun Industry Standards Source License Version 1.1 +# +# Sun Microsystems Inc., October, 2000 +# +# GNU Lesser General Public License Version 2.1 +# ============================================= +# Copyright 2000 by Sun Microsystems, Inc. +# 901 San Antonio Road, Palo Alto, CA 94303, USA +# +# 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., 59 Temple Place, Suite 330, Boston, +# MA 02110-1301 USA +# +# +# Sun Industry Standards Source License Version 1.1 +# ================================================= +# The contents of this file are subject to the Sun Industry Standards +# Source License Version 1.1 (the "License"); You may not use this file +# except in compliance with the License. You may obtain a copy of the +# License at http://www.openoffice.org/license.html. +# +# Software provided under this License is provided on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, +# WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, +# MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. +# See the License for the specific provisions governing your rights and +# obligations concerning the Software. +# +# The Initial Developer of the Original Code is: Sun Microsystems, Inc. +# +# Copyright: 2000 by Sun Microsystems, Inc. +# +# All Rights Reserved. +# +# Contributor(s): _______________________________________ +# +# +# +#************************************************************************* + +PRJ = .. + +PRJNAME = hunspell +TARGET = hunspell +LIBTARGET=NO + +#----- Settings --------------------------------------------------------- + +.INCLUDE : settings.mk + +# --- Files -------------------------------------------------------- + +# all_target: ALLTAR DICTIONARY +all_target: ALLTAR + +##CXXFLAGS += -I..$/..$/lingutil +##CFLAGSCXX += -I..$/..$/lingutil +##CFLAGSCC += -I..$/..$/lingutil + +CDEFS+=-DOPENOFFICEORG + +SLOFILES= \ + $(SLO)$/affentry.obj \ + $(SLO)$/affixmgr.obj \ + $(SLO)$/dictmgr.obj \ + $(SLO)$/csutil.obj \ + $(SLO)$/utf_info.obj \ + $(SLO)$/hashmgr.obj \ + $(SLO)$/suggestmgr.obj \ + $(SLO)$/hunspell.obj + +LIB1TARGET= $(SLB)$/lib$(TARGET).lib +LIB1ARCHIV= $(LB)/lib$(TARGET).a +LIB1OBJFILES= $(SLOFILES) + +# DIC2BIN= \ +# en_US.aff \ +# en_US.dic +# +# de_DE.aff \ +# de_DE.dic + + +# DICTIONARY : +# +$(COPY) $(foreach,i,$(DIC2BIN) $i) $(BIN) + + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + diff --git a/src/myspell/myspell_checker.cpp b/src/myspell/myspell_checker.cpp new file mode 100644 index 0000000..f94d144 --- /dev/null +++ b/src/myspell/myspell_checker.cpp @@ -0,0 +1,650 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003-2004 Joan Moratinos , Dom Lachowicz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#include +#include +#include + +#include +#include + +#include "enchant.h" +#include "enchant-provider.h" + +#ifdef near +#undef near +#endif + +/* built against hunspell 1.2.2 on 2008-04-12 */ + +#ifdef HUNSPELL_STATIC +#include "hunspell.hxx" +#else +#include +#endif + +ENCHANT_PLUGIN_DECLARE("Myspell") + +#define G_ICONV_INVALID (GIConv)-1 + +#include + +/***************************************************************************/ + +class MySpellChecker +{ +public: + MySpellChecker(EnchantBroker * broker); + ~MySpellChecker(); + + bool checkWord (const char *word, size_t len); + char **suggestWord (const char* const word, size_t len, size_t *out_n_suggs); + + bool requestDictionary (const char * szLang); + +private: + GIConv m_translate_in; /* Selected translation from/to Unicode */ + GIConv m_translate_out; + Hunspell *myspell; + EnchantBroker *m_broker; +}; + +/***************************************************************************/ + +#if defined(_WIN32) +static WCHAR* GetRegistryValue(HKEY baseKey, const WCHAR * uKeyName, const WCHAR * uKey) +{ + HKEY hKey; + unsigned long lType; + DWORD dwSize; + WCHAR* wszValue = NULL; + + if(RegOpenKeyExW(baseKey, uKeyName, 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + /* Determine size of string */ + if(RegQueryValueExW( hKey, uKey, NULL, &lType, NULL, &dwSize) == ERROR_SUCCESS) + { + wszValue = g_new0(WCHAR, dwSize + 1); + RegQueryValueExW(hKey, uKey, NULL, &lType, (LPBYTE) wszValue, &dwSize); + } + } + + return wszValue; +} + +static char * +myspell_checker_get_open_office_dicts_dir(void) +{ + WCHAR* wszDirectory; + char* open_office_dir, * open_office_dicts_dir; + + /*start by trying current user*/ + wszDirectory = GetRegistryValue (HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\soffice.exe", L"Path"); + if(wszDirectory == NULL) + { + /*next try local machine*/ + wszDirectory = GetRegistryValue (HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\soffice.exe", L"Path"); + } + + if(wszDirectory == NULL) + { + return NULL; + } + + else { + open_office_dir = g_utf16_to_utf8 ((gunichar2*)wszDirectory, -1, NULL, NULL, NULL); + open_office_dicts_dir = g_build_filename(open_office_dir, "share", "dict", "ooo", NULL); + g_free(wszDirectory); + g_free(open_office_dir); + return open_office_dicts_dir; + } +} +#endif + +static bool +g_iconv_is_valid(GIConv i) +{ + return (i != G_ICONV_INVALID); +} + +MySpellChecker::MySpellChecker(EnchantBroker * broker) +: m_translate_in(G_ICONV_INVALID), m_translate_out(G_ICONV_INVALID), myspell(0), m_broker(broker) +{ +} + +MySpellChecker::~MySpellChecker() +{ + delete myspell; + if (g_iconv_is_valid (m_translate_in )) + g_iconv_close(m_translate_in); + if (g_iconv_is_valid(m_translate_out)) + g_iconv_close(m_translate_out); +} + +bool +MySpellChecker::checkWord(const char *utf8Word, size_t len) +{ + if (len > MAXWORDLEN || !g_iconv_is_valid(m_translate_in)) + return false; + + // the 8bit encodings use precomposed forms + char *normalizedWord = g_utf8_normalize (utf8Word, len, G_NORMALIZE_NFC); + char *in = normalizedWord; + char word8[MAXWORDLEN + 1]; + char *out = word8; + size_t len_in = strlen(in); + size_t len_out = sizeof( word8 ) - 1; + size_t result = g_iconv(m_translate_in, &in, &len_in, &out, &len_out); + g_free(normalizedWord); + if ((size_t)-1 == result) + return false; + *out = '\0'; + if (myspell->spell(word8)) + return true; + else + return false; +} + +char** +MySpellChecker::suggestWord(const char* const utf8Word, size_t len, size_t *nsug) +{ + if (len > MAXWORDLEN + || !g_iconv_is_valid(m_translate_in) + || !g_iconv_is_valid(m_translate_out)) + return 0; + + // the 8bit encodings use precomposed forms + char *normalizedWord = g_utf8_normalize (utf8Word, len, G_NORMALIZE_NFC); + char *in = normalizedWord; + char word8[MAXWORDLEN + 1]; + char *out = word8; + size_t len_in = strlen(in); + size_t len_out = sizeof(word8) - 1; + size_t result = g_iconv(m_translate_in, &in, &len_in, &out, &len_out); + g_free(normalizedWord); + if ((size_t)-1 == result) + return NULL; + + *out = '\0'; + char **sugMS; + *nsug = myspell->suggest(&sugMS, word8); + if (*nsug > 0) { + char **sug = g_new0 (char *, *nsug + 1); + for (size_t i=0; i<*nsug; i++) { + in = sugMS[i]; + len_in = strlen(in); + len_out = MAXWORDLEN; + char *word = g_new0(char, len_out + 1); + out = reinterpret_cast(word); + if ((size_t)-1 == g_iconv(m_translate_out, &in, &len_in, &out, &len_out)) { + for (size_t j = i; j < *nsug; j++) + free(sugMS[j]); + free(sugMS); + + *nsug = i; + return sug; + } + *(out) = 0; + sug[i] = word; + free(sugMS[i]); + } + free(sugMS); + return sug; + } + else + return 0; +} + +static GSList * +myspell_checker_get_dictionary_dirs (EnchantBroker * broker) +{ + GSList *dirs = NULL; + + { + GSList *config_dirs, *iter; + + config_dirs = enchant_get_user_config_dirs (); + + for (iter = config_dirs; iter; iter = iter->next) + { + dirs = g_slist_append (dirs, g_build_filename ((const gchar *)iter->data, + "myspell", NULL)); + } + + g_slist_foreach (config_dirs, (GFunc)g_free, NULL); + g_slist_free (config_dirs); + } + + { + const gchar* const * system_data_dirs = g_get_system_data_dirs (); + const gchar* const * iter; + + for (iter = system_data_dirs; *iter; iter++) + { + dirs = g_slist_append (dirs, g_build_filename (*iter, "myspell", "dicts", NULL)); + } + } + + /* until I work out how to link the modules against enchant in MacOSX - fjf + */ +#ifndef XP_TARGET_COCOA + char * myspell_prefix = NULL; + + /* Look for explicitly set registry values */ + myspell_prefix = enchant_get_registry_value ("Myspell", "Data_Dir"); + if (myspell_prefix) + dirs = g_slist_append (dirs, myspell_prefix); + + /* Dynamically locate library and search for modules relative to it. */ + char * enchant_prefix = enchant_get_prefix_dir(); + if(enchant_prefix) + { + myspell_prefix = g_build_filename(enchant_prefix, "share", "enchant", "myspell", NULL); + g_free(enchant_prefix); + dirs = g_slist_append (dirs, myspell_prefix); + } +#endif + +#ifdef ENCHANT_MYSPELL_DICT_DIR + dirs = g_slist_append (dirs, g_strdup (ENCHANT_MYSPELL_DICT_DIR)); +#endif + +#if defined(_WIN32) + char* open_office_dicts_dir = myspell_checker_get_open_office_dicts_dir (); + if (open_office_dicts_dir) + { + dirs = g_slist_append (dirs, open_office_dicts_dir); + } +#endif + + { + GSList *config_dirs, *iter; + + config_dirs = enchant_get_dirs_from_param (broker, "enchant.myspell.dictionary.path"); + + for (iter = config_dirs; iter; iter = iter->next) + { + dirs = g_slist_append (dirs, g_strdup ((const gchar *)iter->data)); + } + + g_slist_foreach (config_dirs, (GFunc)g_free, NULL); + g_slist_free (config_dirs); + } + + { + char* hun_dir; +#if defined(_WIN32) + WCHAR wsz[MAX_PATH]; + DWORD rv = GetEnvironmentVariableW(L"DICPATH", wsz, MAX_PATH); + if (rv >0 && rv <= MAX_PATH) { + hun_dir = g_utf16_to_utf8((gunichar2*)wsz,-1,NULL,NULL,NULL); + } else hun_dir = NULL; +#else + hun_dir = getenv("DICPATH"); +#endif + if (hun_dir) + { + dirs = g_slist_append(dirs, g_strdup(hun_dir)); +#if defined(_WIN32) + g_free(hun_dir); +#endif + } + } + + return dirs; +} + +static void +s_buildDictionaryDirs (std::vector & dirs, EnchantBroker * broker) +{ + GSList *myspell_dirs, *iter; + + dirs.clear (); + + myspell_dirs = myspell_checker_get_dictionary_dirs (broker); + for (iter = myspell_dirs; iter; iter = iter->next) + { + dirs.push_back ((const char *)iter->data); + } + + g_slist_foreach (myspell_dirs, (GFunc)g_free, NULL); + g_slist_free (myspell_dirs); +} + +static void +s_buildHashNames (std::vector & names, EnchantBroker * broker, const char * dict) +{ + names.clear (); + + std::vector dirs; + s_buildDictionaryDirs (dirs, broker); + + char *dict_dic = g_strconcat(dict, ".dic", NULL); + for (size_t i = 0; i < dirs.size(); i++) + { + char *tmp = g_build_filename (dirs[i].c_str(), dict_dic, NULL); + names.push_back (tmp); + g_free (tmp); + } + + g_free(dict_dic); +} + +static bool +s_hasCorrespondingAffFile(const std::string & dicFile) +{ + std::string aff = dicFile; + aff.replace(aff.end()-3,aff.end(), "aff"); + return g_file_test(aff.c_str(), G_FILE_TEST_EXISTS) != 0; +} + +static bool is_plausible_dict_for_tag(const char *dir_entry, const char *tag) +{ + const char *dic_suffix = ".dic"; + size_t dic_suffix_len = strlen(dic_suffix); + size_t dir_entry_len = strlen(dir_entry); + size_t tag_len = strlen(tag); + + if (dir_entry_len - dic_suffix_len < tag_len) + return false; + if (strcmp(dir_entry+dir_entry_len-dic_suffix_len, dic_suffix) != 0) + return false; + if (strncmp (dir_entry, tag, tag_len) != 0) + return false; + //e.g. requested dict for "fi", + //reject "fil_PH.dic" + //allow "fi-FOO.dic", "fi_FOO.dic", "fi.dic", etc. + if (!ispunct(dir_entry[tag_len])) + return false; + return true; +} + +static char * +myspell_request_dictionary (EnchantBroker * broker, const char * tag) +{ + std::vector names; + + s_buildHashNames (names, broker, tag); + + for (size_t i = 0; i < names.size (); i++) { + if (g_file_test(names[i].c_str(), G_FILE_TEST_EXISTS)) { + if(s_hasCorrespondingAffFile(names[i])){ + return g_strdup (names[i].c_str()); + } + } + } + + std::vector dirs; + s_buildDictionaryDirs (dirs, broker); + + for (size_t i = 0; i < dirs.size(); i++) { + GDir *dir = g_dir_open (dirs[i].c_str(), 0, NULL); + if (dir) { + const char *dir_entry; + while ((dir_entry = g_dir_read_name (dir)) != NULL) { + if (is_plausible_dict_for_tag(dir_entry, tag)) { + char *dict = g_build_filename (dirs[i].c_str(), + dir_entry, NULL); + if(s_hasCorrespondingAffFile(dict)){ + g_dir_close (dir); + return dict; + } + } + } + + g_dir_close (dir); + } + } + + return NULL; +} + +bool +MySpellChecker::requestDictionary(const char *szLang) +{ + char *dic = NULL, *aff = NULL; + + dic = myspell_request_dictionary (m_broker, szLang); + if (!dic) + return false; + + aff = g_strdup(dic); + int len_dic = strlen(dic); + strcpy(aff+len_dic-3, "aff"); + if (g_file_test(aff, G_FILE_TEST_EXISTS)) + { + myspell = new Hunspell(aff, dic); + } + g_free(dic); + g_free(aff); + if(myspell == NULL){ + return false; + } + char *enc = myspell->get_dic_encoding(); + + m_translate_in = g_iconv_open(enc, "UTF-8"); + m_translate_out = g_iconv_open("UTF-8", enc); + + return true; +} + +/* + * Enchant + */ + +static char ** +myspell_dict_suggest (EnchantDict * me, const char *const word, + size_t len, size_t * out_n_suggs) +{ + MySpellChecker * checker; + + checker = (MySpellChecker *) me->user_data; + return checker->suggestWord (word, len, out_n_suggs); +} + +static int +myspell_dict_check (EnchantDict * me, const char *const word, size_t len) +{ + MySpellChecker * checker; + + checker = (MySpellChecker *) me->user_data; + + if (checker->checkWord(word, len)) + return 0; + + return 1; +} + +static void +myspell_provider_enum_dicts (const char * const directory, + std::vector & out_dicts) +{ + GDir * dir = g_dir_open (directory, 0, NULL); + if (dir) { + const char * entry; + + while ((entry = g_dir_read_name (dir)) != NULL) { + char * utf8_entry = g_filename_to_utf8 (entry, -1, NULL, NULL, NULL); + if (utf8_entry) { + std::string dir_entry (utf8_entry); + g_free (utf8_entry); + + int hit = dir_entry.rfind (".dic"); + if (hit != -1) { + /* don't include hyphenation dictionaries + and require .aff file to be present*/ + if(dir_entry.compare (0, 5, "hyph_") != 0) + { + std::string name(dir_entry.substr (0, hit)); + std::string affFileName(name + ".aff"); + char * aff = g_build_filename(directory, affFileName.c_str(), NULL); + if (g_file_test(aff, G_FILE_TEST_EXISTS)) + { + out_dicts.push_back (dir_entry.substr (0, hit)); + } + g_free(aff); + } + } + } + } + + g_dir_close (dir); + } +} + +extern "C" { + +ENCHANT_MODULE_EXPORT (EnchantProvider *) + init_enchant_provider (void); + +static char ** +myspell_provider_list_dicts (EnchantProvider * me, + size_t * out_n_dicts) +{ + std::vector dict_dirs, dicts; + char ** dictionary_list = NULL; + + s_buildDictionaryDirs (dict_dirs, me->owner); + + for (size_t i = 0; i < dict_dirs.size(); i++) + { + myspell_provider_enum_dicts (dict_dirs[i].c_str(), dicts); + } + + if (dicts.size () > 0) { + dictionary_list = g_new0 (char *, dicts.size() + 1); + + for (size_t i = 0; i < dicts.size(); i++) + dictionary_list[i] = g_strdup (dicts[i].c_str()); + } + + *out_n_dicts = dicts.size (); + return dictionary_list; +} + +static void +myspell_provider_free_string_list (EnchantProvider * me, char **str_list) +{ + g_strfreev (str_list); +} + +static EnchantDict * +myspell_provider_request_dict(EnchantProvider * me, const char *const tag) +{ + EnchantDict *dict; + MySpellChecker * checker; + + checker = new MySpellChecker(me->owner); + + if (!checker) + return NULL; + + if (!checker->requestDictionary(tag)) { + delete checker; + return NULL; + } + + dict = g_new0(EnchantDict, 1); + dict->user_data = (void *) checker; + dict->check = myspell_dict_check; + dict->suggest = myspell_dict_suggest; + // don't implement personal, session + + return dict; +} + +static void +myspell_provider_dispose_dict (EnchantProvider * me, EnchantDict * dict) +{ + MySpellChecker *checker; + + checker = (MySpellChecker *) dict->user_data; + delete checker; + + g_free (dict); +} + +static int +myspell_provider_dictionary_exists (struct str_enchant_provider * me, + const char *const tag) +{ + std::vector names; + + s_buildHashNames (names, me->owner, tag); + for (size_t i = 0; i < names.size(); i++) { + if (g_file_test (names[i].c_str(), G_FILE_TEST_EXISTS)) + { + std::string aff(names[i]); + aff.replace(aff.end() - 3, aff.end(), "aff"); + if (g_file_test(aff.c_str(), G_FILE_TEST_EXISTS)) + return 1; + } + } + + return 0; +} + +static void +myspell_provider_dispose (EnchantProvider * me) +{ + g_free (me); +} + +static const char * +myspell_provider_identify (EnchantProvider * me) +{ + return "myspell"; +} + +static const char * +myspell_provider_describe (EnchantProvider * me) +{ + return "Myspell Provider"; +} + +EnchantProvider * +init_enchant_provider (void) +{ + EnchantProvider *provider; + + provider = g_new0(EnchantProvider, 1); + provider->dispose = myspell_provider_dispose; + provider->request_dict = myspell_provider_request_dict; + provider->dispose_dict = myspell_provider_dispose_dict; + provider->dictionary_exists = myspell_provider_dictionary_exists; + provider->identify = myspell_provider_identify; + provider->describe = myspell_provider_describe; + provider->free_string_list = myspell_provider_free_string_list; + provider->list_dicts = myspell_provider_list_dicts; + + return provider; +} + +} // extern C linkage diff --git a/src/myspell/phonet.cxx b/src/myspell/phonet.cxx new file mode 100644 index 0000000..144bd40 --- /dev/null +++ b/src/myspell/phonet.cxx @@ -0,0 +1,292 @@ +/* phonetic.c - generic replacement aglogithms for phonetic transformation + Copyright (C) 2000 Bjoern Jacke + + 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, see + . + + Changelog: + + 2000-01-05 Bjoern Jacke + Initial Release insprired by the article about phonetic + transformations out of c't 25/1999 + + 2007-07-26 Bjoern Jacke + Released under MPL/GPL/LGPL tri-license for Hunspell + + 2007-08-23 Laszlo Nemeth + Porting from Aspell to Hunspell using C-like structs +*/ + +#include +#include +#include +#include + +#include "csutil.hxx" +#include "phonet.hxx" + +void init_phonet_hash(phonetable & parms) + { + int i, k; + + for (i = 0; i < HASHSIZE; i++) { + parms.hash[i] = -1; + } + + for (i = 0; parms.rules[i][0] != '\0'; i += 2) { + /** set hash value **/ + k = (unsigned char) parms.rules[i][0]; + + if (parms.hash[k] < 0) { + parms.hash[k] = i; + } + } + } + +// like strcpy but safe if the strings overlap +// but only if dest < src +static inline void strmove(char * dest, char * src) { + while (*src) + *dest++ = *src++; + *dest = '\0'; +} + +static int myisalpha(char ch) { + if ((unsigned char) ch < 128) return isalpha(ch); + return 1; +} + +/* phonetic transcription algorithm */ +/* see: http://aspell.net/man-html/Phonetic-Code.html */ +/* convert string to uppercase before this call */ +int phonet (const char * inword, char * target, + int len, + phonetable & parms) + { + /** Do phonetic transformation. **/ + /** "len" = length of "inword" incl. '\0'. **/ + + /** result: >= 0: length of "target" **/ + /** otherwise: error **/ + + int i,j,k=0,n,p,z; + int k0,n0,p0=-333,z0; + char c, c0; + const char * s; + typedef unsigned char uchar; + char word[MAXPHONETUTF8LEN + 1]; + if (len == -1) len = strlen(inword); + if (len > MAXPHONETUTF8LEN) return 0; + strcpy(word, inword); + + /** check word **/ + i = j = z = 0; + while ((c = word[i]) != '\0') { + n = parms.hash[(uchar) c]; + z0 = 0; + + if (n >= 0) { + /** check all rules for the same letter **/ + while (parms.rules[n][0] == c) { + + /** check whole string **/ + k = 1; /** number of found letters **/ + p = 5; /** default priority **/ + s = parms.rules[n]; + s++; /** important for (see below) "*(s-1)" **/ + + while (*s != '\0' && word[i+k] == *s + && !isdigit ((unsigned char) *s) && strchr ("(-<^$", *s) == NULL) { + k++; + s++; + } + if (*s == '(') { + /** check letters in "(..)" **/ + if (myisalpha(word[i+k]) // ...could be implied? + && strchr(s+1, word[i+k]) != NULL) { + k++; + while (*s != ')') + s++; + s++; + } + } + p0 = (int) *s; + k0 = k; + while (*s == '-' && k > 1) { + k--; + s++; + } + if (*s == '<') + s++; + if (isdigit ((unsigned char) *s)) { + /** determine priority **/ + p = *s - '0'; + s++; + } + if (*s == '^' && *(s+1) == '^') + s++; + + if (*s == '\0' + || (*s == '^' + && (i == 0 || ! myisalpha(word[i-1])) + && (*(s+1) != '$' + || (! myisalpha(word[i+k0]) ))) + || (*s == '$' && i > 0 + && myisalpha(word[i-1]) + && (! myisalpha(word[i+k0]) ))) + { + /** search for followup rules, if: **/ + /** parms.followup and k > 1 and NO '-' in searchstring **/ + c0 = word[i+k-1]; + n0 = parms.hash[(uchar) c0]; + +// if (parms.followup && k > 1 && n0 >= 0 + if (k > 1 && n0 >= 0 + && p0 != (int) '-' && word[i+k] != '\0') { + /** test follow-up rule for "word[i+k]" **/ + while (parms.rules[n0][0] == c0) { + + /** check whole string **/ + k0 = k; + p0 = 5; + s = parms.rules[n0]; + s++; + while (*s != '\0' && word[i+k0] == *s + && ! isdigit((unsigned char) *s) && strchr("(-<^$",*s) == NULL) { + k0++; + s++; + } + if (*s == '(') { + /** check letters **/ + if (myisalpha(word[i+k0]) + && strchr (s+1, word[i+k0]) != NULL) { + k0++; + while (*s != ')' && *s != '\0') + s++; + if (*s == ')') + s++; + } + } + while (*s == '-') { + /** "k0" gets NOT reduced **/ + /** because "if (k0 == k)" **/ + s++; + } + if (*s == '<') + s++; + if (isdigit ((unsigned char) *s)) { + p0 = *s - '0'; + s++; + } + + if (*s == '\0' + /** *s == '^' cuts **/ + || (*s == '$' && ! myisalpha(word[i+k0]))) + { + if (k0 == k) { + /** this is just a piece of the string **/ + n0 += 2; + continue; + } + + if (p0 < p) { + /** priority too low **/ + n0 += 2; + continue; + } + /** rule fits; stop search **/ + break; + } + n0 += 2; + } /** End of "while (parms.rules[n0][0] == c0)" **/ + + if (p0 >= p && parms.rules[n0][0] == c0) { + n += 2; + continue; + } + } /** end of follow-up stuff **/ + + /** replace string **/ + s = parms.rules[n+1]; + p0 = (parms.rules[n][0] != '\0' + && strchr (parms.rules[n]+1,'<') != NULL) ? 1:0; + if (p0 == 1 && z == 0) { + /** rule with '<' is used **/ + if (j > 0 && *s != '\0' + && (target[j-1] == c || target[j-1] == *s)) { + j--; + } + z0 = 1; + z = 1; + k0 = 0; + while (*s != '\0' && word[i+k0] != '\0') { + word[i+k0] = *s; + k0++; + s++; + } + if (k > k0) + strmove (&word[0]+i+k0, &word[0]+i+k); + + /** new "actual letter" **/ + c = word[i]; + } + else { /** no '<' rule used **/ + i += k - 1; + z = 0; + while (*s != '\0' + && *(s+1) != '\0' && j < len) { + if (j == 0 || target[j-1] != *s) { + target[j] = *s; + j++; + } + s++; + } + /** new "actual letter" **/ + c = *s; + if (parms.rules[n][0] != '\0' + && strstr (parms.rules[n]+1, "^^") != NULL) { + if (c != '\0') { + target[j] = c; + j++; + } + strmove (&word[0], &word[0]+i+1); + i = 0; + z0 = 1; + } + } + break; + } /** end of follow-up stuff **/ + n += 2; + } /** end of while (parms.rules[n][0] == c) **/ + } /** end of if (n >= 0) **/ + if (z0 == 0) { +// if (k && (assert(p0!=-333),!p0) && j < len && c != '\0' +// && (!parms.collapse_result || j == 0 || target[j-1] != c)){ + if (k && !p0 && j < len && c != '\0' + && (1 || j == 0 || target[j-1] != c)){ + /** condense only double letters **/ + target[j] = c; + ///printf("\n setting \n"); + j++; + } + + i++; + z = 0; + k=0; + } + } /** end of while ((c = word[i]) != '\0') **/ + + target[j] = '\0'; + return (j); + + } /** end of function "phonet" **/ diff --git a/src/myspell/phonet.hxx b/src/myspell/phonet.hxx new file mode 100644 index 0000000..f91d3b0 --- /dev/null +++ b/src/myspell/phonet.hxx @@ -0,0 +1,52 @@ +/* phonetic.c - generic replacement aglogithms for phonetic transformation + Copyright (C) 2000 Bjoern Jacke + + 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, see + . + + Changelog: + + 2000-01-05 Bjoern Jacke + Initial Release insprired by the article about phonetic + transformations out of c't 25/1999 + + 2007-07-26 Bjoern Jacke + Released under MPL/GPL/LGPL tri-license for Hunspell + + 2007-08-23 Laszlo Nemeth + Porting from Aspell to Hunspell using C-like structs +*/ + +#ifndef __PHONETHXX__ +#define __PHONETHXX__ + +#define HASHSIZE 256 +#define MAXPHONETLEN 256 +#define MAXPHONETUTF8LEN (MAXPHONETLEN * 4) + +#include "hunvisapi.h" + +struct phonetable { + char utf8; + cs_info * lang; + int num; + char * * rules; + int hash[HASHSIZE]; +}; + +LIBHUNSPELL_DLL_EXPORTED void init_phonet_hash(phonetable & parms); + +LIBHUNSPELL_DLL_EXPORTED int phonet (const char * inword, char * target, + int len, phonetable & phone); + +#endif diff --git a/src/myspell/replist.cxx b/src/myspell/replist.cxx new file mode 100644 index 0000000..080cd68 --- /dev/null +++ b/src/myspell/replist.cxx @@ -0,0 +1,87 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include + +#include "replist.hxx" +#include "csutil.hxx" + +RepList::RepList(int n) { + dat = (replentry **) malloc(sizeof(replentry *) * n); + if (dat == 0) size = 0; else size = n; + pos = 0; +} + +RepList::~RepList() +{ + for (int i = 0; i < pos; i++) { + free(dat[i]->pattern); + free(dat[i]->pattern2); + free(dat[i]); + } + free(dat); +} + +int RepList::get_pos() { + return pos; +} + +replentry * RepList::item(int n) { + return dat[n]; +} + +int RepList::near(const char * word) { + int p1 = 0; + int p2 = pos; + while ((p2 - p1) > 1) { + int m = (p1 + p2) / 2; + int c = strcmp(word, dat[m]->pattern); + if (c <= 0) { + if (c < 0) p2 = m; else p1 = p2 = m; + } else p1 = m; + } + return p1; +} + +int RepList::match(const char * word, int n) { + if (strncmp(word, dat[n]->pattern, strlen(dat[n]->pattern)) == 0) return strlen(dat[n]->pattern); + return 0; +} + +int RepList::add(char * pat1, char * pat2) { + if (pos >= size || pat1 == NULL || pat2 == NULL) return 1; + replentry * r = (replentry *) malloc(sizeof(replentry)); + if (r == NULL) return 1; + r->pattern = mystrrep(pat1, "_", " "); + r->pattern2 = mystrrep(pat2, "_", " "); + r->start = false; + r->end = false; + dat[pos++] = r; + for (int i = pos - 1; i > 0; i--) { + r = dat[i]; + if (strcmp(r->pattern, dat[i - 1]->pattern) < 0) { + dat[i] = dat[i - 1]; + dat[i - 1] = r; + } else break; + } + return 0; +} + +int RepList::conv(const char * word, char * dest) { + int stl = 0; + int change = 0; + for (size_t i = 0; i < strlen(word); i++) { + int n = near(word + i); + int l = match(word + i, n); + if (l) { + strcpy(dest + stl, dat[n]->pattern2); + stl += strlen(dat[n]->pattern2); + i += l - 1; + change = 1; + } else dest[stl++] = word[i]; + } + dest[stl] = '\0'; + return change; +} diff --git a/src/myspell/replist.hxx b/src/myspell/replist.hxx new file mode 100644 index 0000000..9c37e29 --- /dev/null +++ b/src/myspell/replist.hxx @@ -0,0 +1,27 @@ +/* string replacement list class */ +#ifndef _REPLIST_HXX_ +#define _REPLIST_HXX_ + +#include "hunvisapi.h" + +#include "w_char.hxx" + +class LIBHUNSPELL_DLL_EXPORTED RepList +{ +protected: + replentry ** dat; + int size; + int pos; + +public: + RepList(int n); + ~RepList(); + + int get_pos(); + int add(char * pat1, char * pat2); + replentry * item(int n); + int near(const char * word); + int match(const char * word, int n); + int conv(const char * word, char * dest); +}; +#endif diff --git a/src/myspell/suggestmgr.cxx b/src/myspell/suggestmgr.cxx new file mode 100644 index 0000000..bcc90ba --- /dev/null +++ b/src/myspell/suggestmgr.cxx @@ -0,0 +1,1996 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include +#include + +#include "suggestmgr.hxx" +#include "htypes.hxx" +#include "csutil.hxx" + +const w_char W_VLINE = { '\0', '|' }; + +SuggestMgr::SuggestMgr(const char * tryme, int maxn, + AffixMgr * aptr) +{ + + // register affix manager and check in string of chars to + // try when building candidate suggestions + pAMgr = aptr; + + csconv = NULL; + + ckeyl = 0; + ckey = NULL; + ckey_utf = NULL; + + ctryl = 0; + ctry = NULL; + ctry_utf = NULL; + + utf8 = 0; + langnum = 0; + complexprefixes = 0; + + maxSug = maxn; + nosplitsugs = 0; + maxngramsugs = MAXNGRAMSUGS; + maxcpdsugs = MAXCOMPOUNDSUGS; + + if (pAMgr) { + langnum = pAMgr->get_langnum(); + ckey = pAMgr->get_key_string(); + nosplitsugs = pAMgr->get_nosplitsugs(); + if (pAMgr->get_maxngramsugs() >= 0) + maxngramsugs = pAMgr->get_maxngramsugs(); + utf8 = pAMgr->get_utf8(); + if (pAMgr->get_maxcpdsugs() >= 0) + maxcpdsugs = pAMgr->get_maxcpdsugs(); + if (!utf8) + { + char * enc = pAMgr->get_encoding(); + csconv = get_current_cs(enc); + free(enc); + } + complexprefixes = pAMgr->get_complexprefixes(); + } + + if (ckey) { + if (utf8) { + w_char t[MAXSWL]; + ckeyl = u8_u16(t, MAXSWL, ckey); + ckey_utf = (w_char *) malloc(ckeyl * sizeof(w_char)); + if (ckey_utf) memcpy(ckey_utf, t, ckeyl * sizeof(w_char)); + else ckeyl = 0; + } else { + ckeyl = strlen(ckey); + } + } + + if (tryme) { + ctry = mystrdup(tryme); + if (ctry) ctryl = strlen(ctry); + if (ctry && utf8) { + w_char t[MAXSWL]; + ctryl = u8_u16(t, MAXSWL, tryme); + ctry_utf = (w_char *) malloc(ctryl * sizeof(w_char)); + if (ctry_utf) memcpy(ctry_utf, t, ctryl * sizeof(w_char)); + else ctryl = 0; + } + } +} + + +SuggestMgr::~SuggestMgr() +{ + pAMgr = NULL; + if (ckey) free(ckey); + ckey = NULL; + if (ckey_utf) free(ckey_utf); + ckey_utf = NULL; + ckeyl = 0; + if (ctry) free(ctry); + ctry = NULL; + if (ctry_utf) free(ctry_utf); + ctry_utf = NULL; + ctryl = 0; + maxSug = 0; +#ifdef MOZILLA_CLIENT + delete [] csconv; +#endif +} + +int SuggestMgr::testsug(char** wlst, const char * candidate, int wl, int ns, int cpdsuggest, + int * timer, clock_t * timelimit) { + int cwrd = 1; + if (ns == maxSug) return maxSug; + for (int k=0; k < ns; k++) { + if (strcmp(candidate,wlst[k]) == 0) cwrd = 0; + } + if ((cwrd) && checkword(candidate, wl, cpdsuggest, timer, timelimit)) { + wlst[ns] = mystrdup(candidate); + if (wlst[ns] == NULL) { + for (int j=0; j 0) oldSug = nsug; + + // suggestions for an uppercase word (html -> HTML) + if ((nsug < maxSug) && (nsug > -1)) { + nsug = (utf8) ? capchars_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + capchars(wlst, word, nsug, cpdsuggest); + } + + // perhaps we made a typical fault of spelling + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = replchars(wlst, word, nsug, cpdsuggest); + } + + // perhaps we made chose the wrong char from a related set + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = mapchars(wlst, word, nsug, cpdsuggest); + } + + // did we swap the order of chars by mistake + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? swapchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + swapchar(wlst, word, nsug, cpdsuggest); + } + + // did we swap the order of non adjacent chars by mistake + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? longswapchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + longswapchar(wlst, word, nsug, cpdsuggest); + } + + // did we just hit the wrong key in place of a good char (case and keyboard) + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? badcharkey_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + badcharkey(wlst, word, nsug, cpdsuggest); + } + + // only suggest compound words when no other suggestion + if ((cpdsuggest == 0) && (nsug > nsugorig)) nocompoundtwowords=1; + + // did we add a char that should not be there + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? extrachar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + extrachar(wlst, word, nsug, cpdsuggest); + } + + + // did we forgot a char + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? forgotchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + forgotchar(wlst, word, nsug, cpdsuggest); + } + + // did we move a char + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? movechar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + movechar(wlst, word, nsug, cpdsuggest); + } + + // did we just hit the wrong key in place of a good char + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? badchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + badchar(wlst, word, nsug, cpdsuggest); + } + + // did we double two characters + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? doubletwochars_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + doubletwochars(wlst, word, nsug, cpdsuggest); + } + + // perhaps we forgot to hit space and two words ran together + if (!nosplitsugs && (nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = twowords(wlst, word, nsug, cpdsuggest); + } + + } // repeating ``for'' statement compounding support + + if (nsug < 0) { + // we ran out of memory - we should free up as much as possible + for (int i = 0; i < maxSug; i++) + if (wlst[i] != NULL) free(wlst[i]); + free(wlst); + wlst = NULL; + } + + if (!nocompoundtwowords && (nsug > 0) && onlycompoundsug) *onlycompoundsug = 1; + + *slst = wlst; + return nsug; +} + +// generate suggestions for a word with typical mistake +// pass in address of array of char * pointers +#ifdef HUNSPELL_EXPERIMENTAL +int SuggestMgr::suggest_auto(char*** slst, const char * w, int nsug) +{ + int nocompoundtwowords = 0; + char ** wlst; + int oldSug; + + char w2[MAXWORDUTF8LEN]; + const char * word = w; + + // word reversing wrapper for complex prefixes + if (complexprefixes) { + strcpy(w2, w); + if (utf8) reverseword_utf(w2); else reverseword(w2); + word = w2; + } + + if (*slst) { + wlst = *slst; + } else { + wlst = (char **) malloc(maxSug * sizeof(char *)); + if (wlst == NULL) return -1; + } + + for (int cpdsuggest=0; (cpdsuggest<2) && (nocompoundtwowords==0); cpdsuggest++) { + + // limit compound suggestion + if (cpdsuggest > 0) oldSug = nsug; + + // perhaps we made a typical fault of spelling + if ((nsug < maxSug) && (nsug > -1)) + nsug = replchars(wlst, word, nsug, cpdsuggest); + + // perhaps we made chose the wrong char from a related set + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) + nsug = mapchars(wlst, word, nsug, cpdsuggest); + + if ((cpdsuggest==0) && (nsug>0)) nocompoundtwowords=1; + + // perhaps we forgot to hit space and two words ran together + + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs)) && check_forbidden(word, strlen(word))) { + nsug = twowords(wlst, word, nsug, cpdsuggest); + } + + } // repeating ``for'' statement compounding support + + if (nsug < 0) { + for (int i=0;i HTML) +int SuggestMgr::capchars_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + w_char candidate_utf[MAXSWL]; + memcpy(candidate_utf, word, wl * sizeof(w_char)); + mkallcap_utf(candidate_utf, wl, langnum); + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + return testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); +} + +// suggestions for an uppercase word (html -> HTML) +int SuggestMgr::capchars(char** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + strcpy(candidate, word); + mkallcap(candidate, csconv); + return testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); +} + +// suggestions for when chose the wrong char out of a related set +int SuggestMgr::mapchars(char** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + clock_t timelimit; + int timer; + candidate[0] = '\0'; + + int wl = strlen(word); + if (wl < 2 || ! pAMgr) return ns; + + int nummap = pAMgr->get_nummap(); + struct mapentry* maptable = pAMgr->get_maptable(); + if (maptable==NULL) return ns; + + timelimit = clock(); + timer = MINTIMER; + return map_related(word, (char *) &candidate, 0, 0, wlst, cpdsuggest, ns, maptable, nummap, &timer, &timelimit); +} + +int SuggestMgr::map_related(const char * word, char * candidate, int wn, int cn, + char** wlst, int cpdsuggest, int ns, + const mapentry* maptable, int nummap, int * timer, clock_t * timelimit) +{ + if (*(word + wn) == '\0') { + int cwrd = 1; + *(candidate + cn) = '\0'; + int wl = strlen(candidate); + for (int m=0; m < ns; m++) + if (strcmp(candidate, wlst[m]) == 0) cwrd = 0; + if ((cwrd) && checkword(candidate, wl, cpdsuggest, timer, timelimit)) { + if (ns < maxSug) { + wlst[ns] = mystrdup(candidate); + if (wlst[ns] == NULL) return -1; + ns++; + } + } + return ns; + } + int in_map = 0; + for (int j = 0; j < nummap; j++) { + for (int k = 0; k < maptable[j].len; k++) { + int len = strlen(maptable[j].set[k]); + if (strncmp(maptable[j].set[k], word + wn, len) == 0) { + in_map = 1; + for (int l = 0; l < maptable[j].len; l++) { + strcpy(candidate + cn, maptable[j].set[l]); + ns = map_related(word, candidate, wn + len, strlen(candidate), wlst, + cpdsuggest, ns, maptable, nummap, timer, timelimit); + if (!(*timer)) return ns; + } + } + } + } + if (!in_map) { + *(candidate + cn) = *(word + wn); + ns = map_related(word, candidate, wn + 1, cn + 1, wlst, cpdsuggest, + ns, maptable, nummap, timer, timelimit); + } + return ns; +} + +// suggestions for a typical fault of spelling, that +// differs with more, than 1 letter from the right form. +int SuggestMgr::replchars(char** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + const char * r; + int lenr, lenp; + int wl = strlen(word); + if (wl < 2 || ! pAMgr) return ns; + int numrep = pAMgr->get_numrep(); + struct replentry* reptable = pAMgr->get_reptable(); + if (reptable==NULL) return ns; + for (int i=0; i < numrep; i++ ) { + r = word; + lenr = strlen(reptable[i].pattern2); + lenp = strlen(reptable[i].pattern); + // search every occurence of the pattern in the word + while ((r=strstr(r, reptable[i].pattern)) != NULL && (!reptable[i].end || strlen(r) == strlen(reptable[i].pattern)) && + (!reptable[i].start || r == word)) { + strcpy(candidate, word); + if (r-word + lenr + strlen(r+lenp) >= MAXSWUTF8L) break; + strcpy(candidate+(r-word),reptable[i].pattern2); + strcpy(candidate+(r-word)+lenr, r+lenp); + ns = testsug(wlst, candidate, wl-lenp+lenr, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + // check REP suggestions with space + char * sp = strchr(candidate, ' '); + if (sp) { + char * prev = candidate; + while (sp) { + *sp = '\0'; + if (checkword(prev, strlen(prev), 0, NULL, NULL)) { + int oldns = ns; + *sp = ' '; + ns = testsug(wlst, sp + 1, strlen(sp + 1), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + if (oldns < ns) { + free(wlst[ns - 1]); + wlst[ns - 1] = mystrdup(candidate); + if (!wlst[ns - 1]) return -1; + } + } + *sp = ' '; + prev = sp + 1; + sp = strchr(prev, ' '); + } + } + r++; // search for the next letter + } + } + return ns; +} + +// perhaps we doubled two characters (pattern aba -> ababa, for example vacation -> vacacation) +int SuggestMgr::doubletwochars(char** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + int state=0; + int wl = strlen(word); + if (wl < 5 || ! pAMgr) return ns; + for (int i=2; i < wl; i++ ) { + if (word[i]==word[i-2]) { + state++; + if (state==3) { + strcpy(candidate,word); + strcpy(candidate+i-1,word+i+1); + ns = testsug(wlst, candidate, wl-2, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + state=0; + } + } else { + state=0; + } + } + return ns; +} + +// perhaps we doubled two characters (pattern aba -> ababa, for example vacation -> vacacation) +int SuggestMgr::doubletwochars_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + w_char candidate_utf[MAXSWL]; + char candidate[MAXSWUTF8L]; + int state=0; + if (wl < 5 || ! pAMgr) return ns; + for (int i=2; i < wl; i++) { + if (w_char_eq(word[i], word[i-2])) { + state++; + if (state==3) { + memcpy(candidate_utf, word, (i - 1) * sizeof(w_char)); + memcpy(candidate_utf+i-1, word+i+1, (wl-i-1) * sizeof(w_char)); + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl-2); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + state=0; + } + } else { + state=0; + } + } + return ns; +} + +// error is wrong char in place of correct one (case and keyboard related version) +int SuggestMgr::badcharkey(char ** wlst, const char * word, int ns, int cpdsuggest) +{ + char tmpc; + char candidate[MAXSWUTF8L]; + int wl = strlen(word); + strcpy(candidate, word); + // swap out each char one by one and try uppercase and neighbor + // keyboard chars in its place to see if that makes a good word + + for (int i=0; i < wl; i++) { + tmpc = candidate[i]; + // check with uppercase letters + candidate[i] = csconv[((unsigned char)tmpc)].cupper; + if (tmpc != candidate[i]) { + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + candidate[i] = tmpc; + } + // check neighbor characters in keyboard string + if (!ckey) continue; + char * loc = strchr(ckey, tmpc); + while (loc) { + if ((loc > ckey) && (*(loc - 1) != '|')) { + candidate[i] = *(loc - 1); + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + if ((*(loc + 1) != '|') && (*(loc + 1) != '\0')) { + candidate[i] = *(loc + 1); + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + loc = strchr(loc + 1, tmpc); + } + candidate[i] = tmpc; + } + return ns; +} + +// error is wrong char in place of correct one (case and keyboard related version) +int SuggestMgr::badcharkey_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + w_char tmpc; + w_char candidate_utf[MAXSWL]; + char candidate[MAXSWUTF8L]; + memcpy(candidate_utf, word, wl * sizeof(w_char)); + // swap out each char one by one and try all the tryme + // chars in its place to see if that makes a good word + for (int i=0; i < wl; i++) { + tmpc = candidate_utf[i]; + // check with uppercase letters + mkallcap_utf(candidate_utf + i, 1, langnum); + if (!w_char_eq(tmpc, candidate_utf[i])) { + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + candidate_utf[i] = tmpc; + } + // check neighbor characters in keyboard string + if (!ckey) continue; + w_char * loc = ckey_utf; + while ((loc < (ckey_utf + ckeyl)) && !w_char_eq(*loc, tmpc)) loc++; + while (loc < (ckey_utf + ckeyl)) { + if ((loc > ckey_utf) && !w_char_eq(*(loc - 1), W_VLINE)) { + candidate_utf[i] = *(loc - 1); + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + if (((loc + 1) < (ckey_utf + ckeyl)) && !w_char_eq(*(loc + 1), W_VLINE)) { + candidate_utf[i] = *(loc + 1); + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + do { loc++; } while ((loc < (ckey_utf + ckeyl)) && !w_char_eq(*loc, tmpc)); + } + candidate_utf[i] = tmpc; + } + return ns; +} + +// error is wrong char in place of correct one +int SuggestMgr::badchar(char ** wlst, const char * word, int ns, int cpdsuggest) +{ + char tmpc; + char candidate[MAXSWUTF8L]; + clock_t timelimit = clock(); + int timer = MINTIMER; + int wl = strlen(word); + strcpy(candidate, word); + // swap out each char one by one and try all the tryme + // chars in its place to see if that makes a good word + for (int j=0; j < ctryl; j++) { + for (int i=wl-1; i >= 0; i--) { + tmpc = candidate[i]; + if (ctry[j] == tmpc) continue; + candidate[i] = ctry[j]; + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, &timer, &timelimit); + if (ns == -1) return -1; + if (!timer) return ns; + candidate[i] = tmpc; + } + } + return ns; +} + +// error is wrong char in place of correct one +int SuggestMgr::badchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + w_char tmpc; + w_char candidate_utf[MAXSWL]; + char candidate[MAXSWUTF8L]; + clock_t timelimit = clock(); + int timer = MINTIMER; + memcpy(candidate_utf, word, wl * sizeof(w_char)); + // swap out each char one by one and try all the tryme + // chars in its place to see if that makes a good word + for (int j=0; j < ctryl; j++) { + for (int i=wl-1; i >= 0; i--) { + tmpc = candidate_utf[i]; + if (w_char_eq(tmpc, ctry_utf[j])) continue; + candidate_utf[i] = ctry_utf[j]; + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit); + if (ns == -1) return -1; + if (!timer) return ns; + candidate_utf[i] = tmpc; + } + } + return ns; +} + +// error is word has an extra letter it does not need +int SuggestMgr::extrachar_utf(char** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + w_char candidate_utf[MAXSWL]; + w_char * p; + w_char tmpc = W_VLINE; // not used value, only for VCC warning message + if (wl < 2) return ns; + // try omitting one char of word at a time + memcpy(candidate_utf, word, wl * sizeof(w_char)); + for (p = candidate_utf + wl - 1; p >= candidate_utf; p--) { + w_char tmpc2 = *p; + if (p < candidate_utf + wl - 1) *p = tmpc; + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl - 1); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + tmpc = tmpc2; + } + return ns; +} + +// error is word has an extra letter it does not need +int SuggestMgr::extrachar(char** wlst, const char * word, int ns, int cpdsuggest) +{ + char tmpc = '\0'; + char candidate[MAXSWUTF8L]; + char * p; + int wl = strlen(word); + if (wl < 2) return ns; + // try omitting one char of word at a time + strcpy (candidate, word); + for (p = candidate + wl - 1; p >=candidate; p--) { + char tmpc2 = *p; + *p = tmpc; + ns = testsug(wlst, candidate, wl-1, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + tmpc = tmpc2; + } + return ns; +} + +// error is missing a letter it needs +int SuggestMgr::forgotchar(char ** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + char * p; + clock_t timelimit = clock(); + int timer = MINTIMER; + int wl = strlen(word); + // try inserting a tryme character before every letter (and the null terminator) + for (int i = 0; i < ctryl; i++) { + strcpy(candidate, word); + for (p = candidate + wl; p >= candidate; p--) { + *(p+1) = *p; + *p = ctry[i]; + ns = testsug(wlst, candidate, wl+1, ns, cpdsuggest, &timer, &timelimit); + if (ns == -1) return -1; + if (!timer) return ns; + } + } + return ns; +} + +// error is missing a letter it needs +int SuggestMgr::forgotchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + w_char candidate_utf[MAXSWL]; + char candidate[MAXSWUTF8L]; + w_char * p; + clock_t timelimit = clock(); + int timer = MINTIMER; + // try inserting a tryme character at the end of the word and before every letter + for (int i = 0; i < ctryl; i++) { + memcpy (candidate_utf, word, wl * sizeof(w_char)); + for (p = candidate_utf + wl; p >= candidate_utf; p--) { + *(p + 1) = *p; + *p = ctry_utf[i]; + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl + 1); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit); + if (ns == -1) return -1; + if (!timer) return ns; + } + } + return ns; +} + + +/* error is should have been two words */ +int SuggestMgr::twowords(char ** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + char * p; + int c1, c2; + int forbidden = 0; + int cwrd; + + int wl=strlen(word); + if (wl < 3) return ns; + + if (langnum == LANG_hu) forbidden = check_forbidden(word, wl); + + strcpy(candidate + 1, word); + // split the string into two pieces after every char + // if both pieces are good words make them a suggestion + for (p = candidate + 1; p[1] != '\0'; p++) { + p[-1] = *p; + // go to end of the UTF-8 character + while (utf8 && ((p[1] & 0xc0) == 0x80)) { + *p = p[1]; + p++; + } + if (utf8 && p[1] == '\0') break; // last UTF-8 character + *p = '\0'; + c1 = checkword(candidate,strlen(candidate), cpdsuggest, NULL, NULL); + if (c1) { + c2 = checkword((p+1),strlen(p+1), cpdsuggest, NULL, NULL); + if (c2) { + *p = ' '; + + // spec. Hungarian code (need a better compound word support) + if ((langnum == LANG_hu) && !forbidden && + // if 3 repeating letter, use - instead of space + (((p[-1] == p[1]) && (((p>candidate+1) && (p[-1] == p[-2])) || (p[-1] == p[2]))) || + // or multiple compounding, with more, than 6 syllables + ((c1 == 3) && (c2 >= 2)))) *p = '-'; + + cwrd = 1; + for (int k=0; k < ns; k++) + if (strcmp(candidate,wlst[k]) == 0) cwrd = 0; + if (ns < maxSug) { + if (cwrd) { + wlst[ns] = mystrdup(candidate); + if (wlst[ns] == NULL) return -1; + ns++; + } + } else return ns; + // add two word suggestion with dash, if TRY string contains + // "a" or "-" + // NOTE: cwrd doesn't modified for REP twoword sugg. + if (ctry && (strchr(ctry, 'a') || strchr(ctry, '-')) && + mystrlen(p + 1) > 1 && + mystrlen(candidate) - mystrlen(p) > 1) { + *p = '-'; + for (int k=0; k < ns; k++) + if (strcmp(candidate,wlst[k]) == 0) cwrd = 0; + if (ns < maxSug) { + if (cwrd) { + wlst[ns] = mystrdup(candidate); + if (wlst[ns] == NULL) return -1; + ns++; + } + } else return ns; + } + } + } + } + return ns; +} + + +// error is adjacent letter were swapped +int SuggestMgr::swapchar(char ** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + char * p; + char tmpc; + int wl=strlen(word); + // try swapping adjacent chars one by one + strcpy(candidate, word); + for (p = candidate; p[1] != 0; p++) { + tmpc = *p; + *p = p[1]; + p[1] = tmpc; + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + p[1] = *p; + *p = tmpc; + } + // try double swaps for short words + // ahev -> have, owudl -> would + if (wl == 4 || wl == 5) { + candidate[0] = word[1]; + candidate[1] = word[0]; + candidate[2] = word[2]; + candidate[wl - 2] = word[wl - 1]; + candidate[wl - 1] = word[wl - 2]; + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + if (wl == 5) { + candidate[0] = word[0]; + candidate[1] = word[2]; + candidate[2] = word[1]; + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + } + return ns; +} + +// error is adjacent letter were swapped +int SuggestMgr::swapchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + w_char candidate_utf[MAXSWL]; + char candidate[MAXSWUTF8L]; + w_char * p; + w_char tmpc; + int len = 0; + // try swapping adjacent chars one by one + memcpy (candidate_utf, word, wl * sizeof(w_char)); + for (p = candidate_utf; p < (candidate_utf + wl - 1); p++) { + tmpc = *p; + *p = p[1]; + p[1] = tmpc; + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + if (len == 0) len = strlen(candidate); + ns = testsug(wlst, candidate, len, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + p[1] = *p; + *p = tmpc; + } + // try double swaps for short words + // ahev -> have, owudl -> would, suodn -> sound + if (wl == 4 || wl == 5) { + candidate_utf[0] = word[1]; + candidate_utf[1] = word[0]; + candidate_utf[2] = word[2]; + candidate_utf[wl - 2] = word[wl - 1]; + candidate_utf[wl - 1] = word[wl - 2]; + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, len, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + if (wl == 5) { + candidate_utf[0] = word[0]; + candidate_utf[1] = word[2]; + candidate_utf[2] = word[1]; + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, len, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + } + return ns; +} + +// error is not adjacent letter were swapped +int SuggestMgr::longswapchar(char ** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + char * p; + char * q; + char tmpc; + int wl=strlen(word); + // try swapping not adjacent chars one by one + strcpy(candidate, word); + for (p = candidate; *p != 0; p++) { + for (q = candidate; *q != 0; q++) { + if (abs((int)(p-q)) > 1) { + tmpc = *p; + *p = *q; + *q = tmpc; + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + *q = *p; + *p = tmpc; + } + } + } + return ns; +} + + +// error is adjacent letter were swapped +int SuggestMgr::longswapchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + w_char candidate_utf[MAXSWL]; + char candidate[MAXSWUTF8L]; + w_char * p; + w_char * q; + w_char tmpc; + // try swapping not adjacent chars + memcpy (candidate_utf, word, wl * sizeof(w_char)); + for (p = candidate_utf; p < (candidate_utf + wl); p++) { + for (q = candidate_utf; q < (candidate_utf + wl); q++) { + if (abs((int)(p-q)) > 1) { + tmpc = *p; + *p = *q; + *q = tmpc; + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + *q = *p; + *p = tmpc; + } + } + } + return ns; +} + +// error is a letter was moved +int SuggestMgr::movechar(char ** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + char * p; + char * q; + char tmpc; + + int wl=strlen(word); + // try moving a char + strcpy(candidate, word); + for (p = candidate; *p != 0; p++) { + for (q = p + 1; (*q != 0) && ((q - p) < 10); q++) { + tmpc = *(q-1); + *(q-1) = *q; + *q = tmpc; + if ((q-p) < 2) continue; // omit swap char + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + strcpy(candidate, word); + } + for (p = candidate + wl - 1; p > candidate; p--) { + for (q = p - 1; (q >= candidate) && ((p - q) < 10); q--) { + tmpc = *(q+1); + *(q+1) = *q; + *q = tmpc; + if ((p-q) < 2) continue; // omit swap char + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + strcpy(candidate, word); + } + return ns; +} + +// error is a letter was moved +int SuggestMgr::movechar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + w_char candidate_utf[MAXSWL]; + char candidate[MAXSWUTF8L]; + w_char * p; + w_char * q; + w_char tmpc; + // try moving a char + memcpy (candidate_utf, word, wl * sizeof(w_char)); + for (p = candidate_utf; p < (candidate_utf + wl); p++) { + for (q = p + 1; (q < (candidate_utf + wl)) && ((q - p) < 10); q++) { + tmpc = *(q-1); + *(q-1) = *q; + *q = tmpc; + if ((q-p) < 2) continue; // omit swap char + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + memcpy (candidate_utf, word, wl * sizeof(w_char)); + } + for (p = candidate_utf + wl - 1; p > candidate_utf; p--) { + for (q = p - 1; (q >= candidate_utf) && ((p - q) < 10); q--) { + tmpc = *(q+1); + *(q+1) = *q; + *q = tmpc; + if ((p-q) < 2) continue; // omit swap char + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + memcpy (candidate_utf, word, wl * sizeof(w_char)); + } + return ns; +} + +// generate a set of suggestions for very poorly spelled words +int SuggestMgr::ngsuggest(char** wlst, char * w, int ns, HashMgr** pHMgr, int md) +{ + + int i, j; + int lval; + int sc, scphon; + int lp, lpphon; + int nonbmp = 0; + + // exhaustively search through all root words + // keeping track of the MAX_ROOTS most similar root words + struct hentry * roots[MAX_ROOTS]; + char * rootsphon[MAX_ROOTS]; + int scores[MAX_ROOTS]; + int scoresphon[MAX_ROOTS]; + for (i = 0; i < MAX_ROOTS; i++) { + roots[i] = NULL; + scores[i] = -100 * i; + rootsphon[i] = NULL; + scoresphon[i] = -100 * i; + } + lp = MAX_ROOTS - 1; + lpphon = MAX_ROOTS - 1; + scphon = -20000; + + char w2[MAXWORDUTF8LEN]; + char f[MAXSWUTF8L]; + char * word = w; + + // word reversing wrapper for complex prefixes + if (complexprefixes) { + strcpy(w2, w); + if (utf8) reverseword_utf(w2); else reverseword(w2); + word = w2; + } + + char mw[MAXSWUTF8L]; + w_char u8[MAXSWL]; + int nc = strlen(word); + int n = (utf8) ? u8_u16(u8, MAXSWL, word) : nc; + + // set character based ngram suggestion for words with non-BMP Unicode characters + if (n == -1) { + utf8 = 0; + n = nc; + nonbmp = 1; + } + + struct hentry* hp = NULL; + int col = -1; + phonetable * ph = (pAMgr) ? pAMgr->get_phonetable() : NULL; + char target[MAXSWUTF8L]; + char candidate[MAXSWUTF8L]; + if (ph) { + if (utf8) { + w_char _w[MAXSWL]; + int _wl = u8_u16(_w, MAXSWL, word); + mkallcap_utf(_w, _wl, langnum); + u16_u8(candidate, MAXSWUTF8L, _w, _wl); + } else { + strcpy(candidate, word); + mkallcap(candidate, csconv); + } + phonet(candidate, target, n, *ph); + } + + FLAG forbiddenword = pAMgr ? pAMgr->get_forbiddenword() : FLAG_NULL; + FLAG nosuggest = pAMgr ? pAMgr->get_nosuggest() : FLAG_NULL; + FLAG nongramsuggest = pAMgr ? pAMgr->get_nongramsuggest() : FLAG_NULL; + FLAG onlyincompound = pAMgr ? pAMgr->get_onlyincompound() : FLAG_NULL; + + for (i = 0; i < md; i++) { + while (0 != (hp = (pHMgr[i])->walk_hashtable(col, hp))) { + if ((hp->astr) && (pAMgr) && + (TESTAFF(hp->astr, forbiddenword, hp->alen) || + TESTAFF(hp->astr, ONLYUPCASEFLAG, hp->alen) || + TESTAFF(hp->astr, nosuggest, hp->alen) || + TESTAFF(hp->astr, nongramsuggest, hp->alen) || + TESTAFF(hp->astr, onlyincompound, hp->alen))) continue; + + sc = ngram(3, word, HENTRY_WORD(hp), NGRAM_LONGER_WORSE + NGRAM_LOWERING) + + leftcommonsubstring(word, HENTRY_WORD(hp)); + + // check special pronounciation + if ((hp->var & H_OPT_PHON) && copy_field(f, HENTRY_DATA(hp), MORPH_PHON)) { + int sc2 = ngram(3, word, f, NGRAM_LONGER_WORSE + NGRAM_LOWERING) + + + leftcommonsubstring(word, f); + if (sc2 > sc) sc = sc2; + } + + scphon = -20000; + if (ph && (sc > 2) && (abs(n - (int) hp->clen) <= 3)) { + char target2[MAXSWUTF8L]; + if (utf8) { + w_char _w[MAXSWL]; + int _wl = u8_u16(_w, MAXSWL, HENTRY_WORD(hp)); + mkallcap_utf(_w, _wl, langnum); + u16_u8(candidate, MAXSWUTF8L, _w, _wl); + } else { + strcpy(candidate, HENTRY_WORD(hp)); + mkallcap(candidate, csconv); + } + phonet(candidate, target2, -1, *ph); + scphon = 2 * ngram(3, target, target2, NGRAM_LONGER_WORSE); + } + + if (sc > scores[lp]) { + scores[lp] = sc; + roots[lp] = hp; + lval = sc; + for (j=0; j < MAX_ROOTS; j++) + if (scores[j] < lval) { + lp = j; + lval = scores[j]; + } + } + + + if (scphon > scoresphon[lpphon]) { + scoresphon[lpphon] = scphon; + rootsphon[lpphon] = HENTRY_WORD(hp); + lval = scphon; + for (j=0; j < MAX_ROOTS; j++) + if (scoresphon[j] < lval) { + lpphon = j; + lval = scoresphon[j]; + } + } + }} + + // find minimum threshold for a passable suggestion + // mangle original word three differnt ways + // and score them to generate a minimum acceptable score + int thresh = 0; + for (int sp = 1; sp < 4; sp++) { + if (utf8) { + for (int k=sp; k < n; k+=4) *((unsigned short *) u8 + k) = '*'; + u16_u8(mw, MAXSWUTF8L, u8, n); + thresh = thresh + ngram(n, word, mw, NGRAM_ANY_MISMATCH + NGRAM_LOWERING); + } else { + strcpy(mw, word); + for (int k=sp; k < n; k+=4) *(mw + k) = '*'; + thresh = thresh + ngram(n, word, mw, NGRAM_ANY_MISMATCH + NGRAM_LOWERING); + } + } + thresh = thresh / 3; + thresh--; + + // now expand affixes on each of these root words and + // and use length adjusted ngram scores to select + // possible suggestions + char * guess[MAX_GUESS]; + char * guessorig[MAX_GUESS]; + int gscore[MAX_GUESS]; + for(i=0;iexpand_rootword(glst, MAX_WORDS, HENTRY_WORD(rp), rp->blen, + rp->astr, rp->alen, word, nc, + ((rp->var & H_OPT_PHON) ? copy_field(f, HENTRY_DATA(rp), MORPH_PHON) : NULL)); + + for (int k = 0; k < nw ; k++) { + sc = ngram(n, word, glst[k].word, NGRAM_ANY_MISMATCH + NGRAM_LOWERING) + + leftcommonsubstring(word, glst[k].word); + + if (sc > thresh) { + if (sc > gscore[lp]) { + if (guess[lp]) { + free (guess[lp]); + if (guessorig[lp]) { + free(guessorig[lp]); + guessorig[lp] = NULL; + } + } + gscore[lp] = sc; + guess[lp] = glst[k].word; + guessorig[lp] = glst[k].orig; + lval = sc; + for (j=0; j < MAX_GUESS; j++) + if (gscore[j] < lval) { + lp = j; + lval = gscore[j]; + } + } else { + free(glst[k].word); + if (glst[k].orig) free(glst[k].orig); + } + } else { + free(glst[k].word); + if (glst[k].orig) free(glst[k].orig); + } + } + } + } + free(glst); + + // now we are done generating guesses + // sort in order of decreasing score + + + bubblesort(&guess[0], &guessorig[0], &gscore[0], MAX_GUESS); + if (ph) bubblesort(&rootsphon[0], NULL, &scoresphon[0], MAX_ROOTS); + + // weight suggestions with a similarity index, based on + // the longest common subsequent algorithm and resort + + int is_swap = 0; + int re; + float fact = 1.0; + if (pAMgr) { + int maxd = pAMgr->get_maxdiff(); + if (maxd >= 0) fact = (10.0 - maxd)/5.0; + } + + for (i=0; i < MAX_GUESS; i++) { + if (guess[i]) { + // lowering guess[i] + char gl[MAXSWUTF8L]; + int len; + if (utf8) { + w_char _w[MAXSWL]; + len = u8_u16(_w, MAXSWL, guess[i]); + mkallsmall_utf(_w, len, langnum); + u16_u8(gl, MAXSWUTF8L, _w, len); + } else { + strcpy(gl, guess[i]); + mkallsmall(gl, csconv); + len = strlen(guess[i]); + } + + int _lcs = lcslen(word, gl); + + // same characters with different casing + if ((n == len) && (n == _lcs)) { + gscore[i] += 2000; + break; + } + // using 2-gram instead of 3, and other weightening + gscore[i] = + // length of longest common subsequent minus length difference + 2 * _lcs - abs((int) (n - len)) + + // weight length of the left common substring + leftcommonsubstring(word, gl) + + // weight equal character positions + (commoncharacterpositions(word, gl, &is_swap) ? 1: 0) + + // swap character (not neighboring) + ((is_swap) ? 10 : 0) + + // ngram + ngram(4, word, gl, NGRAM_ANY_MISMATCH + NGRAM_LOWERING) + + // weighted ngrams + (re = ngram(2, word, gl, NGRAM_ANY_MISMATCH + NGRAM_LOWERING + NGRAM_WEIGHTED)) + + (re += ngram(2, gl, word, NGRAM_ANY_MISMATCH + NGRAM_LOWERING + NGRAM_WEIGHTED)) + + // different limit for dictionaries with PHONE rules + (ph ? (re < len * fact ? -1000 : 0) : (re < (n + len)*fact? -1000 : 0)); + } + } + + bubblesort(&guess[0], &guessorig[0], &gscore[0], MAX_GUESS); + +// phonetic version + if (ph) for (i=0; i < MAX_ROOTS; i++) { + if (rootsphon[i]) { + // lowering rootphon[i] + char gl[MAXSWUTF8L]; + int len; + if (utf8) { + w_char _w[MAXSWL]; + len = u8_u16(_w, MAXSWL, rootsphon[i]); + mkallsmall_utf(_w, len, langnum); + u16_u8(gl, MAXSWUTF8L, _w, len); + } else { + strcpy(gl, rootsphon[i]); + mkallsmall(gl, csconv); + len = strlen(rootsphon[i]); + } + + // heuristic weigthing of ngram scores + scoresphon[i] += 2 * lcslen(word, gl) - abs((int) (n - len)) + + // weight length of the left common substring + leftcommonsubstring(word, gl); + } + } + + if (ph) bubblesort(&rootsphon[0], NULL, &scoresphon[0], MAX_ROOTS); + + // copy over + int oldns = ns; + + int same = 0; + for (i=0; i < MAX_GUESS; i++) { + if (guess[i]) { + if ((ns < oldns + maxngramsugs) && (ns < maxSug) && (!same || (gscore[i] > 1000))) { + int unique = 1; + // leave only excellent suggestions, if exists + if (gscore[i] > 1000) same = 1; else if (gscore[i] < -100) { + same = 1; + // keep the best ngram suggestions, unless in ONLYMAXDIFF mode + if (ns > oldns || (pAMgr && pAMgr->get_onlymaxdiff())) { + free(guess[i]); + if (guessorig[i]) free(guessorig[i]); + continue; + } + } + for (j = 0; j < ns; j++) { + // don't suggest previous suggestions or a previous suggestion with prefixes or affixes + if ((!guessorig[i] && strstr(guess[i], wlst[j])) || + (guessorig[i] && strstr(guessorig[i], wlst[j])) || + // check forbidden words + !checkword(guess[i], strlen(guess[i]), 0, NULL, NULL)) unique = 0; + } + if (unique) { + wlst[ns++] = guess[i]; + if (guessorig[i]) { + free(guess[i]); + wlst[ns-1] = guessorig[i]; + } + } else { + free(guess[i]); + if (guessorig[i]) free(guessorig[i]); + } + } else { + free(guess[i]); + if (guessorig[i]) free(guessorig[i]); + } + } + } + + oldns = ns; + if (ph) for (i=0; i < MAX_ROOTS; i++) { + if (rootsphon[i]) { + if ((ns < oldns + MAXPHONSUGS) && (ns < maxSug)) { + int unique = 1; + for (j = 0; j < ns; j++) { + // don't suggest previous suggestions or a previous suggestion with prefixes or affixes + if (strstr(rootsphon[i], wlst[j]) || + // check forbidden words + !checkword(rootsphon[i], strlen(rootsphon[i]), 0, NULL, NULL)) unique = 0; + } + if (unique) { + wlst[ns++] = mystrdup(rootsphon[i]); + if (!wlst[ns - 1]) return ns - 1; + } + } + } + } + + if (nonbmp) utf8 = 1; +// if (specpron) free(specpron); + return ns; +} + + +// see if a candidate suggestion is spelled correctly +// needs to check both root words and words with affixes + +// obsolote MySpell-HU modifications: +// return value 2 and 3 marks compounding with hyphen (-) +// `3' marks roots without suffix +int SuggestMgr::checkword(const char * word, int len, int cpdsuggest, int * timer, clock_t * timelimit) +{ + struct hentry * rv=NULL; + struct hentry * rv2=NULL; + int nosuffix = 0; + + // check time limit + if (timer) { + (*timer)--; + if (!(*timer) && timelimit) { + if ((clock() - *timelimit) > TIMELIMIT) return 0; + *timer = MAXPLUSTIMER; + } + } + + if (pAMgr) { + if (cpdsuggest==1) { + if (pAMgr->get_compound()) { + rv = pAMgr->compound_check(word, len, 0, 0, 100, 0, NULL, 0, 1, 0); //EXT + if (rv && (!(rv2 = pAMgr->lookup(word)) || !rv2->astr || + !(TESTAFF(rv2->astr,pAMgr->get_forbiddenword(),rv2->alen) || + TESTAFF(rv2->astr,pAMgr->get_nosuggest(),rv2->alen)))) return 3; // XXX obsolote categorisation + only ICONV needs affix flag check? + } + return 0; + } + + rv = pAMgr->lookup(word); + + if (rv) { + if ((rv->astr) && (TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen) + || TESTAFF(rv->astr,pAMgr->get_nosuggest(),rv->alen))) return 0; + while (rv) { + if (rv->astr && (TESTAFF(rv->astr,pAMgr->get_needaffix(),rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || + TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) { + rv = rv->next_homonym; + } else break; + } + } else rv = pAMgr->prefix_check(word, len, 0); // only prefix, and prefix + suffix XXX + + if (rv) { + nosuffix=1; + } else { + rv = pAMgr->suffix_check(word, len, 0, NULL, NULL, 0, NULL); // only suffix + } + + if (!rv && pAMgr->have_contclass()) { + rv = pAMgr->suffix_check_twosfx(word, len, 0, NULL, FLAG_NULL); + if (!rv) rv = pAMgr->prefix_check_twosfx(word, len, 1, FLAG_NULL); + } + + // check forbidden words + if ((rv) && (rv->astr) && (TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || + TESTAFF(rv->astr,pAMgr->get_nosuggest(),rv->alen) || + TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) return 0; + + if (rv) { // XXX obsolote + if ((pAMgr->get_compoundflag()) && + TESTAFF(rv->astr, pAMgr->get_compoundflag(), rv->alen)) return 2 + nosuffix; + return 1; + } + } + return 0; +} + +int SuggestMgr::check_forbidden(const char * word, int len) +{ + struct hentry * rv = NULL; + + if (pAMgr) { + rv = pAMgr->lookup(word); + if (rv && rv->astr && (TESTAFF(rv->astr,pAMgr->get_needaffix(),rv->alen) || + TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) rv = NULL; + if (!(pAMgr->prefix_check(word,len,1))) + rv = pAMgr->suffix_check(word,len, 0, NULL, NULL, 0, NULL); // prefix+suffix, suffix + // check forbidden words + if ((rv) && (rv->astr) && TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen)) return 1; + } + return 0; +} + +#ifdef HUNSPELL_EXPERIMENTAL +// suggest possible stems +int SuggestMgr::suggest_pos_stems(char*** slst, const char * w, int nsug) +{ + char ** wlst; + + struct hentry * rv = NULL; + + char w2[MAXSWUTF8L]; + const char * word = w; + + // word reversing wrapper for complex prefixes + if (complexprefixes) { + strcpy(w2, w); + if (utf8) reverseword_utf(w2); else reverseword(w2); + word = w2; + } + + int wl = strlen(word); + + + if (*slst) { + wlst = *slst; + } else { + wlst = (char **) calloc(maxSug, sizeof(char *)); + if (wlst == NULL) return -1; + } + + rv = pAMgr->suffix_check(word, wl, 0, NULL, wlst, maxSug, &nsug); + + // delete dash from end of word + if (nsug > 0) { + for (int j=0; j < nsug; j++) { + if (wlst[j][strlen(wlst[j]) - 1] == '-') wlst[j][strlen(wlst[j]) - 1] = '\0'; + } + } + + *slst = wlst; + return nsug; +} +#endif // END OF HUNSPELL_EXPERIMENTAL CODE + + +char * SuggestMgr::suggest_morph(const char * w) +{ + char result[MAXLNLEN]; + char * r = (char *) result; + char * st; + + struct hentry * rv = NULL; + + *result = '\0'; + + if (! pAMgr) return NULL; + + char w2[MAXSWUTF8L]; + const char * word = w; + + // word reversing wrapper for complex prefixes + if (complexprefixes) { + strcpy(w2, w); + if (utf8) reverseword_utf(w2); else reverseword(w2); + word = w2; + } + + rv = pAMgr->lookup(word); + + while (rv) { + if ((!rv->astr) || !(TESTAFF(rv->astr, pAMgr->get_forbiddenword(), rv->alen) || + TESTAFF(rv->astr, pAMgr->get_needaffix(), rv->alen) || + TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) { + if (!HENTRY_FIND(rv, MORPH_STEM)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, MORPH_STEM, MAXLNLEN); + mystrcat(result, word, MAXLNLEN); + } + if (HENTRY_DATA(rv)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + } + mystrcat(result, "\n", MAXLNLEN); + } + rv = rv->next_homonym; + } + + st = pAMgr->affix_check_morph(word,strlen(word)); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + + if (pAMgr->get_compound() && (*result == '\0')) + pAMgr->compound_check_morph(word, strlen(word), + 0, 0, 100, 0,NULL, 0, &r, NULL); + + return (*result) ? mystrdup(line_uniq(result, MSEP_REC)) : NULL; +} + +#ifdef HUNSPELL_EXPERIMENTAL +char * SuggestMgr::suggest_morph_for_spelling_error(const char * word) +{ + char * p = NULL; + char ** wlst = (char **) calloc(maxSug, sizeof(char *)); + if (!**wlst) return NULL; + // we will use only the first suggestion + for (int i = 0; i < maxSug - 1; i++) wlst[i] = ""; + int ns = suggest(&wlst, word, maxSug - 1, NULL); + if (ns == maxSug) { + p = suggest_morph(wlst[maxSug - 1]); + free(wlst[maxSug - 1]); + } + if (wlst) free(wlst); + return p; +} +#endif // END OF HUNSPELL_EXPERIMENTAL CODE + +/* affixation */ +char * SuggestMgr::suggest_hentry_gen(hentry * rv, char * pattern) +{ + char result[MAXLNLEN]; + *result = '\0'; + int sfxcount = get_sfxcount(pattern); + + if (get_sfxcount(HENTRY_DATA(rv)) > sfxcount) return NULL; + + if (HENTRY_DATA(rv)) { + char * aff = pAMgr->morphgen(HENTRY_WORD(rv), rv->blen, rv->astr, rv->alen, + HENTRY_DATA(rv), pattern, 0); + if (aff) { + mystrcat(result, aff, MAXLNLEN); + mystrcat(result, "\n", MAXLNLEN); + free(aff); + } + } + + // check all allomorphs + char allomorph[MAXLNLEN]; + char * p = NULL; + if (HENTRY_DATA(rv)) p = (char *) strstr(HENTRY_DATA2(rv), MORPH_ALLOMORPH); + while (p) { + struct hentry * rv2 = NULL; + p += MORPH_TAG_LEN; + int plen = fieldlen(p); + strncpy(allomorph, p, plen); + allomorph[plen] = '\0'; + rv2 = pAMgr->lookup(allomorph); + while (rv2) { +// if (HENTRY_DATA(rv2) && get_sfxcount(HENTRY_DATA(rv2)) <= sfxcount) { + if (HENTRY_DATA(rv2)) { + char * st = (char *) strstr(HENTRY_DATA2(rv2), MORPH_STEM); + if (st && (strncmp(st + MORPH_TAG_LEN, + HENTRY_WORD(rv), fieldlen(st + MORPH_TAG_LEN)) == 0)) { + char * aff = pAMgr->morphgen(HENTRY_WORD(rv2), rv2->blen, rv2->astr, rv2->alen, + HENTRY_DATA(rv2), pattern, 0); + if (aff) { + mystrcat(result, aff, MAXLNLEN); + mystrcat(result, "\n", MAXLNLEN); + free(aff); + } + } + } + rv2 = rv2->next_homonym; + } + p = strstr(p + plen, MORPH_ALLOMORPH); + } + + return (*result) ? mystrdup(result) : NULL; +} + +char * SuggestMgr::suggest_gen(char ** desc, int n, char * pattern) { + char result[MAXLNLEN]; + char result2[MAXLNLEN]; + char newpattern[MAXLNLEN]; + *newpattern = '\0'; + if (n == 0) return 0; + *result2 = '\0'; + struct hentry * rv = NULL; + if (!pAMgr) return NULL; + +// search affixed forms with and without derivational suffixes + while(1) { + + for (int k = 0; k < n; k++) { + *result = '\0'; + // add compound word parts (except the last one) + char * s = (char *) desc[k]; + char * part = strstr(s, MORPH_PART); + if (part) { + char * nextpart = strstr(part + 1, MORPH_PART); + while (nextpart) { + copy_field(result + strlen(result), part, MORPH_PART); + part = nextpart; + nextpart = strstr(part + 1, MORPH_PART); + } + s = part; + } + + char **pl; + char tok[MAXLNLEN]; + strcpy(tok, s); + char * alt = strstr(tok, " | "); + while (alt) { + alt[1] = MSEP_ALT; + alt = strstr(alt, " | "); + } + int pln = line_tok(tok, &pl, MSEP_ALT); + for (int i = 0; i < pln; i++) { + // remove inflectional and terminal suffixes + char * is = strstr(pl[i], MORPH_INFL_SFX); + if (is) *is = '\0'; + char * ts = strstr(pl[i], MORPH_TERM_SFX); + while (ts) { + *ts = '_'; + ts = strstr(pl[i], MORPH_TERM_SFX); + } + char * st = strstr(s, MORPH_STEM); + if (st) { + copy_field(tok, st, MORPH_STEM); + rv = pAMgr->lookup(tok); + while (rv) { + char newpat[MAXLNLEN]; + strcpy(newpat, pl[i]); + strcat(newpat, pattern); + char * sg = suggest_hentry_gen(rv, newpat); + if (!sg) sg = suggest_hentry_gen(rv, pattern); + if (sg) { + char ** gen; + int genl = line_tok(sg, &gen, MSEP_REC); + free(sg); + sg = NULL; + for (int j = 0; j < genl; j++) { + if (strstr(pl[i], MORPH_SURF_PFX)) { + int r2l = strlen(result2); + result2[r2l] = MSEP_REC; + strcpy(result2 + r2l + 1, result); + copy_field(result2 + strlen(result2), pl[i], MORPH_SURF_PFX); + mystrcat(result2, gen[j], MAXLNLEN); + } else { + sprintf(result2 + strlen(result2), "%c%s%s", + MSEP_REC, result, gen[j]); + } + } + freelist(&gen, genl); + } + rv = rv->next_homonym; + } + } + } + freelist(&pl, pln); + } + + if (*result2 || !strstr(pattern, MORPH_DERI_SFX)) break; + strcpy(newpattern, pattern); + pattern = newpattern; + char * ds = strstr(pattern, MORPH_DERI_SFX); + while (ds) { + strncpy(ds, MORPH_TERM_SFX, MORPH_TAG_LEN); + ds = strstr(pattern, MORPH_DERI_SFX); + } + } + return (*result2 ? mystrdup(result2) : NULL); +} + + +// generate an n-gram score comparing s1 and s2 +int SuggestMgr::ngram(int n, char * s1, const char * s2, int opt) +{ + int nscore = 0; + int ns; + int l1; + int l2; + int test = 0; + + if (utf8) { + w_char su1[MAXSWL]; + w_char su2[MAXSWL]; + l1 = u8_u16(su1, MAXSWL, s1); + l2 = u8_u16(su2, MAXSWL, s2); + if ((l2 <= 0) || (l1 == -1)) return 0; + // lowering dictionary word + if (opt & NGRAM_LOWERING) mkallsmall_utf(su2, l2, langnum); + for (int j = 1; j <= n; j++) { + ns = 0; + for (int i = 0; i <= (l1-j); i++) { + int k = 0; + for (int l = 0; l <= (l2-j); l++) { + for (k = 0; k < j; k++) { + w_char * c1 = su1 + i + k; + w_char * c2 = su2 + l + k; + if ((c1->l != c2->l) || (c1->h != c2->h)) break; + } + if (k == j) { + ns++; + break; + } + } + if (k != j && opt & NGRAM_WEIGHTED) { + ns--; + test++; + if (i == 0 || i == l1-j) ns--; // side weight + } + } + nscore = nscore + ns; + if (ns < 2 && !(opt & NGRAM_WEIGHTED)) break; + } + } else { + l2 = strlen(s2); + if (l2 == 0) return 0; + l1 = strlen(s1); + char *t = mystrdup(s2); + if (opt & NGRAM_LOWERING) mkallsmall(t, csconv); + for (int j = 1; j <= n; j++) { + ns = 0; + for (int i = 0; i <= (l1-j); i++) { + char c = *(s1 + i + j); + *(s1 + i + j) = '\0'; + if (strstr(t,(s1+i))) { + ns++; + } else if (opt & NGRAM_WEIGHTED) { + ns--; +test++; + if (i == 0 || i == l1-j) ns--; // side weight + } + *(s1 + i + j ) = c; + } + nscore = nscore + ns; + if (ns < 2 && !(opt & NGRAM_WEIGHTED)) break; + } + free(t); + } + + ns = 0; + if (opt & NGRAM_LONGER_WORSE) ns = (l2-l1)-2; + if (opt & NGRAM_ANY_MISMATCH) ns = abs(l2-l1)-2; + ns = (nscore - ((ns > 0) ? ns : 0)); + return ns; +} + +// length of the left common substring of s1 and (decapitalised) s2 +int SuggestMgr::leftcommonsubstring(char * s1, const char * s2) { + if (utf8) { + w_char su1[MAXSWL]; + w_char su2[MAXSWL]; + su1[0].l = su2[0].l = su1[0].h = su2[0].h = 0; + // decapitalize dictionary word + if (complexprefixes) { + int l1 = u8_u16(su1, MAXSWL, s1); + int l2 = u8_u16(su2, MAXSWL, s2); + if (*((short *)su1+l1-1) == *((short *)su2+l2-1)) return 1; + } else { + int i; + u8_u16(su1, 1, s1); + u8_u16(su2, 1, s2); + unsigned short idx = (su2->h << 8) + su2->l; + unsigned short otheridx = (su1->h << 8) + su1->l; + if (otheridx != idx && + (otheridx != unicodetolower(idx, langnum))) return 0; + int l1 = u8_u16(su1, MAXSWL, s1); + int l2 = u8_u16(su2, MAXSWL, s2); + for(i = 1; (i < l1) && (i < l2) && + (su1[i].l == su2[i].l) && (su1[i].h == su2[i].h); i++); + return i; + } + } else { + if (complexprefixes) { + int l1 = strlen(s1); + int l2 = strlen(s2); + if (*(s2+l1-1) == *(s2+l2-1)) return 1; + } else { + char * olds = s1; + // decapitalise dictionary word + if ((*s1 != *s2) && (*s1 != csconv[((unsigned char)*s2)].clower)) return 0; + do { + s1++; s2++; + } while ((*s1 == *s2) && (*s1 != '\0')); + return (int)(s1 - olds); + } + } + return 0; +} + +int SuggestMgr::commoncharacterpositions(char * s1, const char * s2, int * is_swap) { + int num = 0; + int diff = 0; + int diffpos[2]; + *is_swap = 0; + if (utf8) { + w_char su1[MAXSWL]; + w_char su2[MAXSWL]; + int l1 = u8_u16(su1, MAXSWL, s1); + int l2 = u8_u16(su2, MAXSWL, s2); + // decapitalize dictionary word + if (complexprefixes) { + mkallsmall_utf(su2+l2-1, 1, langnum); + } else { + mkallsmall_utf(su2, 1, langnum); + } + for (int i = 0; (i < l1) && (i < l2); i++) { + if (((short *) su1)[i] == ((short *) su2)[i]) { + num++; + } else { + if (diff < 2) diffpos[diff] = i; + diff++; + } + } + if ((diff == 2) && (l1 == l2) && + (((short *) su1)[diffpos[0]] == ((short *) su2)[diffpos[1]]) && + (((short *) su1)[diffpos[1]] == ((short *) su2)[diffpos[0]])) *is_swap = 1; + } else { + int i; + char t[MAXSWUTF8L]; + strcpy(t, s2); + // decapitalize dictionary word + if (complexprefixes) { + int l2 = strlen(t); + *(t+l2-1) = csconv[((unsigned char)*(t+l2-1))].clower; + } else { + mkallsmall(t, csconv); + } + for (i = 0; (*(s1+i) != 0) && (*(t+i) != 0); i++) { + if (*(s1+i) == *(t+i)) { + num++; + } else { + if (diff < 2) diffpos[diff] = i; + diff++; + } + } + if ((diff == 2) && (*(s1+i) == 0) && (*(t+i) == 0) && + (*(s1+diffpos[0]) == *(t+diffpos[1])) && + (*(s1+diffpos[1]) == *(t+diffpos[0]))) *is_swap = 1; + } + return num; +} + +int SuggestMgr::mystrlen(const char * word) { + if (utf8) { + w_char w[MAXSWL]; + return u8_u16(w, MAXSWL, word); + } else return strlen(word); +} + +// sort in decreasing order of score +void SuggestMgr::bubblesort(char** rword, char** rword2, int* rsc, int n ) +{ + int m = 1; + while (m < n) { + int j = m; + while (j > 0) { + if (rsc[j-1] < rsc[j]) { + int sctmp = rsc[j-1]; + char * wdtmp = rword[j-1]; + rsc[j-1] = rsc[j]; + rword[j-1] = rword[j]; + rsc[j] = sctmp; + rword[j] = wdtmp; + if (rword2) { + wdtmp = rword2[j-1]; + rword2[j-1] = rword2[j]; + rword2[j] = wdtmp; + } + j--; + } else break; + } + m++; + } + return; +} + +// longest common subsequence +void SuggestMgr::lcs(const char * s, const char * s2, int * l1, int * l2, char ** result) { + int n, m; + w_char su[MAXSWL]; + w_char su2[MAXSWL]; + char * b; + char * c; + int i; + int j; + if (utf8) { + m = u8_u16(su, MAXSWL, s); + n = u8_u16(su2, MAXSWL, s2); + } else { + m = strlen(s); + n = strlen(s2); + } + c = (char *) malloc((m + 1) * (n + 1)); + b = (char *) malloc((m + 1) * (n + 1)); + if (!c || !b) { + if (c) free(c); + if (b) free(b); + *result = NULL; + return; + } + for (i = 1; i <= m; i++) c[i*(n+1)] = 0; + for (j = 0; j <= n; j++) c[j] = 0; + for (i = 1; i <= m; i++) { + for (j = 1; j <= n; j++) { + if ( ((utf8) && (*((short *) su+i-1) == *((short *)su2+j-1))) + || ((!utf8) && ((*(s+i-1)) == (*(s2+j-1))))) { + c[i*(n+1) + j] = c[(i-1)*(n+1) + j-1]+1; + b[i*(n+1) + j] = LCS_UPLEFT; + } else if (c[(i-1)*(n+1) + j] >= c[i*(n+1) + j-1]) { + c[i*(n+1) + j] = c[(i-1)*(n+1) + j]; + b[i*(n+1) + j] = LCS_UP; + } else { + c[i*(n+1) + j] = c[i*(n+1) + j-1]; + b[i*(n+1) + j] = LCS_LEFT; + } + } + } + *result = b; + free(c); + *l1 = m; + *l2 = n; +} + +int SuggestMgr::lcslen(const char * s, const char* s2) { + int m; + int n; + int i; + int j; + char * result; + int len = 0; + lcs(s, s2, &m, &n, &result); + if (!result) return 0; + i = m; + j = n; + while ((i != 0) && (j != 0)) { + if (result[i*(n+1) + j] == LCS_UPLEFT) { + len++; + i--; + j--; + } else if (result[i*(n+1) + j] == LCS_UP) { + i--; + } else j--; + } + free(result); + return len; +} diff --git a/src/myspell/suggestmgr.hxx b/src/myspell/suggestmgr.hxx new file mode 100644 index 0000000..5f043fd --- /dev/null +++ b/src/myspell/suggestmgr.hxx @@ -0,0 +1,111 @@ +#ifndef _SUGGESTMGR_HXX_ +#define _SUGGESTMGR_HXX_ + +#define MAXSWL 100 +#define MAXSWUTF8L (MAXSWL * 4) +#define MAX_ROOTS 100 +#define MAX_WORDS 100 +#define MAX_GUESS 200 +#define MAXNGRAMSUGS 4 +#define MAXPHONSUGS 2 +#define MAXCOMPOUNDSUGS 3 + +// timelimit: max ~1/4 sec (process time on Linux) for a time consuming function +#define TIMELIMIT (CLOCKS_PER_SEC >> 2) +#define MINTIMER 100 +#define MAXPLUSTIMER 100 + +#define NGRAM_LONGER_WORSE (1 << 0) +#define NGRAM_ANY_MISMATCH (1 << 1) +#define NGRAM_LOWERING (1 << 2) +#define NGRAM_WEIGHTED (1 << 3) + +#include "hunvisapi.h" + +#include "atypes.hxx" +#include "affixmgr.hxx" +#include "hashmgr.hxx" +#include "langnum.hxx" +#include + +enum { LCS_UP, LCS_LEFT, LCS_UPLEFT }; + +class LIBHUNSPELL_DLL_EXPORTED SuggestMgr +{ + char * ckey; + int ckeyl; + w_char * ckey_utf; + + char * ctry; + int ctryl; + w_char * ctry_utf; + + AffixMgr* pAMgr; + int maxSug; + struct cs_info * csconv; + int utf8; + int langnum; + int nosplitsugs; + int maxngramsugs; + int maxcpdsugs; + int complexprefixes; + + +public: + SuggestMgr(const char * tryme, int maxn, AffixMgr *aptr); + ~SuggestMgr(); + + int suggest(char*** slst, const char * word, int nsug, int * onlycmpdsug); + int ngsuggest(char ** wlst, char * word, int ns, HashMgr** pHMgr, int md); + int suggest_auto(char*** slst, const char * word, int nsug); + int suggest_stems(char*** slst, const char * word, int nsug); + int suggest_pos_stems(char*** slst, const char * word, int nsug); + + char * suggest_morph(const char * word); + char * suggest_gen(char ** pl, int pln, char * pattern); + char * suggest_morph_for_spelling_error(const char * word); + +private: + int testsug(char** wlst, const char * candidate, int wl, int ns, int cpdsuggest, + int * timer, clock_t * timelimit); + int checkword(const char *, int, int, int *, clock_t *); + int check_forbidden(const char *, int); + + int capchars(char **, const char *, int, int); + int replchars(char**, const char *, int, int); + int doubletwochars(char**, const char *, int, int); + int forgotchar(char **, const char *, int, int); + int swapchar(char **, const char *, int, int); + int longswapchar(char **, const char *, int, int); + int movechar(char **, const char *, int, int); + int extrachar(char **, const char *, int, int); + int badcharkey(char **, const char *, int, int); + int badchar(char **, const char *, int, int); + int twowords(char **, const char *, int, int); + int fixstems(char **, const char *, int); + + int capchars_utf(char **, const w_char *, int wl, int, int); + int doubletwochars_utf(char**, const w_char *, int wl, int, int); + int forgotchar_utf(char**, const w_char *, int wl, int, int); + int extrachar_utf(char**, const w_char *, int wl, int, int); + int badcharkey_utf(char **, const w_char *, int wl, int, int); + int badchar_utf(char **, const w_char *, int wl, int, int); + int swapchar_utf(char **, const w_char *, int wl, int, int); + int longswapchar_utf(char **, const w_char *, int, int, int); + int movechar_utf(char **, const w_char *, int, int, int); + + int mapchars(char**, const char *, int, int); + int map_related(const char *, char *, int, int, char ** wlst, int, int, const mapentry*, int, int *, clock_t *); + int ngram(int n, char * s1, const char * s2, int opt); + int mystrlen(const char * word); + int leftcommonsubstring(char * s1, const char * s2); + int commoncharacterpositions(char * s1, const char * s2, int * is_swap); + void bubblesort( char ** rwd, char ** rwd2, int * rsc, int n); + void lcs(const char * s, const char * s2, int * l1, int * l2, char ** result); + int lcslen(const char * s, const char* s2); + char * suggest_hentry_gen(hentry * rv, char * pattern); + +}; + +#endif + diff --git a/src/myspell/utf_info.cxx b/src/myspell/utf_info.cxx new file mode 100644 index 0000000..4a8e203 --- /dev/null +++ b/src/myspell/utf_info.cxx @@ -0,0 +1,19676 @@ +#include "csutil.hxx" +/* fields: Unicode letter, toupper, tolower */ +static struct unicode_info utf_lst[] = { +{ 0x0041, 0x0041, 0x0061 }, +{ 0x0042, 0x0042, 0x0062 }, +{ 0x0043, 0x0043, 0x0063 }, +{ 0x0044, 0x0044, 0x0064 }, +{ 0x0045, 0x0045, 0x0065 }, +{ 0x0046, 0x0046, 0x0066 }, +{ 0x0047, 0x0047, 0x0067 }, +{ 0x0048, 0x0048, 0x0068 }, +{ 0x0049, 0x0049, 0x0069 }, +{ 0x004A, 0x004A, 0x006A }, +{ 0x004B, 0x004B, 0x006B }, +{ 0x004C, 0x004C, 0x006C }, +{ 0x004D, 0x004D, 0x006D }, +{ 0x004E, 0x004E, 0x006E }, +{ 0x004F, 0x004F, 0x006F }, +{ 0x0050, 0x0050, 0x0070 }, +{ 0x0051, 0x0051, 0x0071 }, +{ 0x0052, 0x0052, 0x0072 }, +{ 0x0053, 0x0053, 0x0073 }, +{ 0x0054, 0x0054, 0x0074 }, +{ 0x0055, 0x0055, 0x0075 }, +{ 0x0056, 0x0056, 0x0076 }, +{ 0x0057, 0x0057, 0x0077 }, +{ 0x0058, 0x0058, 0x0078 }, +{ 0x0059, 0x0059, 0x0079 }, +{ 0x005A, 0x005A, 0x007A }, +{ 0x0061, 0x0041, 0x0061 }, +{ 0x0062, 0x0042, 0x0062 }, +{ 0x0063, 0x0043, 0x0063 }, +{ 0x0064, 0x0044, 0x0064 }, +{ 0x0065, 0x0045, 0x0065 }, +{ 0x0066, 0x0046, 0x0066 }, +{ 0x0067, 0x0047, 0x0067 }, +{ 0x0068, 0x0048, 0x0068 }, +{ 0x0069, 0x0049, 0x0069 }, +{ 0x006A, 0x004A, 0x006A }, +{ 0x006B, 0x004B, 0x006B }, +{ 0x006C, 0x004C, 0x006C }, +{ 0x006D, 0x004D, 0x006D }, +{ 0x006E, 0x004E, 0x006E }, +{ 0x006F, 0x004F, 0x006F }, +{ 0x0070, 0x0050, 0x0070 }, +{ 0x0071, 0x0051, 0x0071 }, +{ 0x0072, 0x0052, 0x0072 }, +{ 0x0073, 0x0053, 0x0073 }, +{ 0x0074, 0x0054, 0x0074 }, +{ 0x0075, 0x0055, 0x0075 }, +{ 0x0076, 0x0056, 0x0076 }, +{ 0x0077, 0x0057, 0x0077 }, +{ 0x0078, 0x0058, 0x0078 }, +{ 0x0079, 0x0059, 0x0079 }, +{ 0x007A, 0x005A, 0x007A }, +{ 0x00AA, 0x00AA, 0x00AA }, +{ 0x00B5, 0x039C, 0x00B5 }, +{ 0x00BA, 0x00BA, 0x00BA }, +{ 0x00C0, 0x00C0, 0x00E0 }, +{ 0x00C1, 0x00C1, 0x00E1 }, +{ 0x00C2, 0x00C2, 0x00E2 }, +{ 0x00C3, 0x00C3, 0x00E3 }, +{ 0x00C4, 0x00C4, 0x00E4 }, +{ 0x00C5, 0x00C5, 0x00E5 }, +{ 0x00C6, 0x00C6, 0x00E6 }, +{ 0x00C7, 0x00C7, 0x00E7 }, +{ 0x00C8, 0x00C8, 0x00E8 }, +{ 0x00C9, 0x00C9, 0x00E9 }, +{ 0x00CA, 0x00CA, 0x00EA }, +{ 0x00CB, 0x00CB, 0x00EB }, +{ 0x00CC, 0x00CC, 0x00EC }, +{ 0x00CD, 0x00CD, 0x00ED }, +{ 0x00CE, 0x00CE, 0x00EE }, +{ 0x00CF, 0x00CF, 0x00EF }, +{ 0x00D0, 0x00D0, 0x00F0 }, +{ 0x00D1, 0x00D1, 0x00F1 }, +{ 0x00D2, 0x00D2, 0x00F2 }, +{ 0x00D3, 0x00D3, 0x00F3 }, +{ 0x00D4, 0x00D4, 0x00F4 }, +{ 0x00D5, 0x00D5, 0x00F5 }, +{ 0x00D6, 0x00D6, 0x00F6 }, +{ 0x00D8, 0x00D8, 0x00F8 }, +{ 0x00D9, 0x00D9, 0x00F9 }, +{ 0x00DA, 0x00DA, 0x00FA }, +{ 0x00DB, 0x00DB, 0x00FB }, +{ 0x00DC, 0x00DC, 0x00FC }, +{ 0x00DD, 0x00DD, 0x00FD }, +{ 0x00DE, 0x00DE, 0x00FE }, +{ 0x00DF, 0x00DF, 0x00DF }, +{ 0x00E0, 0x00C0, 0x00E0 }, +{ 0x00E1, 0x00C1, 0x00E1 }, +{ 0x00E2, 0x00C2, 0x00E2 }, +{ 0x00E3, 0x00C3, 0x00E3 }, +{ 0x00E4, 0x00C4, 0x00E4 }, +{ 0x00E5, 0x00C5, 0x00E5 }, +{ 0x00E6, 0x00C6, 0x00E6 }, +{ 0x00E7, 0x00C7, 0x00E7 }, +{ 0x00E8, 0x00C8, 0x00E8 }, +{ 0x00E9, 0x00C9, 0x00E9 }, +{ 0x00EA, 0x00CA, 0x00EA }, +{ 0x00EB, 0x00CB, 0x00EB }, +{ 0x00EC, 0x00CC, 0x00EC }, +{ 0x00ED, 0x00CD, 0x00ED }, +{ 0x00EE, 0x00CE, 0x00EE }, +{ 0x00EF, 0x00CF, 0x00EF }, +{ 0x00F0, 0x00D0, 0x00F0 }, +{ 0x00F1, 0x00D1, 0x00F1 }, +{ 0x00F2, 0x00D2, 0x00F2 }, +{ 0x00F3, 0x00D3, 0x00F3 }, +{ 0x00F4, 0x00D4, 0x00F4 }, +{ 0x00F5, 0x00D5, 0x00F5 }, +{ 0x00F6, 0x00D6, 0x00F6 }, +{ 0x00F8, 0x00D8, 0x00F8 }, +{ 0x00F9, 0x00D9, 0x00F9 }, +{ 0x00FA, 0x00DA, 0x00FA }, +{ 0x00FB, 0x00DB, 0x00FB }, +{ 0x00FC, 0x00DC, 0x00FC }, +{ 0x00FD, 0x00DD, 0x00FD }, +{ 0x00FE, 0x00DE, 0x00FE }, +{ 0x00FF, 0x0178, 0x00FF }, +{ 0x0100, 0x0100, 0x0101 }, +{ 0x0101, 0x0100, 0x0101 }, +{ 0x0102, 0x0102, 0x0103 }, +{ 0x0103, 0x0102, 0x0103 }, +{ 0x0104, 0x0104, 0x0105 }, +{ 0x0105, 0x0104, 0x0105 }, +{ 0x0106, 0x0106, 0x0107 }, +{ 0x0107, 0x0106, 0x0107 }, +{ 0x0108, 0x0108, 0x0109 }, +{ 0x0109, 0x0108, 0x0109 }, +{ 0x010A, 0x010A, 0x010B }, +{ 0x010B, 0x010A, 0x010B }, +{ 0x010C, 0x010C, 0x010D }, +{ 0x010D, 0x010C, 0x010D }, +{ 0x010E, 0x010E, 0x010F }, +{ 0x010F, 0x010E, 0x010F }, +{ 0x0110, 0x0110, 0x0111 }, +{ 0x0111, 0x0110, 0x0111 }, +{ 0x0112, 0x0112, 0x0113 }, +{ 0x0113, 0x0112, 0x0113 }, +{ 0x0114, 0x0114, 0x0115 }, +{ 0x0115, 0x0114, 0x0115 }, +{ 0x0116, 0x0116, 0x0117 }, +{ 0x0117, 0x0116, 0x0117 }, +{ 0x0118, 0x0118, 0x0119 }, +{ 0x0119, 0x0118, 0x0119 }, +{ 0x011A, 0x011A, 0x011B }, +{ 0x011B, 0x011A, 0x011B }, +{ 0x011C, 0x011C, 0x011D }, +{ 0x011D, 0x011C, 0x011D }, +{ 0x011E, 0x011E, 0x011F }, +{ 0x011F, 0x011E, 0x011F }, +{ 0x0120, 0x0120, 0x0121 }, +{ 0x0121, 0x0120, 0x0121 }, +{ 0x0122, 0x0122, 0x0123 }, +{ 0x0123, 0x0122, 0x0123 }, +{ 0x0124, 0x0124, 0x0125 }, +{ 0x0125, 0x0124, 0x0125 }, +{ 0x0126, 0x0126, 0x0127 }, +{ 0x0127, 0x0126, 0x0127 }, +{ 0x0128, 0x0128, 0x0129 }, +{ 0x0129, 0x0128, 0x0129 }, +{ 0x012A, 0x012A, 0x012B }, +{ 0x012B, 0x012A, 0x012B }, +{ 0x012C, 0x012C, 0x012D }, +{ 0x012D, 0x012C, 0x012D }, +{ 0x012E, 0x012E, 0x012F }, +{ 0x012F, 0x012E, 0x012F }, +{ 0x0130, 0x0130, 0x0069 }, +{ 0x0131, 0x0049, 0x0131 }, +{ 0x0132, 0x0132, 0x0133 }, +{ 0x0133, 0x0132, 0x0133 }, +{ 0x0134, 0x0134, 0x0135 }, +{ 0x0135, 0x0134, 0x0135 }, +{ 0x0136, 0x0136, 0x0137 }, +{ 0x0137, 0x0136, 0x0137 }, +{ 0x0138, 0x0138, 0x0138 }, +{ 0x0139, 0x0139, 0x013A }, +{ 0x013A, 0x0139, 0x013A }, +{ 0x013B, 0x013B, 0x013C }, +{ 0x013C, 0x013B, 0x013C }, +{ 0x013D, 0x013D, 0x013E }, +{ 0x013E, 0x013D, 0x013E }, +{ 0x013F, 0x013F, 0x0140 }, +{ 0x0140, 0x013F, 0x0140 }, +{ 0x0141, 0x0141, 0x0142 }, +{ 0x0142, 0x0141, 0x0142 }, +{ 0x0143, 0x0143, 0x0144 }, +{ 0x0144, 0x0143, 0x0144 }, +{ 0x0145, 0x0145, 0x0146 }, +{ 0x0146, 0x0145, 0x0146 }, +{ 0x0147, 0x0147, 0x0148 }, +{ 0x0148, 0x0147, 0x0148 }, +{ 0x0149, 0x0149, 0x0149 }, +{ 0x014A, 0x014A, 0x014B }, +{ 0x014B, 0x014A, 0x014B }, +{ 0x014C, 0x014C, 0x014D }, +{ 0x014D, 0x014C, 0x014D }, +{ 0x014E, 0x014E, 0x014F }, +{ 0x014F, 0x014E, 0x014F }, +{ 0x0150, 0x0150, 0x0151 }, +{ 0x0151, 0x0150, 0x0151 }, +{ 0x0152, 0x0152, 0x0153 }, +{ 0x0153, 0x0152, 0x0153 }, +{ 0x0154, 0x0154, 0x0155 }, +{ 0x0155, 0x0154, 0x0155 }, +{ 0x0156, 0x0156, 0x0157 }, +{ 0x0157, 0x0156, 0x0157 }, +{ 0x0158, 0x0158, 0x0159 }, +{ 0x0159, 0x0158, 0x0159 }, +{ 0x015A, 0x015A, 0x015B }, +{ 0x015B, 0x015A, 0x015B }, +{ 0x015C, 0x015C, 0x015D }, +{ 0x015D, 0x015C, 0x015D }, +{ 0x015E, 0x015E, 0x015F }, +{ 0x015F, 0x015E, 0x015F }, +{ 0x0160, 0x0160, 0x0161 }, +{ 0x0161, 0x0160, 0x0161 }, +{ 0x0162, 0x0162, 0x0163 }, +{ 0x0163, 0x0162, 0x0163 }, +{ 0x0164, 0x0164, 0x0165 }, +{ 0x0165, 0x0164, 0x0165 }, +{ 0x0166, 0x0166, 0x0167 }, +{ 0x0167, 0x0166, 0x0167 }, +{ 0x0168, 0x0168, 0x0169 }, +{ 0x0169, 0x0168, 0x0169 }, +{ 0x016A, 0x016A, 0x016B }, +{ 0x016B, 0x016A, 0x016B }, +{ 0x016C, 0x016C, 0x016D }, +{ 0x016D, 0x016C, 0x016D }, +{ 0x016E, 0x016E, 0x016F }, +{ 0x016F, 0x016E, 0x016F }, +{ 0x0170, 0x0170, 0x0171 }, +{ 0x0171, 0x0170, 0x0171 }, +{ 0x0172, 0x0172, 0x0173 }, +{ 0x0173, 0x0172, 0x0173 }, +{ 0x0174, 0x0174, 0x0175 }, +{ 0x0175, 0x0174, 0x0175 }, +{ 0x0176, 0x0176, 0x0177 }, +{ 0x0177, 0x0176, 0x0177 }, +{ 0x0178, 0x0178, 0x00FF }, +{ 0x0179, 0x0179, 0x017A }, +{ 0x017A, 0x0179, 0x017A }, +{ 0x017B, 0x017B, 0x017C }, +{ 0x017C, 0x017B, 0x017C }, +{ 0x017D, 0x017D, 0x017E }, +{ 0x017E, 0x017D, 0x017E }, +{ 0x017F, 0x0053, 0x017F }, +{ 0x0180, 0x0180, 0x0180 }, +{ 0x0181, 0x0181, 0x0253 }, +{ 0x0182, 0x0182, 0x0183 }, +{ 0x0183, 0x0182, 0x0183 }, +{ 0x0184, 0x0184, 0x0185 }, +{ 0x0185, 0x0184, 0x0185 }, +{ 0x0186, 0x0186, 0x0254 }, +{ 0x0187, 0x0187, 0x0188 }, +{ 0x0188, 0x0187, 0x0188 }, +{ 0x0189, 0x0189, 0x0256 }, +{ 0x018A, 0x018A, 0x0257 }, +{ 0x018B, 0x018B, 0x018C }, +{ 0x018C, 0x018B, 0x018C }, +{ 0x018D, 0x018D, 0x018D }, +{ 0x018E, 0x018E, 0x01DD }, +{ 0x018F, 0x018F, 0x0259 }, +{ 0x0190, 0x0190, 0x025B }, +{ 0x0191, 0x0191, 0x0192 }, +{ 0x0192, 0x0191, 0x0192 }, +{ 0x0193, 0x0193, 0x0260 }, +{ 0x0194, 0x0194, 0x0263 }, +{ 0x0195, 0x01F6, 0x0195 }, +{ 0x0196, 0x0196, 0x0269 }, +{ 0x0197, 0x0197, 0x0268 }, +{ 0x0198, 0x0198, 0x0199 }, +{ 0x0199, 0x0198, 0x0199 }, +{ 0x019A, 0x023D, 0x019A }, +{ 0x019B, 0x019B, 0x019B }, +{ 0x019C, 0x019C, 0x026F }, +{ 0x019D, 0x019D, 0x0272 }, +{ 0x019E, 0x0220, 0x019E }, +{ 0x019F, 0x019F, 0x0275 }, +{ 0x01A0, 0x01A0, 0x01A1 }, +{ 0x01A1, 0x01A0, 0x01A1 }, +{ 0x01A2, 0x01A2, 0x01A3 }, +{ 0x01A3, 0x01A2, 0x01A3 }, +{ 0x01A4, 0x01A4, 0x01A5 }, +{ 0x01A5, 0x01A4, 0x01A5 }, +{ 0x01A6, 0x01A6, 0x0280 }, +{ 0x01A7, 0x01A7, 0x01A8 }, +{ 0x01A8, 0x01A7, 0x01A8 }, +{ 0x01A9, 0x01A9, 0x0283 }, +{ 0x01AA, 0x01AA, 0x01AA }, +{ 0x01AB, 0x01AB, 0x01AB }, +{ 0x01AC, 0x01AC, 0x01AD }, +{ 0x01AD, 0x01AC, 0x01AD }, +{ 0x01AE, 0x01AE, 0x0288 }, +{ 0x01AF, 0x01AF, 0x01B0 }, +{ 0x01B0, 0x01AF, 0x01B0 }, +{ 0x01B1, 0x01B1, 0x028A }, +{ 0x01B2, 0x01B2, 0x028B }, +{ 0x01B3, 0x01B3, 0x01B4 }, +{ 0x01B4, 0x01B3, 0x01B4 }, +{ 0x01B5, 0x01B5, 0x01B6 }, +{ 0x01B6, 0x01B5, 0x01B6 }, +{ 0x01B7, 0x01B7, 0x0292 }, +{ 0x01B8, 0x01B8, 0x01B9 }, +{ 0x01B9, 0x01B8, 0x01B9 }, +{ 0x01BA, 0x01BA, 0x01BA }, +{ 0x01BB, 0x01BB, 0x01BB }, +{ 0x01BC, 0x01BC, 0x01BD }, +{ 0x01BD, 0x01BC, 0x01BD }, +{ 0x01BE, 0x01BE, 0x01BE }, +{ 0x01BF, 0x01F7, 0x01BF }, +{ 0x01C0, 0x01C0, 0x01C0 }, +{ 0x01C1, 0x01C1, 0x01C1 }, +{ 0x01C2, 0x01C2, 0x01C2 }, +{ 0x01C3, 0x01C3, 0x01C3 }, +{ 0x01C4, 0x01C4, 0x01C6 }, +{ 0x01C5, 0x01C4, 0x01C6 }, +{ 0x01C6, 0x01C4, 0x01C6 }, +{ 0x01C7, 0x01C7, 0x01C9 }, +{ 0x01C8, 0x01C7, 0x01C9 }, +{ 0x01C9, 0x01C7, 0x01C9 }, +{ 0x01CA, 0x01CA, 0x01CC }, +{ 0x01CB, 0x01CA, 0x01CC }, +{ 0x01CC, 0x01CA, 0x01CC }, +{ 0x01CD, 0x01CD, 0x01CE }, +{ 0x01CE, 0x01CD, 0x01CE }, +{ 0x01CF, 0x01CF, 0x01D0 }, +{ 0x01D0, 0x01CF, 0x01D0 }, +{ 0x01D1, 0x01D1, 0x01D2 }, +{ 0x01D2, 0x01D1, 0x01D2 }, +{ 0x01D3, 0x01D3, 0x01D4 }, +{ 0x01D4, 0x01D3, 0x01D4 }, +{ 0x01D5, 0x01D5, 0x01D6 }, +{ 0x01D6, 0x01D5, 0x01D6 }, +{ 0x01D7, 0x01D7, 0x01D8 }, +{ 0x01D8, 0x01D7, 0x01D8 }, +{ 0x01D9, 0x01D9, 0x01DA }, +{ 0x01DA, 0x01D9, 0x01DA }, +{ 0x01DB, 0x01DB, 0x01DC }, +{ 0x01DC, 0x01DB, 0x01DC }, +{ 0x01DD, 0x018E, 0x01DD }, +{ 0x01DE, 0x01DE, 0x01DF }, +{ 0x01DF, 0x01DE, 0x01DF }, +{ 0x01E0, 0x01E0, 0x01E1 }, +{ 0x01E1, 0x01E0, 0x01E1 }, +{ 0x01E2, 0x01E2, 0x01E3 }, +{ 0x01E3, 0x01E2, 0x01E3 }, +{ 0x01E4, 0x01E4, 0x01E5 }, +{ 0x01E5, 0x01E4, 0x01E5 }, +{ 0x01E6, 0x01E6, 0x01E7 }, +{ 0x01E7, 0x01E6, 0x01E7 }, +{ 0x01E8, 0x01E8, 0x01E9 }, +{ 0x01E9, 0x01E8, 0x01E9 }, +{ 0x01EA, 0x01EA, 0x01EB }, +{ 0x01EB, 0x01EA, 0x01EB }, +{ 0x01EC, 0x01EC, 0x01ED }, +{ 0x01ED, 0x01EC, 0x01ED }, +{ 0x01EE, 0x01EE, 0x01EF }, +{ 0x01EF, 0x01EE, 0x01EF }, +{ 0x01F0, 0x01F0, 0x01F0 }, +{ 0x01F1, 0x01F1, 0x01F3 }, +{ 0x01F2, 0x01F1, 0x01F3 }, +{ 0x01F3, 0x01F1, 0x01F3 }, +{ 0x01F4, 0x01F4, 0x01F5 }, +{ 0x01F5, 0x01F4, 0x01F5 }, +{ 0x01F6, 0x01F6, 0x0195 }, +{ 0x01F7, 0x01F7, 0x01BF }, +{ 0x01F8, 0x01F8, 0x01F9 }, +{ 0x01F9, 0x01F8, 0x01F9 }, +{ 0x01FA, 0x01FA, 0x01FB }, +{ 0x01FB, 0x01FA, 0x01FB }, +{ 0x01FC, 0x01FC, 0x01FD }, +{ 0x01FD, 0x01FC, 0x01FD }, +{ 0x01FE, 0x01FE, 0x01FF }, +{ 0x01FF, 0x01FE, 0x01FF }, +{ 0x0200, 0x0200, 0x0201 }, +{ 0x0201, 0x0200, 0x0201 }, +{ 0x0202, 0x0202, 0x0203 }, +{ 0x0203, 0x0202, 0x0203 }, +{ 0x0204, 0x0204, 0x0205 }, +{ 0x0205, 0x0204, 0x0205 }, +{ 0x0206, 0x0206, 0x0207 }, +{ 0x0207, 0x0206, 0x0207 }, +{ 0x0208, 0x0208, 0x0209 }, +{ 0x0209, 0x0208, 0x0209 }, +{ 0x020A, 0x020A, 0x020B }, +{ 0x020B, 0x020A, 0x020B }, +{ 0x020C, 0x020C, 0x020D }, +{ 0x020D, 0x020C, 0x020D }, +{ 0x020E, 0x020E, 0x020F }, +{ 0x020F, 0x020E, 0x020F }, +{ 0x0210, 0x0210, 0x0211 }, +{ 0x0211, 0x0210, 0x0211 }, +{ 0x0212, 0x0212, 0x0213 }, +{ 0x0213, 0x0212, 0x0213 }, +{ 0x0214, 0x0214, 0x0215 }, +{ 0x0215, 0x0214, 0x0215 }, +{ 0x0216, 0x0216, 0x0217 }, +{ 0x0217, 0x0216, 0x0217 }, +{ 0x0218, 0x0218, 0x0219 }, +{ 0x0219, 0x0218, 0x0219 }, +{ 0x021A, 0x021A, 0x021B }, +{ 0x021B, 0x021A, 0x021B }, +{ 0x021C, 0x021C, 0x021D }, +{ 0x021D, 0x021C, 0x021D }, +{ 0x021E, 0x021E, 0x021F }, +{ 0x021F, 0x021E, 0x021F }, +{ 0x0220, 0x0220, 0x019E }, +{ 0x0221, 0x0221, 0x0221 }, +{ 0x0222, 0x0222, 0x0223 }, +{ 0x0223, 0x0222, 0x0223 }, +{ 0x0224, 0x0224, 0x0225 }, +{ 0x0225, 0x0224, 0x0225 }, +{ 0x0226, 0x0226, 0x0227 }, +{ 0x0227, 0x0226, 0x0227 }, +{ 0x0228, 0x0228, 0x0229 }, +{ 0x0229, 0x0228, 0x0229 }, +{ 0x022A, 0x022A, 0x022B }, +{ 0x022B, 0x022A, 0x022B }, +{ 0x022C, 0x022C, 0x022D }, +{ 0x022D, 0x022C, 0x022D }, +{ 0x022E, 0x022E, 0x022F }, +{ 0x022F, 0x022E, 0x022F }, +{ 0x0230, 0x0230, 0x0231 }, +{ 0x0231, 0x0230, 0x0231 }, +{ 0x0232, 0x0232, 0x0233 }, +{ 0x0233, 0x0232, 0x0233 }, +{ 0x0234, 0x0234, 0x0234 }, +{ 0x0235, 0x0235, 0x0235 }, +{ 0x0236, 0x0236, 0x0236 }, +{ 0x0237, 0x0237, 0x0237 }, +{ 0x0238, 0x0238, 0x0238 }, +{ 0x0239, 0x0239, 0x0239 }, +{ 0x023A, 0x023A, 0x023A }, +{ 0x023B, 0x023B, 0x023C }, +{ 0x023C, 0x023B, 0x023C }, +{ 0x023D, 0x023D, 0x019A }, +{ 0x023E, 0x023E, 0x023E }, +{ 0x023F, 0x023F, 0x023F }, +{ 0x0240, 0x0240, 0x0240 }, +{ 0x0241, 0x0241, 0x0294 }, +{ 0x0250, 0x0250, 0x0250 }, +{ 0x0251, 0x0251, 0x0251 }, +{ 0x0252, 0x0252, 0x0252 }, +{ 0x0253, 0x0181, 0x0253 }, +{ 0x0254, 0x0186, 0x0254 }, +{ 0x0255, 0x0255, 0x0255 }, +{ 0x0256, 0x0189, 0x0256 }, +{ 0x0257, 0x018A, 0x0257 }, +{ 0x0258, 0x0258, 0x0258 }, +{ 0x0259, 0x018F, 0x0259 }, +{ 0x025A, 0x025A, 0x025A }, +{ 0x025B, 0x0190, 0x025B }, +{ 0x025C, 0x025C, 0x025C }, +{ 0x025D, 0x025D, 0x025D }, +{ 0x025E, 0x025E, 0x025E }, +{ 0x025F, 0x025F, 0x025F }, +{ 0x0260, 0x0193, 0x0260 }, +{ 0x0261, 0x0261, 0x0261 }, +{ 0x0262, 0x0262, 0x0262 }, +{ 0x0263, 0x0194, 0x0263 }, +{ 0x0264, 0x0264, 0x0264 }, +{ 0x0265, 0x0265, 0x0265 }, +{ 0x0266, 0x0266, 0x0266 }, +{ 0x0267, 0x0267, 0x0267 }, +{ 0x0268, 0x0197, 0x0268 }, +{ 0x0269, 0x0196, 0x0269 }, +{ 0x026A, 0x026A, 0x026A }, +{ 0x026B, 0x026B, 0x026B }, +{ 0x026C, 0x026C, 0x026C }, +{ 0x026D, 0x026D, 0x026D }, +{ 0x026E, 0x026E, 0x026E }, +{ 0x026F, 0x019C, 0x026F }, +{ 0x0270, 0x0270, 0x0270 }, +{ 0x0271, 0x0271, 0x0271 }, +{ 0x0272, 0x019D, 0x0272 }, +{ 0x0273, 0x0273, 0x0273 }, +{ 0x0274, 0x0274, 0x0274 }, +{ 0x0275, 0x019F, 0x0275 }, +{ 0x0276, 0x0276, 0x0276 }, +{ 0x0277, 0x0277, 0x0277 }, +{ 0x0278, 0x0278, 0x0278 }, +{ 0x0279, 0x0279, 0x0279 }, +{ 0x027A, 0x027A, 0x027A }, +{ 0x027B, 0x027B, 0x027B }, +{ 0x027C, 0x027C, 0x027C }, +{ 0x027D, 0x027D, 0x027D }, +{ 0x027E, 0x027E, 0x027E }, +{ 0x027F, 0x027F, 0x027F }, +{ 0x0280, 0x01A6, 0x0280 }, +{ 0x0281, 0x0281, 0x0281 }, +{ 0x0282, 0x0282, 0x0282 }, +{ 0x0283, 0x01A9, 0x0283 }, +{ 0x0284, 0x0284, 0x0284 }, +{ 0x0285, 0x0285, 0x0285 }, +{ 0x0286, 0x0286, 0x0286 }, +{ 0x0287, 0x0287, 0x0287 }, +{ 0x0288, 0x01AE, 0x0288 }, +{ 0x0289, 0x0289, 0x0289 }, +{ 0x028A, 0x01B1, 0x028A }, +{ 0x028B, 0x01B2, 0x028B }, +{ 0x028C, 0x028C, 0x028C }, +{ 0x028D, 0x028D, 0x028D }, +{ 0x028E, 0x028E, 0x028E }, +{ 0x028F, 0x028F, 0x028F }, +{ 0x0290, 0x0290, 0x0290 }, +{ 0x0291, 0x0291, 0x0291 }, +{ 0x0292, 0x01B7, 0x0292 }, +{ 0x0293, 0x0293, 0x0293 }, +{ 0x0294, 0x0241, 0x0294 }, +{ 0x0295, 0x0295, 0x0295 }, +{ 0x0296, 0x0296, 0x0296 }, +{ 0x0297, 0x0297, 0x0297 }, +{ 0x0298, 0x0298, 0x0298 }, +{ 0x0299, 0x0299, 0x0299 }, +{ 0x029A, 0x029A, 0x029A }, +{ 0x029B, 0x029B, 0x029B }, +{ 0x029C, 0x029C, 0x029C }, +{ 0x029D, 0x029D, 0x029D }, +{ 0x029E, 0x029E, 0x029E }, +{ 0x029F, 0x029F, 0x029F }, +{ 0x02A0, 0x02A0, 0x02A0 }, +{ 0x02A1, 0x02A1, 0x02A1 }, +{ 0x02A2, 0x02A2, 0x02A2 }, +{ 0x02A3, 0x02A3, 0x02A3 }, +{ 0x02A4, 0x02A4, 0x02A4 }, +{ 0x02A5, 0x02A5, 0x02A5 }, +{ 0x02A6, 0x02A6, 0x02A6 }, +{ 0x02A7, 0x02A7, 0x02A7 }, +{ 0x02A8, 0x02A8, 0x02A8 }, +{ 0x02A9, 0x02A9, 0x02A9 }, +{ 0x02AA, 0x02AA, 0x02AA }, +{ 0x02AB, 0x02AB, 0x02AB }, +{ 0x02AC, 0x02AC, 0x02AC }, +{ 0x02AD, 0x02AD, 0x02AD }, +{ 0x02AE, 0x02AE, 0x02AE }, +{ 0x02AF, 0x02AF, 0x02AF }, +{ 0x02B0, 0x02B0, 0x02B0 }, +{ 0x02B1, 0x02B1, 0x02B1 }, +{ 0x02B2, 0x02B2, 0x02B2 }, +{ 0x02B3, 0x02B3, 0x02B3 }, +{ 0x02B4, 0x02B4, 0x02B4 }, +{ 0x02B5, 0x02B5, 0x02B5 }, +{ 0x02B6, 0x02B6, 0x02B6 }, +{ 0x02B7, 0x02B7, 0x02B7 }, +{ 0x02B8, 0x02B8, 0x02B8 }, +{ 0x02B9, 0x02B9, 0x02B9 }, +{ 0x02BA, 0x02BA, 0x02BA }, +{ 0x02BB, 0x02BB, 0x02BB }, +{ 0x02BC, 0x02BC, 0x02BC }, +{ 0x02BD, 0x02BD, 0x02BD }, +{ 0x02BE, 0x02BE, 0x02BE }, +{ 0x02BF, 0x02BF, 0x02BF }, +{ 0x02C0, 0x02C0, 0x02C0 }, +{ 0x02C1, 0x02C1, 0x02C1 }, +{ 0x02C6, 0x02C6, 0x02C6 }, +{ 0x02C7, 0x02C7, 0x02C7 }, +{ 0x02C8, 0x02C8, 0x02C8 }, +{ 0x02C9, 0x02C9, 0x02C9 }, +{ 0x02CA, 0x02CA, 0x02CA }, +{ 0x02CB, 0x02CB, 0x02CB }, +{ 0x02CC, 0x02CC, 0x02CC }, +{ 0x02CD, 0x02CD, 0x02CD }, +{ 0x02CE, 0x02CE, 0x02CE }, +{ 0x02CF, 0x02CF, 0x02CF }, +{ 0x02D0, 0x02D0, 0x02D0 }, +{ 0x02D1, 0x02D1, 0x02D1 }, +{ 0x02E0, 0x02E0, 0x02E0 }, +{ 0x02E1, 0x02E1, 0x02E1 }, +{ 0x02E2, 0x02E2, 0x02E2 }, +{ 0x02E3, 0x02E3, 0x02E3 }, +{ 0x02E4, 0x02E4, 0x02E4 }, +{ 0x02EE, 0x02EE, 0x02EE }, +{ 0x0300, 0x0300, 0x0300 }, +{ 0x0301, 0x0301, 0x0301 }, +{ 0x0302, 0x0302, 0x0302 }, +{ 0x0303, 0x0303, 0x0303 }, +{ 0x0304, 0x0304, 0x0304 }, +{ 0x0305, 0x0305, 0x0305 }, +{ 0x0306, 0x0306, 0x0306 }, +{ 0x0307, 0x0307, 0x0307 }, +{ 0x0308, 0x0308, 0x0308 }, +{ 0x0309, 0x0309, 0x0309 }, +{ 0x030A, 0x030A, 0x030A }, +{ 0x030B, 0x030B, 0x030B }, +{ 0x030C, 0x030C, 0x030C }, +{ 0x030D, 0x030D, 0x030D }, +{ 0x030E, 0x030E, 0x030E }, +{ 0x030F, 0x030F, 0x030F }, +{ 0x0310, 0x0310, 0x0310 }, +{ 0x0311, 0x0311, 0x0311 }, +{ 0x0312, 0x0312, 0x0312 }, +{ 0x0313, 0x0313, 0x0313 }, +{ 0x0314, 0x0314, 0x0314 }, +{ 0x0315, 0x0315, 0x0315 }, +{ 0x0316, 0x0316, 0x0316 }, +{ 0x0317, 0x0317, 0x0317 }, +{ 0x0318, 0x0318, 0x0318 }, +{ 0x0319, 0x0319, 0x0319 }, +{ 0x031A, 0x031A, 0x031A }, +{ 0x031B, 0x031B, 0x031B }, +{ 0x031C, 0x031C, 0x031C }, +{ 0x031D, 0x031D, 0x031D }, +{ 0x031E, 0x031E, 0x031E }, +{ 0x031F, 0x031F, 0x031F }, +{ 0x0320, 0x0320, 0x0320 }, +{ 0x0321, 0x0321, 0x0321 }, +{ 0x0322, 0x0322, 0x0322 }, +{ 0x0323, 0x0323, 0x0323 }, +{ 0x0324, 0x0324, 0x0324 }, +{ 0x0325, 0x0325, 0x0325 }, +{ 0x0326, 0x0326, 0x0326 }, +{ 0x0327, 0x0327, 0x0327 }, +{ 0x0328, 0x0328, 0x0328 }, +{ 0x0329, 0x0329, 0x0329 }, +{ 0x032A, 0x032A, 0x032A }, +{ 0x032B, 0x032B, 0x032B }, +{ 0x032C, 0x032C, 0x032C }, +{ 0x032D, 0x032D, 0x032D }, +{ 0x032E, 0x032E, 0x032E }, +{ 0x032F, 0x032F, 0x032F }, +{ 0x0330, 0x0330, 0x0330 }, +{ 0x0331, 0x0331, 0x0331 }, +{ 0x0332, 0x0332, 0x0332 }, +{ 0x0333, 0x0333, 0x0333 }, +{ 0x0334, 0x0334, 0x0334 }, +{ 0x0335, 0x0335, 0x0335 }, +{ 0x0336, 0x0336, 0x0336 }, +{ 0x0337, 0x0337, 0x0337 }, +{ 0x0338, 0x0338, 0x0338 }, +{ 0x0339, 0x0339, 0x0339 }, +{ 0x033A, 0x033A, 0x033A }, +{ 0x033B, 0x033B, 0x033B }, +{ 0x033C, 0x033C, 0x033C }, +{ 0x033D, 0x033D, 0x033D }, +{ 0x033E, 0x033E, 0x033E }, +{ 0x033F, 0x033F, 0x033F }, +{ 0x0340, 0x0340, 0x0340 }, +{ 0x0341, 0x0341, 0x0341 }, +{ 0x0342, 0x0342, 0x0342 }, +{ 0x0343, 0x0343, 0x0343 }, +{ 0x0344, 0x0344, 0x0344 }, +{ 0x0345, 0x0399, 0x0345 }, +{ 0x0346, 0x0346, 0x0346 }, +{ 0x0347, 0x0347, 0x0347 }, +{ 0x0348, 0x0348, 0x0348 }, +{ 0x0349, 0x0349, 0x0349 }, +{ 0x034A, 0x034A, 0x034A }, +{ 0x034B, 0x034B, 0x034B }, +{ 0x034C, 0x034C, 0x034C }, +{ 0x034D, 0x034D, 0x034D }, +{ 0x034E, 0x034E, 0x034E }, +{ 0x034F, 0x034F, 0x034F }, +{ 0x0350, 0x0350, 0x0350 }, +{ 0x0351, 0x0351, 0x0351 }, +{ 0x0352, 0x0352, 0x0352 }, +{ 0x0353, 0x0353, 0x0353 }, +{ 0x0354, 0x0354, 0x0354 }, +{ 0x0355, 0x0355, 0x0355 }, +{ 0x0356, 0x0356, 0x0356 }, +{ 0x0357, 0x0357, 0x0357 }, +{ 0x0358, 0x0358, 0x0358 }, +{ 0x0359, 0x0359, 0x0359 }, +{ 0x035A, 0x035A, 0x035A }, +{ 0x035B, 0x035B, 0x035B }, +{ 0x035C, 0x035C, 0x035C }, +{ 0x035D, 0x035D, 0x035D }, +{ 0x035E, 0x035E, 0x035E }, +{ 0x035F, 0x035F, 0x035F }, +{ 0x0360, 0x0360, 0x0360 }, +{ 0x0361, 0x0361, 0x0361 }, +{ 0x0362, 0x0362, 0x0362 }, +{ 0x0363, 0x0363, 0x0363 }, +{ 0x0364, 0x0364, 0x0364 }, +{ 0x0365, 0x0365, 0x0365 }, +{ 0x0366, 0x0366, 0x0366 }, +{ 0x0367, 0x0367, 0x0367 }, +{ 0x0368, 0x0368, 0x0368 }, +{ 0x0369, 0x0369, 0x0369 }, +{ 0x036A, 0x036A, 0x036A }, +{ 0x036B, 0x036B, 0x036B }, +{ 0x036C, 0x036C, 0x036C }, +{ 0x036D, 0x036D, 0x036D }, +{ 0x036E, 0x036E, 0x036E }, +{ 0x036F, 0x036F, 0x036F }, +{ 0x037A, 0x037A, 0x037A }, +{ 0x0386, 0x0386, 0x03AC }, +{ 0x0388, 0x0388, 0x03AD }, +{ 0x0389, 0x0389, 0x03AE }, +{ 0x038A, 0x038A, 0x03AF }, +{ 0x038C, 0x038C, 0x03CC }, +{ 0x038E, 0x038E, 0x03CD }, +{ 0x038F, 0x038F, 0x03CE }, +{ 0x0390, 0x0390, 0x0390 }, +{ 0x0391, 0x0391, 0x03B1 }, +{ 0x0392, 0x0392, 0x03B2 }, +{ 0x0393, 0x0393, 0x03B3 }, +{ 0x0394, 0x0394, 0x03B4 }, +{ 0x0395, 0x0395, 0x03B5 }, +{ 0x0396, 0x0396, 0x03B6 }, +{ 0x0397, 0x0397, 0x03B7 }, +{ 0x0398, 0x0398, 0x03B8 }, +{ 0x0399, 0x0399, 0x03B9 }, +{ 0x039A, 0x039A, 0x03BA }, +{ 0x039B, 0x039B, 0x03BB }, +{ 0x039C, 0x039C, 0x03BC }, +{ 0x039D, 0x039D, 0x03BD }, +{ 0x039E, 0x039E, 0x03BE }, +{ 0x039F, 0x039F, 0x03BF }, +{ 0x03A0, 0x03A0, 0x03C0 }, +{ 0x03A1, 0x03A1, 0x03C1 }, +{ 0x03A3, 0x03A3, 0x03C3 }, +{ 0x03A4, 0x03A4, 0x03C4 }, +{ 0x03A5, 0x03A5, 0x03C5 }, +{ 0x03A6, 0x03A6, 0x03C6 }, +{ 0x03A7, 0x03A7, 0x03C7 }, +{ 0x03A8, 0x03A8, 0x03C8 }, +{ 0x03A9, 0x03A9, 0x03C9 }, +{ 0x03AA, 0x03AA, 0x03CA }, +{ 0x03AB, 0x03AB, 0x03CB }, +{ 0x03AC, 0x0386, 0x03AC }, +{ 0x03AD, 0x0388, 0x03AD }, +{ 0x03AE, 0x0389, 0x03AE }, +{ 0x03AF, 0x038A, 0x03AF }, +{ 0x03B0, 0x03B0, 0x03B0 }, +{ 0x03B1, 0x0391, 0x03B1 }, +{ 0x03B2, 0x0392, 0x03B2 }, +{ 0x03B3, 0x0393, 0x03B3 }, +{ 0x03B4, 0x0394, 0x03B4 }, +{ 0x03B5, 0x0395, 0x03B5 }, +{ 0x03B6, 0x0396, 0x03B6 }, +{ 0x03B7, 0x0397, 0x03B7 }, +{ 0x03B8, 0x0398, 0x03B8 }, +{ 0x03B9, 0x0399, 0x03B9 }, +{ 0x03BA, 0x039A, 0x03BA }, +{ 0x03BB, 0x039B, 0x03BB }, +{ 0x03BC, 0x039C, 0x03BC }, +{ 0x03BD, 0x039D, 0x03BD }, +{ 0x03BE, 0x039E, 0x03BE }, +{ 0x03BF, 0x039F, 0x03BF }, +{ 0x03C0, 0x03A0, 0x03C0 }, +{ 0x03C1, 0x03A1, 0x03C1 }, +{ 0x03C2, 0x03A3, 0x03C2 }, +{ 0x03C3, 0x03A3, 0x03C3 }, +{ 0x03C4, 0x03A4, 0x03C4 }, +{ 0x03C5, 0x03A5, 0x03C5 }, +{ 0x03C6, 0x03A6, 0x03C6 }, +{ 0x03C7, 0x03A7, 0x03C7 }, +{ 0x03C8, 0x03A8, 0x03C8 }, +{ 0x03C9, 0x03A9, 0x03C9 }, +{ 0x03CA, 0x03AA, 0x03CA }, +{ 0x03CB, 0x03AB, 0x03CB }, +{ 0x03CC, 0x038C, 0x03CC }, +{ 0x03CD, 0x038E, 0x03CD }, +{ 0x03CE, 0x038F, 0x03CE }, +{ 0x03D0, 0x0392, 0x03D0 }, +{ 0x03D1, 0x0398, 0x03D1 }, +{ 0x03D2, 0x03D2, 0x03D2 }, +{ 0x03D3, 0x03D3, 0x03D3 }, +{ 0x03D4, 0x03D4, 0x03D4 }, +{ 0x03D5, 0x03A6, 0x03D5 }, +{ 0x03D6, 0x03A0, 0x03D6 }, +{ 0x03D7, 0x03D7, 0x03D7 }, +{ 0x03D8, 0x03D8, 0x03D9 }, +{ 0x03D9, 0x03D8, 0x03D9 }, +{ 0x03DA, 0x03DA, 0x03DB }, +{ 0x03DB, 0x03DA, 0x03DB }, +{ 0x03DC, 0x03DC, 0x03DD }, +{ 0x03DD, 0x03DC, 0x03DD }, +{ 0x03DE, 0x03DE, 0x03DF }, +{ 0x03DF, 0x03DE, 0x03DF }, +{ 0x03E0, 0x03E0, 0x03E1 }, +{ 0x03E1, 0x03E0, 0x03E1 }, +{ 0x03E2, 0x03E2, 0x03E3 }, +{ 0x03E3, 0x03E2, 0x03E3 }, +{ 0x03E4, 0x03E4, 0x03E5 }, +{ 0x03E5, 0x03E4, 0x03E5 }, +{ 0x03E6, 0x03E6, 0x03E7 }, +{ 0x03E7, 0x03E6, 0x03E7 }, +{ 0x03E8, 0x03E8, 0x03E9 }, +{ 0x03E9, 0x03E8, 0x03E9 }, +{ 0x03EA, 0x03EA, 0x03EB }, +{ 0x03EB, 0x03EA, 0x03EB }, +{ 0x03EC, 0x03EC, 0x03ED }, +{ 0x03ED, 0x03EC, 0x03ED }, +{ 0x03EE, 0x03EE, 0x03EF }, +{ 0x03EF, 0x03EE, 0x03EF }, +{ 0x03F0, 0x039A, 0x03F0 }, +{ 0x03F1, 0x03A1, 0x03F1 }, +{ 0x03F2, 0x03F9, 0x03F2 }, +{ 0x03F3, 0x03F3, 0x03F3 }, +{ 0x03F4, 0x03F4, 0x03B8 }, +{ 0x03F5, 0x0395, 0x03F5 }, +{ 0x03F7, 0x03F7, 0x03F8 }, +{ 0x03F8, 0x03F7, 0x03F8 }, +{ 0x03F9, 0x03F9, 0x03F2 }, +{ 0x03FA, 0x03FA, 0x03FB }, +{ 0x03FB, 0x03FA, 0x03FB }, +{ 0x03FC, 0x03FC, 0x03FC }, +{ 0x03FD, 0x03FD, 0x03FD }, +{ 0x03FE, 0x03FE, 0x03FE }, +{ 0x03FF, 0x03FF, 0x03FF }, +{ 0x0400, 0x0400, 0x0450 }, +{ 0x0401, 0x0401, 0x0451 }, +{ 0x0402, 0x0402, 0x0452 }, +{ 0x0403, 0x0403, 0x0453 }, +{ 0x0404, 0x0404, 0x0454 }, +{ 0x0405, 0x0405, 0x0455 }, +{ 0x0406, 0x0406, 0x0456 }, +{ 0x0407, 0x0407, 0x0457 }, +{ 0x0408, 0x0408, 0x0458 }, +{ 0x0409, 0x0409, 0x0459 }, +{ 0x040A, 0x040A, 0x045A }, +{ 0x040B, 0x040B, 0x045B }, +{ 0x040C, 0x040C, 0x045C }, +{ 0x040D, 0x040D, 0x045D }, +{ 0x040E, 0x040E, 0x045E }, +{ 0x040F, 0x040F, 0x045F }, +{ 0x0410, 0x0410, 0x0430 }, +{ 0x0411, 0x0411, 0x0431 }, +{ 0x0412, 0x0412, 0x0432 }, +{ 0x0413, 0x0413, 0x0433 }, +{ 0x0414, 0x0414, 0x0434 }, +{ 0x0415, 0x0415, 0x0435 }, +{ 0x0416, 0x0416, 0x0436 }, +{ 0x0417, 0x0417, 0x0437 }, +{ 0x0418, 0x0418, 0x0438 }, +{ 0x0419, 0x0419, 0x0439 }, +{ 0x041A, 0x041A, 0x043A }, +{ 0x041B, 0x041B, 0x043B }, +{ 0x041C, 0x041C, 0x043C }, +{ 0x041D, 0x041D, 0x043D }, +{ 0x041E, 0x041E, 0x043E }, +{ 0x041F, 0x041F, 0x043F }, +{ 0x0420, 0x0420, 0x0440 }, +{ 0x0421, 0x0421, 0x0441 }, +{ 0x0422, 0x0422, 0x0442 }, +{ 0x0423, 0x0423, 0x0443 }, +{ 0x0424, 0x0424, 0x0444 }, +{ 0x0425, 0x0425, 0x0445 }, +{ 0x0426, 0x0426, 0x0446 }, +{ 0x0427, 0x0427, 0x0447 }, +{ 0x0428, 0x0428, 0x0448 }, +{ 0x0429, 0x0429, 0x0449 }, +{ 0x042A, 0x042A, 0x044A }, +{ 0x042B, 0x042B, 0x044B }, +{ 0x042C, 0x042C, 0x044C }, +{ 0x042D, 0x042D, 0x044D }, +{ 0x042E, 0x042E, 0x044E }, +{ 0x042F, 0x042F, 0x044F }, +{ 0x0430, 0x0410, 0x0430 }, +{ 0x0431, 0x0411, 0x0431 }, +{ 0x0432, 0x0412, 0x0432 }, +{ 0x0433, 0x0413, 0x0433 }, +{ 0x0434, 0x0414, 0x0434 }, +{ 0x0435, 0x0415, 0x0435 }, +{ 0x0436, 0x0416, 0x0436 }, +{ 0x0437, 0x0417, 0x0437 }, +{ 0x0438, 0x0418, 0x0438 }, +{ 0x0439, 0x0419, 0x0439 }, +{ 0x043A, 0x041A, 0x043A }, +{ 0x043B, 0x041B, 0x043B }, +{ 0x043C, 0x041C, 0x043C }, +{ 0x043D, 0x041D, 0x043D }, +{ 0x043E, 0x041E, 0x043E }, +{ 0x043F, 0x041F, 0x043F }, +{ 0x0440, 0x0420, 0x0440 }, +{ 0x0441, 0x0421, 0x0441 }, +{ 0x0442, 0x0422, 0x0442 }, +{ 0x0443, 0x0423, 0x0443 }, +{ 0x0444, 0x0424, 0x0444 }, +{ 0x0445, 0x0425, 0x0445 }, +{ 0x0446, 0x0426, 0x0446 }, +{ 0x0447, 0x0427, 0x0447 }, +{ 0x0448, 0x0428, 0x0448 }, +{ 0x0449, 0x0429, 0x0449 }, +{ 0x044A, 0x042A, 0x044A }, +{ 0x044B, 0x042B, 0x044B }, +{ 0x044C, 0x042C, 0x044C }, +{ 0x044D, 0x042D, 0x044D }, +{ 0x044E, 0x042E, 0x044E }, +{ 0x044F, 0x042F, 0x044F }, +{ 0x0450, 0x0400, 0x0450 }, +{ 0x0451, 0x0401, 0x0451 }, +{ 0x0452, 0x0402, 0x0452 }, +{ 0x0453, 0x0403, 0x0453 }, +{ 0x0454, 0x0404, 0x0454 }, +{ 0x0455, 0x0405, 0x0455 }, +{ 0x0456, 0x0406, 0x0456 }, +{ 0x0457, 0x0407, 0x0457 }, +{ 0x0458, 0x0408, 0x0458 }, +{ 0x0459, 0x0409, 0x0459 }, +{ 0x045A, 0x040A, 0x045A }, +{ 0x045B, 0x040B, 0x045B }, +{ 0x045C, 0x040C, 0x045C }, +{ 0x045D, 0x040D, 0x045D }, +{ 0x045E, 0x040E, 0x045E }, +{ 0x045F, 0x040F, 0x045F }, +{ 0x0460, 0x0460, 0x0461 }, +{ 0x0461, 0x0460, 0x0461 }, +{ 0x0462, 0x0462, 0x0463 }, +{ 0x0463, 0x0462, 0x0463 }, +{ 0x0464, 0x0464, 0x0465 }, +{ 0x0465, 0x0464, 0x0465 }, +{ 0x0466, 0x0466, 0x0467 }, +{ 0x0467, 0x0466, 0x0467 }, +{ 0x0468, 0x0468, 0x0469 }, +{ 0x0469, 0x0468, 0x0469 }, +{ 0x046A, 0x046A, 0x046B }, +{ 0x046B, 0x046A, 0x046B }, +{ 0x046C, 0x046C, 0x046D }, +{ 0x046D, 0x046C, 0x046D }, +{ 0x046E, 0x046E, 0x046F }, +{ 0x046F, 0x046E, 0x046F }, +{ 0x0470, 0x0470, 0x0471 }, +{ 0x0471, 0x0470, 0x0471 }, +{ 0x0472, 0x0472, 0x0473 }, +{ 0x0473, 0x0472, 0x0473 }, +{ 0x0474, 0x0474, 0x0475 }, +{ 0x0475, 0x0474, 0x0475 }, +{ 0x0476, 0x0476, 0x0477 }, +{ 0x0477, 0x0476, 0x0477 }, +{ 0x0478, 0x0478, 0x0479 }, +{ 0x0479, 0x0478, 0x0479 }, +{ 0x047A, 0x047A, 0x047B }, +{ 0x047B, 0x047A, 0x047B }, +{ 0x047C, 0x047C, 0x047D }, +{ 0x047D, 0x047C, 0x047D }, +{ 0x047E, 0x047E, 0x047F }, +{ 0x047F, 0x047E, 0x047F }, +{ 0x0480, 0x0480, 0x0481 }, +{ 0x0481, 0x0480, 0x0481 }, +{ 0x0483, 0x0483, 0x0483 }, +{ 0x0484, 0x0484, 0x0484 }, +{ 0x0485, 0x0485, 0x0485 }, +{ 0x0486, 0x0486, 0x0486 }, +{ 0x048A, 0x048A, 0x048B }, +{ 0x048B, 0x048A, 0x048B }, +{ 0x048C, 0x048C, 0x048D }, +{ 0x048D, 0x048C, 0x048D }, +{ 0x048E, 0x048E, 0x048F }, +{ 0x048F, 0x048E, 0x048F }, +{ 0x0490, 0x0490, 0x0491 }, +{ 0x0491, 0x0490, 0x0491 }, +{ 0x0492, 0x0492, 0x0493 }, +{ 0x0493, 0x0492, 0x0493 }, +{ 0x0494, 0x0494, 0x0495 }, +{ 0x0495, 0x0494, 0x0495 }, +{ 0x0496, 0x0496, 0x0497 }, +{ 0x0497, 0x0496, 0x0497 }, +{ 0x0498, 0x0498, 0x0499 }, +{ 0x0499, 0x0498, 0x0499 }, +{ 0x049A, 0x049A, 0x049B }, +{ 0x049B, 0x049A, 0x049B }, +{ 0x049C, 0x049C, 0x049D }, +{ 0x049D, 0x049C, 0x049D }, +{ 0x049E, 0x049E, 0x049F }, +{ 0x049F, 0x049E, 0x049F }, +{ 0x04A0, 0x04A0, 0x04A1 }, +{ 0x04A1, 0x04A0, 0x04A1 }, +{ 0x04A2, 0x04A2, 0x04A3 }, +{ 0x04A3, 0x04A2, 0x04A3 }, +{ 0x04A4, 0x04A4, 0x04A5 }, +{ 0x04A5, 0x04A4, 0x04A5 }, +{ 0x04A6, 0x04A6, 0x04A7 }, +{ 0x04A7, 0x04A6, 0x04A7 }, +{ 0x04A8, 0x04A8, 0x04A9 }, +{ 0x04A9, 0x04A8, 0x04A9 }, +{ 0x04AA, 0x04AA, 0x04AB }, +{ 0x04AB, 0x04AA, 0x04AB }, +{ 0x04AC, 0x04AC, 0x04AD }, +{ 0x04AD, 0x04AC, 0x04AD }, +{ 0x04AE, 0x04AE, 0x04AF }, +{ 0x04AF, 0x04AE, 0x04AF }, +{ 0x04B0, 0x04B0, 0x04B1 }, +{ 0x04B1, 0x04B0, 0x04B1 }, +{ 0x04B2, 0x04B2, 0x04B3 }, +{ 0x04B3, 0x04B2, 0x04B3 }, +{ 0x04B4, 0x04B4, 0x04B5 }, +{ 0x04B5, 0x04B4, 0x04B5 }, +{ 0x04B6, 0x04B6, 0x04B7 }, +{ 0x04B7, 0x04B6, 0x04B7 }, +{ 0x04B8, 0x04B8, 0x04B9 }, +{ 0x04B9, 0x04B8, 0x04B9 }, +{ 0x04BA, 0x04BA, 0x04BB }, +{ 0x04BB, 0x04BA, 0x04BB }, +{ 0x04BC, 0x04BC, 0x04BD }, +{ 0x04BD, 0x04BC, 0x04BD }, +{ 0x04BE, 0x04BE, 0x04BF }, +{ 0x04BF, 0x04BE, 0x04BF }, +{ 0x04C0, 0x04C0, 0x04C0 }, +{ 0x04C1, 0x04C1, 0x04C2 }, +{ 0x04C2, 0x04C1, 0x04C2 }, +{ 0x04C3, 0x04C3, 0x04C4 }, +{ 0x04C4, 0x04C3, 0x04C4 }, +{ 0x04C5, 0x04C5, 0x04C6 }, +{ 0x04C6, 0x04C5, 0x04C6 }, +{ 0x04C7, 0x04C7, 0x04C8 }, +{ 0x04C8, 0x04C7, 0x04C8 }, +{ 0x04C9, 0x04C9, 0x04CA }, +{ 0x04CA, 0x04C9, 0x04CA }, +{ 0x04CB, 0x04CB, 0x04CC }, +{ 0x04CC, 0x04CB, 0x04CC }, +{ 0x04CD, 0x04CD, 0x04CE }, +{ 0x04CE, 0x04CD, 0x04CE }, +{ 0x04D0, 0x04D0, 0x04D1 }, +{ 0x04D1, 0x04D0, 0x04D1 }, +{ 0x04D2, 0x04D2, 0x04D3 }, +{ 0x04D3, 0x04D2, 0x04D3 }, +{ 0x04D4, 0x04D4, 0x04D5 }, +{ 0x04D5, 0x04D4, 0x04D5 }, +{ 0x04D6, 0x04D6, 0x04D7 }, +{ 0x04D7, 0x04D6, 0x04D7 }, +{ 0x04D8, 0x04D8, 0x04D9 }, +{ 0x04D9, 0x04D8, 0x04D9 }, +{ 0x04DA, 0x04DA, 0x04DB }, +{ 0x04DB, 0x04DA, 0x04DB }, +{ 0x04DC, 0x04DC, 0x04DD }, +{ 0x04DD, 0x04DC, 0x04DD }, +{ 0x04DE, 0x04DE, 0x04DF }, +{ 0x04DF, 0x04DE, 0x04DF }, +{ 0x04E0, 0x04E0, 0x04E1 }, +{ 0x04E1, 0x04E0, 0x04E1 }, +{ 0x04E2, 0x04E2, 0x04E3 }, +{ 0x04E3, 0x04E2, 0x04E3 }, +{ 0x04E4, 0x04E4, 0x04E5 }, +{ 0x04E5, 0x04E4, 0x04E5 }, +{ 0x04E6, 0x04E6, 0x04E7 }, +{ 0x04E7, 0x04E6, 0x04E7 }, +{ 0x04E8, 0x04E8, 0x04E9 }, +{ 0x04E9, 0x04E8, 0x04E9 }, +{ 0x04EA, 0x04EA, 0x04EB }, +{ 0x04EB, 0x04EA, 0x04EB }, +{ 0x04EC, 0x04EC, 0x04ED }, +{ 0x04ED, 0x04EC, 0x04ED }, +{ 0x04EE, 0x04EE, 0x04EF }, +{ 0x04EF, 0x04EE, 0x04EF }, +{ 0x04F0, 0x04F0, 0x04F1 }, +{ 0x04F1, 0x04F0, 0x04F1 }, +{ 0x04F2, 0x04F2, 0x04F3 }, +{ 0x04F3, 0x04F2, 0x04F3 }, +{ 0x04F4, 0x04F4, 0x04F5 }, +{ 0x04F5, 0x04F4, 0x04F5 }, +{ 0x04F6, 0x04F6, 0x04F7 }, +{ 0x04F7, 0x04F6, 0x04F7 }, +{ 0x04F8, 0x04F8, 0x04F9 }, +{ 0x04F9, 0x04F8, 0x04F9 }, +{ 0x0500, 0x0500, 0x0501 }, +{ 0x0501, 0x0500, 0x0501 }, +{ 0x0502, 0x0502, 0x0503 }, +{ 0x0503, 0x0502, 0x0503 }, +{ 0x0504, 0x0504, 0x0505 }, +{ 0x0505, 0x0504, 0x0505 }, +{ 0x0506, 0x0506, 0x0507 }, +{ 0x0507, 0x0506, 0x0507 }, +{ 0x0508, 0x0508, 0x0509 }, +{ 0x0509, 0x0508, 0x0509 }, +{ 0x050A, 0x050A, 0x050B }, +{ 0x050B, 0x050A, 0x050B }, +{ 0x050C, 0x050C, 0x050D }, +{ 0x050D, 0x050C, 0x050D }, +{ 0x050E, 0x050E, 0x050F }, +{ 0x050F, 0x050E, 0x050F }, +{ 0x0531, 0x0531, 0x0561 }, +{ 0x0532, 0x0532, 0x0562 }, +{ 0x0533, 0x0533, 0x0563 }, +{ 0x0534, 0x0534, 0x0564 }, +{ 0x0535, 0x0535, 0x0565 }, +{ 0x0536, 0x0536, 0x0566 }, +{ 0x0537, 0x0537, 0x0567 }, +{ 0x0538, 0x0538, 0x0568 }, +{ 0x0539, 0x0539, 0x0569 }, +{ 0x053A, 0x053A, 0x056A }, +{ 0x053B, 0x053B, 0x056B }, +{ 0x053C, 0x053C, 0x056C }, +{ 0x053D, 0x053D, 0x056D }, +{ 0x053E, 0x053E, 0x056E }, +{ 0x053F, 0x053F, 0x056F }, +{ 0x0540, 0x0540, 0x0570 }, +{ 0x0541, 0x0541, 0x0571 }, +{ 0x0542, 0x0542, 0x0572 }, +{ 0x0543, 0x0543, 0x0573 }, +{ 0x0544, 0x0544, 0x0574 }, +{ 0x0545, 0x0545, 0x0575 }, +{ 0x0546, 0x0546, 0x0576 }, +{ 0x0547, 0x0547, 0x0577 }, +{ 0x0548, 0x0548, 0x0578 }, +{ 0x0549, 0x0549, 0x0579 }, +{ 0x054A, 0x054A, 0x057A }, +{ 0x054B, 0x054B, 0x057B }, +{ 0x054C, 0x054C, 0x057C }, +{ 0x054D, 0x054D, 0x057D }, +{ 0x054E, 0x054E, 0x057E }, +{ 0x054F, 0x054F, 0x057F }, +{ 0x0550, 0x0550, 0x0580 }, +{ 0x0551, 0x0551, 0x0581 }, +{ 0x0552, 0x0552, 0x0582 }, +{ 0x0553, 0x0553, 0x0583 }, +{ 0x0554, 0x0554, 0x0584 }, +{ 0x0555, 0x0555, 0x0585 }, +{ 0x0556, 0x0556, 0x0586 }, +{ 0x0559, 0x0559, 0x0559 }, +{ 0x0561, 0x0531, 0x0561 }, +{ 0x0562, 0x0532, 0x0562 }, +{ 0x0563, 0x0533, 0x0563 }, +{ 0x0564, 0x0534, 0x0564 }, +{ 0x0565, 0x0535, 0x0565 }, +{ 0x0566, 0x0536, 0x0566 }, +{ 0x0567, 0x0537, 0x0567 }, +{ 0x0568, 0x0538, 0x0568 }, +{ 0x0569, 0x0539, 0x0569 }, +{ 0x056A, 0x053A, 0x056A }, +{ 0x056B, 0x053B, 0x056B }, +{ 0x056C, 0x053C, 0x056C }, +{ 0x056D, 0x053D, 0x056D }, +{ 0x056E, 0x053E, 0x056E }, +{ 0x056F, 0x053F, 0x056F }, +{ 0x0570, 0x0540, 0x0570 }, +{ 0x0571, 0x0541, 0x0571 }, +{ 0x0572, 0x0542, 0x0572 }, +{ 0x0573, 0x0543, 0x0573 }, +{ 0x0574, 0x0544, 0x0574 }, +{ 0x0575, 0x0545, 0x0575 }, +{ 0x0576, 0x0546, 0x0576 }, +{ 0x0577, 0x0547, 0x0577 }, +{ 0x0578, 0x0548, 0x0578 }, +{ 0x0579, 0x0549, 0x0579 }, +{ 0x057A, 0x054A, 0x057A }, +{ 0x057B, 0x054B, 0x057B }, +{ 0x057C, 0x054C, 0x057C }, +{ 0x057D, 0x054D, 0x057D }, +{ 0x057E, 0x054E, 0x057E }, +{ 0x057F, 0x054F, 0x057F }, +{ 0x0580, 0x0550, 0x0580 }, +{ 0x0581, 0x0551, 0x0581 }, +{ 0x0582, 0x0552, 0x0582 }, +{ 0x0583, 0x0553, 0x0583 }, +{ 0x0584, 0x0554, 0x0584 }, +{ 0x0585, 0x0555, 0x0585 }, +{ 0x0586, 0x0556, 0x0586 }, +{ 0x0587, 0x0587, 0x0587 }, +{ 0x0591, 0x0591, 0x0591 }, +{ 0x0592, 0x0592, 0x0592 }, +{ 0x0593, 0x0593, 0x0593 }, +{ 0x0594, 0x0594, 0x0594 }, +{ 0x0595, 0x0595, 0x0595 }, +{ 0x0596, 0x0596, 0x0596 }, +{ 0x0597, 0x0597, 0x0597 }, +{ 0x0598, 0x0598, 0x0598 }, +{ 0x0599, 0x0599, 0x0599 }, +{ 0x059A, 0x059A, 0x059A }, +{ 0x059B, 0x059B, 0x059B }, +{ 0x059C, 0x059C, 0x059C }, +{ 0x059D, 0x059D, 0x059D }, +{ 0x059E, 0x059E, 0x059E }, +{ 0x059F, 0x059F, 0x059F }, +{ 0x05A0, 0x05A0, 0x05A0 }, +{ 0x05A1, 0x05A1, 0x05A1 }, +{ 0x05A2, 0x05A2, 0x05A2 }, +{ 0x05A3, 0x05A3, 0x05A3 }, +{ 0x05A4, 0x05A4, 0x05A4 }, +{ 0x05A5, 0x05A5, 0x05A5 }, +{ 0x05A6, 0x05A6, 0x05A6 }, +{ 0x05A7, 0x05A7, 0x05A7 }, +{ 0x05A8, 0x05A8, 0x05A8 }, +{ 0x05A9, 0x05A9, 0x05A9 }, +{ 0x05AA, 0x05AA, 0x05AA }, +{ 0x05AB, 0x05AB, 0x05AB }, +{ 0x05AC, 0x05AC, 0x05AC }, +{ 0x05AD, 0x05AD, 0x05AD }, +{ 0x05AE, 0x05AE, 0x05AE }, +{ 0x05AF, 0x05AF, 0x05AF }, +{ 0x05B0, 0x05B0, 0x05B0 }, +{ 0x05B1, 0x05B1, 0x05B1 }, +{ 0x05B2, 0x05B2, 0x05B2 }, +{ 0x05B3, 0x05B3, 0x05B3 }, +{ 0x05B4, 0x05B4, 0x05B4 }, +{ 0x05B5, 0x05B5, 0x05B5 }, +{ 0x05B6, 0x05B6, 0x05B6 }, +{ 0x05B7, 0x05B7, 0x05B7 }, +{ 0x05B8, 0x05B8, 0x05B8 }, +{ 0x05B9, 0x05B9, 0x05B9 }, +{ 0x05BB, 0x05BB, 0x05BB }, +{ 0x05BC, 0x05BC, 0x05BC }, +{ 0x05BD, 0x05BD, 0x05BD }, +{ 0x05BF, 0x05BF, 0x05BF }, +{ 0x05C1, 0x05C1, 0x05C1 }, +{ 0x05C2, 0x05C2, 0x05C2 }, +{ 0x05C4, 0x05C4, 0x05C4 }, +{ 0x05C5, 0x05C5, 0x05C5 }, +{ 0x05C7, 0x05C7, 0x05C7 }, +{ 0x05D0, 0x05D0, 0x05D0 }, +{ 0x05D1, 0x05D1, 0x05D1 }, +{ 0x05D2, 0x05D2, 0x05D2 }, +{ 0x05D3, 0x05D3, 0x05D3 }, +{ 0x05D4, 0x05D4, 0x05D4 }, +{ 0x05D5, 0x05D5, 0x05D5 }, +{ 0x05D6, 0x05D6, 0x05D6 }, +{ 0x05D7, 0x05D7, 0x05D7 }, +{ 0x05D8, 0x05D8, 0x05D8 }, +{ 0x05D9, 0x05D9, 0x05D9 }, +{ 0x05DA, 0x05DA, 0x05DA }, +{ 0x05DB, 0x05DB, 0x05DB }, +{ 0x05DC, 0x05DC, 0x05DC }, +{ 0x05DD, 0x05DD, 0x05DD }, +{ 0x05DE, 0x05DE, 0x05DE }, +{ 0x05DF, 0x05DF, 0x05DF }, +{ 0x05E0, 0x05E0, 0x05E0 }, +{ 0x05E1, 0x05E1, 0x05E1 }, +{ 0x05E2, 0x05E2, 0x05E2 }, +{ 0x05E3, 0x05E3, 0x05E3 }, +{ 0x05E4, 0x05E4, 0x05E4 }, +{ 0x05E5, 0x05E5, 0x05E5 }, +{ 0x05E6, 0x05E6, 0x05E6 }, +{ 0x05E7, 0x05E7, 0x05E7 }, +{ 0x05E8, 0x05E8, 0x05E8 }, +{ 0x05E9, 0x05E9, 0x05E9 }, +{ 0x05EA, 0x05EA, 0x05EA }, +{ 0x05F0, 0x05F0, 0x05F0 }, +{ 0x05F1, 0x05F1, 0x05F1 }, +{ 0x05F2, 0x05F2, 0x05F2 }, +{ 0x0610, 0x0610, 0x0610 }, +{ 0x0611, 0x0611, 0x0611 }, +{ 0x0612, 0x0612, 0x0612 }, +{ 0x0613, 0x0613, 0x0613 }, +{ 0x0614, 0x0614, 0x0614 }, +{ 0x0615, 0x0615, 0x0615 }, +{ 0x0621, 0x0621, 0x0621 }, +{ 0x0622, 0x0622, 0x0622 }, +{ 0x0623, 0x0623, 0x0623 }, +{ 0x0624, 0x0624, 0x0624 }, +{ 0x0625, 0x0625, 0x0625 }, +{ 0x0626, 0x0626, 0x0626 }, +{ 0x0627, 0x0627, 0x0627 }, +{ 0x0628, 0x0628, 0x0628 }, +{ 0x0629, 0x0629, 0x0629 }, +{ 0x062A, 0x062A, 0x062A }, +{ 0x062B, 0x062B, 0x062B }, +{ 0x062C, 0x062C, 0x062C }, +{ 0x062D, 0x062D, 0x062D }, +{ 0x062E, 0x062E, 0x062E }, +{ 0x062F, 0x062F, 0x062F }, +{ 0x0630, 0x0630, 0x0630 }, +{ 0x0631, 0x0631, 0x0631 }, +{ 0x0632, 0x0632, 0x0632 }, +{ 0x0633, 0x0633, 0x0633 }, +{ 0x0634, 0x0634, 0x0634 }, +{ 0x0635, 0x0635, 0x0635 }, +{ 0x0636, 0x0636, 0x0636 }, +{ 0x0637, 0x0637, 0x0637 }, +{ 0x0638, 0x0638, 0x0638 }, +{ 0x0639, 0x0639, 0x0639 }, +{ 0x063A, 0x063A, 0x063A }, +{ 0x0640, 0x0640, 0x0640 }, +{ 0x0641, 0x0641, 0x0641 }, +{ 0x0642, 0x0642, 0x0642 }, +{ 0x0643, 0x0643, 0x0643 }, +{ 0x0644, 0x0644, 0x0644 }, +{ 0x0645, 0x0645, 0x0645 }, +{ 0x0646, 0x0646, 0x0646 }, +{ 0x0647, 0x0647, 0x0647 }, +{ 0x0648, 0x0648, 0x0648 }, +{ 0x0649, 0x0649, 0x0649 }, +{ 0x064A, 0x064A, 0x064A }, +{ 0x064B, 0x064B, 0x064B }, +{ 0x064C, 0x064C, 0x064C }, +{ 0x064D, 0x064D, 0x064D }, +{ 0x064E, 0x064E, 0x064E }, +{ 0x064F, 0x064F, 0x064F }, +{ 0x0650, 0x0650, 0x0650 }, +{ 0x0651, 0x0651, 0x0651 }, +{ 0x0652, 0x0652, 0x0652 }, +{ 0x0653, 0x0653, 0x0653 }, +{ 0x0654, 0x0654, 0x0654 }, +{ 0x0655, 0x0655, 0x0655 }, +{ 0x0656, 0x0656, 0x0656 }, +{ 0x0657, 0x0657, 0x0657 }, +{ 0x0658, 0x0658, 0x0658 }, +{ 0x0659, 0x0659, 0x0659 }, +{ 0x065A, 0x065A, 0x065A }, +{ 0x065B, 0x065B, 0x065B }, +{ 0x065C, 0x065C, 0x065C }, +{ 0x065D, 0x065D, 0x065D }, +{ 0x065E, 0x065E, 0x065E }, +{ 0x066E, 0x066E, 0x066E }, +{ 0x066F, 0x066F, 0x066F }, +{ 0x0670, 0x0670, 0x0670 }, +{ 0x0671, 0x0671, 0x0671 }, +{ 0x0672, 0x0672, 0x0672 }, +{ 0x0673, 0x0673, 0x0673 }, +{ 0x0674, 0x0674, 0x0674 }, +{ 0x0675, 0x0675, 0x0675 }, +{ 0x0676, 0x0676, 0x0676 }, +{ 0x0677, 0x0677, 0x0677 }, +{ 0x0678, 0x0678, 0x0678 }, +{ 0x0679, 0x0679, 0x0679 }, +{ 0x067A, 0x067A, 0x067A }, +{ 0x067B, 0x067B, 0x067B }, +{ 0x067C, 0x067C, 0x067C }, +{ 0x067D, 0x067D, 0x067D }, +{ 0x067E, 0x067E, 0x067E }, +{ 0x067F, 0x067F, 0x067F }, +{ 0x0680, 0x0680, 0x0680 }, +{ 0x0681, 0x0681, 0x0681 }, +{ 0x0682, 0x0682, 0x0682 }, +{ 0x0683, 0x0683, 0x0683 }, +{ 0x0684, 0x0684, 0x0684 }, +{ 0x0685, 0x0685, 0x0685 }, +{ 0x0686, 0x0686, 0x0686 }, +{ 0x0687, 0x0687, 0x0687 }, +{ 0x0688, 0x0688, 0x0688 }, +{ 0x0689, 0x0689, 0x0689 }, +{ 0x068A, 0x068A, 0x068A }, +{ 0x068B, 0x068B, 0x068B }, +{ 0x068C, 0x068C, 0x068C }, +{ 0x068D, 0x068D, 0x068D }, +{ 0x068E, 0x068E, 0x068E }, +{ 0x068F, 0x068F, 0x068F }, +{ 0x0690, 0x0690, 0x0690 }, +{ 0x0691, 0x0691, 0x0691 }, +{ 0x0692, 0x0692, 0x0692 }, +{ 0x0693, 0x0693, 0x0693 }, +{ 0x0694, 0x0694, 0x0694 }, +{ 0x0695, 0x0695, 0x0695 }, +{ 0x0696, 0x0696, 0x0696 }, +{ 0x0697, 0x0697, 0x0697 }, +{ 0x0698, 0x0698, 0x0698 }, +{ 0x0699, 0x0699, 0x0699 }, +{ 0x069A, 0x069A, 0x069A }, +{ 0x069B, 0x069B, 0x069B }, +{ 0x069C, 0x069C, 0x069C }, +{ 0x069D, 0x069D, 0x069D }, +{ 0x069E, 0x069E, 0x069E }, +{ 0x069F, 0x069F, 0x069F }, +{ 0x06A0, 0x06A0, 0x06A0 }, +{ 0x06A1, 0x06A1, 0x06A1 }, +{ 0x06A2, 0x06A2, 0x06A2 }, +{ 0x06A3, 0x06A3, 0x06A3 }, +{ 0x06A4, 0x06A4, 0x06A4 }, +{ 0x06A5, 0x06A5, 0x06A5 }, +{ 0x06A6, 0x06A6, 0x06A6 }, +{ 0x06A7, 0x06A7, 0x06A7 }, +{ 0x06A8, 0x06A8, 0x06A8 }, +{ 0x06A9, 0x06A9, 0x06A9 }, +{ 0x06AA, 0x06AA, 0x06AA }, +{ 0x06AB, 0x06AB, 0x06AB }, +{ 0x06AC, 0x06AC, 0x06AC }, +{ 0x06AD, 0x06AD, 0x06AD }, +{ 0x06AE, 0x06AE, 0x06AE }, +{ 0x06AF, 0x06AF, 0x06AF }, +{ 0x06B0, 0x06B0, 0x06B0 }, +{ 0x06B1, 0x06B1, 0x06B1 }, +{ 0x06B2, 0x06B2, 0x06B2 }, +{ 0x06B3, 0x06B3, 0x06B3 }, +{ 0x06B4, 0x06B4, 0x06B4 }, +{ 0x06B5, 0x06B5, 0x06B5 }, +{ 0x06B6, 0x06B6, 0x06B6 }, +{ 0x06B7, 0x06B7, 0x06B7 }, +{ 0x06B8, 0x06B8, 0x06B8 }, +{ 0x06B9, 0x06B9, 0x06B9 }, +{ 0x06BA, 0x06BA, 0x06BA }, +{ 0x06BB, 0x06BB, 0x06BB }, +{ 0x06BC, 0x06BC, 0x06BC }, +{ 0x06BD, 0x06BD, 0x06BD }, +{ 0x06BE, 0x06BE, 0x06BE }, +{ 0x06BF, 0x06BF, 0x06BF }, +{ 0x06C0, 0x06C0, 0x06C0 }, +{ 0x06C1, 0x06C1, 0x06C1 }, +{ 0x06C2, 0x06C2, 0x06C2 }, +{ 0x06C3, 0x06C3, 0x06C3 }, +{ 0x06C4, 0x06C4, 0x06C4 }, +{ 0x06C5, 0x06C5, 0x06C5 }, +{ 0x06C6, 0x06C6, 0x06C6 }, +{ 0x06C7, 0x06C7, 0x06C7 }, +{ 0x06C8, 0x06C8, 0x06C8 }, +{ 0x06C9, 0x06C9, 0x06C9 }, +{ 0x06CA, 0x06CA, 0x06CA }, +{ 0x06CB, 0x06CB, 0x06CB }, +{ 0x06CC, 0x06CC, 0x06CC }, +{ 0x06CD, 0x06CD, 0x06CD }, +{ 0x06CE, 0x06CE, 0x06CE }, +{ 0x06CF, 0x06CF, 0x06CF }, +{ 0x06D0, 0x06D0, 0x06D0 }, +{ 0x06D1, 0x06D1, 0x06D1 }, +{ 0x06D2, 0x06D2, 0x06D2 }, +{ 0x06D3, 0x06D3, 0x06D3 }, +{ 0x06D5, 0x06D5, 0x06D5 }, +{ 0x06D6, 0x06D6, 0x06D6 }, +{ 0x06D7, 0x06D7, 0x06D7 }, +{ 0x06D8, 0x06D8, 0x06D8 }, +{ 0x06D9, 0x06D9, 0x06D9 }, +{ 0x06DA, 0x06DA, 0x06DA }, +{ 0x06DB, 0x06DB, 0x06DB }, +{ 0x06DC, 0x06DC, 0x06DC }, +{ 0x06DF, 0x06DF, 0x06DF }, +{ 0x06E0, 0x06E0, 0x06E0 }, +{ 0x06E1, 0x06E1, 0x06E1 }, +{ 0x06E2, 0x06E2, 0x06E2 }, +{ 0x06E3, 0x06E3, 0x06E3 }, +{ 0x06E4, 0x06E4, 0x06E4 }, +{ 0x06E5, 0x06E5, 0x06E5 }, +{ 0x06E6, 0x06E6, 0x06E6 }, +{ 0x06E7, 0x06E7, 0x06E7 }, +{ 0x06E8, 0x06E8, 0x06E8 }, +{ 0x06EA, 0x06EA, 0x06EA }, +{ 0x06EB, 0x06EB, 0x06EB }, +{ 0x06EC, 0x06EC, 0x06EC }, +{ 0x06ED, 0x06ED, 0x06ED }, +{ 0x06EE, 0x06EE, 0x06EE }, +{ 0x06EF, 0x06EF, 0x06EF }, +{ 0x06FA, 0x06FA, 0x06FA }, +{ 0x06FB, 0x06FB, 0x06FB }, +{ 0x06FC, 0x06FC, 0x06FC }, +{ 0x06FF, 0x06FF, 0x06FF }, +{ 0x0710, 0x0710, 0x0710 }, +{ 0x0711, 0x0711, 0x0711 }, +{ 0x0712, 0x0712, 0x0712 }, +{ 0x0713, 0x0713, 0x0713 }, +{ 0x0714, 0x0714, 0x0714 }, +{ 0x0715, 0x0715, 0x0715 }, +{ 0x0716, 0x0716, 0x0716 }, +{ 0x0717, 0x0717, 0x0717 }, +{ 0x0718, 0x0718, 0x0718 }, +{ 0x0719, 0x0719, 0x0719 }, +{ 0x071A, 0x071A, 0x071A }, +{ 0x071B, 0x071B, 0x071B }, +{ 0x071C, 0x071C, 0x071C }, +{ 0x071D, 0x071D, 0x071D }, +{ 0x071E, 0x071E, 0x071E }, +{ 0x071F, 0x071F, 0x071F }, +{ 0x0720, 0x0720, 0x0720 }, +{ 0x0721, 0x0721, 0x0721 }, +{ 0x0722, 0x0722, 0x0722 }, +{ 0x0723, 0x0723, 0x0723 }, +{ 0x0724, 0x0724, 0x0724 }, +{ 0x0725, 0x0725, 0x0725 }, +{ 0x0726, 0x0726, 0x0726 }, +{ 0x0727, 0x0727, 0x0727 }, +{ 0x0728, 0x0728, 0x0728 }, +{ 0x0729, 0x0729, 0x0729 }, +{ 0x072A, 0x072A, 0x072A }, +{ 0x072B, 0x072B, 0x072B }, +{ 0x072C, 0x072C, 0x072C }, +{ 0x072D, 0x072D, 0x072D }, +{ 0x072E, 0x072E, 0x072E }, +{ 0x072F, 0x072F, 0x072F }, +{ 0x0730, 0x0730, 0x0730 }, +{ 0x0731, 0x0731, 0x0731 }, +{ 0x0732, 0x0732, 0x0732 }, +{ 0x0733, 0x0733, 0x0733 }, +{ 0x0734, 0x0734, 0x0734 }, +{ 0x0735, 0x0735, 0x0735 }, +{ 0x0736, 0x0736, 0x0736 }, +{ 0x0737, 0x0737, 0x0737 }, +{ 0x0738, 0x0738, 0x0738 }, +{ 0x0739, 0x0739, 0x0739 }, +{ 0x073A, 0x073A, 0x073A }, +{ 0x073B, 0x073B, 0x073B }, +{ 0x073C, 0x073C, 0x073C }, +{ 0x073D, 0x073D, 0x073D }, +{ 0x073E, 0x073E, 0x073E }, +{ 0x073F, 0x073F, 0x073F }, +{ 0x0740, 0x0740, 0x0740 }, +{ 0x0741, 0x0741, 0x0741 }, +{ 0x0742, 0x0742, 0x0742 }, +{ 0x0743, 0x0743, 0x0743 }, +{ 0x0744, 0x0744, 0x0744 }, +{ 0x0745, 0x0745, 0x0745 }, +{ 0x0746, 0x0746, 0x0746 }, +{ 0x0747, 0x0747, 0x0747 }, +{ 0x0748, 0x0748, 0x0748 }, +{ 0x0749, 0x0749, 0x0749 }, +{ 0x074A, 0x074A, 0x074A }, +{ 0x074D, 0x074D, 0x074D }, +{ 0x074E, 0x074E, 0x074E }, +{ 0x074F, 0x074F, 0x074F }, +{ 0x0750, 0x0750, 0x0750 }, +{ 0x0751, 0x0751, 0x0751 }, +{ 0x0752, 0x0752, 0x0752 }, +{ 0x0753, 0x0753, 0x0753 }, +{ 0x0754, 0x0754, 0x0754 }, +{ 0x0755, 0x0755, 0x0755 }, +{ 0x0756, 0x0756, 0x0756 }, +{ 0x0757, 0x0757, 0x0757 }, +{ 0x0758, 0x0758, 0x0758 }, +{ 0x0759, 0x0759, 0x0759 }, +{ 0x075A, 0x075A, 0x075A }, +{ 0x075B, 0x075B, 0x075B }, +{ 0x075C, 0x075C, 0x075C }, +{ 0x075D, 0x075D, 0x075D }, +{ 0x075E, 0x075E, 0x075E }, +{ 0x075F, 0x075F, 0x075F }, +{ 0x0760, 0x0760, 0x0760 }, +{ 0x0761, 0x0761, 0x0761 }, +{ 0x0762, 0x0762, 0x0762 }, +{ 0x0763, 0x0763, 0x0763 }, +{ 0x0764, 0x0764, 0x0764 }, +{ 0x0765, 0x0765, 0x0765 }, +{ 0x0766, 0x0766, 0x0766 }, +{ 0x0767, 0x0767, 0x0767 }, +{ 0x0768, 0x0768, 0x0768 }, +{ 0x0769, 0x0769, 0x0769 }, +{ 0x076A, 0x076A, 0x076A }, +{ 0x076B, 0x076B, 0x076B }, +{ 0x076C, 0x076C, 0x076C }, +{ 0x076D, 0x076D, 0x076D }, +{ 0x0780, 0x0780, 0x0780 }, +{ 0x0781, 0x0781, 0x0781 }, +{ 0x0782, 0x0782, 0x0782 }, +{ 0x0783, 0x0783, 0x0783 }, +{ 0x0784, 0x0784, 0x0784 }, +{ 0x0785, 0x0785, 0x0785 }, +{ 0x0786, 0x0786, 0x0786 }, +{ 0x0787, 0x0787, 0x0787 }, +{ 0x0788, 0x0788, 0x0788 }, +{ 0x0789, 0x0789, 0x0789 }, +{ 0x078A, 0x078A, 0x078A }, +{ 0x078B, 0x078B, 0x078B }, +{ 0x078C, 0x078C, 0x078C }, +{ 0x078D, 0x078D, 0x078D }, +{ 0x078E, 0x078E, 0x078E }, +{ 0x078F, 0x078F, 0x078F }, +{ 0x0790, 0x0790, 0x0790 }, +{ 0x0791, 0x0791, 0x0791 }, +{ 0x0792, 0x0792, 0x0792 }, +{ 0x0793, 0x0793, 0x0793 }, +{ 0x0794, 0x0794, 0x0794 }, +{ 0x0795, 0x0795, 0x0795 }, +{ 0x0796, 0x0796, 0x0796 }, +{ 0x0797, 0x0797, 0x0797 }, +{ 0x0798, 0x0798, 0x0798 }, +{ 0x0799, 0x0799, 0x0799 }, +{ 0x079A, 0x079A, 0x079A }, +{ 0x079B, 0x079B, 0x079B }, +{ 0x079C, 0x079C, 0x079C }, +{ 0x079D, 0x079D, 0x079D }, +{ 0x079E, 0x079E, 0x079E }, +{ 0x079F, 0x079F, 0x079F }, +{ 0x07A0, 0x07A0, 0x07A0 }, +{ 0x07A1, 0x07A1, 0x07A1 }, +{ 0x07A2, 0x07A2, 0x07A2 }, +{ 0x07A3, 0x07A3, 0x07A3 }, +{ 0x07A4, 0x07A4, 0x07A4 }, +{ 0x07A5, 0x07A5, 0x07A5 }, +{ 0x07A6, 0x07A6, 0x07A6 }, +{ 0x07A7, 0x07A7, 0x07A7 }, +{ 0x07A8, 0x07A8, 0x07A8 }, +{ 0x07A9, 0x07A9, 0x07A9 }, +{ 0x07AA, 0x07AA, 0x07AA }, +{ 0x07AB, 0x07AB, 0x07AB }, +{ 0x07AC, 0x07AC, 0x07AC }, +{ 0x07AD, 0x07AD, 0x07AD }, +{ 0x07AE, 0x07AE, 0x07AE }, +{ 0x07AF, 0x07AF, 0x07AF }, +{ 0x07B0, 0x07B0, 0x07B0 }, +{ 0x07B1, 0x07B1, 0x07B1 }, +{ 0x0901, 0x0901, 0x0901 }, +{ 0x0902, 0x0902, 0x0902 }, +{ 0x0904, 0x0904, 0x0904 }, +{ 0x0905, 0x0905, 0x0905 }, +{ 0x0906, 0x0906, 0x0906 }, +{ 0x0907, 0x0907, 0x0907 }, +{ 0x0908, 0x0908, 0x0908 }, +{ 0x0909, 0x0909, 0x0909 }, +{ 0x090A, 0x090A, 0x090A }, +{ 0x090B, 0x090B, 0x090B }, +{ 0x090C, 0x090C, 0x090C }, +{ 0x090D, 0x090D, 0x090D }, +{ 0x090E, 0x090E, 0x090E }, +{ 0x090F, 0x090F, 0x090F }, +{ 0x0910, 0x0910, 0x0910 }, +{ 0x0911, 0x0911, 0x0911 }, +{ 0x0912, 0x0912, 0x0912 }, +{ 0x0913, 0x0913, 0x0913 }, +{ 0x0914, 0x0914, 0x0914 }, +{ 0x0915, 0x0915, 0x0915 }, +{ 0x0916, 0x0916, 0x0916 }, +{ 0x0917, 0x0917, 0x0917 }, +{ 0x0918, 0x0918, 0x0918 }, +{ 0x0919, 0x0919, 0x0919 }, +{ 0x091A, 0x091A, 0x091A }, +{ 0x091B, 0x091B, 0x091B }, +{ 0x091C, 0x091C, 0x091C }, +{ 0x091D, 0x091D, 0x091D }, +{ 0x091E, 0x091E, 0x091E }, +{ 0x091F, 0x091F, 0x091F }, +{ 0x0920, 0x0920, 0x0920 }, +{ 0x0921, 0x0921, 0x0921 }, +{ 0x0922, 0x0922, 0x0922 }, +{ 0x0923, 0x0923, 0x0923 }, +{ 0x0924, 0x0924, 0x0924 }, +{ 0x0925, 0x0925, 0x0925 }, +{ 0x0926, 0x0926, 0x0926 }, +{ 0x0927, 0x0927, 0x0927 }, +{ 0x0928, 0x0928, 0x0928 }, +{ 0x0929, 0x0929, 0x0929 }, +{ 0x092A, 0x092A, 0x092A }, +{ 0x092B, 0x092B, 0x092B }, +{ 0x092C, 0x092C, 0x092C }, +{ 0x092D, 0x092D, 0x092D }, +{ 0x092E, 0x092E, 0x092E }, +{ 0x092F, 0x092F, 0x092F }, +{ 0x0930, 0x0930, 0x0930 }, +{ 0x0931, 0x0931, 0x0931 }, +{ 0x0932, 0x0932, 0x0932 }, +{ 0x0933, 0x0933, 0x0933 }, +{ 0x0934, 0x0934, 0x0934 }, +{ 0x0935, 0x0935, 0x0935 }, +{ 0x0936, 0x0936, 0x0936 }, +{ 0x0937, 0x0937, 0x0937 }, +{ 0x0938, 0x0938, 0x0938 }, +{ 0x0939, 0x0939, 0x0939 }, +{ 0x093C, 0x093C, 0x093C }, +{ 0x093D, 0x093D, 0x093D }, +{ 0x0941, 0x0941, 0x0941 }, +{ 0x0942, 0x0942, 0x0942 }, +{ 0x0943, 0x0943, 0x0943 }, +{ 0x0944, 0x0944, 0x0944 }, +{ 0x0945, 0x0945, 0x0945 }, +{ 0x0946, 0x0946, 0x0946 }, +{ 0x0947, 0x0947, 0x0947 }, +{ 0x0948, 0x0948, 0x0948 }, +{ 0x094D, 0x094D, 0x094D }, +{ 0x0950, 0x0950, 0x0950 }, +{ 0x0951, 0x0951, 0x0951 }, +{ 0x0952, 0x0952, 0x0952 }, +{ 0x0953, 0x0953, 0x0953 }, +{ 0x0954, 0x0954, 0x0954 }, +{ 0x0958, 0x0958, 0x0958 }, +{ 0x0959, 0x0959, 0x0959 }, +{ 0x095A, 0x095A, 0x095A }, +{ 0x095B, 0x095B, 0x095B }, +{ 0x095C, 0x095C, 0x095C }, +{ 0x095D, 0x095D, 0x095D }, +{ 0x095E, 0x095E, 0x095E }, +{ 0x095F, 0x095F, 0x095F }, +{ 0x0960, 0x0960, 0x0960 }, +{ 0x0961, 0x0961, 0x0961 }, +{ 0x0962, 0x0962, 0x0962 }, +{ 0x0963, 0x0963, 0x0963 }, +{ 0x097D, 0x097D, 0x097D }, +{ 0x0981, 0x0981, 0x0981 }, +{ 0x0985, 0x0985, 0x0985 }, +{ 0x0986, 0x0986, 0x0986 }, +{ 0x0987, 0x0987, 0x0987 }, +{ 0x0988, 0x0988, 0x0988 }, +{ 0x0989, 0x0989, 0x0989 }, +{ 0x098A, 0x098A, 0x098A }, +{ 0x098B, 0x098B, 0x098B }, +{ 0x098C, 0x098C, 0x098C }, +{ 0x098F, 0x098F, 0x098F }, +{ 0x0990, 0x0990, 0x0990 }, +{ 0x0993, 0x0993, 0x0993 }, +{ 0x0994, 0x0994, 0x0994 }, +{ 0x0995, 0x0995, 0x0995 }, +{ 0x0996, 0x0996, 0x0996 }, +{ 0x0997, 0x0997, 0x0997 }, +{ 0x0998, 0x0998, 0x0998 }, +{ 0x0999, 0x0999, 0x0999 }, +{ 0x099A, 0x099A, 0x099A }, +{ 0x099B, 0x099B, 0x099B }, +{ 0x099C, 0x099C, 0x099C }, +{ 0x099D, 0x099D, 0x099D }, +{ 0x099E, 0x099E, 0x099E }, +{ 0x099F, 0x099F, 0x099F }, +{ 0x09A0, 0x09A0, 0x09A0 }, +{ 0x09A1, 0x09A1, 0x09A1 }, +{ 0x09A2, 0x09A2, 0x09A2 }, +{ 0x09A3, 0x09A3, 0x09A3 }, +{ 0x09A4, 0x09A4, 0x09A4 }, +{ 0x09A5, 0x09A5, 0x09A5 }, +{ 0x09A6, 0x09A6, 0x09A6 }, +{ 0x09A7, 0x09A7, 0x09A7 }, +{ 0x09A8, 0x09A8, 0x09A8 }, +{ 0x09AA, 0x09AA, 0x09AA }, +{ 0x09AB, 0x09AB, 0x09AB }, +{ 0x09AC, 0x09AC, 0x09AC }, +{ 0x09AD, 0x09AD, 0x09AD }, +{ 0x09AE, 0x09AE, 0x09AE }, +{ 0x09AF, 0x09AF, 0x09AF }, +{ 0x09B0, 0x09B0, 0x09B0 }, +{ 0x09B2, 0x09B2, 0x09B2 }, +{ 0x09B6, 0x09B6, 0x09B6 }, +{ 0x09B7, 0x09B7, 0x09B7 }, +{ 0x09B8, 0x09B8, 0x09B8 }, +{ 0x09B9, 0x09B9, 0x09B9 }, +{ 0x09BC, 0x09BC, 0x09BC }, +{ 0x09BD, 0x09BD, 0x09BD }, +{ 0x09C1, 0x09C1, 0x09C1 }, +{ 0x09C2, 0x09C2, 0x09C2 }, +{ 0x09C3, 0x09C3, 0x09C3 }, +{ 0x09C4, 0x09C4, 0x09C4 }, +{ 0x09CD, 0x09CD, 0x09CD }, +{ 0x09CE, 0x09CE, 0x09CE }, +{ 0x09DC, 0x09DC, 0x09DC }, +{ 0x09DD, 0x09DD, 0x09DD }, +{ 0x09DF, 0x09DF, 0x09DF }, +{ 0x09E0, 0x09E0, 0x09E0 }, +{ 0x09E1, 0x09E1, 0x09E1 }, +{ 0x09E2, 0x09E2, 0x09E2 }, +{ 0x09E3, 0x09E3, 0x09E3 }, +{ 0x09F0, 0x09F0, 0x09F0 }, +{ 0x09F1, 0x09F1, 0x09F1 }, +{ 0x0A01, 0x0A01, 0x0A01 }, +{ 0x0A02, 0x0A02, 0x0A02 }, +{ 0x0A05, 0x0A05, 0x0A05 }, +{ 0x0A06, 0x0A06, 0x0A06 }, +{ 0x0A07, 0x0A07, 0x0A07 }, +{ 0x0A08, 0x0A08, 0x0A08 }, +{ 0x0A09, 0x0A09, 0x0A09 }, +{ 0x0A0A, 0x0A0A, 0x0A0A }, +{ 0x0A0F, 0x0A0F, 0x0A0F }, +{ 0x0A10, 0x0A10, 0x0A10 }, +{ 0x0A13, 0x0A13, 0x0A13 }, +{ 0x0A14, 0x0A14, 0x0A14 }, +{ 0x0A15, 0x0A15, 0x0A15 }, +{ 0x0A16, 0x0A16, 0x0A16 }, +{ 0x0A17, 0x0A17, 0x0A17 }, +{ 0x0A18, 0x0A18, 0x0A18 }, +{ 0x0A19, 0x0A19, 0x0A19 }, +{ 0x0A1A, 0x0A1A, 0x0A1A }, +{ 0x0A1B, 0x0A1B, 0x0A1B }, +{ 0x0A1C, 0x0A1C, 0x0A1C }, +{ 0x0A1D, 0x0A1D, 0x0A1D }, +{ 0x0A1E, 0x0A1E, 0x0A1E }, +{ 0x0A1F, 0x0A1F, 0x0A1F }, +{ 0x0A20, 0x0A20, 0x0A20 }, +{ 0x0A21, 0x0A21, 0x0A21 }, +{ 0x0A22, 0x0A22, 0x0A22 }, +{ 0x0A23, 0x0A23, 0x0A23 }, +{ 0x0A24, 0x0A24, 0x0A24 }, +{ 0x0A25, 0x0A25, 0x0A25 }, +{ 0x0A26, 0x0A26, 0x0A26 }, +{ 0x0A27, 0x0A27, 0x0A27 }, +{ 0x0A28, 0x0A28, 0x0A28 }, +{ 0x0A2A, 0x0A2A, 0x0A2A }, +{ 0x0A2B, 0x0A2B, 0x0A2B }, +{ 0x0A2C, 0x0A2C, 0x0A2C }, +{ 0x0A2D, 0x0A2D, 0x0A2D }, +{ 0x0A2E, 0x0A2E, 0x0A2E }, +{ 0x0A2F, 0x0A2F, 0x0A2F }, +{ 0x0A30, 0x0A30, 0x0A30 }, +{ 0x0A32, 0x0A32, 0x0A32 }, +{ 0x0A33, 0x0A33, 0x0A33 }, +{ 0x0A35, 0x0A35, 0x0A35 }, +{ 0x0A36, 0x0A36, 0x0A36 }, +{ 0x0A38, 0x0A38, 0x0A38 }, +{ 0x0A39, 0x0A39, 0x0A39 }, +{ 0x0A3C, 0x0A3C, 0x0A3C }, +{ 0x0A41, 0x0A41, 0x0A41 }, +{ 0x0A42, 0x0A42, 0x0A42 }, +{ 0x0A47, 0x0A47, 0x0A47 }, +{ 0x0A48, 0x0A48, 0x0A48 }, +{ 0x0A4B, 0x0A4B, 0x0A4B }, +{ 0x0A4C, 0x0A4C, 0x0A4C }, +{ 0x0A4D, 0x0A4D, 0x0A4D }, +{ 0x0A59, 0x0A59, 0x0A59 }, +{ 0x0A5A, 0x0A5A, 0x0A5A }, +{ 0x0A5B, 0x0A5B, 0x0A5B }, +{ 0x0A5C, 0x0A5C, 0x0A5C }, +{ 0x0A5E, 0x0A5E, 0x0A5E }, +{ 0x0A70, 0x0A70, 0x0A70 }, +{ 0x0A71, 0x0A71, 0x0A71 }, +{ 0x0A72, 0x0A72, 0x0A72 }, +{ 0x0A73, 0x0A73, 0x0A73 }, +{ 0x0A74, 0x0A74, 0x0A74 }, +{ 0x0A81, 0x0A81, 0x0A81 }, +{ 0x0A82, 0x0A82, 0x0A82 }, +{ 0x0A85, 0x0A85, 0x0A85 }, +{ 0x0A86, 0x0A86, 0x0A86 }, +{ 0x0A87, 0x0A87, 0x0A87 }, +{ 0x0A88, 0x0A88, 0x0A88 }, +{ 0x0A89, 0x0A89, 0x0A89 }, +{ 0x0A8A, 0x0A8A, 0x0A8A }, +{ 0x0A8B, 0x0A8B, 0x0A8B }, +{ 0x0A8C, 0x0A8C, 0x0A8C }, +{ 0x0A8D, 0x0A8D, 0x0A8D }, +{ 0x0A8F, 0x0A8F, 0x0A8F }, +{ 0x0A90, 0x0A90, 0x0A90 }, +{ 0x0A91, 0x0A91, 0x0A91 }, +{ 0x0A93, 0x0A93, 0x0A93 }, +{ 0x0A94, 0x0A94, 0x0A94 }, +{ 0x0A95, 0x0A95, 0x0A95 }, +{ 0x0A96, 0x0A96, 0x0A96 }, +{ 0x0A97, 0x0A97, 0x0A97 }, +{ 0x0A98, 0x0A98, 0x0A98 }, +{ 0x0A99, 0x0A99, 0x0A99 }, +{ 0x0A9A, 0x0A9A, 0x0A9A }, +{ 0x0A9B, 0x0A9B, 0x0A9B }, +{ 0x0A9C, 0x0A9C, 0x0A9C }, +{ 0x0A9D, 0x0A9D, 0x0A9D }, +{ 0x0A9E, 0x0A9E, 0x0A9E }, +{ 0x0A9F, 0x0A9F, 0x0A9F }, +{ 0x0AA0, 0x0AA0, 0x0AA0 }, +{ 0x0AA1, 0x0AA1, 0x0AA1 }, +{ 0x0AA2, 0x0AA2, 0x0AA2 }, +{ 0x0AA3, 0x0AA3, 0x0AA3 }, +{ 0x0AA4, 0x0AA4, 0x0AA4 }, +{ 0x0AA5, 0x0AA5, 0x0AA5 }, +{ 0x0AA6, 0x0AA6, 0x0AA6 }, +{ 0x0AA7, 0x0AA7, 0x0AA7 }, +{ 0x0AA8, 0x0AA8, 0x0AA8 }, +{ 0x0AAA, 0x0AAA, 0x0AAA }, +{ 0x0AAB, 0x0AAB, 0x0AAB }, +{ 0x0AAC, 0x0AAC, 0x0AAC }, +{ 0x0AAD, 0x0AAD, 0x0AAD }, +{ 0x0AAE, 0x0AAE, 0x0AAE }, +{ 0x0AAF, 0x0AAF, 0x0AAF }, +{ 0x0AB0, 0x0AB0, 0x0AB0 }, +{ 0x0AB2, 0x0AB2, 0x0AB2 }, +{ 0x0AB3, 0x0AB3, 0x0AB3 }, +{ 0x0AB5, 0x0AB5, 0x0AB5 }, +{ 0x0AB6, 0x0AB6, 0x0AB6 }, +{ 0x0AB7, 0x0AB7, 0x0AB7 }, +{ 0x0AB8, 0x0AB8, 0x0AB8 }, +{ 0x0AB9, 0x0AB9, 0x0AB9 }, +{ 0x0ABC, 0x0ABC, 0x0ABC }, +{ 0x0ABD, 0x0ABD, 0x0ABD }, +{ 0x0AC1, 0x0AC1, 0x0AC1 }, +{ 0x0AC2, 0x0AC2, 0x0AC2 }, +{ 0x0AC3, 0x0AC3, 0x0AC3 }, +{ 0x0AC4, 0x0AC4, 0x0AC4 }, +{ 0x0AC5, 0x0AC5, 0x0AC5 }, +{ 0x0AC7, 0x0AC7, 0x0AC7 }, +{ 0x0AC8, 0x0AC8, 0x0AC8 }, +{ 0x0ACD, 0x0ACD, 0x0ACD }, +{ 0x0AD0, 0x0AD0, 0x0AD0 }, +{ 0x0AE0, 0x0AE0, 0x0AE0 }, +{ 0x0AE1, 0x0AE1, 0x0AE1 }, +{ 0x0AE2, 0x0AE2, 0x0AE2 }, +{ 0x0AE3, 0x0AE3, 0x0AE3 }, +{ 0x0B01, 0x0B01, 0x0B01 }, +{ 0x0B05, 0x0B05, 0x0B05 }, +{ 0x0B06, 0x0B06, 0x0B06 }, +{ 0x0B07, 0x0B07, 0x0B07 }, +{ 0x0B08, 0x0B08, 0x0B08 }, +{ 0x0B09, 0x0B09, 0x0B09 }, +{ 0x0B0A, 0x0B0A, 0x0B0A }, +{ 0x0B0B, 0x0B0B, 0x0B0B }, +{ 0x0B0C, 0x0B0C, 0x0B0C }, +{ 0x0B0F, 0x0B0F, 0x0B0F }, +{ 0x0B10, 0x0B10, 0x0B10 }, +{ 0x0B13, 0x0B13, 0x0B13 }, +{ 0x0B14, 0x0B14, 0x0B14 }, +{ 0x0B15, 0x0B15, 0x0B15 }, +{ 0x0B16, 0x0B16, 0x0B16 }, +{ 0x0B17, 0x0B17, 0x0B17 }, +{ 0x0B18, 0x0B18, 0x0B18 }, +{ 0x0B19, 0x0B19, 0x0B19 }, +{ 0x0B1A, 0x0B1A, 0x0B1A }, +{ 0x0B1B, 0x0B1B, 0x0B1B }, +{ 0x0B1C, 0x0B1C, 0x0B1C }, +{ 0x0B1D, 0x0B1D, 0x0B1D }, +{ 0x0B1E, 0x0B1E, 0x0B1E }, +{ 0x0B1F, 0x0B1F, 0x0B1F }, +{ 0x0B20, 0x0B20, 0x0B20 }, +{ 0x0B21, 0x0B21, 0x0B21 }, +{ 0x0B22, 0x0B22, 0x0B22 }, +{ 0x0B23, 0x0B23, 0x0B23 }, +{ 0x0B24, 0x0B24, 0x0B24 }, +{ 0x0B25, 0x0B25, 0x0B25 }, +{ 0x0B26, 0x0B26, 0x0B26 }, +{ 0x0B27, 0x0B27, 0x0B27 }, +{ 0x0B28, 0x0B28, 0x0B28 }, +{ 0x0B2A, 0x0B2A, 0x0B2A }, +{ 0x0B2B, 0x0B2B, 0x0B2B }, +{ 0x0B2C, 0x0B2C, 0x0B2C }, +{ 0x0B2D, 0x0B2D, 0x0B2D }, +{ 0x0B2E, 0x0B2E, 0x0B2E }, +{ 0x0B2F, 0x0B2F, 0x0B2F }, +{ 0x0B30, 0x0B30, 0x0B30 }, +{ 0x0B32, 0x0B32, 0x0B32 }, +{ 0x0B33, 0x0B33, 0x0B33 }, +{ 0x0B35, 0x0B35, 0x0B35 }, +{ 0x0B36, 0x0B36, 0x0B36 }, +{ 0x0B37, 0x0B37, 0x0B37 }, +{ 0x0B38, 0x0B38, 0x0B38 }, +{ 0x0B39, 0x0B39, 0x0B39 }, +{ 0x0B3C, 0x0B3C, 0x0B3C }, +{ 0x0B3D, 0x0B3D, 0x0B3D }, +{ 0x0B3F, 0x0B3F, 0x0B3F }, +{ 0x0B41, 0x0B41, 0x0B41 }, +{ 0x0B42, 0x0B42, 0x0B42 }, +{ 0x0B43, 0x0B43, 0x0B43 }, +{ 0x0B4D, 0x0B4D, 0x0B4D }, +{ 0x0B56, 0x0B56, 0x0B56 }, +{ 0x0B5C, 0x0B5C, 0x0B5C }, +{ 0x0B5D, 0x0B5D, 0x0B5D }, +{ 0x0B5F, 0x0B5F, 0x0B5F }, +{ 0x0B60, 0x0B60, 0x0B60 }, +{ 0x0B61, 0x0B61, 0x0B61 }, +{ 0x0B71, 0x0B71, 0x0B71 }, +{ 0x0B82, 0x0B82, 0x0B82 }, +{ 0x0B83, 0x0B83, 0x0B83 }, +{ 0x0B85, 0x0B85, 0x0B85 }, +{ 0x0B86, 0x0B86, 0x0B86 }, +{ 0x0B87, 0x0B87, 0x0B87 }, +{ 0x0B88, 0x0B88, 0x0B88 }, +{ 0x0B89, 0x0B89, 0x0B89 }, +{ 0x0B8A, 0x0B8A, 0x0B8A }, +{ 0x0B8E, 0x0B8E, 0x0B8E }, +{ 0x0B8F, 0x0B8F, 0x0B8F }, +{ 0x0B90, 0x0B90, 0x0B90 }, +{ 0x0B92, 0x0B92, 0x0B92 }, +{ 0x0B93, 0x0B93, 0x0B93 }, +{ 0x0B94, 0x0B94, 0x0B94 }, +{ 0x0B95, 0x0B95, 0x0B95 }, +{ 0x0B99, 0x0B99, 0x0B99 }, +{ 0x0B9A, 0x0B9A, 0x0B9A }, +{ 0x0B9C, 0x0B9C, 0x0B9C }, +{ 0x0B9E, 0x0B9E, 0x0B9E }, +{ 0x0B9F, 0x0B9F, 0x0B9F }, +{ 0x0BA3, 0x0BA3, 0x0BA3 }, +{ 0x0BA4, 0x0BA4, 0x0BA4 }, +{ 0x0BA8, 0x0BA8, 0x0BA8 }, +{ 0x0BA9, 0x0BA9, 0x0BA9 }, +{ 0x0BAA, 0x0BAA, 0x0BAA }, +{ 0x0BAE, 0x0BAE, 0x0BAE }, +{ 0x0BAF, 0x0BAF, 0x0BAF }, +{ 0x0BB0, 0x0BB0, 0x0BB0 }, +{ 0x0BB1, 0x0BB1, 0x0BB1 }, +{ 0x0BB2, 0x0BB2, 0x0BB2 }, +{ 0x0BB3, 0x0BB3, 0x0BB3 }, +{ 0x0BB4, 0x0BB4, 0x0BB4 }, +{ 0x0BB5, 0x0BB5, 0x0BB5 }, +{ 0x0BB6, 0x0BB6, 0x0BB6 }, +{ 0x0BB7, 0x0BB7, 0x0BB7 }, +{ 0x0BB8, 0x0BB8, 0x0BB8 }, +{ 0x0BB9, 0x0BB9, 0x0BB9 }, +{ 0x0BC0, 0x0BC0, 0x0BC0 }, +{ 0x0BCD, 0x0BCD, 0x0BCD }, +{ 0x0C05, 0x0C05, 0x0C05 }, +{ 0x0C06, 0x0C06, 0x0C06 }, +{ 0x0C07, 0x0C07, 0x0C07 }, +{ 0x0C08, 0x0C08, 0x0C08 }, +{ 0x0C09, 0x0C09, 0x0C09 }, +{ 0x0C0A, 0x0C0A, 0x0C0A }, +{ 0x0C0B, 0x0C0B, 0x0C0B }, +{ 0x0C0C, 0x0C0C, 0x0C0C }, +{ 0x0C0E, 0x0C0E, 0x0C0E }, +{ 0x0C0F, 0x0C0F, 0x0C0F }, +{ 0x0C10, 0x0C10, 0x0C10 }, +{ 0x0C12, 0x0C12, 0x0C12 }, +{ 0x0C13, 0x0C13, 0x0C13 }, +{ 0x0C14, 0x0C14, 0x0C14 }, +{ 0x0C15, 0x0C15, 0x0C15 }, +{ 0x0C16, 0x0C16, 0x0C16 }, +{ 0x0C17, 0x0C17, 0x0C17 }, +{ 0x0C18, 0x0C18, 0x0C18 }, +{ 0x0C19, 0x0C19, 0x0C19 }, +{ 0x0C1A, 0x0C1A, 0x0C1A }, +{ 0x0C1B, 0x0C1B, 0x0C1B }, +{ 0x0C1C, 0x0C1C, 0x0C1C }, +{ 0x0C1D, 0x0C1D, 0x0C1D }, +{ 0x0C1E, 0x0C1E, 0x0C1E }, +{ 0x0C1F, 0x0C1F, 0x0C1F }, +{ 0x0C20, 0x0C20, 0x0C20 }, +{ 0x0C21, 0x0C21, 0x0C21 }, +{ 0x0C22, 0x0C22, 0x0C22 }, +{ 0x0C23, 0x0C23, 0x0C23 }, +{ 0x0C24, 0x0C24, 0x0C24 }, +{ 0x0C25, 0x0C25, 0x0C25 }, +{ 0x0C26, 0x0C26, 0x0C26 }, +{ 0x0C27, 0x0C27, 0x0C27 }, +{ 0x0C28, 0x0C28, 0x0C28 }, +{ 0x0C2A, 0x0C2A, 0x0C2A }, +{ 0x0C2B, 0x0C2B, 0x0C2B }, +{ 0x0C2C, 0x0C2C, 0x0C2C }, +{ 0x0C2D, 0x0C2D, 0x0C2D }, +{ 0x0C2E, 0x0C2E, 0x0C2E }, +{ 0x0C2F, 0x0C2F, 0x0C2F }, +{ 0x0C30, 0x0C30, 0x0C30 }, +{ 0x0C31, 0x0C31, 0x0C31 }, +{ 0x0C32, 0x0C32, 0x0C32 }, +{ 0x0C33, 0x0C33, 0x0C33 }, +{ 0x0C35, 0x0C35, 0x0C35 }, +{ 0x0C36, 0x0C36, 0x0C36 }, +{ 0x0C37, 0x0C37, 0x0C37 }, +{ 0x0C38, 0x0C38, 0x0C38 }, +{ 0x0C39, 0x0C39, 0x0C39 }, +{ 0x0C3E, 0x0C3E, 0x0C3E }, +{ 0x0C3F, 0x0C3F, 0x0C3F }, +{ 0x0C40, 0x0C40, 0x0C40 }, +{ 0x0C46, 0x0C46, 0x0C46 }, +{ 0x0C47, 0x0C47, 0x0C47 }, +{ 0x0C48, 0x0C48, 0x0C48 }, +{ 0x0C4A, 0x0C4A, 0x0C4A }, +{ 0x0C4B, 0x0C4B, 0x0C4B }, +{ 0x0C4C, 0x0C4C, 0x0C4C }, +{ 0x0C4D, 0x0C4D, 0x0C4D }, +{ 0x0C55, 0x0C55, 0x0C55 }, +{ 0x0C56, 0x0C56, 0x0C56 }, +{ 0x0C60, 0x0C60, 0x0C60 }, +{ 0x0C61, 0x0C61, 0x0C61 }, +{ 0x0C85, 0x0C85, 0x0C85 }, +{ 0x0C86, 0x0C86, 0x0C86 }, +{ 0x0C87, 0x0C87, 0x0C87 }, +{ 0x0C88, 0x0C88, 0x0C88 }, +{ 0x0C89, 0x0C89, 0x0C89 }, +{ 0x0C8A, 0x0C8A, 0x0C8A }, +{ 0x0C8B, 0x0C8B, 0x0C8B }, +{ 0x0C8C, 0x0C8C, 0x0C8C }, +{ 0x0C8E, 0x0C8E, 0x0C8E }, +{ 0x0C8F, 0x0C8F, 0x0C8F }, +{ 0x0C90, 0x0C90, 0x0C90 }, +{ 0x0C92, 0x0C92, 0x0C92 }, +{ 0x0C93, 0x0C93, 0x0C93 }, +{ 0x0C94, 0x0C94, 0x0C94 }, +{ 0x0C95, 0x0C95, 0x0C95 }, +{ 0x0C96, 0x0C96, 0x0C96 }, +{ 0x0C97, 0x0C97, 0x0C97 }, +{ 0x0C98, 0x0C98, 0x0C98 }, +{ 0x0C99, 0x0C99, 0x0C99 }, +{ 0x0C9A, 0x0C9A, 0x0C9A }, +{ 0x0C9B, 0x0C9B, 0x0C9B }, +{ 0x0C9C, 0x0C9C, 0x0C9C }, +{ 0x0C9D, 0x0C9D, 0x0C9D }, +{ 0x0C9E, 0x0C9E, 0x0C9E }, +{ 0x0C9F, 0x0C9F, 0x0C9F }, +{ 0x0CA0, 0x0CA0, 0x0CA0 }, +{ 0x0CA1, 0x0CA1, 0x0CA1 }, +{ 0x0CA2, 0x0CA2, 0x0CA2 }, +{ 0x0CA3, 0x0CA3, 0x0CA3 }, +{ 0x0CA4, 0x0CA4, 0x0CA4 }, +{ 0x0CA5, 0x0CA5, 0x0CA5 }, +{ 0x0CA6, 0x0CA6, 0x0CA6 }, +{ 0x0CA7, 0x0CA7, 0x0CA7 }, +{ 0x0CA8, 0x0CA8, 0x0CA8 }, +{ 0x0CAA, 0x0CAA, 0x0CAA }, +{ 0x0CAB, 0x0CAB, 0x0CAB }, +{ 0x0CAC, 0x0CAC, 0x0CAC }, +{ 0x0CAD, 0x0CAD, 0x0CAD }, +{ 0x0CAE, 0x0CAE, 0x0CAE }, +{ 0x0CAF, 0x0CAF, 0x0CAF }, +{ 0x0CB0, 0x0CB0, 0x0CB0 }, +{ 0x0CB1, 0x0CB1, 0x0CB1 }, +{ 0x0CB2, 0x0CB2, 0x0CB2 }, +{ 0x0CB3, 0x0CB3, 0x0CB3 }, +{ 0x0CB5, 0x0CB5, 0x0CB5 }, +{ 0x0CB6, 0x0CB6, 0x0CB6 }, +{ 0x0CB7, 0x0CB7, 0x0CB7 }, +{ 0x0CB8, 0x0CB8, 0x0CB8 }, +{ 0x0CB9, 0x0CB9, 0x0CB9 }, +{ 0x0CBC, 0x0CBC, 0x0CBC }, +{ 0x0CBD, 0x0CBD, 0x0CBD }, +{ 0x0CBF, 0x0CBF, 0x0CBF }, +{ 0x0CC6, 0x0CC6, 0x0CC6 }, +{ 0x0CCC, 0x0CCC, 0x0CCC }, +{ 0x0CCD, 0x0CCD, 0x0CCD }, +{ 0x0CDE, 0x0CDE, 0x0CDE }, +{ 0x0CE0, 0x0CE0, 0x0CE0 }, +{ 0x0CE1, 0x0CE1, 0x0CE1 }, +{ 0x0D05, 0x0D05, 0x0D05 }, +{ 0x0D06, 0x0D06, 0x0D06 }, +{ 0x0D07, 0x0D07, 0x0D07 }, +{ 0x0D08, 0x0D08, 0x0D08 }, +{ 0x0D09, 0x0D09, 0x0D09 }, +{ 0x0D0A, 0x0D0A, 0x0D0A }, +{ 0x0D0B, 0x0D0B, 0x0D0B }, +{ 0x0D0C, 0x0D0C, 0x0D0C }, +{ 0x0D0E, 0x0D0E, 0x0D0E }, +{ 0x0D0F, 0x0D0F, 0x0D0F }, +{ 0x0D10, 0x0D10, 0x0D10 }, +{ 0x0D12, 0x0D12, 0x0D12 }, +{ 0x0D13, 0x0D13, 0x0D13 }, +{ 0x0D14, 0x0D14, 0x0D14 }, +{ 0x0D15, 0x0D15, 0x0D15 }, +{ 0x0D16, 0x0D16, 0x0D16 }, +{ 0x0D17, 0x0D17, 0x0D17 }, +{ 0x0D18, 0x0D18, 0x0D18 }, +{ 0x0D19, 0x0D19, 0x0D19 }, +{ 0x0D1A, 0x0D1A, 0x0D1A }, +{ 0x0D1B, 0x0D1B, 0x0D1B }, +{ 0x0D1C, 0x0D1C, 0x0D1C }, +{ 0x0D1D, 0x0D1D, 0x0D1D }, +{ 0x0D1E, 0x0D1E, 0x0D1E }, +{ 0x0D1F, 0x0D1F, 0x0D1F }, +{ 0x0D20, 0x0D20, 0x0D20 }, +{ 0x0D21, 0x0D21, 0x0D21 }, +{ 0x0D22, 0x0D22, 0x0D22 }, +{ 0x0D23, 0x0D23, 0x0D23 }, +{ 0x0D24, 0x0D24, 0x0D24 }, +{ 0x0D25, 0x0D25, 0x0D25 }, +{ 0x0D26, 0x0D26, 0x0D26 }, +{ 0x0D27, 0x0D27, 0x0D27 }, +{ 0x0D28, 0x0D28, 0x0D28 }, +{ 0x0D2A, 0x0D2A, 0x0D2A }, +{ 0x0D2B, 0x0D2B, 0x0D2B }, +{ 0x0D2C, 0x0D2C, 0x0D2C }, +{ 0x0D2D, 0x0D2D, 0x0D2D }, +{ 0x0D2E, 0x0D2E, 0x0D2E }, +{ 0x0D2F, 0x0D2F, 0x0D2F }, +{ 0x0D30, 0x0D30, 0x0D30 }, +{ 0x0D31, 0x0D31, 0x0D31 }, +{ 0x0D32, 0x0D32, 0x0D32 }, +{ 0x0D33, 0x0D33, 0x0D33 }, +{ 0x0D34, 0x0D34, 0x0D34 }, +{ 0x0D35, 0x0D35, 0x0D35 }, +{ 0x0D36, 0x0D36, 0x0D36 }, +{ 0x0D37, 0x0D37, 0x0D37 }, +{ 0x0D38, 0x0D38, 0x0D38 }, +{ 0x0D39, 0x0D39, 0x0D39 }, +{ 0x0D41, 0x0D41, 0x0D41 }, +{ 0x0D42, 0x0D42, 0x0D42 }, +{ 0x0D43, 0x0D43, 0x0D43 }, +{ 0x0D4D, 0x0D4D, 0x0D4D }, +{ 0x0D60, 0x0D60, 0x0D60 }, +{ 0x0D61, 0x0D61, 0x0D61 }, +{ 0x0D85, 0x0D85, 0x0D85 }, +{ 0x0D86, 0x0D86, 0x0D86 }, +{ 0x0D87, 0x0D87, 0x0D87 }, +{ 0x0D88, 0x0D88, 0x0D88 }, +{ 0x0D89, 0x0D89, 0x0D89 }, +{ 0x0D8A, 0x0D8A, 0x0D8A }, +{ 0x0D8B, 0x0D8B, 0x0D8B }, +{ 0x0D8C, 0x0D8C, 0x0D8C }, +{ 0x0D8D, 0x0D8D, 0x0D8D }, +{ 0x0D8E, 0x0D8E, 0x0D8E }, +{ 0x0D8F, 0x0D8F, 0x0D8F }, +{ 0x0D90, 0x0D90, 0x0D90 }, +{ 0x0D91, 0x0D91, 0x0D91 }, +{ 0x0D92, 0x0D92, 0x0D92 }, +{ 0x0D93, 0x0D93, 0x0D93 }, +{ 0x0D94, 0x0D94, 0x0D94 }, +{ 0x0D95, 0x0D95, 0x0D95 }, +{ 0x0D96, 0x0D96, 0x0D96 }, +{ 0x0D9A, 0x0D9A, 0x0D9A }, +{ 0x0D9B, 0x0D9B, 0x0D9B }, +{ 0x0D9C, 0x0D9C, 0x0D9C }, +{ 0x0D9D, 0x0D9D, 0x0D9D }, +{ 0x0D9E, 0x0D9E, 0x0D9E }, +{ 0x0D9F, 0x0D9F, 0x0D9F }, +{ 0x0DA0, 0x0DA0, 0x0DA0 }, +{ 0x0DA1, 0x0DA1, 0x0DA1 }, +{ 0x0DA2, 0x0DA2, 0x0DA2 }, +{ 0x0DA3, 0x0DA3, 0x0DA3 }, +{ 0x0DA4, 0x0DA4, 0x0DA4 }, +{ 0x0DA5, 0x0DA5, 0x0DA5 }, +{ 0x0DA6, 0x0DA6, 0x0DA6 }, +{ 0x0DA7, 0x0DA7, 0x0DA7 }, +{ 0x0DA8, 0x0DA8, 0x0DA8 }, +{ 0x0DA9, 0x0DA9, 0x0DA9 }, +{ 0x0DAA, 0x0DAA, 0x0DAA }, +{ 0x0DAB, 0x0DAB, 0x0DAB }, +{ 0x0DAC, 0x0DAC, 0x0DAC }, +{ 0x0DAD, 0x0DAD, 0x0DAD }, +{ 0x0DAE, 0x0DAE, 0x0DAE }, +{ 0x0DAF, 0x0DAF, 0x0DAF }, +{ 0x0DB0, 0x0DB0, 0x0DB0 }, +{ 0x0DB1, 0x0DB1, 0x0DB1 }, +{ 0x0DB3, 0x0DB3, 0x0DB3 }, +{ 0x0DB4, 0x0DB4, 0x0DB4 }, +{ 0x0DB5, 0x0DB5, 0x0DB5 }, +{ 0x0DB6, 0x0DB6, 0x0DB6 }, +{ 0x0DB7, 0x0DB7, 0x0DB7 }, +{ 0x0DB8, 0x0DB8, 0x0DB8 }, +{ 0x0DB9, 0x0DB9, 0x0DB9 }, +{ 0x0DBA, 0x0DBA, 0x0DBA }, +{ 0x0DBB, 0x0DBB, 0x0DBB }, +{ 0x0DBD, 0x0DBD, 0x0DBD }, +{ 0x0DC0, 0x0DC0, 0x0DC0 }, +{ 0x0DC1, 0x0DC1, 0x0DC1 }, +{ 0x0DC2, 0x0DC2, 0x0DC2 }, +{ 0x0DC3, 0x0DC3, 0x0DC3 }, +{ 0x0DC4, 0x0DC4, 0x0DC4 }, +{ 0x0DC5, 0x0DC5, 0x0DC5 }, +{ 0x0DC6, 0x0DC6, 0x0DC6 }, +{ 0x0DCA, 0x0DCA, 0x0DCA }, +{ 0x0DD2, 0x0DD2, 0x0DD2 }, +{ 0x0DD3, 0x0DD3, 0x0DD3 }, +{ 0x0DD4, 0x0DD4, 0x0DD4 }, +{ 0x0DD6, 0x0DD6, 0x0DD6 }, +{ 0x0E01, 0x0E01, 0x0E01 }, +{ 0x0E02, 0x0E02, 0x0E02 }, +{ 0x0E03, 0x0E03, 0x0E03 }, +{ 0x0E04, 0x0E04, 0x0E04 }, +{ 0x0E05, 0x0E05, 0x0E05 }, +{ 0x0E06, 0x0E06, 0x0E06 }, +{ 0x0E07, 0x0E07, 0x0E07 }, +{ 0x0E08, 0x0E08, 0x0E08 }, +{ 0x0E09, 0x0E09, 0x0E09 }, +{ 0x0E0A, 0x0E0A, 0x0E0A }, +{ 0x0E0B, 0x0E0B, 0x0E0B }, +{ 0x0E0C, 0x0E0C, 0x0E0C }, +{ 0x0E0D, 0x0E0D, 0x0E0D }, +{ 0x0E0E, 0x0E0E, 0x0E0E }, +{ 0x0E0F, 0x0E0F, 0x0E0F }, +{ 0x0E10, 0x0E10, 0x0E10 }, +{ 0x0E11, 0x0E11, 0x0E11 }, +{ 0x0E12, 0x0E12, 0x0E12 }, +{ 0x0E13, 0x0E13, 0x0E13 }, +{ 0x0E14, 0x0E14, 0x0E14 }, +{ 0x0E15, 0x0E15, 0x0E15 }, +{ 0x0E16, 0x0E16, 0x0E16 }, +{ 0x0E17, 0x0E17, 0x0E17 }, +{ 0x0E18, 0x0E18, 0x0E18 }, +{ 0x0E19, 0x0E19, 0x0E19 }, +{ 0x0E1A, 0x0E1A, 0x0E1A }, +{ 0x0E1B, 0x0E1B, 0x0E1B }, +{ 0x0E1C, 0x0E1C, 0x0E1C }, +{ 0x0E1D, 0x0E1D, 0x0E1D }, +{ 0x0E1E, 0x0E1E, 0x0E1E }, +{ 0x0E1F, 0x0E1F, 0x0E1F }, +{ 0x0E20, 0x0E20, 0x0E20 }, +{ 0x0E21, 0x0E21, 0x0E21 }, +{ 0x0E22, 0x0E22, 0x0E22 }, +{ 0x0E23, 0x0E23, 0x0E23 }, +{ 0x0E24, 0x0E24, 0x0E24 }, +{ 0x0E25, 0x0E25, 0x0E25 }, +{ 0x0E26, 0x0E26, 0x0E26 }, +{ 0x0E27, 0x0E27, 0x0E27 }, +{ 0x0E28, 0x0E28, 0x0E28 }, +{ 0x0E29, 0x0E29, 0x0E29 }, +{ 0x0E2A, 0x0E2A, 0x0E2A }, +{ 0x0E2B, 0x0E2B, 0x0E2B }, +{ 0x0E2C, 0x0E2C, 0x0E2C }, +{ 0x0E2D, 0x0E2D, 0x0E2D }, +{ 0x0E2E, 0x0E2E, 0x0E2E }, +{ 0x0E2F, 0x0E2F, 0x0E2F }, +{ 0x0E30, 0x0E30, 0x0E30 }, +{ 0x0E31, 0x0E31, 0x0E31 }, +{ 0x0E32, 0x0E32, 0x0E32 }, +{ 0x0E33, 0x0E33, 0x0E33 }, +{ 0x0E34, 0x0E34, 0x0E34 }, +{ 0x0E35, 0x0E35, 0x0E35 }, +{ 0x0E36, 0x0E36, 0x0E36 }, +{ 0x0E37, 0x0E37, 0x0E37 }, +{ 0x0E38, 0x0E38, 0x0E38 }, +{ 0x0E39, 0x0E39, 0x0E39 }, +{ 0x0E3A, 0x0E3A, 0x0E3A }, +{ 0x0E40, 0x0E40, 0x0E40 }, +{ 0x0E41, 0x0E41, 0x0E41 }, +{ 0x0E42, 0x0E42, 0x0E42 }, +{ 0x0E43, 0x0E43, 0x0E43 }, +{ 0x0E44, 0x0E44, 0x0E44 }, +{ 0x0E45, 0x0E45, 0x0E45 }, +{ 0x0E46, 0x0E46, 0x0E46 }, +{ 0x0E47, 0x0E47, 0x0E47 }, +{ 0x0E48, 0x0E48, 0x0E48 }, +{ 0x0E49, 0x0E49, 0x0E49 }, +{ 0x0E4A, 0x0E4A, 0x0E4A }, +{ 0x0E4B, 0x0E4B, 0x0E4B }, +{ 0x0E4C, 0x0E4C, 0x0E4C }, +{ 0x0E4D, 0x0E4D, 0x0E4D }, +{ 0x0E4E, 0x0E4E, 0x0E4E }, +{ 0x0E81, 0x0E81, 0x0E81 }, +{ 0x0E82, 0x0E82, 0x0E82 }, +{ 0x0E84, 0x0E84, 0x0E84 }, +{ 0x0E87, 0x0E87, 0x0E87 }, +{ 0x0E88, 0x0E88, 0x0E88 }, +{ 0x0E8A, 0x0E8A, 0x0E8A }, +{ 0x0E8D, 0x0E8D, 0x0E8D }, +{ 0x0E94, 0x0E94, 0x0E94 }, +{ 0x0E95, 0x0E95, 0x0E95 }, +{ 0x0E96, 0x0E96, 0x0E96 }, +{ 0x0E97, 0x0E97, 0x0E97 }, +{ 0x0E99, 0x0E99, 0x0E99 }, +{ 0x0E9A, 0x0E9A, 0x0E9A }, +{ 0x0E9B, 0x0E9B, 0x0E9B }, +{ 0x0E9C, 0x0E9C, 0x0E9C }, +{ 0x0E9D, 0x0E9D, 0x0E9D }, +{ 0x0E9E, 0x0E9E, 0x0E9E }, +{ 0x0E9F, 0x0E9F, 0x0E9F }, +{ 0x0EA1, 0x0EA1, 0x0EA1 }, +{ 0x0EA2, 0x0EA2, 0x0EA2 }, +{ 0x0EA3, 0x0EA3, 0x0EA3 }, +{ 0x0EA5, 0x0EA5, 0x0EA5 }, +{ 0x0EA7, 0x0EA7, 0x0EA7 }, +{ 0x0EAA, 0x0EAA, 0x0EAA }, +{ 0x0EAB, 0x0EAB, 0x0EAB }, +{ 0x0EAD, 0x0EAD, 0x0EAD }, +{ 0x0EAE, 0x0EAE, 0x0EAE }, +{ 0x0EAF, 0x0EAF, 0x0EAF }, +{ 0x0EB0, 0x0EB0, 0x0EB0 }, +{ 0x0EB1, 0x0EB1, 0x0EB1 }, +{ 0x0EB2, 0x0EB2, 0x0EB2 }, +{ 0x0EB3, 0x0EB3, 0x0EB3 }, +{ 0x0EB4, 0x0EB4, 0x0EB4 }, +{ 0x0EB5, 0x0EB5, 0x0EB5 }, +{ 0x0EB6, 0x0EB6, 0x0EB6 }, +{ 0x0EB7, 0x0EB7, 0x0EB7 }, +{ 0x0EB8, 0x0EB8, 0x0EB8 }, +{ 0x0EB9, 0x0EB9, 0x0EB9 }, +{ 0x0EBB, 0x0EBB, 0x0EBB }, +{ 0x0EBC, 0x0EBC, 0x0EBC }, +{ 0x0EBD, 0x0EBD, 0x0EBD }, +{ 0x0EC0, 0x0EC0, 0x0EC0 }, +{ 0x0EC1, 0x0EC1, 0x0EC1 }, +{ 0x0EC2, 0x0EC2, 0x0EC2 }, +{ 0x0EC3, 0x0EC3, 0x0EC3 }, +{ 0x0EC4, 0x0EC4, 0x0EC4 }, +{ 0x0EC6, 0x0EC6, 0x0EC6 }, +{ 0x0EC8, 0x0EC8, 0x0EC8 }, +{ 0x0EC9, 0x0EC9, 0x0EC9 }, +{ 0x0ECA, 0x0ECA, 0x0ECA }, +{ 0x0ECB, 0x0ECB, 0x0ECB }, +{ 0x0ECC, 0x0ECC, 0x0ECC }, +{ 0x0ECD, 0x0ECD, 0x0ECD }, +{ 0x0EDC, 0x0EDC, 0x0EDC }, +{ 0x0EDD, 0x0EDD, 0x0EDD }, +{ 0x0F00, 0x0F00, 0x0F00 }, +{ 0x0F18, 0x0F18, 0x0F18 }, +{ 0x0F19, 0x0F19, 0x0F19 }, +{ 0x0F35, 0x0F35, 0x0F35 }, +{ 0x0F37, 0x0F37, 0x0F37 }, +{ 0x0F39, 0x0F39, 0x0F39 }, +{ 0x0F40, 0x0F40, 0x0F40 }, +{ 0x0F41, 0x0F41, 0x0F41 }, +{ 0x0F42, 0x0F42, 0x0F42 }, +{ 0x0F43, 0x0F43, 0x0F43 }, +{ 0x0F44, 0x0F44, 0x0F44 }, +{ 0x0F45, 0x0F45, 0x0F45 }, +{ 0x0F46, 0x0F46, 0x0F46 }, +{ 0x0F47, 0x0F47, 0x0F47 }, +{ 0x0F49, 0x0F49, 0x0F49 }, +{ 0x0F4A, 0x0F4A, 0x0F4A }, +{ 0x0F4B, 0x0F4B, 0x0F4B }, +{ 0x0F4C, 0x0F4C, 0x0F4C }, +{ 0x0F4D, 0x0F4D, 0x0F4D }, +{ 0x0F4E, 0x0F4E, 0x0F4E }, +{ 0x0F4F, 0x0F4F, 0x0F4F }, +{ 0x0F50, 0x0F50, 0x0F50 }, +{ 0x0F51, 0x0F51, 0x0F51 }, +{ 0x0F52, 0x0F52, 0x0F52 }, +{ 0x0F53, 0x0F53, 0x0F53 }, +{ 0x0F54, 0x0F54, 0x0F54 }, +{ 0x0F55, 0x0F55, 0x0F55 }, +{ 0x0F56, 0x0F56, 0x0F56 }, +{ 0x0F57, 0x0F57, 0x0F57 }, +{ 0x0F58, 0x0F58, 0x0F58 }, +{ 0x0F59, 0x0F59, 0x0F59 }, +{ 0x0F5A, 0x0F5A, 0x0F5A }, +{ 0x0F5B, 0x0F5B, 0x0F5B }, +{ 0x0F5C, 0x0F5C, 0x0F5C }, +{ 0x0F5D, 0x0F5D, 0x0F5D }, +{ 0x0F5E, 0x0F5E, 0x0F5E }, +{ 0x0F5F, 0x0F5F, 0x0F5F }, +{ 0x0F60, 0x0F60, 0x0F60 }, +{ 0x0F61, 0x0F61, 0x0F61 }, +{ 0x0F62, 0x0F62, 0x0F62 }, +{ 0x0F63, 0x0F63, 0x0F63 }, +{ 0x0F64, 0x0F64, 0x0F64 }, +{ 0x0F65, 0x0F65, 0x0F65 }, +{ 0x0F66, 0x0F66, 0x0F66 }, +{ 0x0F67, 0x0F67, 0x0F67 }, +{ 0x0F68, 0x0F68, 0x0F68 }, +{ 0x0F69, 0x0F69, 0x0F69 }, +{ 0x0F6A, 0x0F6A, 0x0F6A }, +{ 0x0F71, 0x0F71, 0x0F71 }, +{ 0x0F72, 0x0F72, 0x0F72 }, +{ 0x0F73, 0x0F73, 0x0F73 }, +{ 0x0F74, 0x0F74, 0x0F74 }, +{ 0x0F75, 0x0F75, 0x0F75 }, +{ 0x0F76, 0x0F76, 0x0F76 }, +{ 0x0F77, 0x0F77, 0x0F77 }, +{ 0x0F78, 0x0F78, 0x0F78 }, +{ 0x0F79, 0x0F79, 0x0F79 }, +{ 0x0F7A, 0x0F7A, 0x0F7A }, +{ 0x0F7B, 0x0F7B, 0x0F7B }, +{ 0x0F7C, 0x0F7C, 0x0F7C }, +{ 0x0F7D, 0x0F7D, 0x0F7D }, +{ 0x0F7E, 0x0F7E, 0x0F7E }, +{ 0x0F80, 0x0F80, 0x0F80 }, +{ 0x0F81, 0x0F81, 0x0F81 }, +{ 0x0F82, 0x0F82, 0x0F82 }, +{ 0x0F83, 0x0F83, 0x0F83 }, +{ 0x0F84, 0x0F84, 0x0F84 }, +{ 0x0F86, 0x0F86, 0x0F86 }, +{ 0x0F87, 0x0F87, 0x0F87 }, +{ 0x0F88, 0x0F88, 0x0F88 }, +{ 0x0F89, 0x0F89, 0x0F89 }, +{ 0x0F8A, 0x0F8A, 0x0F8A }, +{ 0x0F8B, 0x0F8B, 0x0F8B }, +{ 0x0F90, 0x0F90, 0x0F90 }, +{ 0x0F91, 0x0F91, 0x0F91 }, +{ 0x0F92, 0x0F92, 0x0F92 }, +{ 0x0F93, 0x0F93, 0x0F93 }, +{ 0x0F94, 0x0F94, 0x0F94 }, +{ 0x0F95, 0x0F95, 0x0F95 }, +{ 0x0F96, 0x0F96, 0x0F96 }, +{ 0x0F97, 0x0F97, 0x0F97 }, +{ 0x0F99, 0x0F99, 0x0F99 }, +{ 0x0F9A, 0x0F9A, 0x0F9A }, +{ 0x0F9B, 0x0F9B, 0x0F9B }, +{ 0x0F9C, 0x0F9C, 0x0F9C }, +{ 0x0F9D, 0x0F9D, 0x0F9D }, +{ 0x0F9E, 0x0F9E, 0x0F9E }, +{ 0x0F9F, 0x0F9F, 0x0F9F }, +{ 0x0FA0, 0x0FA0, 0x0FA0 }, +{ 0x0FA1, 0x0FA1, 0x0FA1 }, +{ 0x0FA2, 0x0FA2, 0x0FA2 }, +{ 0x0FA3, 0x0FA3, 0x0FA3 }, +{ 0x0FA4, 0x0FA4, 0x0FA4 }, +{ 0x0FA5, 0x0FA5, 0x0FA5 }, +{ 0x0FA6, 0x0FA6, 0x0FA6 }, +{ 0x0FA7, 0x0FA7, 0x0FA7 }, +{ 0x0FA8, 0x0FA8, 0x0FA8 }, +{ 0x0FA9, 0x0FA9, 0x0FA9 }, +{ 0x0FAA, 0x0FAA, 0x0FAA }, +{ 0x0FAB, 0x0FAB, 0x0FAB }, +{ 0x0FAC, 0x0FAC, 0x0FAC }, +{ 0x0FAD, 0x0FAD, 0x0FAD }, +{ 0x0FAE, 0x0FAE, 0x0FAE }, +{ 0x0FAF, 0x0FAF, 0x0FAF }, +{ 0x0FB0, 0x0FB0, 0x0FB0 }, +{ 0x0FB1, 0x0FB1, 0x0FB1 }, +{ 0x0FB2, 0x0FB2, 0x0FB2 }, +{ 0x0FB3, 0x0FB3, 0x0FB3 }, +{ 0x0FB4, 0x0FB4, 0x0FB4 }, +{ 0x0FB5, 0x0FB5, 0x0FB5 }, +{ 0x0FB6, 0x0FB6, 0x0FB6 }, +{ 0x0FB7, 0x0FB7, 0x0FB7 }, +{ 0x0FB8, 0x0FB8, 0x0FB8 }, +{ 0x0FB9, 0x0FB9, 0x0FB9 }, +{ 0x0FBA, 0x0FBA, 0x0FBA }, +{ 0x0FBB, 0x0FBB, 0x0FBB }, +{ 0x0FBC, 0x0FBC, 0x0FBC }, +{ 0x0FC6, 0x0FC6, 0x0FC6 }, +{ 0x1000, 0x1000, 0x1000 }, +{ 0x1001, 0x1001, 0x1001 }, +{ 0x1002, 0x1002, 0x1002 }, +{ 0x1003, 0x1003, 0x1003 }, +{ 0x1004, 0x1004, 0x1004 }, +{ 0x1005, 0x1005, 0x1005 }, +{ 0x1006, 0x1006, 0x1006 }, +{ 0x1007, 0x1007, 0x1007 }, +{ 0x1008, 0x1008, 0x1008 }, +{ 0x1009, 0x1009, 0x1009 }, +{ 0x100A, 0x100A, 0x100A }, +{ 0x100B, 0x100B, 0x100B }, +{ 0x100C, 0x100C, 0x100C }, +{ 0x100D, 0x100D, 0x100D }, +{ 0x100E, 0x100E, 0x100E }, +{ 0x100F, 0x100F, 0x100F }, +{ 0x1010, 0x1010, 0x1010 }, +{ 0x1011, 0x1011, 0x1011 }, +{ 0x1012, 0x1012, 0x1012 }, +{ 0x1013, 0x1013, 0x1013 }, +{ 0x1014, 0x1014, 0x1014 }, +{ 0x1015, 0x1015, 0x1015 }, +{ 0x1016, 0x1016, 0x1016 }, +{ 0x1017, 0x1017, 0x1017 }, +{ 0x1018, 0x1018, 0x1018 }, +{ 0x1019, 0x1019, 0x1019 }, +{ 0x101A, 0x101A, 0x101A }, +{ 0x101B, 0x101B, 0x101B }, +{ 0x101C, 0x101C, 0x101C }, +{ 0x101D, 0x101D, 0x101D }, +{ 0x101E, 0x101E, 0x101E }, +{ 0x101F, 0x101F, 0x101F }, +{ 0x1020, 0x1020, 0x1020 }, +{ 0x1021, 0x1021, 0x1021 }, +{ 0x1023, 0x1023, 0x1023 }, +{ 0x1024, 0x1024, 0x1024 }, +{ 0x1025, 0x1025, 0x1025 }, +{ 0x1026, 0x1026, 0x1026 }, +{ 0x1027, 0x1027, 0x1027 }, +{ 0x1029, 0x1029, 0x1029 }, +{ 0x102A, 0x102A, 0x102A }, +{ 0x102D, 0x102D, 0x102D }, +{ 0x102E, 0x102E, 0x102E }, +{ 0x102F, 0x102F, 0x102F }, +{ 0x1030, 0x1030, 0x1030 }, +{ 0x1032, 0x1032, 0x1032 }, +{ 0x1036, 0x1036, 0x1036 }, +{ 0x1037, 0x1037, 0x1037 }, +{ 0x1039, 0x1039, 0x1039 }, +{ 0x1050, 0x1050, 0x1050 }, +{ 0x1051, 0x1051, 0x1051 }, +{ 0x1052, 0x1052, 0x1052 }, +{ 0x1053, 0x1053, 0x1053 }, +{ 0x1054, 0x1054, 0x1054 }, +{ 0x1055, 0x1055, 0x1055 }, +{ 0x1058, 0x1058, 0x1058 }, +{ 0x1059, 0x1059, 0x1059 }, +{ 0x10A0, 0x10A0, 0x2D00 }, +{ 0x10A1, 0x10A1, 0x2D01 }, +{ 0x10A2, 0x10A2, 0x2D02 }, +{ 0x10A3, 0x10A3, 0x2D03 }, +{ 0x10A4, 0x10A4, 0x2D04 }, +{ 0x10A5, 0x10A5, 0x2D05 }, +{ 0x10A6, 0x10A6, 0x2D06 }, +{ 0x10A7, 0x10A7, 0x2D07 }, +{ 0x10A8, 0x10A8, 0x2D08 }, +{ 0x10A9, 0x10A9, 0x2D09 }, +{ 0x10AA, 0x10AA, 0x2D0A }, +{ 0x10AB, 0x10AB, 0x2D0B }, +{ 0x10AC, 0x10AC, 0x2D0C }, +{ 0x10AD, 0x10AD, 0x2D0D }, +{ 0x10AE, 0x10AE, 0x2D0E }, +{ 0x10AF, 0x10AF, 0x2D0F }, +{ 0x10B0, 0x10B0, 0x2D10 }, +{ 0x10B1, 0x10B1, 0x2D11 }, +{ 0x10B2, 0x10B2, 0x2D12 }, +{ 0x10B3, 0x10B3, 0x2D13 }, +{ 0x10B4, 0x10B4, 0x2D14 }, +{ 0x10B5, 0x10B5, 0x2D15 }, +{ 0x10B6, 0x10B6, 0x2D16 }, +{ 0x10B7, 0x10B7, 0x2D17 }, +{ 0x10B8, 0x10B8, 0x2D18 }, +{ 0x10B9, 0x10B9, 0x2D19 }, +{ 0x10BA, 0x10BA, 0x2D1A }, +{ 0x10BB, 0x10BB, 0x2D1B }, +{ 0x10BC, 0x10BC, 0x2D1C }, +{ 0x10BD, 0x10BD, 0x2D1D }, +{ 0x10BE, 0x10BE, 0x2D1E }, +{ 0x10BF, 0x10BF, 0x2D1F }, +{ 0x10C0, 0x10C0, 0x2D20 }, +{ 0x10C1, 0x10C1, 0x2D21 }, +{ 0x10C2, 0x10C2, 0x2D22 }, +{ 0x10C3, 0x10C3, 0x2D23 }, +{ 0x10C4, 0x10C4, 0x2D24 }, +{ 0x10C5, 0x10C5, 0x2D25 }, +{ 0x10D0, 0x10D0, 0x10D0 }, +{ 0x10D1, 0x10D1, 0x10D1 }, +{ 0x10D2, 0x10D2, 0x10D2 }, +{ 0x10D3, 0x10D3, 0x10D3 }, +{ 0x10D4, 0x10D4, 0x10D4 }, +{ 0x10D5, 0x10D5, 0x10D5 }, +{ 0x10D6, 0x10D6, 0x10D6 }, +{ 0x10D7, 0x10D7, 0x10D7 }, +{ 0x10D8, 0x10D8, 0x10D8 }, +{ 0x10D9, 0x10D9, 0x10D9 }, +{ 0x10DA, 0x10DA, 0x10DA }, +{ 0x10DB, 0x10DB, 0x10DB }, +{ 0x10DC, 0x10DC, 0x10DC }, +{ 0x10DD, 0x10DD, 0x10DD }, +{ 0x10DE, 0x10DE, 0x10DE }, +{ 0x10DF, 0x10DF, 0x10DF }, +{ 0x10E0, 0x10E0, 0x10E0 }, +{ 0x10E1, 0x10E1, 0x10E1 }, +{ 0x10E2, 0x10E2, 0x10E2 }, +{ 0x10E3, 0x10E3, 0x10E3 }, +{ 0x10E4, 0x10E4, 0x10E4 }, +{ 0x10E5, 0x10E5, 0x10E5 }, +{ 0x10E6, 0x10E6, 0x10E6 }, +{ 0x10E7, 0x10E7, 0x10E7 }, +{ 0x10E8, 0x10E8, 0x10E8 }, +{ 0x10E9, 0x10E9, 0x10E9 }, +{ 0x10EA, 0x10EA, 0x10EA }, +{ 0x10EB, 0x10EB, 0x10EB }, +{ 0x10EC, 0x10EC, 0x10EC }, +{ 0x10ED, 0x10ED, 0x10ED }, +{ 0x10EE, 0x10EE, 0x10EE }, +{ 0x10EF, 0x10EF, 0x10EF }, +{ 0x10F0, 0x10F0, 0x10F0 }, +{ 0x10F1, 0x10F1, 0x10F1 }, +{ 0x10F2, 0x10F2, 0x10F2 }, +{ 0x10F3, 0x10F3, 0x10F3 }, +{ 0x10F4, 0x10F4, 0x10F4 }, +{ 0x10F5, 0x10F5, 0x10F5 }, +{ 0x10F6, 0x10F6, 0x10F6 }, +{ 0x10F7, 0x10F7, 0x10F7 }, +{ 0x10F8, 0x10F8, 0x10F8 }, +{ 0x10F9, 0x10F9, 0x10F9 }, +{ 0x10FA, 0x10FA, 0x10FA }, +{ 0x10FC, 0x10FC, 0x10FC }, +{ 0x1100, 0x1100, 0x1100 }, +{ 0x1101, 0x1101, 0x1101 }, +{ 0x1102, 0x1102, 0x1102 }, +{ 0x1103, 0x1103, 0x1103 }, +{ 0x1104, 0x1104, 0x1104 }, +{ 0x1105, 0x1105, 0x1105 }, +{ 0x1106, 0x1106, 0x1106 }, +{ 0x1107, 0x1107, 0x1107 }, +{ 0x1108, 0x1108, 0x1108 }, +{ 0x1109, 0x1109, 0x1109 }, +{ 0x110A, 0x110A, 0x110A }, +{ 0x110B, 0x110B, 0x110B }, +{ 0x110C, 0x110C, 0x110C }, +{ 0x110D, 0x110D, 0x110D }, +{ 0x110E, 0x110E, 0x110E }, +{ 0x110F, 0x110F, 0x110F }, +{ 0x1110, 0x1110, 0x1110 }, +{ 0x1111, 0x1111, 0x1111 }, +{ 0x1112, 0x1112, 0x1112 }, +{ 0x1113, 0x1113, 0x1113 }, +{ 0x1114, 0x1114, 0x1114 }, +{ 0x1115, 0x1115, 0x1115 }, +{ 0x1116, 0x1116, 0x1116 }, +{ 0x1117, 0x1117, 0x1117 }, +{ 0x1118, 0x1118, 0x1118 }, +{ 0x1119, 0x1119, 0x1119 }, +{ 0x111A, 0x111A, 0x111A }, +{ 0x111B, 0x111B, 0x111B }, +{ 0x111C, 0x111C, 0x111C }, +{ 0x111D, 0x111D, 0x111D }, +{ 0x111E, 0x111E, 0x111E }, +{ 0x111F, 0x111F, 0x111F }, +{ 0x1120, 0x1120, 0x1120 }, +{ 0x1121, 0x1121, 0x1121 }, +{ 0x1122, 0x1122, 0x1122 }, +{ 0x1123, 0x1123, 0x1123 }, +{ 0x1124, 0x1124, 0x1124 }, +{ 0x1125, 0x1125, 0x1125 }, +{ 0x1126, 0x1126, 0x1126 }, +{ 0x1127, 0x1127, 0x1127 }, +{ 0x1128, 0x1128, 0x1128 }, +{ 0x1129, 0x1129, 0x1129 }, +{ 0x112A, 0x112A, 0x112A }, +{ 0x112B, 0x112B, 0x112B }, +{ 0x112C, 0x112C, 0x112C }, +{ 0x112D, 0x112D, 0x112D }, +{ 0x112E, 0x112E, 0x112E }, +{ 0x112F, 0x112F, 0x112F }, +{ 0x1130, 0x1130, 0x1130 }, +{ 0x1131, 0x1131, 0x1131 }, +{ 0x1132, 0x1132, 0x1132 }, +{ 0x1133, 0x1133, 0x1133 }, +{ 0x1134, 0x1134, 0x1134 }, +{ 0x1135, 0x1135, 0x1135 }, +{ 0x1136, 0x1136, 0x1136 }, +{ 0x1137, 0x1137, 0x1137 }, +{ 0x1138, 0x1138, 0x1138 }, +{ 0x1139, 0x1139, 0x1139 }, +{ 0x113A, 0x113A, 0x113A }, +{ 0x113B, 0x113B, 0x113B }, +{ 0x113C, 0x113C, 0x113C }, +{ 0x113D, 0x113D, 0x113D }, +{ 0x113E, 0x113E, 0x113E }, +{ 0x113F, 0x113F, 0x113F }, +{ 0x1140, 0x1140, 0x1140 }, +{ 0x1141, 0x1141, 0x1141 }, +{ 0x1142, 0x1142, 0x1142 }, +{ 0x1143, 0x1143, 0x1143 }, +{ 0x1144, 0x1144, 0x1144 }, +{ 0x1145, 0x1145, 0x1145 }, +{ 0x1146, 0x1146, 0x1146 }, +{ 0x1147, 0x1147, 0x1147 }, +{ 0x1148, 0x1148, 0x1148 }, +{ 0x1149, 0x1149, 0x1149 }, +{ 0x114A, 0x114A, 0x114A }, +{ 0x114B, 0x114B, 0x114B }, +{ 0x114C, 0x114C, 0x114C }, +{ 0x114D, 0x114D, 0x114D }, +{ 0x114E, 0x114E, 0x114E }, +{ 0x114F, 0x114F, 0x114F }, +{ 0x1150, 0x1150, 0x1150 }, +{ 0x1151, 0x1151, 0x1151 }, +{ 0x1152, 0x1152, 0x1152 }, +{ 0x1153, 0x1153, 0x1153 }, +{ 0x1154, 0x1154, 0x1154 }, +{ 0x1155, 0x1155, 0x1155 }, +{ 0x1156, 0x1156, 0x1156 }, +{ 0x1157, 0x1157, 0x1157 }, +{ 0x1158, 0x1158, 0x1158 }, +{ 0x1159, 0x1159, 0x1159 }, +{ 0x115F, 0x115F, 0x115F }, +{ 0x1160, 0x1160, 0x1160 }, +{ 0x1161, 0x1161, 0x1161 }, +{ 0x1162, 0x1162, 0x1162 }, +{ 0x1163, 0x1163, 0x1163 }, +{ 0x1164, 0x1164, 0x1164 }, +{ 0x1165, 0x1165, 0x1165 }, +{ 0x1166, 0x1166, 0x1166 }, +{ 0x1167, 0x1167, 0x1167 }, +{ 0x1168, 0x1168, 0x1168 }, +{ 0x1169, 0x1169, 0x1169 }, +{ 0x116A, 0x116A, 0x116A }, +{ 0x116B, 0x116B, 0x116B }, +{ 0x116C, 0x116C, 0x116C }, +{ 0x116D, 0x116D, 0x116D }, +{ 0x116E, 0x116E, 0x116E }, +{ 0x116F, 0x116F, 0x116F }, +{ 0x1170, 0x1170, 0x1170 }, +{ 0x1171, 0x1171, 0x1171 }, +{ 0x1172, 0x1172, 0x1172 }, +{ 0x1173, 0x1173, 0x1173 }, +{ 0x1174, 0x1174, 0x1174 }, +{ 0x1175, 0x1175, 0x1175 }, +{ 0x1176, 0x1176, 0x1176 }, +{ 0x1177, 0x1177, 0x1177 }, +{ 0x1178, 0x1178, 0x1178 }, +{ 0x1179, 0x1179, 0x1179 }, +{ 0x117A, 0x117A, 0x117A }, +{ 0x117B, 0x117B, 0x117B }, +{ 0x117C, 0x117C, 0x117C }, +{ 0x117D, 0x117D, 0x117D }, +{ 0x117E, 0x117E, 0x117E }, +{ 0x117F, 0x117F, 0x117F }, +{ 0x1180, 0x1180, 0x1180 }, +{ 0x1181, 0x1181, 0x1181 }, +{ 0x1182, 0x1182, 0x1182 }, +{ 0x1183, 0x1183, 0x1183 }, +{ 0x1184, 0x1184, 0x1184 }, +{ 0x1185, 0x1185, 0x1185 }, +{ 0x1186, 0x1186, 0x1186 }, +{ 0x1187, 0x1187, 0x1187 }, +{ 0x1188, 0x1188, 0x1188 }, +{ 0x1189, 0x1189, 0x1189 }, +{ 0x118A, 0x118A, 0x118A }, +{ 0x118B, 0x118B, 0x118B }, +{ 0x118C, 0x118C, 0x118C }, +{ 0x118D, 0x118D, 0x118D }, +{ 0x118E, 0x118E, 0x118E }, +{ 0x118F, 0x118F, 0x118F }, +{ 0x1190, 0x1190, 0x1190 }, +{ 0x1191, 0x1191, 0x1191 }, +{ 0x1192, 0x1192, 0x1192 }, +{ 0x1193, 0x1193, 0x1193 }, +{ 0x1194, 0x1194, 0x1194 }, +{ 0x1195, 0x1195, 0x1195 }, +{ 0x1196, 0x1196, 0x1196 }, +{ 0x1197, 0x1197, 0x1197 }, +{ 0x1198, 0x1198, 0x1198 }, +{ 0x1199, 0x1199, 0x1199 }, +{ 0x119A, 0x119A, 0x119A }, +{ 0x119B, 0x119B, 0x119B }, +{ 0x119C, 0x119C, 0x119C }, +{ 0x119D, 0x119D, 0x119D }, +{ 0x119E, 0x119E, 0x119E }, +{ 0x119F, 0x119F, 0x119F }, +{ 0x11A0, 0x11A0, 0x11A0 }, +{ 0x11A1, 0x11A1, 0x11A1 }, +{ 0x11A2, 0x11A2, 0x11A2 }, +{ 0x11A8, 0x11A8, 0x11A8 }, +{ 0x11A9, 0x11A9, 0x11A9 }, +{ 0x11AA, 0x11AA, 0x11AA }, +{ 0x11AB, 0x11AB, 0x11AB }, +{ 0x11AC, 0x11AC, 0x11AC }, +{ 0x11AD, 0x11AD, 0x11AD }, +{ 0x11AE, 0x11AE, 0x11AE }, +{ 0x11AF, 0x11AF, 0x11AF }, +{ 0x11B0, 0x11B0, 0x11B0 }, +{ 0x11B1, 0x11B1, 0x11B1 }, +{ 0x11B2, 0x11B2, 0x11B2 }, +{ 0x11B3, 0x11B3, 0x11B3 }, +{ 0x11B4, 0x11B4, 0x11B4 }, +{ 0x11B5, 0x11B5, 0x11B5 }, +{ 0x11B6, 0x11B6, 0x11B6 }, +{ 0x11B7, 0x11B7, 0x11B7 }, +{ 0x11B8, 0x11B8, 0x11B8 }, +{ 0x11B9, 0x11B9, 0x11B9 }, +{ 0x11BA, 0x11BA, 0x11BA }, +{ 0x11BB, 0x11BB, 0x11BB }, +{ 0x11BC, 0x11BC, 0x11BC }, +{ 0x11BD, 0x11BD, 0x11BD }, +{ 0x11BE, 0x11BE, 0x11BE }, +{ 0x11BF, 0x11BF, 0x11BF }, +{ 0x11C0, 0x11C0, 0x11C0 }, +{ 0x11C1, 0x11C1, 0x11C1 }, +{ 0x11C2, 0x11C2, 0x11C2 }, +{ 0x11C3, 0x11C3, 0x11C3 }, +{ 0x11C4, 0x11C4, 0x11C4 }, +{ 0x11C5, 0x11C5, 0x11C5 }, +{ 0x11C6, 0x11C6, 0x11C6 }, +{ 0x11C7, 0x11C7, 0x11C7 }, +{ 0x11C8, 0x11C8, 0x11C8 }, +{ 0x11C9, 0x11C9, 0x11C9 }, +{ 0x11CA, 0x11CA, 0x11CA }, +{ 0x11CB, 0x11CB, 0x11CB }, +{ 0x11CC, 0x11CC, 0x11CC }, +{ 0x11CD, 0x11CD, 0x11CD }, +{ 0x11CE, 0x11CE, 0x11CE }, +{ 0x11CF, 0x11CF, 0x11CF }, +{ 0x11D0, 0x11D0, 0x11D0 }, +{ 0x11D1, 0x11D1, 0x11D1 }, +{ 0x11D2, 0x11D2, 0x11D2 }, +{ 0x11D3, 0x11D3, 0x11D3 }, +{ 0x11D4, 0x11D4, 0x11D4 }, +{ 0x11D5, 0x11D5, 0x11D5 }, +{ 0x11D6, 0x11D6, 0x11D6 }, +{ 0x11D7, 0x11D7, 0x11D7 }, +{ 0x11D8, 0x11D8, 0x11D8 }, +{ 0x11D9, 0x11D9, 0x11D9 }, +{ 0x11DA, 0x11DA, 0x11DA }, +{ 0x11DB, 0x11DB, 0x11DB }, +{ 0x11DC, 0x11DC, 0x11DC }, +{ 0x11DD, 0x11DD, 0x11DD }, +{ 0x11DE, 0x11DE, 0x11DE }, +{ 0x11DF, 0x11DF, 0x11DF }, +{ 0x11E0, 0x11E0, 0x11E0 }, +{ 0x11E1, 0x11E1, 0x11E1 }, +{ 0x11E2, 0x11E2, 0x11E2 }, +{ 0x11E3, 0x11E3, 0x11E3 }, +{ 0x11E4, 0x11E4, 0x11E4 }, +{ 0x11E5, 0x11E5, 0x11E5 }, +{ 0x11E6, 0x11E6, 0x11E6 }, +{ 0x11E7, 0x11E7, 0x11E7 }, +{ 0x11E8, 0x11E8, 0x11E8 }, +{ 0x11E9, 0x11E9, 0x11E9 }, +{ 0x11EA, 0x11EA, 0x11EA }, +{ 0x11EB, 0x11EB, 0x11EB }, +{ 0x11EC, 0x11EC, 0x11EC }, +{ 0x11ED, 0x11ED, 0x11ED }, +{ 0x11EE, 0x11EE, 0x11EE }, +{ 0x11EF, 0x11EF, 0x11EF }, +{ 0x11F0, 0x11F0, 0x11F0 }, +{ 0x11F1, 0x11F1, 0x11F1 }, +{ 0x11F2, 0x11F2, 0x11F2 }, +{ 0x11F3, 0x11F3, 0x11F3 }, +{ 0x11F4, 0x11F4, 0x11F4 }, +{ 0x11F5, 0x11F5, 0x11F5 }, +{ 0x11F6, 0x11F6, 0x11F6 }, +{ 0x11F7, 0x11F7, 0x11F7 }, +{ 0x11F8, 0x11F8, 0x11F8 }, +{ 0x11F9, 0x11F9, 0x11F9 }, +{ 0x1200, 0x1200, 0x1200 }, +{ 0x1201, 0x1201, 0x1201 }, +{ 0x1202, 0x1202, 0x1202 }, +{ 0x1203, 0x1203, 0x1203 }, +{ 0x1204, 0x1204, 0x1204 }, +{ 0x1205, 0x1205, 0x1205 }, +{ 0x1206, 0x1206, 0x1206 }, +{ 0x1207, 0x1207, 0x1207 }, +{ 0x1208, 0x1208, 0x1208 }, +{ 0x1209, 0x1209, 0x1209 }, +{ 0x120A, 0x120A, 0x120A }, +{ 0x120B, 0x120B, 0x120B }, +{ 0x120C, 0x120C, 0x120C }, +{ 0x120D, 0x120D, 0x120D }, +{ 0x120E, 0x120E, 0x120E }, +{ 0x120F, 0x120F, 0x120F }, +{ 0x1210, 0x1210, 0x1210 }, +{ 0x1211, 0x1211, 0x1211 }, +{ 0x1212, 0x1212, 0x1212 }, +{ 0x1213, 0x1213, 0x1213 }, +{ 0x1214, 0x1214, 0x1214 }, +{ 0x1215, 0x1215, 0x1215 }, +{ 0x1216, 0x1216, 0x1216 }, +{ 0x1217, 0x1217, 0x1217 }, +{ 0x1218, 0x1218, 0x1218 }, +{ 0x1219, 0x1219, 0x1219 }, +{ 0x121A, 0x121A, 0x121A }, +{ 0x121B, 0x121B, 0x121B }, +{ 0x121C, 0x121C, 0x121C }, +{ 0x121D, 0x121D, 0x121D }, +{ 0x121E, 0x121E, 0x121E }, +{ 0x121F, 0x121F, 0x121F }, +{ 0x1220, 0x1220, 0x1220 }, +{ 0x1221, 0x1221, 0x1221 }, +{ 0x1222, 0x1222, 0x1222 }, +{ 0x1223, 0x1223, 0x1223 }, +{ 0x1224, 0x1224, 0x1224 }, +{ 0x1225, 0x1225, 0x1225 }, +{ 0x1226, 0x1226, 0x1226 }, +{ 0x1227, 0x1227, 0x1227 }, +{ 0x1228, 0x1228, 0x1228 }, +{ 0x1229, 0x1229, 0x1229 }, +{ 0x122A, 0x122A, 0x122A }, +{ 0x122B, 0x122B, 0x122B }, +{ 0x122C, 0x122C, 0x122C }, +{ 0x122D, 0x122D, 0x122D }, +{ 0x122E, 0x122E, 0x122E }, +{ 0x122F, 0x122F, 0x122F }, +{ 0x1230, 0x1230, 0x1230 }, +{ 0x1231, 0x1231, 0x1231 }, +{ 0x1232, 0x1232, 0x1232 }, +{ 0x1233, 0x1233, 0x1233 }, +{ 0x1234, 0x1234, 0x1234 }, +{ 0x1235, 0x1235, 0x1235 }, +{ 0x1236, 0x1236, 0x1236 }, +{ 0x1237, 0x1237, 0x1237 }, +{ 0x1238, 0x1238, 0x1238 }, +{ 0x1239, 0x1239, 0x1239 }, +{ 0x123A, 0x123A, 0x123A }, +{ 0x123B, 0x123B, 0x123B }, +{ 0x123C, 0x123C, 0x123C }, +{ 0x123D, 0x123D, 0x123D }, +{ 0x123E, 0x123E, 0x123E }, +{ 0x123F, 0x123F, 0x123F }, +{ 0x1240, 0x1240, 0x1240 }, +{ 0x1241, 0x1241, 0x1241 }, +{ 0x1242, 0x1242, 0x1242 }, +{ 0x1243, 0x1243, 0x1243 }, +{ 0x1244, 0x1244, 0x1244 }, +{ 0x1245, 0x1245, 0x1245 }, +{ 0x1246, 0x1246, 0x1246 }, +{ 0x1247, 0x1247, 0x1247 }, +{ 0x1248, 0x1248, 0x1248 }, +{ 0x124A, 0x124A, 0x124A }, +{ 0x124B, 0x124B, 0x124B }, +{ 0x124C, 0x124C, 0x124C }, +{ 0x124D, 0x124D, 0x124D }, +{ 0x1250, 0x1250, 0x1250 }, +{ 0x1251, 0x1251, 0x1251 }, +{ 0x1252, 0x1252, 0x1252 }, +{ 0x1253, 0x1253, 0x1253 }, +{ 0x1254, 0x1254, 0x1254 }, +{ 0x1255, 0x1255, 0x1255 }, +{ 0x1256, 0x1256, 0x1256 }, +{ 0x1258, 0x1258, 0x1258 }, +{ 0x125A, 0x125A, 0x125A }, +{ 0x125B, 0x125B, 0x125B }, +{ 0x125C, 0x125C, 0x125C }, +{ 0x125D, 0x125D, 0x125D }, +{ 0x1260, 0x1260, 0x1260 }, +{ 0x1261, 0x1261, 0x1261 }, +{ 0x1262, 0x1262, 0x1262 }, +{ 0x1263, 0x1263, 0x1263 }, +{ 0x1264, 0x1264, 0x1264 }, +{ 0x1265, 0x1265, 0x1265 }, +{ 0x1266, 0x1266, 0x1266 }, +{ 0x1267, 0x1267, 0x1267 }, +{ 0x1268, 0x1268, 0x1268 }, +{ 0x1269, 0x1269, 0x1269 }, +{ 0x126A, 0x126A, 0x126A }, +{ 0x126B, 0x126B, 0x126B }, +{ 0x126C, 0x126C, 0x126C }, +{ 0x126D, 0x126D, 0x126D }, +{ 0x126E, 0x126E, 0x126E }, +{ 0x126F, 0x126F, 0x126F }, +{ 0x1270, 0x1270, 0x1270 }, +{ 0x1271, 0x1271, 0x1271 }, +{ 0x1272, 0x1272, 0x1272 }, +{ 0x1273, 0x1273, 0x1273 }, +{ 0x1274, 0x1274, 0x1274 }, +{ 0x1275, 0x1275, 0x1275 }, +{ 0x1276, 0x1276, 0x1276 }, +{ 0x1277, 0x1277, 0x1277 }, +{ 0x1278, 0x1278, 0x1278 }, +{ 0x1279, 0x1279, 0x1279 }, +{ 0x127A, 0x127A, 0x127A }, +{ 0x127B, 0x127B, 0x127B }, +{ 0x127C, 0x127C, 0x127C }, +{ 0x127D, 0x127D, 0x127D }, +{ 0x127E, 0x127E, 0x127E }, +{ 0x127F, 0x127F, 0x127F }, +{ 0x1280, 0x1280, 0x1280 }, +{ 0x1281, 0x1281, 0x1281 }, +{ 0x1282, 0x1282, 0x1282 }, +{ 0x1283, 0x1283, 0x1283 }, +{ 0x1284, 0x1284, 0x1284 }, +{ 0x1285, 0x1285, 0x1285 }, +{ 0x1286, 0x1286, 0x1286 }, +{ 0x1287, 0x1287, 0x1287 }, +{ 0x1288, 0x1288, 0x1288 }, +{ 0x128A, 0x128A, 0x128A }, +{ 0x128B, 0x128B, 0x128B }, +{ 0x128C, 0x128C, 0x128C }, +{ 0x128D, 0x128D, 0x128D }, +{ 0x1290, 0x1290, 0x1290 }, +{ 0x1291, 0x1291, 0x1291 }, +{ 0x1292, 0x1292, 0x1292 }, +{ 0x1293, 0x1293, 0x1293 }, +{ 0x1294, 0x1294, 0x1294 }, +{ 0x1295, 0x1295, 0x1295 }, +{ 0x1296, 0x1296, 0x1296 }, +{ 0x1297, 0x1297, 0x1297 }, +{ 0x1298, 0x1298, 0x1298 }, +{ 0x1299, 0x1299, 0x1299 }, +{ 0x129A, 0x129A, 0x129A }, +{ 0x129B, 0x129B, 0x129B }, +{ 0x129C, 0x129C, 0x129C }, +{ 0x129D, 0x129D, 0x129D }, +{ 0x129E, 0x129E, 0x129E }, +{ 0x129F, 0x129F, 0x129F }, +{ 0x12A0, 0x12A0, 0x12A0 }, +{ 0x12A1, 0x12A1, 0x12A1 }, +{ 0x12A2, 0x12A2, 0x12A2 }, +{ 0x12A3, 0x12A3, 0x12A3 }, +{ 0x12A4, 0x12A4, 0x12A4 }, +{ 0x12A5, 0x12A5, 0x12A5 }, +{ 0x12A6, 0x12A6, 0x12A6 }, +{ 0x12A7, 0x12A7, 0x12A7 }, +{ 0x12A8, 0x12A8, 0x12A8 }, +{ 0x12A9, 0x12A9, 0x12A9 }, +{ 0x12AA, 0x12AA, 0x12AA }, +{ 0x12AB, 0x12AB, 0x12AB }, +{ 0x12AC, 0x12AC, 0x12AC }, +{ 0x12AD, 0x12AD, 0x12AD }, +{ 0x12AE, 0x12AE, 0x12AE }, +{ 0x12AF, 0x12AF, 0x12AF }, +{ 0x12B0, 0x12B0, 0x12B0 }, +{ 0x12B2, 0x12B2, 0x12B2 }, +{ 0x12B3, 0x12B3, 0x12B3 }, +{ 0x12B4, 0x12B4, 0x12B4 }, +{ 0x12B5, 0x12B5, 0x12B5 }, +{ 0x12B8, 0x12B8, 0x12B8 }, +{ 0x12B9, 0x12B9, 0x12B9 }, +{ 0x12BA, 0x12BA, 0x12BA }, +{ 0x12BB, 0x12BB, 0x12BB }, +{ 0x12BC, 0x12BC, 0x12BC }, +{ 0x12BD, 0x12BD, 0x12BD }, +{ 0x12BE, 0x12BE, 0x12BE }, +{ 0x12C0, 0x12C0, 0x12C0 }, +{ 0x12C2, 0x12C2, 0x12C2 }, +{ 0x12C3, 0x12C3, 0x12C3 }, +{ 0x12C4, 0x12C4, 0x12C4 }, +{ 0x12C5, 0x12C5, 0x12C5 }, +{ 0x12C8, 0x12C8, 0x12C8 }, +{ 0x12C9, 0x12C9, 0x12C9 }, +{ 0x12CA, 0x12CA, 0x12CA }, +{ 0x12CB, 0x12CB, 0x12CB }, +{ 0x12CC, 0x12CC, 0x12CC }, +{ 0x12CD, 0x12CD, 0x12CD }, +{ 0x12CE, 0x12CE, 0x12CE }, +{ 0x12CF, 0x12CF, 0x12CF }, +{ 0x12D0, 0x12D0, 0x12D0 }, +{ 0x12D1, 0x12D1, 0x12D1 }, +{ 0x12D2, 0x12D2, 0x12D2 }, +{ 0x12D3, 0x12D3, 0x12D3 }, +{ 0x12D4, 0x12D4, 0x12D4 }, +{ 0x12D5, 0x12D5, 0x12D5 }, +{ 0x12D6, 0x12D6, 0x12D6 }, +{ 0x12D8, 0x12D8, 0x12D8 }, +{ 0x12D9, 0x12D9, 0x12D9 }, +{ 0x12DA, 0x12DA, 0x12DA }, +{ 0x12DB, 0x12DB, 0x12DB }, +{ 0x12DC, 0x12DC, 0x12DC }, +{ 0x12DD, 0x12DD, 0x12DD }, +{ 0x12DE, 0x12DE, 0x12DE }, +{ 0x12DF, 0x12DF, 0x12DF }, +{ 0x12E0, 0x12E0, 0x12E0 }, +{ 0x12E1, 0x12E1, 0x12E1 }, +{ 0x12E2, 0x12E2, 0x12E2 }, +{ 0x12E3, 0x12E3, 0x12E3 }, +{ 0x12E4, 0x12E4, 0x12E4 }, +{ 0x12E5, 0x12E5, 0x12E5 }, +{ 0x12E6, 0x12E6, 0x12E6 }, +{ 0x12E7, 0x12E7, 0x12E7 }, +{ 0x12E8, 0x12E8, 0x12E8 }, +{ 0x12E9, 0x12E9, 0x12E9 }, +{ 0x12EA, 0x12EA, 0x12EA }, +{ 0x12EB, 0x12EB, 0x12EB }, +{ 0x12EC, 0x12EC, 0x12EC }, +{ 0x12ED, 0x12ED, 0x12ED }, +{ 0x12EE, 0x12EE, 0x12EE }, +{ 0x12EF, 0x12EF, 0x12EF }, +{ 0x12F0, 0x12F0, 0x12F0 }, +{ 0x12F1, 0x12F1, 0x12F1 }, +{ 0x12F2, 0x12F2, 0x12F2 }, +{ 0x12F3, 0x12F3, 0x12F3 }, +{ 0x12F4, 0x12F4, 0x12F4 }, +{ 0x12F5, 0x12F5, 0x12F5 }, +{ 0x12F6, 0x12F6, 0x12F6 }, +{ 0x12F7, 0x12F7, 0x12F7 }, +{ 0x12F8, 0x12F8, 0x12F8 }, +{ 0x12F9, 0x12F9, 0x12F9 }, +{ 0x12FA, 0x12FA, 0x12FA }, +{ 0x12FB, 0x12FB, 0x12FB }, +{ 0x12FC, 0x12FC, 0x12FC }, +{ 0x12FD, 0x12FD, 0x12FD }, +{ 0x12FE, 0x12FE, 0x12FE }, +{ 0x12FF, 0x12FF, 0x12FF }, +{ 0x1300, 0x1300, 0x1300 }, +{ 0x1301, 0x1301, 0x1301 }, +{ 0x1302, 0x1302, 0x1302 }, +{ 0x1303, 0x1303, 0x1303 }, +{ 0x1304, 0x1304, 0x1304 }, +{ 0x1305, 0x1305, 0x1305 }, +{ 0x1306, 0x1306, 0x1306 }, +{ 0x1307, 0x1307, 0x1307 }, +{ 0x1308, 0x1308, 0x1308 }, +{ 0x1309, 0x1309, 0x1309 }, +{ 0x130A, 0x130A, 0x130A }, +{ 0x130B, 0x130B, 0x130B }, +{ 0x130C, 0x130C, 0x130C }, +{ 0x130D, 0x130D, 0x130D }, +{ 0x130E, 0x130E, 0x130E }, +{ 0x130F, 0x130F, 0x130F }, +{ 0x1310, 0x1310, 0x1310 }, +{ 0x1312, 0x1312, 0x1312 }, +{ 0x1313, 0x1313, 0x1313 }, +{ 0x1314, 0x1314, 0x1314 }, +{ 0x1315, 0x1315, 0x1315 }, +{ 0x1318, 0x1318, 0x1318 }, +{ 0x1319, 0x1319, 0x1319 }, +{ 0x131A, 0x131A, 0x131A }, +{ 0x131B, 0x131B, 0x131B }, +{ 0x131C, 0x131C, 0x131C }, +{ 0x131D, 0x131D, 0x131D }, +{ 0x131E, 0x131E, 0x131E }, +{ 0x131F, 0x131F, 0x131F }, +{ 0x1320, 0x1320, 0x1320 }, +{ 0x1321, 0x1321, 0x1321 }, +{ 0x1322, 0x1322, 0x1322 }, +{ 0x1323, 0x1323, 0x1323 }, +{ 0x1324, 0x1324, 0x1324 }, +{ 0x1325, 0x1325, 0x1325 }, +{ 0x1326, 0x1326, 0x1326 }, +{ 0x1327, 0x1327, 0x1327 }, +{ 0x1328, 0x1328, 0x1328 }, +{ 0x1329, 0x1329, 0x1329 }, +{ 0x132A, 0x132A, 0x132A }, +{ 0x132B, 0x132B, 0x132B }, +{ 0x132C, 0x132C, 0x132C }, +{ 0x132D, 0x132D, 0x132D }, +{ 0x132E, 0x132E, 0x132E }, +{ 0x132F, 0x132F, 0x132F }, +{ 0x1330, 0x1330, 0x1330 }, +{ 0x1331, 0x1331, 0x1331 }, +{ 0x1332, 0x1332, 0x1332 }, +{ 0x1333, 0x1333, 0x1333 }, +{ 0x1334, 0x1334, 0x1334 }, +{ 0x1335, 0x1335, 0x1335 }, +{ 0x1336, 0x1336, 0x1336 }, +{ 0x1337, 0x1337, 0x1337 }, +{ 0x1338, 0x1338, 0x1338 }, +{ 0x1339, 0x1339, 0x1339 }, +{ 0x133A, 0x133A, 0x133A }, +{ 0x133B, 0x133B, 0x133B }, +{ 0x133C, 0x133C, 0x133C }, +{ 0x133D, 0x133D, 0x133D }, +{ 0x133E, 0x133E, 0x133E }, +{ 0x133F, 0x133F, 0x133F }, +{ 0x1340, 0x1340, 0x1340 }, +{ 0x1341, 0x1341, 0x1341 }, +{ 0x1342, 0x1342, 0x1342 }, +{ 0x1343, 0x1343, 0x1343 }, +{ 0x1344, 0x1344, 0x1344 }, +{ 0x1345, 0x1345, 0x1345 }, +{ 0x1346, 0x1346, 0x1346 }, +{ 0x1347, 0x1347, 0x1347 }, +{ 0x1348, 0x1348, 0x1348 }, +{ 0x1349, 0x1349, 0x1349 }, +{ 0x134A, 0x134A, 0x134A }, +{ 0x134B, 0x134B, 0x134B }, +{ 0x134C, 0x134C, 0x134C }, +{ 0x134D, 0x134D, 0x134D }, +{ 0x134E, 0x134E, 0x134E }, +{ 0x134F, 0x134F, 0x134F }, +{ 0x1350, 0x1350, 0x1350 }, +{ 0x1351, 0x1351, 0x1351 }, +{ 0x1352, 0x1352, 0x1352 }, +{ 0x1353, 0x1353, 0x1353 }, +{ 0x1354, 0x1354, 0x1354 }, +{ 0x1355, 0x1355, 0x1355 }, +{ 0x1356, 0x1356, 0x1356 }, +{ 0x1357, 0x1357, 0x1357 }, +{ 0x1358, 0x1358, 0x1358 }, +{ 0x1359, 0x1359, 0x1359 }, +{ 0x135A, 0x135A, 0x135A }, +{ 0x135F, 0x135F, 0x135F }, +{ 0x1380, 0x1380, 0x1380 }, +{ 0x1381, 0x1381, 0x1381 }, +{ 0x1382, 0x1382, 0x1382 }, +{ 0x1383, 0x1383, 0x1383 }, +{ 0x1384, 0x1384, 0x1384 }, +{ 0x1385, 0x1385, 0x1385 }, +{ 0x1386, 0x1386, 0x1386 }, +{ 0x1387, 0x1387, 0x1387 }, +{ 0x1388, 0x1388, 0x1388 }, +{ 0x1389, 0x1389, 0x1389 }, +{ 0x138A, 0x138A, 0x138A }, +{ 0x138B, 0x138B, 0x138B }, +{ 0x138C, 0x138C, 0x138C }, +{ 0x138D, 0x138D, 0x138D }, +{ 0x138E, 0x138E, 0x138E }, +{ 0x138F, 0x138F, 0x138F }, +{ 0x13A0, 0x13A0, 0x13A0 }, +{ 0x13A1, 0x13A1, 0x13A1 }, +{ 0x13A2, 0x13A2, 0x13A2 }, +{ 0x13A3, 0x13A3, 0x13A3 }, +{ 0x13A4, 0x13A4, 0x13A4 }, +{ 0x13A5, 0x13A5, 0x13A5 }, +{ 0x13A6, 0x13A6, 0x13A6 }, +{ 0x13A7, 0x13A7, 0x13A7 }, +{ 0x13A8, 0x13A8, 0x13A8 }, +{ 0x13A9, 0x13A9, 0x13A9 }, +{ 0x13AA, 0x13AA, 0x13AA }, +{ 0x13AB, 0x13AB, 0x13AB }, +{ 0x13AC, 0x13AC, 0x13AC }, +{ 0x13AD, 0x13AD, 0x13AD }, +{ 0x13AE, 0x13AE, 0x13AE }, +{ 0x13AF, 0x13AF, 0x13AF }, +{ 0x13B0, 0x13B0, 0x13B0 }, +{ 0x13B1, 0x13B1, 0x13B1 }, +{ 0x13B2, 0x13B2, 0x13B2 }, +{ 0x13B3, 0x13B3, 0x13B3 }, +{ 0x13B4, 0x13B4, 0x13B4 }, +{ 0x13B5, 0x13B5, 0x13B5 }, +{ 0x13B6, 0x13B6, 0x13B6 }, +{ 0x13B7, 0x13B7, 0x13B7 }, +{ 0x13B8, 0x13B8, 0x13B8 }, +{ 0x13B9, 0x13B9, 0x13B9 }, +{ 0x13BA, 0x13BA, 0x13BA }, +{ 0x13BB, 0x13BB, 0x13BB }, +{ 0x13BC, 0x13BC, 0x13BC }, +{ 0x13BD, 0x13BD, 0x13BD }, +{ 0x13BE, 0x13BE, 0x13BE }, +{ 0x13BF, 0x13BF, 0x13BF }, +{ 0x13C0, 0x13C0, 0x13C0 }, +{ 0x13C1, 0x13C1, 0x13C1 }, +{ 0x13C2, 0x13C2, 0x13C2 }, +{ 0x13C3, 0x13C3, 0x13C3 }, +{ 0x13C4, 0x13C4, 0x13C4 }, +{ 0x13C5, 0x13C5, 0x13C5 }, +{ 0x13C6, 0x13C6, 0x13C6 }, +{ 0x13C7, 0x13C7, 0x13C7 }, +{ 0x13C8, 0x13C8, 0x13C8 }, +{ 0x13C9, 0x13C9, 0x13C9 }, +{ 0x13CA, 0x13CA, 0x13CA }, +{ 0x13CB, 0x13CB, 0x13CB }, +{ 0x13CC, 0x13CC, 0x13CC }, +{ 0x13CD, 0x13CD, 0x13CD }, +{ 0x13CE, 0x13CE, 0x13CE }, +{ 0x13CF, 0x13CF, 0x13CF }, +{ 0x13D0, 0x13D0, 0x13D0 }, +{ 0x13D1, 0x13D1, 0x13D1 }, +{ 0x13D2, 0x13D2, 0x13D2 }, +{ 0x13D3, 0x13D3, 0x13D3 }, +{ 0x13D4, 0x13D4, 0x13D4 }, +{ 0x13D5, 0x13D5, 0x13D5 }, +{ 0x13D6, 0x13D6, 0x13D6 }, +{ 0x13D7, 0x13D7, 0x13D7 }, +{ 0x13D8, 0x13D8, 0x13D8 }, +{ 0x13D9, 0x13D9, 0x13D9 }, +{ 0x13DA, 0x13DA, 0x13DA }, +{ 0x13DB, 0x13DB, 0x13DB }, +{ 0x13DC, 0x13DC, 0x13DC }, +{ 0x13DD, 0x13DD, 0x13DD }, +{ 0x13DE, 0x13DE, 0x13DE }, +{ 0x13DF, 0x13DF, 0x13DF }, +{ 0x13E0, 0x13E0, 0x13E0 }, +{ 0x13E1, 0x13E1, 0x13E1 }, +{ 0x13E2, 0x13E2, 0x13E2 }, +{ 0x13E3, 0x13E3, 0x13E3 }, +{ 0x13E4, 0x13E4, 0x13E4 }, +{ 0x13E5, 0x13E5, 0x13E5 }, +{ 0x13E6, 0x13E6, 0x13E6 }, +{ 0x13E7, 0x13E7, 0x13E7 }, +{ 0x13E8, 0x13E8, 0x13E8 }, +{ 0x13E9, 0x13E9, 0x13E9 }, +{ 0x13EA, 0x13EA, 0x13EA }, +{ 0x13EB, 0x13EB, 0x13EB }, +{ 0x13EC, 0x13EC, 0x13EC }, +{ 0x13ED, 0x13ED, 0x13ED }, +{ 0x13EE, 0x13EE, 0x13EE }, +{ 0x13EF, 0x13EF, 0x13EF }, +{ 0x13F0, 0x13F0, 0x13F0 }, +{ 0x13F1, 0x13F1, 0x13F1 }, +{ 0x13F2, 0x13F2, 0x13F2 }, +{ 0x13F3, 0x13F3, 0x13F3 }, +{ 0x13F4, 0x13F4, 0x13F4 }, +{ 0x1401, 0x1401, 0x1401 }, +{ 0x1402, 0x1402, 0x1402 }, +{ 0x1403, 0x1403, 0x1403 }, +{ 0x1404, 0x1404, 0x1404 }, +{ 0x1405, 0x1405, 0x1405 }, +{ 0x1406, 0x1406, 0x1406 }, +{ 0x1407, 0x1407, 0x1407 }, +{ 0x1408, 0x1408, 0x1408 }, +{ 0x1409, 0x1409, 0x1409 }, +{ 0x140A, 0x140A, 0x140A }, +{ 0x140B, 0x140B, 0x140B }, +{ 0x140C, 0x140C, 0x140C }, +{ 0x140D, 0x140D, 0x140D }, +{ 0x140E, 0x140E, 0x140E }, +{ 0x140F, 0x140F, 0x140F }, +{ 0x1410, 0x1410, 0x1410 }, +{ 0x1411, 0x1411, 0x1411 }, +{ 0x1412, 0x1412, 0x1412 }, +{ 0x1413, 0x1413, 0x1413 }, +{ 0x1414, 0x1414, 0x1414 }, +{ 0x1415, 0x1415, 0x1415 }, +{ 0x1416, 0x1416, 0x1416 }, +{ 0x1417, 0x1417, 0x1417 }, +{ 0x1418, 0x1418, 0x1418 }, +{ 0x1419, 0x1419, 0x1419 }, +{ 0x141A, 0x141A, 0x141A }, +{ 0x141B, 0x141B, 0x141B }, +{ 0x141C, 0x141C, 0x141C }, +{ 0x141D, 0x141D, 0x141D }, +{ 0x141E, 0x141E, 0x141E }, +{ 0x141F, 0x141F, 0x141F }, +{ 0x1420, 0x1420, 0x1420 }, +{ 0x1421, 0x1421, 0x1421 }, +{ 0x1422, 0x1422, 0x1422 }, +{ 0x1423, 0x1423, 0x1423 }, +{ 0x1424, 0x1424, 0x1424 }, +{ 0x1425, 0x1425, 0x1425 }, +{ 0x1426, 0x1426, 0x1426 }, +{ 0x1427, 0x1427, 0x1427 }, +{ 0x1428, 0x1428, 0x1428 }, +{ 0x1429, 0x1429, 0x1429 }, +{ 0x142A, 0x142A, 0x142A }, +{ 0x142B, 0x142B, 0x142B }, +{ 0x142C, 0x142C, 0x142C }, +{ 0x142D, 0x142D, 0x142D }, +{ 0x142E, 0x142E, 0x142E }, +{ 0x142F, 0x142F, 0x142F }, +{ 0x1430, 0x1430, 0x1430 }, +{ 0x1431, 0x1431, 0x1431 }, +{ 0x1432, 0x1432, 0x1432 }, +{ 0x1433, 0x1433, 0x1433 }, +{ 0x1434, 0x1434, 0x1434 }, +{ 0x1435, 0x1435, 0x1435 }, +{ 0x1436, 0x1436, 0x1436 }, +{ 0x1437, 0x1437, 0x1437 }, +{ 0x1438, 0x1438, 0x1438 }, +{ 0x1439, 0x1439, 0x1439 }, +{ 0x143A, 0x143A, 0x143A }, +{ 0x143B, 0x143B, 0x143B }, +{ 0x143C, 0x143C, 0x143C }, +{ 0x143D, 0x143D, 0x143D }, +{ 0x143E, 0x143E, 0x143E }, +{ 0x143F, 0x143F, 0x143F }, +{ 0x1440, 0x1440, 0x1440 }, +{ 0x1441, 0x1441, 0x1441 }, +{ 0x1442, 0x1442, 0x1442 }, +{ 0x1443, 0x1443, 0x1443 }, +{ 0x1444, 0x1444, 0x1444 }, +{ 0x1445, 0x1445, 0x1445 }, +{ 0x1446, 0x1446, 0x1446 }, +{ 0x1447, 0x1447, 0x1447 }, +{ 0x1448, 0x1448, 0x1448 }, +{ 0x1449, 0x1449, 0x1449 }, +{ 0x144A, 0x144A, 0x144A }, +{ 0x144B, 0x144B, 0x144B }, +{ 0x144C, 0x144C, 0x144C }, +{ 0x144D, 0x144D, 0x144D }, +{ 0x144E, 0x144E, 0x144E }, +{ 0x144F, 0x144F, 0x144F }, +{ 0x1450, 0x1450, 0x1450 }, +{ 0x1451, 0x1451, 0x1451 }, +{ 0x1452, 0x1452, 0x1452 }, +{ 0x1453, 0x1453, 0x1453 }, +{ 0x1454, 0x1454, 0x1454 }, +{ 0x1455, 0x1455, 0x1455 }, +{ 0x1456, 0x1456, 0x1456 }, +{ 0x1457, 0x1457, 0x1457 }, +{ 0x1458, 0x1458, 0x1458 }, +{ 0x1459, 0x1459, 0x1459 }, +{ 0x145A, 0x145A, 0x145A }, +{ 0x145B, 0x145B, 0x145B }, +{ 0x145C, 0x145C, 0x145C }, +{ 0x145D, 0x145D, 0x145D }, +{ 0x145E, 0x145E, 0x145E }, +{ 0x145F, 0x145F, 0x145F }, +{ 0x1460, 0x1460, 0x1460 }, +{ 0x1461, 0x1461, 0x1461 }, +{ 0x1462, 0x1462, 0x1462 }, +{ 0x1463, 0x1463, 0x1463 }, +{ 0x1464, 0x1464, 0x1464 }, +{ 0x1465, 0x1465, 0x1465 }, +{ 0x1466, 0x1466, 0x1466 }, +{ 0x1467, 0x1467, 0x1467 }, +{ 0x1468, 0x1468, 0x1468 }, +{ 0x1469, 0x1469, 0x1469 }, +{ 0x146A, 0x146A, 0x146A }, +{ 0x146B, 0x146B, 0x146B }, +{ 0x146C, 0x146C, 0x146C }, +{ 0x146D, 0x146D, 0x146D }, +{ 0x146E, 0x146E, 0x146E }, +{ 0x146F, 0x146F, 0x146F }, +{ 0x1470, 0x1470, 0x1470 }, +{ 0x1471, 0x1471, 0x1471 }, +{ 0x1472, 0x1472, 0x1472 }, +{ 0x1473, 0x1473, 0x1473 }, +{ 0x1474, 0x1474, 0x1474 }, +{ 0x1475, 0x1475, 0x1475 }, +{ 0x1476, 0x1476, 0x1476 }, +{ 0x1477, 0x1477, 0x1477 }, +{ 0x1478, 0x1478, 0x1478 }, +{ 0x1479, 0x1479, 0x1479 }, +{ 0x147A, 0x147A, 0x147A }, +{ 0x147B, 0x147B, 0x147B }, +{ 0x147C, 0x147C, 0x147C }, +{ 0x147D, 0x147D, 0x147D }, +{ 0x147E, 0x147E, 0x147E }, +{ 0x147F, 0x147F, 0x147F }, +{ 0x1480, 0x1480, 0x1480 }, +{ 0x1481, 0x1481, 0x1481 }, +{ 0x1482, 0x1482, 0x1482 }, +{ 0x1483, 0x1483, 0x1483 }, +{ 0x1484, 0x1484, 0x1484 }, +{ 0x1485, 0x1485, 0x1485 }, +{ 0x1486, 0x1486, 0x1486 }, +{ 0x1487, 0x1487, 0x1487 }, +{ 0x1488, 0x1488, 0x1488 }, +{ 0x1489, 0x1489, 0x1489 }, +{ 0x148A, 0x148A, 0x148A }, +{ 0x148B, 0x148B, 0x148B }, +{ 0x148C, 0x148C, 0x148C }, +{ 0x148D, 0x148D, 0x148D }, +{ 0x148E, 0x148E, 0x148E }, +{ 0x148F, 0x148F, 0x148F }, +{ 0x1490, 0x1490, 0x1490 }, +{ 0x1491, 0x1491, 0x1491 }, +{ 0x1492, 0x1492, 0x1492 }, +{ 0x1493, 0x1493, 0x1493 }, +{ 0x1494, 0x1494, 0x1494 }, +{ 0x1495, 0x1495, 0x1495 }, +{ 0x1496, 0x1496, 0x1496 }, +{ 0x1497, 0x1497, 0x1497 }, +{ 0x1498, 0x1498, 0x1498 }, +{ 0x1499, 0x1499, 0x1499 }, +{ 0x149A, 0x149A, 0x149A }, +{ 0x149B, 0x149B, 0x149B }, +{ 0x149C, 0x149C, 0x149C }, +{ 0x149D, 0x149D, 0x149D }, +{ 0x149E, 0x149E, 0x149E }, +{ 0x149F, 0x149F, 0x149F }, +{ 0x14A0, 0x14A0, 0x14A0 }, +{ 0x14A1, 0x14A1, 0x14A1 }, +{ 0x14A2, 0x14A2, 0x14A2 }, +{ 0x14A3, 0x14A3, 0x14A3 }, +{ 0x14A4, 0x14A4, 0x14A4 }, +{ 0x14A5, 0x14A5, 0x14A5 }, +{ 0x14A6, 0x14A6, 0x14A6 }, +{ 0x14A7, 0x14A7, 0x14A7 }, +{ 0x14A8, 0x14A8, 0x14A8 }, +{ 0x14A9, 0x14A9, 0x14A9 }, +{ 0x14AA, 0x14AA, 0x14AA }, +{ 0x14AB, 0x14AB, 0x14AB }, +{ 0x14AC, 0x14AC, 0x14AC }, +{ 0x14AD, 0x14AD, 0x14AD }, +{ 0x14AE, 0x14AE, 0x14AE }, +{ 0x14AF, 0x14AF, 0x14AF }, +{ 0x14B0, 0x14B0, 0x14B0 }, +{ 0x14B1, 0x14B1, 0x14B1 }, +{ 0x14B2, 0x14B2, 0x14B2 }, +{ 0x14B3, 0x14B3, 0x14B3 }, +{ 0x14B4, 0x14B4, 0x14B4 }, +{ 0x14B5, 0x14B5, 0x14B5 }, +{ 0x14B6, 0x14B6, 0x14B6 }, +{ 0x14B7, 0x14B7, 0x14B7 }, +{ 0x14B8, 0x14B8, 0x14B8 }, +{ 0x14B9, 0x14B9, 0x14B9 }, +{ 0x14BA, 0x14BA, 0x14BA }, +{ 0x14BB, 0x14BB, 0x14BB }, +{ 0x14BC, 0x14BC, 0x14BC }, +{ 0x14BD, 0x14BD, 0x14BD }, +{ 0x14BE, 0x14BE, 0x14BE }, +{ 0x14BF, 0x14BF, 0x14BF }, +{ 0x14C0, 0x14C0, 0x14C0 }, +{ 0x14C1, 0x14C1, 0x14C1 }, +{ 0x14C2, 0x14C2, 0x14C2 }, +{ 0x14C3, 0x14C3, 0x14C3 }, +{ 0x14C4, 0x14C4, 0x14C4 }, +{ 0x14C5, 0x14C5, 0x14C5 }, +{ 0x14C6, 0x14C6, 0x14C6 }, +{ 0x14C7, 0x14C7, 0x14C7 }, +{ 0x14C8, 0x14C8, 0x14C8 }, +{ 0x14C9, 0x14C9, 0x14C9 }, +{ 0x14CA, 0x14CA, 0x14CA }, +{ 0x14CB, 0x14CB, 0x14CB }, +{ 0x14CC, 0x14CC, 0x14CC }, +{ 0x14CD, 0x14CD, 0x14CD }, +{ 0x14CE, 0x14CE, 0x14CE }, +{ 0x14CF, 0x14CF, 0x14CF }, +{ 0x14D0, 0x14D0, 0x14D0 }, +{ 0x14D1, 0x14D1, 0x14D1 }, +{ 0x14D2, 0x14D2, 0x14D2 }, +{ 0x14D3, 0x14D3, 0x14D3 }, +{ 0x14D4, 0x14D4, 0x14D4 }, +{ 0x14D5, 0x14D5, 0x14D5 }, +{ 0x14D6, 0x14D6, 0x14D6 }, +{ 0x14D7, 0x14D7, 0x14D7 }, +{ 0x14D8, 0x14D8, 0x14D8 }, +{ 0x14D9, 0x14D9, 0x14D9 }, +{ 0x14DA, 0x14DA, 0x14DA }, +{ 0x14DB, 0x14DB, 0x14DB }, +{ 0x14DC, 0x14DC, 0x14DC }, +{ 0x14DD, 0x14DD, 0x14DD }, +{ 0x14DE, 0x14DE, 0x14DE }, +{ 0x14DF, 0x14DF, 0x14DF }, +{ 0x14E0, 0x14E0, 0x14E0 }, +{ 0x14E1, 0x14E1, 0x14E1 }, +{ 0x14E2, 0x14E2, 0x14E2 }, +{ 0x14E3, 0x14E3, 0x14E3 }, +{ 0x14E4, 0x14E4, 0x14E4 }, +{ 0x14E5, 0x14E5, 0x14E5 }, +{ 0x14E6, 0x14E6, 0x14E6 }, +{ 0x14E7, 0x14E7, 0x14E7 }, +{ 0x14E8, 0x14E8, 0x14E8 }, +{ 0x14E9, 0x14E9, 0x14E9 }, +{ 0x14EA, 0x14EA, 0x14EA }, +{ 0x14EB, 0x14EB, 0x14EB }, +{ 0x14EC, 0x14EC, 0x14EC }, +{ 0x14ED, 0x14ED, 0x14ED }, +{ 0x14EE, 0x14EE, 0x14EE }, +{ 0x14EF, 0x14EF, 0x14EF }, +{ 0x14F0, 0x14F0, 0x14F0 }, +{ 0x14F1, 0x14F1, 0x14F1 }, +{ 0x14F2, 0x14F2, 0x14F2 }, +{ 0x14F3, 0x14F3, 0x14F3 }, +{ 0x14F4, 0x14F4, 0x14F4 }, +{ 0x14F5, 0x14F5, 0x14F5 }, +{ 0x14F6, 0x14F6, 0x14F6 }, +{ 0x14F7, 0x14F7, 0x14F7 }, +{ 0x14F8, 0x14F8, 0x14F8 }, +{ 0x14F9, 0x14F9, 0x14F9 }, +{ 0x14FA, 0x14FA, 0x14FA }, +{ 0x14FB, 0x14FB, 0x14FB }, +{ 0x14FC, 0x14FC, 0x14FC }, +{ 0x14FD, 0x14FD, 0x14FD }, +{ 0x14FE, 0x14FE, 0x14FE }, +{ 0x14FF, 0x14FF, 0x14FF }, +{ 0x1500, 0x1500, 0x1500 }, +{ 0x1501, 0x1501, 0x1501 }, +{ 0x1502, 0x1502, 0x1502 }, +{ 0x1503, 0x1503, 0x1503 }, +{ 0x1504, 0x1504, 0x1504 }, +{ 0x1505, 0x1505, 0x1505 }, +{ 0x1506, 0x1506, 0x1506 }, +{ 0x1507, 0x1507, 0x1507 }, +{ 0x1508, 0x1508, 0x1508 }, +{ 0x1509, 0x1509, 0x1509 }, +{ 0x150A, 0x150A, 0x150A }, +{ 0x150B, 0x150B, 0x150B }, +{ 0x150C, 0x150C, 0x150C }, +{ 0x150D, 0x150D, 0x150D }, +{ 0x150E, 0x150E, 0x150E }, +{ 0x150F, 0x150F, 0x150F }, +{ 0x1510, 0x1510, 0x1510 }, +{ 0x1511, 0x1511, 0x1511 }, +{ 0x1512, 0x1512, 0x1512 }, +{ 0x1513, 0x1513, 0x1513 }, +{ 0x1514, 0x1514, 0x1514 }, +{ 0x1515, 0x1515, 0x1515 }, +{ 0x1516, 0x1516, 0x1516 }, +{ 0x1517, 0x1517, 0x1517 }, +{ 0x1518, 0x1518, 0x1518 }, +{ 0x1519, 0x1519, 0x1519 }, +{ 0x151A, 0x151A, 0x151A }, +{ 0x151B, 0x151B, 0x151B }, +{ 0x151C, 0x151C, 0x151C }, +{ 0x151D, 0x151D, 0x151D }, +{ 0x151E, 0x151E, 0x151E }, +{ 0x151F, 0x151F, 0x151F }, +{ 0x1520, 0x1520, 0x1520 }, +{ 0x1521, 0x1521, 0x1521 }, +{ 0x1522, 0x1522, 0x1522 }, +{ 0x1523, 0x1523, 0x1523 }, +{ 0x1524, 0x1524, 0x1524 }, +{ 0x1525, 0x1525, 0x1525 }, +{ 0x1526, 0x1526, 0x1526 }, +{ 0x1527, 0x1527, 0x1527 }, +{ 0x1528, 0x1528, 0x1528 }, +{ 0x1529, 0x1529, 0x1529 }, +{ 0x152A, 0x152A, 0x152A }, +{ 0x152B, 0x152B, 0x152B }, +{ 0x152C, 0x152C, 0x152C }, +{ 0x152D, 0x152D, 0x152D }, +{ 0x152E, 0x152E, 0x152E }, +{ 0x152F, 0x152F, 0x152F }, +{ 0x1530, 0x1530, 0x1530 }, +{ 0x1531, 0x1531, 0x1531 }, +{ 0x1532, 0x1532, 0x1532 }, +{ 0x1533, 0x1533, 0x1533 }, +{ 0x1534, 0x1534, 0x1534 }, +{ 0x1535, 0x1535, 0x1535 }, +{ 0x1536, 0x1536, 0x1536 }, +{ 0x1537, 0x1537, 0x1537 }, +{ 0x1538, 0x1538, 0x1538 }, +{ 0x1539, 0x1539, 0x1539 }, +{ 0x153A, 0x153A, 0x153A }, +{ 0x153B, 0x153B, 0x153B }, +{ 0x153C, 0x153C, 0x153C }, +{ 0x153D, 0x153D, 0x153D }, +{ 0x153E, 0x153E, 0x153E }, +{ 0x153F, 0x153F, 0x153F }, +{ 0x1540, 0x1540, 0x1540 }, +{ 0x1541, 0x1541, 0x1541 }, +{ 0x1542, 0x1542, 0x1542 }, +{ 0x1543, 0x1543, 0x1543 }, +{ 0x1544, 0x1544, 0x1544 }, +{ 0x1545, 0x1545, 0x1545 }, +{ 0x1546, 0x1546, 0x1546 }, +{ 0x1547, 0x1547, 0x1547 }, +{ 0x1548, 0x1548, 0x1548 }, +{ 0x1549, 0x1549, 0x1549 }, +{ 0x154A, 0x154A, 0x154A }, +{ 0x154B, 0x154B, 0x154B }, +{ 0x154C, 0x154C, 0x154C }, +{ 0x154D, 0x154D, 0x154D }, +{ 0x154E, 0x154E, 0x154E }, +{ 0x154F, 0x154F, 0x154F }, +{ 0x1550, 0x1550, 0x1550 }, +{ 0x1551, 0x1551, 0x1551 }, +{ 0x1552, 0x1552, 0x1552 }, +{ 0x1553, 0x1553, 0x1553 }, +{ 0x1554, 0x1554, 0x1554 }, +{ 0x1555, 0x1555, 0x1555 }, +{ 0x1556, 0x1556, 0x1556 }, +{ 0x1557, 0x1557, 0x1557 }, +{ 0x1558, 0x1558, 0x1558 }, +{ 0x1559, 0x1559, 0x1559 }, +{ 0x155A, 0x155A, 0x155A }, +{ 0x155B, 0x155B, 0x155B }, +{ 0x155C, 0x155C, 0x155C }, +{ 0x155D, 0x155D, 0x155D }, +{ 0x155E, 0x155E, 0x155E }, +{ 0x155F, 0x155F, 0x155F }, +{ 0x1560, 0x1560, 0x1560 }, +{ 0x1561, 0x1561, 0x1561 }, +{ 0x1562, 0x1562, 0x1562 }, +{ 0x1563, 0x1563, 0x1563 }, +{ 0x1564, 0x1564, 0x1564 }, +{ 0x1565, 0x1565, 0x1565 }, +{ 0x1566, 0x1566, 0x1566 }, +{ 0x1567, 0x1567, 0x1567 }, +{ 0x1568, 0x1568, 0x1568 }, +{ 0x1569, 0x1569, 0x1569 }, +{ 0x156A, 0x156A, 0x156A }, +{ 0x156B, 0x156B, 0x156B }, +{ 0x156C, 0x156C, 0x156C }, +{ 0x156D, 0x156D, 0x156D }, +{ 0x156E, 0x156E, 0x156E }, +{ 0x156F, 0x156F, 0x156F }, +{ 0x1570, 0x1570, 0x1570 }, +{ 0x1571, 0x1571, 0x1571 }, +{ 0x1572, 0x1572, 0x1572 }, +{ 0x1573, 0x1573, 0x1573 }, +{ 0x1574, 0x1574, 0x1574 }, +{ 0x1575, 0x1575, 0x1575 }, +{ 0x1576, 0x1576, 0x1576 }, +{ 0x1577, 0x1577, 0x1577 }, +{ 0x1578, 0x1578, 0x1578 }, +{ 0x1579, 0x1579, 0x1579 }, +{ 0x157A, 0x157A, 0x157A }, +{ 0x157B, 0x157B, 0x157B }, +{ 0x157C, 0x157C, 0x157C }, +{ 0x157D, 0x157D, 0x157D }, +{ 0x157E, 0x157E, 0x157E }, +{ 0x157F, 0x157F, 0x157F }, +{ 0x1580, 0x1580, 0x1580 }, +{ 0x1581, 0x1581, 0x1581 }, +{ 0x1582, 0x1582, 0x1582 }, +{ 0x1583, 0x1583, 0x1583 }, +{ 0x1584, 0x1584, 0x1584 }, +{ 0x1585, 0x1585, 0x1585 }, +{ 0x1586, 0x1586, 0x1586 }, +{ 0x1587, 0x1587, 0x1587 }, +{ 0x1588, 0x1588, 0x1588 }, +{ 0x1589, 0x1589, 0x1589 }, +{ 0x158A, 0x158A, 0x158A }, +{ 0x158B, 0x158B, 0x158B }, +{ 0x158C, 0x158C, 0x158C }, +{ 0x158D, 0x158D, 0x158D }, +{ 0x158E, 0x158E, 0x158E }, +{ 0x158F, 0x158F, 0x158F }, +{ 0x1590, 0x1590, 0x1590 }, +{ 0x1591, 0x1591, 0x1591 }, +{ 0x1592, 0x1592, 0x1592 }, +{ 0x1593, 0x1593, 0x1593 }, +{ 0x1594, 0x1594, 0x1594 }, +{ 0x1595, 0x1595, 0x1595 }, +{ 0x1596, 0x1596, 0x1596 }, +{ 0x1597, 0x1597, 0x1597 }, +{ 0x1598, 0x1598, 0x1598 }, +{ 0x1599, 0x1599, 0x1599 }, +{ 0x159A, 0x159A, 0x159A }, +{ 0x159B, 0x159B, 0x159B }, +{ 0x159C, 0x159C, 0x159C }, +{ 0x159D, 0x159D, 0x159D }, +{ 0x159E, 0x159E, 0x159E }, +{ 0x159F, 0x159F, 0x159F }, +{ 0x15A0, 0x15A0, 0x15A0 }, +{ 0x15A1, 0x15A1, 0x15A1 }, +{ 0x15A2, 0x15A2, 0x15A2 }, +{ 0x15A3, 0x15A3, 0x15A3 }, +{ 0x15A4, 0x15A4, 0x15A4 }, +{ 0x15A5, 0x15A5, 0x15A5 }, +{ 0x15A6, 0x15A6, 0x15A6 }, +{ 0x15A7, 0x15A7, 0x15A7 }, +{ 0x15A8, 0x15A8, 0x15A8 }, +{ 0x15A9, 0x15A9, 0x15A9 }, +{ 0x15AA, 0x15AA, 0x15AA }, +{ 0x15AB, 0x15AB, 0x15AB }, +{ 0x15AC, 0x15AC, 0x15AC }, +{ 0x15AD, 0x15AD, 0x15AD }, +{ 0x15AE, 0x15AE, 0x15AE }, +{ 0x15AF, 0x15AF, 0x15AF }, +{ 0x15B0, 0x15B0, 0x15B0 }, +{ 0x15B1, 0x15B1, 0x15B1 }, +{ 0x15B2, 0x15B2, 0x15B2 }, +{ 0x15B3, 0x15B3, 0x15B3 }, +{ 0x15B4, 0x15B4, 0x15B4 }, +{ 0x15B5, 0x15B5, 0x15B5 }, +{ 0x15B6, 0x15B6, 0x15B6 }, +{ 0x15B7, 0x15B7, 0x15B7 }, +{ 0x15B8, 0x15B8, 0x15B8 }, +{ 0x15B9, 0x15B9, 0x15B9 }, +{ 0x15BA, 0x15BA, 0x15BA }, +{ 0x15BB, 0x15BB, 0x15BB }, +{ 0x15BC, 0x15BC, 0x15BC }, +{ 0x15BD, 0x15BD, 0x15BD }, +{ 0x15BE, 0x15BE, 0x15BE }, +{ 0x15BF, 0x15BF, 0x15BF }, +{ 0x15C0, 0x15C0, 0x15C0 }, +{ 0x15C1, 0x15C1, 0x15C1 }, +{ 0x15C2, 0x15C2, 0x15C2 }, +{ 0x15C3, 0x15C3, 0x15C3 }, +{ 0x15C4, 0x15C4, 0x15C4 }, +{ 0x15C5, 0x15C5, 0x15C5 }, +{ 0x15C6, 0x15C6, 0x15C6 }, +{ 0x15C7, 0x15C7, 0x15C7 }, +{ 0x15C8, 0x15C8, 0x15C8 }, +{ 0x15C9, 0x15C9, 0x15C9 }, +{ 0x15CA, 0x15CA, 0x15CA }, +{ 0x15CB, 0x15CB, 0x15CB }, +{ 0x15CC, 0x15CC, 0x15CC }, +{ 0x15CD, 0x15CD, 0x15CD }, +{ 0x15CE, 0x15CE, 0x15CE }, +{ 0x15CF, 0x15CF, 0x15CF }, +{ 0x15D0, 0x15D0, 0x15D0 }, +{ 0x15D1, 0x15D1, 0x15D1 }, +{ 0x15D2, 0x15D2, 0x15D2 }, +{ 0x15D3, 0x15D3, 0x15D3 }, +{ 0x15D4, 0x15D4, 0x15D4 }, +{ 0x15D5, 0x15D5, 0x15D5 }, +{ 0x15D6, 0x15D6, 0x15D6 }, +{ 0x15D7, 0x15D7, 0x15D7 }, +{ 0x15D8, 0x15D8, 0x15D8 }, +{ 0x15D9, 0x15D9, 0x15D9 }, +{ 0x15DA, 0x15DA, 0x15DA }, +{ 0x15DB, 0x15DB, 0x15DB }, +{ 0x15DC, 0x15DC, 0x15DC }, +{ 0x15DD, 0x15DD, 0x15DD }, +{ 0x15DE, 0x15DE, 0x15DE }, +{ 0x15DF, 0x15DF, 0x15DF }, +{ 0x15E0, 0x15E0, 0x15E0 }, +{ 0x15E1, 0x15E1, 0x15E1 }, +{ 0x15E2, 0x15E2, 0x15E2 }, +{ 0x15E3, 0x15E3, 0x15E3 }, +{ 0x15E4, 0x15E4, 0x15E4 }, +{ 0x15E5, 0x15E5, 0x15E5 }, +{ 0x15E6, 0x15E6, 0x15E6 }, +{ 0x15E7, 0x15E7, 0x15E7 }, +{ 0x15E8, 0x15E8, 0x15E8 }, +{ 0x15E9, 0x15E9, 0x15E9 }, +{ 0x15EA, 0x15EA, 0x15EA }, +{ 0x15EB, 0x15EB, 0x15EB }, +{ 0x15EC, 0x15EC, 0x15EC }, +{ 0x15ED, 0x15ED, 0x15ED }, +{ 0x15EE, 0x15EE, 0x15EE }, +{ 0x15EF, 0x15EF, 0x15EF }, +{ 0x15F0, 0x15F0, 0x15F0 }, +{ 0x15F1, 0x15F1, 0x15F1 }, +{ 0x15F2, 0x15F2, 0x15F2 }, +{ 0x15F3, 0x15F3, 0x15F3 }, +{ 0x15F4, 0x15F4, 0x15F4 }, +{ 0x15F5, 0x15F5, 0x15F5 }, +{ 0x15F6, 0x15F6, 0x15F6 }, +{ 0x15F7, 0x15F7, 0x15F7 }, +{ 0x15F8, 0x15F8, 0x15F8 }, +{ 0x15F9, 0x15F9, 0x15F9 }, +{ 0x15FA, 0x15FA, 0x15FA }, +{ 0x15FB, 0x15FB, 0x15FB }, +{ 0x15FC, 0x15FC, 0x15FC }, +{ 0x15FD, 0x15FD, 0x15FD }, +{ 0x15FE, 0x15FE, 0x15FE }, +{ 0x15FF, 0x15FF, 0x15FF }, +{ 0x1600, 0x1600, 0x1600 }, +{ 0x1601, 0x1601, 0x1601 }, +{ 0x1602, 0x1602, 0x1602 }, +{ 0x1603, 0x1603, 0x1603 }, +{ 0x1604, 0x1604, 0x1604 }, +{ 0x1605, 0x1605, 0x1605 }, +{ 0x1606, 0x1606, 0x1606 }, +{ 0x1607, 0x1607, 0x1607 }, +{ 0x1608, 0x1608, 0x1608 }, +{ 0x1609, 0x1609, 0x1609 }, +{ 0x160A, 0x160A, 0x160A }, +{ 0x160B, 0x160B, 0x160B }, +{ 0x160C, 0x160C, 0x160C }, +{ 0x160D, 0x160D, 0x160D }, +{ 0x160E, 0x160E, 0x160E }, +{ 0x160F, 0x160F, 0x160F }, +{ 0x1610, 0x1610, 0x1610 }, +{ 0x1611, 0x1611, 0x1611 }, +{ 0x1612, 0x1612, 0x1612 }, +{ 0x1613, 0x1613, 0x1613 }, +{ 0x1614, 0x1614, 0x1614 }, +{ 0x1615, 0x1615, 0x1615 }, +{ 0x1616, 0x1616, 0x1616 }, +{ 0x1617, 0x1617, 0x1617 }, +{ 0x1618, 0x1618, 0x1618 }, +{ 0x1619, 0x1619, 0x1619 }, +{ 0x161A, 0x161A, 0x161A }, +{ 0x161B, 0x161B, 0x161B }, +{ 0x161C, 0x161C, 0x161C }, +{ 0x161D, 0x161D, 0x161D }, +{ 0x161E, 0x161E, 0x161E }, +{ 0x161F, 0x161F, 0x161F }, +{ 0x1620, 0x1620, 0x1620 }, +{ 0x1621, 0x1621, 0x1621 }, +{ 0x1622, 0x1622, 0x1622 }, +{ 0x1623, 0x1623, 0x1623 }, +{ 0x1624, 0x1624, 0x1624 }, +{ 0x1625, 0x1625, 0x1625 }, +{ 0x1626, 0x1626, 0x1626 }, +{ 0x1627, 0x1627, 0x1627 }, +{ 0x1628, 0x1628, 0x1628 }, +{ 0x1629, 0x1629, 0x1629 }, +{ 0x162A, 0x162A, 0x162A }, +{ 0x162B, 0x162B, 0x162B }, +{ 0x162C, 0x162C, 0x162C }, +{ 0x162D, 0x162D, 0x162D }, +{ 0x162E, 0x162E, 0x162E }, +{ 0x162F, 0x162F, 0x162F }, +{ 0x1630, 0x1630, 0x1630 }, +{ 0x1631, 0x1631, 0x1631 }, +{ 0x1632, 0x1632, 0x1632 }, +{ 0x1633, 0x1633, 0x1633 }, +{ 0x1634, 0x1634, 0x1634 }, +{ 0x1635, 0x1635, 0x1635 }, +{ 0x1636, 0x1636, 0x1636 }, +{ 0x1637, 0x1637, 0x1637 }, +{ 0x1638, 0x1638, 0x1638 }, +{ 0x1639, 0x1639, 0x1639 }, +{ 0x163A, 0x163A, 0x163A }, +{ 0x163B, 0x163B, 0x163B }, +{ 0x163C, 0x163C, 0x163C }, +{ 0x163D, 0x163D, 0x163D }, +{ 0x163E, 0x163E, 0x163E }, +{ 0x163F, 0x163F, 0x163F }, +{ 0x1640, 0x1640, 0x1640 }, +{ 0x1641, 0x1641, 0x1641 }, +{ 0x1642, 0x1642, 0x1642 }, +{ 0x1643, 0x1643, 0x1643 }, +{ 0x1644, 0x1644, 0x1644 }, +{ 0x1645, 0x1645, 0x1645 }, +{ 0x1646, 0x1646, 0x1646 }, +{ 0x1647, 0x1647, 0x1647 }, +{ 0x1648, 0x1648, 0x1648 }, +{ 0x1649, 0x1649, 0x1649 }, +{ 0x164A, 0x164A, 0x164A }, +{ 0x164B, 0x164B, 0x164B }, +{ 0x164C, 0x164C, 0x164C }, +{ 0x164D, 0x164D, 0x164D }, +{ 0x164E, 0x164E, 0x164E }, +{ 0x164F, 0x164F, 0x164F }, +{ 0x1650, 0x1650, 0x1650 }, +{ 0x1651, 0x1651, 0x1651 }, +{ 0x1652, 0x1652, 0x1652 }, +{ 0x1653, 0x1653, 0x1653 }, +{ 0x1654, 0x1654, 0x1654 }, +{ 0x1655, 0x1655, 0x1655 }, +{ 0x1656, 0x1656, 0x1656 }, +{ 0x1657, 0x1657, 0x1657 }, +{ 0x1658, 0x1658, 0x1658 }, +{ 0x1659, 0x1659, 0x1659 }, +{ 0x165A, 0x165A, 0x165A }, +{ 0x165B, 0x165B, 0x165B }, +{ 0x165C, 0x165C, 0x165C }, +{ 0x165D, 0x165D, 0x165D }, +{ 0x165E, 0x165E, 0x165E }, +{ 0x165F, 0x165F, 0x165F }, +{ 0x1660, 0x1660, 0x1660 }, +{ 0x1661, 0x1661, 0x1661 }, +{ 0x1662, 0x1662, 0x1662 }, +{ 0x1663, 0x1663, 0x1663 }, +{ 0x1664, 0x1664, 0x1664 }, +{ 0x1665, 0x1665, 0x1665 }, +{ 0x1666, 0x1666, 0x1666 }, +{ 0x1667, 0x1667, 0x1667 }, +{ 0x1668, 0x1668, 0x1668 }, +{ 0x1669, 0x1669, 0x1669 }, +{ 0x166A, 0x166A, 0x166A }, +{ 0x166B, 0x166B, 0x166B }, +{ 0x166C, 0x166C, 0x166C }, +{ 0x166F, 0x166F, 0x166F }, +{ 0x1670, 0x1670, 0x1670 }, +{ 0x1671, 0x1671, 0x1671 }, +{ 0x1672, 0x1672, 0x1672 }, +{ 0x1673, 0x1673, 0x1673 }, +{ 0x1674, 0x1674, 0x1674 }, +{ 0x1675, 0x1675, 0x1675 }, +{ 0x1676, 0x1676, 0x1676 }, +{ 0x1681, 0x1681, 0x1681 }, +{ 0x1682, 0x1682, 0x1682 }, +{ 0x1683, 0x1683, 0x1683 }, +{ 0x1684, 0x1684, 0x1684 }, +{ 0x1685, 0x1685, 0x1685 }, +{ 0x1686, 0x1686, 0x1686 }, +{ 0x1687, 0x1687, 0x1687 }, +{ 0x1688, 0x1688, 0x1688 }, +{ 0x1689, 0x1689, 0x1689 }, +{ 0x168A, 0x168A, 0x168A }, +{ 0x168B, 0x168B, 0x168B }, +{ 0x168C, 0x168C, 0x168C }, +{ 0x168D, 0x168D, 0x168D }, +{ 0x168E, 0x168E, 0x168E }, +{ 0x168F, 0x168F, 0x168F }, +{ 0x1690, 0x1690, 0x1690 }, +{ 0x1691, 0x1691, 0x1691 }, +{ 0x1692, 0x1692, 0x1692 }, +{ 0x1693, 0x1693, 0x1693 }, +{ 0x1694, 0x1694, 0x1694 }, +{ 0x1695, 0x1695, 0x1695 }, +{ 0x1696, 0x1696, 0x1696 }, +{ 0x1697, 0x1697, 0x1697 }, +{ 0x1698, 0x1698, 0x1698 }, +{ 0x1699, 0x1699, 0x1699 }, +{ 0x169A, 0x169A, 0x169A }, +{ 0x16A0, 0x16A0, 0x16A0 }, +{ 0x16A1, 0x16A1, 0x16A1 }, +{ 0x16A2, 0x16A2, 0x16A2 }, +{ 0x16A3, 0x16A3, 0x16A3 }, +{ 0x16A4, 0x16A4, 0x16A4 }, +{ 0x16A5, 0x16A5, 0x16A5 }, +{ 0x16A6, 0x16A6, 0x16A6 }, +{ 0x16A7, 0x16A7, 0x16A7 }, +{ 0x16A8, 0x16A8, 0x16A8 }, +{ 0x16A9, 0x16A9, 0x16A9 }, +{ 0x16AA, 0x16AA, 0x16AA }, +{ 0x16AB, 0x16AB, 0x16AB }, +{ 0x16AC, 0x16AC, 0x16AC }, +{ 0x16AD, 0x16AD, 0x16AD }, +{ 0x16AE, 0x16AE, 0x16AE }, +{ 0x16AF, 0x16AF, 0x16AF }, +{ 0x16B0, 0x16B0, 0x16B0 }, +{ 0x16B1, 0x16B1, 0x16B1 }, +{ 0x16B2, 0x16B2, 0x16B2 }, +{ 0x16B3, 0x16B3, 0x16B3 }, +{ 0x16B4, 0x16B4, 0x16B4 }, +{ 0x16B5, 0x16B5, 0x16B5 }, +{ 0x16B6, 0x16B6, 0x16B6 }, +{ 0x16B7, 0x16B7, 0x16B7 }, +{ 0x16B8, 0x16B8, 0x16B8 }, +{ 0x16B9, 0x16B9, 0x16B9 }, +{ 0x16BA, 0x16BA, 0x16BA }, +{ 0x16BB, 0x16BB, 0x16BB }, +{ 0x16BC, 0x16BC, 0x16BC }, +{ 0x16BD, 0x16BD, 0x16BD }, +{ 0x16BE, 0x16BE, 0x16BE }, +{ 0x16BF, 0x16BF, 0x16BF }, +{ 0x16C0, 0x16C0, 0x16C0 }, +{ 0x16C1, 0x16C1, 0x16C1 }, +{ 0x16C2, 0x16C2, 0x16C2 }, +{ 0x16C3, 0x16C3, 0x16C3 }, +{ 0x16C4, 0x16C4, 0x16C4 }, +{ 0x16C5, 0x16C5, 0x16C5 }, +{ 0x16C6, 0x16C6, 0x16C6 }, +{ 0x16C7, 0x16C7, 0x16C7 }, +{ 0x16C8, 0x16C8, 0x16C8 }, +{ 0x16C9, 0x16C9, 0x16C9 }, +{ 0x16CA, 0x16CA, 0x16CA }, +{ 0x16CB, 0x16CB, 0x16CB }, +{ 0x16CC, 0x16CC, 0x16CC }, +{ 0x16CD, 0x16CD, 0x16CD }, +{ 0x16CE, 0x16CE, 0x16CE }, +{ 0x16CF, 0x16CF, 0x16CF }, +{ 0x16D0, 0x16D0, 0x16D0 }, +{ 0x16D1, 0x16D1, 0x16D1 }, +{ 0x16D2, 0x16D2, 0x16D2 }, +{ 0x16D3, 0x16D3, 0x16D3 }, +{ 0x16D4, 0x16D4, 0x16D4 }, +{ 0x16D5, 0x16D5, 0x16D5 }, +{ 0x16D6, 0x16D6, 0x16D6 }, +{ 0x16D7, 0x16D7, 0x16D7 }, +{ 0x16D8, 0x16D8, 0x16D8 }, +{ 0x16D9, 0x16D9, 0x16D9 }, +{ 0x16DA, 0x16DA, 0x16DA }, +{ 0x16DB, 0x16DB, 0x16DB }, +{ 0x16DC, 0x16DC, 0x16DC }, +{ 0x16DD, 0x16DD, 0x16DD }, +{ 0x16DE, 0x16DE, 0x16DE }, +{ 0x16DF, 0x16DF, 0x16DF }, +{ 0x16E0, 0x16E0, 0x16E0 }, +{ 0x16E1, 0x16E1, 0x16E1 }, +{ 0x16E2, 0x16E2, 0x16E2 }, +{ 0x16E3, 0x16E3, 0x16E3 }, +{ 0x16E4, 0x16E4, 0x16E4 }, +{ 0x16E5, 0x16E5, 0x16E5 }, +{ 0x16E6, 0x16E6, 0x16E6 }, +{ 0x16E7, 0x16E7, 0x16E7 }, +{ 0x16E8, 0x16E8, 0x16E8 }, +{ 0x16E9, 0x16E9, 0x16E9 }, +{ 0x16EA, 0x16EA, 0x16EA }, +{ 0x1700, 0x1700, 0x1700 }, +{ 0x1701, 0x1701, 0x1701 }, +{ 0x1702, 0x1702, 0x1702 }, +{ 0x1703, 0x1703, 0x1703 }, +{ 0x1704, 0x1704, 0x1704 }, +{ 0x1705, 0x1705, 0x1705 }, +{ 0x1706, 0x1706, 0x1706 }, +{ 0x1707, 0x1707, 0x1707 }, +{ 0x1708, 0x1708, 0x1708 }, +{ 0x1709, 0x1709, 0x1709 }, +{ 0x170A, 0x170A, 0x170A }, +{ 0x170B, 0x170B, 0x170B }, +{ 0x170C, 0x170C, 0x170C }, +{ 0x170E, 0x170E, 0x170E }, +{ 0x170F, 0x170F, 0x170F }, +{ 0x1710, 0x1710, 0x1710 }, +{ 0x1711, 0x1711, 0x1711 }, +{ 0x1712, 0x1712, 0x1712 }, +{ 0x1713, 0x1713, 0x1713 }, +{ 0x1714, 0x1714, 0x1714 }, +{ 0x1720, 0x1720, 0x1720 }, +{ 0x1721, 0x1721, 0x1721 }, +{ 0x1722, 0x1722, 0x1722 }, +{ 0x1723, 0x1723, 0x1723 }, +{ 0x1724, 0x1724, 0x1724 }, +{ 0x1725, 0x1725, 0x1725 }, +{ 0x1726, 0x1726, 0x1726 }, +{ 0x1727, 0x1727, 0x1727 }, +{ 0x1728, 0x1728, 0x1728 }, +{ 0x1729, 0x1729, 0x1729 }, +{ 0x172A, 0x172A, 0x172A }, +{ 0x172B, 0x172B, 0x172B }, +{ 0x172C, 0x172C, 0x172C }, +{ 0x172D, 0x172D, 0x172D }, +{ 0x172E, 0x172E, 0x172E }, +{ 0x172F, 0x172F, 0x172F }, +{ 0x1730, 0x1730, 0x1730 }, +{ 0x1731, 0x1731, 0x1731 }, +{ 0x1732, 0x1732, 0x1732 }, +{ 0x1733, 0x1733, 0x1733 }, +{ 0x1734, 0x1734, 0x1734 }, +{ 0x1740, 0x1740, 0x1740 }, +{ 0x1741, 0x1741, 0x1741 }, +{ 0x1742, 0x1742, 0x1742 }, +{ 0x1743, 0x1743, 0x1743 }, +{ 0x1744, 0x1744, 0x1744 }, +{ 0x1745, 0x1745, 0x1745 }, +{ 0x1746, 0x1746, 0x1746 }, +{ 0x1747, 0x1747, 0x1747 }, +{ 0x1748, 0x1748, 0x1748 }, +{ 0x1749, 0x1749, 0x1749 }, +{ 0x174A, 0x174A, 0x174A }, +{ 0x174B, 0x174B, 0x174B }, +{ 0x174C, 0x174C, 0x174C }, +{ 0x174D, 0x174D, 0x174D }, +{ 0x174E, 0x174E, 0x174E }, +{ 0x174F, 0x174F, 0x174F }, +{ 0x1750, 0x1750, 0x1750 }, +{ 0x1751, 0x1751, 0x1751 }, +{ 0x1752, 0x1752, 0x1752 }, +{ 0x1753, 0x1753, 0x1753 }, +{ 0x1760, 0x1760, 0x1760 }, +{ 0x1761, 0x1761, 0x1761 }, +{ 0x1762, 0x1762, 0x1762 }, +{ 0x1763, 0x1763, 0x1763 }, +{ 0x1764, 0x1764, 0x1764 }, +{ 0x1765, 0x1765, 0x1765 }, +{ 0x1766, 0x1766, 0x1766 }, +{ 0x1767, 0x1767, 0x1767 }, +{ 0x1768, 0x1768, 0x1768 }, +{ 0x1769, 0x1769, 0x1769 }, +{ 0x176A, 0x176A, 0x176A }, +{ 0x176B, 0x176B, 0x176B }, +{ 0x176C, 0x176C, 0x176C }, +{ 0x176E, 0x176E, 0x176E }, +{ 0x176F, 0x176F, 0x176F }, +{ 0x1770, 0x1770, 0x1770 }, +{ 0x1772, 0x1772, 0x1772 }, +{ 0x1773, 0x1773, 0x1773 }, +{ 0x1780, 0x1780, 0x1780 }, +{ 0x1781, 0x1781, 0x1781 }, +{ 0x1782, 0x1782, 0x1782 }, +{ 0x1783, 0x1783, 0x1783 }, +{ 0x1784, 0x1784, 0x1784 }, +{ 0x1785, 0x1785, 0x1785 }, +{ 0x1786, 0x1786, 0x1786 }, +{ 0x1787, 0x1787, 0x1787 }, +{ 0x1788, 0x1788, 0x1788 }, +{ 0x1789, 0x1789, 0x1789 }, +{ 0x178A, 0x178A, 0x178A }, +{ 0x178B, 0x178B, 0x178B }, +{ 0x178C, 0x178C, 0x178C }, +{ 0x178D, 0x178D, 0x178D }, +{ 0x178E, 0x178E, 0x178E }, +{ 0x178F, 0x178F, 0x178F }, +{ 0x1790, 0x1790, 0x1790 }, +{ 0x1791, 0x1791, 0x1791 }, +{ 0x1792, 0x1792, 0x1792 }, +{ 0x1793, 0x1793, 0x1793 }, +{ 0x1794, 0x1794, 0x1794 }, +{ 0x1795, 0x1795, 0x1795 }, +{ 0x1796, 0x1796, 0x1796 }, +{ 0x1797, 0x1797, 0x1797 }, +{ 0x1798, 0x1798, 0x1798 }, +{ 0x1799, 0x1799, 0x1799 }, +{ 0x179A, 0x179A, 0x179A }, +{ 0x179B, 0x179B, 0x179B }, +{ 0x179C, 0x179C, 0x179C }, +{ 0x179D, 0x179D, 0x179D }, +{ 0x179E, 0x179E, 0x179E }, +{ 0x179F, 0x179F, 0x179F }, +{ 0x17A0, 0x17A0, 0x17A0 }, +{ 0x17A1, 0x17A1, 0x17A1 }, +{ 0x17A2, 0x17A2, 0x17A2 }, +{ 0x17A3, 0x17A3, 0x17A3 }, +{ 0x17A4, 0x17A4, 0x17A4 }, +{ 0x17A5, 0x17A5, 0x17A5 }, +{ 0x17A6, 0x17A6, 0x17A6 }, +{ 0x17A7, 0x17A7, 0x17A7 }, +{ 0x17A8, 0x17A8, 0x17A8 }, +{ 0x17A9, 0x17A9, 0x17A9 }, +{ 0x17AA, 0x17AA, 0x17AA }, +{ 0x17AB, 0x17AB, 0x17AB }, +{ 0x17AC, 0x17AC, 0x17AC }, +{ 0x17AD, 0x17AD, 0x17AD }, +{ 0x17AE, 0x17AE, 0x17AE }, +{ 0x17AF, 0x17AF, 0x17AF }, +{ 0x17B0, 0x17B0, 0x17B0 }, +{ 0x17B1, 0x17B1, 0x17B1 }, +{ 0x17B2, 0x17B2, 0x17B2 }, +{ 0x17B3, 0x17B3, 0x17B3 }, +{ 0x17B7, 0x17B7, 0x17B7 }, +{ 0x17B8, 0x17B8, 0x17B8 }, +{ 0x17B9, 0x17B9, 0x17B9 }, +{ 0x17BA, 0x17BA, 0x17BA }, +{ 0x17BB, 0x17BB, 0x17BB }, +{ 0x17BC, 0x17BC, 0x17BC }, +{ 0x17BD, 0x17BD, 0x17BD }, +{ 0x17C6, 0x17C6, 0x17C6 }, +{ 0x17C9, 0x17C9, 0x17C9 }, +{ 0x17CA, 0x17CA, 0x17CA }, +{ 0x17CB, 0x17CB, 0x17CB }, +{ 0x17CC, 0x17CC, 0x17CC }, +{ 0x17CD, 0x17CD, 0x17CD }, +{ 0x17CE, 0x17CE, 0x17CE }, +{ 0x17CF, 0x17CF, 0x17CF }, +{ 0x17D0, 0x17D0, 0x17D0 }, +{ 0x17D1, 0x17D1, 0x17D1 }, +{ 0x17D2, 0x17D2, 0x17D2 }, +{ 0x17D3, 0x17D3, 0x17D3 }, +{ 0x17D7, 0x17D7, 0x17D7 }, +{ 0x17DC, 0x17DC, 0x17DC }, +{ 0x17DD, 0x17DD, 0x17DD }, +{ 0x180B, 0x180B, 0x180B }, +{ 0x180C, 0x180C, 0x180C }, +{ 0x180D, 0x180D, 0x180D }, +{ 0x1820, 0x1820, 0x1820 }, +{ 0x1821, 0x1821, 0x1821 }, +{ 0x1822, 0x1822, 0x1822 }, +{ 0x1823, 0x1823, 0x1823 }, +{ 0x1824, 0x1824, 0x1824 }, +{ 0x1825, 0x1825, 0x1825 }, +{ 0x1826, 0x1826, 0x1826 }, +{ 0x1827, 0x1827, 0x1827 }, +{ 0x1828, 0x1828, 0x1828 }, +{ 0x1829, 0x1829, 0x1829 }, +{ 0x182A, 0x182A, 0x182A }, +{ 0x182B, 0x182B, 0x182B }, +{ 0x182C, 0x182C, 0x182C }, +{ 0x182D, 0x182D, 0x182D }, +{ 0x182E, 0x182E, 0x182E }, +{ 0x182F, 0x182F, 0x182F }, +{ 0x1830, 0x1830, 0x1830 }, +{ 0x1831, 0x1831, 0x1831 }, +{ 0x1832, 0x1832, 0x1832 }, +{ 0x1833, 0x1833, 0x1833 }, +{ 0x1834, 0x1834, 0x1834 }, +{ 0x1835, 0x1835, 0x1835 }, +{ 0x1836, 0x1836, 0x1836 }, +{ 0x1837, 0x1837, 0x1837 }, +{ 0x1838, 0x1838, 0x1838 }, +{ 0x1839, 0x1839, 0x1839 }, +{ 0x183A, 0x183A, 0x183A }, +{ 0x183B, 0x183B, 0x183B }, +{ 0x183C, 0x183C, 0x183C }, +{ 0x183D, 0x183D, 0x183D }, +{ 0x183E, 0x183E, 0x183E }, +{ 0x183F, 0x183F, 0x183F }, +{ 0x1840, 0x1840, 0x1840 }, +{ 0x1841, 0x1841, 0x1841 }, +{ 0x1842, 0x1842, 0x1842 }, +{ 0x1843, 0x1843, 0x1843 }, +{ 0x1844, 0x1844, 0x1844 }, +{ 0x1845, 0x1845, 0x1845 }, +{ 0x1846, 0x1846, 0x1846 }, +{ 0x1847, 0x1847, 0x1847 }, +{ 0x1848, 0x1848, 0x1848 }, +{ 0x1849, 0x1849, 0x1849 }, +{ 0x184A, 0x184A, 0x184A }, +{ 0x184B, 0x184B, 0x184B }, +{ 0x184C, 0x184C, 0x184C }, +{ 0x184D, 0x184D, 0x184D }, +{ 0x184E, 0x184E, 0x184E }, +{ 0x184F, 0x184F, 0x184F }, +{ 0x1850, 0x1850, 0x1850 }, +{ 0x1851, 0x1851, 0x1851 }, +{ 0x1852, 0x1852, 0x1852 }, +{ 0x1853, 0x1853, 0x1853 }, +{ 0x1854, 0x1854, 0x1854 }, +{ 0x1855, 0x1855, 0x1855 }, +{ 0x1856, 0x1856, 0x1856 }, +{ 0x1857, 0x1857, 0x1857 }, +{ 0x1858, 0x1858, 0x1858 }, +{ 0x1859, 0x1859, 0x1859 }, +{ 0x185A, 0x185A, 0x185A }, +{ 0x185B, 0x185B, 0x185B }, +{ 0x185C, 0x185C, 0x185C }, +{ 0x185D, 0x185D, 0x185D }, +{ 0x185E, 0x185E, 0x185E }, +{ 0x185F, 0x185F, 0x185F }, +{ 0x1860, 0x1860, 0x1860 }, +{ 0x1861, 0x1861, 0x1861 }, +{ 0x1862, 0x1862, 0x1862 }, +{ 0x1863, 0x1863, 0x1863 }, +{ 0x1864, 0x1864, 0x1864 }, +{ 0x1865, 0x1865, 0x1865 }, +{ 0x1866, 0x1866, 0x1866 }, +{ 0x1867, 0x1867, 0x1867 }, +{ 0x1868, 0x1868, 0x1868 }, +{ 0x1869, 0x1869, 0x1869 }, +{ 0x186A, 0x186A, 0x186A }, +{ 0x186B, 0x186B, 0x186B }, +{ 0x186C, 0x186C, 0x186C }, +{ 0x186D, 0x186D, 0x186D }, +{ 0x186E, 0x186E, 0x186E }, +{ 0x186F, 0x186F, 0x186F }, +{ 0x1870, 0x1870, 0x1870 }, +{ 0x1871, 0x1871, 0x1871 }, +{ 0x1872, 0x1872, 0x1872 }, +{ 0x1873, 0x1873, 0x1873 }, +{ 0x1874, 0x1874, 0x1874 }, +{ 0x1875, 0x1875, 0x1875 }, +{ 0x1876, 0x1876, 0x1876 }, +{ 0x1877, 0x1877, 0x1877 }, +{ 0x1880, 0x1880, 0x1880 }, +{ 0x1881, 0x1881, 0x1881 }, +{ 0x1882, 0x1882, 0x1882 }, +{ 0x1883, 0x1883, 0x1883 }, +{ 0x1884, 0x1884, 0x1884 }, +{ 0x1885, 0x1885, 0x1885 }, +{ 0x1886, 0x1886, 0x1886 }, +{ 0x1887, 0x1887, 0x1887 }, +{ 0x1888, 0x1888, 0x1888 }, +{ 0x1889, 0x1889, 0x1889 }, +{ 0x188A, 0x188A, 0x188A }, +{ 0x188B, 0x188B, 0x188B }, +{ 0x188C, 0x188C, 0x188C }, +{ 0x188D, 0x188D, 0x188D }, +{ 0x188E, 0x188E, 0x188E }, +{ 0x188F, 0x188F, 0x188F }, +{ 0x1890, 0x1890, 0x1890 }, +{ 0x1891, 0x1891, 0x1891 }, +{ 0x1892, 0x1892, 0x1892 }, +{ 0x1893, 0x1893, 0x1893 }, +{ 0x1894, 0x1894, 0x1894 }, +{ 0x1895, 0x1895, 0x1895 }, +{ 0x1896, 0x1896, 0x1896 }, +{ 0x1897, 0x1897, 0x1897 }, +{ 0x1898, 0x1898, 0x1898 }, +{ 0x1899, 0x1899, 0x1899 }, +{ 0x189A, 0x189A, 0x189A }, +{ 0x189B, 0x189B, 0x189B }, +{ 0x189C, 0x189C, 0x189C }, +{ 0x189D, 0x189D, 0x189D }, +{ 0x189E, 0x189E, 0x189E }, +{ 0x189F, 0x189F, 0x189F }, +{ 0x18A0, 0x18A0, 0x18A0 }, +{ 0x18A1, 0x18A1, 0x18A1 }, +{ 0x18A2, 0x18A2, 0x18A2 }, +{ 0x18A3, 0x18A3, 0x18A3 }, +{ 0x18A4, 0x18A4, 0x18A4 }, +{ 0x18A5, 0x18A5, 0x18A5 }, +{ 0x18A6, 0x18A6, 0x18A6 }, +{ 0x18A7, 0x18A7, 0x18A7 }, +{ 0x18A8, 0x18A8, 0x18A8 }, +{ 0x18A9, 0x18A9, 0x18A9 }, +{ 0x1900, 0x1900, 0x1900 }, +{ 0x1901, 0x1901, 0x1901 }, +{ 0x1902, 0x1902, 0x1902 }, +{ 0x1903, 0x1903, 0x1903 }, +{ 0x1904, 0x1904, 0x1904 }, +{ 0x1905, 0x1905, 0x1905 }, +{ 0x1906, 0x1906, 0x1906 }, +{ 0x1907, 0x1907, 0x1907 }, +{ 0x1908, 0x1908, 0x1908 }, +{ 0x1909, 0x1909, 0x1909 }, +{ 0x190A, 0x190A, 0x190A }, +{ 0x190B, 0x190B, 0x190B }, +{ 0x190C, 0x190C, 0x190C }, +{ 0x190D, 0x190D, 0x190D }, +{ 0x190E, 0x190E, 0x190E }, +{ 0x190F, 0x190F, 0x190F }, +{ 0x1910, 0x1910, 0x1910 }, +{ 0x1911, 0x1911, 0x1911 }, +{ 0x1912, 0x1912, 0x1912 }, +{ 0x1913, 0x1913, 0x1913 }, +{ 0x1914, 0x1914, 0x1914 }, +{ 0x1915, 0x1915, 0x1915 }, +{ 0x1916, 0x1916, 0x1916 }, +{ 0x1917, 0x1917, 0x1917 }, +{ 0x1918, 0x1918, 0x1918 }, +{ 0x1919, 0x1919, 0x1919 }, +{ 0x191A, 0x191A, 0x191A }, +{ 0x191B, 0x191B, 0x191B }, +{ 0x191C, 0x191C, 0x191C }, +{ 0x1920, 0x1920, 0x1920 }, +{ 0x1921, 0x1921, 0x1921 }, +{ 0x1922, 0x1922, 0x1922 }, +{ 0x1927, 0x1927, 0x1927 }, +{ 0x1928, 0x1928, 0x1928 }, +{ 0x1932, 0x1932, 0x1932 }, +{ 0x1939, 0x1939, 0x1939 }, +{ 0x193A, 0x193A, 0x193A }, +{ 0x193B, 0x193B, 0x193B }, +{ 0x1950, 0x1950, 0x1950 }, +{ 0x1951, 0x1951, 0x1951 }, +{ 0x1952, 0x1952, 0x1952 }, +{ 0x1953, 0x1953, 0x1953 }, +{ 0x1954, 0x1954, 0x1954 }, +{ 0x1955, 0x1955, 0x1955 }, +{ 0x1956, 0x1956, 0x1956 }, +{ 0x1957, 0x1957, 0x1957 }, +{ 0x1958, 0x1958, 0x1958 }, +{ 0x1959, 0x1959, 0x1959 }, +{ 0x195A, 0x195A, 0x195A }, +{ 0x195B, 0x195B, 0x195B }, +{ 0x195C, 0x195C, 0x195C }, +{ 0x195D, 0x195D, 0x195D }, +{ 0x195E, 0x195E, 0x195E }, +{ 0x195F, 0x195F, 0x195F }, +{ 0x1960, 0x1960, 0x1960 }, +{ 0x1961, 0x1961, 0x1961 }, +{ 0x1962, 0x1962, 0x1962 }, +{ 0x1963, 0x1963, 0x1963 }, +{ 0x1964, 0x1964, 0x1964 }, +{ 0x1965, 0x1965, 0x1965 }, +{ 0x1966, 0x1966, 0x1966 }, +{ 0x1967, 0x1967, 0x1967 }, +{ 0x1968, 0x1968, 0x1968 }, +{ 0x1969, 0x1969, 0x1969 }, +{ 0x196A, 0x196A, 0x196A }, +{ 0x196B, 0x196B, 0x196B }, +{ 0x196C, 0x196C, 0x196C }, +{ 0x196D, 0x196D, 0x196D }, +{ 0x1970, 0x1970, 0x1970 }, +{ 0x1971, 0x1971, 0x1971 }, +{ 0x1972, 0x1972, 0x1972 }, +{ 0x1973, 0x1973, 0x1973 }, +{ 0x1974, 0x1974, 0x1974 }, +{ 0x1980, 0x1980, 0x1980 }, +{ 0x1981, 0x1981, 0x1981 }, +{ 0x1982, 0x1982, 0x1982 }, +{ 0x1983, 0x1983, 0x1983 }, +{ 0x1984, 0x1984, 0x1984 }, +{ 0x1985, 0x1985, 0x1985 }, +{ 0x1986, 0x1986, 0x1986 }, +{ 0x1987, 0x1987, 0x1987 }, +{ 0x1988, 0x1988, 0x1988 }, +{ 0x1989, 0x1989, 0x1989 }, +{ 0x198A, 0x198A, 0x198A }, +{ 0x198B, 0x198B, 0x198B }, +{ 0x198C, 0x198C, 0x198C }, +{ 0x198D, 0x198D, 0x198D }, +{ 0x198E, 0x198E, 0x198E }, +{ 0x198F, 0x198F, 0x198F }, +{ 0x1990, 0x1990, 0x1990 }, +{ 0x1991, 0x1991, 0x1991 }, +{ 0x1992, 0x1992, 0x1992 }, +{ 0x1993, 0x1993, 0x1993 }, +{ 0x1994, 0x1994, 0x1994 }, +{ 0x1995, 0x1995, 0x1995 }, +{ 0x1996, 0x1996, 0x1996 }, +{ 0x1997, 0x1997, 0x1997 }, +{ 0x1998, 0x1998, 0x1998 }, +{ 0x1999, 0x1999, 0x1999 }, +{ 0x199A, 0x199A, 0x199A }, +{ 0x199B, 0x199B, 0x199B }, +{ 0x199C, 0x199C, 0x199C }, +{ 0x199D, 0x199D, 0x199D }, +{ 0x199E, 0x199E, 0x199E }, +{ 0x199F, 0x199F, 0x199F }, +{ 0x19A0, 0x19A0, 0x19A0 }, +{ 0x19A1, 0x19A1, 0x19A1 }, +{ 0x19A2, 0x19A2, 0x19A2 }, +{ 0x19A3, 0x19A3, 0x19A3 }, +{ 0x19A4, 0x19A4, 0x19A4 }, +{ 0x19A5, 0x19A5, 0x19A5 }, +{ 0x19A6, 0x19A6, 0x19A6 }, +{ 0x19A7, 0x19A7, 0x19A7 }, +{ 0x19A8, 0x19A8, 0x19A8 }, +{ 0x19A9, 0x19A9, 0x19A9 }, +{ 0x19C1, 0x19C1, 0x19C1 }, +{ 0x19C2, 0x19C2, 0x19C2 }, +{ 0x19C3, 0x19C3, 0x19C3 }, +{ 0x19C4, 0x19C4, 0x19C4 }, +{ 0x19C5, 0x19C5, 0x19C5 }, +{ 0x19C6, 0x19C6, 0x19C6 }, +{ 0x19C7, 0x19C7, 0x19C7 }, +{ 0x1A00, 0x1A00, 0x1A00 }, +{ 0x1A01, 0x1A01, 0x1A01 }, +{ 0x1A02, 0x1A02, 0x1A02 }, +{ 0x1A03, 0x1A03, 0x1A03 }, +{ 0x1A04, 0x1A04, 0x1A04 }, +{ 0x1A05, 0x1A05, 0x1A05 }, +{ 0x1A06, 0x1A06, 0x1A06 }, +{ 0x1A07, 0x1A07, 0x1A07 }, +{ 0x1A08, 0x1A08, 0x1A08 }, +{ 0x1A09, 0x1A09, 0x1A09 }, +{ 0x1A0A, 0x1A0A, 0x1A0A }, +{ 0x1A0B, 0x1A0B, 0x1A0B }, +{ 0x1A0C, 0x1A0C, 0x1A0C }, +{ 0x1A0D, 0x1A0D, 0x1A0D }, +{ 0x1A0E, 0x1A0E, 0x1A0E }, +{ 0x1A0F, 0x1A0F, 0x1A0F }, +{ 0x1A10, 0x1A10, 0x1A10 }, +{ 0x1A11, 0x1A11, 0x1A11 }, +{ 0x1A12, 0x1A12, 0x1A12 }, +{ 0x1A13, 0x1A13, 0x1A13 }, +{ 0x1A14, 0x1A14, 0x1A14 }, +{ 0x1A15, 0x1A15, 0x1A15 }, +{ 0x1A16, 0x1A16, 0x1A16 }, +{ 0x1A17, 0x1A17, 0x1A17 }, +{ 0x1A18, 0x1A18, 0x1A18 }, +{ 0x1D00, 0x1D00, 0x1D00 }, +{ 0x1D01, 0x1D01, 0x1D01 }, +{ 0x1D02, 0x1D02, 0x1D02 }, +{ 0x1D03, 0x1D03, 0x1D03 }, +{ 0x1D04, 0x1D04, 0x1D04 }, +{ 0x1D05, 0x1D05, 0x1D05 }, +{ 0x1D06, 0x1D06, 0x1D06 }, +{ 0x1D07, 0x1D07, 0x1D07 }, +{ 0x1D08, 0x1D08, 0x1D08 }, +{ 0x1D09, 0x1D09, 0x1D09 }, +{ 0x1D0A, 0x1D0A, 0x1D0A }, +{ 0x1D0B, 0x1D0B, 0x1D0B }, +{ 0x1D0C, 0x1D0C, 0x1D0C }, +{ 0x1D0D, 0x1D0D, 0x1D0D }, +{ 0x1D0E, 0x1D0E, 0x1D0E }, +{ 0x1D0F, 0x1D0F, 0x1D0F }, +{ 0x1D10, 0x1D10, 0x1D10 }, +{ 0x1D11, 0x1D11, 0x1D11 }, +{ 0x1D12, 0x1D12, 0x1D12 }, +{ 0x1D13, 0x1D13, 0x1D13 }, +{ 0x1D14, 0x1D14, 0x1D14 }, +{ 0x1D15, 0x1D15, 0x1D15 }, +{ 0x1D16, 0x1D16, 0x1D16 }, +{ 0x1D17, 0x1D17, 0x1D17 }, +{ 0x1D18, 0x1D18, 0x1D18 }, +{ 0x1D19, 0x1D19, 0x1D19 }, +{ 0x1D1A, 0x1D1A, 0x1D1A }, +{ 0x1D1B, 0x1D1B, 0x1D1B }, +{ 0x1D1C, 0x1D1C, 0x1D1C }, +{ 0x1D1D, 0x1D1D, 0x1D1D }, +{ 0x1D1E, 0x1D1E, 0x1D1E }, +{ 0x1D1F, 0x1D1F, 0x1D1F }, +{ 0x1D20, 0x1D20, 0x1D20 }, +{ 0x1D21, 0x1D21, 0x1D21 }, +{ 0x1D22, 0x1D22, 0x1D22 }, +{ 0x1D23, 0x1D23, 0x1D23 }, +{ 0x1D24, 0x1D24, 0x1D24 }, +{ 0x1D25, 0x1D25, 0x1D25 }, +{ 0x1D26, 0x1D26, 0x1D26 }, +{ 0x1D27, 0x1D27, 0x1D27 }, +{ 0x1D28, 0x1D28, 0x1D28 }, +{ 0x1D29, 0x1D29, 0x1D29 }, +{ 0x1D2A, 0x1D2A, 0x1D2A }, +{ 0x1D2B, 0x1D2B, 0x1D2B }, +{ 0x1D2C, 0x1D2C, 0x1D2C }, +{ 0x1D2D, 0x1D2D, 0x1D2D }, +{ 0x1D2E, 0x1D2E, 0x1D2E }, +{ 0x1D2F, 0x1D2F, 0x1D2F }, +{ 0x1D30, 0x1D30, 0x1D30 }, +{ 0x1D31, 0x1D31, 0x1D31 }, +{ 0x1D32, 0x1D32, 0x1D32 }, +{ 0x1D33, 0x1D33, 0x1D33 }, +{ 0x1D34, 0x1D34, 0x1D34 }, +{ 0x1D35, 0x1D35, 0x1D35 }, +{ 0x1D36, 0x1D36, 0x1D36 }, +{ 0x1D37, 0x1D37, 0x1D37 }, +{ 0x1D38, 0x1D38, 0x1D38 }, +{ 0x1D39, 0x1D39, 0x1D39 }, +{ 0x1D3A, 0x1D3A, 0x1D3A }, +{ 0x1D3B, 0x1D3B, 0x1D3B }, +{ 0x1D3C, 0x1D3C, 0x1D3C }, +{ 0x1D3D, 0x1D3D, 0x1D3D }, +{ 0x1D3E, 0x1D3E, 0x1D3E }, +{ 0x1D3F, 0x1D3F, 0x1D3F }, +{ 0x1D40, 0x1D40, 0x1D40 }, +{ 0x1D41, 0x1D41, 0x1D41 }, +{ 0x1D42, 0x1D42, 0x1D42 }, +{ 0x1D43, 0x1D43, 0x1D43 }, +{ 0x1D44, 0x1D44, 0x1D44 }, +{ 0x1D45, 0x1D45, 0x1D45 }, +{ 0x1D46, 0x1D46, 0x1D46 }, +{ 0x1D47, 0x1D47, 0x1D47 }, +{ 0x1D48, 0x1D48, 0x1D48 }, +{ 0x1D49, 0x1D49, 0x1D49 }, +{ 0x1D4A, 0x1D4A, 0x1D4A }, +{ 0x1D4B, 0x1D4B, 0x1D4B }, +{ 0x1D4C, 0x1D4C, 0x1D4C }, +{ 0x1D4D, 0x1D4D, 0x1D4D }, +{ 0x1D4E, 0x1D4E, 0x1D4E }, +{ 0x1D4F, 0x1D4F, 0x1D4F }, +{ 0x1D50, 0x1D50, 0x1D50 }, +{ 0x1D51, 0x1D51, 0x1D51 }, +{ 0x1D52, 0x1D52, 0x1D52 }, +{ 0x1D53, 0x1D53, 0x1D53 }, +{ 0x1D54, 0x1D54, 0x1D54 }, +{ 0x1D55, 0x1D55, 0x1D55 }, +{ 0x1D56, 0x1D56, 0x1D56 }, +{ 0x1D57, 0x1D57, 0x1D57 }, +{ 0x1D58, 0x1D58, 0x1D58 }, +{ 0x1D59, 0x1D59, 0x1D59 }, +{ 0x1D5A, 0x1D5A, 0x1D5A }, +{ 0x1D5B, 0x1D5B, 0x1D5B }, +{ 0x1D5C, 0x1D5C, 0x1D5C }, +{ 0x1D5D, 0x1D5D, 0x1D5D }, +{ 0x1D5E, 0x1D5E, 0x1D5E }, +{ 0x1D5F, 0x1D5F, 0x1D5F }, +{ 0x1D60, 0x1D60, 0x1D60 }, +{ 0x1D61, 0x1D61, 0x1D61 }, +{ 0x1D62, 0x1D62, 0x1D62 }, +{ 0x1D63, 0x1D63, 0x1D63 }, +{ 0x1D64, 0x1D64, 0x1D64 }, +{ 0x1D65, 0x1D65, 0x1D65 }, +{ 0x1D66, 0x1D66, 0x1D66 }, +{ 0x1D67, 0x1D67, 0x1D67 }, +{ 0x1D68, 0x1D68, 0x1D68 }, +{ 0x1D69, 0x1D69, 0x1D69 }, +{ 0x1D6A, 0x1D6A, 0x1D6A }, +{ 0x1D6B, 0x1D6B, 0x1D6B }, +{ 0x1D6C, 0x1D6C, 0x1D6C }, +{ 0x1D6D, 0x1D6D, 0x1D6D }, +{ 0x1D6E, 0x1D6E, 0x1D6E }, +{ 0x1D6F, 0x1D6F, 0x1D6F }, +{ 0x1D70, 0x1D70, 0x1D70 }, +{ 0x1D71, 0x1D71, 0x1D71 }, +{ 0x1D72, 0x1D72, 0x1D72 }, +{ 0x1D73, 0x1D73, 0x1D73 }, +{ 0x1D74, 0x1D74, 0x1D74 }, +{ 0x1D75, 0x1D75, 0x1D75 }, +{ 0x1D76, 0x1D76, 0x1D76 }, +{ 0x1D77, 0x1D77, 0x1D77 }, +{ 0x1D78, 0x1D78, 0x1D78 }, +{ 0x1D79, 0x1D79, 0x1D79 }, +{ 0x1D7A, 0x1D7A, 0x1D7A }, +{ 0x1D7B, 0x1D7B, 0x1D7B }, +{ 0x1D7C, 0x1D7C, 0x1D7C }, +{ 0x1D7D, 0x1D7D, 0x1D7D }, +{ 0x1D7E, 0x1D7E, 0x1D7E }, +{ 0x1D7F, 0x1D7F, 0x1D7F }, +{ 0x1D80, 0x1D80, 0x1D80 }, +{ 0x1D81, 0x1D81, 0x1D81 }, +{ 0x1D82, 0x1D82, 0x1D82 }, +{ 0x1D83, 0x1D83, 0x1D83 }, +{ 0x1D84, 0x1D84, 0x1D84 }, +{ 0x1D85, 0x1D85, 0x1D85 }, +{ 0x1D86, 0x1D86, 0x1D86 }, +{ 0x1D87, 0x1D87, 0x1D87 }, +{ 0x1D88, 0x1D88, 0x1D88 }, +{ 0x1D89, 0x1D89, 0x1D89 }, +{ 0x1D8A, 0x1D8A, 0x1D8A }, +{ 0x1D8B, 0x1D8B, 0x1D8B }, +{ 0x1D8C, 0x1D8C, 0x1D8C }, +{ 0x1D8D, 0x1D8D, 0x1D8D }, +{ 0x1D8E, 0x1D8E, 0x1D8E }, +{ 0x1D8F, 0x1D8F, 0x1D8F }, +{ 0x1D90, 0x1D90, 0x1D90 }, +{ 0x1D91, 0x1D91, 0x1D91 }, +{ 0x1D92, 0x1D92, 0x1D92 }, +{ 0x1D93, 0x1D93, 0x1D93 }, +{ 0x1D94, 0x1D94, 0x1D94 }, +{ 0x1D95, 0x1D95, 0x1D95 }, +{ 0x1D96, 0x1D96, 0x1D96 }, +{ 0x1D97, 0x1D97, 0x1D97 }, +{ 0x1D98, 0x1D98, 0x1D98 }, +{ 0x1D99, 0x1D99, 0x1D99 }, +{ 0x1D9A, 0x1D9A, 0x1D9A }, +{ 0x1D9B, 0x1D9B, 0x1D9B }, +{ 0x1D9C, 0x1D9C, 0x1D9C }, +{ 0x1D9D, 0x1D9D, 0x1D9D }, +{ 0x1D9E, 0x1D9E, 0x1D9E }, +{ 0x1D9F, 0x1D9F, 0x1D9F }, +{ 0x1DA0, 0x1DA0, 0x1DA0 }, +{ 0x1DA1, 0x1DA1, 0x1DA1 }, +{ 0x1DA2, 0x1DA2, 0x1DA2 }, +{ 0x1DA3, 0x1DA3, 0x1DA3 }, +{ 0x1DA4, 0x1DA4, 0x1DA4 }, +{ 0x1DA5, 0x1DA5, 0x1DA5 }, +{ 0x1DA6, 0x1DA6, 0x1DA6 }, +{ 0x1DA7, 0x1DA7, 0x1DA7 }, +{ 0x1DA8, 0x1DA8, 0x1DA8 }, +{ 0x1DA9, 0x1DA9, 0x1DA9 }, +{ 0x1DAA, 0x1DAA, 0x1DAA }, +{ 0x1DAB, 0x1DAB, 0x1DAB }, +{ 0x1DAC, 0x1DAC, 0x1DAC }, +{ 0x1DAD, 0x1DAD, 0x1DAD }, +{ 0x1DAE, 0x1DAE, 0x1DAE }, +{ 0x1DAF, 0x1DAF, 0x1DAF }, +{ 0x1DB0, 0x1DB0, 0x1DB0 }, +{ 0x1DB1, 0x1DB1, 0x1DB1 }, +{ 0x1DB2, 0x1DB2, 0x1DB2 }, +{ 0x1DB3, 0x1DB3, 0x1DB3 }, +{ 0x1DB4, 0x1DB4, 0x1DB4 }, +{ 0x1DB5, 0x1DB5, 0x1DB5 }, +{ 0x1DB6, 0x1DB6, 0x1DB6 }, +{ 0x1DB7, 0x1DB7, 0x1DB7 }, +{ 0x1DB8, 0x1DB8, 0x1DB8 }, +{ 0x1DB9, 0x1DB9, 0x1DB9 }, +{ 0x1DBA, 0x1DBA, 0x1DBA }, +{ 0x1DBB, 0x1DBB, 0x1DBB }, +{ 0x1DBC, 0x1DBC, 0x1DBC }, +{ 0x1DBD, 0x1DBD, 0x1DBD }, +{ 0x1DBE, 0x1DBE, 0x1DBE }, +{ 0x1DBF, 0x1DBF, 0x1DBF }, +{ 0x1DC0, 0x1DC0, 0x1DC0 }, +{ 0x1DC1, 0x1DC1, 0x1DC1 }, +{ 0x1DC2, 0x1DC2, 0x1DC2 }, +{ 0x1DC3, 0x1DC3, 0x1DC3 }, +{ 0x1E00, 0x1E00, 0x1E01 }, +{ 0x1E01, 0x1E00, 0x1E01 }, +{ 0x1E02, 0x1E02, 0x1E03 }, +{ 0x1E03, 0x1E02, 0x1E03 }, +{ 0x1E04, 0x1E04, 0x1E05 }, +{ 0x1E05, 0x1E04, 0x1E05 }, +{ 0x1E06, 0x1E06, 0x1E07 }, +{ 0x1E07, 0x1E06, 0x1E07 }, +{ 0x1E08, 0x1E08, 0x1E09 }, +{ 0x1E09, 0x1E08, 0x1E09 }, +{ 0x1E0A, 0x1E0A, 0x1E0B }, +{ 0x1E0B, 0x1E0A, 0x1E0B }, +{ 0x1E0C, 0x1E0C, 0x1E0D }, +{ 0x1E0D, 0x1E0C, 0x1E0D }, +{ 0x1E0E, 0x1E0E, 0x1E0F }, +{ 0x1E0F, 0x1E0E, 0x1E0F }, +{ 0x1E10, 0x1E10, 0x1E11 }, +{ 0x1E11, 0x1E10, 0x1E11 }, +{ 0x1E12, 0x1E12, 0x1E13 }, +{ 0x1E13, 0x1E12, 0x1E13 }, +{ 0x1E14, 0x1E14, 0x1E15 }, +{ 0x1E15, 0x1E14, 0x1E15 }, +{ 0x1E16, 0x1E16, 0x1E17 }, +{ 0x1E17, 0x1E16, 0x1E17 }, +{ 0x1E18, 0x1E18, 0x1E19 }, +{ 0x1E19, 0x1E18, 0x1E19 }, +{ 0x1E1A, 0x1E1A, 0x1E1B }, +{ 0x1E1B, 0x1E1A, 0x1E1B }, +{ 0x1E1C, 0x1E1C, 0x1E1D }, +{ 0x1E1D, 0x1E1C, 0x1E1D }, +{ 0x1E1E, 0x1E1E, 0x1E1F }, +{ 0x1E1F, 0x1E1E, 0x1E1F }, +{ 0x1E20, 0x1E20, 0x1E21 }, +{ 0x1E21, 0x1E20, 0x1E21 }, +{ 0x1E22, 0x1E22, 0x1E23 }, +{ 0x1E23, 0x1E22, 0x1E23 }, +{ 0x1E24, 0x1E24, 0x1E25 }, +{ 0x1E25, 0x1E24, 0x1E25 }, +{ 0x1E26, 0x1E26, 0x1E27 }, +{ 0x1E27, 0x1E26, 0x1E27 }, +{ 0x1E28, 0x1E28, 0x1E29 }, +{ 0x1E29, 0x1E28, 0x1E29 }, +{ 0x1E2A, 0x1E2A, 0x1E2B }, +{ 0x1E2B, 0x1E2A, 0x1E2B }, +{ 0x1E2C, 0x1E2C, 0x1E2D }, +{ 0x1E2D, 0x1E2C, 0x1E2D }, +{ 0x1E2E, 0x1E2E, 0x1E2F }, +{ 0x1E2F, 0x1E2E, 0x1E2F }, +{ 0x1E30, 0x1E30, 0x1E31 }, +{ 0x1E31, 0x1E30, 0x1E31 }, +{ 0x1E32, 0x1E32, 0x1E33 }, +{ 0x1E33, 0x1E32, 0x1E33 }, +{ 0x1E34, 0x1E34, 0x1E35 }, +{ 0x1E35, 0x1E34, 0x1E35 }, +{ 0x1E36, 0x1E36, 0x1E37 }, +{ 0x1E37, 0x1E36, 0x1E37 }, +{ 0x1E38, 0x1E38, 0x1E39 }, +{ 0x1E39, 0x1E38, 0x1E39 }, +{ 0x1E3A, 0x1E3A, 0x1E3B }, +{ 0x1E3B, 0x1E3A, 0x1E3B }, +{ 0x1E3C, 0x1E3C, 0x1E3D }, +{ 0x1E3D, 0x1E3C, 0x1E3D }, +{ 0x1E3E, 0x1E3E, 0x1E3F }, +{ 0x1E3F, 0x1E3E, 0x1E3F }, +{ 0x1E40, 0x1E40, 0x1E41 }, +{ 0x1E41, 0x1E40, 0x1E41 }, +{ 0x1E42, 0x1E42, 0x1E43 }, +{ 0x1E43, 0x1E42, 0x1E43 }, +{ 0x1E44, 0x1E44, 0x1E45 }, +{ 0x1E45, 0x1E44, 0x1E45 }, +{ 0x1E46, 0x1E46, 0x1E47 }, +{ 0x1E47, 0x1E46, 0x1E47 }, +{ 0x1E48, 0x1E48, 0x1E49 }, +{ 0x1E49, 0x1E48, 0x1E49 }, +{ 0x1E4A, 0x1E4A, 0x1E4B }, +{ 0x1E4B, 0x1E4A, 0x1E4B }, +{ 0x1E4C, 0x1E4C, 0x1E4D }, +{ 0x1E4D, 0x1E4C, 0x1E4D }, +{ 0x1E4E, 0x1E4E, 0x1E4F }, +{ 0x1E4F, 0x1E4E, 0x1E4F }, +{ 0x1E50, 0x1E50, 0x1E51 }, +{ 0x1E51, 0x1E50, 0x1E51 }, +{ 0x1E52, 0x1E52, 0x1E53 }, +{ 0x1E53, 0x1E52, 0x1E53 }, +{ 0x1E54, 0x1E54, 0x1E55 }, +{ 0x1E55, 0x1E54, 0x1E55 }, +{ 0x1E56, 0x1E56, 0x1E57 }, +{ 0x1E57, 0x1E56, 0x1E57 }, +{ 0x1E58, 0x1E58, 0x1E59 }, +{ 0x1E59, 0x1E58, 0x1E59 }, +{ 0x1E5A, 0x1E5A, 0x1E5B }, +{ 0x1E5B, 0x1E5A, 0x1E5B }, +{ 0x1E5C, 0x1E5C, 0x1E5D }, +{ 0x1E5D, 0x1E5C, 0x1E5D }, +{ 0x1E5E, 0x1E5E, 0x1E5F }, +{ 0x1E5F, 0x1E5E, 0x1E5F }, +{ 0x1E60, 0x1E60, 0x1E61 }, +{ 0x1E61, 0x1E60, 0x1E61 }, +{ 0x1E62, 0x1E62, 0x1E63 }, +{ 0x1E63, 0x1E62, 0x1E63 }, +{ 0x1E64, 0x1E64, 0x1E65 }, +{ 0x1E65, 0x1E64, 0x1E65 }, +{ 0x1E66, 0x1E66, 0x1E67 }, +{ 0x1E67, 0x1E66, 0x1E67 }, +{ 0x1E68, 0x1E68, 0x1E69 }, +{ 0x1E69, 0x1E68, 0x1E69 }, +{ 0x1E6A, 0x1E6A, 0x1E6B }, +{ 0x1E6B, 0x1E6A, 0x1E6B }, +{ 0x1E6C, 0x1E6C, 0x1E6D }, +{ 0x1E6D, 0x1E6C, 0x1E6D }, +{ 0x1E6E, 0x1E6E, 0x1E6F }, +{ 0x1E6F, 0x1E6E, 0x1E6F }, +{ 0x1E70, 0x1E70, 0x1E71 }, +{ 0x1E71, 0x1E70, 0x1E71 }, +{ 0x1E72, 0x1E72, 0x1E73 }, +{ 0x1E73, 0x1E72, 0x1E73 }, +{ 0x1E74, 0x1E74, 0x1E75 }, +{ 0x1E75, 0x1E74, 0x1E75 }, +{ 0x1E76, 0x1E76, 0x1E77 }, +{ 0x1E77, 0x1E76, 0x1E77 }, +{ 0x1E78, 0x1E78, 0x1E79 }, +{ 0x1E79, 0x1E78, 0x1E79 }, +{ 0x1E7A, 0x1E7A, 0x1E7B }, +{ 0x1E7B, 0x1E7A, 0x1E7B }, +{ 0x1E7C, 0x1E7C, 0x1E7D }, +{ 0x1E7D, 0x1E7C, 0x1E7D }, +{ 0x1E7E, 0x1E7E, 0x1E7F }, +{ 0x1E7F, 0x1E7E, 0x1E7F }, +{ 0x1E80, 0x1E80, 0x1E81 }, +{ 0x1E81, 0x1E80, 0x1E81 }, +{ 0x1E82, 0x1E82, 0x1E83 }, +{ 0x1E83, 0x1E82, 0x1E83 }, +{ 0x1E84, 0x1E84, 0x1E85 }, +{ 0x1E85, 0x1E84, 0x1E85 }, +{ 0x1E86, 0x1E86, 0x1E87 }, +{ 0x1E87, 0x1E86, 0x1E87 }, +{ 0x1E88, 0x1E88, 0x1E89 }, +{ 0x1E89, 0x1E88, 0x1E89 }, +{ 0x1E8A, 0x1E8A, 0x1E8B }, +{ 0x1E8B, 0x1E8A, 0x1E8B }, +{ 0x1E8C, 0x1E8C, 0x1E8D }, +{ 0x1E8D, 0x1E8C, 0x1E8D }, +{ 0x1E8E, 0x1E8E, 0x1E8F }, +{ 0x1E8F, 0x1E8E, 0x1E8F }, +{ 0x1E90, 0x1E90, 0x1E91 }, +{ 0x1E91, 0x1E90, 0x1E91 }, +{ 0x1E92, 0x1E92, 0x1E93 }, +{ 0x1E93, 0x1E92, 0x1E93 }, +{ 0x1E94, 0x1E94, 0x1E95 }, +{ 0x1E95, 0x1E94, 0x1E95 }, +{ 0x1E96, 0x1E96, 0x1E96 }, +{ 0x1E97, 0x1E97, 0x1E97 }, +{ 0x1E98, 0x1E98, 0x1E98 }, +{ 0x1E99, 0x1E99, 0x1E99 }, +{ 0x1E9A, 0x1E9A, 0x1E9A }, +{ 0x1E9B, 0x1E60, 0x1E9B }, +{ 0x1EA0, 0x1EA0, 0x1EA1 }, +{ 0x1EA1, 0x1EA0, 0x1EA1 }, +{ 0x1EA2, 0x1EA2, 0x1EA3 }, +{ 0x1EA3, 0x1EA2, 0x1EA3 }, +{ 0x1EA4, 0x1EA4, 0x1EA5 }, +{ 0x1EA5, 0x1EA4, 0x1EA5 }, +{ 0x1EA6, 0x1EA6, 0x1EA7 }, +{ 0x1EA7, 0x1EA6, 0x1EA7 }, +{ 0x1EA8, 0x1EA8, 0x1EA9 }, +{ 0x1EA9, 0x1EA8, 0x1EA9 }, +{ 0x1EAA, 0x1EAA, 0x1EAB }, +{ 0x1EAB, 0x1EAA, 0x1EAB }, +{ 0x1EAC, 0x1EAC, 0x1EAD }, +{ 0x1EAD, 0x1EAC, 0x1EAD }, +{ 0x1EAE, 0x1EAE, 0x1EAF }, +{ 0x1EAF, 0x1EAE, 0x1EAF }, +{ 0x1EB0, 0x1EB0, 0x1EB1 }, +{ 0x1EB1, 0x1EB0, 0x1EB1 }, +{ 0x1EB2, 0x1EB2, 0x1EB3 }, +{ 0x1EB3, 0x1EB2, 0x1EB3 }, +{ 0x1EB4, 0x1EB4, 0x1EB5 }, +{ 0x1EB5, 0x1EB4, 0x1EB5 }, +{ 0x1EB6, 0x1EB6, 0x1EB7 }, +{ 0x1EB7, 0x1EB6, 0x1EB7 }, +{ 0x1EB8, 0x1EB8, 0x1EB9 }, +{ 0x1EB9, 0x1EB8, 0x1EB9 }, +{ 0x1EBA, 0x1EBA, 0x1EBB }, +{ 0x1EBB, 0x1EBA, 0x1EBB }, +{ 0x1EBC, 0x1EBC, 0x1EBD }, +{ 0x1EBD, 0x1EBC, 0x1EBD }, +{ 0x1EBE, 0x1EBE, 0x1EBF }, +{ 0x1EBF, 0x1EBE, 0x1EBF }, +{ 0x1EC0, 0x1EC0, 0x1EC1 }, +{ 0x1EC1, 0x1EC0, 0x1EC1 }, +{ 0x1EC2, 0x1EC2, 0x1EC3 }, +{ 0x1EC3, 0x1EC2, 0x1EC3 }, +{ 0x1EC4, 0x1EC4, 0x1EC5 }, +{ 0x1EC5, 0x1EC4, 0x1EC5 }, +{ 0x1EC6, 0x1EC6, 0x1EC7 }, +{ 0x1EC7, 0x1EC6, 0x1EC7 }, +{ 0x1EC8, 0x1EC8, 0x1EC9 }, +{ 0x1EC9, 0x1EC8, 0x1EC9 }, +{ 0x1ECA, 0x1ECA, 0x1ECB }, +{ 0x1ECB, 0x1ECA, 0x1ECB }, +{ 0x1ECC, 0x1ECC, 0x1ECD }, +{ 0x1ECD, 0x1ECC, 0x1ECD }, +{ 0x1ECE, 0x1ECE, 0x1ECF }, +{ 0x1ECF, 0x1ECE, 0x1ECF }, +{ 0x1ED0, 0x1ED0, 0x1ED1 }, +{ 0x1ED1, 0x1ED0, 0x1ED1 }, +{ 0x1ED2, 0x1ED2, 0x1ED3 }, +{ 0x1ED3, 0x1ED2, 0x1ED3 }, +{ 0x1ED4, 0x1ED4, 0x1ED5 }, +{ 0x1ED5, 0x1ED4, 0x1ED5 }, +{ 0x1ED6, 0x1ED6, 0x1ED7 }, +{ 0x1ED7, 0x1ED6, 0x1ED7 }, +{ 0x1ED8, 0x1ED8, 0x1ED9 }, +{ 0x1ED9, 0x1ED8, 0x1ED9 }, +{ 0x1EDA, 0x1EDA, 0x1EDB }, +{ 0x1EDB, 0x1EDA, 0x1EDB }, +{ 0x1EDC, 0x1EDC, 0x1EDD }, +{ 0x1EDD, 0x1EDC, 0x1EDD }, +{ 0x1EDE, 0x1EDE, 0x1EDF }, +{ 0x1EDF, 0x1EDE, 0x1EDF }, +{ 0x1EE0, 0x1EE0, 0x1EE1 }, +{ 0x1EE1, 0x1EE0, 0x1EE1 }, +{ 0x1EE2, 0x1EE2, 0x1EE3 }, +{ 0x1EE3, 0x1EE2, 0x1EE3 }, +{ 0x1EE4, 0x1EE4, 0x1EE5 }, +{ 0x1EE5, 0x1EE4, 0x1EE5 }, +{ 0x1EE6, 0x1EE6, 0x1EE7 }, +{ 0x1EE7, 0x1EE6, 0x1EE7 }, +{ 0x1EE8, 0x1EE8, 0x1EE9 }, +{ 0x1EE9, 0x1EE8, 0x1EE9 }, +{ 0x1EEA, 0x1EEA, 0x1EEB }, +{ 0x1EEB, 0x1EEA, 0x1EEB }, +{ 0x1EEC, 0x1EEC, 0x1EED }, +{ 0x1EED, 0x1EEC, 0x1EED }, +{ 0x1EEE, 0x1EEE, 0x1EEF }, +{ 0x1EEF, 0x1EEE, 0x1EEF }, +{ 0x1EF0, 0x1EF0, 0x1EF1 }, +{ 0x1EF1, 0x1EF0, 0x1EF1 }, +{ 0x1EF2, 0x1EF2, 0x1EF3 }, +{ 0x1EF3, 0x1EF2, 0x1EF3 }, +{ 0x1EF4, 0x1EF4, 0x1EF5 }, +{ 0x1EF5, 0x1EF4, 0x1EF5 }, +{ 0x1EF6, 0x1EF6, 0x1EF7 }, +{ 0x1EF7, 0x1EF6, 0x1EF7 }, +{ 0x1EF8, 0x1EF8, 0x1EF9 }, +{ 0x1EF9, 0x1EF8, 0x1EF9 }, +{ 0x1F00, 0x1F08, 0x1F00 }, +{ 0x1F01, 0x1F09, 0x1F01 }, +{ 0x1F02, 0x1F0A, 0x1F02 }, +{ 0x1F03, 0x1F0B, 0x1F03 }, +{ 0x1F04, 0x1F0C, 0x1F04 }, +{ 0x1F05, 0x1F0D, 0x1F05 }, +{ 0x1F06, 0x1F0E, 0x1F06 }, +{ 0x1F07, 0x1F0F, 0x1F07 }, +{ 0x1F08, 0x1F08, 0x1F00 }, +{ 0x1F09, 0x1F09, 0x1F01 }, +{ 0x1F0A, 0x1F0A, 0x1F02 }, +{ 0x1F0B, 0x1F0B, 0x1F03 }, +{ 0x1F0C, 0x1F0C, 0x1F04 }, +{ 0x1F0D, 0x1F0D, 0x1F05 }, +{ 0x1F0E, 0x1F0E, 0x1F06 }, +{ 0x1F0F, 0x1F0F, 0x1F07 }, +{ 0x1F10, 0x1F18, 0x1F10 }, +{ 0x1F11, 0x1F19, 0x1F11 }, +{ 0x1F12, 0x1F1A, 0x1F12 }, +{ 0x1F13, 0x1F1B, 0x1F13 }, +{ 0x1F14, 0x1F1C, 0x1F14 }, +{ 0x1F15, 0x1F1D, 0x1F15 }, +{ 0x1F18, 0x1F18, 0x1F10 }, +{ 0x1F19, 0x1F19, 0x1F11 }, +{ 0x1F1A, 0x1F1A, 0x1F12 }, +{ 0x1F1B, 0x1F1B, 0x1F13 }, +{ 0x1F1C, 0x1F1C, 0x1F14 }, +{ 0x1F1D, 0x1F1D, 0x1F15 }, +{ 0x1F20, 0x1F28, 0x1F20 }, +{ 0x1F21, 0x1F29, 0x1F21 }, +{ 0x1F22, 0x1F2A, 0x1F22 }, +{ 0x1F23, 0x1F2B, 0x1F23 }, +{ 0x1F24, 0x1F2C, 0x1F24 }, +{ 0x1F25, 0x1F2D, 0x1F25 }, +{ 0x1F26, 0x1F2E, 0x1F26 }, +{ 0x1F27, 0x1F2F, 0x1F27 }, +{ 0x1F28, 0x1F28, 0x1F20 }, +{ 0x1F29, 0x1F29, 0x1F21 }, +{ 0x1F2A, 0x1F2A, 0x1F22 }, +{ 0x1F2B, 0x1F2B, 0x1F23 }, +{ 0x1F2C, 0x1F2C, 0x1F24 }, +{ 0x1F2D, 0x1F2D, 0x1F25 }, +{ 0x1F2E, 0x1F2E, 0x1F26 }, +{ 0x1F2F, 0x1F2F, 0x1F27 }, +{ 0x1F30, 0x1F38, 0x1F30 }, +{ 0x1F31, 0x1F39, 0x1F31 }, +{ 0x1F32, 0x1F3A, 0x1F32 }, +{ 0x1F33, 0x1F3B, 0x1F33 }, +{ 0x1F34, 0x1F3C, 0x1F34 }, +{ 0x1F35, 0x1F3D, 0x1F35 }, +{ 0x1F36, 0x1F3E, 0x1F36 }, +{ 0x1F37, 0x1F3F, 0x1F37 }, +{ 0x1F38, 0x1F38, 0x1F30 }, +{ 0x1F39, 0x1F39, 0x1F31 }, +{ 0x1F3A, 0x1F3A, 0x1F32 }, +{ 0x1F3B, 0x1F3B, 0x1F33 }, +{ 0x1F3C, 0x1F3C, 0x1F34 }, +{ 0x1F3D, 0x1F3D, 0x1F35 }, +{ 0x1F3E, 0x1F3E, 0x1F36 }, +{ 0x1F3F, 0x1F3F, 0x1F37 }, +{ 0x1F40, 0x1F48, 0x1F40 }, +{ 0x1F41, 0x1F49, 0x1F41 }, +{ 0x1F42, 0x1F4A, 0x1F42 }, +{ 0x1F43, 0x1F4B, 0x1F43 }, +{ 0x1F44, 0x1F4C, 0x1F44 }, +{ 0x1F45, 0x1F4D, 0x1F45 }, +{ 0x1F48, 0x1F48, 0x1F40 }, +{ 0x1F49, 0x1F49, 0x1F41 }, +{ 0x1F4A, 0x1F4A, 0x1F42 }, +{ 0x1F4B, 0x1F4B, 0x1F43 }, +{ 0x1F4C, 0x1F4C, 0x1F44 }, +{ 0x1F4D, 0x1F4D, 0x1F45 }, +{ 0x1F50, 0x1F50, 0x1F50 }, +{ 0x1F51, 0x1F59, 0x1F51 }, +{ 0x1F52, 0x1F52, 0x1F52 }, +{ 0x1F53, 0x1F5B, 0x1F53 }, +{ 0x1F54, 0x1F54, 0x1F54 }, +{ 0x1F55, 0x1F5D, 0x1F55 }, +{ 0x1F56, 0x1F56, 0x1F56 }, +{ 0x1F57, 0x1F5F, 0x1F57 }, +{ 0x1F59, 0x1F59, 0x1F51 }, +{ 0x1F5B, 0x1F5B, 0x1F53 }, +{ 0x1F5D, 0x1F5D, 0x1F55 }, +{ 0x1F5F, 0x1F5F, 0x1F57 }, +{ 0x1F60, 0x1F68, 0x1F60 }, +{ 0x1F61, 0x1F69, 0x1F61 }, +{ 0x1F62, 0x1F6A, 0x1F62 }, +{ 0x1F63, 0x1F6B, 0x1F63 }, +{ 0x1F64, 0x1F6C, 0x1F64 }, +{ 0x1F65, 0x1F6D, 0x1F65 }, +{ 0x1F66, 0x1F6E, 0x1F66 }, +{ 0x1F67, 0x1F6F, 0x1F67 }, +{ 0x1F68, 0x1F68, 0x1F60 }, +{ 0x1F69, 0x1F69, 0x1F61 }, +{ 0x1F6A, 0x1F6A, 0x1F62 }, +{ 0x1F6B, 0x1F6B, 0x1F63 }, +{ 0x1F6C, 0x1F6C, 0x1F64 }, +{ 0x1F6D, 0x1F6D, 0x1F65 }, +{ 0x1F6E, 0x1F6E, 0x1F66 }, +{ 0x1F6F, 0x1F6F, 0x1F67 }, +{ 0x1F70, 0x1FBA, 0x1F70 }, +{ 0x1F71, 0x1FBB, 0x1F71 }, +{ 0x1F72, 0x1FC8, 0x1F72 }, +{ 0x1F73, 0x1FC9, 0x1F73 }, +{ 0x1F74, 0x1FCA, 0x1F74 }, +{ 0x1F75, 0x1FCB, 0x1F75 }, +{ 0x1F76, 0x1FDA, 0x1F76 }, +{ 0x1F77, 0x1FDB, 0x1F77 }, +{ 0x1F78, 0x1FF8, 0x1F78 }, +{ 0x1F79, 0x1FF9, 0x1F79 }, +{ 0x1F7A, 0x1FEA, 0x1F7A }, +{ 0x1F7B, 0x1FEB, 0x1F7B }, +{ 0x1F7C, 0x1FFA, 0x1F7C }, +{ 0x1F7D, 0x1FFB, 0x1F7D }, +{ 0x1F80, 0x1F88, 0x1F80 }, +{ 0x1F81, 0x1F89, 0x1F81 }, +{ 0x1F82, 0x1F8A, 0x1F82 }, +{ 0x1F83, 0x1F8B, 0x1F83 }, +{ 0x1F84, 0x1F8C, 0x1F84 }, +{ 0x1F85, 0x1F8D, 0x1F85 }, +{ 0x1F86, 0x1F8E, 0x1F86 }, +{ 0x1F87, 0x1F8F, 0x1F87 }, +{ 0x1F88, 0x1F88, 0x1F80 }, +{ 0x1F89, 0x1F89, 0x1F81 }, +{ 0x1F8A, 0x1F8A, 0x1F82 }, +{ 0x1F8B, 0x1F8B, 0x1F83 }, +{ 0x1F8C, 0x1F8C, 0x1F84 }, +{ 0x1F8D, 0x1F8D, 0x1F85 }, +{ 0x1F8E, 0x1F8E, 0x1F86 }, +{ 0x1F8F, 0x1F8F, 0x1F87 }, +{ 0x1F90, 0x1F98, 0x1F90 }, +{ 0x1F91, 0x1F99, 0x1F91 }, +{ 0x1F92, 0x1F9A, 0x1F92 }, +{ 0x1F93, 0x1F9B, 0x1F93 }, +{ 0x1F94, 0x1F9C, 0x1F94 }, +{ 0x1F95, 0x1F9D, 0x1F95 }, +{ 0x1F96, 0x1F9E, 0x1F96 }, +{ 0x1F97, 0x1F9F, 0x1F97 }, +{ 0x1F98, 0x1F98, 0x1F90 }, +{ 0x1F99, 0x1F99, 0x1F91 }, +{ 0x1F9A, 0x1F9A, 0x1F92 }, +{ 0x1F9B, 0x1F9B, 0x1F93 }, +{ 0x1F9C, 0x1F9C, 0x1F94 }, +{ 0x1F9D, 0x1F9D, 0x1F95 }, +{ 0x1F9E, 0x1F9E, 0x1F96 }, +{ 0x1F9F, 0x1F9F, 0x1F97 }, +{ 0x1FA0, 0x1FA8, 0x1FA0 }, +{ 0x1FA1, 0x1FA9, 0x1FA1 }, +{ 0x1FA2, 0x1FAA, 0x1FA2 }, +{ 0x1FA3, 0x1FAB, 0x1FA3 }, +{ 0x1FA4, 0x1FAC, 0x1FA4 }, +{ 0x1FA5, 0x1FAD, 0x1FA5 }, +{ 0x1FA6, 0x1FAE, 0x1FA6 }, +{ 0x1FA7, 0x1FAF, 0x1FA7 }, +{ 0x1FA8, 0x1FA8, 0x1FA0 }, +{ 0x1FA9, 0x1FA9, 0x1FA1 }, +{ 0x1FAA, 0x1FAA, 0x1FA2 }, +{ 0x1FAB, 0x1FAB, 0x1FA3 }, +{ 0x1FAC, 0x1FAC, 0x1FA4 }, +{ 0x1FAD, 0x1FAD, 0x1FA5 }, +{ 0x1FAE, 0x1FAE, 0x1FA6 }, +{ 0x1FAF, 0x1FAF, 0x1FA7 }, +{ 0x1FB0, 0x1FB8, 0x1FB0 }, +{ 0x1FB1, 0x1FB9, 0x1FB1 }, +{ 0x1FB2, 0x1FB2, 0x1FB2 }, +{ 0x1FB3, 0x1FBC, 0x1FB3 }, +{ 0x1FB4, 0x1FB4, 0x1FB4 }, +{ 0x1FB6, 0x1FB6, 0x1FB6 }, +{ 0x1FB7, 0x1FB7, 0x1FB7 }, +{ 0x1FB8, 0x1FB8, 0x1FB0 }, +{ 0x1FB9, 0x1FB9, 0x1FB1 }, +{ 0x1FBA, 0x1FBA, 0x1F70 }, +{ 0x1FBB, 0x1FBB, 0x1F71 }, +{ 0x1FBC, 0x1FBC, 0x1FB3 }, +{ 0x1FBE, 0x0399, 0x1FBE }, +{ 0x1FC2, 0x1FC2, 0x1FC2 }, +{ 0x1FC3, 0x1FCC, 0x1FC3 }, +{ 0x1FC4, 0x1FC4, 0x1FC4 }, +{ 0x1FC6, 0x1FC6, 0x1FC6 }, +{ 0x1FC7, 0x1FC7, 0x1FC7 }, +{ 0x1FC8, 0x1FC8, 0x1F72 }, +{ 0x1FC9, 0x1FC9, 0x1F73 }, +{ 0x1FCA, 0x1FCA, 0x1F74 }, +{ 0x1FCB, 0x1FCB, 0x1F75 }, +{ 0x1FCC, 0x1FCC, 0x1FC3 }, +{ 0x1FD0, 0x1FD8, 0x1FD0 }, +{ 0x1FD1, 0x1FD9, 0x1FD1 }, +{ 0x1FD2, 0x1FD2, 0x1FD2 }, +{ 0x1FD3, 0x1FD3, 0x1FD3 }, +{ 0x1FD6, 0x1FD6, 0x1FD6 }, +{ 0x1FD7, 0x1FD7, 0x1FD7 }, +{ 0x1FD8, 0x1FD8, 0x1FD0 }, +{ 0x1FD9, 0x1FD9, 0x1FD1 }, +{ 0x1FDA, 0x1FDA, 0x1F76 }, +{ 0x1FDB, 0x1FDB, 0x1F77 }, +{ 0x1FE0, 0x1FE8, 0x1FE0 }, +{ 0x1FE1, 0x1FE9, 0x1FE1 }, +{ 0x1FE2, 0x1FE2, 0x1FE2 }, +{ 0x1FE3, 0x1FE3, 0x1FE3 }, +{ 0x1FE4, 0x1FE4, 0x1FE4 }, +{ 0x1FE5, 0x1FEC, 0x1FE5 }, +{ 0x1FE6, 0x1FE6, 0x1FE6 }, +{ 0x1FE7, 0x1FE7, 0x1FE7 }, +{ 0x1FE8, 0x1FE8, 0x1FE0 }, +{ 0x1FE9, 0x1FE9, 0x1FE1 }, +{ 0x1FEA, 0x1FEA, 0x1F7A }, +{ 0x1FEB, 0x1FEB, 0x1F7B }, +{ 0x1FEC, 0x1FEC, 0x1FE5 }, +{ 0x1FF2, 0x1FF2, 0x1FF2 }, +{ 0x1FF3, 0x1FFC, 0x1FF3 }, +{ 0x1FF4, 0x1FF4, 0x1FF4 }, +{ 0x1FF6, 0x1FF6, 0x1FF6 }, +{ 0x1FF7, 0x1FF7, 0x1FF7 }, +{ 0x1FF8, 0x1FF8, 0x1F78 }, +{ 0x1FF9, 0x1FF9, 0x1F79 }, +{ 0x1FFA, 0x1FFA, 0x1F7C }, +{ 0x1FFB, 0x1FFB, 0x1F7D }, +{ 0x1FFC, 0x1FFC, 0x1FF3 }, +{ 0x2071, 0x2071, 0x2071 }, +{ 0x207F, 0x207F, 0x207F }, +{ 0x2090, 0x2090, 0x2090 }, +{ 0x2091, 0x2091, 0x2091 }, +{ 0x2092, 0x2092, 0x2092 }, +{ 0x2093, 0x2093, 0x2093 }, +{ 0x2094, 0x2094, 0x2094 }, +{ 0x20D0, 0x20D0, 0x20D0 }, +{ 0x20D1, 0x20D1, 0x20D1 }, +{ 0x20D2, 0x20D2, 0x20D2 }, +{ 0x20D3, 0x20D3, 0x20D3 }, +{ 0x20D4, 0x20D4, 0x20D4 }, +{ 0x20D5, 0x20D5, 0x20D5 }, +{ 0x20D6, 0x20D6, 0x20D6 }, +{ 0x20D7, 0x20D7, 0x20D7 }, +{ 0x20D8, 0x20D8, 0x20D8 }, +{ 0x20D9, 0x20D9, 0x20D9 }, +{ 0x20DA, 0x20DA, 0x20DA }, +{ 0x20DB, 0x20DB, 0x20DB }, +{ 0x20DC, 0x20DC, 0x20DC }, +{ 0x20E1, 0x20E1, 0x20E1 }, +{ 0x20E5, 0x20E5, 0x20E5 }, +{ 0x20E6, 0x20E6, 0x20E6 }, +{ 0x20E7, 0x20E7, 0x20E7 }, +{ 0x20E8, 0x20E8, 0x20E8 }, +{ 0x20E9, 0x20E9, 0x20E9 }, +{ 0x20EA, 0x20EA, 0x20EA }, +{ 0x20EB, 0x20EB, 0x20EB }, +{ 0x2102, 0x2102, 0x2102 }, +{ 0x2107, 0x2107, 0x2107 }, +{ 0x210A, 0x210A, 0x210A }, +{ 0x210B, 0x210B, 0x210B }, +{ 0x210C, 0x210C, 0x210C }, +{ 0x210D, 0x210D, 0x210D }, +{ 0x210E, 0x210E, 0x210E }, +{ 0x210F, 0x210F, 0x210F }, +{ 0x2110, 0x2110, 0x2110 }, +{ 0x2111, 0x2111, 0x2111 }, +{ 0x2112, 0x2112, 0x2112 }, +{ 0x2113, 0x2113, 0x2113 }, +{ 0x2115, 0x2115, 0x2115 }, +{ 0x2119, 0x2119, 0x2119 }, +{ 0x211A, 0x211A, 0x211A }, +{ 0x211B, 0x211B, 0x211B }, +{ 0x211C, 0x211C, 0x211C }, +{ 0x211D, 0x211D, 0x211D }, +{ 0x2124, 0x2124, 0x2124 }, +{ 0x2126, 0x2126, 0x03C9 }, +{ 0x2128, 0x2128, 0x2128 }, +{ 0x212A, 0x212A, 0x006B }, +{ 0x212B, 0x212B, 0x00E5 }, +{ 0x212C, 0x212C, 0x212C }, +{ 0x212D, 0x212D, 0x212D }, +{ 0x212F, 0x212F, 0x212F }, +{ 0x2130, 0x2130, 0x2130 }, +{ 0x2131, 0x2131, 0x2131 }, +{ 0x2133, 0x2133, 0x2133 }, +{ 0x2134, 0x2134, 0x2134 }, +{ 0x2135, 0x2135, 0x2135 }, +{ 0x2136, 0x2136, 0x2136 }, +{ 0x2137, 0x2137, 0x2137 }, +{ 0x2138, 0x2138, 0x2138 }, +{ 0x2139, 0x2139, 0x2139 }, +{ 0x213C, 0x213C, 0x213C }, +{ 0x213D, 0x213D, 0x213D }, +{ 0x213E, 0x213E, 0x213E }, +{ 0x213F, 0x213F, 0x213F }, +{ 0x2145, 0x2145, 0x2145 }, +{ 0x2146, 0x2146, 0x2146 }, +{ 0x2147, 0x2147, 0x2147 }, +{ 0x2148, 0x2148, 0x2148 }, +{ 0x2149, 0x2149, 0x2149 }, +{ 0x2C00, 0x2C00, 0x2C30 }, +{ 0x2C01, 0x2C01, 0x2C31 }, +{ 0x2C02, 0x2C02, 0x2C32 }, +{ 0x2C03, 0x2C03, 0x2C33 }, +{ 0x2C04, 0x2C04, 0x2C34 }, +{ 0x2C05, 0x2C05, 0x2C35 }, +{ 0x2C06, 0x2C06, 0x2C36 }, +{ 0x2C07, 0x2C07, 0x2C37 }, +{ 0x2C08, 0x2C08, 0x2C38 }, +{ 0x2C09, 0x2C09, 0x2C39 }, +{ 0x2C0A, 0x2C0A, 0x2C3A }, +{ 0x2C0B, 0x2C0B, 0x2C3B }, +{ 0x2C0C, 0x2C0C, 0x2C3C }, +{ 0x2C0D, 0x2C0D, 0x2C3D }, +{ 0x2C0E, 0x2C0E, 0x2C3E }, +{ 0x2C0F, 0x2C0F, 0x2C3F }, +{ 0x2C10, 0x2C10, 0x2C40 }, +{ 0x2C11, 0x2C11, 0x2C41 }, +{ 0x2C12, 0x2C12, 0x2C42 }, +{ 0x2C13, 0x2C13, 0x2C43 }, +{ 0x2C14, 0x2C14, 0x2C44 }, +{ 0x2C15, 0x2C15, 0x2C45 }, +{ 0x2C16, 0x2C16, 0x2C46 }, +{ 0x2C17, 0x2C17, 0x2C47 }, +{ 0x2C18, 0x2C18, 0x2C48 }, +{ 0x2C19, 0x2C19, 0x2C49 }, +{ 0x2C1A, 0x2C1A, 0x2C4A }, +{ 0x2C1B, 0x2C1B, 0x2C4B }, +{ 0x2C1C, 0x2C1C, 0x2C4C }, +{ 0x2C1D, 0x2C1D, 0x2C4D }, +{ 0x2C1E, 0x2C1E, 0x2C4E }, +{ 0x2C1F, 0x2C1F, 0x2C4F }, +{ 0x2C20, 0x2C20, 0x2C50 }, +{ 0x2C21, 0x2C21, 0x2C51 }, +{ 0x2C22, 0x2C22, 0x2C52 }, +{ 0x2C23, 0x2C23, 0x2C53 }, +{ 0x2C24, 0x2C24, 0x2C54 }, +{ 0x2C25, 0x2C25, 0x2C55 }, +{ 0x2C26, 0x2C26, 0x2C56 }, +{ 0x2C27, 0x2C27, 0x2C57 }, +{ 0x2C28, 0x2C28, 0x2C58 }, +{ 0x2C29, 0x2C29, 0x2C59 }, +{ 0x2C2A, 0x2C2A, 0x2C5A }, +{ 0x2C2B, 0x2C2B, 0x2C5B }, +{ 0x2C2C, 0x2C2C, 0x2C5C }, +{ 0x2C2D, 0x2C2D, 0x2C5D }, +{ 0x2C2E, 0x2C2E, 0x2C5E }, +{ 0x2C30, 0x2C00, 0x2C30 }, +{ 0x2C31, 0x2C01, 0x2C31 }, +{ 0x2C32, 0x2C02, 0x2C32 }, +{ 0x2C33, 0x2C03, 0x2C33 }, +{ 0x2C34, 0x2C04, 0x2C34 }, +{ 0x2C35, 0x2C05, 0x2C35 }, +{ 0x2C36, 0x2C06, 0x2C36 }, +{ 0x2C37, 0x2C07, 0x2C37 }, +{ 0x2C38, 0x2C08, 0x2C38 }, +{ 0x2C39, 0x2C09, 0x2C39 }, +{ 0x2C3A, 0x2C0A, 0x2C3A }, +{ 0x2C3B, 0x2C0B, 0x2C3B }, +{ 0x2C3C, 0x2C0C, 0x2C3C }, +{ 0x2C3D, 0x2C0D, 0x2C3D }, +{ 0x2C3E, 0x2C0E, 0x2C3E }, +{ 0x2C3F, 0x2C0F, 0x2C3F }, +{ 0x2C40, 0x2C10, 0x2C40 }, +{ 0x2C41, 0x2C11, 0x2C41 }, +{ 0x2C42, 0x2C12, 0x2C42 }, +{ 0x2C43, 0x2C13, 0x2C43 }, +{ 0x2C44, 0x2C14, 0x2C44 }, +{ 0x2C45, 0x2C15, 0x2C45 }, +{ 0x2C46, 0x2C16, 0x2C46 }, +{ 0x2C47, 0x2C17, 0x2C47 }, +{ 0x2C48, 0x2C18, 0x2C48 }, +{ 0x2C49, 0x2C19, 0x2C49 }, +{ 0x2C4A, 0x2C1A, 0x2C4A }, +{ 0x2C4B, 0x2C1B, 0x2C4B }, +{ 0x2C4C, 0x2C1C, 0x2C4C }, +{ 0x2C4D, 0x2C1D, 0x2C4D }, +{ 0x2C4E, 0x2C1E, 0x2C4E }, +{ 0x2C4F, 0x2C1F, 0x2C4F }, +{ 0x2C50, 0x2C20, 0x2C50 }, +{ 0x2C51, 0x2C21, 0x2C51 }, +{ 0x2C52, 0x2C22, 0x2C52 }, +{ 0x2C53, 0x2C23, 0x2C53 }, +{ 0x2C54, 0x2C24, 0x2C54 }, +{ 0x2C55, 0x2C25, 0x2C55 }, +{ 0x2C56, 0x2C26, 0x2C56 }, +{ 0x2C57, 0x2C27, 0x2C57 }, +{ 0x2C58, 0x2C28, 0x2C58 }, +{ 0x2C59, 0x2C29, 0x2C59 }, +{ 0x2C5A, 0x2C2A, 0x2C5A }, +{ 0x2C5B, 0x2C2B, 0x2C5B }, +{ 0x2C5C, 0x2C2C, 0x2C5C }, +{ 0x2C5D, 0x2C2D, 0x2C5D }, +{ 0x2C5E, 0x2C2E, 0x2C5E }, +{ 0x2C80, 0x2C80, 0x2C81 }, +{ 0x2C81, 0x2C80, 0x2C81 }, +{ 0x2C82, 0x2C82, 0x2C83 }, +{ 0x2C83, 0x2C82, 0x2C83 }, +{ 0x2C84, 0x2C84, 0x2C85 }, +{ 0x2C85, 0x2C84, 0x2C85 }, +{ 0x2C86, 0x2C86, 0x2C87 }, +{ 0x2C87, 0x2C86, 0x2C87 }, +{ 0x2C88, 0x2C88, 0x2C89 }, +{ 0x2C89, 0x2C88, 0x2C89 }, +{ 0x2C8A, 0x2C8A, 0x2C8B }, +{ 0x2C8B, 0x2C8A, 0x2C8B }, +{ 0x2C8C, 0x2C8C, 0x2C8D }, +{ 0x2C8D, 0x2C8C, 0x2C8D }, +{ 0x2C8E, 0x2C8E, 0x2C8F }, +{ 0x2C8F, 0x2C8E, 0x2C8F }, +{ 0x2C90, 0x2C90, 0x2C91 }, +{ 0x2C91, 0x2C90, 0x2C91 }, +{ 0x2C92, 0x2C92, 0x2C93 }, +{ 0x2C93, 0x2C92, 0x2C93 }, +{ 0x2C94, 0x2C94, 0x2C95 }, +{ 0x2C95, 0x2C94, 0x2C95 }, +{ 0x2C96, 0x2C96, 0x2C97 }, +{ 0x2C97, 0x2C96, 0x2C97 }, +{ 0x2C98, 0x2C98, 0x2C99 }, +{ 0x2C99, 0x2C98, 0x2C99 }, +{ 0x2C9A, 0x2C9A, 0x2C9B }, +{ 0x2C9B, 0x2C9A, 0x2C9B }, +{ 0x2C9C, 0x2C9C, 0x2C9D }, +{ 0x2C9D, 0x2C9C, 0x2C9D }, +{ 0x2C9E, 0x2C9E, 0x2C9F }, +{ 0x2C9F, 0x2C9E, 0x2C9F }, +{ 0x2CA0, 0x2CA0, 0x2CA1 }, +{ 0x2CA1, 0x2CA0, 0x2CA1 }, +{ 0x2CA2, 0x2CA2, 0x2CA3 }, +{ 0x2CA3, 0x2CA2, 0x2CA3 }, +{ 0x2CA4, 0x2CA4, 0x2CA5 }, +{ 0x2CA5, 0x2CA4, 0x2CA5 }, +{ 0x2CA6, 0x2CA6, 0x2CA7 }, +{ 0x2CA7, 0x2CA6, 0x2CA7 }, +{ 0x2CA8, 0x2CA8, 0x2CA9 }, +{ 0x2CA9, 0x2CA8, 0x2CA9 }, +{ 0x2CAA, 0x2CAA, 0x2CAB }, +{ 0x2CAB, 0x2CAA, 0x2CAB }, +{ 0x2CAC, 0x2CAC, 0x2CAD }, +{ 0x2CAD, 0x2CAC, 0x2CAD }, +{ 0x2CAE, 0x2CAE, 0x2CAF }, +{ 0x2CAF, 0x2CAE, 0x2CAF }, +{ 0x2CB0, 0x2CB0, 0x2CB1 }, +{ 0x2CB1, 0x2CB0, 0x2CB1 }, +{ 0x2CB2, 0x2CB2, 0x2CB3 }, +{ 0x2CB3, 0x2CB2, 0x2CB3 }, +{ 0x2CB4, 0x2CB4, 0x2CB5 }, +{ 0x2CB5, 0x2CB4, 0x2CB5 }, +{ 0x2CB6, 0x2CB6, 0x2CB7 }, +{ 0x2CB7, 0x2CB6, 0x2CB7 }, +{ 0x2CB8, 0x2CB8, 0x2CB9 }, +{ 0x2CB9, 0x2CB8, 0x2CB9 }, +{ 0x2CBA, 0x2CBA, 0x2CBB }, +{ 0x2CBB, 0x2CBA, 0x2CBB }, +{ 0x2CBC, 0x2CBC, 0x2CBD }, +{ 0x2CBD, 0x2CBC, 0x2CBD }, +{ 0x2CBE, 0x2CBE, 0x2CBF }, +{ 0x2CBF, 0x2CBE, 0x2CBF }, +{ 0x2CC0, 0x2CC0, 0x2CC1 }, +{ 0x2CC1, 0x2CC0, 0x2CC1 }, +{ 0x2CC2, 0x2CC2, 0x2CC3 }, +{ 0x2CC3, 0x2CC2, 0x2CC3 }, +{ 0x2CC4, 0x2CC4, 0x2CC5 }, +{ 0x2CC5, 0x2CC4, 0x2CC5 }, +{ 0x2CC6, 0x2CC6, 0x2CC7 }, +{ 0x2CC7, 0x2CC6, 0x2CC7 }, +{ 0x2CC8, 0x2CC8, 0x2CC9 }, +{ 0x2CC9, 0x2CC8, 0x2CC9 }, +{ 0x2CCA, 0x2CCA, 0x2CCB }, +{ 0x2CCB, 0x2CCA, 0x2CCB }, +{ 0x2CCC, 0x2CCC, 0x2CCD }, +{ 0x2CCD, 0x2CCC, 0x2CCD }, +{ 0x2CCE, 0x2CCE, 0x2CCF }, +{ 0x2CCF, 0x2CCE, 0x2CCF }, +{ 0x2CD0, 0x2CD0, 0x2CD1 }, +{ 0x2CD1, 0x2CD0, 0x2CD1 }, +{ 0x2CD2, 0x2CD2, 0x2CD3 }, +{ 0x2CD3, 0x2CD2, 0x2CD3 }, +{ 0x2CD4, 0x2CD4, 0x2CD5 }, +{ 0x2CD5, 0x2CD4, 0x2CD5 }, +{ 0x2CD6, 0x2CD6, 0x2CD7 }, +{ 0x2CD7, 0x2CD6, 0x2CD7 }, +{ 0x2CD8, 0x2CD8, 0x2CD9 }, +{ 0x2CD9, 0x2CD8, 0x2CD9 }, +{ 0x2CDA, 0x2CDA, 0x2CDB }, +{ 0x2CDB, 0x2CDA, 0x2CDB }, +{ 0x2CDC, 0x2CDC, 0x2CDD }, +{ 0x2CDD, 0x2CDC, 0x2CDD }, +{ 0x2CDE, 0x2CDE, 0x2CDF }, +{ 0x2CDF, 0x2CDE, 0x2CDF }, +{ 0x2CE0, 0x2CE0, 0x2CE1 }, +{ 0x2CE1, 0x2CE0, 0x2CE1 }, +{ 0x2CE2, 0x2CE2, 0x2CE3 }, +{ 0x2CE3, 0x2CE2, 0x2CE3 }, +{ 0x2CE4, 0x2CE4, 0x2CE4 }, +{ 0x2D00, 0x10A0, 0x2D00 }, +{ 0x2D01, 0x10A1, 0x2D01 }, +{ 0x2D02, 0x10A2, 0x2D02 }, +{ 0x2D03, 0x10A3, 0x2D03 }, +{ 0x2D04, 0x10A4, 0x2D04 }, +{ 0x2D05, 0x10A5, 0x2D05 }, +{ 0x2D06, 0x10A6, 0x2D06 }, +{ 0x2D07, 0x10A7, 0x2D07 }, +{ 0x2D08, 0x10A8, 0x2D08 }, +{ 0x2D09, 0x10A9, 0x2D09 }, +{ 0x2D0A, 0x10AA, 0x2D0A }, +{ 0x2D0B, 0x10AB, 0x2D0B }, +{ 0x2D0C, 0x10AC, 0x2D0C }, +{ 0x2D0D, 0x10AD, 0x2D0D }, +{ 0x2D0E, 0x10AE, 0x2D0E }, +{ 0x2D0F, 0x10AF, 0x2D0F }, +{ 0x2D10, 0x10B0, 0x2D10 }, +{ 0x2D11, 0x10B1, 0x2D11 }, +{ 0x2D12, 0x10B2, 0x2D12 }, +{ 0x2D13, 0x10B3, 0x2D13 }, +{ 0x2D14, 0x10B4, 0x2D14 }, +{ 0x2D15, 0x10B5, 0x2D15 }, +{ 0x2D16, 0x10B6, 0x2D16 }, +{ 0x2D17, 0x10B7, 0x2D17 }, +{ 0x2D18, 0x10B8, 0x2D18 }, +{ 0x2D19, 0x10B9, 0x2D19 }, +{ 0x2D1A, 0x10BA, 0x2D1A }, +{ 0x2D1B, 0x10BB, 0x2D1B }, +{ 0x2D1C, 0x10BC, 0x2D1C }, +{ 0x2D1D, 0x10BD, 0x2D1D }, +{ 0x2D1E, 0x10BE, 0x2D1E }, +{ 0x2D1F, 0x10BF, 0x2D1F }, +{ 0x2D20, 0x10C0, 0x2D20 }, +{ 0x2D21, 0x10C1, 0x2D21 }, +{ 0x2D22, 0x10C2, 0x2D22 }, +{ 0x2D23, 0x10C3, 0x2D23 }, +{ 0x2D24, 0x10C4, 0x2D24 }, +{ 0x2D25, 0x10C5, 0x2D25 }, +{ 0x2D30, 0x2D30, 0x2D30 }, +{ 0x2D31, 0x2D31, 0x2D31 }, +{ 0x2D32, 0x2D32, 0x2D32 }, +{ 0x2D33, 0x2D33, 0x2D33 }, +{ 0x2D34, 0x2D34, 0x2D34 }, +{ 0x2D35, 0x2D35, 0x2D35 }, +{ 0x2D36, 0x2D36, 0x2D36 }, +{ 0x2D37, 0x2D37, 0x2D37 }, +{ 0x2D38, 0x2D38, 0x2D38 }, +{ 0x2D39, 0x2D39, 0x2D39 }, +{ 0x2D3A, 0x2D3A, 0x2D3A }, +{ 0x2D3B, 0x2D3B, 0x2D3B }, +{ 0x2D3C, 0x2D3C, 0x2D3C }, +{ 0x2D3D, 0x2D3D, 0x2D3D }, +{ 0x2D3E, 0x2D3E, 0x2D3E }, +{ 0x2D3F, 0x2D3F, 0x2D3F }, +{ 0x2D40, 0x2D40, 0x2D40 }, +{ 0x2D41, 0x2D41, 0x2D41 }, +{ 0x2D42, 0x2D42, 0x2D42 }, +{ 0x2D43, 0x2D43, 0x2D43 }, +{ 0x2D44, 0x2D44, 0x2D44 }, +{ 0x2D45, 0x2D45, 0x2D45 }, +{ 0x2D46, 0x2D46, 0x2D46 }, +{ 0x2D47, 0x2D47, 0x2D47 }, +{ 0x2D48, 0x2D48, 0x2D48 }, +{ 0x2D49, 0x2D49, 0x2D49 }, +{ 0x2D4A, 0x2D4A, 0x2D4A }, +{ 0x2D4B, 0x2D4B, 0x2D4B }, +{ 0x2D4C, 0x2D4C, 0x2D4C }, +{ 0x2D4D, 0x2D4D, 0x2D4D }, +{ 0x2D4E, 0x2D4E, 0x2D4E }, +{ 0x2D4F, 0x2D4F, 0x2D4F }, +{ 0x2D50, 0x2D50, 0x2D50 }, +{ 0x2D51, 0x2D51, 0x2D51 }, +{ 0x2D52, 0x2D52, 0x2D52 }, +{ 0x2D53, 0x2D53, 0x2D53 }, +{ 0x2D54, 0x2D54, 0x2D54 }, +{ 0x2D55, 0x2D55, 0x2D55 }, +{ 0x2D56, 0x2D56, 0x2D56 }, +{ 0x2D57, 0x2D57, 0x2D57 }, +{ 0x2D58, 0x2D58, 0x2D58 }, +{ 0x2D59, 0x2D59, 0x2D59 }, +{ 0x2D5A, 0x2D5A, 0x2D5A }, +{ 0x2D5B, 0x2D5B, 0x2D5B }, +{ 0x2D5C, 0x2D5C, 0x2D5C }, +{ 0x2D5D, 0x2D5D, 0x2D5D }, +{ 0x2D5E, 0x2D5E, 0x2D5E }, +{ 0x2D5F, 0x2D5F, 0x2D5F }, +{ 0x2D60, 0x2D60, 0x2D60 }, +{ 0x2D61, 0x2D61, 0x2D61 }, +{ 0x2D62, 0x2D62, 0x2D62 }, +{ 0x2D63, 0x2D63, 0x2D63 }, +{ 0x2D64, 0x2D64, 0x2D64 }, +{ 0x2D65, 0x2D65, 0x2D65 }, +{ 0x2D6F, 0x2D6F, 0x2D6F }, +{ 0x2D80, 0x2D80, 0x2D80 }, +{ 0x2D81, 0x2D81, 0x2D81 }, +{ 0x2D82, 0x2D82, 0x2D82 }, +{ 0x2D83, 0x2D83, 0x2D83 }, +{ 0x2D84, 0x2D84, 0x2D84 }, +{ 0x2D85, 0x2D85, 0x2D85 }, +{ 0x2D86, 0x2D86, 0x2D86 }, +{ 0x2D87, 0x2D87, 0x2D87 }, +{ 0x2D88, 0x2D88, 0x2D88 }, +{ 0x2D89, 0x2D89, 0x2D89 }, +{ 0x2D8A, 0x2D8A, 0x2D8A }, +{ 0x2D8B, 0x2D8B, 0x2D8B }, +{ 0x2D8C, 0x2D8C, 0x2D8C }, +{ 0x2D8D, 0x2D8D, 0x2D8D }, +{ 0x2D8E, 0x2D8E, 0x2D8E }, +{ 0x2D8F, 0x2D8F, 0x2D8F }, +{ 0x2D90, 0x2D90, 0x2D90 }, +{ 0x2D91, 0x2D91, 0x2D91 }, +{ 0x2D92, 0x2D92, 0x2D92 }, +{ 0x2D93, 0x2D93, 0x2D93 }, +{ 0x2D94, 0x2D94, 0x2D94 }, +{ 0x2D95, 0x2D95, 0x2D95 }, +{ 0x2D96, 0x2D96, 0x2D96 }, +{ 0x2DA0, 0x2DA0, 0x2DA0 }, +{ 0x2DA1, 0x2DA1, 0x2DA1 }, +{ 0x2DA2, 0x2DA2, 0x2DA2 }, +{ 0x2DA3, 0x2DA3, 0x2DA3 }, +{ 0x2DA4, 0x2DA4, 0x2DA4 }, +{ 0x2DA5, 0x2DA5, 0x2DA5 }, +{ 0x2DA6, 0x2DA6, 0x2DA6 }, +{ 0x2DA8, 0x2DA8, 0x2DA8 }, +{ 0x2DA9, 0x2DA9, 0x2DA9 }, +{ 0x2DAA, 0x2DAA, 0x2DAA }, +{ 0x2DAB, 0x2DAB, 0x2DAB }, +{ 0x2DAC, 0x2DAC, 0x2DAC }, +{ 0x2DAD, 0x2DAD, 0x2DAD }, +{ 0x2DAE, 0x2DAE, 0x2DAE }, +{ 0x2DB0, 0x2DB0, 0x2DB0 }, +{ 0x2DB1, 0x2DB1, 0x2DB1 }, +{ 0x2DB2, 0x2DB2, 0x2DB2 }, +{ 0x2DB3, 0x2DB3, 0x2DB3 }, +{ 0x2DB4, 0x2DB4, 0x2DB4 }, +{ 0x2DB5, 0x2DB5, 0x2DB5 }, +{ 0x2DB6, 0x2DB6, 0x2DB6 }, +{ 0x2DB8, 0x2DB8, 0x2DB8 }, +{ 0x2DB9, 0x2DB9, 0x2DB9 }, +{ 0x2DBA, 0x2DBA, 0x2DBA }, +{ 0x2DBB, 0x2DBB, 0x2DBB }, +{ 0x2DBC, 0x2DBC, 0x2DBC }, +{ 0x2DBD, 0x2DBD, 0x2DBD }, +{ 0x2DBE, 0x2DBE, 0x2DBE }, +{ 0x2DC0, 0x2DC0, 0x2DC0 }, +{ 0x2DC1, 0x2DC1, 0x2DC1 }, +{ 0x2DC2, 0x2DC2, 0x2DC2 }, +{ 0x2DC3, 0x2DC3, 0x2DC3 }, +{ 0x2DC4, 0x2DC4, 0x2DC4 }, +{ 0x2DC5, 0x2DC5, 0x2DC5 }, +{ 0x2DC6, 0x2DC6, 0x2DC6 }, +{ 0x2DC8, 0x2DC8, 0x2DC8 }, +{ 0x2DC9, 0x2DC9, 0x2DC9 }, +{ 0x2DCA, 0x2DCA, 0x2DCA }, +{ 0x2DCB, 0x2DCB, 0x2DCB }, +{ 0x2DCC, 0x2DCC, 0x2DCC }, +{ 0x2DCD, 0x2DCD, 0x2DCD }, +{ 0x2DCE, 0x2DCE, 0x2DCE }, +{ 0x2DD0, 0x2DD0, 0x2DD0 }, +{ 0x2DD1, 0x2DD1, 0x2DD1 }, +{ 0x2DD2, 0x2DD2, 0x2DD2 }, +{ 0x2DD3, 0x2DD3, 0x2DD3 }, +{ 0x2DD4, 0x2DD4, 0x2DD4 }, +{ 0x2DD5, 0x2DD5, 0x2DD5 }, +{ 0x2DD6, 0x2DD6, 0x2DD6 }, +{ 0x2DD8, 0x2DD8, 0x2DD8 }, +{ 0x2DD9, 0x2DD9, 0x2DD9 }, +{ 0x2DDA, 0x2DDA, 0x2DDA }, +{ 0x2DDB, 0x2DDB, 0x2DDB }, +{ 0x2DDC, 0x2DDC, 0x2DDC }, +{ 0x2DDD, 0x2DDD, 0x2DDD }, +{ 0x2DDE, 0x2DDE, 0x2DDE }, +{ 0x3005, 0x3005, 0x3005 }, +{ 0x3006, 0x3006, 0x3006 }, +{ 0x302A, 0x302A, 0x302A }, +{ 0x302B, 0x302B, 0x302B }, +{ 0x302C, 0x302C, 0x302C }, +{ 0x302D, 0x302D, 0x302D }, +{ 0x302E, 0x302E, 0x302E }, +{ 0x302F, 0x302F, 0x302F }, +{ 0x3031, 0x3031, 0x3031 }, +{ 0x3032, 0x3032, 0x3032 }, +{ 0x3033, 0x3033, 0x3033 }, +{ 0x3034, 0x3034, 0x3034 }, +{ 0x3035, 0x3035, 0x3035 }, +{ 0x303B, 0x303B, 0x303B }, +{ 0x303C, 0x303C, 0x303C }, +{ 0x3041, 0x3041, 0x3041 }, +{ 0x3042, 0x3042, 0x3042 }, +{ 0x3043, 0x3043, 0x3043 }, +{ 0x3044, 0x3044, 0x3044 }, +{ 0x3045, 0x3045, 0x3045 }, +{ 0x3046, 0x3046, 0x3046 }, +{ 0x3047, 0x3047, 0x3047 }, +{ 0x3048, 0x3048, 0x3048 }, +{ 0x3049, 0x3049, 0x3049 }, +{ 0x304A, 0x304A, 0x304A }, +{ 0x304B, 0x304B, 0x304B }, +{ 0x304C, 0x304C, 0x304C }, +{ 0x304D, 0x304D, 0x304D }, +{ 0x304E, 0x304E, 0x304E }, +{ 0x304F, 0x304F, 0x304F }, +{ 0x3050, 0x3050, 0x3050 }, +{ 0x3051, 0x3051, 0x3051 }, +{ 0x3052, 0x3052, 0x3052 }, +{ 0x3053, 0x3053, 0x3053 }, +{ 0x3054, 0x3054, 0x3054 }, +{ 0x3055, 0x3055, 0x3055 }, +{ 0x3056, 0x3056, 0x3056 }, +{ 0x3057, 0x3057, 0x3057 }, +{ 0x3058, 0x3058, 0x3058 }, +{ 0x3059, 0x3059, 0x3059 }, +{ 0x305A, 0x305A, 0x305A }, +{ 0x305B, 0x305B, 0x305B }, +{ 0x305C, 0x305C, 0x305C }, +{ 0x305D, 0x305D, 0x305D }, +{ 0x305E, 0x305E, 0x305E }, +{ 0x305F, 0x305F, 0x305F }, +{ 0x3060, 0x3060, 0x3060 }, +{ 0x3061, 0x3061, 0x3061 }, +{ 0x3062, 0x3062, 0x3062 }, +{ 0x3063, 0x3063, 0x3063 }, +{ 0x3064, 0x3064, 0x3064 }, +{ 0x3065, 0x3065, 0x3065 }, +{ 0x3066, 0x3066, 0x3066 }, +{ 0x3067, 0x3067, 0x3067 }, +{ 0x3068, 0x3068, 0x3068 }, +{ 0x3069, 0x3069, 0x3069 }, +{ 0x306A, 0x306A, 0x306A }, +{ 0x306B, 0x306B, 0x306B }, +{ 0x306C, 0x306C, 0x306C }, +{ 0x306D, 0x306D, 0x306D }, +{ 0x306E, 0x306E, 0x306E }, +{ 0x306F, 0x306F, 0x306F }, +{ 0x3070, 0x3070, 0x3070 }, +{ 0x3071, 0x3071, 0x3071 }, +{ 0x3072, 0x3072, 0x3072 }, +{ 0x3073, 0x3073, 0x3073 }, +{ 0x3074, 0x3074, 0x3074 }, +{ 0x3075, 0x3075, 0x3075 }, +{ 0x3076, 0x3076, 0x3076 }, +{ 0x3077, 0x3077, 0x3077 }, +{ 0x3078, 0x3078, 0x3078 }, +{ 0x3079, 0x3079, 0x3079 }, +{ 0x307A, 0x307A, 0x307A }, +{ 0x307B, 0x307B, 0x307B }, +{ 0x307C, 0x307C, 0x307C }, +{ 0x307D, 0x307D, 0x307D }, +{ 0x307E, 0x307E, 0x307E }, +{ 0x307F, 0x307F, 0x307F }, +{ 0x3080, 0x3080, 0x3080 }, +{ 0x3081, 0x3081, 0x3081 }, +{ 0x3082, 0x3082, 0x3082 }, +{ 0x3083, 0x3083, 0x3083 }, +{ 0x3084, 0x3084, 0x3084 }, +{ 0x3085, 0x3085, 0x3085 }, +{ 0x3086, 0x3086, 0x3086 }, +{ 0x3087, 0x3087, 0x3087 }, +{ 0x3088, 0x3088, 0x3088 }, +{ 0x3089, 0x3089, 0x3089 }, +{ 0x308A, 0x308A, 0x308A }, +{ 0x308B, 0x308B, 0x308B }, +{ 0x308C, 0x308C, 0x308C }, +{ 0x308D, 0x308D, 0x308D }, +{ 0x308E, 0x308E, 0x308E }, +{ 0x308F, 0x308F, 0x308F }, +{ 0x3090, 0x3090, 0x3090 }, +{ 0x3091, 0x3091, 0x3091 }, +{ 0x3092, 0x3092, 0x3092 }, +{ 0x3093, 0x3093, 0x3093 }, +{ 0x3094, 0x3094, 0x3094 }, +{ 0x3095, 0x3095, 0x3095 }, +{ 0x3096, 0x3096, 0x3096 }, +{ 0x3099, 0x3099, 0x3099 }, +{ 0x309A, 0x309A, 0x309A }, +{ 0x309D, 0x309D, 0x309D }, +{ 0x309E, 0x309E, 0x309E }, +{ 0x309F, 0x309F, 0x309F }, +{ 0x30A1, 0x30A1, 0x30A1 }, +{ 0x30A2, 0x30A2, 0x30A2 }, +{ 0x30A3, 0x30A3, 0x30A3 }, +{ 0x30A4, 0x30A4, 0x30A4 }, +{ 0x30A5, 0x30A5, 0x30A5 }, +{ 0x30A6, 0x30A6, 0x30A6 }, +{ 0x30A7, 0x30A7, 0x30A7 }, +{ 0x30A8, 0x30A8, 0x30A8 }, +{ 0x30A9, 0x30A9, 0x30A9 }, +{ 0x30AA, 0x30AA, 0x30AA }, +{ 0x30AB, 0x30AB, 0x30AB }, +{ 0x30AC, 0x30AC, 0x30AC }, +{ 0x30AD, 0x30AD, 0x30AD }, +{ 0x30AE, 0x30AE, 0x30AE }, +{ 0x30AF, 0x30AF, 0x30AF }, +{ 0x30B0, 0x30B0, 0x30B0 }, +{ 0x30B1, 0x30B1, 0x30B1 }, +{ 0x30B2, 0x30B2, 0x30B2 }, +{ 0x30B3, 0x30B3, 0x30B3 }, +{ 0x30B4, 0x30B4, 0x30B4 }, +{ 0x30B5, 0x30B5, 0x30B5 }, +{ 0x30B6, 0x30B6, 0x30B6 }, +{ 0x30B7, 0x30B7, 0x30B7 }, +{ 0x30B8, 0x30B8, 0x30B8 }, +{ 0x30B9, 0x30B9, 0x30B9 }, +{ 0x30BA, 0x30BA, 0x30BA }, +{ 0x30BB, 0x30BB, 0x30BB }, +{ 0x30BC, 0x30BC, 0x30BC }, +{ 0x30BD, 0x30BD, 0x30BD }, +{ 0x30BE, 0x30BE, 0x30BE }, +{ 0x30BF, 0x30BF, 0x30BF }, +{ 0x30C0, 0x30C0, 0x30C0 }, +{ 0x30C1, 0x30C1, 0x30C1 }, +{ 0x30C2, 0x30C2, 0x30C2 }, +{ 0x30C3, 0x30C3, 0x30C3 }, +{ 0x30C4, 0x30C4, 0x30C4 }, +{ 0x30C5, 0x30C5, 0x30C5 }, +{ 0x30C6, 0x30C6, 0x30C6 }, +{ 0x30C7, 0x30C7, 0x30C7 }, +{ 0x30C8, 0x30C8, 0x30C8 }, +{ 0x30C9, 0x30C9, 0x30C9 }, +{ 0x30CA, 0x30CA, 0x30CA }, +{ 0x30CB, 0x30CB, 0x30CB }, +{ 0x30CC, 0x30CC, 0x30CC }, +{ 0x30CD, 0x30CD, 0x30CD }, +{ 0x30CE, 0x30CE, 0x30CE }, +{ 0x30CF, 0x30CF, 0x30CF }, +{ 0x30D0, 0x30D0, 0x30D0 }, +{ 0x30D1, 0x30D1, 0x30D1 }, +{ 0x30D2, 0x30D2, 0x30D2 }, +{ 0x30D3, 0x30D3, 0x30D3 }, +{ 0x30D4, 0x30D4, 0x30D4 }, +{ 0x30D5, 0x30D5, 0x30D5 }, +{ 0x30D6, 0x30D6, 0x30D6 }, +{ 0x30D7, 0x30D7, 0x30D7 }, +{ 0x30D8, 0x30D8, 0x30D8 }, +{ 0x30D9, 0x30D9, 0x30D9 }, +{ 0x30DA, 0x30DA, 0x30DA }, +{ 0x30DB, 0x30DB, 0x30DB }, +{ 0x30DC, 0x30DC, 0x30DC }, +{ 0x30DD, 0x30DD, 0x30DD }, +{ 0x30DE, 0x30DE, 0x30DE }, +{ 0x30DF, 0x30DF, 0x30DF }, +{ 0x30E0, 0x30E0, 0x30E0 }, +{ 0x30E1, 0x30E1, 0x30E1 }, +{ 0x30E2, 0x30E2, 0x30E2 }, +{ 0x30E3, 0x30E3, 0x30E3 }, +{ 0x30E4, 0x30E4, 0x30E4 }, +{ 0x30E5, 0x30E5, 0x30E5 }, +{ 0x30E6, 0x30E6, 0x30E6 }, +{ 0x30E7, 0x30E7, 0x30E7 }, +{ 0x30E8, 0x30E8, 0x30E8 }, +{ 0x30E9, 0x30E9, 0x30E9 }, +{ 0x30EA, 0x30EA, 0x30EA }, +{ 0x30EB, 0x30EB, 0x30EB }, +{ 0x30EC, 0x30EC, 0x30EC }, +{ 0x30ED, 0x30ED, 0x30ED }, +{ 0x30EE, 0x30EE, 0x30EE }, +{ 0x30EF, 0x30EF, 0x30EF }, +{ 0x30F0, 0x30F0, 0x30F0 }, +{ 0x30F1, 0x30F1, 0x30F1 }, +{ 0x30F2, 0x30F2, 0x30F2 }, +{ 0x30F3, 0x30F3, 0x30F3 }, +{ 0x30F4, 0x30F4, 0x30F4 }, +{ 0x30F5, 0x30F5, 0x30F5 }, +{ 0x30F6, 0x30F6, 0x30F6 }, +{ 0x30F7, 0x30F7, 0x30F7 }, +{ 0x30F8, 0x30F8, 0x30F8 }, +{ 0x30F9, 0x30F9, 0x30F9 }, +{ 0x30FA, 0x30FA, 0x30FA }, +{ 0x30FC, 0x30FC, 0x30FC }, +{ 0x30FD, 0x30FD, 0x30FD }, +{ 0x30FE, 0x30FE, 0x30FE }, +{ 0x30FF, 0x30FF, 0x30FF }, +{ 0x3105, 0x3105, 0x3105 }, +{ 0x3106, 0x3106, 0x3106 }, +{ 0x3107, 0x3107, 0x3107 }, +{ 0x3108, 0x3108, 0x3108 }, +{ 0x3109, 0x3109, 0x3109 }, +{ 0x310A, 0x310A, 0x310A }, +{ 0x310B, 0x310B, 0x310B }, +{ 0x310C, 0x310C, 0x310C }, +{ 0x310D, 0x310D, 0x310D }, +{ 0x310E, 0x310E, 0x310E }, +{ 0x310F, 0x310F, 0x310F }, +{ 0x3110, 0x3110, 0x3110 }, +{ 0x3111, 0x3111, 0x3111 }, +{ 0x3112, 0x3112, 0x3112 }, +{ 0x3113, 0x3113, 0x3113 }, +{ 0x3114, 0x3114, 0x3114 }, +{ 0x3115, 0x3115, 0x3115 }, +{ 0x3116, 0x3116, 0x3116 }, +{ 0x3117, 0x3117, 0x3117 }, +{ 0x3118, 0x3118, 0x3118 }, +{ 0x3119, 0x3119, 0x3119 }, +{ 0x311A, 0x311A, 0x311A }, +{ 0x311B, 0x311B, 0x311B }, +{ 0x311C, 0x311C, 0x311C }, +{ 0x311D, 0x311D, 0x311D }, +{ 0x311E, 0x311E, 0x311E }, +{ 0x311F, 0x311F, 0x311F }, +{ 0x3120, 0x3120, 0x3120 }, +{ 0x3121, 0x3121, 0x3121 }, +{ 0x3122, 0x3122, 0x3122 }, +{ 0x3123, 0x3123, 0x3123 }, +{ 0x3124, 0x3124, 0x3124 }, +{ 0x3125, 0x3125, 0x3125 }, +{ 0x3126, 0x3126, 0x3126 }, +{ 0x3127, 0x3127, 0x3127 }, +{ 0x3128, 0x3128, 0x3128 }, +{ 0x3129, 0x3129, 0x3129 }, +{ 0x312A, 0x312A, 0x312A }, +{ 0x312B, 0x312B, 0x312B }, +{ 0x312C, 0x312C, 0x312C }, +{ 0x3131, 0x3131, 0x3131 }, +{ 0x3132, 0x3132, 0x3132 }, +{ 0x3133, 0x3133, 0x3133 }, +{ 0x3134, 0x3134, 0x3134 }, +{ 0x3135, 0x3135, 0x3135 }, +{ 0x3136, 0x3136, 0x3136 }, +{ 0x3137, 0x3137, 0x3137 }, +{ 0x3138, 0x3138, 0x3138 }, +{ 0x3139, 0x3139, 0x3139 }, +{ 0x313A, 0x313A, 0x313A }, +{ 0x313B, 0x313B, 0x313B }, +{ 0x313C, 0x313C, 0x313C }, +{ 0x313D, 0x313D, 0x313D }, +{ 0x313E, 0x313E, 0x313E }, +{ 0x313F, 0x313F, 0x313F }, +{ 0x3140, 0x3140, 0x3140 }, +{ 0x3141, 0x3141, 0x3141 }, +{ 0x3142, 0x3142, 0x3142 }, +{ 0x3143, 0x3143, 0x3143 }, +{ 0x3144, 0x3144, 0x3144 }, +{ 0x3145, 0x3145, 0x3145 }, +{ 0x3146, 0x3146, 0x3146 }, +{ 0x3147, 0x3147, 0x3147 }, +{ 0x3148, 0x3148, 0x3148 }, +{ 0x3149, 0x3149, 0x3149 }, +{ 0x314A, 0x314A, 0x314A }, +{ 0x314B, 0x314B, 0x314B }, +{ 0x314C, 0x314C, 0x314C }, +{ 0x314D, 0x314D, 0x314D }, +{ 0x314E, 0x314E, 0x314E }, +{ 0x314F, 0x314F, 0x314F }, +{ 0x3150, 0x3150, 0x3150 }, +{ 0x3151, 0x3151, 0x3151 }, +{ 0x3152, 0x3152, 0x3152 }, +{ 0x3153, 0x3153, 0x3153 }, +{ 0x3154, 0x3154, 0x3154 }, +{ 0x3155, 0x3155, 0x3155 }, +{ 0x3156, 0x3156, 0x3156 }, +{ 0x3157, 0x3157, 0x3157 }, +{ 0x3158, 0x3158, 0x3158 }, +{ 0x3159, 0x3159, 0x3159 }, +{ 0x315A, 0x315A, 0x315A }, +{ 0x315B, 0x315B, 0x315B }, +{ 0x315C, 0x315C, 0x315C }, +{ 0x315D, 0x315D, 0x315D }, +{ 0x315E, 0x315E, 0x315E }, +{ 0x315F, 0x315F, 0x315F }, +{ 0x3160, 0x3160, 0x3160 }, +{ 0x3161, 0x3161, 0x3161 }, +{ 0x3162, 0x3162, 0x3162 }, +{ 0x3163, 0x3163, 0x3163 }, +{ 0x3164, 0x3164, 0x3164 }, +{ 0x3165, 0x3165, 0x3165 }, +{ 0x3166, 0x3166, 0x3166 }, +{ 0x3167, 0x3167, 0x3167 }, +{ 0x3168, 0x3168, 0x3168 }, +{ 0x3169, 0x3169, 0x3169 }, +{ 0x316A, 0x316A, 0x316A }, +{ 0x316B, 0x316B, 0x316B }, +{ 0x316C, 0x316C, 0x316C }, +{ 0x316D, 0x316D, 0x316D }, +{ 0x316E, 0x316E, 0x316E }, +{ 0x316F, 0x316F, 0x316F }, +{ 0x3170, 0x3170, 0x3170 }, +{ 0x3171, 0x3171, 0x3171 }, +{ 0x3172, 0x3172, 0x3172 }, +{ 0x3173, 0x3173, 0x3173 }, +{ 0x3174, 0x3174, 0x3174 }, +{ 0x3175, 0x3175, 0x3175 }, +{ 0x3176, 0x3176, 0x3176 }, +{ 0x3177, 0x3177, 0x3177 }, +{ 0x3178, 0x3178, 0x3178 }, +{ 0x3179, 0x3179, 0x3179 }, +{ 0x317A, 0x317A, 0x317A }, +{ 0x317B, 0x317B, 0x317B }, +{ 0x317C, 0x317C, 0x317C }, +{ 0x317D, 0x317D, 0x317D }, +{ 0x317E, 0x317E, 0x317E }, +{ 0x317F, 0x317F, 0x317F }, +{ 0x3180, 0x3180, 0x3180 }, +{ 0x3181, 0x3181, 0x3181 }, +{ 0x3182, 0x3182, 0x3182 }, +{ 0x3183, 0x3183, 0x3183 }, +{ 0x3184, 0x3184, 0x3184 }, +{ 0x3185, 0x3185, 0x3185 }, +{ 0x3186, 0x3186, 0x3186 }, +{ 0x3187, 0x3187, 0x3187 }, +{ 0x3188, 0x3188, 0x3188 }, +{ 0x3189, 0x3189, 0x3189 }, +{ 0x318A, 0x318A, 0x318A }, +{ 0x318B, 0x318B, 0x318B }, +{ 0x318C, 0x318C, 0x318C }, +{ 0x318D, 0x318D, 0x318D }, +{ 0x318E, 0x318E, 0x318E }, +{ 0x31A0, 0x31A0, 0x31A0 }, +{ 0x31A1, 0x31A1, 0x31A1 }, +{ 0x31A2, 0x31A2, 0x31A2 }, +{ 0x31A3, 0x31A3, 0x31A3 }, +{ 0x31A4, 0x31A4, 0x31A4 }, +{ 0x31A5, 0x31A5, 0x31A5 }, +{ 0x31A6, 0x31A6, 0x31A6 }, +{ 0x31A7, 0x31A7, 0x31A7 }, +{ 0x31A8, 0x31A8, 0x31A8 }, +{ 0x31A9, 0x31A9, 0x31A9 }, +{ 0x31AA, 0x31AA, 0x31AA }, +{ 0x31AB, 0x31AB, 0x31AB }, +{ 0x31AC, 0x31AC, 0x31AC }, +{ 0x31AD, 0x31AD, 0x31AD }, +{ 0x31AE, 0x31AE, 0x31AE }, +{ 0x31AF, 0x31AF, 0x31AF }, +{ 0x31B0, 0x31B0, 0x31B0 }, +{ 0x31B1, 0x31B1, 0x31B1 }, +{ 0x31B2, 0x31B2, 0x31B2 }, +{ 0x31B3, 0x31B3, 0x31B3 }, +{ 0x31B4, 0x31B4, 0x31B4 }, +{ 0x31B5, 0x31B5, 0x31B5 }, +{ 0x31B6, 0x31B6, 0x31B6 }, +{ 0x31B7, 0x31B7, 0x31B7 }, +{ 0x31F0, 0x31F0, 0x31F0 }, +{ 0x31F1, 0x31F1, 0x31F1 }, +{ 0x31F2, 0x31F2, 0x31F2 }, +{ 0x31F3, 0x31F3, 0x31F3 }, +{ 0x31F4, 0x31F4, 0x31F4 }, +{ 0x31F5, 0x31F5, 0x31F5 }, +{ 0x31F6, 0x31F6, 0x31F6 }, +{ 0x31F7, 0x31F7, 0x31F7 }, +{ 0x31F8, 0x31F8, 0x31F8 }, +{ 0x31F9, 0x31F9, 0x31F9 }, +{ 0x31FA, 0x31FA, 0x31FA }, +{ 0x31FB, 0x31FB, 0x31FB }, +{ 0x31FC, 0x31FC, 0x31FC }, +{ 0x31FD, 0x31FD, 0x31FD }, +{ 0x31FE, 0x31FE, 0x31FE }, +{ 0x31FF, 0x31FF, 0x31FF }, +{ 0x3400, 0x3400, 0x3400 }, +{ 0x4DB5, 0x4DB5, 0x4DB5 }, +{ 0x4E00, 0x4E00, 0x4E00 }, +{ 0x9FBB, 0x9FBB, 0x9FBB }, +{ 0xA000, 0xA000, 0xA000 }, +{ 0xA001, 0xA001, 0xA001 }, +{ 0xA002, 0xA002, 0xA002 }, +{ 0xA003, 0xA003, 0xA003 }, +{ 0xA004, 0xA004, 0xA004 }, +{ 0xA005, 0xA005, 0xA005 }, +{ 0xA006, 0xA006, 0xA006 }, +{ 0xA007, 0xA007, 0xA007 }, +{ 0xA008, 0xA008, 0xA008 }, +{ 0xA009, 0xA009, 0xA009 }, +{ 0xA00A, 0xA00A, 0xA00A }, +{ 0xA00B, 0xA00B, 0xA00B }, +{ 0xA00C, 0xA00C, 0xA00C }, +{ 0xA00D, 0xA00D, 0xA00D }, +{ 0xA00E, 0xA00E, 0xA00E }, +{ 0xA00F, 0xA00F, 0xA00F }, +{ 0xA010, 0xA010, 0xA010 }, +{ 0xA011, 0xA011, 0xA011 }, +{ 0xA012, 0xA012, 0xA012 }, +{ 0xA013, 0xA013, 0xA013 }, +{ 0xA014, 0xA014, 0xA014 }, +{ 0xA015, 0xA015, 0xA015 }, +{ 0xA016, 0xA016, 0xA016 }, +{ 0xA017, 0xA017, 0xA017 }, +{ 0xA018, 0xA018, 0xA018 }, +{ 0xA019, 0xA019, 0xA019 }, +{ 0xA01A, 0xA01A, 0xA01A }, +{ 0xA01B, 0xA01B, 0xA01B }, +{ 0xA01C, 0xA01C, 0xA01C }, +{ 0xA01D, 0xA01D, 0xA01D }, +{ 0xA01E, 0xA01E, 0xA01E }, +{ 0xA01F, 0xA01F, 0xA01F }, +{ 0xA020, 0xA020, 0xA020 }, +{ 0xA021, 0xA021, 0xA021 }, +{ 0xA022, 0xA022, 0xA022 }, +{ 0xA023, 0xA023, 0xA023 }, +{ 0xA024, 0xA024, 0xA024 }, +{ 0xA025, 0xA025, 0xA025 }, +{ 0xA026, 0xA026, 0xA026 }, +{ 0xA027, 0xA027, 0xA027 }, +{ 0xA028, 0xA028, 0xA028 }, +{ 0xA029, 0xA029, 0xA029 }, +{ 0xA02A, 0xA02A, 0xA02A }, +{ 0xA02B, 0xA02B, 0xA02B }, +{ 0xA02C, 0xA02C, 0xA02C }, +{ 0xA02D, 0xA02D, 0xA02D }, +{ 0xA02E, 0xA02E, 0xA02E }, +{ 0xA02F, 0xA02F, 0xA02F }, +{ 0xA030, 0xA030, 0xA030 }, +{ 0xA031, 0xA031, 0xA031 }, +{ 0xA032, 0xA032, 0xA032 }, +{ 0xA033, 0xA033, 0xA033 }, +{ 0xA034, 0xA034, 0xA034 }, +{ 0xA035, 0xA035, 0xA035 }, +{ 0xA036, 0xA036, 0xA036 }, +{ 0xA037, 0xA037, 0xA037 }, +{ 0xA038, 0xA038, 0xA038 }, +{ 0xA039, 0xA039, 0xA039 }, +{ 0xA03A, 0xA03A, 0xA03A }, +{ 0xA03B, 0xA03B, 0xA03B }, +{ 0xA03C, 0xA03C, 0xA03C }, +{ 0xA03D, 0xA03D, 0xA03D }, +{ 0xA03E, 0xA03E, 0xA03E }, +{ 0xA03F, 0xA03F, 0xA03F }, +{ 0xA040, 0xA040, 0xA040 }, +{ 0xA041, 0xA041, 0xA041 }, +{ 0xA042, 0xA042, 0xA042 }, +{ 0xA043, 0xA043, 0xA043 }, +{ 0xA044, 0xA044, 0xA044 }, +{ 0xA045, 0xA045, 0xA045 }, +{ 0xA046, 0xA046, 0xA046 }, +{ 0xA047, 0xA047, 0xA047 }, +{ 0xA048, 0xA048, 0xA048 }, +{ 0xA049, 0xA049, 0xA049 }, +{ 0xA04A, 0xA04A, 0xA04A }, +{ 0xA04B, 0xA04B, 0xA04B }, +{ 0xA04C, 0xA04C, 0xA04C }, +{ 0xA04D, 0xA04D, 0xA04D }, +{ 0xA04E, 0xA04E, 0xA04E }, +{ 0xA04F, 0xA04F, 0xA04F }, +{ 0xA050, 0xA050, 0xA050 }, +{ 0xA051, 0xA051, 0xA051 }, +{ 0xA052, 0xA052, 0xA052 }, +{ 0xA053, 0xA053, 0xA053 }, +{ 0xA054, 0xA054, 0xA054 }, +{ 0xA055, 0xA055, 0xA055 }, +{ 0xA056, 0xA056, 0xA056 }, +{ 0xA057, 0xA057, 0xA057 }, +{ 0xA058, 0xA058, 0xA058 }, +{ 0xA059, 0xA059, 0xA059 }, +{ 0xA05A, 0xA05A, 0xA05A }, +{ 0xA05B, 0xA05B, 0xA05B }, +{ 0xA05C, 0xA05C, 0xA05C }, +{ 0xA05D, 0xA05D, 0xA05D }, +{ 0xA05E, 0xA05E, 0xA05E }, +{ 0xA05F, 0xA05F, 0xA05F }, +{ 0xA060, 0xA060, 0xA060 }, +{ 0xA061, 0xA061, 0xA061 }, +{ 0xA062, 0xA062, 0xA062 }, +{ 0xA063, 0xA063, 0xA063 }, +{ 0xA064, 0xA064, 0xA064 }, +{ 0xA065, 0xA065, 0xA065 }, +{ 0xA066, 0xA066, 0xA066 }, +{ 0xA067, 0xA067, 0xA067 }, +{ 0xA068, 0xA068, 0xA068 }, +{ 0xA069, 0xA069, 0xA069 }, +{ 0xA06A, 0xA06A, 0xA06A }, +{ 0xA06B, 0xA06B, 0xA06B }, +{ 0xA06C, 0xA06C, 0xA06C }, +{ 0xA06D, 0xA06D, 0xA06D }, +{ 0xA06E, 0xA06E, 0xA06E }, +{ 0xA06F, 0xA06F, 0xA06F }, +{ 0xA070, 0xA070, 0xA070 }, +{ 0xA071, 0xA071, 0xA071 }, +{ 0xA072, 0xA072, 0xA072 }, +{ 0xA073, 0xA073, 0xA073 }, +{ 0xA074, 0xA074, 0xA074 }, +{ 0xA075, 0xA075, 0xA075 }, +{ 0xA076, 0xA076, 0xA076 }, +{ 0xA077, 0xA077, 0xA077 }, +{ 0xA078, 0xA078, 0xA078 }, +{ 0xA079, 0xA079, 0xA079 }, +{ 0xA07A, 0xA07A, 0xA07A }, +{ 0xA07B, 0xA07B, 0xA07B }, +{ 0xA07C, 0xA07C, 0xA07C }, +{ 0xA07D, 0xA07D, 0xA07D }, +{ 0xA07E, 0xA07E, 0xA07E }, +{ 0xA07F, 0xA07F, 0xA07F }, +{ 0xA080, 0xA080, 0xA080 }, +{ 0xA081, 0xA081, 0xA081 }, +{ 0xA082, 0xA082, 0xA082 }, +{ 0xA083, 0xA083, 0xA083 }, +{ 0xA084, 0xA084, 0xA084 }, +{ 0xA085, 0xA085, 0xA085 }, +{ 0xA086, 0xA086, 0xA086 }, +{ 0xA087, 0xA087, 0xA087 }, +{ 0xA088, 0xA088, 0xA088 }, +{ 0xA089, 0xA089, 0xA089 }, +{ 0xA08A, 0xA08A, 0xA08A }, +{ 0xA08B, 0xA08B, 0xA08B }, +{ 0xA08C, 0xA08C, 0xA08C }, +{ 0xA08D, 0xA08D, 0xA08D }, +{ 0xA08E, 0xA08E, 0xA08E }, +{ 0xA08F, 0xA08F, 0xA08F }, +{ 0xA090, 0xA090, 0xA090 }, +{ 0xA091, 0xA091, 0xA091 }, +{ 0xA092, 0xA092, 0xA092 }, +{ 0xA093, 0xA093, 0xA093 }, +{ 0xA094, 0xA094, 0xA094 }, +{ 0xA095, 0xA095, 0xA095 }, +{ 0xA096, 0xA096, 0xA096 }, +{ 0xA097, 0xA097, 0xA097 }, +{ 0xA098, 0xA098, 0xA098 }, +{ 0xA099, 0xA099, 0xA099 }, +{ 0xA09A, 0xA09A, 0xA09A }, +{ 0xA09B, 0xA09B, 0xA09B }, +{ 0xA09C, 0xA09C, 0xA09C }, +{ 0xA09D, 0xA09D, 0xA09D }, +{ 0xA09E, 0xA09E, 0xA09E }, +{ 0xA09F, 0xA09F, 0xA09F }, +{ 0xA0A0, 0xA0A0, 0xA0A0 }, +{ 0xA0A1, 0xA0A1, 0xA0A1 }, +{ 0xA0A2, 0xA0A2, 0xA0A2 }, +{ 0xA0A3, 0xA0A3, 0xA0A3 }, +{ 0xA0A4, 0xA0A4, 0xA0A4 }, +{ 0xA0A5, 0xA0A5, 0xA0A5 }, +{ 0xA0A6, 0xA0A6, 0xA0A6 }, +{ 0xA0A7, 0xA0A7, 0xA0A7 }, +{ 0xA0A8, 0xA0A8, 0xA0A8 }, +{ 0xA0A9, 0xA0A9, 0xA0A9 }, +{ 0xA0AA, 0xA0AA, 0xA0AA }, +{ 0xA0AB, 0xA0AB, 0xA0AB }, +{ 0xA0AC, 0xA0AC, 0xA0AC }, +{ 0xA0AD, 0xA0AD, 0xA0AD }, +{ 0xA0AE, 0xA0AE, 0xA0AE }, +{ 0xA0AF, 0xA0AF, 0xA0AF }, +{ 0xA0B0, 0xA0B0, 0xA0B0 }, +{ 0xA0B1, 0xA0B1, 0xA0B1 }, +{ 0xA0B2, 0xA0B2, 0xA0B2 }, +{ 0xA0B3, 0xA0B3, 0xA0B3 }, +{ 0xA0B4, 0xA0B4, 0xA0B4 }, +{ 0xA0B5, 0xA0B5, 0xA0B5 }, +{ 0xA0B6, 0xA0B6, 0xA0B6 }, +{ 0xA0B7, 0xA0B7, 0xA0B7 }, +{ 0xA0B8, 0xA0B8, 0xA0B8 }, +{ 0xA0B9, 0xA0B9, 0xA0B9 }, +{ 0xA0BA, 0xA0BA, 0xA0BA }, +{ 0xA0BB, 0xA0BB, 0xA0BB }, +{ 0xA0BC, 0xA0BC, 0xA0BC }, +{ 0xA0BD, 0xA0BD, 0xA0BD }, +{ 0xA0BE, 0xA0BE, 0xA0BE }, +{ 0xA0BF, 0xA0BF, 0xA0BF }, +{ 0xA0C0, 0xA0C0, 0xA0C0 }, +{ 0xA0C1, 0xA0C1, 0xA0C1 }, +{ 0xA0C2, 0xA0C2, 0xA0C2 }, +{ 0xA0C3, 0xA0C3, 0xA0C3 }, +{ 0xA0C4, 0xA0C4, 0xA0C4 }, +{ 0xA0C5, 0xA0C5, 0xA0C5 }, +{ 0xA0C6, 0xA0C6, 0xA0C6 }, +{ 0xA0C7, 0xA0C7, 0xA0C7 }, +{ 0xA0C8, 0xA0C8, 0xA0C8 }, +{ 0xA0C9, 0xA0C9, 0xA0C9 }, +{ 0xA0CA, 0xA0CA, 0xA0CA }, +{ 0xA0CB, 0xA0CB, 0xA0CB }, +{ 0xA0CC, 0xA0CC, 0xA0CC }, +{ 0xA0CD, 0xA0CD, 0xA0CD }, +{ 0xA0CE, 0xA0CE, 0xA0CE }, +{ 0xA0CF, 0xA0CF, 0xA0CF }, +{ 0xA0D0, 0xA0D0, 0xA0D0 }, +{ 0xA0D1, 0xA0D1, 0xA0D1 }, +{ 0xA0D2, 0xA0D2, 0xA0D2 }, +{ 0xA0D3, 0xA0D3, 0xA0D3 }, +{ 0xA0D4, 0xA0D4, 0xA0D4 }, +{ 0xA0D5, 0xA0D5, 0xA0D5 }, +{ 0xA0D6, 0xA0D6, 0xA0D6 }, +{ 0xA0D7, 0xA0D7, 0xA0D7 }, +{ 0xA0D8, 0xA0D8, 0xA0D8 }, +{ 0xA0D9, 0xA0D9, 0xA0D9 }, +{ 0xA0DA, 0xA0DA, 0xA0DA }, +{ 0xA0DB, 0xA0DB, 0xA0DB }, +{ 0xA0DC, 0xA0DC, 0xA0DC }, +{ 0xA0DD, 0xA0DD, 0xA0DD }, +{ 0xA0DE, 0xA0DE, 0xA0DE }, +{ 0xA0DF, 0xA0DF, 0xA0DF }, +{ 0xA0E0, 0xA0E0, 0xA0E0 }, +{ 0xA0E1, 0xA0E1, 0xA0E1 }, +{ 0xA0E2, 0xA0E2, 0xA0E2 }, +{ 0xA0E3, 0xA0E3, 0xA0E3 }, +{ 0xA0E4, 0xA0E4, 0xA0E4 }, +{ 0xA0E5, 0xA0E5, 0xA0E5 }, +{ 0xA0E6, 0xA0E6, 0xA0E6 }, +{ 0xA0E7, 0xA0E7, 0xA0E7 }, +{ 0xA0E8, 0xA0E8, 0xA0E8 }, +{ 0xA0E9, 0xA0E9, 0xA0E9 }, +{ 0xA0EA, 0xA0EA, 0xA0EA }, +{ 0xA0EB, 0xA0EB, 0xA0EB }, +{ 0xA0EC, 0xA0EC, 0xA0EC }, +{ 0xA0ED, 0xA0ED, 0xA0ED }, +{ 0xA0EE, 0xA0EE, 0xA0EE }, +{ 0xA0EF, 0xA0EF, 0xA0EF }, +{ 0xA0F0, 0xA0F0, 0xA0F0 }, +{ 0xA0F1, 0xA0F1, 0xA0F1 }, +{ 0xA0F2, 0xA0F2, 0xA0F2 }, +{ 0xA0F3, 0xA0F3, 0xA0F3 }, +{ 0xA0F4, 0xA0F4, 0xA0F4 }, +{ 0xA0F5, 0xA0F5, 0xA0F5 }, +{ 0xA0F6, 0xA0F6, 0xA0F6 }, +{ 0xA0F7, 0xA0F7, 0xA0F7 }, +{ 0xA0F8, 0xA0F8, 0xA0F8 }, +{ 0xA0F9, 0xA0F9, 0xA0F9 }, +{ 0xA0FA, 0xA0FA, 0xA0FA }, +{ 0xA0FB, 0xA0FB, 0xA0FB }, +{ 0xA0FC, 0xA0FC, 0xA0FC }, +{ 0xA0FD, 0xA0FD, 0xA0FD }, +{ 0xA0FE, 0xA0FE, 0xA0FE }, +{ 0xA0FF, 0xA0FF, 0xA0FF }, +{ 0xA100, 0xA100, 0xA100 }, +{ 0xA101, 0xA101, 0xA101 }, +{ 0xA102, 0xA102, 0xA102 }, +{ 0xA103, 0xA103, 0xA103 }, +{ 0xA104, 0xA104, 0xA104 }, +{ 0xA105, 0xA105, 0xA105 }, +{ 0xA106, 0xA106, 0xA106 }, +{ 0xA107, 0xA107, 0xA107 }, +{ 0xA108, 0xA108, 0xA108 }, +{ 0xA109, 0xA109, 0xA109 }, +{ 0xA10A, 0xA10A, 0xA10A }, +{ 0xA10B, 0xA10B, 0xA10B }, +{ 0xA10C, 0xA10C, 0xA10C }, +{ 0xA10D, 0xA10D, 0xA10D }, +{ 0xA10E, 0xA10E, 0xA10E }, +{ 0xA10F, 0xA10F, 0xA10F }, +{ 0xA110, 0xA110, 0xA110 }, +{ 0xA111, 0xA111, 0xA111 }, +{ 0xA112, 0xA112, 0xA112 }, +{ 0xA113, 0xA113, 0xA113 }, +{ 0xA114, 0xA114, 0xA114 }, +{ 0xA115, 0xA115, 0xA115 }, +{ 0xA116, 0xA116, 0xA116 }, +{ 0xA117, 0xA117, 0xA117 }, +{ 0xA118, 0xA118, 0xA118 }, +{ 0xA119, 0xA119, 0xA119 }, +{ 0xA11A, 0xA11A, 0xA11A }, +{ 0xA11B, 0xA11B, 0xA11B }, +{ 0xA11C, 0xA11C, 0xA11C }, +{ 0xA11D, 0xA11D, 0xA11D }, +{ 0xA11E, 0xA11E, 0xA11E }, +{ 0xA11F, 0xA11F, 0xA11F }, +{ 0xA120, 0xA120, 0xA120 }, +{ 0xA121, 0xA121, 0xA121 }, +{ 0xA122, 0xA122, 0xA122 }, +{ 0xA123, 0xA123, 0xA123 }, +{ 0xA124, 0xA124, 0xA124 }, +{ 0xA125, 0xA125, 0xA125 }, +{ 0xA126, 0xA126, 0xA126 }, +{ 0xA127, 0xA127, 0xA127 }, +{ 0xA128, 0xA128, 0xA128 }, +{ 0xA129, 0xA129, 0xA129 }, +{ 0xA12A, 0xA12A, 0xA12A }, +{ 0xA12B, 0xA12B, 0xA12B }, +{ 0xA12C, 0xA12C, 0xA12C }, +{ 0xA12D, 0xA12D, 0xA12D }, +{ 0xA12E, 0xA12E, 0xA12E }, +{ 0xA12F, 0xA12F, 0xA12F }, +{ 0xA130, 0xA130, 0xA130 }, +{ 0xA131, 0xA131, 0xA131 }, +{ 0xA132, 0xA132, 0xA132 }, +{ 0xA133, 0xA133, 0xA133 }, +{ 0xA134, 0xA134, 0xA134 }, +{ 0xA135, 0xA135, 0xA135 }, +{ 0xA136, 0xA136, 0xA136 }, +{ 0xA137, 0xA137, 0xA137 }, +{ 0xA138, 0xA138, 0xA138 }, +{ 0xA139, 0xA139, 0xA139 }, +{ 0xA13A, 0xA13A, 0xA13A }, +{ 0xA13B, 0xA13B, 0xA13B }, +{ 0xA13C, 0xA13C, 0xA13C }, +{ 0xA13D, 0xA13D, 0xA13D }, +{ 0xA13E, 0xA13E, 0xA13E }, +{ 0xA13F, 0xA13F, 0xA13F }, +{ 0xA140, 0xA140, 0xA140 }, +{ 0xA141, 0xA141, 0xA141 }, +{ 0xA142, 0xA142, 0xA142 }, +{ 0xA143, 0xA143, 0xA143 }, +{ 0xA144, 0xA144, 0xA144 }, +{ 0xA145, 0xA145, 0xA145 }, +{ 0xA146, 0xA146, 0xA146 }, +{ 0xA147, 0xA147, 0xA147 }, +{ 0xA148, 0xA148, 0xA148 }, +{ 0xA149, 0xA149, 0xA149 }, +{ 0xA14A, 0xA14A, 0xA14A }, +{ 0xA14B, 0xA14B, 0xA14B }, +{ 0xA14C, 0xA14C, 0xA14C }, +{ 0xA14D, 0xA14D, 0xA14D }, +{ 0xA14E, 0xA14E, 0xA14E }, +{ 0xA14F, 0xA14F, 0xA14F }, +{ 0xA150, 0xA150, 0xA150 }, +{ 0xA151, 0xA151, 0xA151 }, +{ 0xA152, 0xA152, 0xA152 }, +{ 0xA153, 0xA153, 0xA153 }, +{ 0xA154, 0xA154, 0xA154 }, +{ 0xA155, 0xA155, 0xA155 }, +{ 0xA156, 0xA156, 0xA156 }, +{ 0xA157, 0xA157, 0xA157 }, +{ 0xA158, 0xA158, 0xA158 }, +{ 0xA159, 0xA159, 0xA159 }, +{ 0xA15A, 0xA15A, 0xA15A }, +{ 0xA15B, 0xA15B, 0xA15B }, +{ 0xA15C, 0xA15C, 0xA15C }, +{ 0xA15D, 0xA15D, 0xA15D }, +{ 0xA15E, 0xA15E, 0xA15E }, +{ 0xA15F, 0xA15F, 0xA15F }, +{ 0xA160, 0xA160, 0xA160 }, +{ 0xA161, 0xA161, 0xA161 }, +{ 0xA162, 0xA162, 0xA162 }, +{ 0xA163, 0xA163, 0xA163 }, +{ 0xA164, 0xA164, 0xA164 }, +{ 0xA165, 0xA165, 0xA165 }, +{ 0xA166, 0xA166, 0xA166 }, +{ 0xA167, 0xA167, 0xA167 }, +{ 0xA168, 0xA168, 0xA168 }, +{ 0xA169, 0xA169, 0xA169 }, +{ 0xA16A, 0xA16A, 0xA16A }, +{ 0xA16B, 0xA16B, 0xA16B }, +{ 0xA16C, 0xA16C, 0xA16C }, +{ 0xA16D, 0xA16D, 0xA16D }, +{ 0xA16E, 0xA16E, 0xA16E }, +{ 0xA16F, 0xA16F, 0xA16F }, +{ 0xA170, 0xA170, 0xA170 }, +{ 0xA171, 0xA171, 0xA171 }, +{ 0xA172, 0xA172, 0xA172 }, +{ 0xA173, 0xA173, 0xA173 }, +{ 0xA174, 0xA174, 0xA174 }, +{ 0xA175, 0xA175, 0xA175 }, +{ 0xA176, 0xA176, 0xA176 }, +{ 0xA177, 0xA177, 0xA177 }, +{ 0xA178, 0xA178, 0xA178 }, +{ 0xA179, 0xA179, 0xA179 }, +{ 0xA17A, 0xA17A, 0xA17A }, +{ 0xA17B, 0xA17B, 0xA17B }, +{ 0xA17C, 0xA17C, 0xA17C }, +{ 0xA17D, 0xA17D, 0xA17D }, +{ 0xA17E, 0xA17E, 0xA17E }, +{ 0xA17F, 0xA17F, 0xA17F }, +{ 0xA180, 0xA180, 0xA180 }, +{ 0xA181, 0xA181, 0xA181 }, +{ 0xA182, 0xA182, 0xA182 }, +{ 0xA183, 0xA183, 0xA183 }, +{ 0xA184, 0xA184, 0xA184 }, +{ 0xA185, 0xA185, 0xA185 }, +{ 0xA186, 0xA186, 0xA186 }, +{ 0xA187, 0xA187, 0xA187 }, +{ 0xA188, 0xA188, 0xA188 }, +{ 0xA189, 0xA189, 0xA189 }, +{ 0xA18A, 0xA18A, 0xA18A }, +{ 0xA18B, 0xA18B, 0xA18B }, +{ 0xA18C, 0xA18C, 0xA18C }, +{ 0xA18D, 0xA18D, 0xA18D }, +{ 0xA18E, 0xA18E, 0xA18E }, +{ 0xA18F, 0xA18F, 0xA18F }, +{ 0xA190, 0xA190, 0xA190 }, +{ 0xA191, 0xA191, 0xA191 }, +{ 0xA192, 0xA192, 0xA192 }, +{ 0xA193, 0xA193, 0xA193 }, +{ 0xA194, 0xA194, 0xA194 }, +{ 0xA195, 0xA195, 0xA195 }, +{ 0xA196, 0xA196, 0xA196 }, +{ 0xA197, 0xA197, 0xA197 }, +{ 0xA198, 0xA198, 0xA198 }, +{ 0xA199, 0xA199, 0xA199 }, +{ 0xA19A, 0xA19A, 0xA19A }, +{ 0xA19B, 0xA19B, 0xA19B }, +{ 0xA19C, 0xA19C, 0xA19C }, +{ 0xA19D, 0xA19D, 0xA19D }, +{ 0xA19E, 0xA19E, 0xA19E }, +{ 0xA19F, 0xA19F, 0xA19F }, +{ 0xA1A0, 0xA1A0, 0xA1A0 }, +{ 0xA1A1, 0xA1A1, 0xA1A1 }, +{ 0xA1A2, 0xA1A2, 0xA1A2 }, +{ 0xA1A3, 0xA1A3, 0xA1A3 }, +{ 0xA1A4, 0xA1A4, 0xA1A4 }, +{ 0xA1A5, 0xA1A5, 0xA1A5 }, +{ 0xA1A6, 0xA1A6, 0xA1A6 }, +{ 0xA1A7, 0xA1A7, 0xA1A7 }, +{ 0xA1A8, 0xA1A8, 0xA1A8 }, +{ 0xA1A9, 0xA1A9, 0xA1A9 }, +{ 0xA1AA, 0xA1AA, 0xA1AA }, +{ 0xA1AB, 0xA1AB, 0xA1AB }, +{ 0xA1AC, 0xA1AC, 0xA1AC }, +{ 0xA1AD, 0xA1AD, 0xA1AD }, +{ 0xA1AE, 0xA1AE, 0xA1AE }, +{ 0xA1AF, 0xA1AF, 0xA1AF }, +{ 0xA1B0, 0xA1B0, 0xA1B0 }, +{ 0xA1B1, 0xA1B1, 0xA1B1 }, +{ 0xA1B2, 0xA1B2, 0xA1B2 }, +{ 0xA1B3, 0xA1B3, 0xA1B3 }, +{ 0xA1B4, 0xA1B4, 0xA1B4 }, +{ 0xA1B5, 0xA1B5, 0xA1B5 }, +{ 0xA1B6, 0xA1B6, 0xA1B6 }, +{ 0xA1B7, 0xA1B7, 0xA1B7 }, +{ 0xA1B8, 0xA1B8, 0xA1B8 }, +{ 0xA1B9, 0xA1B9, 0xA1B9 }, +{ 0xA1BA, 0xA1BA, 0xA1BA }, +{ 0xA1BB, 0xA1BB, 0xA1BB }, +{ 0xA1BC, 0xA1BC, 0xA1BC }, +{ 0xA1BD, 0xA1BD, 0xA1BD }, +{ 0xA1BE, 0xA1BE, 0xA1BE }, +{ 0xA1BF, 0xA1BF, 0xA1BF }, +{ 0xA1C0, 0xA1C0, 0xA1C0 }, +{ 0xA1C1, 0xA1C1, 0xA1C1 }, +{ 0xA1C2, 0xA1C2, 0xA1C2 }, +{ 0xA1C3, 0xA1C3, 0xA1C3 }, +{ 0xA1C4, 0xA1C4, 0xA1C4 }, +{ 0xA1C5, 0xA1C5, 0xA1C5 }, +{ 0xA1C6, 0xA1C6, 0xA1C6 }, +{ 0xA1C7, 0xA1C7, 0xA1C7 }, +{ 0xA1C8, 0xA1C8, 0xA1C8 }, +{ 0xA1C9, 0xA1C9, 0xA1C9 }, +{ 0xA1CA, 0xA1CA, 0xA1CA }, +{ 0xA1CB, 0xA1CB, 0xA1CB }, +{ 0xA1CC, 0xA1CC, 0xA1CC }, +{ 0xA1CD, 0xA1CD, 0xA1CD }, +{ 0xA1CE, 0xA1CE, 0xA1CE }, +{ 0xA1CF, 0xA1CF, 0xA1CF }, +{ 0xA1D0, 0xA1D0, 0xA1D0 }, +{ 0xA1D1, 0xA1D1, 0xA1D1 }, +{ 0xA1D2, 0xA1D2, 0xA1D2 }, +{ 0xA1D3, 0xA1D3, 0xA1D3 }, +{ 0xA1D4, 0xA1D4, 0xA1D4 }, +{ 0xA1D5, 0xA1D5, 0xA1D5 }, +{ 0xA1D6, 0xA1D6, 0xA1D6 }, +{ 0xA1D7, 0xA1D7, 0xA1D7 }, +{ 0xA1D8, 0xA1D8, 0xA1D8 }, +{ 0xA1D9, 0xA1D9, 0xA1D9 }, +{ 0xA1DA, 0xA1DA, 0xA1DA }, +{ 0xA1DB, 0xA1DB, 0xA1DB }, +{ 0xA1DC, 0xA1DC, 0xA1DC }, +{ 0xA1DD, 0xA1DD, 0xA1DD }, +{ 0xA1DE, 0xA1DE, 0xA1DE }, +{ 0xA1DF, 0xA1DF, 0xA1DF }, +{ 0xA1E0, 0xA1E0, 0xA1E0 }, +{ 0xA1E1, 0xA1E1, 0xA1E1 }, +{ 0xA1E2, 0xA1E2, 0xA1E2 }, +{ 0xA1E3, 0xA1E3, 0xA1E3 }, +{ 0xA1E4, 0xA1E4, 0xA1E4 }, +{ 0xA1E5, 0xA1E5, 0xA1E5 }, +{ 0xA1E6, 0xA1E6, 0xA1E6 }, +{ 0xA1E7, 0xA1E7, 0xA1E7 }, +{ 0xA1E8, 0xA1E8, 0xA1E8 }, +{ 0xA1E9, 0xA1E9, 0xA1E9 }, +{ 0xA1EA, 0xA1EA, 0xA1EA }, +{ 0xA1EB, 0xA1EB, 0xA1EB }, +{ 0xA1EC, 0xA1EC, 0xA1EC }, +{ 0xA1ED, 0xA1ED, 0xA1ED }, +{ 0xA1EE, 0xA1EE, 0xA1EE }, +{ 0xA1EF, 0xA1EF, 0xA1EF }, +{ 0xA1F0, 0xA1F0, 0xA1F0 }, +{ 0xA1F1, 0xA1F1, 0xA1F1 }, +{ 0xA1F2, 0xA1F2, 0xA1F2 }, +{ 0xA1F3, 0xA1F3, 0xA1F3 }, +{ 0xA1F4, 0xA1F4, 0xA1F4 }, +{ 0xA1F5, 0xA1F5, 0xA1F5 }, +{ 0xA1F6, 0xA1F6, 0xA1F6 }, +{ 0xA1F7, 0xA1F7, 0xA1F7 }, +{ 0xA1F8, 0xA1F8, 0xA1F8 }, +{ 0xA1F9, 0xA1F9, 0xA1F9 }, +{ 0xA1FA, 0xA1FA, 0xA1FA }, +{ 0xA1FB, 0xA1FB, 0xA1FB }, +{ 0xA1FC, 0xA1FC, 0xA1FC }, +{ 0xA1FD, 0xA1FD, 0xA1FD }, +{ 0xA1FE, 0xA1FE, 0xA1FE }, +{ 0xA1FF, 0xA1FF, 0xA1FF }, +{ 0xA200, 0xA200, 0xA200 }, +{ 0xA201, 0xA201, 0xA201 }, +{ 0xA202, 0xA202, 0xA202 }, +{ 0xA203, 0xA203, 0xA203 }, +{ 0xA204, 0xA204, 0xA204 }, +{ 0xA205, 0xA205, 0xA205 }, +{ 0xA206, 0xA206, 0xA206 }, +{ 0xA207, 0xA207, 0xA207 }, +{ 0xA208, 0xA208, 0xA208 }, +{ 0xA209, 0xA209, 0xA209 }, +{ 0xA20A, 0xA20A, 0xA20A }, +{ 0xA20B, 0xA20B, 0xA20B }, +{ 0xA20C, 0xA20C, 0xA20C }, +{ 0xA20D, 0xA20D, 0xA20D }, +{ 0xA20E, 0xA20E, 0xA20E }, +{ 0xA20F, 0xA20F, 0xA20F }, +{ 0xA210, 0xA210, 0xA210 }, +{ 0xA211, 0xA211, 0xA211 }, +{ 0xA212, 0xA212, 0xA212 }, +{ 0xA213, 0xA213, 0xA213 }, +{ 0xA214, 0xA214, 0xA214 }, +{ 0xA215, 0xA215, 0xA215 }, +{ 0xA216, 0xA216, 0xA216 }, +{ 0xA217, 0xA217, 0xA217 }, +{ 0xA218, 0xA218, 0xA218 }, +{ 0xA219, 0xA219, 0xA219 }, +{ 0xA21A, 0xA21A, 0xA21A }, +{ 0xA21B, 0xA21B, 0xA21B }, +{ 0xA21C, 0xA21C, 0xA21C }, +{ 0xA21D, 0xA21D, 0xA21D }, +{ 0xA21E, 0xA21E, 0xA21E }, +{ 0xA21F, 0xA21F, 0xA21F }, +{ 0xA220, 0xA220, 0xA220 }, +{ 0xA221, 0xA221, 0xA221 }, +{ 0xA222, 0xA222, 0xA222 }, +{ 0xA223, 0xA223, 0xA223 }, +{ 0xA224, 0xA224, 0xA224 }, +{ 0xA225, 0xA225, 0xA225 }, +{ 0xA226, 0xA226, 0xA226 }, +{ 0xA227, 0xA227, 0xA227 }, +{ 0xA228, 0xA228, 0xA228 }, +{ 0xA229, 0xA229, 0xA229 }, +{ 0xA22A, 0xA22A, 0xA22A }, +{ 0xA22B, 0xA22B, 0xA22B }, +{ 0xA22C, 0xA22C, 0xA22C }, +{ 0xA22D, 0xA22D, 0xA22D }, +{ 0xA22E, 0xA22E, 0xA22E }, +{ 0xA22F, 0xA22F, 0xA22F }, +{ 0xA230, 0xA230, 0xA230 }, +{ 0xA231, 0xA231, 0xA231 }, +{ 0xA232, 0xA232, 0xA232 }, +{ 0xA233, 0xA233, 0xA233 }, +{ 0xA234, 0xA234, 0xA234 }, +{ 0xA235, 0xA235, 0xA235 }, +{ 0xA236, 0xA236, 0xA236 }, +{ 0xA237, 0xA237, 0xA237 }, +{ 0xA238, 0xA238, 0xA238 }, +{ 0xA239, 0xA239, 0xA239 }, +{ 0xA23A, 0xA23A, 0xA23A }, +{ 0xA23B, 0xA23B, 0xA23B }, +{ 0xA23C, 0xA23C, 0xA23C }, +{ 0xA23D, 0xA23D, 0xA23D }, +{ 0xA23E, 0xA23E, 0xA23E }, +{ 0xA23F, 0xA23F, 0xA23F }, +{ 0xA240, 0xA240, 0xA240 }, +{ 0xA241, 0xA241, 0xA241 }, +{ 0xA242, 0xA242, 0xA242 }, +{ 0xA243, 0xA243, 0xA243 }, +{ 0xA244, 0xA244, 0xA244 }, +{ 0xA245, 0xA245, 0xA245 }, +{ 0xA246, 0xA246, 0xA246 }, +{ 0xA247, 0xA247, 0xA247 }, +{ 0xA248, 0xA248, 0xA248 }, +{ 0xA249, 0xA249, 0xA249 }, +{ 0xA24A, 0xA24A, 0xA24A }, +{ 0xA24B, 0xA24B, 0xA24B }, +{ 0xA24C, 0xA24C, 0xA24C }, +{ 0xA24D, 0xA24D, 0xA24D }, +{ 0xA24E, 0xA24E, 0xA24E }, +{ 0xA24F, 0xA24F, 0xA24F }, +{ 0xA250, 0xA250, 0xA250 }, +{ 0xA251, 0xA251, 0xA251 }, +{ 0xA252, 0xA252, 0xA252 }, +{ 0xA253, 0xA253, 0xA253 }, +{ 0xA254, 0xA254, 0xA254 }, +{ 0xA255, 0xA255, 0xA255 }, +{ 0xA256, 0xA256, 0xA256 }, +{ 0xA257, 0xA257, 0xA257 }, +{ 0xA258, 0xA258, 0xA258 }, +{ 0xA259, 0xA259, 0xA259 }, +{ 0xA25A, 0xA25A, 0xA25A }, +{ 0xA25B, 0xA25B, 0xA25B }, +{ 0xA25C, 0xA25C, 0xA25C }, +{ 0xA25D, 0xA25D, 0xA25D }, +{ 0xA25E, 0xA25E, 0xA25E }, +{ 0xA25F, 0xA25F, 0xA25F }, +{ 0xA260, 0xA260, 0xA260 }, +{ 0xA261, 0xA261, 0xA261 }, +{ 0xA262, 0xA262, 0xA262 }, +{ 0xA263, 0xA263, 0xA263 }, +{ 0xA264, 0xA264, 0xA264 }, +{ 0xA265, 0xA265, 0xA265 }, +{ 0xA266, 0xA266, 0xA266 }, +{ 0xA267, 0xA267, 0xA267 }, +{ 0xA268, 0xA268, 0xA268 }, +{ 0xA269, 0xA269, 0xA269 }, +{ 0xA26A, 0xA26A, 0xA26A }, +{ 0xA26B, 0xA26B, 0xA26B }, +{ 0xA26C, 0xA26C, 0xA26C }, +{ 0xA26D, 0xA26D, 0xA26D }, +{ 0xA26E, 0xA26E, 0xA26E }, +{ 0xA26F, 0xA26F, 0xA26F }, +{ 0xA270, 0xA270, 0xA270 }, +{ 0xA271, 0xA271, 0xA271 }, +{ 0xA272, 0xA272, 0xA272 }, +{ 0xA273, 0xA273, 0xA273 }, +{ 0xA274, 0xA274, 0xA274 }, +{ 0xA275, 0xA275, 0xA275 }, +{ 0xA276, 0xA276, 0xA276 }, +{ 0xA277, 0xA277, 0xA277 }, +{ 0xA278, 0xA278, 0xA278 }, +{ 0xA279, 0xA279, 0xA279 }, +{ 0xA27A, 0xA27A, 0xA27A }, +{ 0xA27B, 0xA27B, 0xA27B }, +{ 0xA27C, 0xA27C, 0xA27C }, +{ 0xA27D, 0xA27D, 0xA27D }, +{ 0xA27E, 0xA27E, 0xA27E }, +{ 0xA27F, 0xA27F, 0xA27F }, +{ 0xA280, 0xA280, 0xA280 }, +{ 0xA281, 0xA281, 0xA281 }, +{ 0xA282, 0xA282, 0xA282 }, +{ 0xA283, 0xA283, 0xA283 }, +{ 0xA284, 0xA284, 0xA284 }, +{ 0xA285, 0xA285, 0xA285 }, +{ 0xA286, 0xA286, 0xA286 }, +{ 0xA287, 0xA287, 0xA287 }, +{ 0xA288, 0xA288, 0xA288 }, +{ 0xA289, 0xA289, 0xA289 }, +{ 0xA28A, 0xA28A, 0xA28A }, +{ 0xA28B, 0xA28B, 0xA28B }, +{ 0xA28C, 0xA28C, 0xA28C }, +{ 0xA28D, 0xA28D, 0xA28D }, +{ 0xA28E, 0xA28E, 0xA28E }, +{ 0xA28F, 0xA28F, 0xA28F }, +{ 0xA290, 0xA290, 0xA290 }, +{ 0xA291, 0xA291, 0xA291 }, +{ 0xA292, 0xA292, 0xA292 }, +{ 0xA293, 0xA293, 0xA293 }, +{ 0xA294, 0xA294, 0xA294 }, +{ 0xA295, 0xA295, 0xA295 }, +{ 0xA296, 0xA296, 0xA296 }, +{ 0xA297, 0xA297, 0xA297 }, +{ 0xA298, 0xA298, 0xA298 }, +{ 0xA299, 0xA299, 0xA299 }, +{ 0xA29A, 0xA29A, 0xA29A }, +{ 0xA29B, 0xA29B, 0xA29B }, +{ 0xA29C, 0xA29C, 0xA29C }, +{ 0xA29D, 0xA29D, 0xA29D }, +{ 0xA29E, 0xA29E, 0xA29E }, +{ 0xA29F, 0xA29F, 0xA29F }, +{ 0xA2A0, 0xA2A0, 0xA2A0 }, +{ 0xA2A1, 0xA2A1, 0xA2A1 }, +{ 0xA2A2, 0xA2A2, 0xA2A2 }, +{ 0xA2A3, 0xA2A3, 0xA2A3 }, +{ 0xA2A4, 0xA2A4, 0xA2A4 }, +{ 0xA2A5, 0xA2A5, 0xA2A5 }, +{ 0xA2A6, 0xA2A6, 0xA2A6 }, +{ 0xA2A7, 0xA2A7, 0xA2A7 }, +{ 0xA2A8, 0xA2A8, 0xA2A8 }, +{ 0xA2A9, 0xA2A9, 0xA2A9 }, +{ 0xA2AA, 0xA2AA, 0xA2AA }, +{ 0xA2AB, 0xA2AB, 0xA2AB }, +{ 0xA2AC, 0xA2AC, 0xA2AC }, +{ 0xA2AD, 0xA2AD, 0xA2AD }, +{ 0xA2AE, 0xA2AE, 0xA2AE }, +{ 0xA2AF, 0xA2AF, 0xA2AF }, +{ 0xA2B0, 0xA2B0, 0xA2B0 }, +{ 0xA2B1, 0xA2B1, 0xA2B1 }, +{ 0xA2B2, 0xA2B2, 0xA2B2 }, +{ 0xA2B3, 0xA2B3, 0xA2B3 }, +{ 0xA2B4, 0xA2B4, 0xA2B4 }, +{ 0xA2B5, 0xA2B5, 0xA2B5 }, +{ 0xA2B6, 0xA2B6, 0xA2B6 }, +{ 0xA2B7, 0xA2B7, 0xA2B7 }, +{ 0xA2B8, 0xA2B8, 0xA2B8 }, +{ 0xA2B9, 0xA2B9, 0xA2B9 }, +{ 0xA2BA, 0xA2BA, 0xA2BA }, +{ 0xA2BB, 0xA2BB, 0xA2BB }, +{ 0xA2BC, 0xA2BC, 0xA2BC }, +{ 0xA2BD, 0xA2BD, 0xA2BD }, +{ 0xA2BE, 0xA2BE, 0xA2BE }, +{ 0xA2BF, 0xA2BF, 0xA2BF }, +{ 0xA2C0, 0xA2C0, 0xA2C0 }, +{ 0xA2C1, 0xA2C1, 0xA2C1 }, +{ 0xA2C2, 0xA2C2, 0xA2C2 }, +{ 0xA2C3, 0xA2C3, 0xA2C3 }, +{ 0xA2C4, 0xA2C4, 0xA2C4 }, +{ 0xA2C5, 0xA2C5, 0xA2C5 }, +{ 0xA2C6, 0xA2C6, 0xA2C6 }, +{ 0xA2C7, 0xA2C7, 0xA2C7 }, +{ 0xA2C8, 0xA2C8, 0xA2C8 }, +{ 0xA2C9, 0xA2C9, 0xA2C9 }, +{ 0xA2CA, 0xA2CA, 0xA2CA }, +{ 0xA2CB, 0xA2CB, 0xA2CB }, +{ 0xA2CC, 0xA2CC, 0xA2CC }, +{ 0xA2CD, 0xA2CD, 0xA2CD }, +{ 0xA2CE, 0xA2CE, 0xA2CE }, +{ 0xA2CF, 0xA2CF, 0xA2CF }, +{ 0xA2D0, 0xA2D0, 0xA2D0 }, +{ 0xA2D1, 0xA2D1, 0xA2D1 }, +{ 0xA2D2, 0xA2D2, 0xA2D2 }, +{ 0xA2D3, 0xA2D3, 0xA2D3 }, +{ 0xA2D4, 0xA2D4, 0xA2D4 }, +{ 0xA2D5, 0xA2D5, 0xA2D5 }, +{ 0xA2D6, 0xA2D6, 0xA2D6 }, +{ 0xA2D7, 0xA2D7, 0xA2D7 }, +{ 0xA2D8, 0xA2D8, 0xA2D8 }, +{ 0xA2D9, 0xA2D9, 0xA2D9 }, +{ 0xA2DA, 0xA2DA, 0xA2DA }, +{ 0xA2DB, 0xA2DB, 0xA2DB }, +{ 0xA2DC, 0xA2DC, 0xA2DC }, +{ 0xA2DD, 0xA2DD, 0xA2DD }, +{ 0xA2DE, 0xA2DE, 0xA2DE }, +{ 0xA2DF, 0xA2DF, 0xA2DF }, +{ 0xA2E0, 0xA2E0, 0xA2E0 }, +{ 0xA2E1, 0xA2E1, 0xA2E1 }, +{ 0xA2E2, 0xA2E2, 0xA2E2 }, +{ 0xA2E3, 0xA2E3, 0xA2E3 }, +{ 0xA2E4, 0xA2E4, 0xA2E4 }, +{ 0xA2E5, 0xA2E5, 0xA2E5 }, +{ 0xA2E6, 0xA2E6, 0xA2E6 }, +{ 0xA2E7, 0xA2E7, 0xA2E7 }, +{ 0xA2E8, 0xA2E8, 0xA2E8 }, +{ 0xA2E9, 0xA2E9, 0xA2E9 }, +{ 0xA2EA, 0xA2EA, 0xA2EA }, +{ 0xA2EB, 0xA2EB, 0xA2EB }, +{ 0xA2EC, 0xA2EC, 0xA2EC }, +{ 0xA2ED, 0xA2ED, 0xA2ED }, +{ 0xA2EE, 0xA2EE, 0xA2EE }, +{ 0xA2EF, 0xA2EF, 0xA2EF }, +{ 0xA2F0, 0xA2F0, 0xA2F0 }, +{ 0xA2F1, 0xA2F1, 0xA2F1 }, +{ 0xA2F2, 0xA2F2, 0xA2F2 }, +{ 0xA2F3, 0xA2F3, 0xA2F3 }, +{ 0xA2F4, 0xA2F4, 0xA2F4 }, +{ 0xA2F5, 0xA2F5, 0xA2F5 }, +{ 0xA2F6, 0xA2F6, 0xA2F6 }, +{ 0xA2F7, 0xA2F7, 0xA2F7 }, +{ 0xA2F8, 0xA2F8, 0xA2F8 }, +{ 0xA2F9, 0xA2F9, 0xA2F9 }, +{ 0xA2FA, 0xA2FA, 0xA2FA }, +{ 0xA2FB, 0xA2FB, 0xA2FB }, +{ 0xA2FC, 0xA2FC, 0xA2FC }, +{ 0xA2FD, 0xA2FD, 0xA2FD }, +{ 0xA2FE, 0xA2FE, 0xA2FE }, +{ 0xA2FF, 0xA2FF, 0xA2FF }, +{ 0xA300, 0xA300, 0xA300 }, +{ 0xA301, 0xA301, 0xA301 }, +{ 0xA302, 0xA302, 0xA302 }, +{ 0xA303, 0xA303, 0xA303 }, +{ 0xA304, 0xA304, 0xA304 }, +{ 0xA305, 0xA305, 0xA305 }, +{ 0xA306, 0xA306, 0xA306 }, +{ 0xA307, 0xA307, 0xA307 }, +{ 0xA308, 0xA308, 0xA308 }, +{ 0xA309, 0xA309, 0xA309 }, +{ 0xA30A, 0xA30A, 0xA30A }, +{ 0xA30B, 0xA30B, 0xA30B }, +{ 0xA30C, 0xA30C, 0xA30C }, +{ 0xA30D, 0xA30D, 0xA30D }, +{ 0xA30E, 0xA30E, 0xA30E }, +{ 0xA30F, 0xA30F, 0xA30F }, +{ 0xA310, 0xA310, 0xA310 }, +{ 0xA311, 0xA311, 0xA311 }, +{ 0xA312, 0xA312, 0xA312 }, +{ 0xA313, 0xA313, 0xA313 }, +{ 0xA314, 0xA314, 0xA314 }, +{ 0xA315, 0xA315, 0xA315 }, +{ 0xA316, 0xA316, 0xA316 }, +{ 0xA317, 0xA317, 0xA317 }, +{ 0xA318, 0xA318, 0xA318 }, +{ 0xA319, 0xA319, 0xA319 }, +{ 0xA31A, 0xA31A, 0xA31A }, +{ 0xA31B, 0xA31B, 0xA31B }, +{ 0xA31C, 0xA31C, 0xA31C }, +{ 0xA31D, 0xA31D, 0xA31D }, +{ 0xA31E, 0xA31E, 0xA31E }, +{ 0xA31F, 0xA31F, 0xA31F }, +{ 0xA320, 0xA320, 0xA320 }, +{ 0xA321, 0xA321, 0xA321 }, +{ 0xA322, 0xA322, 0xA322 }, +{ 0xA323, 0xA323, 0xA323 }, +{ 0xA324, 0xA324, 0xA324 }, +{ 0xA325, 0xA325, 0xA325 }, +{ 0xA326, 0xA326, 0xA326 }, +{ 0xA327, 0xA327, 0xA327 }, +{ 0xA328, 0xA328, 0xA328 }, +{ 0xA329, 0xA329, 0xA329 }, +{ 0xA32A, 0xA32A, 0xA32A }, +{ 0xA32B, 0xA32B, 0xA32B }, +{ 0xA32C, 0xA32C, 0xA32C }, +{ 0xA32D, 0xA32D, 0xA32D }, +{ 0xA32E, 0xA32E, 0xA32E }, +{ 0xA32F, 0xA32F, 0xA32F }, +{ 0xA330, 0xA330, 0xA330 }, +{ 0xA331, 0xA331, 0xA331 }, +{ 0xA332, 0xA332, 0xA332 }, +{ 0xA333, 0xA333, 0xA333 }, +{ 0xA334, 0xA334, 0xA334 }, +{ 0xA335, 0xA335, 0xA335 }, +{ 0xA336, 0xA336, 0xA336 }, +{ 0xA337, 0xA337, 0xA337 }, +{ 0xA338, 0xA338, 0xA338 }, +{ 0xA339, 0xA339, 0xA339 }, +{ 0xA33A, 0xA33A, 0xA33A }, +{ 0xA33B, 0xA33B, 0xA33B }, +{ 0xA33C, 0xA33C, 0xA33C }, +{ 0xA33D, 0xA33D, 0xA33D }, +{ 0xA33E, 0xA33E, 0xA33E }, +{ 0xA33F, 0xA33F, 0xA33F }, +{ 0xA340, 0xA340, 0xA340 }, +{ 0xA341, 0xA341, 0xA341 }, +{ 0xA342, 0xA342, 0xA342 }, +{ 0xA343, 0xA343, 0xA343 }, +{ 0xA344, 0xA344, 0xA344 }, +{ 0xA345, 0xA345, 0xA345 }, +{ 0xA346, 0xA346, 0xA346 }, +{ 0xA347, 0xA347, 0xA347 }, +{ 0xA348, 0xA348, 0xA348 }, +{ 0xA349, 0xA349, 0xA349 }, +{ 0xA34A, 0xA34A, 0xA34A }, +{ 0xA34B, 0xA34B, 0xA34B }, +{ 0xA34C, 0xA34C, 0xA34C }, +{ 0xA34D, 0xA34D, 0xA34D }, +{ 0xA34E, 0xA34E, 0xA34E }, +{ 0xA34F, 0xA34F, 0xA34F }, +{ 0xA350, 0xA350, 0xA350 }, +{ 0xA351, 0xA351, 0xA351 }, +{ 0xA352, 0xA352, 0xA352 }, +{ 0xA353, 0xA353, 0xA353 }, +{ 0xA354, 0xA354, 0xA354 }, +{ 0xA355, 0xA355, 0xA355 }, +{ 0xA356, 0xA356, 0xA356 }, +{ 0xA357, 0xA357, 0xA357 }, +{ 0xA358, 0xA358, 0xA358 }, +{ 0xA359, 0xA359, 0xA359 }, +{ 0xA35A, 0xA35A, 0xA35A }, +{ 0xA35B, 0xA35B, 0xA35B }, +{ 0xA35C, 0xA35C, 0xA35C }, +{ 0xA35D, 0xA35D, 0xA35D }, +{ 0xA35E, 0xA35E, 0xA35E }, +{ 0xA35F, 0xA35F, 0xA35F }, +{ 0xA360, 0xA360, 0xA360 }, +{ 0xA361, 0xA361, 0xA361 }, +{ 0xA362, 0xA362, 0xA362 }, +{ 0xA363, 0xA363, 0xA363 }, +{ 0xA364, 0xA364, 0xA364 }, +{ 0xA365, 0xA365, 0xA365 }, +{ 0xA366, 0xA366, 0xA366 }, +{ 0xA367, 0xA367, 0xA367 }, +{ 0xA368, 0xA368, 0xA368 }, +{ 0xA369, 0xA369, 0xA369 }, +{ 0xA36A, 0xA36A, 0xA36A }, +{ 0xA36B, 0xA36B, 0xA36B }, +{ 0xA36C, 0xA36C, 0xA36C }, +{ 0xA36D, 0xA36D, 0xA36D }, +{ 0xA36E, 0xA36E, 0xA36E }, +{ 0xA36F, 0xA36F, 0xA36F }, +{ 0xA370, 0xA370, 0xA370 }, +{ 0xA371, 0xA371, 0xA371 }, +{ 0xA372, 0xA372, 0xA372 }, +{ 0xA373, 0xA373, 0xA373 }, +{ 0xA374, 0xA374, 0xA374 }, +{ 0xA375, 0xA375, 0xA375 }, +{ 0xA376, 0xA376, 0xA376 }, +{ 0xA377, 0xA377, 0xA377 }, +{ 0xA378, 0xA378, 0xA378 }, +{ 0xA379, 0xA379, 0xA379 }, +{ 0xA37A, 0xA37A, 0xA37A }, +{ 0xA37B, 0xA37B, 0xA37B }, +{ 0xA37C, 0xA37C, 0xA37C }, +{ 0xA37D, 0xA37D, 0xA37D }, +{ 0xA37E, 0xA37E, 0xA37E }, +{ 0xA37F, 0xA37F, 0xA37F }, +{ 0xA380, 0xA380, 0xA380 }, +{ 0xA381, 0xA381, 0xA381 }, +{ 0xA382, 0xA382, 0xA382 }, +{ 0xA383, 0xA383, 0xA383 }, +{ 0xA384, 0xA384, 0xA384 }, +{ 0xA385, 0xA385, 0xA385 }, +{ 0xA386, 0xA386, 0xA386 }, +{ 0xA387, 0xA387, 0xA387 }, +{ 0xA388, 0xA388, 0xA388 }, +{ 0xA389, 0xA389, 0xA389 }, +{ 0xA38A, 0xA38A, 0xA38A }, +{ 0xA38B, 0xA38B, 0xA38B }, +{ 0xA38C, 0xA38C, 0xA38C }, +{ 0xA38D, 0xA38D, 0xA38D }, +{ 0xA38E, 0xA38E, 0xA38E }, +{ 0xA38F, 0xA38F, 0xA38F }, +{ 0xA390, 0xA390, 0xA390 }, +{ 0xA391, 0xA391, 0xA391 }, +{ 0xA392, 0xA392, 0xA392 }, +{ 0xA393, 0xA393, 0xA393 }, +{ 0xA394, 0xA394, 0xA394 }, +{ 0xA395, 0xA395, 0xA395 }, +{ 0xA396, 0xA396, 0xA396 }, +{ 0xA397, 0xA397, 0xA397 }, +{ 0xA398, 0xA398, 0xA398 }, +{ 0xA399, 0xA399, 0xA399 }, +{ 0xA39A, 0xA39A, 0xA39A }, +{ 0xA39B, 0xA39B, 0xA39B }, +{ 0xA39C, 0xA39C, 0xA39C }, +{ 0xA39D, 0xA39D, 0xA39D }, +{ 0xA39E, 0xA39E, 0xA39E }, +{ 0xA39F, 0xA39F, 0xA39F }, +{ 0xA3A0, 0xA3A0, 0xA3A0 }, +{ 0xA3A1, 0xA3A1, 0xA3A1 }, +{ 0xA3A2, 0xA3A2, 0xA3A2 }, +{ 0xA3A3, 0xA3A3, 0xA3A3 }, +{ 0xA3A4, 0xA3A4, 0xA3A4 }, +{ 0xA3A5, 0xA3A5, 0xA3A5 }, +{ 0xA3A6, 0xA3A6, 0xA3A6 }, +{ 0xA3A7, 0xA3A7, 0xA3A7 }, +{ 0xA3A8, 0xA3A8, 0xA3A8 }, +{ 0xA3A9, 0xA3A9, 0xA3A9 }, +{ 0xA3AA, 0xA3AA, 0xA3AA }, +{ 0xA3AB, 0xA3AB, 0xA3AB }, +{ 0xA3AC, 0xA3AC, 0xA3AC }, +{ 0xA3AD, 0xA3AD, 0xA3AD }, +{ 0xA3AE, 0xA3AE, 0xA3AE }, +{ 0xA3AF, 0xA3AF, 0xA3AF }, +{ 0xA3B0, 0xA3B0, 0xA3B0 }, +{ 0xA3B1, 0xA3B1, 0xA3B1 }, +{ 0xA3B2, 0xA3B2, 0xA3B2 }, +{ 0xA3B3, 0xA3B3, 0xA3B3 }, +{ 0xA3B4, 0xA3B4, 0xA3B4 }, +{ 0xA3B5, 0xA3B5, 0xA3B5 }, +{ 0xA3B6, 0xA3B6, 0xA3B6 }, +{ 0xA3B7, 0xA3B7, 0xA3B7 }, +{ 0xA3B8, 0xA3B8, 0xA3B8 }, +{ 0xA3B9, 0xA3B9, 0xA3B9 }, +{ 0xA3BA, 0xA3BA, 0xA3BA }, +{ 0xA3BB, 0xA3BB, 0xA3BB }, +{ 0xA3BC, 0xA3BC, 0xA3BC }, +{ 0xA3BD, 0xA3BD, 0xA3BD }, +{ 0xA3BE, 0xA3BE, 0xA3BE }, +{ 0xA3BF, 0xA3BF, 0xA3BF }, +{ 0xA3C0, 0xA3C0, 0xA3C0 }, +{ 0xA3C1, 0xA3C1, 0xA3C1 }, +{ 0xA3C2, 0xA3C2, 0xA3C2 }, +{ 0xA3C3, 0xA3C3, 0xA3C3 }, +{ 0xA3C4, 0xA3C4, 0xA3C4 }, +{ 0xA3C5, 0xA3C5, 0xA3C5 }, +{ 0xA3C6, 0xA3C6, 0xA3C6 }, +{ 0xA3C7, 0xA3C7, 0xA3C7 }, +{ 0xA3C8, 0xA3C8, 0xA3C8 }, +{ 0xA3C9, 0xA3C9, 0xA3C9 }, +{ 0xA3CA, 0xA3CA, 0xA3CA }, +{ 0xA3CB, 0xA3CB, 0xA3CB }, +{ 0xA3CC, 0xA3CC, 0xA3CC }, +{ 0xA3CD, 0xA3CD, 0xA3CD }, +{ 0xA3CE, 0xA3CE, 0xA3CE }, +{ 0xA3CF, 0xA3CF, 0xA3CF }, +{ 0xA3D0, 0xA3D0, 0xA3D0 }, +{ 0xA3D1, 0xA3D1, 0xA3D1 }, +{ 0xA3D2, 0xA3D2, 0xA3D2 }, +{ 0xA3D3, 0xA3D3, 0xA3D3 }, +{ 0xA3D4, 0xA3D4, 0xA3D4 }, +{ 0xA3D5, 0xA3D5, 0xA3D5 }, +{ 0xA3D6, 0xA3D6, 0xA3D6 }, +{ 0xA3D7, 0xA3D7, 0xA3D7 }, +{ 0xA3D8, 0xA3D8, 0xA3D8 }, +{ 0xA3D9, 0xA3D9, 0xA3D9 }, +{ 0xA3DA, 0xA3DA, 0xA3DA }, +{ 0xA3DB, 0xA3DB, 0xA3DB }, +{ 0xA3DC, 0xA3DC, 0xA3DC }, +{ 0xA3DD, 0xA3DD, 0xA3DD }, +{ 0xA3DE, 0xA3DE, 0xA3DE }, +{ 0xA3DF, 0xA3DF, 0xA3DF }, +{ 0xA3E0, 0xA3E0, 0xA3E0 }, +{ 0xA3E1, 0xA3E1, 0xA3E1 }, +{ 0xA3E2, 0xA3E2, 0xA3E2 }, +{ 0xA3E3, 0xA3E3, 0xA3E3 }, +{ 0xA3E4, 0xA3E4, 0xA3E4 }, +{ 0xA3E5, 0xA3E5, 0xA3E5 }, +{ 0xA3E6, 0xA3E6, 0xA3E6 }, +{ 0xA3E7, 0xA3E7, 0xA3E7 }, +{ 0xA3E8, 0xA3E8, 0xA3E8 }, +{ 0xA3E9, 0xA3E9, 0xA3E9 }, +{ 0xA3EA, 0xA3EA, 0xA3EA }, +{ 0xA3EB, 0xA3EB, 0xA3EB }, +{ 0xA3EC, 0xA3EC, 0xA3EC }, +{ 0xA3ED, 0xA3ED, 0xA3ED }, +{ 0xA3EE, 0xA3EE, 0xA3EE }, +{ 0xA3EF, 0xA3EF, 0xA3EF }, +{ 0xA3F0, 0xA3F0, 0xA3F0 }, +{ 0xA3F1, 0xA3F1, 0xA3F1 }, +{ 0xA3F2, 0xA3F2, 0xA3F2 }, +{ 0xA3F3, 0xA3F3, 0xA3F3 }, +{ 0xA3F4, 0xA3F4, 0xA3F4 }, +{ 0xA3F5, 0xA3F5, 0xA3F5 }, +{ 0xA3F6, 0xA3F6, 0xA3F6 }, +{ 0xA3F7, 0xA3F7, 0xA3F7 }, +{ 0xA3F8, 0xA3F8, 0xA3F8 }, +{ 0xA3F9, 0xA3F9, 0xA3F9 }, +{ 0xA3FA, 0xA3FA, 0xA3FA }, +{ 0xA3FB, 0xA3FB, 0xA3FB }, +{ 0xA3FC, 0xA3FC, 0xA3FC }, +{ 0xA3FD, 0xA3FD, 0xA3FD }, +{ 0xA3FE, 0xA3FE, 0xA3FE }, +{ 0xA3FF, 0xA3FF, 0xA3FF }, +{ 0xA400, 0xA400, 0xA400 }, +{ 0xA401, 0xA401, 0xA401 }, +{ 0xA402, 0xA402, 0xA402 }, +{ 0xA403, 0xA403, 0xA403 }, +{ 0xA404, 0xA404, 0xA404 }, +{ 0xA405, 0xA405, 0xA405 }, +{ 0xA406, 0xA406, 0xA406 }, +{ 0xA407, 0xA407, 0xA407 }, +{ 0xA408, 0xA408, 0xA408 }, +{ 0xA409, 0xA409, 0xA409 }, +{ 0xA40A, 0xA40A, 0xA40A }, +{ 0xA40B, 0xA40B, 0xA40B }, +{ 0xA40C, 0xA40C, 0xA40C }, +{ 0xA40D, 0xA40D, 0xA40D }, +{ 0xA40E, 0xA40E, 0xA40E }, +{ 0xA40F, 0xA40F, 0xA40F }, +{ 0xA410, 0xA410, 0xA410 }, +{ 0xA411, 0xA411, 0xA411 }, +{ 0xA412, 0xA412, 0xA412 }, +{ 0xA413, 0xA413, 0xA413 }, +{ 0xA414, 0xA414, 0xA414 }, +{ 0xA415, 0xA415, 0xA415 }, +{ 0xA416, 0xA416, 0xA416 }, +{ 0xA417, 0xA417, 0xA417 }, +{ 0xA418, 0xA418, 0xA418 }, +{ 0xA419, 0xA419, 0xA419 }, +{ 0xA41A, 0xA41A, 0xA41A }, +{ 0xA41B, 0xA41B, 0xA41B }, +{ 0xA41C, 0xA41C, 0xA41C }, +{ 0xA41D, 0xA41D, 0xA41D }, +{ 0xA41E, 0xA41E, 0xA41E }, +{ 0xA41F, 0xA41F, 0xA41F }, +{ 0xA420, 0xA420, 0xA420 }, +{ 0xA421, 0xA421, 0xA421 }, +{ 0xA422, 0xA422, 0xA422 }, +{ 0xA423, 0xA423, 0xA423 }, +{ 0xA424, 0xA424, 0xA424 }, +{ 0xA425, 0xA425, 0xA425 }, +{ 0xA426, 0xA426, 0xA426 }, +{ 0xA427, 0xA427, 0xA427 }, +{ 0xA428, 0xA428, 0xA428 }, +{ 0xA429, 0xA429, 0xA429 }, +{ 0xA42A, 0xA42A, 0xA42A }, +{ 0xA42B, 0xA42B, 0xA42B }, +{ 0xA42C, 0xA42C, 0xA42C }, +{ 0xA42D, 0xA42D, 0xA42D }, +{ 0xA42E, 0xA42E, 0xA42E }, +{ 0xA42F, 0xA42F, 0xA42F }, +{ 0xA430, 0xA430, 0xA430 }, +{ 0xA431, 0xA431, 0xA431 }, +{ 0xA432, 0xA432, 0xA432 }, +{ 0xA433, 0xA433, 0xA433 }, +{ 0xA434, 0xA434, 0xA434 }, +{ 0xA435, 0xA435, 0xA435 }, +{ 0xA436, 0xA436, 0xA436 }, +{ 0xA437, 0xA437, 0xA437 }, +{ 0xA438, 0xA438, 0xA438 }, +{ 0xA439, 0xA439, 0xA439 }, +{ 0xA43A, 0xA43A, 0xA43A }, +{ 0xA43B, 0xA43B, 0xA43B }, +{ 0xA43C, 0xA43C, 0xA43C }, +{ 0xA43D, 0xA43D, 0xA43D }, +{ 0xA43E, 0xA43E, 0xA43E }, +{ 0xA43F, 0xA43F, 0xA43F }, +{ 0xA440, 0xA440, 0xA440 }, +{ 0xA441, 0xA441, 0xA441 }, +{ 0xA442, 0xA442, 0xA442 }, +{ 0xA443, 0xA443, 0xA443 }, +{ 0xA444, 0xA444, 0xA444 }, +{ 0xA445, 0xA445, 0xA445 }, +{ 0xA446, 0xA446, 0xA446 }, +{ 0xA447, 0xA447, 0xA447 }, +{ 0xA448, 0xA448, 0xA448 }, +{ 0xA449, 0xA449, 0xA449 }, +{ 0xA44A, 0xA44A, 0xA44A }, +{ 0xA44B, 0xA44B, 0xA44B }, +{ 0xA44C, 0xA44C, 0xA44C }, +{ 0xA44D, 0xA44D, 0xA44D }, +{ 0xA44E, 0xA44E, 0xA44E }, +{ 0xA44F, 0xA44F, 0xA44F }, +{ 0xA450, 0xA450, 0xA450 }, +{ 0xA451, 0xA451, 0xA451 }, +{ 0xA452, 0xA452, 0xA452 }, +{ 0xA453, 0xA453, 0xA453 }, +{ 0xA454, 0xA454, 0xA454 }, +{ 0xA455, 0xA455, 0xA455 }, +{ 0xA456, 0xA456, 0xA456 }, +{ 0xA457, 0xA457, 0xA457 }, +{ 0xA458, 0xA458, 0xA458 }, +{ 0xA459, 0xA459, 0xA459 }, +{ 0xA45A, 0xA45A, 0xA45A }, +{ 0xA45B, 0xA45B, 0xA45B }, +{ 0xA45C, 0xA45C, 0xA45C }, +{ 0xA45D, 0xA45D, 0xA45D }, +{ 0xA45E, 0xA45E, 0xA45E }, +{ 0xA45F, 0xA45F, 0xA45F }, +{ 0xA460, 0xA460, 0xA460 }, +{ 0xA461, 0xA461, 0xA461 }, +{ 0xA462, 0xA462, 0xA462 }, +{ 0xA463, 0xA463, 0xA463 }, +{ 0xA464, 0xA464, 0xA464 }, +{ 0xA465, 0xA465, 0xA465 }, +{ 0xA466, 0xA466, 0xA466 }, +{ 0xA467, 0xA467, 0xA467 }, +{ 0xA468, 0xA468, 0xA468 }, +{ 0xA469, 0xA469, 0xA469 }, +{ 0xA46A, 0xA46A, 0xA46A }, +{ 0xA46B, 0xA46B, 0xA46B }, +{ 0xA46C, 0xA46C, 0xA46C }, +{ 0xA46D, 0xA46D, 0xA46D }, +{ 0xA46E, 0xA46E, 0xA46E }, +{ 0xA46F, 0xA46F, 0xA46F }, +{ 0xA470, 0xA470, 0xA470 }, +{ 0xA471, 0xA471, 0xA471 }, +{ 0xA472, 0xA472, 0xA472 }, +{ 0xA473, 0xA473, 0xA473 }, +{ 0xA474, 0xA474, 0xA474 }, +{ 0xA475, 0xA475, 0xA475 }, +{ 0xA476, 0xA476, 0xA476 }, +{ 0xA477, 0xA477, 0xA477 }, +{ 0xA478, 0xA478, 0xA478 }, +{ 0xA479, 0xA479, 0xA479 }, +{ 0xA47A, 0xA47A, 0xA47A }, +{ 0xA47B, 0xA47B, 0xA47B }, +{ 0xA47C, 0xA47C, 0xA47C }, +{ 0xA47D, 0xA47D, 0xA47D }, +{ 0xA47E, 0xA47E, 0xA47E }, +{ 0xA47F, 0xA47F, 0xA47F }, +{ 0xA480, 0xA480, 0xA480 }, +{ 0xA481, 0xA481, 0xA481 }, +{ 0xA482, 0xA482, 0xA482 }, +{ 0xA483, 0xA483, 0xA483 }, +{ 0xA484, 0xA484, 0xA484 }, +{ 0xA485, 0xA485, 0xA485 }, +{ 0xA486, 0xA486, 0xA486 }, +{ 0xA487, 0xA487, 0xA487 }, +{ 0xA488, 0xA488, 0xA488 }, +{ 0xA489, 0xA489, 0xA489 }, +{ 0xA48A, 0xA48A, 0xA48A }, +{ 0xA48B, 0xA48B, 0xA48B }, +{ 0xA48C, 0xA48C, 0xA48C }, +{ 0xA800, 0xA800, 0xA800 }, +{ 0xA801, 0xA801, 0xA801 }, +{ 0xA803, 0xA803, 0xA803 }, +{ 0xA804, 0xA804, 0xA804 }, +{ 0xA805, 0xA805, 0xA805 }, +{ 0xA806, 0xA806, 0xA806 }, +{ 0xA807, 0xA807, 0xA807 }, +{ 0xA808, 0xA808, 0xA808 }, +{ 0xA809, 0xA809, 0xA809 }, +{ 0xA80A, 0xA80A, 0xA80A }, +{ 0xA80B, 0xA80B, 0xA80B }, +{ 0xA80C, 0xA80C, 0xA80C }, +{ 0xA80D, 0xA80D, 0xA80D }, +{ 0xA80E, 0xA80E, 0xA80E }, +{ 0xA80F, 0xA80F, 0xA80F }, +{ 0xA810, 0xA810, 0xA810 }, +{ 0xA811, 0xA811, 0xA811 }, +{ 0xA812, 0xA812, 0xA812 }, +{ 0xA813, 0xA813, 0xA813 }, +{ 0xA814, 0xA814, 0xA814 }, +{ 0xA815, 0xA815, 0xA815 }, +{ 0xA816, 0xA816, 0xA816 }, +{ 0xA817, 0xA817, 0xA817 }, +{ 0xA818, 0xA818, 0xA818 }, +{ 0xA819, 0xA819, 0xA819 }, +{ 0xA81A, 0xA81A, 0xA81A }, +{ 0xA81B, 0xA81B, 0xA81B }, +{ 0xA81C, 0xA81C, 0xA81C }, +{ 0xA81D, 0xA81D, 0xA81D }, +{ 0xA81E, 0xA81E, 0xA81E }, +{ 0xA81F, 0xA81F, 0xA81F }, +{ 0xA820, 0xA820, 0xA820 }, +{ 0xA821, 0xA821, 0xA821 }, +{ 0xA822, 0xA822, 0xA822 }, +{ 0xA825, 0xA825, 0xA825 }, +{ 0xA826, 0xA826, 0xA826 }, +{ 0xAC00, 0xAC00, 0xAC00 }, +{ 0xAC01, 0xAC01, 0xAC01 }, +{ 0xAC02, 0xAC02, 0xAC02 }, +{ 0xAC03, 0xAC03, 0xAC03 }, +{ 0xAC04, 0xAC04, 0xAC04 }, +{ 0xAC05, 0xAC05, 0xAC05 }, +{ 0xAC06, 0xAC06, 0xAC06 }, +{ 0xAC07, 0xAC07, 0xAC07 }, +{ 0xAC08, 0xAC08, 0xAC08 }, +{ 0xAC09, 0xAC09, 0xAC09 }, +{ 0xAC0A, 0xAC0A, 0xAC0A }, +{ 0xAC0B, 0xAC0B, 0xAC0B }, +{ 0xAC0C, 0xAC0C, 0xAC0C }, +{ 0xAC0D, 0xAC0D, 0xAC0D }, +{ 0xAC0E, 0xAC0E, 0xAC0E }, +{ 0xAC0F, 0xAC0F, 0xAC0F }, +{ 0xAC10, 0xAC10, 0xAC10 }, +{ 0xAC11, 0xAC11, 0xAC11 }, +{ 0xAC12, 0xAC12, 0xAC12 }, +{ 0xAC13, 0xAC13, 0xAC13 }, +{ 0xAC14, 0xAC14, 0xAC14 }, +{ 0xAC15, 0xAC15, 0xAC15 }, +{ 0xAC16, 0xAC16, 0xAC16 }, +{ 0xAC17, 0xAC17, 0xAC17 }, +{ 0xAC18, 0xAC18, 0xAC18 }, +{ 0xAC19, 0xAC19, 0xAC19 }, +{ 0xAC1A, 0xAC1A, 0xAC1A }, +{ 0xAC1B, 0xAC1B, 0xAC1B }, +{ 0xAC1C, 0xAC1C, 0xAC1C }, +{ 0xAC1D, 0xAC1D, 0xAC1D }, +{ 0xAC1E, 0xAC1E, 0xAC1E }, +{ 0xAC1F, 0xAC1F, 0xAC1F }, +{ 0xAC20, 0xAC20, 0xAC20 }, +{ 0xAC21, 0xAC21, 0xAC21 }, +{ 0xAC22, 0xAC22, 0xAC22 }, +{ 0xAC23, 0xAC23, 0xAC23 }, +{ 0xAC24, 0xAC24, 0xAC24 }, +{ 0xAC25, 0xAC25, 0xAC25 }, +{ 0xAC26, 0xAC26, 0xAC26 }, +{ 0xAC27, 0xAC27, 0xAC27 }, +{ 0xAC28, 0xAC28, 0xAC28 }, +{ 0xAC29, 0xAC29, 0xAC29 }, +{ 0xAC2A, 0xAC2A, 0xAC2A }, +{ 0xAC2B, 0xAC2B, 0xAC2B }, +{ 0xAC2C, 0xAC2C, 0xAC2C }, +{ 0xAC2D, 0xAC2D, 0xAC2D }, +{ 0xAC2E, 0xAC2E, 0xAC2E }, +{ 0xAC2F, 0xAC2F, 0xAC2F }, +{ 0xAC30, 0xAC30, 0xAC30 }, +{ 0xAC31, 0xAC31, 0xAC31 }, +{ 0xAC32, 0xAC32, 0xAC32 }, +{ 0xAC33, 0xAC33, 0xAC33 }, +{ 0xAC34, 0xAC34, 0xAC34 }, +{ 0xAC35, 0xAC35, 0xAC35 }, +{ 0xAC36, 0xAC36, 0xAC36 }, +{ 0xAC37, 0xAC37, 0xAC37 }, +{ 0xAC38, 0xAC38, 0xAC38 }, +{ 0xAC39, 0xAC39, 0xAC39 }, +{ 0xAC3A, 0xAC3A, 0xAC3A }, +{ 0xAC3B, 0xAC3B, 0xAC3B }, +{ 0xAC3C, 0xAC3C, 0xAC3C }, +{ 0xAC3D, 0xAC3D, 0xAC3D }, +{ 0xAC3E, 0xAC3E, 0xAC3E }, +{ 0xAC3F, 0xAC3F, 0xAC3F }, +{ 0xAC40, 0xAC40, 0xAC40 }, +{ 0xAC41, 0xAC41, 0xAC41 }, +{ 0xAC42, 0xAC42, 0xAC42 }, +{ 0xAC43, 0xAC43, 0xAC43 }, +{ 0xAC44, 0xAC44, 0xAC44 }, +{ 0xAC45, 0xAC45, 0xAC45 }, +{ 0xAC46, 0xAC46, 0xAC46 }, +{ 0xAC47, 0xAC47, 0xAC47 }, +{ 0xAC48, 0xAC48, 0xAC48 }, +{ 0xAC49, 0xAC49, 0xAC49 }, +{ 0xAC4A, 0xAC4A, 0xAC4A }, +{ 0xAC4B, 0xAC4B, 0xAC4B }, +{ 0xAC4C, 0xAC4C, 0xAC4C }, +{ 0xAC4D, 0xAC4D, 0xAC4D }, +{ 0xAC4E, 0xAC4E, 0xAC4E }, +{ 0xAC4F, 0xAC4F, 0xAC4F }, +{ 0xAC50, 0xAC50, 0xAC50 }, +{ 0xAC51, 0xAC51, 0xAC51 }, +{ 0xAC52, 0xAC52, 0xAC52 }, +{ 0xAC53, 0xAC53, 0xAC53 }, +{ 0xAC54, 0xAC54, 0xAC54 }, +{ 0xAC55, 0xAC55, 0xAC55 }, +{ 0xAC56, 0xAC56, 0xAC56 }, +{ 0xAC57, 0xAC57, 0xAC57 }, +{ 0xAC58, 0xAC58, 0xAC58 }, +{ 0xAC59, 0xAC59, 0xAC59 }, +{ 0xAC5A, 0xAC5A, 0xAC5A }, +{ 0xAC5B, 0xAC5B, 0xAC5B }, +{ 0xAC5C, 0xAC5C, 0xAC5C }, +{ 0xAC5D, 0xAC5D, 0xAC5D }, +{ 0xAC5E, 0xAC5E, 0xAC5E }, +{ 0xAC5F, 0xAC5F, 0xAC5F }, +{ 0xAC60, 0xAC60, 0xAC60 }, +{ 0xAC61, 0xAC61, 0xAC61 }, +{ 0xAC62, 0xAC62, 0xAC62 }, +{ 0xAC63, 0xAC63, 0xAC63 }, +{ 0xAC64, 0xAC64, 0xAC64 }, +{ 0xAC65, 0xAC65, 0xAC65 }, +{ 0xAC66, 0xAC66, 0xAC66 }, +{ 0xAC67, 0xAC67, 0xAC67 }, +{ 0xAC68, 0xAC68, 0xAC68 }, +{ 0xAC69, 0xAC69, 0xAC69 }, +{ 0xAC6A, 0xAC6A, 0xAC6A }, +{ 0xAC6B, 0xAC6B, 0xAC6B }, +{ 0xAC6C, 0xAC6C, 0xAC6C }, +{ 0xAC6D, 0xAC6D, 0xAC6D }, +{ 0xAC6E, 0xAC6E, 0xAC6E }, +{ 0xAC6F, 0xAC6F, 0xAC6F }, +{ 0xAC70, 0xAC70, 0xAC70 }, +{ 0xAC71, 0xAC71, 0xAC71 }, +{ 0xAC72, 0xAC72, 0xAC72 }, +{ 0xAC73, 0xAC73, 0xAC73 }, +{ 0xAC74, 0xAC74, 0xAC74 }, +{ 0xAC75, 0xAC75, 0xAC75 }, +{ 0xAC76, 0xAC76, 0xAC76 }, +{ 0xAC77, 0xAC77, 0xAC77 }, +{ 0xAC78, 0xAC78, 0xAC78 }, +{ 0xAC79, 0xAC79, 0xAC79 }, +{ 0xAC7A, 0xAC7A, 0xAC7A }, +{ 0xAC7B, 0xAC7B, 0xAC7B }, +{ 0xAC7C, 0xAC7C, 0xAC7C }, +{ 0xAC7D, 0xAC7D, 0xAC7D }, +{ 0xAC7E, 0xAC7E, 0xAC7E }, +{ 0xAC7F, 0xAC7F, 0xAC7F }, +{ 0xAC80, 0xAC80, 0xAC80 }, +{ 0xAC81, 0xAC81, 0xAC81 }, +{ 0xAC82, 0xAC82, 0xAC82 }, +{ 0xAC83, 0xAC83, 0xAC83 }, +{ 0xAC84, 0xAC84, 0xAC84 }, +{ 0xAC85, 0xAC85, 0xAC85 }, +{ 0xAC86, 0xAC86, 0xAC86 }, +{ 0xAC87, 0xAC87, 0xAC87 }, +{ 0xAC88, 0xAC88, 0xAC88 }, +{ 0xAC89, 0xAC89, 0xAC89 }, +{ 0xAC8A, 0xAC8A, 0xAC8A }, +{ 0xAC8B, 0xAC8B, 0xAC8B }, +{ 0xAC8C, 0xAC8C, 0xAC8C }, +{ 0xAC8D, 0xAC8D, 0xAC8D }, +{ 0xAC8E, 0xAC8E, 0xAC8E }, +{ 0xAC8F, 0xAC8F, 0xAC8F }, +{ 0xAC90, 0xAC90, 0xAC90 }, +{ 0xAC91, 0xAC91, 0xAC91 }, +{ 0xAC92, 0xAC92, 0xAC92 }, +{ 0xAC93, 0xAC93, 0xAC93 }, +{ 0xAC94, 0xAC94, 0xAC94 }, +{ 0xAC95, 0xAC95, 0xAC95 }, +{ 0xAC96, 0xAC96, 0xAC96 }, +{ 0xAC97, 0xAC97, 0xAC97 }, +{ 0xAC98, 0xAC98, 0xAC98 }, +{ 0xAC99, 0xAC99, 0xAC99 }, +{ 0xAC9A, 0xAC9A, 0xAC9A }, +{ 0xAC9B, 0xAC9B, 0xAC9B }, +{ 0xAC9C, 0xAC9C, 0xAC9C }, +{ 0xAC9D, 0xAC9D, 0xAC9D }, +{ 0xAC9E, 0xAC9E, 0xAC9E }, +{ 0xAC9F, 0xAC9F, 0xAC9F }, +{ 0xACA0, 0xACA0, 0xACA0 }, +{ 0xACA1, 0xACA1, 0xACA1 }, +{ 0xACA2, 0xACA2, 0xACA2 }, +{ 0xACA3, 0xACA3, 0xACA3 }, +{ 0xACA4, 0xACA4, 0xACA4 }, +{ 0xACA5, 0xACA5, 0xACA5 }, +{ 0xACA6, 0xACA6, 0xACA6 }, +{ 0xACA7, 0xACA7, 0xACA7 }, +{ 0xACA8, 0xACA8, 0xACA8 }, +{ 0xACA9, 0xACA9, 0xACA9 }, +{ 0xACAA, 0xACAA, 0xACAA }, +{ 0xACAB, 0xACAB, 0xACAB }, +{ 0xACAC, 0xACAC, 0xACAC }, +{ 0xACAD, 0xACAD, 0xACAD }, +{ 0xACAE, 0xACAE, 0xACAE }, +{ 0xACAF, 0xACAF, 0xACAF }, +{ 0xACB0, 0xACB0, 0xACB0 }, +{ 0xACB1, 0xACB1, 0xACB1 }, +{ 0xACB2, 0xACB2, 0xACB2 }, +{ 0xACB3, 0xACB3, 0xACB3 }, +{ 0xACB4, 0xACB4, 0xACB4 }, +{ 0xACB5, 0xACB5, 0xACB5 }, +{ 0xACB6, 0xACB6, 0xACB6 }, +{ 0xACB7, 0xACB7, 0xACB7 }, +{ 0xACB8, 0xACB8, 0xACB8 }, +{ 0xACB9, 0xACB9, 0xACB9 }, +{ 0xACBA, 0xACBA, 0xACBA }, +{ 0xACBB, 0xACBB, 0xACBB }, +{ 0xACBC, 0xACBC, 0xACBC }, +{ 0xACBD, 0xACBD, 0xACBD }, +{ 0xACBE, 0xACBE, 0xACBE }, +{ 0xACBF, 0xACBF, 0xACBF }, +{ 0xACC0, 0xACC0, 0xACC0 }, +{ 0xACC1, 0xACC1, 0xACC1 }, +{ 0xACC2, 0xACC2, 0xACC2 }, +{ 0xACC3, 0xACC3, 0xACC3 }, +{ 0xACC4, 0xACC4, 0xACC4 }, +{ 0xACC5, 0xACC5, 0xACC5 }, +{ 0xACC6, 0xACC6, 0xACC6 }, +{ 0xACC7, 0xACC7, 0xACC7 }, +{ 0xACC8, 0xACC8, 0xACC8 }, +{ 0xACC9, 0xACC9, 0xACC9 }, +{ 0xACCA, 0xACCA, 0xACCA }, +{ 0xACCB, 0xACCB, 0xACCB }, +{ 0xACCC, 0xACCC, 0xACCC }, +{ 0xACCD, 0xACCD, 0xACCD }, +{ 0xACCE, 0xACCE, 0xACCE }, +{ 0xACCF, 0xACCF, 0xACCF }, +{ 0xACD0, 0xACD0, 0xACD0 }, +{ 0xACD1, 0xACD1, 0xACD1 }, +{ 0xACD2, 0xACD2, 0xACD2 }, +{ 0xACD3, 0xACD3, 0xACD3 }, +{ 0xACD4, 0xACD4, 0xACD4 }, +{ 0xACD5, 0xACD5, 0xACD5 }, +{ 0xACD6, 0xACD6, 0xACD6 }, +{ 0xACD7, 0xACD7, 0xACD7 }, +{ 0xACD8, 0xACD8, 0xACD8 }, +{ 0xACD9, 0xACD9, 0xACD9 }, +{ 0xACDA, 0xACDA, 0xACDA }, +{ 0xACDB, 0xACDB, 0xACDB }, +{ 0xACDC, 0xACDC, 0xACDC }, +{ 0xACDD, 0xACDD, 0xACDD }, +{ 0xACDE, 0xACDE, 0xACDE }, +{ 0xACDF, 0xACDF, 0xACDF }, +{ 0xACE0, 0xACE0, 0xACE0 }, +{ 0xACE1, 0xACE1, 0xACE1 }, +{ 0xACE2, 0xACE2, 0xACE2 }, +{ 0xACE3, 0xACE3, 0xACE3 }, +{ 0xACE4, 0xACE4, 0xACE4 }, +{ 0xACE5, 0xACE5, 0xACE5 }, +{ 0xACE6, 0xACE6, 0xACE6 }, +{ 0xACE7, 0xACE7, 0xACE7 }, +{ 0xACE8, 0xACE8, 0xACE8 }, +{ 0xACE9, 0xACE9, 0xACE9 }, +{ 0xACEA, 0xACEA, 0xACEA }, +{ 0xACEB, 0xACEB, 0xACEB }, +{ 0xACEC, 0xACEC, 0xACEC }, +{ 0xACED, 0xACED, 0xACED }, +{ 0xACEE, 0xACEE, 0xACEE }, +{ 0xACEF, 0xACEF, 0xACEF }, +{ 0xACF0, 0xACF0, 0xACF0 }, +{ 0xACF1, 0xACF1, 0xACF1 }, +{ 0xACF2, 0xACF2, 0xACF2 }, +{ 0xACF3, 0xACF3, 0xACF3 }, +{ 0xACF4, 0xACF4, 0xACF4 }, +{ 0xACF5, 0xACF5, 0xACF5 }, +{ 0xACF6, 0xACF6, 0xACF6 }, +{ 0xACF7, 0xACF7, 0xACF7 }, +{ 0xACF8, 0xACF8, 0xACF8 }, +{ 0xACF9, 0xACF9, 0xACF9 }, +{ 0xACFA, 0xACFA, 0xACFA }, +{ 0xACFB, 0xACFB, 0xACFB }, +{ 0xACFC, 0xACFC, 0xACFC }, +{ 0xACFD, 0xACFD, 0xACFD }, +{ 0xACFE, 0xACFE, 0xACFE }, +{ 0xACFF, 0xACFF, 0xACFF }, +{ 0xAD00, 0xAD00, 0xAD00 }, +{ 0xAD01, 0xAD01, 0xAD01 }, +{ 0xAD02, 0xAD02, 0xAD02 }, +{ 0xAD03, 0xAD03, 0xAD03 }, +{ 0xAD04, 0xAD04, 0xAD04 }, +{ 0xAD05, 0xAD05, 0xAD05 }, +{ 0xAD06, 0xAD06, 0xAD06 }, +{ 0xAD07, 0xAD07, 0xAD07 }, +{ 0xAD08, 0xAD08, 0xAD08 }, +{ 0xAD09, 0xAD09, 0xAD09 }, +{ 0xAD0A, 0xAD0A, 0xAD0A }, +{ 0xAD0B, 0xAD0B, 0xAD0B }, +{ 0xAD0C, 0xAD0C, 0xAD0C }, +{ 0xAD0D, 0xAD0D, 0xAD0D }, +{ 0xAD0E, 0xAD0E, 0xAD0E }, +{ 0xAD0F, 0xAD0F, 0xAD0F }, +{ 0xAD10, 0xAD10, 0xAD10 }, +{ 0xAD11, 0xAD11, 0xAD11 }, +{ 0xAD12, 0xAD12, 0xAD12 }, +{ 0xAD13, 0xAD13, 0xAD13 }, +{ 0xAD14, 0xAD14, 0xAD14 }, +{ 0xAD15, 0xAD15, 0xAD15 }, +{ 0xAD16, 0xAD16, 0xAD16 }, +{ 0xAD17, 0xAD17, 0xAD17 }, +{ 0xAD18, 0xAD18, 0xAD18 }, +{ 0xAD19, 0xAD19, 0xAD19 }, +{ 0xAD1A, 0xAD1A, 0xAD1A }, +{ 0xAD1B, 0xAD1B, 0xAD1B }, +{ 0xAD1C, 0xAD1C, 0xAD1C }, +{ 0xAD1D, 0xAD1D, 0xAD1D }, +{ 0xAD1E, 0xAD1E, 0xAD1E }, +{ 0xAD1F, 0xAD1F, 0xAD1F }, +{ 0xAD20, 0xAD20, 0xAD20 }, +{ 0xAD21, 0xAD21, 0xAD21 }, +{ 0xAD22, 0xAD22, 0xAD22 }, +{ 0xAD23, 0xAD23, 0xAD23 }, +{ 0xAD24, 0xAD24, 0xAD24 }, +{ 0xAD25, 0xAD25, 0xAD25 }, +{ 0xAD26, 0xAD26, 0xAD26 }, +{ 0xAD27, 0xAD27, 0xAD27 }, +{ 0xAD28, 0xAD28, 0xAD28 }, +{ 0xAD29, 0xAD29, 0xAD29 }, +{ 0xAD2A, 0xAD2A, 0xAD2A }, +{ 0xAD2B, 0xAD2B, 0xAD2B }, +{ 0xAD2C, 0xAD2C, 0xAD2C }, +{ 0xAD2D, 0xAD2D, 0xAD2D }, +{ 0xAD2E, 0xAD2E, 0xAD2E }, +{ 0xAD2F, 0xAD2F, 0xAD2F }, +{ 0xAD30, 0xAD30, 0xAD30 }, +{ 0xAD31, 0xAD31, 0xAD31 }, +{ 0xAD32, 0xAD32, 0xAD32 }, +{ 0xAD33, 0xAD33, 0xAD33 }, +{ 0xAD34, 0xAD34, 0xAD34 }, +{ 0xAD35, 0xAD35, 0xAD35 }, +{ 0xAD36, 0xAD36, 0xAD36 }, +{ 0xAD37, 0xAD37, 0xAD37 }, +{ 0xAD38, 0xAD38, 0xAD38 }, +{ 0xAD39, 0xAD39, 0xAD39 }, +{ 0xAD3A, 0xAD3A, 0xAD3A }, +{ 0xAD3B, 0xAD3B, 0xAD3B }, +{ 0xAD3C, 0xAD3C, 0xAD3C }, +{ 0xAD3D, 0xAD3D, 0xAD3D }, +{ 0xAD3E, 0xAD3E, 0xAD3E }, +{ 0xAD3F, 0xAD3F, 0xAD3F }, +{ 0xAD40, 0xAD40, 0xAD40 }, +{ 0xAD41, 0xAD41, 0xAD41 }, +{ 0xAD42, 0xAD42, 0xAD42 }, +{ 0xAD43, 0xAD43, 0xAD43 }, +{ 0xAD44, 0xAD44, 0xAD44 }, +{ 0xAD45, 0xAD45, 0xAD45 }, +{ 0xAD46, 0xAD46, 0xAD46 }, +{ 0xAD47, 0xAD47, 0xAD47 }, +{ 0xAD48, 0xAD48, 0xAD48 }, +{ 0xAD49, 0xAD49, 0xAD49 }, +{ 0xAD4A, 0xAD4A, 0xAD4A }, +{ 0xAD4B, 0xAD4B, 0xAD4B }, +{ 0xAD4C, 0xAD4C, 0xAD4C }, +{ 0xAD4D, 0xAD4D, 0xAD4D }, +{ 0xAD4E, 0xAD4E, 0xAD4E }, +{ 0xAD4F, 0xAD4F, 0xAD4F }, +{ 0xAD50, 0xAD50, 0xAD50 }, +{ 0xAD51, 0xAD51, 0xAD51 }, +{ 0xAD52, 0xAD52, 0xAD52 }, +{ 0xAD53, 0xAD53, 0xAD53 }, +{ 0xAD54, 0xAD54, 0xAD54 }, +{ 0xAD55, 0xAD55, 0xAD55 }, +{ 0xAD56, 0xAD56, 0xAD56 }, +{ 0xAD57, 0xAD57, 0xAD57 }, +{ 0xAD58, 0xAD58, 0xAD58 }, +{ 0xAD59, 0xAD59, 0xAD59 }, +{ 0xAD5A, 0xAD5A, 0xAD5A }, +{ 0xAD5B, 0xAD5B, 0xAD5B }, +{ 0xAD5C, 0xAD5C, 0xAD5C }, +{ 0xAD5D, 0xAD5D, 0xAD5D }, +{ 0xAD5E, 0xAD5E, 0xAD5E }, +{ 0xAD5F, 0xAD5F, 0xAD5F }, +{ 0xAD60, 0xAD60, 0xAD60 }, +{ 0xAD61, 0xAD61, 0xAD61 }, +{ 0xAD62, 0xAD62, 0xAD62 }, +{ 0xAD63, 0xAD63, 0xAD63 }, +{ 0xAD64, 0xAD64, 0xAD64 }, +{ 0xAD65, 0xAD65, 0xAD65 }, +{ 0xAD66, 0xAD66, 0xAD66 }, +{ 0xAD67, 0xAD67, 0xAD67 }, +{ 0xAD68, 0xAD68, 0xAD68 }, +{ 0xAD69, 0xAD69, 0xAD69 }, +{ 0xAD6A, 0xAD6A, 0xAD6A }, +{ 0xAD6B, 0xAD6B, 0xAD6B }, +{ 0xAD6C, 0xAD6C, 0xAD6C }, +{ 0xAD6D, 0xAD6D, 0xAD6D }, +{ 0xAD6E, 0xAD6E, 0xAD6E }, +{ 0xAD6F, 0xAD6F, 0xAD6F }, +{ 0xAD70, 0xAD70, 0xAD70 }, +{ 0xAD71, 0xAD71, 0xAD71 }, +{ 0xAD72, 0xAD72, 0xAD72 }, +{ 0xAD73, 0xAD73, 0xAD73 }, +{ 0xAD74, 0xAD74, 0xAD74 }, +{ 0xAD75, 0xAD75, 0xAD75 }, +{ 0xAD76, 0xAD76, 0xAD76 }, +{ 0xAD77, 0xAD77, 0xAD77 }, +{ 0xAD78, 0xAD78, 0xAD78 }, +{ 0xAD79, 0xAD79, 0xAD79 }, +{ 0xAD7A, 0xAD7A, 0xAD7A }, +{ 0xAD7B, 0xAD7B, 0xAD7B }, +{ 0xAD7C, 0xAD7C, 0xAD7C }, +{ 0xAD7D, 0xAD7D, 0xAD7D }, +{ 0xAD7E, 0xAD7E, 0xAD7E }, +{ 0xAD7F, 0xAD7F, 0xAD7F }, +{ 0xAD80, 0xAD80, 0xAD80 }, +{ 0xAD81, 0xAD81, 0xAD81 }, +{ 0xAD82, 0xAD82, 0xAD82 }, +{ 0xAD83, 0xAD83, 0xAD83 }, +{ 0xAD84, 0xAD84, 0xAD84 }, +{ 0xAD85, 0xAD85, 0xAD85 }, +{ 0xAD86, 0xAD86, 0xAD86 }, +{ 0xAD87, 0xAD87, 0xAD87 }, +{ 0xAD88, 0xAD88, 0xAD88 }, +{ 0xAD89, 0xAD89, 0xAD89 }, +{ 0xAD8A, 0xAD8A, 0xAD8A }, +{ 0xAD8B, 0xAD8B, 0xAD8B }, +{ 0xAD8C, 0xAD8C, 0xAD8C }, +{ 0xAD8D, 0xAD8D, 0xAD8D }, +{ 0xAD8E, 0xAD8E, 0xAD8E }, +{ 0xAD8F, 0xAD8F, 0xAD8F }, +{ 0xAD90, 0xAD90, 0xAD90 }, +{ 0xAD91, 0xAD91, 0xAD91 }, +{ 0xAD92, 0xAD92, 0xAD92 }, +{ 0xAD93, 0xAD93, 0xAD93 }, +{ 0xAD94, 0xAD94, 0xAD94 }, +{ 0xAD95, 0xAD95, 0xAD95 }, +{ 0xAD96, 0xAD96, 0xAD96 }, +{ 0xAD97, 0xAD97, 0xAD97 }, +{ 0xAD98, 0xAD98, 0xAD98 }, +{ 0xAD99, 0xAD99, 0xAD99 }, +{ 0xAD9A, 0xAD9A, 0xAD9A }, +{ 0xAD9B, 0xAD9B, 0xAD9B }, +{ 0xAD9C, 0xAD9C, 0xAD9C }, +{ 0xAD9D, 0xAD9D, 0xAD9D }, +{ 0xAD9E, 0xAD9E, 0xAD9E }, +{ 0xAD9F, 0xAD9F, 0xAD9F }, +{ 0xADA0, 0xADA0, 0xADA0 }, +{ 0xADA1, 0xADA1, 0xADA1 }, +{ 0xADA2, 0xADA2, 0xADA2 }, +{ 0xADA3, 0xADA3, 0xADA3 }, +{ 0xADA4, 0xADA4, 0xADA4 }, +{ 0xADA5, 0xADA5, 0xADA5 }, +{ 0xADA6, 0xADA6, 0xADA6 }, +{ 0xADA7, 0xADA7, 0xADA7 }, +{ 0xADA8, 0xADA8, 0xADA8 }, +{ 0xADA9, 0xADA9, 0xADA9 }, +{ 0xADAA, 0xADAA, 0xADAA }, +{ 0xADAB, 0xADAB, 0xADAB }, +{ 0xADAC, 0xADAC, 0xADAC }, +{ 0xADAD, 0xADAD, 0xADAD }, +{ 0xADAE, 0xADAE, 0xADAE }, +{ 0xADAF, 0xADAF, 0xADAF }, +{ 0xADB0, 0xADB0, 0xADB0 }, +{ 0xADB1, 0xADB1, 0xADB1 }, +{ 0xADB2, 0xADB2, 0xADB2 }, +{ 0xADB3, 0xADB3, 0xADB3 }, +{ 0xADB4, 0xADB4, 0xADB4 }, +{ 0xADB5, 0xADB5, 0xADB5 }, +{ 0xADB6, 0xADB6, 0xADB6 }, +{ 0xADB7, 0xADB7, 0xADB7 }, +{ 0xADB8, 0xADB8, 0xADB8 }, +{ 0xADB9, 0xADB9, 0xADB9 }, +{ 0xADBA, 0xADBA, 0xADBA }, +{ 0xADBB, 0xADBB, 0xADBB }, +{ 0xADBC, 0xADBC, 0xADBC }, +{ 0xADBD, 0xADBD, 0xADBD }, +{ 0xADBE, 0xADBE, 0xADBE }, +{ 0xADBF, 0xADBF, 0xADBF }, +{ 0xADC0, 0xADC0, 0xADC0 }, +{ 0xADC1, 0xADC1, 0xADC1 }, +{ 0xADC2, 0xADC2, 0xADC2 }, +{ 0xADC3, 0xADC3, 0xADC3 }, +{ 0xADC4, 0xADC4, 0xADC4 }, +{ 0xADC5, 0xADC5, 0xADC5 }, +{ 0xADC6, 0xADC6, 0xADC6 }, +{ 0xADC7, 0xADC7, 0xADC7 }, +{ 0xADC8, 0xADC8, 0xADC8 }, +{ 0xADC9, 0xADC9, 0xADC9 }, +{ 0xADCA, 0xADCA, 0xADCA }, +{ 0xADCB, 0xADCB, 0xADCB }, +{ 0xADCC, 0xADCC, 0xADCC }, +{ 0xADCD, 0xADCD, 0xADCD }, +{ 0xADCE, 0xADCE, 0xADCE }, +{ 0xADCF, 0xADCF, 0xADCF }, +{ 0xADD0, 0xADD0, 0xADD0 }, +{ 0xADD1, 0xADD1, 0xADD1 }, +{ 0xADD2, 0xADD2, 0xADD2 }, +{ 0xADD3, 0xADD3, 0xADD3 }, +{ 0xADD4, 0xADD4, 0xADD4 }, +{ 0xADD5, 0xADD5, 0xADD5 }, +{ 0xADD6, 0xADD6, 0xADD6 }, +{ 0xADD7, 0xADD7, 0xADD7 }, +{ 0xADD8, 0xADD8, 0xADD8 }, +{ 0xADD9, 0xADD9, 0xADD9 }, +{ 0xADDA, 0xADDA, 0xADDA }, +{ 0xADDB, 0xADDB, 0xADDB }, +{ 0xADDC, 0xADDC, 0xADDC }, +{ 0xADDD, 0xADDD, 0xADDD }, +{ 0xADDE, 0xADDE, 0xADDE }, +{ 0xADDF, 0xADDF, 0xADDF }, +{ 0xADE0, 0xADE0, 0xADE0 }, +{ 0xADE1, 0xADE1, 0xADE1 }, +{ 0xADE2, 0xADE2, 0xADE2 }, +{ 0xADE3, 0xADE3, 0xADE3 }, +{ 0xADE4, 0xADE4, 0xADE4 }, +{ 0xADE5, 0xADE5, 0xADE5 }, +{ 0xADE6, 0xADE6, 0xADE6 }, +{ 0xADE7, 0xADE7, 0xADE7 }, +{ 0xADE8, 0xADE8, 0xADE8 }, +{ 0xADE9, 0xADE9, 0xADE9 }, +{ 0xADEA, 0xADEA, 0xADEA }, +{ 0xADEB, 0xADEB, 0xADEB }, +{ 0xADEC, 0xADEC, 0xADEC }, +{ 0xADED, 0xADED, 0xADED }, +{ 0xADEE, 0xADEE, 0xADEE }, +{ 0xADEF, 0xADEF, 0xADEF }, +{ 0xADF0, 0xADF0, 0xADF0 }, +{ 0xADF1, 0xADF1, 0xADF1 }, +{ 0xADF2, 0xADF2, 0xADF2 }, +{ 0xADF3, 0xADF3, 0xADF3 }, +{ 0xADF4, 0xADF4, 0xADF4 }, +{ 0xADF5, 0xADF5, 0xADF5 }, +{ 0xADF6, 0xADF6, 0xADF6 }, +{ 0xADF7, 0xADF7, 0xADF7 }, +{ 0xADF8, 0xADF8, 0xADF8 }, +{ 0xADF9, 0xADF9, 0xADF9 }, +{ 0xADFA, 0xADFA, 0xADFA }, +{ 0xADFB, 0xADFB, 0xADFB }, +{ 0xADFC, 0xADFC, 0xADFC }, +{ 0xADFD, 0xADFD, 0xADFD }, +{ 0xADFE, 0xADFE, 0xADFE }, +{ 0xADFF, 0xADFF, 0xADFF }, +{ 0xAE00, 0xAE00, 0xAE00 }, +{ 0xAE01, 0xAE01, 0xAE01 }, +{ 0xAE02, 0xAE02, 0xAE02 }, +{ 0xAE03, 0xAE03, 0xAE03 }, +{ 0xAE04, 0xAE04, 0xAE04 }, +{ 0xAE05, 0xAE05, 0xAE05 }, +{ 0xAE06, 0xAE06, 0xAE06 }, +{ 0xAE07, 0xAE07, 0xAE07 }, +{ 0xAE08, 0xAE08, 0xAE08 }, +{ 0xAE09, 0xAE09, 0xAE09 }, +{ 0xAE0A, 0xAE0A, 0xAE0A }, +{ 0xAE0B, 0xAE0B, 0xAE0B }, +{ 0xAE0C, 0xAE0C, 0xAE0C }, +{ 0xAE0D, 0xAE0D, 0xAE0D }, +{ 0xAE0E, 0xAE0E, 0xAE0E }, +{ 0xAE0F, 0xAE0F, 0xAE0F }, +{ 0xAE10, 0xAE10, 0xAE10 }, +{ 0xAE11, 0xAE11, 0xAE11 }, +{ 0xAE12, 0xAE12, 0xAE12 }, +{ 0xAE13, 0xAE13, 0xAE13 }, +{ 0xAE14, 0xAE14, 0xAE14 }, +{ 0xAE15, 0xAE15, 0xAE15 }, +{ 0xAE16, 0xAE16, 0xAE16 }, +{ 0xAE17, 0xAE17, 0xAE17 }, +{ 0xAE18, 0xAE18, 0xAE18 }, +{ 0xAE19, 0xAE19, 0xAE19 }, +{ 0xAE1A, 0xAE1A, 0xAE1A }, +{ 0xAE1B, 0xAE1B, 0xAE1B }, +{ 0xAE1C, 0xAE1C, 0xAE1C }, +{ 0xAE1D, 0xAE1D, 0xAE1D }, +{ 0xAE1E, 0xAE1E, 0xAE1E }, +{ 0xAE1F, 0xAE1F, 0xAE1F }, +{ 0xAE20, 0xAE20, 0xAE20 }, +{ 0xAE21, 0xAE21, 0xAE21 }, +{ 0xAE22, 0xAE22, 0xAE22 }, +{ 0xAE23, 0xAE23, 0xAE23 }, +{ 0xAE24, 0xAE24, 0xAE24 }, +{ 0xAE25, 0xAE25, 0xAE25 }, +{ 0xAE26, 0xAE26, 0xAE26 }, +{ 0xAE27, 0xAE27, 0xAE27 }, +{ 0xAE28, 0xAE28, 0xAE28 }, +{ 0xAE29, 0xAE29, 0xAE29 }, +{ 0xAE2A, 0xAE2A, 0xAE2A }, +{ 0xAE2B, 0xAE2B, 0xAE2B }, +{ 0xAE2C, 0xAE2C, 0xAE2C }, +{ 0xAE2D, 0xAE2D, 0xAE2D }, +{ 0xAE2E, 0xAE2E, 0xAE2E }, +{ 0xAE2F, 0xAE2F, 0xAE2F }, +{ 0xAE30, 0xAE30, 0xAE30 }, +{ 0xAE31, 0xAE31, 0xAE31 }, +{ 0xAE32, 0xAE32, 0xAE32 }, +{ 0xAE33, 0xAE33, 0xAE33 }, +{ 0xAE34, 0xAE34, 0xAE34 }, +{ 0xAE35, 0xAE35, 0xAE35 }, +{ 0xAE36, 0xAE36, 0xAE36 }, +{ 0xAE37, 0xAE37, 0xAE37 }, +{ 0xAE38, 0xAE38, 0xAE38 }, +{ 0xAE39, 0xAE39, 0xAE39 }, +{ 0xAE3A, 0xAE3A, 0xAE3A }, +{ 0xAE3B, 0xAE3B, 0xAE3B }, +{ 0xAE3C, 0xAE3C, 0xAE3C }, +{ 0xAE3D, 0xAE3D, 0xAE3D }, +{ 0xAE3E, 0xAE3E, 0xAE3E }, +{ 0xAE3F, 0xAE3F, 0xAE3F }, +{ 0xAE40, 0xAE40, 0xAE40 }, +{ 0xAE41, 0xAE41, 0xAE41 }, +{ 0xAE42, 0xAE42, 0xAE42 }, +{ 0xAE43, 0xAE43, 0xAE43 }, +{ 0xAE44, 0xAE44, 0xAE44 }, +{ 0xAE45, 0xAE45, 0xAE45 }, +{ 0xAE46, 0xAE46, 0xAE46 }, +{ 0xAE47, 0xAE47, 0xAE47 }, +{ 0xAE48, 0xAE48, 0xAE48 }, +{ 0xAE49, 0xAE49, 0xAE49 }, +{ 0xAE4A, 0xAE4A, 0xAE4A }, +{ 0xAE4B, 0xAE4B, 0xAE4B }, +{ 0xAE4C, 0xAE4C, 0xAE4C }, +{ 0xAE4D, 0xAE4D, 0xAE4D }, +{ 0xAE4E, 0xAE4E, 0xAE4E }, +{ 0xAE4F, 0xAE4F, 0xAE4F }, +{ 0xAE50, 0xAE50, 0xAE50 }, +{ 0xAE51, 0xAE51, 0xAE51 }, +{ 0xAE52, 0xAE52, 0xAE52 }, +{ 0xAE53, 0xAE53, 0xAE53 }, +{ 0xAE54, 0xAE54, 0xAE54 }, +{ 0xAE55, 0xAE55, 0xAE55 }, +{ 0xAE56, 0xAE56, 0xAE56 }, +{ 0xAE57, 0xAE57, 0xAE57 }, +{ 0xAE58, 0xAE58, 0xAE58 }, +{ 0xAE59, 0xAE59, 0xAE59 }, +{ 0xAE5A, 0xAE5A, 0xAE5A }, +{ 0xAE5B, 0xAE5B, 0xAE5B }, +{ 0xAE5C, 0xAE5C, 0xAE5C }, +{ 0xAE5D, 0xAE5D, 0xAE5D }, +{ 0xAE5E, 0xAE5E, 0xAE5E }, +{ 0xAE5F, 0xAE5F, 0xAE5F }, +{ 0xAE60, 0xAE60, 0xAE60 }, +{ 0xAE61, 0xAE61, 0xAE61 }, +{ 0xAE62, 0xAE62, 0xAE62 }, +{ 0xAE63, 0xAE63, 0xAE63 }, +{ 0xAE64, 0xAE64, 0xAE64 }, +{ 0xAE65, 0xAE65, 0xAE65 }, +{ 0xAE66, 0xAE66, 0xAE66 }, +{ 0xAE67, 0xAE67, 0xAE67 }, +{ 0xAE68, 0xAE68, 0xAE68 }, +{ 0xAE69, 0xAE69, 0xAE69 }, +{ 0xAE6A, 0xAE6A, 0xAE6A }, +{ 0xAE6B, 0xAE6B, 0xAE6B }, +{ 0xAE6C, 0xAE6C, 0xAE6C }, +{ 0xAE6D, 0xAE6D, 0xAE6D }, +{ 0xAE6E, 0xAE6E, 0xAE6E }, +{ 0xAE6F, 0xAE6F, 0xAE6F }, +{ 0xAE70, 0xAE70, 0xAE70 }, +{ 0xAE71, 0xAE71, 0xAE71 }, +{ 0xAE72, 0xAE72, 0xAE72 }, +{ 0xAE73, 0xAE73, 0xAE73 }, +{ 0xAE74, 0xAE74, 0xAE74 }, +{ 0xAE75, 0xAE75, 0xAE75 }, +{ 0xAE76, 0xAE76, 0xAE76 }, +{ 0xAE77, 0xAE77, 0xAE77 }, +{ 0xAE78, 0xAE78, 0xAE78 }, +{ 0xAE79, 0xAE79, 0xAE79 }, +{ 0xAE7A, 0xAE7A, 0xAE7A }, +{ 0xAE7B, 0xAE7B, 0xAE7B }, +{ 0xAE7C, 0xAE7C, 0xAE7C }, +{ 0xAE7D, 0xAE7D, 0xAE7D }, +{ 0xAE7E, 0xAE7E, 0xAE7E }, +{ 0xAE7F, 0xAE7F, 0xAE7F }, +{ 0xAE80, 0xAE80, 0xAE80 }, +{ 0xAE81, 0xAE81, 0xAE81 }, +{ 0xAE82, 0xAE82, 0xAE82 }, +{ 0xAE83, 0xAE83, 0xAE83 }, +{ 0xAE84, 0xAE84, 0xAE84 }, +{ 0xAE85, 0xAE85, 0xAE85 }, +{ 0xAE86, 0xAE86, 0xAE86 }, +{ 0xAE87, 0xAE87, 0xAE87 }, +{ 0xAE88, 0xAE88, 0xAE88 }, +{ 0xAE89, 0xAE89, 0xAE89 }, +{ 0xAE8A, 0xAE8A, 0xAE8A }, +{ 0xAE8B, 0xAE8B, 0xAE8B }, +{ 0xAE8C, 0xAE8C, 0xAE8C }, +{ 0xAE8D, 0xAE8D, 0xAE8D }, +{ 0xAE8E, 0xAE8E, 0xAE8E }, +{ 0xAE8F, 0xAE8F, 0xAE8F }, +{ 0xAE90, 0xAE90, 0xAE90 }, +{ 0xAE91, 0xAE91, 0xAE91 }, +{ 0xAE92, 0xAE92, 0xAE92 }, +{ 0xAE93, 0xAE93, 0xAE93 }, +{ 0xAE94, 0xAE94, 0xAE94 }, +{ 0xAE95, 0xAE95, 0xAE95 }, +{ 0xAE96, 0xAE96, 0xAE96 }, +{ 0xAE97, 0xAE97, 0xAE97 }, +{ 0xAE98, 0xAE98, 0xAE98 }, +{ 0xAE99, 0xAE99, 0xAE99 }, +{ 0xAE9A, 0xAE9A, 0xAE9A }, +{ 0xAE9B, 0xAE9B, 0xAE9B }, +{ 0xAE9C, 0xAE9C, 0xAE9C }, +{ 0xAE9D, 0xAE9D, 0xAE9D }, +{ 0xAE9E, 0xAE9E, 0xAE9E }, +{ 0xAE9F, 0xAE9F, 0xAE9F }, +{ 0xAEA0, 0xAEA0, 0xAEA0 }, +{ 0xAEA1, 0xAEA1, 0xAEA1 }, +{ 0xAEA2, 0xAEA2, 0xAEA2 }, +{ 0xAEA3, 0xAEA3, 0xAEA3 }, +{ 0xAEA4, 0xAEA4, 0xAEA4 }, +{ 0xAEA5, 0xAEA5, 0xAEA5 }, +{ 0xAEA6, 0xAEA6, 0xAEA6 }, +{ 0xAEA7, 0xAEA7, 0xAEA7 }, +{ 0xAEA8, 0xAEA8, 0xAEA8 }, +{ 0xAEA9, 0xAEA9, 0xAEA9 }, +{ 0xAEAA, 0xAEAA, 0xAEAA }, +{ 0xAEAB, 0xAEAB, 0xAEAB }, +{ 0xAEAC, 0xAEAC, 0xAEAC }, +{ 0xAEAD, 0xAEAD, 0xAEAD }, +{ 0xAEAE, 0xAEAE, 0xAEAE }, +{ 0xAEAF, 0xAEAF, 0xAEAF }, +{ 0xAEB0, 0xAEB0, 0xAEB0 }, +{ 0xAEB1, 0xAEB1, 0xAEB1 }, +{ 0xAEB2, 0xAEB2, 0xAEB2 }, +{ 0xAEB3, 0xAEB3, 0xAEB3 }, +{ 0xAEB4, 0xAEB4, 0xAEB4 }, +{ 0xAEB5, 0xAEB5, 0xAEB5 }, +{ 0xAEB6, 0xAEB6, 0xAEB6 }, +{ 0xAEB7, 0xAEB7, 0xAEB7 }, +{ 0xAEB8, 0xAEB8, 0xAEB8 }, +{ 0xAEB9, 0xAEB9, 0xAEB9 }, +{ 0xAEBA, 0xAEBA, 0xAEBA }, +{ 0xAEBB, 0xAEBB, 0xAEBB }, +{ 0xAEBC, 0xAEBC, 0xAEBC }, +{ 0xAEBD, 0xAEBD, 0xAEBD }, +{ 0xAEBE, 0xAEBE, 0xAEBE }, +{ 0xAEBF, 0xAEBF, 0xAEBF }, +{ 0xAEC0, 0xAEC0, 0xAEC0 }, +{ 0xAEC1, 0xAEC1, 0xAEC1 }, +{ 0xAEC2, 0xAEC2, 0xAEC2 }, +{ 0xAEC3, 0xAEC3, 0xAEC3 }, +{ 0xAEC4, 0xAEC4, 0xAEC4 }, +{ 0xAEC5, 0xAEC5, 0xAEC5 }, +{ 0xAEC6, 0xAEC6, 0xAEC6 }, +{ 0xAEC7, 0xAEC7, 0xAEC7 }, +{ 0xAEC8, 0xAEC8, 0xAEC8 }, +{ 0xAEC9, 0xAEC9, 0xAEC9 }, +{ 0xAECA, 0xAECA, 0xAECA }, +{ 0xAECB, 0xAECB, 0xAECB }, +{ 0xAECC, 0xAECC, 0xAECC }, +{ 0xAECD, 0xAECD, 0xAECD }, +{ 0xAECE, 0xAECE, 0xAECE }, +{ 0xAECF, 0xAECF, 0xAECF }, +{ 0xAED0, 0xAED0, 0xAED0 }, +{ 0xAED1, 0xAED1, 0xAED1 }, +{ 0xAED2, 0xAED2, 0xAED2 }, +{ 0xAED3, 0xAED3, 0xAED3 }, +{ 0xAED4, 0xAED4, 0xAED4 }, +{ 0xAED5, 0xAED5, 0xAED5 }, +{ 0xAED6, 0xAED6, 0xAED6 }, +{ 0xAED7, 0xAED7, 0xAED7 }, +{ 0xAED8, 0xAED8, 0xAED8 }, +{ 0xAED9, 0xAED9, 0xAED9 }, +{ 0xAEDA, 0xAEDA, 0xAEDA }, +{ 0xAEDB, 0xAEDB, 0xAEDB }, +{ 0xAEDC, 0xAEDC, 0xAEDC }, +{ 0xAEDD, 0xAEDD, 0xAEDD }, +{ 0xAEDE, 0xAEDE, 0xAEDE }, +{ 0xAEDF, 0xAEDF, 0xAEDF }, +{ 0xAEE0, 0xAEE0, 0xAEE0 }, +{ 0xAEE1, 0xAEE1, 0xAEE1 }, +{ 0xAEE2, 0xAEE2, 0xAEE2 }, +{ 0xAEE3, 0xAEE3, 0xAEE3 }, +{ 0xAEE4, 0xAEE4, 0xAEE4 }, +{ 0xAEE5, 0xAEE5, 0xAEE5 }, +{ 0xAEE6, 0xAEE6, 0xAEE6 }, +{ 0xAEE7, 0xAEE7, 0xAEE7 }, +{ 0xAEE8, 0xAEE8, 0xAEE8 }, +{ 0xAEE9, 0xAEE9, 0xAEE9 }, +{ 0xAEEA, 0xAEEA, 0xAEEA }, +{ 0xAEEB, 0xAEEB, 0xAEEB }, +{ 0xAEEC, 0xAEEC, 0xAEEC }, +{ 0xAEED, 0xAEED, 0xAEED }, +{ 0xAEEE, 0xAEEE, 0xAEEE }, +{ 0xAEEF, 0xAEEF, 0xAEEF }, +{ 0xAEF0, 0xAEF0, 0xAEF0 }, +{ 0xAEF1, 0xAEF1, 0xAEF1 }, +{ 0xAEF2, 0xAEF2, 0xAEF2 }, +{ 0xAEF3, 0xAEF3, 0xAEF3 }, +{ 0xAEF4, 0xAEF4, 0xAEF4 }, +{ 0xAEF5, 0xAEF5, 0xAEF5 }, +{ 0xAEF6, 0xAEF6, 0xAEF6 }, +{ 0xAEF7, 0xAEF7, 0xAEF7 }, +{ 0xAEF8, 0xAEF8, 0xAEF8 }, +{ 0xAEF9, 0xAEF9, 0xAEF9 }, +{ 0xAEFA, 0xAEFA, 0xAEFA }, +{ 0xAEFB, 0xAEFB, 0xAEFB }, +{ 0xAEFC, 0xAEFC, 0xAEFC }, +{ 0xAEFD, 0xAEFD, 0xAEFD }, +{ 0xAEFE, 0xAEFE, 0xAEFE }, +{ 0xAEFF, 0xAEFF, 0xAEFF }, +{ 0xAF00, 0xAF00, 0xAF00 }, +{ 0xAF01, 0xAF01, 0xAF01 }, +{ 0xAF02, 0xAF02, 0xAF02 }, +{ 0xAF03, 0xAF03, 0xAF03 }, +{ 0xAF04, 0xAF04, 0xAF04 }, +{ 0xAF05, 0xAF05, 0xAF05 }, +{ 0xAF06, 0xAF06, 0xAF06 }, +{ 0xAF07, 0xAF07, 0xAF07 }, +{ 0xAF08, 0xAF08, 0xAF08 }, +{ 0xAF09, 0xAF09, 0xAF09 }, +{ 0xAF0A, 0xAF0A, 0xAF0A }, +{ 0xAF0B, 0xAF0B, 0xAF0B }, +{ 0xAF0C, 0xAF0C, 0xAF0C }, +{ 0xAF0D, 0xAF0D, 0xAF0D }, +{ 0xAF0E, 0xAF0E, 0xAF0E }, +{ 0xAF0F, 0xAF0F, 0xAF0F }, +{ 0xAF10, 0xAF10, 0xAF10 }, +{ 0xAF11, 0xAF11, 0xAF11 }, +{ 0xAF12, 0xAF12, 0xAF12 }, +{ 0xAF13, 0xAF13, 0xAF13 }, +{ 0xAF14, 0xAF14, 0xAF14 }, +{ 0xAF15, 0xAF15, 0xAF15 }, +{ 0xAF16, 0xAF16, 0xAF16 }, +{ 0xAF17, 0xAF17, 0xAF17 }, +{ 0xAF18, 0xAF18, 0xAF18 }, +{ 0xAF19, 0xAF19, 0xAF19 }, +{ 0xAF1A, 0xAF1A, 0xAF1A }, +{ 0xAF1B, 0xAF1B, 0xAF1B }, +{ 0xAF1C, 0xAF1C, 0xAF1C }, +{ 0xAF1D, 0xAF1D, 0xAF1D }, +{ 0xAF1E, 0xAF1E, 0xAF1E }, +{ 0xAF1F, 0xAF1F, 0xAF1F }, +{ 0xAF20, 0xAF20, 0xAF20 }, +{ 0xAF21, 0xAF21, 0xAF21 }, +{ 0xAF22, 0xAF22, 0xAF22 }, +{ 0xAF23, 0xAF23, 0xAF23 }, +{ 0xAF24, 0xAF24, 0xAF24 }, +{ 0xAF25, 0xAF25, 0xAF25 }, +{ 0xAF26, 0xAF26, 0xAF26 }, +{ 0xAF27, 0xAF27, 0xAF27 }, +{ 0xAF28, 0xAF28, 0xAF28 }, +{ 0xAF29, 0xAF29, 0xAF29 }, +{ 0xAF2A, 0xAF2A, 0xAF2A }, +{ 0xAF2B, 0xAF2B, 0xAF2B }, +{ 0xAF2C, 0xAF2C, 0xAF2C }, +{ 0xAF2D, 0xAF2D, 0xAF2D }, +{ 0xAF2E, 0xAF2E, 0xAF2E }, +{ 0xAF2F, 0xAF2F, 0xAF2F }, +{ 0xAF30, 0xAF30, 0xAF30 }, +{ 0xAF31, 0xAF31, 0xAF31 }, +{ 0xAF32, 0xAF32, 0xAF32 }, +{ 0xAF33, 0xAF33, 0xAF33 }, +{ 0xAF34, 0xAF34, 0xAF34 }, +{ 0xAF35, 0xAF35, 0xAF35 }, +{ 0xAF36, 0xAF36, 0xAF36 }, +{ 0xAF37, 0xAF37, 0xAF37 }, +{ 0xAF38, 0xAF38, 0xAF38 }, +{ 0xAF39, 0xAF39, 0xAF39 }, +{ 0xAF3A, 0xAF3A, 0xAF3A }, +{ 0xAF3B, 0xAF3B, 0xAF3B }, +{ 0xAF3C, 0xAF3C, 0xAF3C }, +{ 0xAF3D, 0xAF3D, 0xAF3D }, +{ 0xAF3E, 0xAF3E, 0xAF3E }, +{ 0xAF3F, 0xAF3F, 0xAF3F }, +{ 0xAF40, 0xAF40, 0xAF40 }, +{ 0xAF41, 0xAF41, 0xAF41 }, +{ 0xAF42, 0xAF42, 0xAF42 }, +{ 0xAF43, 0xAF43, 0xAF43 }, +{ 0xAF44, 0xAF44, 0xAF44 }, +{ 0xAF45, 0xAF45, 0xAF45 }, +{ 0xAF46, 0xAF46, 0xAF46 }, +{ 0xAF47, 0xAF47, 0xAF47 }, +{ 0xAF48, 0xAF48, 0xAF48 }, +{ 0xAF49, 0xAF49, 0xAF49 }, +{ 0xAF4A, 0xAF4A, 0xAF4A }, +{ 0xAF4B, 0xAF4B, 0xAF4B }, +{ 0xAF4C, 0xAF4C, 0xAF4C }, +{ 0xAF4D, 0xAF4D, 0xAF4D }, +{ 0xAF4E, 0xAF4E, 0xAF4E }, +{ 0xAF4F, 0xAF4F, 0xAF4F }, +{ 0xAF50, 0xAF50, 0xAF50 }, +{ 0xAF51, 0xAF51, 0xAF51 }, +{ 0xAF52, 0xAF52, 0xAF52 }, +{ 0xAF53, 0xAF53, 0xAF53 }, +{ 0xAF54, 0xAF54, 0xAF54 }, +{ 0xAF55, 0xAF55, 0xAF55 }, +{ 0xAF56, 0xAF56, 0xAF56 }, +{ 0xAF57, 0xAF57, 0xAF57 }, +{ 0xAF58, 0xAF58, 0xAF58 }, +{ 0xAF59, 0xAF59, 0xAF59 }, +{ 0xAF5A, 0xAF5A, 0xAF5A }, +{ 0xAF5B, 0xAF5B, 0xAF5B }, +{ 0xAF5C, 0xAF5C, 0xAF5C }, +{ 0xAF5D, 0xAF5D, 0xAF5D }, +{ 0xAF5E, 0xAF5E, 0xAF5E }, +{ 0xAF5F, 0xAF5F, 0xAF5F }, +{ 0xAF60, 0xAF60, 0xAF60 }, +{ 0xAF61, 0xAF61, 0xAF61 }, +{ 0xAF62, 0xAF62, 0xAF62 }, +{ 0xAF63, 0xAF63, 0xAF63 }, +{ 0xAF64, 0xAF64, 0xAF64 }, +{ 0xAF65, 0xAF65, 0xAF65 }, +{ 0xAF66, 0xAF66, 0xAF66 }, +{ 0xAF67, 0xAF67, 0xAF67 }, +{ 0xAF68, 0xAF68, 0xAF68 }, +{ 0xAF69, 0xAF69, 0xAF69 }, +{ 0xAF6A, 0xAF6A, 0xAF6A }, +{ 0xAF6B, 0xAF6B, 0xAF6B }, +{ 0xAF6C, 0xAF6C, 0xAF6C }, +{ 0xAF6D, 0xAF6D, 0xAF6D }, +{ 0xAF6E, 0xAF6E, 0xAF6E }, +{ 0xAF6F, 0xAF6F, 0xAF6F }, +{ 0xAF70, 0xAF70, 0xAF70 }, +{ 0xAF71, 0xAF71, 0xAF71 }, +{ 0xAF72, 0xAF72, 0xAF72 }, +{ 0xAF73, 0xAF73, 0xAF73 }, +{ 0xAF74, 0xAF74, 0xAF74 }, +{ 0xAF75, 0xAF75, 0xAF75 }, +{ 0xAF76, 0xAF76, 0xAF76 }, +{ 0xAF77, 0xAF77, 0xAF77 }, +{ 0xAF78, 0xAF78, 0xAF78 }, +{ 0xAF79, 0xAF79, 0xAF79 }, +{ 0xAF7A, 0xAF7A, 0xAF7A }, +{ 0xAF7B, 0xAF7B, 0xAF7B }, +{ 0xAF7C, 0xAF7C, 0xAF7C }, +{ 0xAF7D, 0xAF7D, 0xAF7D }, +{ 0xAF7E, 0xAF7E, 0xAF7E }, +{ 0xAF7F, 0xAF7F, 0xAF7F }, +{ 0xAF80, 0xAF80, 0xAF80 }, +{ 0xAF81, 0xAF81, 0xAF81 }, +{ 0xAF82, 0xAF82, 0xAF82 }, +{ 0xAF83, 0xAF83, 0xAF83 }, +{ 0xAF84, 0xAF84, 0xAF84 }, +{ 0xAF85, 0xAF85, 0xAF85 }, +{ 0xAF86, 0xAF86, 0xAF86 }, +{ 0xAF87, 0xAF87, 0xAF87 }, +{ 0xAF88, 0xAF88, 0xAF88 }, +{ 0xAF89, 0xAF89, 0xAF89 }, +{ 0xAF8A, 0xAF8A, 0xAF8A }, +{ 0xAF8B, 0xAF8B, 0xAF8B }, +{ 0xAF8C, 0xAF8C, 0xAF8C }, +{ 0xAF8D, 0xAF8D, 0xAF8D }, +{ 0xAF8E, 0xAF8E, 0xAF8E }, +{ 0xAF8F, 0xAF8F, 0xAF8F }, +{ 0xAF90, 0xAF90, 0xAF90 }, +{ 0xAF91, 0xAF91, 0xAF91 }, +{ 0xAF92, 0xAF92, 0xAF92 }, +{ 0xAF93, 0xAF93, 0xAF93 }, +{ 0xAF94, 0xAF94, 0xAF94 }, +{ 0xAF95, 0xAF95, 0xAF95 }, +{ 0xAF96, 0xAF96, 0xAF96 }, +{ 0xAF97, 0xAF97, 0xAF97 }, +{ 0xAF98, 0xAF98, 0xAF98 }, +{ 0xAF99, 0xAF99, 0xAF99 }, +{ 0xAF9A, 0xAF9A, 0xAF9A }, +{ 0xAF9B, 0xAF9B, 0xAF9B }, +{ 0xAF9C, 0xAF9C, 0xAF9C }, +{ 0xAF9D, 0xAF9D, 0xAF9D }, +{ 0xAF9E, 0xAF9E, 0xAF9E }, +{ 0xAF9F, 0xAF9F, 0xAF9F }, +{ 0xAFA0, 0xAFA0, 0xAFA0 }, +{ 0xAFA1, 0xAFA1, 0xAFA1 }, +{ 0xAFA2, 0xAFA2, 0xAFA2 }, +{ 0xAFA3, 0xAFA3, 0xAFA3 }, +{ 0xAFA4, 0xAFA4, 0xAFA4 }, +{ 0xAFA5, 0xAFA5, 0xAFA5 }, +{ 0xAFA6, 0xAFA6, 0xAFA6 }, +{ 0xAFA7, 0xAFA7, 0xAFA7 }, +{ 0xAFA8, 0xAFA8, 0xAFA8 }, +{ 0xAFA9, 0xAFA9, 0xAFA9 }, +{ 0xAFAA, 0xAFAA, 0xAFAA }, +{ 0xAFAB, 0xAFAB, 0xAFAB }, +{ 0xAFAC, 0xAFAC, 0xAFAC }, +{ 0xAFAD, 0xAFAD, 0xAFAD }, +{ 0xAFAE, 0xAFAE, 0xAFAE }, +{ 0xAFAF, 0xAFAF, 0xAFAF }, +{ 0xAFB0, 0xAFB0, 0xAFB0 }, +{ 0xAFB1, 0xAFB1, 0xAFB1 }, +{ 0xAFB2, 0xAFB2, 0xAFB2 }, +{ 0xAFB3, 0xAFB3, 0xAFB3 }, +{ 0xAFB4, 0xAFB4, 0xAFB4 }, +{ 0xAFB5, 0xAFB5, 0xAFB5 }, +{ 0xAFB6, 0xAFB6, 0xAFB6 }, +{ 0xAFB7, 0xAFB7, 0xAFB7 }, +{ 0xAFB8, 0xAFB8, 0xAFB8 }, +{ 0xAFB9, 0xAFB9, 0xAFB9 }, +{ 0xAFBA, 0xAFBA, 0xAFBA }, +{ 0xAFBB, 0xAFBB, 0xAFBB }, +{ 0xAFBC, 0xAFBC, 0xAFBC }, +{ 0xAFBD, 0xAFBD, 0xAFBD }, +{ 0xAFBE, 0xAFBE, 0xAFBE }, +{ 0xAFBF, 0xAFBF, 0xAFBF }, +{ 0xAFC0, 0xAFC0, 0xAFC0 }, +{ 0xAFC1, 0xAFC1, 0xAFC1 }, +{ 0xAFC2, 0xAFC2, 0xAFC2 }, +{ 0xAFC3, 0xAFC3, 0xAFC3 }, +{ 0xAFC4, 0xAFC4, 0xAFC4 }, +{ 0xAFC5, 0xAFC5, 0xAFC5 }, +{ 0xAFC6, 0xAFC6, 0xAFC6 }, +{ 0xAFC7, 0xAFC7, 0xAFC7 }, +{ 0xAFC8, 0xAFC8, 0xAFC8 }, +{ 0xAFC9, 0xAFC9, 0xAFC9 }, +{ 0xAFCA, 0xAFCA, 0xAFCA }, +{ 0xAFCB, 0xAFCB, 0xAFCB }, +{ 0xAFCC, 0xAFCC, 0xAFCC }, +{ 0xAFCD, 0xAFCD, 0xAFCD }, +{ 0xAFCE, 0xAFCE, 0xAFCE }, +{ 0xAFCF, 0xAFCF, 0xAFCF }, +{ 0xAFD0, 0xAFD0, 0xAFD0 }, +{ 0xAFD1, 0xAFD1, 0xAFD1 }, +{ 0xAFD2, 0xAFD2, 0xAFD2 }, +{ 0xAFD3, 0xAFD3, 0xAFD3 }, +{ 0xAFD4, 0xAFD4, 0xAFD4 }, +{ 0xAFD5, 0xAFD5, 0xAFD5 }, +{ 0xAFD6, 0xAFD6, 0xAFD6 }, +{ 0xAFD7, 0xAFD7, 0xAFD7 }, +{ 0xAFD8, 0xAFD8, 0xAFD8 }, +{ 0xAFD9, 0xAFD9, 0xAFD9 }, +{ 0xAFDA, 0xAFDA, 0xAFDA }, +{ 0xAFDB, 0xAFDB, 0xAFDB }, +{ 0xAFDC, 0xAFDC, 0xAFDC }, +{ 0xAFDD, 0xAFDD, 0xAFDD }, +{ 0xAFDE, 0xAFDE, 0xAFDE }, +{ 0xAFDF, 0xAFDF, 0xAFDF }, +{ 0xAFE0, 0xAFE0, 0xAFE0 }, +{ 0xAFE1, 0xAFE1, 0xAFE1 }, +{ 0xAFE2, 0xAFE2, 0xAFE2 }, +{ 0xAFE3, 0xAFE3, 0xAFE3 }, +{ 0xAFE4, 0xAFE4, 0xAFE4 }, +{ 0xAFE5, 0xAFE5, 0xAFE5 }, +{ 0xAFE6, 0xAFE6, 0xAFE6 }, +{ 0xAFE7, 0xAFE7, 0xAFE7 }, +{ 0xAFE8, 0xAFE8, 0xAFE8 }, +{ 0xAFE9, 0xAFE9, 0xAFE9 }, +{ 0xAFEA, 0xAFEA, 0xAFEA }, +{ 0xAFEB, 0xAFEB, 0xAFEB }, +{ 0xAFEC, 0xAFEC, 0xAFEC }, +{ 0xAFED, 0xAFED, 0xAFED }, +{ 0xAFEE, 0xAFEE, 0xAFEE }, +{ 0xAFEF, 0xAFEF, 0xAFEF }, +{ 0xAFF0, 0xAFF0, 0xAFF0 }, +{ 0xAFF1, 0xAFF1, 0xAFF1 }, +{ 0xAFF2, 0xAFF2, 0xAFF2 }, +{ 0xAFF3, 0xAFF3, 0xAFF3 }, +{ 0xAFF4, 0xAFF4, 0xAFF4 }, +{ 0xAFF5, 0xAFF5, 0xAFF5 }, +{ 0xAFF6, 0xAFF6, 0xAFF6 }, +{ 0xAFF7, 0xAFF7, 0xAFF7 }, +{ 0xAFF8, 0xAFF8, 0xAFF8 }, +{ 0xAFF9, 0xAFF9, 0xAFF9 }, +{ 0xAFFA, 0xAFFA, 0xAFFA }, +{ 0xAFFB, 0xAFFB, 0xAFFB }, +{ 0xAFFC, 0xAFFC, 0xAFFC }, +{ 0xAFFD, 0xAFFD, 0xAFFD }, +{ 0xAFFE, 0xAFFE, 0xAFFE }, +{ 0xAFFF, 0xAFFF, 0xAFFF }, +{ 0xB000, 0xB000, 0xB000 }, +{ 0xB001, 0xB001, 0xB001 }, +{ 0xB002, 0xB002, 0xB002 }, +{ 0xB003, 0xB003, 0xB003 }, +{ 0xB004, 0xB004, 0xB004 }, +{ 0xB005, 0xB005, 0xB005 }, +{ 0xB006, 0xB006, 0xB006 }, +{ 0xB007, 0xB007, 0xB007 }, +{ 0xB008, 0xB008, 0xB008 }, +{ 0xB009, 0xB009, 0xB009 }, +{ 0xB00A, 0xB00A, 0xB00A }, +{ 0xB00B, 0xB00B, 0xB00B }, +{ 0xB00C, 0xB00C, 0xB00C }, +{ 0xB00D, 0xB00D, 0xB00D }, +{ 0xB00E, 0xB00E, 0xB00E }, +{ 0xB00F, 0xB00F, 0xB00F }, +{ 0xB010, 0xB010, 0xB010 }, +{ 0xB011, 0xB011, 0xB011 }, +{ 0xB012, 0xB012, 0xB012 }, +{ 0xB013, 0xB013, 0xB013 }, +{ 0xB014, 0xB014, 0xB014 }, +{ 0xB015, 0xB015, 0xB015 }, +{ 0xB016, 0xB016, 0xB016 }, +{ 0xB017, 0xB017, 0xB017 }, +{ 0xB018, 0xB018, 0xB018 }, +{ 0xB019, 0xB019, 0xB019 }, +{ 0xB01A, 0xB01A, 0xB01A }, +{ 0xB01B, 0xB01B, 0xB01B }, +{ 0xB01C, 0xB01C, 0xB01C }, +{ 0xB01D, 0xB01D, 0xB01D }, +{ 0xB01E, 0xB01E, 0xB01E }, +{ 0xB01F, 0xB01F, 0xB01F }, +{ 0xB020, 0xB020, 0xB020 }, +{ 0xB021, 0xB021, 0xB021 }, +{ 0xB022, 0xB022, 0xB022 }, +{ 0xB023, 0xB023, 0xB023 }, +{ 0xB024, 0xB024, 0xB024 }, +{ 0xB025, 0xB025, 0xB025 }, +{ 0xB026, 0xB026, 0xB026 }, +{ 0xB027, 0xB027, 0xB027 }, +{ 0xB028, 0xB028, 0xB028 }, +{ 0xB029, 0xB029, 0xB029 }, +{ 0xB02A, 0xB02A, 0xB02A }, +{ 0xB02B, 0xB02B, 0xB02B }, +{ 0xB02C, 0xB02C, 0xB02C }, +{ 0xB02D, 0xB02D, 0xB02D }, +{ 0xB02E, 0xB02E, 0xB02E }, +{ 0xB02F, 0xB02F, 0xB02F }, +{ 0xB030, 0xB030, 0xB030 }, +{ 0xB031, 0xB031, 0xB031 }, +{ 0xB032, 0xB032, 0xB032 }, +{ 0xB033, 0xB033, 0xB033 }, +{ 0xB034, 0xB034, 0xB034 }, +{ 0xB035, 0xB035, 0xB035 }, +{ 0xB036, 0xB036, 0xB036 }, +{ 0xB037, 0xB037, 0xB037 }, +{ 0xB038, 0xB038, 0xB038 }, +{ 0xB039, 0xB039, 0xB039 }, +{ 0xB03A, 0xB03A, 0xB03A }, +{ 0xB03B, 0xB03B, 0xB03B }, +{ 0xB03C, 0xB03C, 0xB03C }, +{ 0xB03D, 0xB03D, 0xB03D }, +{ 0xB03E, 0xB03E, 0xB03E }, +{ 0xB03F, 0xB03F, 0xB03F }, +{ 0xB040, 0xB040, 0xB040 }, +{ 0xB041, 0xB041, 0xB041 }, +{ 0xB042, 0xB042, 0xB042 }, +{ 0xB043, 0xB043, 0xB043 }, +{ 0xB044, 0xB044, 0xB044 }, +{ 0xB045, 0xB045, 0xB045 }, +{ 0xB046, 0xB046, 0xB046 }, +{ 0xB047, 0xB047, 0xB047 }, +{ 0xB048, 0xB048, 0xB048 }, +{ 0xB049, 0xB049, 0xB049 }, +{ 0xB04A, 0xB04A, 0xB04A }, +{ 0xB04B, 0xB04B, 0xB04B }, +{ 0xB04C, 0xB04C, 0xB04C }, +{ 0xB04D, 0xB04D, 0xB04D }, +{ 0xB04E, 0xB04E, 0xB04E }, +{ 0xB04F, 0xB04F, 0xB04F }, +{ 0xB050, 0xB050, 0xB050 }, +{ 0xB051, 0xB051, 0xB051 }, +{ 0xB052, 0xB052, 0xB052 }, +{ 0xB053, 0xB053, 0xB053 }, +{ 0xB054, 0xB054, 0xB054 }, +{ 0xB055, 0xB055, 0xB055 }, +{ 0xB056, 0xB056, 0xB056 }, +{ 0xB057, 0xB057, 0xB057 }, +{ 0xB058, 0xB058, 0xB058 }, +{ 0xB059, 0xB059, 0xB059 }, +{ 0xB05A, 0xB05A, 0xB05A }, +{ 0xB05B, 0xB05B, 0xB05B }, +{ 0xB05C, 0xB05C, 0xB05C }, +{ 0xB05D, 0xB05D, 0xB05D }, +{ 0xB05E, 0xB05E, 0xB05E }, +{ 0xB05F, 0xB05F, 0xB05F }, +{ 0xB060, 0xB060, 0xB060 }, +{ 0xB061, 0xB061, 0xB061 }, +{ 0xB062, 0xB062, 0xB062 }, +{ 0xB063, 0xB063, 0xB063 }, +{ 0xB064, 0xB064, 0xB064 }, +{ 0xB065, 0xB065, 0xB065 }, +{ 0xB066, 0xB066, 0xB066 }, +{ 0xB067, 0xB067, 0xB067 }, +{ 0xB068, 0xB068, 0xB068 }, +{ 0xB069, 0xB069, 0xB069 }, +{ 0xB06A, 0xB06A, 0xB06A }, +{ 0xB06B, 0xB06B, 0xB06B }, +{ 0xB06C, 0xB06C, 0xB06C }, +{ 0xB06D, 0xB06D, 0xB06D }, +{ 0xB06E, 0xB06E, 0xB06E }, +{ 0xB06F, 0xB06F, 0xB06F }, +{ 0xB070, 0xB070, 0xB070 }, +{ 0xB071, 0xB071, 0xB071 }, +{ 0xB072, 0xB072, 0xB072 }, +{ 0xB073, 0xB073, 0xB073 }, +{ 0xB074, 0xB074, 0xB074 }, +{ 0xB075, 0xB075, 0xB075 }, +{ 0xB076, 0xB076, 0xB076 }, +{ 0xB077, 0xB077, 0xB077 }, +{ 0xB078, 0xB078, 0xB078 }, +{ 0xB079, 0xB079, 0xB079 }, +{ 0xB07A, 0xB07A, 0xB07A }, +{ 0xB07B, 0xB07B, 0xB07B }, +{ 0xB07C, 0xB07C, 0xB07C }, +{ 0xB07D, 0xB07D, 0xB07D }, +{ 0xB07E, 0xB07E, 0xB07E }, +{ 0xB07F, 0xB07F, 0xB07F }, +{ 0xB080, 0xB080, 0xB080 }, +{ 0xB081, 0xB081, 0xB081 }, +{ 0xB082, 0xB082, 0xB082 }, +{ 0xB083, 0xB083, 0xB083 }, +{ 0xB084, 0xB084, 0xB084 }, +{ 0xB085, 0xB085, 0xB085 }, +{ 0xB086, 0xB086, 0xB086 }, +{ 0xB087, 0xB087, 0xB087 }, +{ 0xB088, 0xB088, 0xB088 }, +{ 0xB089, 0xB089, 0xB089 }, +{ 0xB08A, 0xB08A, 0xB08A }, +{ 0xB08B, 0xB08B, 0xB08B }, +{ 0xB08C, 0xB08C, 0xB08C }, +{ 0xB08D, 0xB08D, 0xB08D }, +{ 0xB08E, 0xB08E, 0xB08E }, +{ 0xB08F, 0xB08F, 0xB08F }, +{ 0xB090, 0xB090, 0xB090 }, +{ 0xB091, 0xB091, 0xB091 }, +{ 0xB092, 0xB092, 0xB092 }, +{ 0xB093, 0xB093, 0xB093 }, +{ 0xB094, 0xB094, 0xB094 }, +{ 0xB095, 0xB095, 0xB095 }, +{ 0xB096, 0xB096, 0xB096 }, +{ 0xB097, 0xB097, 0xB097 }, +{ 0xB098, 0xB098, 0xB098 }, +{ 0xB099, 0xB099, 0xB099 }, +{ 0xB09A, 0xB09A, 0xB09A }, +{ 0xB09B, 0xB09B, 0xB09B }, +{ 0xB09C, 0xB09C, 0xB09C }, +{ 0xB09D, 0xB09D, 0xB09D }, +{ 0xB09E, 0xB09E, 0xB09E }, +{ 0xB09F, 0xB09F, 0xB09F }, +{ 0xB0A0, 0xB0A0, 0xB0A0 }, +{ 0xB0A1, 0xB0A1, 0xB0A1 }, +{ 0xB0A2, 0xB0A2, 0xB0A2 }, +{ 0xB0A3, 0xB0A3, 0xB0A3 }, +{ 0xB0A4, 0xB0A4, 0xB0A4 }, +{ 0xB0A5, 0xB0A5, 0xB0A5 }, +{ 0xB0A6, 0xB0A6, 0xB0A6 }, +{ 0xB0A7, 0xB0A7, 0xB0A7 }, +{ 0xB0A8, 0xB0A8, 0xB0A8 }, +{ 0xB0A9, 0xB0A9, 0xB0A9 }, +{ 0xB0AA, 0xB0AA, 0xB0AA }, +{ 0xB0AB, 0xB0AB, 0xB0AB }, +{ 0xB0AC, 0xB0AC, 0xB0AC }, +{ 0xB0AD, 0xB0AD, 0xB0AD }, +{ 0xB0AE, 0xB0AE, 0xB0AE }, +{ 0xB0AF, 0xB0AF, 0xB0AF }, +{ 0xB0B0, 0xB0B0, 0xB0B0 }, +{ 0xB0B1, 0xB0B1, 0xB0B1 }, +{ 0xB0B2, 0xB0B2, 0xB0B2 }, +{ 0xB0B3, 0xB0B3, 0xB0B3 }, +{ 0xB0B4, 0xB0B4, 0xB0B4 }, +{ 0xB0B5, 0xB0B5, 0xB0B5 }, +{ 0xB0B6, 0xB0B6, 0xB0B6 }, +{ 0xB0B7, 0xB0B7, 0xB0B7 }, +{ 0xB0B8, 0xB0B8, 0xB0B8 }, +{ 0xB0B9, 0xB0B9, 0xB0B9 }, +{ 0xB0BA, 0xB0BA, 0xB0BA }, +{ 0xB0BB, 0xB0BB, 0xB0BB }, +{ 0xB0BC, 0xB0BC, 0xB0BC }, +{ 0xB0BD, 0xB0BD, 0xB0BD }, +{ 0xB0BE, 0xB0BE, 0xB0BE }, +{ 0xB0BF, 0xB0BF, 0xB0BF }, +{ 0xB0C0, 0xB0C0, 0xB0C0 }, +{ 0xB0C1, 0xB0C1, 0xB0C1 }, +{ 0xB0C2, 0xB0C2, 0xB0C2 }, +{ 0xB0C3, 0xB0C3, 0xB0C3 }, +{ 0xB0C4, 0xB0C4, 0xB0C4 }, +{ 0xB0C5, 0xB0C5, 0xB0C5 }, +{ 0xB0C6, 0xB0C6, 0xB0C6 }, +{ 0xB0C7, 0xB0C7, 0xB0C7 }, +{ 0xB0C8, 0xB0C8, 0xB0C8 }, +{ 0xB0C9, 0xB0C9, 0xB0C9 }, +{ 0xB0CA, 0xB0CA, 0xB0CA }, +{ 0xB0CB, 0xB0CB, 0xB0CB }, +{ 0xB0CC, 0xB0CC, 0xB0CC }, +{ 0xB0CD, 0xB0CD, 0xB0CD }, +{ 0xB0CE, 0xB0CE, 0xB0CE }, +{ 0xB0CF, 0xB0CF, 0xB0CF }, +{ 0xB0D0, 0xB0D0, 0xB0D0 }, +{ 0xB0D1, 0xB0D1, 0xB0D1 }, +{ 0xB0D2, 0xB0D2, 0xB0D2 }, +{ 0xB0D3, 0xB0D3, 0xB0D3 }, +{ 0xB0D4, 0xB0D4, 0xB0D4 }, +{ 0xB0D5, 0xB0D5, 0xB0D5 }, +{ 0xB0D6, 0xB0D6, 0xB0D6 }, +{ 0xB0D7, 0xB0D7, 0xB0D7 }, +{ 0xB0D8, 0xB0D8, 0xB0D8 }, +{ 0xB0D9, 0xB0D9, 0xB0D9 }, +{ 0xB0DA, 0xB0DA, 0xB0DA }, +{ 0xB0DB, 0xB0DB, 0xB0DB }, +{ 0xB0DC, 0xB0DC, 0xB0DC }, +{ 0xB0DD, 0xB0DD, 0xB0DD }, +{ 0xB0DE, 0xB0DE, 0xB0DE }, +{ 0xB0DF, 0xB0DF, 0xB0DF }, +{ 0xB0E0, 0xB0E0, 0xB0E0 }, +{ 0xB0E1, 0xB0E1, 0xB0E1 }, +{ 0xB0E2, 0xB0E2, 0xB0E2 }, +{ 0xB0E3, 0xB0E3, 0xB0E3 }, +{ 0xB0E4, 0xB0E4, 0xB0E4 }, +{ 0xB0E5, 0xB0E5, 0xB0E5 }, +{ 0xB0E6, 0xB0E6, 0xB0E6 }, +{ 0xB0E7, 0xB0E7, 0xB0E7 }, +{ 0xB0E8, 0xB0E8, 0xB0E8 }, +{ 0xB0E9, 0xB0E9, 0xB0E9 }, +{ 0xB0EA, 0xB0EA, 0xB0EA }, +{ 0xB0EB, 0xB0EB, 0xB0EB }, +{ 0xB0EC, 0xB0EC, 0xB0EC }, +{ 0xB0ED, 0xB0ED, 0xB0ED }, +{ 0xB0EE, 0xB0EE, 0xB0EE }, +{ 0xB0EF, 0xB0EF, 0xB0EF }, +{ 0xB0F0, 0xB0F0, 0xB0F0 }, +{ 0xB0F1, 0xB0F1, 0xB0F1 }, +{ 0xB0F2, 0xB0F2, 0xB0F2 }, +{ 0xB0F3, 0xB0F3, 0xB0F3 }, +{ 0xB0F4, 0xB0F4, 0xB0F4 }, +{ 0xB0F5, 0xB0F5, 0xB0F5 }, +{ 0xB0F6, 0xB0F6, 0xB0F6 }, +{ 0xB0F7, 0xB0F7, 0xB0F7 }, +{ 0xB0F8, 0xB0F8, 0xB0F8 }, +{ 0xB0F9, 0xB0F9, 0xB0F9 }, +{ 0xB0FA, 0xB0FA, 0xB0FA }, +{ 0xB0FB, 0xB0FB, 0xB0FB }, +{ 0xB0FC, 0xB0FC, 0xB0FC }, +{ 0xB0FD, 0xB0FD, 0xB0FD }, +{ 0xB0FE, 0xB0FE, 0xB0FE }, +{ 0xB0FF, 0xB0FF, 0xB0FF }, +{ 0xB100, 0xB100, 0xB100 }, +{ 0xB101, 0xB101, 0xB101 }, +{ 0xB102, 0xB102, 0xB102 }, +{ 0xB103, 0xB103, 0xB103 }, +{ 0xB104, 0xB104, 0xB104 }, +{ 0xB105, 0xB105, 0xB105 }, +{ 0xB106, 0xB106, 0xB106 }, +{ 0xB107, 0xB107, 0xB107 }, +{ 0xB108, 0xB108, 0xB108 }, +{ 0xB109, 0xB109, 0xB109 }, +{ 0xB10A, 0xB10A, 0xB10A }, +{ 0xB10B, 0xB10B, 0xB10B }, +{ 0xB10C, 0xB10C, 0xB10C }, +{ 0xB10D, 0xB10D, 0xB10D }, +{ 0xB10E, 0xB10E, 0xB10E }, +{ 0xB10F, 0xB10F, 0xB10F }, +{ 0xB110, 0xB110, 0xB110 }, +{ 0xB111, 0xB111, 0xB111 }, +{ 0xB112, 0xB112, 0xB112 }, +{ 0xB113, 0xB113, 0xB113 }, +{ 0xB114, 0xB114, 0xB114 }, +{ 0xB115, 0xB115, 0xB115 }, +{ 0xB116, 0xB116, 0xB116 }, +{ 0xB117, 0xB117, 0xB117 }, +{ 0xB118, 0xB118, 0xB118 }, +{ 0xB119, 0xB119, 0xB119 }, +{ 0xB11A, 0xB11A, 0xB11A }, +{ 0xB11B, 0xB11B, 0xB11B }, +{ 0xB11C, 0xB11C, 0xB11C }, +{ 0xB11D, 0xB11D, 0xB11D }, +{ 0xB11E, 0xB11E, 0xB11E }, +{ 0xB11F, 0xB11F, 0xB11F }, +{ 0xB120, 0xB120, 0xB120 }, +{ 0xB121, 0xB121, 0xB121 }, +{ 0xB122, 0xB122, 0xB122 }, +{ 0xB123, 0xB123, 0xB123 }, +{ 0xB124, 0xB124, 0xB124 }, +{ 0xB125, 0xB125, 0xB125 }, +{ 0xB126, 0xB126, 0xB126 }, +{ 0xB127, 0xB127, 0xB127 }, +{ 0xB128, 0xB128, 0xB128 }, +{ 0xB129, 0xB129, 0xB129 }, +{ 0xB12A, 0xB12A, 0xB12A }, +{ 0xB12B, 0xB12B, 0xB12B }, +{ 0xB12C, 0xB12C, 0xB12C }, +{ 0xB12D, 0xB12D, 0xB12D }, +{ 0xB12E, 0xB12E, 0xB12E }, +{ 0xB12F, 0xB12F, 0xB12F }, +{ 0xB130, 0xB130, 0xB130 }, +{ 0xB131, 0xB131, 0xB131 }, +{ 0xB132, 0xB132, 0xB132 }, +{ 0xB133, 0xB133, 0xB133 }, +{ 0xB134, 0xB134, 0xB134 }, +{ 0xB135, 0xB135, 0xB135 }, +{ 0xB136, 0xB136, 0xB136 }, +{ 0xB137, 0xB137, 0xB137 }, +{ 0xB138, 0xB138, 0xB138 }, +{ 0xB139, 0xB139, 0xB139 }, +{ 0xB13A, 0xB13A, 0xB13A }, +{ 0xB13B, 0xB13B, 0xB13B }, +{ 0xB13C, 0xB13C, 0xB13C }, +{ 0xB13D, 0xB13D, 0xB13D }, +{ 0xB13E, 0xB13E, 0xB13E }, +{ 0xB13F, 0xB13F, 0xB13F }, +{ 0xB140, 0xB140, 0xB140 }, +{ 0xB141, 0xB141, 0xB141 }, +{ 0xB142, 0xB142, 0xB142 }, +{ 0xB143, 0xB143, 0xB143 }, +{ 0xB144, 0xB144, 0xB144 }, +{ 0xB145, 0xB145, 0xB145 }, +{ 0xB146, 0xB146, 0xB146 }, +{ 0xB147, 0xB147, 0xB147 }, +{ 0xB148, 0xB148, 0xB148 }, +{ 0xB149, 0xB149, 0xB149 }, +{ 0xB14A, 0xB14A, 0xB14A }, +{ 0xB14B, 0xB14B, 0xB14B }, +{ 0xB14C, 0xB14C, 0xB14C }, +{ 0xB14D, 0xB14D, 0xB14D }, +{ 0xB14E, 0xB14E, 0xB14E }, +{ 0xB14F, 0xB14F, 0xB14F }, +{ 0xB150, 0xB150, 0xB150 }, +{ 0xB151, 0xB151, 0xB151 }, +{ 0xB152, 0xB152, 0xB152 }, +{ 0xB153, 0xB153, 0xB153 }, +{ 0xB154, 0xB154, 0xB154 }, +{ 0xB155, 0xB155, 0xB155 }, +{ 0xB156, 0xB156, 0xB156 }, +{ 0xB157, 0xB157, 0xB157 }, +{ 0xB158, 0xB158, 0xB158 }, +{ 0xB159, 0xB159, 0xB159 }, +{ 0xB15A, 0xB15A, 0xB15A }, +{ 0xB15B, 0xB15B, 0xB15B }, +{ 0xB15C, 0xB15C, 0xB15C }, +{ 0xB15D, 0xB15D, 0xB15D }, +{ 0xB15E, 0xB15E, 0xB15E }, +{ 0xB15F, 0xB15F, 0xB15F }, +{ 0xB160, 0xB160, 0xB160 }, +{ 0xB161, 0xB161, 0xB161 }, +{ 0xB162, 0xB162, 0xB162 }, +{ 0xB163, 0xB163, 0xB163 }, +{ 0xB164, 0xB164, 0xB164 }, +{ 0xB165, 0xB165, 0xB165 }, +{ 0xB166, 0xB166, 0xB166 }, +{ 0xB167, 0xB167, 0xB167 }, +{ 0xB168, 0xB168, 0xB168 }, +{ 0xB169, 0xB169, 0xB169 }, +{ 0xB16A, 0xB16A, 0xB16A }, +{ 0xB16B, 0xB16B, 0xB16B }, +{ 0xB16C, 0xB16C, 0xB16C }, +{ 0xB16D, 0xB16D, 0xB16D }, +{ 0xB16E, 0xB16E, 0xB16E }, +{ 0xB16F, 0xB16F, 0xB16F }, +{ 0xB170, 0xB170, 0xB170 }, +{ 0xB171, 0xB171, 0xB171 }, +{ 0xB172, 0xB172, 0xB172 }, +{ 0xB173, 0xB173, 0xB173 }, +{ 0xB174, 0xB174, 0xB174 }, +{ 0xB175, 0xB175, 0xB175 }, +{ 0xB176, 0xB176, 0xB176 }, +{ 0xB177, 0xB177, 0xB177 }, +{ 0xB178, 0xB178, 0xB178 }, +{ 0xB179, 0xB179, 0xB179 }, +{ 0xB17A, 0xB17A, 0xB17A }, +{ 0xB17B, 0xB17B, 0xB17B }, +{ 0xB17C, 0xB17C, 0xB17C }, +{ 0xB17D, 0xB17D, 0xB17D }, +{ 0xB17E, 0xB17E, 0xB17E }, +{ 0xB17F, 0xB17F, 0xB17F }, +{ 0xB180, 0xB180, 0xB180 }, +{ 0xB181, 0xB181, 0xB181 }, +{ 0xB182, 0xB182, 0xB182 }, +{ 0xB183, 0xB183, 0xB183 }, +{ 0xB184, 0xB184, 0xB184 }, +{ 0xB185, 0xB185, 0xB185 }, +{ 0xB186, 0xB186, 0xB186 }, +{ 0xB187, 0xB187, 0xB187 }, +{ 0xB188, 0xB188, 0xB188 }, +{ 0xB189, 0xB189, 0xB189 }, +{ 0xB18A, 0xB18A, 0xB18A }, +{ 0xB18B, 0xB18B, 0xB18B }, +{ 0xB18C, 0xB18C, 0xB18C }, +{ 0xB18D, 0xB18D, 0xB18D }, +{ 0xB18E, 0xB18E, 0xB18E }, +{ 0xB18F, 0xB18F, 0xB18F }, +{ 0xB190, 0xB190, 0xB190 }, +{ 0xB191, 0xB191, 0xB191 }, +{ 0xB192, 0xB192, 0xB192 }, +{ 0xB193, 0xB193, 0xB193 }, +{ 0xB194, 0xB194, 0xB194 }, +{ 0xB195, 0xB195, 0xB195 }, +{ 0xB196, 0xB196, 0xB196 }, +{ 0xB197, 0xB197, 0xB197 }, +{ 0xB198, 0xB198, 0xB198 }, +{ 0xB199, 0xB199, 0xB199 }, +{ 0xB19A, 0xB19A, 0xB19A }, +{ 0xB19B, 0xB19B, 0xB19B }, +{ 0xB19C, 0xB19C, 0xB19C }, +{ 0xB19D, 0xB19D, 0xB19D }, +{ 0xB19E, 0xB19E, 0xB19E }, +{ 0xB19F, 0xB19F, 0xB19F }, +{ 0xB1A0, 0xB1A0, 0xB1A0 }, +{ 0xB1A1, 0xB1A1, 0xB1A1 }, +{ 0xB1A2, 0xB1A2, 0xB1A2 }, +{ 0xB1A3, 0xB1A3, 0xB1A3 }, +{ 0xB1A4, 0xB1A4, 0xB1A4 }, +{ 0xB1A5, 0xB1A5, 0xB1A5 }, +{ 0xB1A6, 0xB1A6, 0xB1A6 }, +{ 0xB1A7, 0xB1A7, 0xB1A7 }, +{ 0xB1A8, 0xB1A8, 0xB1A8 }, +{ 0xB1A9, 0xB1A9, 0xB1A9 }, +{ 0xB1AA, 0xB1AA, 0xB1AA }, +{ 0xB1AB, 0xB1AB, 0xB1AB }, +{ 0xB1AC, 0xB1AC, 0xB1AC }, +{ 0xB1AD, 0xB1AD, 0xB1AD }, +{ 0xB1AE, 0xB1AE, 0xB1AE }, +{ 0xB1AF, 0xB1AF, 0xB1AF }, +{ 0xB1B0, 0xB1B0, 0xB1B0 }, +{ 0xB1B1, 0xB1B1, 0xB1B1 }, +{ 0xB1B2, 0xB1B2, 0xB1B2 }, +{ 0xB1B3, 0xB1B3, 0xB1B3 }, +{ 0xB1B4, 0xB1B4, 0xB1B4 }, +{ 0xB1B5, 0xB1B5, 0xB1B5 }, +{ 0xB1B6, 0xB1B6, 0xB1B6 }, +{ 0xB1B7, 0xB1B7, 0xB1B7 }, +{ 0xB1B8, 0xB1B8, 0xB1B8 }, +{ 0xB1B9, 0xB1B9, 0xB1B9 }, +{ 0xB1BA, 0xB1BA, 0xB1BA }, +{ 0xB1BB, 0xB1BB, 0xB1BB }, +{ 0xB1BC, 0xB1BC, 0xB1BC }, +{ 0xB1BD, 0xB1BD, 0xB1BD }, +{ 0xB1BE, 0xB1BE, 0xB1BE }, +{ 0xB1BF, 0xB1BF, 0xB1BF }, +{ 0xB1C0, 0xB1C0, 0xB1C0 }, +{ 0xB1C1, 0xB1C1, 0xB1C1 }, +{ 0xB1C2, 0xB1C2, 0xB1C2 }, +{ 0xB1C3, 0xB1C3, 0xB1C3 }, +{ 0xB1C4, 0xB1C4, 0xB1C4 }, +{ 0xB1C5, 0xB1C5, 0xB1C5 }, +{ 0xB1C6, 0xB1C6, 0xB1C6 }, +{ 0xB1C7, 0xB1C7, 0xB1C7 }, +{ 0xB1C8, 0xB1C8, 0xB1C8 }, +{ 0xB1C9, 0xB1C9, 0xB1C9 }, +{ 0xB1CA, 0xB1CA, 0xB1CA }, +{ 0xB1CB, 0xB1CB, 0xB1CB }, +{ 0xB1CC, 0xB1CC, 0xB1CC }, +{ 0xB1CD, 0xB1CD, 0xB1CD }, +{ 0xB1CE, 0xB1CE, 0xB1CE }, +{ 0xB1CF, 0xB1CF, 0xB1CF }, +{ 0xB1D0, 0xB1D0, 0xB1D0 }, +{ 0xB1D1, 0xB1D1, 0xB1D1 }, +{ 0xB1D2, 0xB1D2, 0xB1D2 }, +{ 0xB1D3, 0xB1D3, 0xB1D3 }, +{ 0xB1D4, 0xB1D4, 0xB1D4 }, +{ 0xB1D5, 0xB1D5, 0xB1D5 }, +{ 0xB1D6, 0xB1D6, 0xB1D6 }, +{ 0xB1D7, 0xB1D7, 0xB1D7 }, +{ 0xB1D8, 0xB1D8, 0xB1D8 }, +{ 0xB1D9, 0xB1D9, 0xB1D9 }, +{ 0xB1DA, 0xB1DA, 0xB1DA }, +{ 0xB1DB, 0xB1DB, 0xB1DB }, +{ 0xB1DC, 0xB1DC, 0xB1DC }, +{ 0xB1DD, 0xB1DD, 0xB1DD }, +{ 0xB1DE, 0xB1DE, 0xB1DE }, +{ 0xB1DF, 0xB1DF, 0xB1DF }, +{ 0xB1E0, 0xB1E0, 0xB1E0 }, +{ 0xB1E1, 0xB1E1, 0xB1E1 }, +{ 0xB1E2, 0xB1E2, 0xB1E2 }, +{ 0xB1E3, 0xB1E3, 0xB1E3 }, +{ 0xB1E4, 0xB1E4, 0xB1E4 }, +{ 0xB1E5, 0xB1E5, 0xB1E5 }, +{ 0xB1E6, 0xB1E6, 0xB1E6 }, +{ 0xB1E7, 0xB1E7, 0xB1E7 }, +{ 0xB1E8, 0xB1E8, 0xB1E8 }, +{ 0xB1E9, 0xB1E9, 0xB1E9 }, +{ 0xB1EA, 0xB1EA, 0xB1EA }, +{ 0xB1EB, 0xB1EB, 0xB1EB }, +{ 0xB1EC, 0xB1EC, 0xB1EC }, +{ 0xB1ED, 0xB1ED, 0xB1ED }, +{ 0xB1EE, 0xB1EE, 0xB1EE }, +{ 0xB1EF, 0xB1EF, 0xB1EF }, +{ 0xB1F0, 0xB1F0, 0xB1F0 }, +{ 0xB1F1, 0xB1F1, 0xB1F1 }, +{ 0xB1F2, 0xB1F2, 0xB1F2 }, +{ 0xB1F3, 0xB1F3, 0xB1F3 }, +{ 0xB1F4, 0xB1F4, 0xB1F4 }, +{ 0xB1F5, 0xB1F5, 0xB1F5 }, +{ 0xB1F6, 0xB1F6, 0xB1F6 }, +{ 0xB1F7, 0xB1F7, 0xB1F7 }, +{ 0xB1F8, 0xB1F8, 0xB1F8 }, +{ 0xB1F9, 0xB1F9, 0xB1F9 }, +{ 0xB1FA, 0xB1FA, 0xB1FA }, +{ 0xB1FB, 0xB1FB, 0xB1FB }, +{ 0xB1FC, 0xB1FC, 0xB1FC }, +{ 0xB1FD, 0xB1FD, 0xB1FD }, +{ 0xB1FE, 0xB1FE, 0xB1FE }, +{ 0xB1FF, 0xB1FF, 0xB1FF }, +{ 0xB200, 0xB200, 0xB200 }, +{ 0xB201, 0xB201, 0xB201 }, +{ 0xB202, 0xB202, 0xB202 }, +{ 0xB203, 0xB203, 0xB203 }, +{ 0xB204, 0xB204, 0xB204 }, +{ 0xB205, 0xB205, 0xB205 }, +{ 0xB206, 0xB206, 0xB206 }, +{ 0xB207, 0xB207, 0xB207 }, +{ 0xB208, 0xB208, 0xB208 }, +{ 0xB209, 0xB209, 0xB209 }, +{ 0xB20A, 0xB20A, 0xB20A }, +{ 0xB20B, 0xB20B, 0xB20B }, +{ 0xB20C, 0xB20C, 0xB20C }, +{ 0xB20D, 0xB20D, 0xB20D }, +{ 0xB20E, 0xB20E, 0xB20E }, +{ 0xB20F, 0xB20F, 0xB20F }, +{ 0xB210, 0xB210, 0xB210 }, +{ 0xB211, 0xB211, 0xB211 }, +{ 0xB212, 0xB212, 0xB212 }, +{ 0xB213, 0xB213, 0xB213 }, +{ 0xB214, 0xB214, 0xB214 }, +{ 0xB215, 0xB215, 0xB215 }, +{ 0xB216, 0xB216, 0xB216 }, +{ 0xB217, 0xB217, 0xB217 }, +{ 0xB218, 0xB218, 0xB218 }, +{ 0xB219, 0xB219, 0xB219 }, +{ 0xB21A, 0xB21A, 0xB21A }, +{ 0xB21B, 0xB21B, 0xB21B }, +{ 0xB21C, 0xB21C, 0xB21C }, +{ 0xB21D, 0xB21D, 0xB21D }, +{ 0xB21E, 0xB21E, 0xB21E }, +{ 0xB21F, 0xB21F, 0xB21F }, +{ 0xB220, 0xB220, 0xB220 }, +{ 0xB221, 0xB221, 0xB221 }, +{ 0xB222, 0xB222, 0xB222 }, +{ 0xB223, 0xB223, 0xB223 }, +{ 0xB224, 0xB224, 0xB224 }, +{ 0xB225, 0xB225, 0xB225 }, +{ 0xB226, 0xB226, 0xB226 }, +{ 0xB227, 0xB227, 0xB227 }, +{ 0xB228, 0xB228, 0xB228 }, +{ 0xB229, 0xB229, 0xB229 }, +{ 0xB22A, 0xB22A, 0xB22A }, +{ 0xB22B, 0xB22B, 0xB22B }, +{ 0xB22C, 0xB22C, 0xB22C }, +{ 0xB22D, 0xB22D, 0xB22D }, +{ 0xB22E, 0xB22E, 0xB22E }, +{ 0xB22F, 0xB22F, 0xB22F }, +{ 0xB230, 0xB230, 0xB230 }, +{ 0xB231, 0xB231, 0xB231 }, +{ 0xB232, 0xB232, 0xB232 }, +{ 0xB233, 0xB233, 0xB233 }, +{ 0xB234, 0xB234, 0xB234 }, +{ 0xB235, 0xB235, 0xB235 }, +{ 0xB236, 0xB236, 0xB236 }, +{ 0xB237, 0xB237, 0xB237 }, +{ 0xB238, 0xB238, 0xB238 }, +{ 0xB239, 0xB239, 0xB239 }, +{ 0xB23A, 0xB23A, 0xB23A }, +{ 0xB23B, 0xB23B, 0xB23B }, +{ 0xB23C, 0xB23C, 0xB23C }, +{ 0xB23D, 0xB23D, 0xB23D }, +{ 0xB23E, 0xB23E, 0xB23E }, +{ 0xB23F, 0xB23F, 0xB23F }, +{ 0xB240, 0xB240, 0xB240 }, +{ 0xB241, 0xB241, 0xB241 }, +{ 0xB242, 0xB242, 0xB242 }, +{ 0xB243, 0xB243, 0xB243 }, +{ 0xB244, 0xB244, 0xB244 }, +{ 0xB245, 0xB245, 0xB245 }, +{ 0xB246, 0xB246, 0xB246 }, +{ 0xB247, 0xB247, 0xB247 }, +{ 0xB248, 0xB248, 0xB248 }, +{ 0xB249, 0xB249, 0xB249 }, +{ 0xB24A, 0xB24A, 0xB24A }, +{ 0xB24B, 0xB24B, 0xB24B }, +{ 0xB24C, 0xB24C, 0xB24C }, +{ 0xB24D, 0xB24D, 0xB24D }, +{ 0xB24E, 0xB24E, 0xB24E }, +{ 0xB24F, 0xB24F, 0xB24F }, +{ 0xB250, 0xB250, 0xB250 }, +{ 0xB251, 0xB251, 0xB251 }, +{ 0xB252, 0xB252, 0xB252 }, +{ 0xB253, 0xB253, 0xB253 }, +{ 0xB254, 0xB254, 0xB254 }, +{ 0xB255, 0xB255, 0xB255 }, +{ 0xB256, 0xB256, 0xB256 }, +{ 0xB257, 0xB257, 0xB257 }, +{ 0xB258, 0xB258, 0xB258 }, +{ 0xB259, 0xB259, 0xB259 }, +{ 0xB25A, 0xB25A, 0xB25A }, +{ 0xB25B, 0xB25B, 0xB25B }, +{ 0xB25C, 0xB25C, 0xB25C }, +{ 0xB25D, 0xB25D, 0xB25D }, +{ 0xB25E, 0xB25E, 0xB25E }, +{ 0xB25F, 0xB25F, 0xB25F }, +{ 0xB260, 0xB260, 0xB260 }, +{ 0xB261, 0xB261, 0xB261 }, +{ 0xB262, 0xB262, 0xB262 }, +{ 0xB263, 0xB263, 0xB263 }, +{ 0xB264, 0xB264, 0xB264 }, +{ 0xB265, 0xB265, 0xB265 }, +{ 0xB266, 0xB266, 0xB266 }, +{ 0xB267, 0xB267, 0xB267 }, +{ 0xB268, 0xB268, 0xB268 }, +{ 0xB269, 0xB269, 0xB269 }, +{ 0xB26A, 0xB26A, 0xB26A }, +{ 0xB26B, 0xB26B, 0xB26B }, +{ 0xB26C, 0xB26C, 0xB26C }, +{ 0xB26D, 0xB26D, 0xB26D }, +{ 0xB26E, 0xB26E, 0xB26E }, +{ 0xB26F, 0xB26F, 0xB26F }, +{ 0xB270, 0xB270, 0xB270 }, +{ 0xB271, 0xB271, 0xB271 }, +{ 0xB272, 0xB272, 0xB272 }, +{ 0xB273, 0xB273, 0xB273 }, +{ 0xB274, 0xB274, 0xB274 }, +{ 0xB275, 0xB275, 0xB275 }, +{ 0xB276, 0xB276, 0xB276 }, +{ 0xB277, 0xB277, 0xB277 }, +{ 0xB278, 0xB278, 0xB278 }, +{ 0xB279, 0xB279, 0xB279 }, +{ 0xB27A, 0xB27A, 0xB27A }, +{ 0xB27B, 0xB27B, 0xB27B }, +{ 0xB27C, 0xB27C, 0xB27C }, +{ 0xB27D, 0xB27D, 0xB27D }, +{ 0xB27E, 0xB27E, 0xB27E }, +{ 0xB27F, 0xB27F, 0xB27F }, +{ 0xB280, 0xB280, 0xB280 }, +{ 0xB281, 0xB281, 0xB281 }, +{ 0xB282, 0xB282, 0xB282 }, +{ 0xB283, 0xB283, 0xB283 }, +{ 0xB284, 0xB284, 0xB284 }, +{ 0xB285, 0xB285, 0xB285 }, +{ 0xB286, 0xB286, 0xB286 }, +{ 0xB287, 0xB287, 0xB287 }, +{ 0xB288, 0xB288, 0xB288 }, +{ 0xB289, 0xB289, 0xB289 }, +{ 0xB28A, 0xB28A, 0xB28A }, +{ 0xB28B, 0xB28B, 0xB28B }, +{ 0xB28C, 0xB28C, 0xB28C }, +{ 0xB28D, 0xB28D, 0xB28D }, +{ 0xB28E, 0xB28E, 0xB28E }, +{ 0xB28F, 0xB28F, 0xB28F }, +{ 0xB290, 0xB290, 0xB290 }, +{ 0xB291, 0xB291, 0xB291 }, +{ 0xB292, 0xB292, 0xB292 }, +{ 0xB293, 0xB293, 0xB293 }, +{ 0xB294, 0xB294, 0xB294 }, +{ 0xB295, 0xB295, 0xB295 }, +{ 0xB296, 0xB296, 0xB296 }, +{ 0xB297, 0xB297, 0xB297 }, +{ 0xB298, 0xB298, 0xB298 }, +{ 0xB299, 0xB299, 0xB299 }, +{ 0xB29A, 0xB29A, 0xB29A }, +{ 0xB29B, 0xB29B, 0xB29B }, +{ 0xB29C, 0xB29C, 0xB29C }, +{ 0xB29D, 0xB29D, 0xB29D }, +{ 0xB29E, 0xB29E, 0xB29E }, +{ 0xB29F, 0xB29F, 0xB29F }, +{ 0xB2A0, 0xB2A0, 0xB2A0 }, +{ 0xB2A1, 0xB2A1, 0xB2A1 }, +{ 0xB2A2, 0xB2A2, 0xB2A2 }, +{ 0xB2A3, 0xB2A3, 0xB2A3 }, +{ 0xB2A4, 0xB2A4, 0xB2A4 }, +{ 0xB2A5, 0xB2A5, 0xB2A5 }, +{ 0xB2A6, 0xB2A6, 0xB2A6 }, +{ 0xB2A7, 0xB2A7, 0xB2A7 }, +{ 0xB2A8, 0xB2A8, 0xB2A8 }, +{ 0xB2A9, 0xB2A9, 0xB2A9 }, +{ 0xB2AA, 0xB2AA, 0xB2AA }, +{ 0xB2AB, 0xB2AB, 0xB2AB }, +{ 0xB2AC, 0xB2AC, 0xB2AC }, +{ 0xB2AD, 0xB2AD, 0xB2AD }, +{ 0xB2AE, 0xB2AE, 0xB2AE }, +{ 0xB2AF, 0xB2AF, 0xB2AF }, +{ 0xB2B0, 0xB2B0, 0xB2B0 }, +{ 0xB2B1, 0xB2B1, 0xB2B1 }, +{ 0xB2B2, 0xB2B2, 0xB2B2 }, +{ 0xB2B3, 0xB2B3, 0xB2B3 }, +{ 0xB2B4, 0xB2B4, 0xB2B4 }, +{ 0xB2B5, 0xB2B5, 0xB2B5 }, +{ 0xB2B6, 0xB2B6, 0xB2B6 }, +{ 0xB2B7, 0xB2B7, 0xB2B7 }, +{ 0xB2B8, 0xB2B8, 0xB2B8 }, +{ 0xB2B9, 0xB2B9, 0xB2B9 }, +{ 0xB2BA, 0xB2BA, 0xB2BA }, +{ 0xB2BB, 0xB2BB, 0xB2BB }, +{ 0xB2BC, 0xB2BC, 0xB2BC }, +{ 0xB2BD, 0xB2BD, 0xB2BD }, +{ 0xB2BE, 0xB2BE, 0xB2BE }, +{ 0xB2BF, 0xB2BF, 0xB2BF }, +{ 0xB2C0, 0xB2C0, 0xB2C0 }, +{ 0xB2C1, 0xB2C1, 0xB2C1 }, +{ 0xB2C2, 0xB2C2, 0xB2C2 }, +{ 0xB2C3, 0xB2C3, 0xB2C3 }, +{ 0xB2C4, 0xB2C4, 0xB2C4 }, +{ 0xB2C5, 0xB2C5, 0xB2C5 }, +{ 0xB2C6, 0xB2C6, 0xB2C6 }, +{ 0xB2C7, 0xB2C7, 0xB2C7 }, +{ 0xB2C8, 0xB2C8, 0xB2C8 }, +{ 0xB2C9, 0xB2C9, 0xB2C9 }, +{ 0xB2CA, 0xB2CA, 0xB2CA }, +{ 0xB2CB, 0xB2CB, 0xB2CB }, +{ 0xB2CC, 0xB2CC, 0xB2CC }, +{ 0xB2CD, 0xB2CD, 0xB2CD }, +{ 0xB2CE, 0xB2CE, 0xB2CE }, +{ 0xB2CF, 0xB2CF, 0xB2CF }, +{ 0xB2D0, 0xB2D0, 0xB2D0 }, +{ 0xB2D1, 0xB2D1, 0xB2D1 }, +{ 0xB2D2, 0xB2D2, 0xB2D2 }, +{ 0xB2D3, 0xB2D3, 0xB2D3 }, +{ 0xB2D4, 0xB2D4, 0xB2D4 }, +{ 0xB2D5, 0xB2D5, 0xB2D5 }, +{ 0xB2D6, 0xB2D6, 0xB2D6 }, +{ 0xB2D7, 0xB2D7, 0xB2D7 }, +{ 0xB2D8, 0xB2D8, 0xB2D8 }, +{ 0xB2D9, 0xB2D9, 0xB2D9 }, +{ 0xB2DA, 0xB2DA, 0xB2DA }, +{ 0xB2DB, 0xB2DB, 0xB2DB }, +{ 0xB2DC, 0xB2DC, 0xB2DC }, +{ 0xB2DD, 0xB2DD, 0xB2DD }, +{ 0xB2DE, 0xB2DE, 0xB2DE }, +{ 0xB2DF, 0xB2DF, 0xB2DF }, +{ 0xB2E0, 0xB2E0, 0xB2E0 }, +{ 0xB2E1, 0xB2E1, 0xB2E1 }, +{ 0xB2E2, 0xB2E2, 0xB2E2 }, +{ 0xB2E3, 0xB2E3, 0xB2E3 }, +{ 0xB2E4, 0xB2E4, 0xB2E4 }, +{ 0xB2E5, 0xB2E5, 0xB2E5 }, +{ 0xB2E6, 0xB2E6, 0xB2E6 }, +{ 0xB2E7, 0xB2E7, 0xB2E7 }, +{ 0xB2E8, 0xB2E8, 0xB2E8 }, +{ 0xB2E9, 0xB2E9, 0xB2E9 }, +{ 0xB2EA, 0xB2EA, 0xB2EA }, +{ 0xB2EB, 0xB2EB, 0xB2EB }, +{ 0xB2EC, 0xB2EC, 0xB2EC }, +{ 0xB2ED, 0xB2ED, 0xB2ED }, +{ 0xB2EE, 0xB2EE, 0xB2EE }, +{ 0xB2EF, 0xB2EF, 0xB2EF }, +{ 0xB2F0, 0xB2F0, 0xB2F0 }, +{ 0xB2F1, 0xB2F1, 0xB2F1 }, +{ 0xB2F2, 0xB2F2, 0xB2F2 }, +{ 0xB2F3, 0xB2F3, 0xB2F3 }, +{ 0xB2F4, 0xB2F4, 0xB2F4 }, +{ 0xB2F5, 0xB2F5, 0xB2F5 }, +{ 0xB2F6, 0xB2F6, 0xB2F6 }, +{ 0xB2F7, 0xB2F7, 0xB2F7 }, +{ 0xB2F8, 0xB2F8, 0xB2F8 }, +{ 0xB2F9, 0xB2F9, 0xB2F9 }, +{ 0xB2FA, 0xB2FA, 0xB2FA }, +{ 0xB2FB, 0xB2FB, 0xB2FB }, +{ 0xB2FC, 0xB2FC, 0xB2FC }, +{ 0xB2FD, 0xB2FD, 0xB2FD }, +{ 0xB2FE, 0xB2FE, 0xB2FE }, +{ 0xB2FF, 0xB2FF, 0xB2FF }, +{ 0xB300, 0xB300, 0xB300 }, +{ 0xB301, 0xB301, 0xB301 }, +{ 0xB302, 0xB302, 0xB302 }, +{ 0xB303, 0xB303, 0xB303 }, +{ 0xB304, 0xB304, 0xB304 }, +{ 0xB305, 0xB305, 0xB305 }, +{ 0xB306, 0xB306, 0xB306 }, +{ 0xB307, 0xB307, 0xB307 }, +{ 0xB308, 0xB308, 0xB308 }, +{ 0xB309, 0xB309, 0xB309 }, +{ 0xB30A, 0xB30A, 0xB30A }, +{ 0xB30B, 0xB30B, 0xB30B }, +{ 0xB30C, 0xB30C, 0xB30C }, +{ 0xB30D, 0xB30D, 0xB30D }, +{ 0xB30E, 0xB30E, 0xB30E }, +{ 0xB30F, 0xB30F, 0xB30F }, +{ 0xB310, 0xB310, 0xB310 }, +{ 0xB311, 0xB311, 0xB311 }, +{ 0xB312, 0xB312, 0xB312 }, +{ 0xB313, 0xB313, 0xB313 }, +{ 0xB314, 0xB314, 0xB314 }, +{ 0xB315, 0xB315, 0xB315 }, +{ 0xB316, 0xB316, 0xB316 }, +{ 0xB317, 0xB317, 0xB317 }, +{ 0xB318, 0xB318, 0xB318 }, +{ 0xB319, 0xB319, 0xB319 }, +{ 0xB31A, 0xB31A, 0xB31A }, +{ 0xB31B, 0xB31B, 0xB31B }, +{ 0xB31C, 0xB31C, 0xB31C }, +{ 0xB31D, 0xB31D, 0xB31D }, +{ 0xB31E, 0xB31E, 0xB31E }, +{ 0xB31F, 0xB31F, 0xB31F }, +{ 0xB320, 0xB320, 0xB320 }, +{ 0xB321, 0xB321, 0xB321 }, +{ 0xB322, 0xB322, 0xB322 }, +{ 0xB323, 0xB323, 0xB323 }, +{ 0xB324, 0xB324, 0xB324 }, +{ 0xB325, 0xB325, 0xB325 }, +{ 0xB326, 0xB326, 0xB326 }, +{ 0xB327, 0xB327, 0xB327 }, +{ 0xB328, 0xB328, 0xB328 }, +{ 0xB329, 0xB329, 0xB329 }, +{ 0xB32A, 0xB32A, 0xB32A }, +{ 0xB32B, 0xB32B, 0xB32B }, +{ 0xB32C, 0xB32C, 0xB32C }, +{ 0xB32D, 0xB32D, 0xB32D }, +{ 0xB32E, 0xB32E, 0xB32E }, +{ 0xB32F, 0xB32F, 0xB32F }, +{ 0xB330, 0xB330, 0xB330 }, +{ 0xB331, 0xB331, 0xB331 }, +{ 0xB332, 0xB332, 0xB332 }, +{ 0xB333, 0xB333, 0xB333 }, +{ 0xB334, 0xB334, 0xB334 }, +{ 0xB335, 0xB335, 0xB335 }, +{ 0xB336, 0xB336, 0xB336 }, +{ 0xB337, 0xB337, 0xB337 }, +{ 0xB338, 0xB338, 0xB338 }, +{ 0xB339, 0xB339, 0xB339 }, +{ 0xB33A, 0xB33A, 0xB33A }, +{ 0xB33B, 0xB33B, 0xB33B }, +{ 0xB33C, 0xB33C, 0xB33C }, +{ 0xB33D, 0xB33D, 0xB33D }, +{ 0xB33E, 0xB33E, 0xB33E }, +{ 0xB33F, 0xB33F, 0xB33F }, +{ 0xB340, 0xB340, 0xB340 }, +{ 0xB341, 0xB341, 0xB341 }, +{ 0xB342, 0xB342, 0xB342 }, +{ 0xB343, 0xB343, 0xB343 }, +{ 0xB344, 0xB344, 0xB344 }, +{ 0xB345, 0xB345, 0xB345 }, +{ 0xB346, 0xB346, 0xB346 }, +{ 0xB347, 0xB347, 0xB347 }, +{ 0xB348, 0xB348, 0xB348 }, +{ 0xB349, 0xB349, 0xB349 }, +{ 0xB34A, 0xB34A, 0xB34A }, +{ 0xB34B, 0xB34B, 0xB34B }, +{ 0xB34C, 0xB34C, 0xB34C }, +{ 0xB34D, 0xB34D, 0xB34D }, +{ 0xB34E, 0xB34E, 0xB34E }, +{ 0xB34F, 0xB34F, 0xB34F }, +{ 0xB350, 0xB350, 0xB350 }, +{ 0xB351, 0xB351, 0xB351 }, +{ 0xB352, 0xB352, 0xB352 }, +{ 0xB353, 0xB353, 0xB353 }, +{ 0xB354, 0xB354, 0xB354 }, +{ 0xB355, 0xB355, 0xB355 }, +{ 0xB356, 0xB356, 0xB356 }, +{ 0xB357, 0xB357, 0xB357 }, +{ 0xB358, 0xB358, 0xB358 }, +{ 0xB359, 0xB359, 0xB359 }, +{ 0xB35A, 0xB35A, 0xB35A }, +{ 0xB35B, 0xB35B, 0xB35B }, +{ 0xB35C, 0xB35C, 0xB35C }, +{ 0xB35D, 0xB35D, 0xB35D }, +{ 0xB35E, 0xB35E, 0xB35E }, +{ 0xB35F, 0xB35F, 0xB35F }, +{ 0xB360, 0xB360, 0xB360 }, +{ 0xB361, 0xB361, 0xB361 }, +{ 0xB362, 0xB362, 0xB362 }, +{ 0xB363, 0xB363, 0xB363 }, +{ 0xB364, 0xB364, 0xB364 }, +{ 0xB365, 0xB365, 0xB365 }, +{ 0xB366, 0xB366, 0xB366 }, +{ 0xB367, 0xB367, 0xB367 }, +{ 0xB368, 0xB368, 0xB368 }, +{ 0xB369, 0xB369, 0xB369 }, +{ 0xB36A, 0xB36A, 0xB36A }, +{ 0xB36B, 0xB36B, 0xB36B }, +{ 0xB36C, 0xB36C, 0xB36C }, +{ 0xB36D, 0xB36D, 0xB36D }, +{ 0xB36E, 0xB36E, 0xB36E }, +{ 0xB36F, 0xB36F, 0xB36F }, +{ 0xB370, 0xB370, 0xB370 }, +{ 0xB371, 0xB371, 0xB371 }, +{ 0xB372, 0xB372, 0xB372 }, +{ 0xB373, 0xB373, 0xB373 }, +{ 0xB374, 0xB374, 0xB374 }, +{ 0xB375, 0xB375, 0xB375 }, +{ 0xB376, 0xB376, 0xB376 }, +{ 0xB377, 0xB377, 0xB377 }, +{ 0xB378, 0xB378, 0xB378 }, +{ 0xB379, 0xB379, 0xB379 }, +{ 0xB37A, 0xB37A, 0xB37A }, +{ 0xB37B, 0xB37B, 0xB37B }, +{ 0xB37C, 0xB37C, 0xB37C }, +{ 0xB37D, 0xB37D, 0xB37D }, +{ 0xB37E, 0xB37E, 0xB37E }, +{ 0xB37F, 0xB37F, 0xB37F }, +{ 0xB380, 0xB380, 0xB380 }, +{ 0xB381, 0xB381, 0xB381 }, +{ 0xB382, 0xB382, 0xB382 }, +{ 0xB383, 0xB383, 0xB383 }, +{ 0xB384, 0xB384, 0xB384 }, +{ 0xB385, 0xB385, 0xB385 }, +{ 0xB386, 0xB386, 0xB386 }, +{ 0xB387, 0xB387, 0xB387 }, +{ 0xB388, 0xB388, 0xB388 }, +{ 0xB389, 0xB389, 0xB389 }, +{ 0xB38A, 0xB38A, 0xB38A }, +{ 0xB38B, 0xB38B, 0xB38B }, +{ 0xB38C, 0xB38C, 0xB38C }, +{ 0xB38D, 0xB38D, 0xB38D }, +{ 0xB38E, 0xB38E, 0xB38E }, +{ 0xB38F, 0xB38F, 0xB38F }, +{ 0xB390, 0xB390, 0xB390 }, +{ 0xB391, 0xB391, 0xB391 }, +{ 0xB392, 0xB392, 0xB392 }, +{ 0xB393, 0xB393, 0xB393 }, +{ 0xB394, 0xB394, 0xB394 }, +{ 0xB395, 0xB395, 0xB395 }, +{ 0xB396, 0xB396, 0xB396 }, +{ 0xB397, 0xB397, 0xB397 }, +{ 0xB398, 0xB398, 0xB398 }, +{ 0xB399, 0xB399, 0xB399 }, +{ 0xB39A, 0xB39A, 0xB39A }, +{ 0xB39B, 0xB39B, 0xB39B }, +{ 0xB39C, 0xB39C, 0xB39C }, +{ 0xB39D, 0xB39D, 0xB39D }, +{ 0xB39E, 0xB39E, 0xB39E }, +{ 0xB39F, 0xB39F, 0xB39F }, +{ 0xB3A0, 0xB3A0, 0xB3A0 }, +{ 0xB3A1, 0xB3A1, 0xB3A1 }, +{ 0xB3A2, 0xB3A2, 0xB3A2 }, +{ 0xB3A3, 0xB3A3, 0xB3A3 }, +{ 0xB3A4, 0xB3A4, 0xB3A4 }, +{ 0xB3A5, 0xB3A5, 0xB3A5 }, +{ 0xB3A6, 0xB3A6, 0xB3A6 }, +{ 0xB3A7, 0xB3A7, 0xB3A7 }, +{ 0xB3A8, 0xB3A8, 0xB3A8 }, +{ 0xB3A9, 0xB3A9, 0xB3A9 }, +{ 0xB3AA, 0xB3AA, 0xB3AA }, +{ 0xB3AB, 0xB3AB, 0xB3AB }, +{ 0xB3AC, 0xB3AC, 0xB3AC }, +{ 0xB3AD, 0xB3AD, 0xB3AD }, +{ 0xB3AE, 0xB3AE, 0xB3AE }, +{ 0xB3AF, 0xB3AF, 0xB3AF }, +{ 0xB3B0, 0xB3B0, 0xB3B0 }, +{ 0xB3B1, 0xB3B1, 0xB3B1 }, +{ 0xB3B2, 0xB3B2, 0xB3B2 }, +{ 0xB3B3, 0xB3B3, 0xB3B3 }, +{ 0xB3B4, 0xB3B4, 0xB3B4 }, +{ 0xB3B5, 0xB3B5, 0xB3B5 }, +{ 0xB3B6, 0xB3B6, 0xB3B6 }, +{ 0xB3B7, 0xB3B7, 0xB3B7 }, +{ 0xB3B8, 0xB3B8, 0xB3B8 }, +{ 0xB3B9, 0xB3B9, 0xB3B9 }, +{ 0xB3BA, 0xB3BA, 0xB3BA }, +{ 0xB3BB, 0xB3BB, 0xB3BB }, +{ 0xB3BC, 0xB3BC, 0xB3BC }, +{ 0xB3BD, 0xB3BD, 0xB3BD }, +{ 0xB3BE, 0xB3BE, 0xB3BE }, +{ 0xB3BF, 0xB3BF, 0xB3BF }, +{ 0xB3C0, 0xB3C0, 0xB3C0 }, +{ 0xB3C1, 0xB3C1, 0xB3C1 }, +{ 0xB3C2, 0xB3C2, 0xB3C2 }, +{ 0xB3C3, 0xB3C3, 0xB3C3 }, +{ 0xB3C4, 0xB3C4, 0xB3C4 }, +{ 0xB3C5, 0xB3C5, 0xB3C5 }, +{ 0xB3C6, 0xB3C6, 0xB3C6 }, +{ 0xB3C7, 0xB3C7, 0xB3C7 }, +{ 0xB3C8, 0xB3C8, 0xB3C8 }, +{ 0xB3C9, 0xB3C9, 0xB3C9 }, +{ 0xB3CA, 0xB3CA, 0xB3CA }, +{ 0xB3CB, 0xB3CB, 0xB3CB }, +{ 0xB3CC, 0xB3CC, 0xB3CC }, +{ 0xB3CD, 0xB3CD, 0xB3CD }, +{ 0xB3CE, 0xB3CE, 0xB3CE }, +{ 0xB3CF, 0xB3CF, 0xB3CF }, +{ 0xB3D0, 0xB3D0, 0xB3D0 }, +{ 0xB3D1, 0xB3D1, 0xB3D1 }, +{ 0xB3D2, 0xB3D2, 0xB3D2 }, +{ 0xB3D3, 0xB3D3, 0xB3D3 }, +{ 0xB3D4, 0xB3D4, 0xB3D4 }, +{ 0xB3D5, 0xB3D5, 0xB3D5 }, +{ 0xB3D6, 0xB3D6, 0xB3D6 }, +{ 0xB3D7, 0xB3D7, 0xB3D7 }, +{ 0xB3D8, 0xB3D8, 0xB3D8 }, +{ 0xB3D9, 0xB3D9, 0xB3D9 }, +{ 0xB3DA, 0xB3DA, 0xB3DA }, +{ 0xB3DB, 0xB3DB, 0xB3DB }, +{ 0xB3DC, 0xB3DC, 0xB3DC }, +{ 0xB3DD, 0xB3DD, 0xB3DD }, +{ 0xB3DE, 0xB3DE, 0xB3DE }, +{ 0xB3DF, 0xB3DF, 0xB3DF }, +{ 0xB3E0, 0xB3E0, 0xB3E0 }, +{ 0xB3E1, 0xB3E1, 0xB3E1 }, +{ 0xB3E2, 0xB3E2, 0xB3E2 }, +{ 0xB3E3, 0xB3E3, 0xB3E3 }, +{ 0xB3E4, 0xB3E4, 0xB3E4 }, +{ 0xB3E5, 0xB3E5, 0xB3E5 }, +{ 0xB3E6, 0xB3E6, 0xB3E6 }, +{ 0xB3E7, 0xB3E7, 0xB3E7 }, +{ 0xB3E8, 0xB3E8, 0xB3E8 }, +{ 0xB3E9, 0xB3E9, 0xB3E9 }, +{ 0xB3EA, 0xB3EA, 0xB3EA }, +{ 0xB3EB, 0xB3EB, 0xB3EB }, +{ 0xB3EC, 0xB3EC, 0xB3EC }, +{ 0xB3ED, 0xB3ED, 0xB3ED }, +{ 0xB3EE, 0xB3EE, 0xB3EE }, +{ 0xB3EF, 0xB3EF, 0xB3EF }, +{ 0xB3F0, 0xB3F0, 0xB3F0 }, +{ 0xB3F1, 0xB3F1, 0xB3F1 }, +{ 0xB3F2, 0xB3F2, 0xB3F2 }, +{ 0xB3F3, 0xB3F3, 0xB3F3 }, +{ 0xB3F4, 0xB3F4, 0xB3F4 }, +{ 0xB3F5, 0xB3F5, 0xB3F5 }, +{ 0xB3F6, 0xB3F6, 0xB3F6 }, +{ 0xB3F7, 0xB3F7, 0xB3F7 }, +{ 0xB3F8, 0xB3F8, 0xB3F8 }, +{ 0xB3F9, 0xB3F9, 0xB3F9 }, +{ 0xB3FA, 0xB3FA, 0xB3FA }, +{ 0xB3FB, 0xB3FB, 0xB3FB }, +{ 0xB3FC, 0xB3FC, 0xB3FC }, +{ 0xB3FD, 0xB3FD, 0xB3FD }, +{ 0xB3FE, 0xB3FE, 0xB3FE }, +{ 0xB3FF, 0xB3FF, 0xB3FF }, +{ 0xB400, 0xB400, 0xB400 }, +{ 0xB401, 0xB401, 0xB401 }, +{ 0xB402, 0xB402, 0xB402 }, +{ 0xB403, 0xB403, 0xB403 }, +{ 0xB404, 0xB404, 0xB404 }, +{ 0xB405, 0xB405, 0xB405 }, +{ 0xB406, 0xB406, 0xB406 }, +{ 0xB407, 0xB407, 0xB407 }, +{ 0xB408, 0xB408, 0xB408 }, +{ 0xB409, 0xB409, 0xB409 }, +{ 0xB40A, 0xB40A, 0xB40A }, +{ 0xB40B, 0xB40B, 0xB40B }, +{ 0xB40C, 0xB40C, 0xB40C }, +{ 0xB40D, 0xB40D, 0xB40D }, +{ 0xB40E, 0xB40E, 0xB40E }, +{ 0xB40F, 0xB40F, 0xB40F }, +{ 0xB410, 0xB410, 0xB410 }, +{ 0xB411, 0xB411, 0xB411 }, +{ 0xB412, 0xB412, 0xB412 }, +{ 0xB413, 0xB413, 0xB413 }, +{ 0xB414, 0xB414, 0xB414 }, +{ 0xB415, 0xB415, 0xB415 }, +{ 0xB416, 0xB416, 0xB416 }, +{ 0xB417, 0xB417, 0xB417 }, +{ 0xB418, 0xB418, 0xB418 }, +{ 0xB419, 0xB419, 0xB419 }, +{ 0xB41A, 0xB41A, 0xB41A }, +{ 0xB41B, 0xB41B, 0xB41B }, +{ 0xB41C, 0xB41C, 0xB41C }, +{ 0xB41D, 0xB41D, 0xB41D }, +{ 0xB41E, 0xB41E, 0xB41E }, +{ 0xB41F, 0xB41F, 0xB41F }, +{ 0xB420, 0xB420, 0xB420 }, +{ 0xB421, 0xB421, 0xB421 }, +{ 0xB422, 0xB422, 0xB422 }, +{ 0xB423, 0xB423, 0xB423 }, +{ 0xB424, 0xB424, 0xB424 }, +{ 0xB425, 0xB425, 0xB425 }, +{ 0xB426, 0xB426, 0xB426 }, +{ 0xB427, 0xB427, 0xB427 }, +{ 0xB428, 0xB428, 0xB428 }, +{ 0xB429, 0xB429, 0xB429 }, +{ 0xB42A, 0xB42A, 0xB42A }, +{ 0xB42B, 0xB42B, 0xB42B }, +{ 0xB42C, 0xB42C, 0xB42C }, +{ 0xB42D, 0xB42D, 0xB42D }, +{ 0xB42E, 0xB42E, 0xB42E }, +{ 0xB42F, 0xB42F, 0xB42F }, +{ 0xB430, 0xB430, 0xB430 }, +{ 0xB431, 0xB431, 0xB431 }, +{ 0xB432, 0xB432, 0xB432 }, +{ 0xB433, 0xB433, 0xB433 }, +{ 0xB434, 0xB434, 0xB434 }, +{ 0xB435, 0xB435, 0xB435 }, +{ 0xB436, 0xB436, 0xB436 }, +{ 0xB437, 0xB437, 0xB437 }, +{ 0xB438, 0xB438, 0xB438 }, +{ 0xB439, 0xB439, 0xB439 }, +{ 0xB43A, 0xB43A, 0xB43A }, +{ 0xB43B, 0xB43B, 0xB43B }, +{ 0xB43C, 0xB43C, 0xB43C }, +{ 0xB43D, 0xB43D, 0xB43D }, +{ 0xB43E, 0xB43E, 0xB43E }, +{ 0xB43F, 0xB43F, 0xB43F }, +{ 0xB440, 0xB440, 0xB440 }, +{ 0xB441, 0xB441, 0xB441 }, +{ 0xB442, 0xB442, 0xB442 }, +{ 0xB443, 0xB443, 0xB443 }, +{ 0xB444, 0xB444, 0xB444 }, +{ 0xB445, 0xB445, 0xB445 }, +{ 0xB446, 0xB446, 0xB446 }, +{ 0xB447, 0xB447, 0xB447 }, +{ 0xB448, 0xB448, 0xB448 }, +{ 0xB449, 0xB449, 0xB449 }, +{ 0xB44A, 0xB44A, 0xB44A }, +{ 0xB44B, 0xB44B, 0xB44B }, +{ 0xB44C, 0xB44C, 0xB44C }, +{ 0xB44D, 0xB44D, 0xB44D }, +{ 0xB44E, 0xB44E, 0xB44E }, +{ 0xB44F, 0xB44F, 0xB44F }, +{ 0xB450, 0xB450, 0xB450 }, +{ 0xB451, 0xB451, 0xB451 }, +{ 0xB452, 0xB452, 0xB452 }, +{ 0xB453, 0xB453, 0xB453 }, +{ 0xB454, 0xB454, 0xB454 }, +{ 0xB455, 0xB455, 0xB455 }, +{ 0xB456, 0xB456, 0xB456 }, +{ 0xB457, 0xB457, 0xB457 }, +{ 0xB458, 0xB458, 0xB458 }, +{ 0xB459, 0xB459, 0xB459 }, +{ 0xB45A, 0xB45A, 0xB45A }, +{ 0xB45B, 0xB45B, 0xB45B }, +{ 0xB45C, 0xB45C, 0xB45C }, +{ 0xB45D, 0xB45D, 0xB45D }, +{ 0xB45E, 0xB45E, 0xB45E }, +{ 0xB45F, 0xB45F, 0xB45F }, +{ 0xB460, 0xB460, 0xB460 }, +{ 0xB461, 0xB461, 0xB461 }, +{ 0xB462, 0xB462, 0xB462 }, +{ 0xB463, 0xB463, 0xB463 }, +{ 0xB464, 0xB464, 0xB464 }, +{ 0xB465, 0xB465, 0xB465 }, +{ 0xB466, 0xB466, 0xB466 }, +{ 0xB467, 0xB467, 0xB467 }, +{ 0xB468, 0xB468, 0xB468 }, +{ 0xB469, 0xB469, 0xB469 }, +{ 0xB46A, 0xB46A, 0xB46A }, +{ 0xB46B, 0xB46B, 0xB46B }, +{ 0xB46C, 0xB46C, 0xB46C }, +{ 0xB46D, 0xB46D, 0xB46D }, +{ 0xB46E, 0xB46E, 0xB46E }, +{ 0xB46F, 0xB46F, 0xB46F }, +{ 0xB470, 0xB470, 0xB470 }, +{ 0xB471, 0xB471, 0xB471 }, +{ 0xB472, 0xB472, 0xB472 }, +{ 0xB473, 0xB473, 0xB473 }, +{ 0xB474, 0xB474, 0xB474 }, +{ 0xB475, 0xB475, 0xB475 }, +{ 0xB476, 0xB476, 0xB476 }, +{ 0xB477, 0xB477, 0xB477 }, +{ 0xB478, 0xB478, 0xB478 }, +{ 0xB479, 0xB479, 0xB479 }, +{ 0xB47A, 0xB47A, 0xB47A }, +{ 0xB47B, 0xB47B, 0xB47B }, +{ 0xB47C, 0xB47C, 0xB47C }, +{ 0xB47D, 0xB47D, 0xB47D }, +{ 0xB47E, 0xB47E, 0xB47E }, +{ 0xB47F, 0xB47F, 0xB47F }, +{ 0xB480, 0xB480, 0xB480 }, +{ 0xB481, 0xB481, 0xB481 }, +{ 0xB482, 0xB482, 0xB482 }, +{ 0xB483, 0xB483, 0xB483 }, +{ 0xB484, 0xB484, 0xB484 }, +{ 0xB485, 0xB485, 0xB485 }, +{ 0xB486, 0xB486, 0xB486 }, +{ 0xB487, 0xB487, 0xB487 }, +{ 0xB488, 0xB488, 0xB488 }, +{ 0xB489, 0xB489, 0xB489 }, +{ 0xB48A, 0xB48A, 0xB48A }, +{ 0xB48B, 0xB48B, 0xB48B }, +{ 0xB48C, 0xB48C, 0xB48C }, +{ 0xB48D, 0xB48D, 0xB48D }, +{ 0xB48E, 0xB48E, 0xB48E }, +{ 0xB48F, 0xB48F, 0xB48F }, +{ 0xB490, 0xB490, 0xB490 }, +{ 0xB491, 0xB491, 0xB491 }, +{ 0xB492, 0xB492, 0xB492 }, +{ 0xB493, 0xB493, 0xB493 }, +{ 0xB494, 0xB494, 0xB494 }, +{ 0xB495, 0xB495, 0xB495 }, +{ 0xB496, 0xB496, 0xB496 }, +{ 0xB497, 0xB497, 0xB497 }, +{ 0xB498, 0xB498, 0xB498 }, +{ 0xB499, 0xB499, 0xB499 }, +{ 0xB49A, 0xB49A, 0xB49A }, +{ 0xB49B, 0xB49B, 0xB49B }, +{ 0xB49C, 0xB49C, 0xB49C }, +{ 0xB49D, 0xB49D, 0xB49D }, +{ 0xB49E, 0xB49E, 0xB49E }, +{ 0xB49F, 0xB49F, 0xB49F }, +{ 0xB4A0, 0xB4A0, 0xB4A0 }, +{ 0xB4A1, 0xB4A1, 0xB4A1 }, +{ 0xB4A2, 0xB4A2, 0xB4A2 }, +{ 0xB4A3, 0xB4A3, 0xB4A3 }, +{ 0xB4A4, 0xB4A4, 0xB4A4 }, +{ 0xB4A5, 0xB4A5, 0xB4A5 }, +{ 0xB4A6, 0xB4A6, 0xB4A6 }, +{ 0xB4A7, 0xB4A7, 0xB4A7 }, +{ 0xB4A8, 0xB4A8, 0xB4A8 }, +{ 0xB4A9, 0xB4A9, 0xB4A9 }, +{ 0xB4AA, 0xB4AA, 0xB4AA }, +{ 0xB4AB, 0xB4AB, 0xB4AB }, +{ 0xB4AC, 0xB4AC, 0xB4AC }, +{ 0xB4AD, 0xB4AD, 0xB4AD }, +{ 0xB4AE, 0xB4AE, 0xB4AE }, +{ 0xB4AF, 0xB4AF, 0xB4AF }, +{ 0xB4B0, 0xB4B0, 0xB4B0 }, +{ 0xB4B1, 0xB4B1, 0xB4B1 }, +{ 0xB4B2, 0xB4B2, 0xB4B2 }, +{ 0xB4B3, 0xB4B3, 0xB4B3 }, +{ 0xB4B4, 0xB4B4, 0xB4B4 }, +{ 0xB4B5, 0xB4B5, 0xB4B5 }, +{ 0xB4B6, 0xB4B6, 0xB4B6 }, +{ 0xB4B7, 0xB4B7, 0xB4B7 }, +{ 0xB4B8, 0xB4B8, 0xB4B8 }, +{ 0xB4B9, 0xB4B9, 0xB4B9 }, +{ 0xB4BA, 0xB4BA, 0xB4BA }, +{ 0xB4BB, 0xB4BB, 0xB4BB }, +{ 0xB4BC, 0xB4BC, 0xB4BC }, +{ 0xB4BD, 0xB4BD, 0xB4BD }, +{ 0xB4BE, 0xB4BE, 0xB4BE }, +{ 0xB4BF, 0xB4BF, 0xB4BF }, +{ 0xB4C0, 0xB4C0, 0xB4C0 }, +{ 0xB4C1, 0xB4C1, 0xB4C1 }, +{ 0xB4C2, 0xB4C2, 0xB4C2 }, +{ 0xB4C3, 0xB4C3, 0xB4C3 }, +{ 0xB4C4, 0xB4C4, 0xB4C4 }, +{ 0xB4C5, 0xB4C5, 0xB4C5 }, +{ 0xB4C6, 0xB4C6, 0xB4C6 }, +{ 0xB4C7, 0xB4C7, 0xB4C7 }, +{ 0xB4C8, 0xB4C8, 0xB4C8 }, +{ 0xB4C9, 0xB4C9, 0xB4C9 }, +{ 0xB4CA, 0xB4CA, 0xB4CA }, +{ 0xB4CB, 0xB4CB, 0xB4CB }, +{ 0xB4CC, 0xB4CC, 0xB4CC }, +{ 0xB4CD, 0xB4CD, 0xB4CD }, +{ 0xB4CE, 0xB4CE, 0xB4CE }, +{ 0xB4CF, 0xB4CF, 0xB4CF }, +{ 0xB4D0, 0xB4D0, 0xB4D0 }, +{ 0xB4D1, 0xB4D1, 0xB4D1 }, +{ 0xB4D2, 0xB4D2, 0xB4D2 }, +{ 0xB4D3, 0xB4D3, 0xB4D3 }, +{ 0xB4D4, 0xB4D4, 0xB4D4 }, +{ 0xB4D5, 0xB4D5, 0xB4D5 }, +{ 0xB4D6, 0xB4D6, 0xB4D6 }, +{ 0xB4D7, 0xB4D7, 0xB4D7 }, +{ 0xB4D8, 0xB4D8, 0xB4D8 }, +{ 0xB4D9, 0xB4D9, 0xB4D9 }, +{ 0xB4DA, 0xB4DA, 0xB4DA }, +{ 0xB4DB, 0xB4DB, 0xB4DB }, +{ 0xB4DC, 0xB4DC, 0xB4DC }, +{ 0xB4DD, 0xB4DD, 0xB4DD }, +{ 0xB4DE, 0xB4DE, 0xB4DE }, +{ 0xB4DF, 0xB4DF, 0xB4DF }, +{ 0xB4E0, 0xB4E0, 0xB4E0 }, +{ 0xB4E1, 0xB4E1, 0xB4E1 }, +{ 0xB4E2, 0xB4E2, 0xB4E2 }, +{ 0xB4E3, 0xB4E3, 0xB4E3 }, +{ 0xB4E4, 0xB4E4, 0xB4E4 }, +{ 0xB4E5, 0xB4E5, 0xB4E5 }, +{ 0xB4E6, 0xB4E6, 0xB4E6 }, +{ 0xB4E7, 0xB4E7, 0xB4E7 }, +{ 0xB4E8, 0xB4E8, 0xB4E8 }, +{ 0xB4E9, 0xB4E9, 0xB4E9 }, +{ 0xB4EA, 0xB4EA, 0xB4EA }, +{ 0xB4EB, 0xB4EB, 0xB4EB }, +{ 0xB4EC, 0xB4EC, 0xB4EC }, +{ 0xB4ED, 0xB4ED, 0xB4ED }, +{ 0xB4EE, 0xB4EE, 0xB4EE }, +{ 0xB4EF, 0xB4EF, 0xB4EF }, +{ 0xB4F0, 0xB4F0, 0xB4F0 }, +{ 0xB4F1, 0xB4F1, 0xB4F1 }, +{ 0xB4F2, 0xB4F2, 0xB4F2 }, +{ 0xB4F3, 0xB4F3, 0xB4F3 }, +{ 0xB4F4, 0xB4F4, 0xB4F4 }, +{ 0xB4F5, 0xB4F5, 0xB4F5 }, +{ 0xB4F6, 0xB4F6, 0xB4F6 }, +{ 0xB4F7, 0xB4F7, 0xB4F7 }, +{ 0xB4F8, 0xB4F8, 0xB4F8 }, +{ 0xB4F9, 0xB4F9, 0xB4F9 }, +{ 0xB4FA, 0xB4FA, 0xB4FA }, +{ 0xB4FB, 0xB4FB, 0xB4FB }, +{ 0xB4FC, 0xB4FC, 0xB4FC }, +{ 0xB4FD, 0xB4FD, 0xB4FD }, +{ 0xB4FE, 0xB4FE, 0xB4FE }, +{ 0xB4FF, 0xB4FF, 0xB4FF }, +{ 0xB500, 0xB500, 0xB500 }, +{ 0xB501, 0xB501, 0xB501 }, +{ 0xB502, 0xB502, 0xB502 }, +{ 0xB503, 0xB503, 0xB503 }, +{ 0xB504, 0xB504, 0xB504 }, +{ 0xB505, 0xB505, 0xB505 }, +{ 0xB506, 0xB506, 0xB506 }, +{ 0xB507, 0xB507, 0xB507 }, +{ 0xB508, 0xB508, 0xB508 }, +{ 0xB509, 0xB509, 0xB509 }, +{ 0xB50A, 0xB50A, 0xB50A }, +{ 0xB50B, 0xB50B, 0xB50B }, +{ 0xB50C, 0xB50C, 0xB50C }, +{ 0xB50D, 0xB50D, 0xB50D }, +{ 0xB50E, 0xB50E, 0xB50E }, +{ 0xB50F, 0xB50F, 0xB50F }, +{ 0xB510, 0xB510, 0xB510 }, +{ 0xB511, 0xB511, 0xB511 }, +{ 0xB512, 0xB512, 0xB512 }, +{ 0xB513, 0xB513, 0xB513 }, +{ 0xB514, 0xB514, 0xB514 }, +{ 0xB515, 0xB515, 0xB515 }, +{ 0xB516, 0xB516, 0xB516 }, +{ 0xB517, 0xB517, 0xB517 }, +{ 0xB518, 0xB518, 0xB518 }, +{ 0xB519, 0xB519, 0xB519 }, +{ 0xB51A, 0xB51A, 0xB51A }, +{ 0xB51B, 0xB51B, 0xB51B }, +{ 0xB51C, 0xB51C, 0xB51C }, +{ 0xB51D, 0xB51D, 0xB51D }, +{ 0xB51E, 0xB51E, 0xB51E }, +{ 0xB51F, 0xB51F, 0xB51F }, +{ 0xB520, 0xB520, 0xB520 }, +{ 0xB521, 0xB521, 0xB521 }, +{ 0xB522, 0xB522, 0xB522 }, +{ 0xB523, 0xB523, 0xB523 }, +{ 0xB524, 0xB524, 0xB524 }, +{ 0xB525, 0xB525, 0xB525 }, +{ 0xB526, 0xB526, 0xB526 }, +{ 0xB527, 0xB527, 0xB527 }, +{ 0xB528, 0xB528, 0xB528 }, +{ 0xB529, 0xB529, 0xB529 }, +{ 0xB52A, 0xB52A, 0xB52A }, +{ 0xB52B, 0xB52B, 0xB52B }, +{ 0xB52C, 0xB52C, 0xB52C }, +{ 0xB52D, 0xB52D, 0xB52D }, +{ 0xB52E, 0xB52E, 0xB52E }, +{ 0xB52F, 0xB52F, 0xB52F }, +{ 0xB530, 0xB530, 0xB530 }, +{ 0xB531, 0xB531, 0xB531 }, +{ 0xB532, 0xB532, 0xB532 }, +{ 0xB533, 0xB533, 0xB533 }, +{ 0xB534, 0xB534, 0xB534 }, +{ 0xB535, 0xB535, 0xB535 }, +{ 0xB536, 0xB536, 0xB536 }, +{ 0xB537, 0xB537, 0xB537 }, +{ 0xB538, 0xB538, 0xB538 }, +{ 0xB539, 0xB539, 0xB539 }, +{ 0xB53A, 0xB53A, 0xB53A }, +{ 0xB53B, 0xB53B, 0xB53B }, +{ 0xB53C, 0xB53C, 0xB53C }, +{ 0xB53D, 0xB53D, 0xB53D }, +{ 0xB53E, 0xB53E, 0xB53E }, +{ 0xB53F, 0xB53F, 0xB53F }, +{ 0xB540, 0xB540, 0xB540 }, +{ 0xB541, 0xB541, 0xB541 }, +{ 0xB542, 0xB542, 0xB542 }, +{ 0xB543, 0xB543, 0xB543 }, +{ 0xB544, 0xB544, 0xB544 }, +{ 0xB545, 0xB545, 0xB545 }, +{ 0xB546, 0xB546, 0xB546 }, +{ 0xB547, 0xB547, 0xB547 }, +{ 0xB548, 0xB548, 0xB548 }, +{ 0xB549, 0xB549, 0xB549 }, +{ 0xB54A, 0xB54A, 0xB54A }, +{ 0xB54B, 0xB54B, 0xB54B }, +{ 0xB54C, 0xB54C, 0xB54C }, +{ 0xB54D, 0xB54D, 0xB54D }, +{ 0xB54E, 0xB54E, 0xB54E }, +{ 0xB54F, 0xB54F, 0xB54F }, +{ 0xB550, 0xB550, 0xB550 }, +{ 0xB551, 0xB551, 0xB551 }, +{ 0xB552, 0xB552, 0xB552 }, +{ 0xB553, 0xB553, 0xB553 }, +{ 0xB554, 0xB554, 0xB554 }, +{ 0xB555, 0xB555, 0xB555 }, +{ 0xB556, 0xB556, 0xB556 }, +{ 0xB557, 0xB557, 0xB557 }, +{ 0xB558, 0xB558, 0xB558 }, +{ 0xB559, 0xB559, 0xB559 }, +{ 0xB55A, 0xB55A, 0xB55A }, +{ 0xB55B, 0xB55B, 0xB55B }, +{ 0xB55C, 0xB55C, 0xB55C }, +{ 0xB55D, 0xB55D, 0xB55D }, +{ 0xB55E, 0xB55E, 0xB55E }, +{ 0xB55F, 0xB55F, 0xB55F }, +{ 0xB560, 0xB560, 0xB560 }, +{ 0xB561, 0xB561, 0xB561 }, +{ 0xB562, 0xB562, 0xB562 }, +{ 0xB563, 0xB563, 0xB563 }, +{ 0xB564, 0xB564, 0xB564 }, +{ 0xB565, 0xB565, 0xB565 }, +{ 0xB566, 0xB566, 0xB566 }, +{ 0xB567, 0xB567, 0xB567 }, +{ 0xB568, 0xB568, 0xB568 }, +{ 0xB569, 0xB569, 0xB569 }, +{ 0xB56A, 0xB56A, 0xB56A }, +{ 0xB56B, 0xB56B, 0xB56B }, +{ 0xB56C, 0xB56C, 0xB56C }, +{ 0xB56D, 0xB56D, 0xB56D }, +{ 0xB56E, 0xB56E, 0xB56E }, +{ 0xB56F, 0xB56F, 0xB56F }, +{ 0xB570, 0xB570, 0xB570 }, +{ 0xB571, 0xB571, 0xB571 }, +{ 0xB572, 0xB572, 0xB572 }, +{ 0xB573, 0xB573, 0xB573 }, +{ 0xB574, 0xB574, 0xB574 }, +{ 0xB575, 0xB575, 0xB575 }, +{ 0xB576, 0xB576, 0xB576 }, +{ 0xB577, 0xB577, 0xB577 }, +{ 0xB578, 0xB578, 0xB578 }, +{ 0xB579, 0xB579, 0xB579 }, +{ 0xB57A, 0xB57A, 0xB57A }, +{ 0xB57B, 0xB57B, 0xB57B }, +{ 0xB57C, 0xB57C, 0xB57C }, +{ 0xB57D, 0xB57D, 0xB57D }, +{ 0xB57E, 0xB57E, 0xB57E }, +{ 0xB57F, 0xB57F, 0xB57F }, +{ 0xB580, 0xB580, 0xB580 }, +{ 0xB581, 0xB581, 0xB581 }, +{ 0xB582, 0xB582, 0xB582 }, +{ 0xB583, 0xB583, 0xB583 }, +{ 0xB584, 0xB584, 0xB584 }, +{ 0xB585, 0xB585, 0xB585 }, +{ 0xB586, 0xB586, 0xB586 }, +{ 0xB587, 0xB587, 0xB587 }, +{ 0xB588, 0xB588, 0xB588 }, +{ 0xB589, 0xB589, 0xB589 }, +{ 0xB58A, 0xB58A, 0xB58A }, +{ 0xB58B, 0xB58B, 0xB58B }, +{ 0xB58C, 0xB58C, 0xB58C }, +{ 0xB58D, 0xB58D, 0xB58D }, +{ 0xB58E, 0xB58E, 0xB58E }, +{ 0xB58F, 0xB58F, 0xB58F }, +{ 0xB590, 0xB590, 0xB590 }, +{ 0xB591, 0xB591, 0xB591 }, +{ 0xB592, 0xB592, 0xB592 }, +{ 0xB593, 0xB593, 0xB593 }, +{ 0xB594, 0xB594, 0xB594 }, +{ 0xB595, 0xB595, 0xB595 }, +{ 0xB596, 0xB596, 0xB596 }, +{ 0xB597, 0xB597, 0xB597 }, +{ 0xB598, 0xB598, 0xB598 }, +{ 0xB599, 0xB599, 0xB599 }, +{ 0xB59A, 0xB59A, 0xB59A }, +{ 0xB59B, 0xB59B, 0xB59B }, +{ 0xB59C, 0xB59C, 0xB59C }, +{ 0xB59D, 0xB59D, 0xB59D }, +{ 0xB59E, 0xB59E, 0xB59E }, +{ 0xB59F, 0xB59F, 0xB59F }, +{ 0xB5A0, 0xB5A0, 0xB5A0 }, +{ 0xB5A1, 0xB5A1, 0xB5A1 }, +{ 0xB5A2, 0xB5A2, 0xB5A2 }, +{ 0xB5A3, 0xB5A3, 0xB5A3 }, +{ 0xB5A4, 0xB5A4, 0xB5A4 }, +{ 0xB5A5, 0xB5A5, 0xB5A5 }, +{ 0xB5A6, 0xB5A6, 0xB5A6 }, +{ 0xB5A7, 0xB5A7, 0xB5A7 }, +{ 0xB5A8, 0xB5A8, 0xB5A8 }, +{ 0xB5A9, 0xB5A9, 0xB5A9 }, +{ 0xB5AA, 0xB5AA, 0xB5AA }, +{ 0xB5AB, 0xB5AB, 0xB5AB }, +{ 0xB5AC, 0xB5AC, 0xB5AC }, +{ 0xB5AD, 0xB5AD, 0xB5AD }, +{ 0xB5AE, 0xB5AE, 0xB5AE }, +{ 0xB5AF, 0xB5AF, 0xB5AF }, +{ 0xB5B0, 0xB5B0, 0xB5B0 }, +{ 0xB5B1, 0xB5B1, 0xB5B1 }, +{ 0xB5B2, 0xB5B2, 0xB5B2 }, +{ 0xB5B3, 0xB5B3, 0xB5B3 }, +{ 0xB5B4, 0xB5B4, 0xB5B4 }, +{ 0xB5B5, 0xB5B5, 0xB5B5 }, +{ 0xB5B6, 0xB5B6, 0xB5B6 }, +{ 0xB5B7, 0xB5B7, 0xB5B7 }, +{ 0xB5B8, 0xB5B8, 0xB5B8 }, +{ 0xB5B9, 0xB5B9, 0xB5B9 }, +{ 0xB5BA, 0xB5BA, 0xB5BA }, +{ 0xB5BB, 0xB5BB, 0xB5BB }, +{ 0xB5BC, 0xB5BC, 0xB5BC }, +{ 0xB5BD, 0xB5BD, 0xB5BD }, +{ 0xB5BE, 0xB5BE, 0xB5BE }, +{ 0xB5BF, 0xB5BF, 0xB5BF }, +{ 0xB5C0, 0xB5C0, 0xB5C0 }, +{ 0xB5C1, 0xB5C1, 0xB5C1 }, +{ 0xB5C2, 0xB5C2, 0xB5C2 }, +{ 0xB5C3, 0xB5C3, 0xB5C3 }, +{ 0xB5C4, 0xB5C4, 0xB5C4 }, +{ 0xB5C5, 0xB5C5, 0xB5C5 }, +{ 0xB5C6, 0xB5C6, 0xB5C6 }, +{ 0xB5C7, 0xB5C7, 0xB5C7 }, +{ 0xB5C8, 0xB5C8, 0xB5C8 }, +{ 0xB5C9, 0xB5C9, 0xB5C9 }, +{ 0xB5CA, 0xB5CA, 0xB5CA }, +{ 0xB5CB, 0xB5CB, 0xB5CB }, +{ 0xB5CC, 0xB5CC, 0xB5CC }, +{ 0xB5CD, 0xB5CD, 0xB5CD }, +{ 0xB5CE, 0xB5CE, 0xB5CE }, +{ 0xB5CF, 0xB5CF, 0xB5CF }, +{ 0xB5D0, 0xB5D0, 0xB5D0 }, +{ 0xB5D1, 0xB5D1, 0xB5D1 }, +{ 0xB5D2, 0xB5D2, 0xB5D2 }, +{ 0xB5D3, 0xB5D3, 0xB5D3 }, +{ 0xB5D4, 0xB5D4, 0xB5D4 }, +{ 0xB5D5, 0xB5D5, 0xB5D5 }, +{ 0xB5D6, 0xB5D6, 0xB5D6 }, +{ 0xB5D7, 0xB5D7, 0xB5D7 }, +{ 0xB5D8, 0xB5D8, 0xB5D8 }, +{ 0xB5D9, 0xB5D9, 0xB5D9 }, +{ 0xB5DA, 0xB5DA, 0xB5DA }, +{ 0xB5DB, 0xB5DB, 0xB5DB }, +{ 0xB5DC, 0xB5DC, 0xB5DC }, +{ 0xB5DD, 0xB5DD, 0xB5DD }, +{ 0xB5DE, 0xB5DE, 0xB5DE }, +{ 0xB5DF, 0xB5DF, 0xB5DF }, +{ 0xB5E0, 0xB5E0, 0xB5E0 }, +{ 0xB5E1, 0xB5E1, 0xB5E1 }, +{ 0xB5E2, 0xB5E2, 0xB5E2 }, +{ 0xB5E3, 0xB5E3, 0xB5E3 }, +{ 0xB5E4, 0xB5E4, 0xB5E4 }, +{ 0xB5E5, 0xB5E5, 0xB5E5 }, +{ 0xB5E6, 0xB5E6, 0xB5E6 }, +{ 0xB5E7, 0xB5E7, 0xB5E7 }, +{ 0xB5E8, 0xB5E8, 0xB5E8 }, +{ 0xB5E9, 0xB5E9, 0xB5E9 }, +{ 0xB5EA, 0xB5EA, 0xB5EA }, +{ 0xB5EB, 0xB5EB, 0xB5EB }, +{ 0xB5EC, 0xB5EC, 0xB5EC }, +{ 0xB5ED, 0xB5ED, 0xB5ED }, +{ 0xB5EE, 0xB5EE, 0xB5EE }, +{ 0xB5EF, 0xB5EF, 0xB5EF }, +{ 0xB5F0, 0xB5F0, 0xB5F0 }, +{ 0xB5F1, 0xB5F1, 0xB5F1 }, +{ 0xB5F2, 0xB5F2, 0xB5F2 }, +{ 0xB5F3, 0xB5F3, 0xB5F3 }, +{ 0xB5F4, 0xB5F4, 0xB5F4 }, +{ 0xB5F5, 0xB5F5, 0xB5F5 }, +{ 0xB5F6, 0xB5F6, 0xB5F6 }, +{ 0xB5F7, 0xB5F7, 0xB5F7 }, +{ 0xB5F8, 0xB5F8, 0xB5F8 }, +{ 0xB5F9, 0xB5F9, 0xB5F9 }, +{ 0xB5FA, 0xB5FA, 0xB5FA }, +{ 0xB5FB, 0xB5FB, 0xB5FB }, +{ 0xB5FC, 0xB5FC, 0xB5FC }, +{ 0xB5FD, 0xB5FD, 0xB5FD }, +{ 0xB5FE, 0xB5FE, 0xB5FE }, +{ 0xB5FF, 0xB5FF, 0xB5FF }, +{ 0xB600, 0xB600, 0xB600 }, +{ 0xB601, 0xB601, 0xB601 }, +{ 0xB602, 0xB602, 0xB602 }, +{ 0xB603, 0xB603, 0xB603 }, +{ 0xB604, 0xB604, 0xB604 }, +{ 0xB605, 0xB605, 0xB605 }, +{ 0xB606, 0xB606, 0xB606 }, +{ 0xB607, 0xB607, 0xB607 }, +{ 0xB608, 0xB608, 0xB608 }, +{ 0xB609, 0xB609, 0xB609 }, +{ 0xB60A, 0xB60A, 0xB60A }, +{ 0xB60B, 0xB60B, 0xB60B }, +{ 0xB60C, 0xB60C, 0xB60C }, +{ 0xB60D, 0xB60D, 0xB60D }, +{ 0xB60E, 0xB60E, 0xB60E }, +{ 0xB60F, 0xB60F, 0xB60F }, +{ 0xB610, 0xB610, 0xB610 }, +{ 0xB611, 0xB611, 0xB611 }, +{ 0xB612, 0xB612, 0xB612 }, +{ 0xB613, 0xB613, 0xB613 }, +{ 0xB614, 0xB614, 0xB614 }, +{ 0xB615, 0xB615, 0xB615 }, +{ 0xB616, 0xB616, 0xB616 }, +{ 0xB617, 0xB617, 0xB617 }, +{ 0xB618, 0xB618, 0xB618 }, +{ 0xB619, 0xB619, 0xB619 }, +{ 0xB61A, 0xB61A, 0xB61A }, +{ 0xB61B, 0xB61B, 0xB61B }, +{ 0xB61C, 0xB61C, 0xB61C }, +{ 0xB61D, 0xB61D, 0xB61D }, +{ 0xB61E, 0xB61E, 0xB61E }, +{ 0xB61F, 0xB61F, 0xB61F }, +{ 0xB620, 0xB620, 0xB620 }, +{ 0xB621, 0xB621, 0xB621 }, +{ 0xB622, 0xB622, 0xB622 }, +{ 0xB623, 0xB623, 0xB623 }, +{ 0xB624, 0xB624, 0xB624 }, +{ 0xB625, 0xB625, 0xB625 }, +{ 0xB626, 0xB626, 0xB626 }, +{ 0xB627, 0xB627, 0xB627 }, +{ 0xB628, 0xB628, 0xB628 }, +{ 0xB629, 0xB629, 0xB629 }, +{ 0xB62A, 0xB62A, 0xB62A }, +{ 0xB62B, 0xB62B, 0xB62B }, +{ 0xB62C, 0xB62C, 0xB62C }, +{ 0xB62D, 0xB62D, 0xB62D }, +{ 0xB62E, 0xB62E, 0xB62E }, +{ 0xB62F, 0xB62F, 0xB62F }, +{ 0xB630, 0xB630, 0xB630 }, +{ 0xB631, 0xB631, 0xB631 }, +{ 0xB632, 0xB632, 0xB632 }, +{ 0xB633, 0xB633, 0xB633 }, +{ 0xB634, 0xB634, 0xB634 }, +{ 0xB635, 0xB635, 0xB635 }, +{ 0xB636, 0xB636, 0xB636 }, +{ 0xB637, 0xB637, 0xB637 }, +{ 0xB638, 0xB638, 0xB638 }, +{ 0xB639, 0xB639, 0xB639 }, +{ 0xB63A, 0xB63A, 0xB63A }, +{ 0xB63B, 0xB63B, 0xB63B }, +{ 0xB63C, 0xB63C, 0xB63C }, +{ 0xB63D, 0xB63D, 0xB63D }, +{ 0xB63E, 0xB63E, 0xB63E }, +{ 0xB63F, 0xB63F, 0xB63F }, +{ 0xB640, 0xB640, 0xB640 }, +{ 0xB641, 0xB641, 0xB641 }, +{ 0xB642, 0xB642, 0xB642 }, +{ 0xB643, 0xB643, 0xB643 }, +{ 0xB644, 0xB644, 0xB644 }, +{ 0xB645, 0xB645, 0xB645 }, +{ 0xB646, 0xB646, 0xB646 }, +{ 0xB647, 0xB647, 0xB647 }, +{ 0xB648, 0xB648, 0xB648 }, +{ 0xB649, 0xB649, 0xB649 }, +{ 0xB64A, 0xB64A, 0xB64A }, +{ 0xB64B, 0xB64B, 0xB64B }, +{ 0xB64C, 0xB64C, 0xB64C }, +{ 0xB64D, 0xB64D, 0xB64D }, +{ 0xB64E, 0xB64E, 0xB64E }, +{ 0xB64F, 0xB64F, 0xB64F }, +{ 0xB650, 0xB650, 0xB650 }, +{ 0xB651, 0xB651, 0xB651 }, +{ 0xB652, 0xB652, 0xB652 }, +{ 0xB653, 0xB653, 0xB653 }, +{ 0xB654, 0xB654, 0xB654 }, +{ 0xB655, 0xB655, 0xB655 }, +{ 0xB656, 0xB656, 0xB656 }, +{ 0xB657, 0xB657, 0xB657 }, +{ 0xB658, 0xB658, 0xB658 }, +{ 0xB659, 0xB659, 0xB659 }, +{ 0xB65A, 0xB65A, 0xB65A }, +{ 0xB65B, 0xB65B, 0xB65B }, +{ 0xB65C, 0xB65C, 0xB65C }, +{ 0xB65D, 0xB65D, 0xB65D }, +{ 0xB65E, 0xB65E, 0xB65E }, +{ 0xB65F, 0xB65F, 0xB65F }, +{ 0xB660, 0xB660, 0xB660 }, +{ 0xB661, 0xB661, 0xB661 }, +{ 0xB662, 0xB662, 0xB662 }, +{ 0xB663, 0xB663, 0xB663 }, +{ 0xB664, 0xB664, 0xB664 }, +{ 0xB665, 0xB665, 0xB665 }, +{ 0xB666, 0xB666, 0xB666 }, +{ 0xB667, 0xB667, 0xB667 }, +{ 0xB668, 0xB668, 0xB668 }, +{ 0xB669, 0xB669, 0xB669 }, +{ 0xB66A, 0xB66A, 0xB66A }, +{ 0xB66B, 0xB66B, 0xB66B }, +{ 0xB66C, 0xB66C, 0xB66C }, +{ 0xB66D, 0xB66D, 0xB66D }, +{ 0xB66E, 0xB66E, 0xB66E }, +{ 0xB66F, 0xB66F, 0xB66F }, +{ 0xB670, 0xB670, 0xB670 }, +{ 0xB671, 0xB671, 0xB671 }, +{ 0xB672, 0xB672, 0xB672 }, +{ 0xB673, 0xB673, 0xB673 }, +{ 0xB674, 0xB674, 0xB674 }, +{ 0xB675, 0xB675, 0xB675 }, +{ 0xB676, 0xB676, 0xB676 }, +{ 0xB677, 0xB677, 0xB677 }, +{ 0xB678, 0xB678, 0xB678 }, +{ 0xB679, 0xB679, 0xB679 }, +{ 0xB67A, 0xB67A, 0xB67A }, +{ 0xB67B, 0xB67B, 0xB67B }, +{ 0xB67C, 0xB67C, 0xB67C }, +{ 0xB67D, 0xB67D, 0xB67D }, +{ 0xB67E, 0xB67E, 0xB67E }, +{ 0xB67F, 0xB67F, 0xB67F }, +{ 0xB680, 0xB680, 0xB680 }, +{ 0xB681, 0xB681, 0xB681 }, +{ 0xB682, 0xB682, 0xB682 }, +{ 0xB683, 0xB683, 0xB683 }, +{ 0xB684, 0xB684, 0xB684 }, +{ 0xB685, 0xB685, 0xB685 }, +{ 0xB686, 0xB686, 0xB686 }, +{ 0xB687, 0xB687, 0xB687 }, +{ 0xB688, 0xB688, 0xB688 }, +{ 0xB689, 0xB689, 0xB689 }, +{ 0xB68A, 0xB68A, 0xB68A }, +{ 0xB68B, 0xB68B, 0xB68B }, +{ 0xB68C, 0xB68C, 0xB68C }, +{ 0xB68D, 0xB68D, 0xB68D }, +{ 0xB68E, 0xB68E, 0xB68E }, +{ 0xB68F, 0xB68F, 0xB68F }, +{ 0xB690, 0xB690, 0xB690 }, +{ 0xB691, 0xB691, 0xB691 }, +{ 0xB692, 0xB692, 0xB692 }, +{ 0xB693, 0xB693, 0xB693 }, +{ 0xB694, 0xB694, 0xB694 }, +{ 0xB695, 0xB695, 0xB695 }, +{ 0xB696, 0xB696, 0xB696 }, +{ 0xB697, 0xB697, 0xB697 }, +{ 0xB698, 0xB698, 0xB698 }, +{ 0xB699, 0xB699, 0xB699 }, +{ 0xB69A, 0xB69A, 0xB69A }, +{ 0xB69B, 0xB69B, 0xB69B }, +{ 0xB69C, 0xB69C, 0xB69C }, +{ 0xB69D, 0xB69D, 0xB69D }, +{ 0xB69E, 0xB69E, 0xB69E }, +{ 0xB69F, 0xB69F, 0xB69F }, +{ 0xB6A0, 0xB6A0, 0xB6A0 }, +{ 0xB6A1, 0xB6A1, 0xB6A1 }, +{ 0xB6A2, 0xB6A2, 0xB6A2 }, +{ 0xB6A3, 0xB6A3, 0xB6A3 }, +{ 0xB6A4, 0xB6A4, 0xB6A4 }, +{ 0xB6A5, 0xB6A5, 0xB6A5 }, +{ 0xB6A6, 0xB6A6, 0xB6A6 }, +{ 0xB6A7, 0xB6A7, 0xB6A7 }, +{ 0xB6A8, 0xB6A8, 0xB6A8 }, +{ 0xB6A9, 0xB6A9, 0xB6A9 }, +{ 0xB6AA, 0xB6AA, 0xB6AA }, +{ 0xB6AB, 0xB6AB, 0xB6AB }, +{ 0xB6AC, 0xB6AC, 0xB6AC }, +{ 0xB6AD, 0xB6AD, 0xB6AD }, +{ 0xB6AE, 0xB6AE, 0xB6AE }, +{ 0xB6AF, 0xB6AF, 0xB6AF }, +{ 0xB6B0, 0xB6B0, 0xB6B0 }, +{ 0xB6B1, 0xB6B1, 0xB6B1 }, +{ 0xB6B2, 0xB6B2, 0xB6B2 }, +{ 0xB6B3, 0xB6B3, 0xB6B3 }, +{ 0xB6B4, 0xB6B4, 0xB6B4 }, +{ 0xB6B5, 0xB6B5, 0xB6B5 }, +{ 0xB6B6, 0xB6B6, 0xB6B6 }, +{ 0xB6B7, 0xB6B7, 0xB6B7 }, +{ 0xB6B8, 0xB6B8, 0xB6B8 }, +{ 0xB6B9, 0xB6B9, 0xB6B9 }, +{ 0xB6BA, 0xB6BA, 0xB6BA }, +{ 0xB6BB, 0xB6BB, 0xB6BB }, +{ 0xB6BC, 0xB6BC, 0xB6BC }, +{ 0xB6BD, 0xB6BD, 0xB6BD }, +{ 0xB6BE, 0xB6BE, 0xB6BE }, +{ 0xB6BF, 0xB6BF, 0xB6BF }, +{ 0xB6C0, 0xB6C0, 0xB6C0 }, +{ 0xB6C1, 0xB6C1, 0xB6C1 }, +{ 0xB6C2, 0xB6C2, 0xB6C2 }, +{ 0xB6C3, 0xB6C3, 0xB6C3 }, +{ 0xB6C4, 0xB6C4, 0xB6C4 }, +{ 0xB6C5, 0xB6C5, 0xB6C5 }, +{ 0xB6C6, 0xB6C6, 0xB6C6 }, +{ 0xB6C7, 0xB6C7, 0xB6C7 }, +{ 0xB6C8, 0xB6C8, 0xB6C8 }, +{ 0xB6C9, 0xB6C9, 0xB6C9 }, +{ 0xB6CA, 0xB6CA, 0xB6CA }, +{ 0xB6CB, 0xB6CB, 0xB6CB }, +{ 0xB6CC, 0xB6CC, 0xB6CC }, +{ 0xB6CD, 0xB6CD, 0xB6CD }, +{ 0xB6CE, 0xB6CE, 0xB6CE }, +{ 0xB6CF, 0xB6CF, 0xB6CF }, +{ 0xB6D0, 0xB6D0, 0xB6D0 }, +{ 0xB6D1, 0xB6D1, 0xB6D1 }, +{ 0xB6D2, 0xB6D2, 0xB6D2 }, +{ 0xB6D3, 0xB6D3, 0xB6D3 }, +{ 0xB6D4, 0xB6D4, 0xB6D4 }, +{ 0xB6D5, 0xB6D5, 0xB6D5 }, +{ 0xB6D6, 0xB6D6, 0xB6D6 }, +{ 0xB6D7, 0xB6D7, 0xB6D7 }, +{ 0xB6D8, 0xB6D8, 0xB6D8 }, +{ 0xB6D9, 0xB6D9, 0xB6D9 }, +{ 0xB6DA, 0xB6DA, 0xB6DA }, +{ 0xB6DB, 0xB6DB, 0xB6DB }, +{ 0xB6DC, 0xB6DC, 0xB6DC }, +{ 0xB6DD, 0xB6DD, 0xB6DD }, +{ 0xB6DE, 0xB6DE, 0xB6DE }, +{ 0xB6DF, 0xB6DF, 0xB6DF }, +{ 0xB6E0, 0xB6E0, 0xB6E0 }, +{ 0xB6E1, 0xB6E1, 0xB6E1 }, +{ 0xB6E2, 0xB6E2, 0xB6E2 }, +{ 0xB6E3, 0xB6E3, 0xB6E3 }, +{ 0xB6E4, 0xB6E4, 0xB6E4 }, +{ 0xB6E5, 0xB6E5, 0xB6E5 }, +{ 0xB6E6, 0xB6E6, 0xB6E6 }, +{ 0xB6E7, 0xB6E7, 0xB6E7 }, +{ 0xB6E8, 0xB6E8, 0xB6E8 }, +{ 0xB6E9, 0xB6E9, 0xB6E9 }, +{ 0xB6EA, 0xB6EA, 0xB6EA }, +{ 0xB6EB, 0xB6EB, 0xB6EB }, +{ 0xB6EC, 0xB6EC, 0xB6EC }, +{ 0xB6ED, 0xB6ED, 0xB6ED }, +{ 0xB6EE, 0xB6EE, 0xB6EE }, +{ 0xB6EF, 0xB6EF, 0xB6EF }, +{ 0xB6F0, 0xB6F0, 0xB6F0 }, +{ 0xB6F1, 0xB6F1, 0xB6F1 }, +{ 0xB6F2, 0xB6F2, 0xB6F2 }, +{ 0xB6F3, 0xB6F3, 0xB6F3 }, +{ 0xB6F4, 0xB6F4, 0xB6F4 }, +{ 0xB6F5, 0xB6F5, 0xB6F5 }, +{ 0xB6F6, 0xB6F6, 0xB6F6 }, +{ 0xB6F7, 0xB6F7, 0xB6F7 }, +{ 0xB6F8, 0xB6F8, 0xB6F8 }, +{ 0xB6F9, 0xB6F9, 0xB6F9 }, +{ 0xB6FA, 0xB6FA, 0xB6FA }, +{ 0xB6FB, 0xB6FB, 0xB6FB }, +{ 0xB6FC, 0xB6FC, 0xB6FC }, +{ 0xB6FD, 0xB6FD, 0xB6FD }, +{ 0xB6FE, 0xB6FE, 0xB6FE }, +{ 0xB6FF, 0xB6FF, 0xB6FF }, +{ 0xB700, 0xB700, 0xB700 }, +{ 0xB701, 0xB701, 0xB701 }, +{ 0xB702, 0xB702, 0xB702 }, +{ 0xB703, 0xB703, 0xB703 }, +{ 0xB704, 0xB704, 0xB704 }, +{ 0xB705, 0xB705, 0xB705 }, +{ 0xB706, 0xB706, 0xB706 }, +{ 0xB707, 0xB707, 0xB707 }, +{ 0xB708, 0xB708, 0xB708 }, +{ 0xB709, 0xB709, 0xB709 }, +{ 0xB70A, 0xB70A, 0xB70A }, +{ 0xB70B, 0xB70B, 0xB70B }, +{ 0xB70C, 0xB70C, 0xB70C }, +{ 0xB70D, 0xB70D, 0xB70D }, +{ 0xB70E, 0xB70E, 0xB70E }, +{ 0xB70F, 0xB70F, 0xB70F }, +{ 0xB710, 0xB710, 0xB710 }, +{ 0xB711, 0xB711, 0xB711 }, +{ 0xB712, 0xB712, 0xB712 }, +{ 0xB713, 0xB713, 0xB713 }, +{ 0xB714, 0xB714, 0xB714 }, +{ 0xB715, 0xB715, 0xB715 }, +{ 0xB716, 0xB716, 0xB716 }, +{ 0xB717, 0xB717, 0xB717 }, +{ 0xB718, 0xB718, 0xB718 }, +{ 0xB719, 0xB719, 0xB719 }, +{ 0xB71A, 0xB71A, 0xB71A }, +{ 0xB71B, 0xB71B, 0xB71B }, +{ 0xB71C, 0xB71C, 0xB71C }, +{ 0xB71D, 0xB71D, 0xB71D }, +{ 0xB71E, 0xB71E, 0xB71E }, +{ 0xB71F, 0xB71F, 0xB71F }, +{ 0xB720, 0xB720, 0xB720 }, +{ 0xB721, 0xB721, 0xB721 }, +{ 0xB722, 0xB722, 0xB722 }, +{ 0xB723, 0xB723, 0xB723 }, +{ 0xB724, 0xB724, 0xB724 }, +{ 0xB725, 0xB725, 0xB725 }, +{ 0xB726, 0xB726, 0xB726 }, +{ 0xB727, 0xB727, 0xB727 }, +{ 0xB728, 0xB728, 0xB728 }, +{ 0xB729, 0xB729, 0xB729 }, +{ 0xB72A, 0xB72A, 0xB72A }, +{ 0xB72B, 0xB72B, 0xB72B }, +{ 0xB72C, 0xB72C, 0xB72C }, +{ 0xB72D, 0xB72D, 0xB72D }, +{ 0xB72E, 0xB72E, 0xB72E }, +{ 0xB72F, 0xB72F, 0xB72F }, +{ 0xB730, 0xB730, 0xB730 }, +{ 0xB731, 0xB731, 0xB731 }, +{ 0xB732, 0xB732, 0xB732 }, +{ 0xB733, 0xB733, 0xB733 }, +{ 0xB734, 0xB734, 0xB734 }, +{ 0xB735, 0xB735, 0xB735 }, +{ 0xB736, 0xB736, 0xB736 }, +{ 0xB737, 0xB737, 0xB737 }, +{ 0xB738, 0xB738, 0xB738 }, +{ 0xB739, 0xB739, 0xB739 }, +{ 0xB73A, 0xB73A, 0xB73A }, +{ 0xB73B, 0xB73B, 0xB73B }, +{ 0xB73C, 0xB73C, 0xB73C }, +{ 0xB73D, 0xB73D, 0xB73D }, +{ 0xB73E, 0xB73E, 0xB73E }, +{ 0xB73F, 0xB73F, 0xB73F }, +{ 0xB740, 0xB740, 0xB740 }, +{ 0xB741, 0xB741, 0xB741 }, +{ 0xB742, 0xB742, 0xB742 }, +{ 0xB743, 0xB743, 0xB743 }, +{ 0xB744, 0xB744, 0xB744 }, +{ 0xB745, 0xB745, 0xB745 }, +{ 0xB746, 0xB746, 0xB746 }, +{ 0xB747, 0xB747, 0xB747 }, +{ 0xB748, 0xB748, 0xB748 }, +{ 0xB749, 0xB749, 0xB749 }, +{ 0xB74A, 0xB74A, 0xB74A }, +{ 0xB74B, 0xB74B, 0xB74B }, +{ 0xB74C, 0xB74C, 0xB74C }, +{ 0xB74D, 0xB74D, 0xB74D }, +{ 0xB74E, 0xB74E, 0xB74E }, +{ 0xB74F, 0xB74F, 0xB74F }, +{ 0xB750, 0xB750, 0xB750 }, +{ 0xB751, 0xB751, 0xB751 }, +{ 0xB752, 0xB752, 0xB752 }, +{ 0xB753, 0xB753, 0xB753 }, +{ 0xB754, 0xB754, 0xB754 }, +{ 0xB755, 0xB755, 0xB755 }, +{ 0xB756, 0xB756, 0xB756 }, +{ 0xB757, 0xB757, 0xB757 }, +{ 0xB758, 0xB758, 0xB758 }, +{ 0xB759, 0xB759, 0xB759 }, +{ 0xB75A, 0xB75A, 0xB75A }, +{ 0xB75B, 0xB75B, 0xB75B }, +{ 0xB75C, 0xB75C, 0xB75C }, +{ 0xB75D, 0xB75D, 0xB75D }, +{ 0xB75E, 0xB75E, 0xB75E }, +{ 0xB75F, 0xB75F, 0xB75F }, +{ 0xB760, 0xB760, 0xB760 }, +{ 0xB761, 0xB761, 0xB761 }, +{ 0xB762, 0xB762, 0xB762 }, +{ 0xB763, 0xB763, 0xB763 }, +{ 0xB764, 0xB764, 0xB764 }, +{ 0xB765, 0xB765, 0xB765 }, +{ 0xB766, 0xB766, 0xB766 }, +{ 0xB767, 0xB767, 0xB767 }, +{ 0xB768, 0xB768, 0xB768 }, +{ 0xB769, 0xB769, 0xB769 }, +{ 0xB76A, 0xB76A, 0xB76A }, +{ 0xB76B, 0xB76B, 0xB76B }, +{ 0xB76C, 0xB76C, 0xB76C }, +{ 0xB76D, 0xB76D, 0xB76D }, +{ 0xB76E, 0xB76E, 0xB76E }, +{ 0xB76F, 0xB76F, 0xB76F }, +{ 0xB770, 0xB770, 0xB770 }, +{ 0xB771, 0xB771, 0xB771 }, +{ 0xB772, 0xB772, 0xB772 }, +{ 0xB773, 0xB773, 0xB773 }, +{ 0xB774, 0xB774, 0xB774 }, +{ 0xB775, 0xB775, 0xB775 }, +{ 0xB776, 0xB776, 0xB776 }, +{ 0xB777, 0xB777, 0xB777 }, +{ 0xB778, 0xB778, 0xB778 }, +{ 0xB779, 0xB779, 0xB779 }, +{ 0xB77A, 0xB77A, 0xB77A }, +{ 0xB77B, 0xB77B, 0xB77B }, +{ 0xB77C, 0xB77C, 0xB77C }, +{ 0xB77D, 0xB77D, 0xB77D }, +{ 0xB77E, 0xB77E, 0xB77E }, +{ 0xB77F, 0xB77F, 0xB77F }, +{ 0xB780, 0xB780, 0xB780 }, +{ 0xB781, 0xB781, 0xB781 }, +{ 0xB782, 0xB782, 0xB782 }, +{ 0xB783, 0xB783, 0xB783 }, +{ 0xB784, 0xB784, 0xB784 }, +{ 0xB785, 0xB785, 0xB785 }, +{ 0xB786, 0xB786, 0xB786 }, +{ 0xB787, 0xB787, 0xB787 }, +{ 0xB788, 0xB788, 0xB788 }, +{ 0xB789, 0xB789, 0xB789 }, +{ 0xB78A, 0xB78A, 0xB78A }, +{ 0xB78B, 0xB78B, 0xB78B }, +{ 0xB78C, 0xB78C, 0xB78C }, +{ 0xB78D, 0xB78D, 0xB78D }, +{ 0xB78E, 0xB78E, 0xB78E }, +{ 0xB78F, 0xB78F, 0xB78F }, +{ 0xB790, 0xB790, 0xB790 }, +{ 0xB791, 0xB791, 0xB791 }, +{ 0xB792, 0xB792, 0xB792 }, +{ 0xB793, 0xB793, 0xB793 }, +{ 0xB794, 0xB794, 0xB794 }, +{ 0xB795, 0xB795, 0xB795 }, +{ 0xB796, 0xB796, 0xB796 }, +{ 0xB797, 0xB797, 0xB797 }, +{ 0xB798, 0xB798, 0xB798 }, +{ 0xB799, 0xB799, 0xB799 }, +{ 0xB79A, 0xB79A, 0xB79A }, +{ 0xB79B, 0xB79B, 0xB79B }, +{ 0xB79C, 0xB79C, 0xB79C }, +{ 0xB79D, 0xB79D, 0xB79D }, +{ 0xB79E, 0xB79E, 0xB79E }, +{ 0xB79F, 0xB79F, 0xB79F }, +{ 0xB7A0, 0xB7A0, 0xB7A0 }, +{ 0xB7A1, 0xB7A1, 0xB7A1 }, +{ 0xB7A2, 0xB7A2, 0xB7A2 }, +{ 0xB7A3, 0xB7A3, 0xB7A3 }, +{ 0xB7A4, 0xB7A4, 0xB7A4 }, +{ 0xB7A5, 0xB7A5, 0xB7A5 }, +{ 0xB7A6, 0xB7A6, 0xB7A6 }, +{ 0xB7A7, 0xB7A7, 0xB7A7 }, +{ 0xB7A8, 0xB7A8, 0xB7A8 }, +{ 0xB7A9, 0xB7A9, 0xB7A9 }, +{ 0xB7AA, 0xB7AA, 0xB7AA }, +{ 0xB7AB, 0xB7AB, 0xB7AB }, +{ 0xB7AC, 0xB7AC, 0xB7AC }, +{ 0xB7AD, 0xB7AD, 0xB7AD }, +{ 0xB7AE, 0xB7AE, 0xB7AE }, +{ 0xB7AF, 0xB7AF, 0xB7AF }, +{ 0xB7B0, 0xB7B0, 0xB7B0 }, +{ 0xB7B1, 0xB7B1, 0xB7B1 }, +{ 0xB7B2, 0xB7B2, 0xB7B2 }, +{ 0xB7B3, 0xB7B3, 0xB7B3 }, +{ 0xB7B4, 0xB7B4, 0xB7B4 }, +{ 0xB7B5, 0xB7B5, 0xB7B5 }, +{ 0xB7B6, 0xB7B6, 0xB7B6 }, +{ 0xB7B7, 0xB7B7, 0xB7B7 }, +{ 0xB7B8, 0xB7B8, 0xB7B8 }, +{ 0xB7B9, 0xB7B9, 0xB7B9 }, +{ 0xB7BA, 0xB7BA, 0xB7BA }, +{ 0xB7BB, 0xB7BB, 0xB7BB }, +{ 0xB7BC, 0xB7BC, 0xB7BC }, +{ 0xB7BD, 0xB7BD, 0xB7BD }, +{ 0xB7BE, 0xB7BE, 0xB7BE }, +{ 0xB7BF, 0xB7BF, 0xB7BF }, +{ 0xB7C0, 0xB7C0, 0xB7C0 }, +{ 0xB7C1, 0xB7C1, 0xB7C1 }, +{ 0xB7C2, 0xB7C2, 0xB7C2 }, +{ 0xB7C3, 0xB7C3, 0xB7C3 }, +{ 0xB7C4, 0xB7C4, 0xB7C4 }, +{ 0xB7C5, 0xB7C5, 0xB7C5 }, +{ 0xB7C6, 0xB7C6, 0xB7C6 }, +{ 0xB7C7, 0xB7C7, 0xB7C7 }, +{ 0xB7C8, 0xB7C8, 0xB7C8 }, +{ 0xB7C9, 0xB7C9, 0xB7C9 }, +{ 0xB7CA, 0xB7CA, 0xB7CA }, +{ 0xB7CB, 0xB7CB, 0xB7CB }, +{ 0xB7CC, 0xB7CC, 0xB7CC }, +{ 0xB7CD, 0xB7CD, 0xB7CD }, +{ 0xB7CE, 0xB7CE, 0xB7CE }, +{ 0xB7CF, 0xB7CF, 0xB7CF }, +{ 0xB7D0, 0xB7D0, 0xB7D0 }, +{ 0xB7D1, 0xB7D1, 0xB7D1 }, +{ 0xB7D2, 0xB7D2, 0xB7D2 }, +{ 0xB7D3, 0xB7D3, 0xB7D3 }, +{ 0xB7D4, 0xB7D4, 0xB7D4 }, +{ 0xB7D5, 0xB7D5, 0xB7D5 }, +{ 0xB7D6, 0xB7D6, 0xB7D6 }, +{ 0xB7D7, 0xB7D7, 0xB7D7 }, +{ 0xB7D8, 0xB7D8, 0xB7D8 }, +{ 0xB7D9, 0xB7D9, 0xB7D9 }, +{ 0xB7DA, 0xB7DA, 0xB7DA }, +{ 0xB7DB, 0xB7DB, 0xB7DB }, +{ 0xB7DC, 0xB7DC, 0xB7DC }, +{ 0xB7DD, 0xB7DD, 0xB7DD }, +{ 0xB7DE, 0xB7DE, 0xB7DE }, +{ 0xB7DF, 0xB7DF, 0xB7DF }, +{ 0xB7E0, 0xB7E0, 0xB7E0 }, +{ 0xB7E1, 0xB7E1, 0xB7E1 }, +{ 0xB7E2, 0xB7E2, 0xB7E2 }, +{ 0xB7E3, 0xB7E3, 0xB7E3 }, +{ 0xB7E4, 0xB7E4, 0xB7E4 }, +{ 0xB7E5, 0xB7E5, 0xB7E5 }, +{ 0xB7E6, 0xB7E6, 0xB7E6 }, +{ 0xB7E7, 0xB7E7, 0xB7E7 }, +{ 0xB7E8, 0xB7E8, 0xB7E8 }, +{ 0xB7E9, 0xB7E9, 0xB7E9 }, +{ 0xB7EA, 0xB7EA, 0xB7EA }, +{ 0xB7EB, 0xB7EB, 0xB7EB }, +{ 0xB7EC, 0xB7EC, 0xB7EC }, +{ 0xB7ED, 0xB7ED, 0xB7ED }, +{ 0xB7EE, 0xB7EE, 0xB7EE }, +{ 0xB7EF, 0xB7EF, 0xB7EF }, +{ 0xB7F0, 0xB7F0, 0xB7F0 }, +{ 0xB7F1, 0xB7F1, 0xB7F1 }, +{ 0xB7F2, 0xB7F2, 0xB7F2 }, +{ 0xB7F3, 0xB7F3, 0xB7F3 }, +{ 0xB7F4, 0xB7F4, 0xB7F4 }, +{ 0xB7F5, 0xB7F5, 0xB7F5 }, +{ 0xB7F6, 0xB7F6, 0xB7F6 }, +{ 0xB7F7, 0xB7F7, 0xB7F7 }, +{ 0xB7F8, 0xB7F8, 0xB7F8 }, +{ 0xB7F9, 0xB7F9, 0xB7F9 }, +{ 0xB7FA, 0xB7FA, 0xB7FA }, +{ 0xB7FB, 0xB7FB, 0xB7FB }, +{ 0xB7FC, 0xB7FC, 0xB7FC }, +{ 0xB7FD, 0xB7FD, 0xB7FD }, +{ 0xB7FE, 0xB7FE, 0xB7FE }, +{ 0xB7FF, 0xB7FF, 0xB7FF }, +{ 0xB800, 0xB800, 0xB800 }, +{ 0xB801, 0xB801, 0xB801 }, +{ 0xB802, 0xB802, 0xB802 }, +{ 0xB803, 0xB803, 0xB803 }, +{ 0xB804, 0xB804, 0xB804 }, +{ 0xB805, 0xB805, 0xB805 }, +{ 0xB806, 0xB806, 0xB806 }, +{ 0xB807, 0xB807, 0xB807 }, +{ 0xB808, 0xB808, 0xB808 }, +{ 0xB809, 0xB809, 0xB809 }, +{ 0xB80A, 0xB80A, 0xB80A }, +{ 0xB80B, 0xB80B, 0xB80B }, +{ 0xB80C, 0xB80C, 0xB80C }, +{ 0xB80D, 0xB80D, 0xB80D }, +{ 0xB80E, 0xB80E, 0xB80E }, +{ 0xB80F, 0xB80F, 0xB80F }, +{ 0xB810, 0xB810, 0xB810 }, +{ 0xB811, 0xB811, 0xB811 }, +{ 0xB812, 0xB812, 0xB812 }, +{ 0xB813, 0xB813, 0xB813 }, +{ 0xB814, 0xB814, 0xB814 }, +{ 0xB815, 0xB815, 0xB815 }, +{ 0xB816, 0xB816, 0xB816 }, +{ 0xB817, 0xB817, 0xB817 }, +{ 0xB818, 0xB818, 0xB818 }, +{ 0xB819, 0xB819, 0xB819 }, +{ 0xB81A, 0xB81A, 0xB81A }, +{ 0xB81B, 0xB81B, 0xB81B }, +{ 0xB81C, 0xB81C, 0xB81C }, +{ 0xB81D, 0xB81D, 0xB81D }, +{ 0xB81E, 0xB81E, 0xB81E }, +{ 0xB81F, 0xB81F, 0xB81F }, +{ 0xB820, 0xB820, 0xB820 }, +{ 0xB821, 0xB821, 0xB821 }, +{ 0xB822, 0xB822, 0xB822 }, +{ 0xB823, 0xB823, 0xB823 }, +{ 0xB824, 0xB824, 0xB824 }, +{ 0xB825, 0xB825, 0xB825 }, +{ 0xB826, 0xB826, 0xB826 }, +{ 0xB827, 0xB827, 0xB827 }, +{ 0xB828, 0xB828, 0xB828 }, +{ 0xB829, 0xB829, 0xB829 }, +{ 0xB82A, 0xB82A, 0xB82A }, +{ 0xB82B, 0xB82B, 0xB82B }, +{ 0xB82C, 0xB82C, 0xB82C }, +{ 0xB82D, 0xB82D, 0xB82D }, +{ 0xB82E, 0xB82E, 0xB82E }, +{ 0xB82F, 0xB82F, 0xB82F }, +{ 0xB830, 0xB830, 0xB830 }, +{ 0xB831, 0xB831, 0xB831 }, +{ 0xB832, 0xB832, 0xB832 }, +{ 0xB833, 0xB833, 0xB833 }, +{ 0xB834, 0xB834, 0xB834 }, +{ 0xB835, 0xB835, 0xB835 }, +{ 0xB836, 0xB836, 0xB836 }, +{ 0xB837, 0xB837, 0xB837 }, +{ 0xB838, 0xB838, 0xB838 }, +{ 0xB839, 0xB839, 0xB839 }, +{ 0xB83A, 0xB83A, 0xB83A }, +{ 0xB83B, 0xB83B, 0xB83B }, +{ 0xB83C, 0xB83C, 0xB83C }, +{ 0xB83D, 0xB83D, 0xB83D }, +{ 0xB83E, 0xB83E, 0xB83E }, +{ 0xB83F, 0xB83F, 0xB83F }, +{ 0xB840, 0xB840, 0xB840 }, +{ 0xB841, 0xB841, 0xB841 }, +{ 0xB842, 0xB842, 0xB842 }, +{ 0xB843, 0xB843, 0xB843 }, +{ 0xB844, 0xB844, 0xB844 }, +{ 0xB845, 0xB845, 0xB845 }, +{ 0xB846, 0xB846, 0xB846 }, +{ 0xB847, 0xB847, 0xB847 }, +{ 0xB848, 0xB848, 0xB848 }, +{ 0xB849, 0xB849, 0xB849 }, +{ 0xB84A, 0xB84A, 0xB84A }, +{ 0xB84B, 0xB84B, 0xB84B }, +{ 0xB84C, 0xB84C, 0xB84C }, +{ 0xB84D, 0xB84D, 0xB84D }, +{ 0xB84E, 0xB84E, 0xB84E }, +{ 0xB84F, 0xB84F, 0xB84F }, +{ 0xB850, 0xB850, 0xB850 }, +{ 0xB851, 0xB851, 0xB851 }, +{ 0xB852, 0xB852, 0xB852 }, +{ 0xB853, 0xB853, 0xB853 }, +{ 0xB854, 0xB854, 0xB854 }, +{ 0xB855, 0xB855, 0xB855 }, +{ 0xB856, 0xB856, 0xB856 }, +{ 0xB857, 0xB857, 0xB857 }, +{ 0xB858, 0xB858, 0xB858 }, +{ 0xB859, 0xB859, 0xB859 }, +{ 0xB85A, 0xB85A, 0xB85A }, +{ 0xB85B, 0xB85B, 0xB85B }, +{ 0xB85C, 0xB85C, 0xB85C }, +{ 0xB85D, 0xB85D, 0xB85D }, +{ 0xB85E, 0xB85E, 0xB85E }, +{ 0xB85F, 0xB85F, 0xB85F }, +{ 0xB860, 0xB860, 0xB860 }, +{ 0xB861, 0xB861, 0xB861 }, +{ 0xB862, 0xB862, 0xB862 }, +{ 0xB863, 0xB863, 0xB863 }, +{ 0xB864, 0xB864, 0xB864 }, +{ 0xB865, 0xB865, 0xB865 }, +{ 0xB866, 0xB866, 0xB866 }, +{ 0xB867, 0xB867, 0xB867 }, +{ 0xB868, 0xB868, 0xB868 }, +{ 0xB869, 0xB869, 0xB869 }, +{ 0xB86A, 0xB86A, 0xB86A }, +{ 0xB86B, 0xB86B, 0xB86B }, +{ 0xB86C, 0xB86C, 0xB86C }, +{ 0xB86D, 0xB86D, 0xB86D }, +{ 0xB86E, 0xB86E, 0xB86E }, +{ 0xB86F, 0xB86F, 0xB86F }, +{ 0xB870, 0xB870, 0xB870 }, +{ 0xB871, 0xB871, 0xB871 }, +{ 0xB872, 0xB872, 0xB872 }, +{ 0xB873, 0xB873, 0xB873 }, +{ 0xB874, 0xB874, 0xB874 }, +{ 0xB875, 0xB875, 0xB875 }, +{ 0xB876, 0xB876, 0xB876 }, +{ 0xB877, 0xB877, 0xB877 }, +{ 0xB878, 0xB878, 0xB878 }, +{ 0xB879, 0xB879, 0xB879 }, +{ 0xB87A, 0xB87A, 0xB87A }, +{ 0xB87B, 0xB87B, 0xB87B }, +{ 0xB87C, 0xB87C, 0xB87C }, +{ 0xB87D, 0xB87D, 0xB87D }, +{ 0xB87E, 0xB87E, 0xB87E }, +{ 0xB87F, 0xB87F, 0xB87F }, +{ 0xB880, 0xB880, 0xB880 }, +{ 0xB881, 0xB881, 0xB881 }, +{ 0xB882, 0xB882, 0xB882 }, +{ 0xB883, 0xB883, 0xB883 }, +{ 0xB884, 0xB884, 0xB884 }, +{ 0xB885, 0xB885, 0xB885 }, +{ 0xB886, 0xB886, 0xB886 }, +{ 0xB887, 0xB887, 0xB887 }, +{ 0xB888, 0xB888, 0xB888 }, +{ 0xB889, 0xB889, 0xB889 }, +{ 0xB88A, 0xB88A, 0xB88A }, +{ 0xB88B, 0xB88B, 0xB88B }, +{ 0xB88C, 0xB88C, 0xB88C }, +{ 0xB88D, 0xB88D, 0xB88D }, +{ 0xB88E, 0xB88E, 0xB88E }, +{ 0xB88F, 0xB88F, 0xB88F }, +{ 0xB890, 0xB890, 0xB890 }, +{ 0xB891, 0xB891, 0xB891 }, +{ 0xB892, 0xB892, 0xB892 }, +{ 0xB893, 0xB893, 0xB893 }, +{ 0xB894, 0xB894, 0xB894 }, +{ 0xB895, 0xB895, 0xB895 }, +{ 0xB896, 0xB896, 0xB896 }, +{ 0xB897, 0xB897, 0xB897 }, +{ 0xB898, 0xB898, 0xB898 }, +{ 0xB899, 0xB899, 0xB899 }, +{ 0xB89A, 0xB89A, 0xB89A }, +{ 0xB89B, 0xB89B, 0xB89B }, +{ 0xB89C, 0xB89C, 0xB89C }, +{ 0xB89D, 0xB89D, 0xB89D }, +{ 0xB89E, 0xB89E, 0xB89E }, +{ 0xB89F, 0xB89F, 0xB89F }, +{ 0xB8A0, 0xB8A0, 0xB8A0 }, +{ 0xB8A1, 0xB8A1, 0xB8A1 }, +{ 0xB8A2, 0xB8A2, 0xB8A2 }, +{ 0xB8A3, 0xB8A3, 0xB8A3 }, +{ 0xB8A4, 0xB8A4, 0xB8A4 }, +{ 0xB8A5, 0xB8A5, 0xB8A5 }, +{ 0xB8A6, 0xB8A6, 0xB8A6 }, +{ 0xB8A7, 0xB8A7, 0xB8A7 }, +{ 0xB8A8, 0xB8A8, 0xB8A8 }, +{ 0xB8A9, 0xB8A9, 0xB8A9 }, +{ 0xB8AA, 0xB8AA, 0xB8AA }, +{ 0xB8AB, 0xB8AB, 0xB8AB }, +{ 0xB8AC, 0xB8AC, 0xB8AC }, +{ 0xB8AD, 0xB8AD, 0xB8AD }, +{ 0xB8AE, 0xB8AE, 0xB8AE }, +{ 0xB8AF, 0xB8AF, 0xB8AF }, +{ 0xB8B0, 0xB8B0, 0xB8B0 }, +{ 0xB8B1, 0xB8B1, 0xB8B1 }, +{ 0xB8B2, 0xB8B2, 0xB8B2 }, +{ 0xB8B3, 0xB8B3, 0xB8B3 }, +{ 0xB8B4, 0xB8B4, 0xB8B4 }, +{ 0xB8B5, 0xB8B5, 0xB8B5 }, +{ 0xB8B6, 0xB8B6, 0xB8B6 }, +{ 0xB8B7, 0xB8B7, 0xB8B7 }, +{ 0xB8B8, 0xB8B8, 0xB8B8 }, +{ 0xB8B9, 0xB8B9, 0xB8B9 }, +{ 0xB8BA, 0xB8BA, 0xB8BA }, +{ 0xB8BB, 0xB8BB, 0xB8BB }, +{ 0xB8BC, 0xB8BC, 0xB8BC }, +{ 0xB8BD, 0xB8BD, 0xB8BD }, +{ 0xB8BE, 0xB8BE, 0xB8BE }, +{ 0xB8BF, 0xB8BF, 0xB8BF }, +{ 0xB8C0, 0xB8C0, 0xB8C0 }, +{ 0xB8C1, 0xB8C1, 0xB8C1 }, +{ 0xB8C2, 0xB8C2, 0xB8C2 }, +{ 0xB8C3, 0xB8C3, 0xB8C3 }, +{ 0xB8C4, 0xB8C4, 0xB8C4 }, +{ 0xB8C5, 0xB8C5, 0xB8C5 }, +{ 0xB8C6, 0xB8C6, 0xB8C6 }, +{ 0xB8C7, 0xB8C7, 0xB8C7 }, +{ 0xB8C8, 0xB8C8, 0xB8C8 }, +{ 0xB8C9, 0xB8C9, 0xB8C9 }, +{ 0xB8CA, 0xB8CA, 0xB8CA }, +{ 0xB8CB, 0xB8CB, 0xB8CB }, +{ 0xB8CC, 0xB8CC, 0xB8CC }, +{ 0xB8CD, 0xB8CD, 0xB8CD }, +{ 0xB8CE, 0xB8CE, 0xB8CE }, +{ 0xB8CF, 0xB8CF, 0xB8CF }, +{ 0xB8D0, 0xB8D0, 0xB8D0 }, +{ 0xB8D1, 0xB8D1, 0xB8D1 }, +{ 0xB8D2, 0xB8D2, 0xB8D2 }, +{ 0xB8D3, 0xB8D3, 0xB8D3 }, +{ 0xB8D4, 0xB8D4, 0xB8D4 }, +{ 0xB8D5, 0xB8D5, 0xB8D5 }, +{ 0xB8D6, 0xB8D6, 0xB8D6 }, +{ 0xB8D7, 0xB8D7, 0xB8D7 }, +{ 0xB8D8, 0xB8D8, 0xB8D8 }, +{ 0xB8D9, 0xB8D9, 0xB8D9 }, +{ 0xB8DA, 0xB8DA, 0xB8DA }, +{ 0xB8DB, 0xB8DB, 0xB8DB }, +{ 0xB8DC, 0xB8DC, 0xB8DC }, +{ 0xB8DD, 0xB8DD, 0xB8DD }, +{ 0xB8DE, 0xB8DE, 0xB8DE }, +{ 0xB8DF, 0xB8DF, 0xB8DF }, +{ 0xB8E0, 0xB8E0, 0xB8E0 }, +{ 0xB8E1, 0xB8E1, 0xB8E1 }, +{ 0xB8E2, 0xB8E2, 0xB8E2 }, +{ 0xB8E3, 0xB8E3, 0xB8E3 }, +{ 0xB8E4, 0xB8E4, 0xB8E4 }, +{ 0xB8E5, 0xB8E5, 0xB8E5 }, +{ 0xB8E6, 0xB8E6, 0xB8E6 }, +{ 0xB8E7, 0xB8E7, 0xB8E7 }, +{ 0xB8E8, 0xB8E8, 0xB8E8 }, +{ 0xB8E9, 0xB8E9, 0xB8E9 }, +{ 0xB8EA, 0xB8EA, 0xB8EA }, +{ 0xB8EB, 0xB8EB, 0xB8EB }, +{ 0xB8EC, 0xB8EC, 0xB8EC }, +{ 0xB8ED, 0xB8ED, 0xB8ED }, +{ 0xB8EE, 0xB8EE, 0xB8EE }, +{ 0xB8EF, 0xB8EF, 0xB8EF }, +{ 0xB8F0, 0xB8F0, 0xB8F0 }, +{ 0xB8F1, 0xB8F1, 0xB8F1 }, +{ 0xB8F2, 0xB8F2, 0xB8F2 }, +{ 0xB8F3, 0xB8F3, 0xB8F3 }, +{ 0xB8F4, 0xB8F4, 0xB8F4 }, +{ 0xB8F5, 0xB8F5, 0xB8F5 }, +{ 0xB8F6, 0xB8F6, 0xB8F6 }, +{ 0xB8F7, 0xB8F7, 0xB8F7 }, +{ 0xB8F8, 0xB8F8, 0xB8F8 }, +{ 0xB8F9, 0xB8F9, 0xB8F9 }, +{ 0xB8FA, 0xB8FA, 0xB8FA }, +{ 0xB8FB, 0xB8FB, 0xB8FB }, +{ 0xB8FC, 0xB8FC, 0xB8FC }, +{ 0xB8FD, 0xB8FD, 0xB8FD }, +{ 0xB8FE, 0xB8FE, 0xB8FE }, +{ 0xB8FF, 0xB8FF, 0xB8FF }, +{ 0xB900, 0xB900, 0xB900 }, +{ 0xB901, 0xB901, 0xB901 }, +{ 0xB902, 0xB902, 0xB902 }, +{ 0xB903, 0xB903, 0xB903 }, +{ 0xB904, 0xB904, 0xB904 }, +{ 0xB905, 0xB905, 0xB905 }, +{ 0xB906, 0xB906, 0xB906 }, +{ 0xB907, 0xB907, 0xB907 }, +{ 0xB908, 0xB908, 0xB908 }, +{ 0xB909, 0xB909, 0xB909 }, +{ 0xB90A, 0xB90A, 0xB90A }, +{ 0xB90B, 0xB90B, 0xB90B }, +{ 0xB90C, 0xB90C, 0xB90C }, +{ 0xB90D, 0xB90D, 0xB90D }, +{ 0xB90E, 0xB90E, 0xB90E }, +{ 0xB90F, 0xB90F, 0xB90F }, +{ 0xB910, 0xB910, 0xB910 }, +{ 0xB911, 0xB911, 0xB911 }, +{ 0xB912, 0xB912, 0xB912 }, +{ 0xB913, 0xB913, 0xB913 }, +{ 0xB914, 0xB914, 0xB914 }, +{ 0xB915, 0xB915, 0xB915 }, +{ 0xB916, 0xB916, 0xB916 }, +{ 0xB917, 0xB917, 0xB917 }, +{ 0xB918, 0xB918, 0xB918 }, +{ 0xB919, 0xB919, 0xB919 }, +{ 0xB91A, 0xB91A, 0xB91A }, +{ 0xB91B, 0xB91B, 0xB91B }, +{ 0xB91C, 0xB91C, 0xB91C }, +{ 0xB91D, 0xB91D, 0xB91D }, +{ 0xB91E, 0xB91E, 0xB91E }, +{ 0xB91F, 0xB91F, 0xB91F }, +{ 0xB920, 0xB920, 0xB920 }, +{ 0xB921, 0xB921, 0xB921 }, +{ 0xB922, 0xB922, 0xB922 }, +{ 0xB923, 0xB923, 0xB923 }, +{ 0xB924, 0xB924, 0xB924 }, +{ 0xB925, 0xB925, 0xB925 }, +{ 0xB926, 0xB926, 0xB926 }, +{ 0xB927, 0xB927, 0xB927 }, +{ 0xB928, 0xB928, 0xB928 }, +{ 0xB929, 0xB929, 0xB929 }, +{ 0xB92A, 0xB92A, 0xB92A }, +{ 0xB92B, 0xB92B, 0xB92B }, +{ 0xB92C, 0xB92C, 0xB92C }, +{ 0xB92D, 0xB92D, 0xB92D }, +{ 0xB92E, 0xB92E, 0xB92E }, +{ 0xB92F, 0xB92F, 0xB92F }, +{ 0xB930, 0xB930, 0xB930 }, +{ 0xB931, 0xB931, 0xB931 }, +{ 0xB932, 0xB932, 0xB932 }, +{ 0xB933, 0xB933, 0xB933 }, +{ 0xB934, 0xB934, 0xB934 }, +{ 0xB935, 0xB935, 0xB935 }, +{ 0xB936, 0xB936, 0xB936 }, +{ 0xB937, 0xB937, 0xB937 }, +{ 0xB938, 0xB938, 0xB938 }, +{ 0xB939, 0xB939, 0xB939 }, +{ 0xB93A, 0xB93A, 0xB93A }, +{ 0xB93B, 0xB93B, 0xB93B }, +{ 0xB93C, 0xB93C, 0xB93C }, +{ 0xB93D, 0xB93D, 0xB93D }, +{ 0xB93E, 0xB93E, 0xB93E }, +{ 0xB93F, 0xB93F, 0xB93F }, +{ 0xB940, 0xB940, 0xB940 }, +{ 0xB941, 0xB941, 0xB941 }, +{ 0xB942, 0xB942, 0xB942 }, +{ 0xB943, 0xB943, 0xB943 }, +{ 0xB944, 0xB944, 0xB944 }, +{ 0xB945, 0xB945, 0xB945 }, +{ 0xB946, 0xB946, 0xB946 }, +{ 0xB947, 0xB947, 0xB947 }, +{ 0xB948, 0xB948, 0xB948 }, +{ 0xB949, 0xB949, 0xB949 }, +{ 0xB94A, 0xB94A, 0xB94A }, +{ 0xB94B, 0xB94B, 0xB94B }, +{ 0xB94C, 0xB94C, 0xB94C }, +{ 0xB94D, 0xB94D, 0xB94D }, +{ 0xB94E, 0xB94E, 0xB94E }, +{ 0xB94F, 0xB94F, 0xB94F }, +{ 0xB950, 0xB950, 0xB950 }, +{ 0xB951, 0xB951, 0xB951 }, +{ 0xB952, 0xB952, 0xB952 }, +{ 0xB953, 0xB953, 0xB953 }, +{ 0xB954, 0xB954, 0xB954 }, +{ 0xB955, 0xB955, 0xB955 }, +{ 0xB956, 0xB956, 0xB956 }, +{ 0xB957, 0xB957, 0xB957 }, +{ 0xB958, 0xB958, 0xB958 }, +{ 0xB959, 0xB959, 0xB959 }, +{ 0xB95A, 0xB95A, 0xB95A }, +{ 0xB95B, 0xB95B, 0xB95B }, +{ 0xB95C, 0xB95C, 0xB95C }, +{ 0xB95D, 0xB95D, 0xB95D }, +{ 0xB95E, 0xB95E, 0xB95E }, +{ 0xB95F, 0xB95F, 0xB95F }, +{ 0xB960, 0xB960, 0xB960 }, +{ 0xB961, 0xB961, 0xB961 }, +{ 0xB962, 0xB962, 0xB962 }, +{ 0xB963, 0xB963, 0xB963 }, +{ 0xB964, 0xB964, 0xB964 }, +{ 0xB965, 0xB965, 0xB965 }, +{ 0xB966, 0xB966, 0xB966 }, +{ 0xB967, 0xB967, 0xB967 }, +{ 0xB968, 0xB968, 0xB968 }, +{ 0xB969, 0xB969, 0xB969 }, +{ 0xB96A, 0xB96A, 0xB96A }, +{ 0xB96B, 0xB96B, 0xB96B }, +{ 0xB96C, 0xB96C, 0xB96C }, +{ 0xB96D, 0xB96D, 0xB96D }, +{ 0xB96E, 0xB96E, 0xB96E }, +{ 0xB96F, 0xB96F, 0xB96F }, +{ 0xB970, 0xB970, 0xB970 }, +{ 0xB971, 0xB971, 0xB971 }, +{ 0xB972, 0xB972, 0xB972 }, +{ 0xB973, 0xB973, 0xB973 }, +{ 0xB974, 0xB974, 0xB974 }, +{ 0xB975, 0xB975, 0xB975 }, +{ 0xB976, 0xB976, 0xB976 }, +{ 0xB977, 0xB977, 0xB977 }, +{ 0xB978, 0xB978, 0xB978 }, +{ 0xB979, 0xB979, 0xB979 }, +{ 0xB97A, 0xB97A, 0xB97A }, +{ 0xB97B, 0xB97B, 0xB97B }, +{ 0xB97C, 0xB97C, 0xB97C }, +{ 0xB97D, 0xB97D, 0xB97D }, +{ 0xB97E, 0xB97E, 0xB97E }, +{ 0xB97F, 0xB97F, 0xB97F }, +{ 0xB980, 0xB980, 0xB980 }, +{ 0xB981, 0xB981, 0xB981 }, +{ 0xB982, 0xB982, 0xB982 }, +{ 0xB983, 0xB983, 0xB983 }, +{ 0xB984, 0xB984, 0xB984 }, +{ 0xB985, 0xB985, 0xB985 }, +{ 0xB986, 0xB986, 0xB986 }, +{ 0xB987, 0xB987, 0xB987 }, +{ 0xB988, 0xB988, 0xB988 }, +{ 0xB989, 0xB989, 0xB989 }, +{ 0xB98A, 0xB98A, 0xB98A }, +{ 0xB98B, 0xB98B, 0xB98B }, +{ 0xB98C, 0xB98C, 0xB98C }, +{ 0xB98D, 0xB98D, 0xB98D }, +{ 0xB98E, 0xB98E, 0xB98E }, +{ 0xB98F, 0xB98F, 0xB98F }, +{ 0xB990, 0xB990, 0xB990 }, +{ 0xB991, 0xB991, 0xB991 }, +{ 0xB992, 0xB992, 0xB992 }, +{ 0xB993, 0xB993, 0xB993 }, +{ 0xB994, 0xB994, 0xB994 }, +{ 0xB995, 0xB995, 0xB995 }, +{ 0xB996, 0xB996, 0xB996 }, +{ 0xB997, 0xB997, 0xB997 }, +{ 0xB998, 0xB998, 0xB998 }, +{ 0xB999, 0xB999, 0xB999 }, +{ 0xB99A, 0xB99A, 0xB99A }, +{ 0xB99B, 0xB99B, 0xB99B }, +{ 0xB99C, 0xB99C, 0xB99C }, +{ 0xB99D, 0xB99D, 0xB99D }, +{ 0xB99E, 0xB99E, 0xB99E }, +{ 0xB99F, 0xB99F, 0xB99F }, +{ 0xB9A0, 0xB9A0, 0xB9A0 }, +{ 0xB9A1, 0xB9A1, 0xB9A1 }, +{ 0xB9A2, 0xB9A2, 0xB9A2 }, +{ 0xB9A3, 0xB9A3, 0xB9A3 }, +{ 0xB9A4, 0xB9A4, 0xB9A4 }, +{ 0xB9A5, 0xB9A5, 0xB9A5 }, +{ 0xB9A6, 0xB9A6, 0xB9A6 }, +{ 0xB9A7, 0xB9A7, 0xB9A7 }, +{ 0xB9A8, 0xB9A8, 0xB9A8 }, +{ 0xB9A9, 0xB9A9, 0xB9A9 }, +{ 0xB9AA, 0xB9AA, 0xB9AA }, +{ 0xB9AB, 0xB9AB, 0xB9AB }, +{ 0xB9AC, 0xB9AC, 0xB9AC }, +{ 0xB9AD, 0xB9AD, 0xB9AD }, +{ 0xB9AE, 0xB9AE, 0xB9AE }, +{ 0xB9AF, 0xB9AF, 0xB9AF }, +{ 0xB9B0, 0xB9B0, 0xB9B0 }, +{ 0xB9B1, 0xB9B1, 0xB9B1 }, +{ 0xB9B2, 0xB9B2, 0xB9B2 }, +{ 0xB9B3, 0xB9B3, 0xB9B3 }, +{ 0xB9B4, 0xB9B4, 0xB9B4 }, +{ 0xB9B5, 0xB9B5, 0xB9B5 }, +{ 0xB9B6, 0xB9B6, 0xB9B6 }, +{ 0xB9B7, 0xB9B7, 0xB9B7 }, +{ 0xB9B8, 0xB9B8, 0xB9B8 }, +{ 0xB9B9, 0xB9B9, 0xB9B9 }, +{ 0xB9BA, 0xB9BA, 0xB9BA }, +{ 0xB9BB, 0xB9BB, 0xB9BB }, +{ 0xB9BC, 0xB9BC, 0xB9BC }, +{ 0xB9BD, 0xB9BD, 0xB9BD }, +{ 0xB9BE, 0xB9BE, 0xB9BE }, +{ 0xB9BF, 0xB9BF, 0xB9BF }, +{ 0xB9C0, 0xB9C0, 0xB9C0 }, +{ 0xB9C1, 0xB9C1, 0xB9C1 }, +{ 0xB9C2, 0xB9C2, 0xB9C2 }, +{ 0xB9C3, 0xB9C3, 0xB9C3 }, +{ 0xB9C4, 0xB9C4, 0xB9C4 }, +{ 0xB9C5, 0xB9C5, 0xB9C5 }, +{ 0xB9C6, 0xB9C6, 0xB9C6 }, +{ 0xB9C7, 0xB9C7, 0xB9C7 }, +{ 0xB9C8, 0xB9C8, 0xB9C8 }, +{ 0xB9C9, 0xB9C9, 0xB9C9 }, +{ 0xB9CA, 0xB9CA, 0xB9CA }, +{ 0xB9CB, 0xB9CB, 0xB9CB }, +{ 0xB9CC, 0xB9CC, 0xB9CC }, +{ 0xB9CD, 0xB9CD, 0xB9CD }, +{ 0xB9CE, 0xB9CE, 0xB9CE }, +{ 0xB9CF, 0xB9CF, 0xB9CF }, +{ 0xB9D0, 0xB9D0, 0xB9D0 }, +{ 0xB9D1, 0xB9D1, 0xB9D1 }, +{ 0xB9D2, 0xB9D2, 0xB9D2 }, +{ 0xB9D3, 0xB9D3, 0xB9D3 }, +{ 0xB9D4, 0xB9D4, 0xB9D4 }, +{ 0xB9D5, 0xB9D5, 0xB9D5 }, +{ 0xB9D6, 0xB9D6, 0xB9D6 }, +{ 0xB9D7, 0xB9D7, 0xB9D7 }, +{ 0xB9D8, 0xB9D8, 0xB9D8 }, +{ 0xB9D9, 0xB9D9, 0xB9D9 }, +{ 0xB9DA, 0xB9DA, 0xB9DA }, +{ 0xB9DB, 0xB9DB, 0xB9DB }, +{ 0xB9DC, 0xB9DC, 0xB9DC }, +{ 0xB9DD, 0xB9DD, 0xB9DD }, +{ 0xB9DE, 0xB9DE, 0xB9DE }, +{ 0xB9DF, 0xB9DF, 0xB9DF }, +{ 0xB9E0, 0xB9E0, 0xB9E0 }, +{ 0xB9E1, 0xB9E1, 0xB9E1 }, +{ 0xB9E2, 0xB9E2, 0xB9E2 }, +{ 0xB9E3, 0xB9E3, 0xB9E3 }, +{ 0xB9E4, 0xB9E4, 0xB9E4 }, +{ 0xB9E5, 0xB9E5, 0xB9E5 }, +{ 0xB9E6, 0xB9E6, 0xB9E6 }, +{ 0xB9E7, 0xB9E7, 0xB9E7 }, +{ 0xB9E8, 0xB9E8, 0xB9E8 }, +{ 0xB9E9, 0xB9E9, 0xB9E9 }, +{ 0xB9EA, 0xB9EA, 0xB9EA }, +{ 0xB9EB, 0xB9EB, 0xB9EB }, +{ 0xB9EC, 0xB9EC, 0xB9EC }, +{ 0xB9ED, 0xB9ED, 0xB9ED }, +{ 0xB9EE, 0xB9EE, 0xB9EE }, +{ 0xB9EF, 0xB9EF, 0xB9EF }, +{ 0xB9F0, 0xB9F0, 0xB9F0 }, +{ 0xB9F1, 0xB9F1, 0xB9F1 }, +{ 0xB9F2, 0xB9F2, 0xB9F2 }, +{ 0xB9F3, 0xB9F3, 0xB9F3 }, +{ 0xB9F4, 0xB9F4, 0xB9F4 }, +{ 0xB9F5, 0xB9F5, 0xB9F5 }, +{ 0xB9F6, 0xB9F6, 0xB9F6 }, +{ 0xB9F7, 0xB9F7, 0xB9F7 }, +{ 0xB9F8, 0xB9F8, 0xB9F8 }, +{ 0xB9F9, 0xB9F9, 0xB9F9 }, +{ 0xB9FA, 0xB9FA, 0xB9FA }, +{ 0xB9FB, 0xB9FB, 0xB9FB }, +{ 0xB9FC, 0xB9FC, 0xB9FC }, +{ 0xB9FD, 0xB9FD, 0xB9FD }, +{ 0xB9FE, 0xB9FE, 0xB9FE }, +{ 0xB9FF, 0xB9FF, 0xB9FF }, +{ 0xBA00, 0xBA00, 0xBA00 }, +{ 0xBA01, 0xBA01, 0xBA01 }, +{ 0xBA02, 0xBA02, 0xBA02 }, +{ 0xBA03, 0xBA03, 0xBA03 }, +{ 0xBA04, 0xBA04, 0xBA04 }, +{ 0xBA05, 0xBA05, 0xBA05 }, +{ 0xBA06, 0xBA06, 0xBA06 }, +{ 0xBA07, 0xBA07, 0xBA07 }, +{ 0xBA08, 0xBA08, 0xBA08 }, +{ 0xBA09, 0xBA09, 0xBA09 }, +{ 0xBA0A, 0xBA0A, 0xBA0A }, +{ 0xBA0B, 0xBA0B, 0xBA0B }, +{ 0xBA0C, 0xBA0C, 0xBA0C }, +{ 0xBA0D, 0xBA0D, 0xBA0D }, +{ 0xBA0E, 0xBA0E, 0xBA0E }, +{ 0xBA0F, 0xBA0F, 0xBA0F }, +{ 0xBA10, 0xBA10, 0xBA10 }, +{ 0xBA11, 0xBA11, 0xBA11 }, +{ 0xBA12, 0xBA12, 0xBA12 }, +{ 0xBA13, 0xBA13, 0xBA13 }, +{ 0xBA14, 0xBA14, 0xBA14 }, +{ 0xBA15, 0xBA15, 0xBA15 }, +{ 0xBA16, 0xBA16, 0xBA16 }, +{ 0xBA17, 0xBA17, 0xBA17 }, +{ 0xBA18, 0xBA18, 0xBA18 }, +{ 0xBA19, 0xBA19, 0xBA19 }, +{ 0xBA1A, 0xBA1A, 0xBA1A }, +{ 0xBA1B, 0xBA1B, 0xBA1B }, +{ 0xBA1C, 0xBA1C, 0xBA1C }, +{ 0xBA1D, 0xBA1D, 0xBA1D }, +{ 0xBA1E, 0xBA1E, 0xBA1E }, +{ 0xBA1F, 0xBA1F, 0xBA1F }, +{ 0xBA20, 0xBA20, 0xBA20 }, +{ 0xBA21, 0xBA21, 0xBA21 }, +{ 0xBA22, 0xBA22, 0xBA22 }, +{ 0xBA23, 0xBA23, 0xBA23 }, +{ 0xBA24, 0xBA24, 0xBA24 }, +{ 0xBA25, 0xBA25, 0xBA25 }, +{ 0xBA26, 0xBA26, 0xBA26 }, +{ 0xBA27, 0xBA27, 0xBA27 }, +{ 0xBA28, 0xBA28, 0xBA28 }, +{ 0xBA29, 0xBA29, 0xBA29 }, +{ 0xBA2A, 0xBA2A, 0xBA2A }, +{ 0xBA2B, 0xBA2B, 0xBA2B }, +{ 0xBA2C, 0xBA2C, 0xBA2C }, +{ 0xBA2D, 0xBA2D, 0xBA2D }, +{ 0xBA2E, 0xBA2E, 0xBA2E }, +{ 0xBA2F, 0xBA2F, 0xBA2F }, +{ 0xBA30, 0xBA30, 0xBA30 }, +{ 0xBA31, 0xBA31, 0xBA31 }, +{ 0xBA32, 0xBA32, 0xBA32 }, +{ 0xBA33, 0xBA33, 0xBA33 }, +{ 0xBA34, 0xBA34, 0xBA34 }, +{ 0xBA35, 0xBA35, 0xBA35 }, +{ 0xBA36, 0xBA36, 0xBA36 }, +{ 0xBA37, 0xBA37, 0xBA37 }, +{ 0xBA38, 0xBA38, 0xBA38 }, +{ 0xBA39, 0xBA39, 0xBA39 }, +{ 0xBA3A, 0xBA3A, 0xBA3A }, +{ 0xBA3B, 0xBA3B, 0xBA3B }, +{ 0xBA3C, 0xBA3C, 0xBA3C }, +{ 0xBA3D, 0xBA3D, 0xBA3D }, +{ 0xBA3E, 0xBA3E, 0xBA3E }, +{ 0xBA3F, 0xBA3F, 0xBA3F }, +{ 0xBA40, 0xBA40, 0xBA40 }, +{ 0xBA41, 0xBA41, 0xBA41 }, +{ 0xBA42, 0xBA42, 0xBA42 }, +{ 0xBA43, 0xBA43, 0xBA43 }, +{ 0xBA44, 0xBA44, 0xBA44 }, +{ 0xBA45, 0xBA45, 0xBA45 }, +{ 0xBA46, 0xBA46, 0xBA46 }, +{ 0xBA47, 0xBA47, 0xBA47 }, +{ 0xBA48, 0xBA48, 0xBA48 }, +{ 0xBA49, 0xBA49, 0xBA49 }, +{ 0xBA4A, 0xBA4A, 0xBA4A }, +{ 0xBA4B, 0xBA4B, 0xBA4B }, +{ 0xBA4C, 0xBA4C, 0xBA4C }, +{ 0xBA4D, 0xBA4D, 0xBA4D }, +{ 0xBA4E, 0xBA4E, 0xBA4E }, +{ 0xBA4F, 0xBA4F, 0xBA4F }, +{ 0xBA50, 0xBA50, 0xBA50 }, +{ 0xBA51, 0xBA51, 0xBA51 }, +{ 0xBA52, 0xBA52, 0xBA52 }, +{ 0xBA53, 0xBA53, 0xBA53 }, +{ 0xBA54, 0xBA54, 0xBA54 }, +{ 0xBA55, 0xBA55, 0xBA55 }, +{ 0xBA56, 0xBA56, 0xBA56 }, +{ 0xBA57, 0xBA57, 0xBA57 }, +{ 0xBA58, 0xBA58, 0xBA58 }, +{ 0xBA59, 0xBA59, 0xBA59 }, +{ 0xBA5A, 0xBA5A, 0xBA5A }, +{ 0xBA5B, 0xBA5B, 0xBA5B }, +{ 0xBA5C, 0xBA5C, 0xBA5C }, +{ 0xBA5D, 0xBA5D, 0xBA5D }, +{ 0xBA5E, 0xBA5E, 0xBA5E }, +{ 0xBA5F, 0xBA5F, 0xBA5F }, +{ 0xBA60, 0xBA60, 0xBA60 }, +{ 0xBA61, 0xBA61, 0xBA61 }, +{ 0xBA62, 0xBA62, 0xBA62 }, +{ 0xBA63, 0xBA63, 0xBA63 }, +{ 0xBA64, 0xBA64, 0xBA64 }, +{ 0xBA65, 0xBA65, 0xBA65 }, +{ 0xBA66, 0xBA66, 0xBA66 }, +{ 0xBA67, 0xBA67, 0xBA67 }, +{ 0xBA68, 0xBA68, 0xBA68 }, +{ 0xBA69, 0xBA69, 0xBA69 }, +{ 0xBA6A, 0xBA6A, 0xBA6A }, +{ 0xBA6B, 0xBA6B, 0xBA6B }, +{ 0xBA6C, 0xBA6C, 0xBA6C }, +{ 0xBA6D, 0xBA6D, 0xBA6D }, +{ 0xBA6E, 0xBA6E, 0xBA6E }, +{ 0xBA6F, 0xBA6F, 0xBA6F }, +{ 0xBA70, 0xBA70, 0xBA70 }, +{ 0xBA71, 0xBA71, 0xBA71 }, +{ 0xBA72, 0xBA72, 0xBA72 }, +{ 0xBA73, 0xBA73, 0xBA73 }, +{ 0xBA74, 0xBA74, 0xBA74 }, +{ 0xBA75, 0xBA75, 0xBA75 }, +{ 0xBA76, 0xBA76, 0xBA76 }, +{ 0xBA77, 0xBA77, 0xBA77 }, +{ 0xBA78, 0xBA78, 0xBA78 }, +{ 0xBA79, 0xBA79, 0xBA79 }, +{ 0xBA7A, 0xBA7A, 0xBA7A }, +{ 0xBA7B, 0xBA7B, 0xBA7B }, +{ 0xBA7C, 0xBA7C, 0xBA7C }, +{ 0xBA7D, 0xBA7D, 0xBA7D }, +{ 0xBA7E, 0xBA7E, 0xBA7E }, +{ 0xBA7F, 0xBA7F, 0xBA7F }, +{ 0xBA80, 0xBA80, 0xBA80 }, +{ 0xBA81, 0xBA81, 0xBA81 }, +{ 0xBA82, 0xBA82, 0xBA82 }, +{ 0xBA83, 0xBA83, 0xBA83 }, +{ 0xBA84, 0xBA84, 0xBA84 }, +{ 0xBA85, 0xBA85, 0xBA85 }, +{ 0xBA86, 0xBA86, 0xBA86 }, +{ 0xBA87, 0xBA87, 0xBA87 }, +{ 0xBA88, 0xBA88, 0xBA88 }, +{ 0xBA89, 0xBA89, 0xBA89 }, +{ 0xBA8A, 0xBA8A, 0xBA8A }, +{ 0xBA8B, 0xBA8B, 0xBA8B }, +{ 0xBA8C, 0xBA8C, 0xBA8C }, +{ 0xBA8D, 0xBA8D, 0xBA8D }, +{ 0xBA8E, 0xBA8E, 0xBA8E }, +{ 0xBA8F, 0xBA8F, 0xBA8F }, +{ 0xBA90, 0xBA90, 0xBA90 }, +{ 0xBA91, 0xBA91, 0xBA91 }, +{ 0xBA92, 0xBA92, 0xBA92 }, +{ 0xBA93, 0xBA93, 0xBA93 }, +{ 0xBA94, 0xBA94, 0xBA94 }, +{ 0xBA95, 0xBA95, 0xBA95 }, +{ 0xBA96, 0xBA96, 0xBA96 }, +{ 0xBA97, 0xBA97, 0xBA97 }, +{ 0xBA98, 0xBA98, 0xBA98 }, +{ 0xBA99, 0xBA99, 0xBA99 }, +{ 0xBA9A, 0xBA9A, 0xBA9A }, +{ 0xBA9B, 0xBA9B, 0xBA9B }, +{ 0xBA9C, 0xBA9C, 0xBA9C }, +{ 0xBA9D, 0xBA9D, 0xBA9D }, +{ 0xBA9E, 0xBA9E, 0xBA9E }, +{ 0xBA9F, 0xBA9F, 0xBA9F }, +{ 0xBAA0, 0xBAA0, 0xBAA0 }, +{ 0xBAA1, 0xBAA1, 0xBAA1 }, +{ 0xBAA2, 0xBAA2, 0xBAA2 }, +{ 0xBAA3, 0xBAA3, 0xBAA3 }, +{ 0xBAA4, 0xBAA4, 0xBAA4 }, +{ 0xBAA5, 0xBAA5, 0xBAA5 }, +{ 0xBAA6, 0xBAA6, 0xBAA6 }, +{ 0xBAA7, 0xBAA7, 0xBAA7 }, +{ 0xBAA8, 0xBAA8, 0xBAA8 }, +{ 0xBAA9, 0xBAA9, 0xBAA9 }, +{ 0xBAAA, 0xBAAA, 0xBAAA }, +{ 0xBAAB, 0xBAAB, 0xBAAB }, +{ 0xBAAC, 0xBAAC, 0xBAAC }, +{ 0xBAAD, 0xBAAD, 0xBAAD }, +{ 0xBAAE, 0xBAAE, 0xBAAE }, +{ 0xBAAF, 0xBAAF, 0xBAAF }, +{ 0xBAB0, 0xBAB0, 0xBAB0 }, +{ 0xBAB1, 0xBAB1, 0xBAB1 }, +{ 0xBAB2, 0xBAB2, 0xBAB2 }, +{ 0xBAB3, 0xBAB3, 0xBAB3 }, +{ 0xBAB4, 0xBAB4, 0xBAB4 }, +{ 0xBAB5, 0xBAB5, 0xBAB5 }, +{ 0xBAB6, 0xBAB6, 0xBAB6 }, +{ 0xBAB7, 0xBAB7, 0xBAB7 }, +{ 0xBAB8, 0xBAB8, 0xBAB8 }, +{ 0xBAB9, 0xBAB9, 0xBAB9 }, +{ 0xBABA, 0xBABA, 0xBABA }, +{ 0xBABB, 0xBABB, 0xBABB }, +{ 0xBABC, 0xBABC, 0xBABC }, +{ 0xBABD, 0xBABD, 0xBABD }, +{ 0xBABE, 0xBABE, 0xBABE }, +{ 0xBABF, 0xBABF, 0xBABF }, +{ 0xBAC0, 0xBAC0, 0xBAC0 }, +{ 0xBAC1, 0xBAC1, 0xBAC1 }, +{ 0xBAC2, 0xBAC2, 0xBAC2 }, +{ 0xBAC3, 0xBAC3, 0xBAC3 }, +{ 0xBAC4, 0xBAC4, 0xBAC4 }, +{ 0xBAC5, 0xBAC5, 0xBAC5 }, +{ 0xBAC6, 0xBAC6, 0xBAC6 }, +{ 0xBAC7, 0xBAC7, 0xBAC7 }, +{ 0xBAC8, 0xBAC8, 0xBAC8 }, +{ 0xBAC9, 0xBAC9, 0xBAC9 }, +{ 0xBACA, 0xBACA, 0xBACA }, +{ 0xBACB, 0xBACB, 0xBACB }, +{ 0xBACC, 0xBACC, 0xBACC }, +{ 0xBACD, 0xBACD, 0xBACD }, +{ 0xBACE, 0xBACE, 0xBACE }, +{ 0xBACF, 0xBACF, 0xBACF }, +{ 0xBAD0, 0xBAD0, 0xBAD0 }, +{ 0xBAD1, 0xBAD1, 0xBAD1 }, +{ 0xBAD2, 0xBAD2, 0xBAD2 }, +{ 0xBAD3, 0xBAD3, 0xBAD3 }, +{ 0xBAD4, 0xBAD4, 0xBAD4 }, +{ 0xBAD5, 0xBAD5, 0xBAD5 }, +{ 0xBAD6, 0xBAD6, 0xBAD6 }, +{ 0xBAD7, 0xBAD7, 0xBAD7 }, +{ 0xBAD8, 0xBAD8, 0xBAD8 }, +{ 0xBAD9, 0xBAD9, 0xBAD9 }, +{ 0xBADA, 0xBADA, 0xBADA }, +{ 0xBADB, 0xBADB, 0xBADB }, +{ 0xBADC, 0xBADC, 0xBADC }, +{ 0xBADD, 0xBADD, 0xBADD }, +{ 0xBADE, 0xBADE, 0xBADE }, +{ 0xBADF, 0xBADF, 0xBADF }, +{ 0xBAE0, 0xBAE0, 0xBAE0 }, +{ 0xBAE1, 0xBAE1, 0xBAE1 }, +{ 0xBAE2, 0xBAE2, 0xBAE2 }, +{ 0xBAE3, 0xBAE3, 0xBAE3 }, +{ 0xBAE4, 0xBAE4, 0xBAE4 }, +{ 0xBAE5, 0xBAE5, 0xBAE5 }, +{ 0xBAE6, 0xBAE6, 0xBAE6 }, +{ 0xBAE7, 0xBAE7, 0xBAE7 }, +{ 0xBAE8, 0xBAE8, 0xBAE8 }, +{ 0xBAE9, 0xBAE9, 0xBAE9 }, +{ 0xBAEA, 0xBAEA, 0xBAEA }, +{ 0xBAEB, 0xBAEB, 0xBAEB }, +{ 0xBAEC, 0xBAEC, 0xBAEC }, +{ 0xBAED, 0xBAED, 0xBAED }, +{ 0xBAEE, 0xBAEE, 0xBAEE }, +{ 0xBAEF, 0xBAEF, 0xBAEF }, +{ 0xBAF0, 0xBAF0, 0xBAF0 }, +{ 0xBAF1, 0xBAF1, 0xBAF1 }, +{ 0xBAF2, 0xBAF2, 0xBAF2 }, +{ 0xBAF3, 0xBAF3, 0xBAF3 }, +{ 0xBAF4, 0xBAF4, 0xBAF4 }, +{ 0xBAF5, 0xBAF5, 0xBAF5 }, +{ 0xBAF6, 0xBAF6, 0xBAF6 }, +{ 0xBAF7, 0xBAF7, 0xBAF7 }, +{ 0xBAF8, 0xBAF8, 0xBAF8 }, +{ 0xBAF9, 0xBAF9, 0xBAF9 }, +{ 0xBAFA, 0xBAFA, 0xBAFA }, +{ 0xBAFB, 0xBAFB, 0xBAFB }, +{ 0xBAFC, 0xBAFC, 0xBAFC }, +{ 0xBAFD, 0xBAFD, 0xBAFD }, +{ 0xBAFE, 0xBAFE, 0xBAFE }, +{ 0xBAFF, 0xBAFF, 0xBAFF }, +{ 0xBB00, 0xBB00, 0xBB00 }, +{ 0xBB01, 0xBB01, 0xBB01 }, +{ 0xBB02, 0xBB02, 0xBB02 }, +{ 0xBB03, 0xBB03, 0xBB03 }, +{ 0xBB04, 0xBB04, 0xBB04 }, +{ 0xBB05, 0xBB05, 0xBB05 }, +{ 0xBB06, 0xBB06, 0xBB06 }, +{ 0xBB07, 0xBB07, 0xBB07 }, +{ 0xBB08, 0xBB08, 0xBB08 }, +{ 0xBB09, 0xBB09, 0xBB09 }, +{ 0xBB0A, 0xBB0A, 0xBB0A }, +{ 0xBB0B, 0xBB0B, 0xBB0B }, +{ 0xBB0C, 0xBB0C, 0xBB0C }, +{ 0xBB0D, 0xBB0D, 0xBB0D }, +{ 0xBB0E, 0xBB0E, 0xBB0E }, +{ 0xBB0F, 0xBB0F, 0xBB0F }, +{ 0xBB10, 0xBB10, 0xBB10 }, +{ 0xBB11, 0xBB11, 0xBB11 }, +{ 0xBB12, 0xBB12, 0xBB12 }, +{ 0xBB13, 0xBB13, 0xBB13 }, +{ 0xBB14, 0xBB14, 0xBB14 }, +{ 0xBB15, 0xBB15, 0xBB15 }, +{ 0xBB16, 0xBB16, 0xBB16 }, +{ 0xBB17, 0xBB17, 0xBB17 }, +{ 0xBB18, 0xBB18, 0xBB18 }, +{ 0xBB19, 0xBB19, 0xBB19 }, +{ 0xBB1A, 0xBB1A, 0xBB1A }, +{ 0xBB1B, 0xBB1B, 0xBB1B }, +{ 0xBB1C, 0xBB1C, 0xBB1C }, +{ 0xBB1D, 0xBB1D, 0xBB1D }, +{ 0xBB1E, 0xBB1E, 0xBB1E }, +{ 0xBB1F, 0xBB1F, 0xBB1F }, +{ 0xBB20, 0xBB20, 0xBB20 }, +{ 0xBB21, 0xBB21, 0xBB21 }, +{ 0xBB22, 0xBB22, 0xBB22 }, +{ 0xBB23, 0xBB23, 0xBB23 }, +{ 0xBB24, 0xBB24, 0xBB24 }, +{ 0xBB25, 0xBB25, 0xBB25 }, +{ 0xBB26, 0xBB26, 0xBB26 }, +{ 0xBB27, 0xBB27, 0xBB27 }, +{ 0xBB28, 0xBB28, 0xBB28 }, +{ 0xBB29, 0xBB29, 0xBB29 }, +{ 0xBB2A, 0xBB2A, 0xBB2A }, +{ 0xBB2B, 0xBB2B, 0xBB2B }, +{ 0xBB2C, 0xBB2C, 0xBB2C }, +{ 0xBB2D, 0xBB2D, 0xBB2D }, +{ 0xBB2E, 0xBB2E, 0xBB2E }, +{ 0xBB2F, 0xBB2F, 0xBB2F }, +{ 0xBB30, 0xBB30, 0xBB30 }, +{ 0xBB31, 0xBB31, 0xBB31 }, +{ 0xBB32, 0xBB32, 0xBB32 }, +{ 0xBB33, 0xBB33, 0xBB33 }, +{ 0xBB34, 0xBB34, 0xBB34 }, +{ 0xBB35, 0xBB35, 0xBB35 }, +{ 0xBB36, 0xBB36, 0xBB36 }, +{ 0xBB37, 0xBB37, 0xBB37 }, +{ 0xBB38, 0xBB38, 0xBB38 }, +{ 0xBB39, 0xBB39, 0xBB39 }, +{ 0xBB3A, 0xBB3A, 0xBB3A }, +{ 0xBB3B, 0xBB3B, 0xBB3B }, +{ 0xBB3C, 0xBB3C, 0xBB3C }, +{ 0xBB3D, 0xBB3D, 0xBB3D }, +{ 0xBB3E, 0xBB3E, 0xBB3E }, +{ 0xBB3F, 0xBB3F, 0xBB3F }, +{ 0xBB40, 0xBB40, 0xBB40 }, +{ 0xBB41, 0xBB41, 0xBB41 }, +{ 0xBB42, 0xBB42, 0xBB42 }, +{ 0xBB43, 0xBB43, 0xBB43 }, +{ 0xBB44, 0xBB44, 0xBB44 }, +{ 0xBB45, 0xBB45, 0xBB45 }, +{ 0xBB46, 0xBB46, 0xBB46 }, +{ 0xBB47, 0xBB47, 0xBB47 }, +{ 0xBB48, 0xBB48, 0xBB48 }, +{ 0xBB49, 0xBB49, 0xBB49 }, +{ 0xBB4A, 0xBB4A, 0xBB4A }, +{ 0xBB4B, 0xBB4B, 0xBB4B }, +{ 0xBB4C, 0xBB4C, 0xBB4C }, +{ 0xBB4D, 0xBB4D, 0xBB4D }, +{ 0xBB4E, 0xBB4E, 0xBB4E }, +{ 0xBB4F, 0xBB4F, 0xBB4F }, +{ 0xBB50, 0xBB50, 0xBB50 }, +{ 0xBB51, 0xBB51, 0xBB51 }, +{ 0xBB52, 0xBB52, 0xBB52 }, +{ 0xBB53, 0xBB53, 0xBB53 }, +{ 0xBB54, 0xBB54, 0xBB54 }, +{ 0xBB55, 0xBB55, 0xBB55 }, +{ 0xBB56, 0xBB56, 0xBB56 }, +{ 0xBB57, 0xBB57, 0xBB57 }, +{ 0xBB58, 0xBB58, 0xBB58 }, +{ 0xBB59, 0xBB59, 0xBB59 }, +{ 0xBB5A, 0xBB5A, 0xBB5A }, +{ 0xBB5B, 0xBB5B, 0xBB5B }, +{ 0xBB5C, 0xBB5C, 0xBB5C }, +{ 0xBB5D, 0xBB5D, 0xBB5D }, +{ 0xBB5E, 0xBB5E, 0xBB5E }, +{ 0xBB5F, 0xBB5F, 0xBB5F }, +{ 0xBB60, 0xBB60, 0xBB60 }, +{ 0xBB61, 0xBB61, 0xBB61 }, +{ 0xBB62, 0xBB62, 0xBB62 }, +{ 0xBB63, 0xBB63, 0xBB63 }, +{ 0xBB64, 0xBB64, 0xBB64 }, +{ 0xBB65, 0xBB65, 0xBB65 }, +{ 0xBB66, 0xBB66, 0xBB66 }, +{ 0xBB67, 0xBB67, 0xBB67 }, +{ 0xBB68, 0xBB68, 0xBB68 }, +{ 0xBB69, 0xBB69, 0xBB69 }, +{ 0xBB6A, 0xBB6A, 0xBB6A }, +{ 0xBB6B, 0xBB6B, 0xBB6B }, +{ 0xBB6C, 0xBB6C, 0xBB6C }, +{ 0xBB6D, 0xBB6D, 0xBB6D }, +{ 0xBB6E, 0xBB6E, 0xBB6E }, +{ 0xBB6F, 0xBB6F, 0xBB6F }, +{ 0xBB70, 0xBB70, 0xBB70 }, +{ 0xBB71, 0xBB71, 0xBB71 }, +{ 0xBB72, 0xBB72, 0xBB72 }, +{ 0xBB73, 0xBB73, 0xBB73 }, +{ 0xBB74, 0xBB74, 0xBB74 }, +{ 0xBB75, 0xBB75, 0xBB75 }, +{ 0xBB76, 0xBB76, 0xBB76 }, +{ 0xBB77, 0xBB77, 0xBB77 }, +{ 0xBB78, 0xBB78, 0xBB78 }, +{ 0xBB79, 0xBB79, 0xBB79 }, +{ 0xBB7A, 0xBB7A, 0xBB7A }, +{ 0xBB7B, 0xBB7B, 0xBB7B }, +{ 0xBB7C, 0xBB7C, 0xBB7C }, +{ 0xBB7D, 0xBB7D, 0xBB7D }, +{ 0xBB7E, 0xBB7E, 0xBB7E }, +{ 0xBB7F, 0xBB7F, 0xBB7F }, +{ 0xBB80, 0xBB80, 0xBB80 }, +{ 0xBB81, 0xBB81, 0xBB81 }, +{ 0xBB82, 0xBB82, 0xBB82 }, +{ 0xBB83, 0xBB83, 0xBB83 }, +{ 0xBB84, 0xBB84, 0xBB84 }, +{ 0xBB85, 0xBB85, 0xBB85 }, +{ 0xBB86, 0xBB86, 0xBB86 }, +{ 0xBB87, 0xBB87, 0xBB87 }, +{ 0xBB88, 0xBB88, 0xBB88 }, +{ 0xBB89, 0xBB89, 0xBB89 }, +{ 0xBB8A, 0xBB8A, 0xBB8A }, +{ 0xBB8B, 0xBB8B, 0xBB8B }, +{ 0xBB8C, 0xBB8C, 0xBB8C }, +{ 0xBB8D, 0xBB8D, 0xBB8D }, +{ 0xBB8E, 0xBB8E, 0xBB8E }, +{ 0xBB8F, 0xBB8F, 0xBB8F }, +{ 0xBB90, 0xBB90, 0xBB90 }, +{ 0xBB91, 0xBB91, 0xBB91 }, +{ 0xBB92, 0xBB92, 0xBB92 }, +{ 0xBB93, 0xBB93, 0xBB93 }, +{ 0xBB94, 0xBB94, 0xBB94 }, +{ 0xBB95, 0xBB95, 0xBB95 }, +{ 0xBB96, 0xBB96, 0xBB96 }, +{ 0xBB97, 0xBB97, 0xBB97 }, +{ 0xBB98, 0xBB98, 0xBB98 }, +{ 0xBB99, 0xBB99, 0xBB99 }, +{ 0xBB9A, 0xBB9A, 0xBB9A }, +{ 0xBB9B, 0xBB9B, 0xBB9B }, +{ 0xBB9C, 0xBB9C, 0xBB9C }, +{ 0xBB9D, 0xBB9D, 0xBB9D }, +{ 0xBB9E, 0xBB9E, 0xBB9E }, +{ 0xBB9F, 0xBB9F, 0xBB9F }, +{ 0xBBA0, 0xBBA0, 0xBBA0 }, +{ 0xBBA1, 0xBBA1, 0xBBA1 }, +{ 0xBBA2, 0xBBA2, 0xBBA2 }, +{ 0xBBA3, 0xBBA3, 0xBBA3 }, +{ 0xBBA4, 0xBBA4, 0xBBA4 }, +{ 0xBBA5, 0xBBA5, 0xBBA5 }, +{ 0xBBA6, 0xBBA6, 0xBBA6 }, +{ 0xBBA7, 0xBBA7, 0xBBA7 }, +{ 0xBBA8, 0xBBA8, 0xBBA8 }, +{ 0xBBA9, 0xBBA9, 0xBBA9 }, +{ 0xBBAA, 0xBBAA, 0xBBAA }, +{ 0xBBAB, 0xBBAB, 0xBBAB }, +{ 0xBBAC, 0xBBAC, 0xBBAC }, +{ 0xBBAD, 0xBBAD, 0xBBAD }, +{ 0xBBAE, 0xBBAE, 0xBBAE }, +{ 0xBBAF, 0xBBAF, 0xBBAF }, +{ 0xBBB0, 0xBBB0, 0xBBB0 }, +{ 0xBBB1, 0xBBB1, 0xBBB1 }, +{ 0xBBB2, 0xBBB2, 0xBBB2 }, +{ 0xBBB3, 0xBBB3, 0xBBB3 }, +{ 0xBBB4, 0xBBB4, 0xBBB4 }, +{ 0xBBB5, 0xBBB5, 0xBBB5 }, +{ 0xBBB6, 0xBBB6, 0xBBB6 }, +{ 0xBBB7, 0xBBB7, 0xBBB7 }, +{ 0xBBB8, 0xBBB8, 0xBBB8 }, +{ 0xBBB9, 0xBBB9, 0xBBB9 }, +{ 0xBBBA, 0xBBBA, 0xBBBA }, +{ 0xBBBB, 0xBBBB, 0xBBBB }, +{ 0xBBBC, 0xBBBC, 0xBBBC }, +{ 0xBBBD, 0xBBBD, 0xBBBD }, +{ 0xBBBE, 0xBBBE, 0xBBBE }, +{ 0xBBBF, 0xBBBF, 0xBBBF }, +{ 0xBBC0, 0xBBC0, 0xBBC0 }, +{ 0xBBC1, 0xBBC1, 0xBBC1 }, +{ 0xBBC2, 0xBBC2, 0xBBC2 }, +{ 0xBBC3, 0xBBC3, 0xBBC3 }, +{ 0xBBC4, 0xBBC4, 0xBBC4 }, +{ 0xBBC5, 0xBBC5, 0xBBC5 }, +{ 0xBBC6, 0xBBC6, 0xBBC6 }, +{ 0xBBC7, 0xBBC7, 0xBBC7 }, +{ 0xBBC8, 0xBBC8, 0xBBC8 }, +{ 0xBBC9, 0xBBC9, 0xBBC9 }, +{ 0xBBCA, 0xBBCA, 0xBBCA }, +{ 0xBBCB, 0xBBCB, 0xBBCB }, +{ 0xBBCC, 0xBBCC, 0xBBCC }, +{ 0xBBCD, 0xBBCD, 0xBBCD }, +{ 0xBBCE, 0xBBCE, 0xBBCE }, +{ 0xBBCF, 0xBBCF, 0xBBCF }, +{ 0xBBD0, 0xBBD0, 0xBBD0 }, +{ 0xBBD1, 0xBBD1, 0xBBD1 }, +{ 0xBBD2, 0xBBD2, 0xBBD2 }, +{ 0xBBD3, 0xBBD3, 0xBBD3 }, +{ 0xBBD4, 0xBBD4, 0xBBD4 }, +{ 0xBBD5, 0xBBD5, 0xBBD5 }, +{ 0xBBD6, 0xBBD6, 0xBBD6 }, +{ 0xBBD7, 0xBBD7, 0xBBD7 }, +{ 0xBBD8, 0xBBD8, 0xBBD8 }, +{ 0xBBD9, 0xBBD9, 0xBBD9 }, +{ 0xBBDA, 0xBBDA, 0xBBDA }, +{ 0xBBDB, 0xBBDB, 0xBBDB }, +{ 0xBBDC, 0xBBDC, 0xBBDC }, +{ 0xBBDD, 0xBBDD, 0xBBDD }, +{ 0xBBDE, 0xBBDE, 0xBBDE }, +{ 0xBBDF, 0xBBDF, 0xBBDF }, +{ 0xBBE0, 0xBBE0, 0xBBE0 }, +{ 0xBBE1, 0xBBE1, 0xBBE1 }, +{ 0xBBE2, 0xBBE2, 0xBBE2 }, +{ 0xBBE3, 0xBBE3, 0xBBE3 }, +{ 0xBBE4, 0xBBE4, 0xBBE4 }, +{ 0xBBE5, 0xBBE5, 0xBBE5 }, +{ 0xBBE6, 0xBBE6, 0xBBE6 }, +{ 0xBBE7, 0xBBE7, 0xBBE7 }, +{ 0xBBE8, 0xBBE8, 0xBBE8 }, +{ 0xBBE9, 0xBBE9, 0xBBE9 }, +{ 0xBBEA, 0xBBEA, 0xBBEA }, +{ 0xBBEB, 0xBBEB, 0xBBEB }, +{ 0xBBEC, 0xBBEC, 0xBBEC }, +{ 0xBBED, 0xBBED, 0xBBED }, +{ 0xBBEE, 0xBBEE, 0xBBEE }, +{ 0xBBEF, 0xBBEF, 0xBBEF }, +{ 0xBBF0, 0xBBF0, 0xBBF0 }, +{ 0xBBF1, 0xBBF1, 0xBBF1 }, +{ 0xBBF2, 0xBBF2, 0xBBF2 }, +{ 0xBBF3, 0xBBF3, 0xBBF3 }, +{ 0xBBF4, 0xBBF4, 0xBBF4 }, +{ 0xBBF5, 0xBBF5, 0xBBF5 }, +{ 0xBBF6, 0xBBF6, 0xBBF6 }, +{ 0xBBF7, 0xBBF7, 0xBBF7 }, +{ 0xBBF8, 0xBBF8, 0xBBF8 }, +{ 0xBBF9, 0xBBF9, 0xBBF9 }, +{ 0xBBFA, 0xBBFA, 0xBBFA }, +{ 0xBBFB, 0xBBFB, 0xBBFB }, +{ 0xBBFC, 0xBBFC, 0xBBFC }, +{ 0xBBFD, 0xBBFD, 0xBBFD }, +{ 0xBBFE, 0xBBFE, 0xBBFE }, +{ 0xBBFF, 0xBBFF, 0xBBFF }, +{ 0xBC00, 0xBC00, 0xBC00 }, +{ 0xBC01, 0xBC01, 0xBC01 }, +{ 0xBC02, 0xBC02, 0xBC02 }, +{ 0xBC03, 0xBC03, 0xBC03 }, +{ 0xBC04, 0xBC04, 0xBC04 }, +{ 0xBC05, 0xBC05, 0xBC05 }, +{ 0xBC06, 0xBC06, 0xBC06 }, +{ 0xBC07, 0xBC07, 0xBC07 }, +{ 0xBC08, 0xBC08, 0xBC08 }, +{ 0xBC09, 0xBC09, 0xBC09 }, +{ 0xBC0A, 0xBC0A, 0xBC0A }, +{ 0xBC0B, 0xBC0B, 0xBC0B }, +{ 0xBC0C, 0xBC0C, 0xBC0C }, +{ 0xBC0D, 0xBC0D, 0xBC0D }, +{ 0xBC0E, 0xBC0E, 0xBC0E }, +{ 0xBC0F, 0xBC0F, 0xBC0F }, +{ 0xBC10, 0xBC10, 0xBC10 }, +{ 0xBC11, 0xBC11, 0xBC11 }, +{ 0xBC12, 0xBC12, 0xBC12 }, +{ 0xBC13, 0xBC13, 0xBC13 }, +{ 0xBC14, 0xBC14, 0xBC14 }, +{ 0xBC15, 0xBC15, 0xBC15 }, +{ 0xBC16, 0xBC16, 0xBC16 }, +{ 0xBC17, 0xBC17, 0xBC17 }, +{ 0xBC18, 0xBC18, 0xBC18 }, +{ 0xBC19, 0xBC19, 0xBC19 }, +{ 0xBC1A, 0xBC1A, 0xBC1A }, +{ 0xBC1B, 0xBC1B, 0xBC1B }, +{ 0xBC1C, 0xBC1C, 0xBC1C }, +{ 0xBC1D, 0xBC1D, 0xBC1D }, +{ 0xBC1E, 0xBC1E, 0xBC1E }, +{ 0xBC1F, 0xBC1F, 0xBC1F }, +{ 0xBC20, 0xBC20, 0xBC20 }, +{ 0xBC21, 0xBC21, 0xBC21 }, +{ 0xBC22, 0xBC22, 0xBC22 }, +{ 0xBC23, 0xBC23, 0xBC23 }, +{ 0xBC24, 0xBC24, 0xBC24 }, +{ 0xBC25, 0xBC25, 0xBC25 }, +{ 0xBC26, 0xBC26, 0xBC26 }, +{ 0xBC27, 0xBC27, 0xBC27 }, +{ 0xBC28, 0xBC28, 0xBC28 }, +{ 0xBC29, 0xBC29, 0xBC29 }, +{ 0xBC2A, 0xBC2A, 0xBC2A }, +{ 0xBC2B, 0xBC2B, 0xBC2B }, +{ 0xBC2C, 0xBC2C, 0xBC2C }, +{ 0xBC2D, 0xBC2D, 0xBC2D }, +{ 0xBC2E, 0xBC2E, 0xBC2E }, +{ 0xBC2F, 0xBC2F, 0xBC2F }, +{ 0xBC30, 0xBC30, 0xBC30 }, +{ 0xBC31, 0xBC31, 0xBC31 }, +{ 0xBC32, 0xBC32, 0xBC32 }, +{ 0xBC33, 0xBC33, 0xBC33 }, +{ 0xBC34, 0xBC34, 0xBC34 }, +{ 0xBC35, 0xBC35, 0xBC35 }, +{ 0xBC36, 0xBC36, 0xBC36 }, +{ 0xBC37, 0xBC37, 0xBC37 }, +{ 0xBC38, 0xBC38, 0xBC38 }, +{ 0xBC39, 0xBC39, 0xBC39 }, +{ 0xBC3A, 0xBC3A, 0xBC3A }, +{ 0xBC3B, 0xBC3B, 0xBC3B }, +{ 0xBC3C, 0xBC3C, 0xBC3C }, +{ 0xBC3D, 0xBC3D, 0xBC3D }, +{ 0xBC3E, 0xBC3E, 0xBC3E }, +{ 0xBC3F, 0xBC3F, 0xBC3F }, +{ 0xBC40, 0xBC40, 0xBC40 }, +{ 0xBC41, 0xBC41, 0xBC41 }, +{ 0xBC42, 0xBC42, 0xBC42 }, +{ 0xBC43, 0xBC43, 0xBC43 }, +{ 0xBC44, 0xBC44, 0xBC44 }, +{ 0xBC45, 0xBC45, 0xBC45 }, +{ 0xBC46, 0xBC46, 0xBC46 }, +{ 0xBC47, 0xBC47, 0xBC47 }, +{ 0xBC48, 0xBC48, 0xBC48 }, +{ 0xBC49, 0xBC49, 0xBC49 }, +{ 0xBC4A, 0xBC4A, 0xBC4A }, +{ 0xBC4B, 0xBC4B, 0xBC4B }, +{ 0xBC4C, 0xBC4C, 0xBC4C }, +{ 0xBC4D, 0xBC4D, 0xBC4D }, +{ 0xBC4E, 0xBC4E, 0xBC4E }, +{ 0xBC4F, 0xBC4F, 0xBC4F }, +{ 0xBC50, 0xBC50, 0xBC50 }, +{ 0xBC51, 0xBC51, 0xBC51 }, +{ 0xBC52, 0xBC52, 0xBC52 }, +{ 0xBC53, 0xBC53, 0xBC53 }, +{ 0xBC54, 0xBC54, 0xBC54 }, +{ 0xBC55, 0xBC55, 0xBC55 }, +{ 0xBC56, 0xBC56, 0xBC56 }, +{ 0xBC57, 0xBC57, 0xBC57 }, +{ 0xBC58, 0xBC58, 0xBC58 }, +{ 0xBC59, 0xBC59, 0xBC59 }, +{ 0xBC5A, 0xBC5A, 0xBC5A }, +{ 0xBC5B, 0xBC5B, 0xBC5B }, +{ 0xBC5C, 0xBC5C, 0xBC5C }, +{ 0xBC5D, 0xBC5D, 0xBC5D }, +{ 0xBC5E, 0xBC5E, 0xBC5E }, +{ 0xBC5F, 0xBC5F, 0xBC5F }, +{ 0xBC60, 0xBC60, 0xBC60 }, +{ 0xBC61, 0xBC61, 0xBC61 }, +{ 0xBC62, 0xBC62, 0xBC62 }, +{ 0xBC63, 0xBC63, 0xBC63 }, +{ 0xBC64, 0xBC64, 0xBC64 }, +{ 0xBC65, 0xBC65, 0xBC65 }, +{ 0xBC66, 0xBC66, 0xBC66 }, +{ 0xBC67, 0xBC67, 0xBC67 }, +{ 0xBC68, 0xBC68, 0xBC68 }, +{ 0xBC69, 0xBC69, 0xBC69 }, +{ 0xBC6A, 0xBC6A, 0xBC6A }, +{ 0xBC6B, 0xBC6B, 0xBC6B }, +{ 0xBC6C, 0xBC6C, 0xBC6C }, +{ 0xBC6D, 0xBC6D, 0xBC6D }, +{ 0xBC6E, 0xBC6E, 0xBC6E }, +{ 0xBC6F, 0xBC6F, 0xBC6F }, +{ 0xBC70, 0xBC70, 0xBC70 }, +{ 0xBC71, 0xBC71, 0xBC71 }, +{ 0xBC72, 0xBC72, 0xBC72 }, +{ 0xBC73, 0xBC73, 0xBC73 }, +{ 0xBC74, 0xBC74, 0xBC74 }, +{ 0xBC75, 0xBC75, 0xBC75 }, +{ 0xBC76, 0xBC76, 0xBC76 }, +{ 0xBC77, 0xBC77, 0xBC77 }, +{ 0xBC78, 0xBC78, 0xBC78 }, +{ 0xBC79, 0xBC79, 0xBC79 }, +{ 0xBC7A, 0xBC7A, 0xBC7A }, +{ 0xBC7B, 0xBC7B, 0xBC7B }, +{ 0xBC7C, 0xBC7C, 0xBC7C }, +{ 0xBC7D, 0xBC7D, 0xBC7D }, +{ 0xBC7E, 0xBC7E, 0xBC7E }, +{ 0xBC7F, 0xBC7F, 0xBC7F }, +{ 0xBC80, 0xBC80, 0xBC80 }, +{ 0xBC81, 0xBC81, 0xBC81 }, +{ 0xBC82, 0xBC82, 0xBC82 }, +{ 0xBC83, 0xBC83, 0xBC83 }, +{ 0xBC84, 0xBC84, 0xBC84 }, +{ 0xBC85, 0xBC85, 0xBC85 }, +{ 0xBC86, 0xBC86, 0xBC86 }, +{ 0xBC87, 0xBC87, 0xBC87 }, +{ 0xBC88, 0xBC88, 0xBC88 }, +{ 0xBC89, 0xBC89, 0xBC89 }, +{ 0xBC8A, 0xBC8A, 0xBC8A }, +{ 0xBC8B, 0xBC8B, 0xBC8B }, +{ 0xBC8C, 0xBC8C, 0xBC8C }, +{ 0xBC8D, 0xBC8D, 0xBC8D }, +{ 0xBC8E, 0xBC8E, 0xBC8E }, +{ 0xBC8F, 0xBC8F, 0xBC8F }, +{ 0xBC90, 0xBC90, 0xBC90 }, +{ 0xBC91, 0xBC91, 0xBC91 }, +{ 0xBC92, 0xBC92, 0xBC92 }, +{ 0xBC93, 0xBC93, 0xBC93 }, +{ 0xBC94, 0xBC94, 0xBC94 }, +{ 0xBC95, 0xBC95, 0xBC95 }, +{ 0xBC96, 0xBC96, 0xBC96 }, +{ 0xBC97, 0xBC97, 0xBC97 }, +{ 0xBC98, 0xBC98, 0xBC98 }, +{ 0xBC99, 0xBC99, 0xBC99 }, +{ 0xBC9A, 0xBC9A, 0xBC9A }, +{ 0xBC9B, 0xBC9B, 0xBC9B }, +{ 0xBC9C, 0xBC9C, 0xBC9C }, +{ 0xBC9D, 0xBC9D, 0xBC9D }, +{ 0xBC9E, 0xBC9E, 0xBC9E }, +{ 0xBC9F, 0xBC9F, 0xBC9F }, +{ 0xBCA0, 0xBCA0, 0xBCA0 }, +{ 0xBCA1, 0xBCA1, 0xBCA1 }, +{ 0xBCA2, 0xBCA2, 0xBCA2 }, +{ 0xBCA3, 0xBCA3, 0xBCA3 }, +{ 0xBCA4, 0xBCA4, 0xBCA4 }, +{ 0xBCA5, 0xBCA5, 0xBCA5 }, +{ 0xBCA6, 0xBCA6, 0xBCA6 }, +{ 0xBCA7, 0xBCA7, 0xBCA7 }, +{ 0xBCA8, 0xBCA8, 0xBCA8 }, +{ 0xBCA9, 0xBCA9, 0xBCA9 }, +{ 0xBCAA, 0xBCAA, 0xBCAA }, +{ 0xBCAB, 0xBCAB, 0xBCAB }, +{ 0xBCAC, 0xBCAC, 0xBCAC }, +{ 0xBCAD, 0xBCAD, 0xBCAD }, +{ 0xBCAE, 0xBCAE, 0xBCAE }, +{ 0xBCAF, 0xBCAF, 0xBCAF }, +{ 0xBCB0, 0xBCB0, 0xBCB0 }, +{ 0xBCB1, 0xBCB1, 0xBCB1 }, +{ 0xBCB2, 0xBCB2, 0xBCB2 }, +{ 0xBCB3, 0xBCB3, 0xBCB3 }, +{ 0xBCB4, 0xBCB4, 0xBCB4 }, +{ 0xBCB5, 0xBCB5, 0xBCB5 }, +{ 0xBCB6, 0xBCB6, 0xBCB6 }, +{ 0xBCB7, 0xBCB7, 0xBCB7 }, +{ 0xBCB8, 0xBCB8, 0xBCB8 }, +{ 0xBCB9, 0xBCB9, 0xBCB9 }, +{ 0xBCBA, 0xBCBA, 0xBCBA }, +{ 0xBCBB, 0xBCBB, 0xBCBB }, +{ 0xBCBC, 0xBCBC, 0xBCBC }, +{ 0xBCBD, 0xBCBD, 0xBCBD }, +{ 0xBCBE, 0xBCBE, 0xBCBE }, +{ 0xBCBF, 0xBCBF, 0xBCBF }, +{ 0xBCC0, 0xBCC0, 0xBCC0 }, +{ 0xBCC1, 0xBCC1, 0xBCC1 }, +{ 0xBCC2, 0xBCC2, 0xBCC2 }, +{ 0xBCC3, 0xBCC3, 0xBCC3 }, +{ 0xBCC4, 0xBCC4, 0xBCC4 }, +{ 0xBCC5, 0xBCC5, 0xBCC5 }, +{ 0xBCC6, 0xBCC6, 0xBCC6 }, +{ 0xBCC7, 0xBCC7, 0xBCC7 }, +{ 0xBCC8, 0xBCC8, 0xBCC8 }, +{ 0xBCC9, 0xBCC9, 0xBCC9 }, +{ 0xBCCA, 0xBCCA, 0xBCCA }, +{ 0xBCCB, 0xBCCB, 0xBCCB }, +{ 0xBCCC, 0xBCCC, 0xBCCC }, +{ 0xBCCD, 0xBCCD, 0xBCCD }, +{ 0xBCCE, 0xBCCE, 0xBCCE }, +{ 0xBCCF, 0xBCCF, 0xBCCF }, +{ 0xBCD0, 0xBCD0, 0xBCD0 }, +{ 0xBCD1, 0xBCD1, 0xBCD1 }, +{ 0xBCD2, 0xBCD2, 0xBCD2 }, +{ 0xBCD3, 0xBCD3, 0xBCD3 }, +{ 0xBCD4, 0xBCD4, 0xBCD4 }, +{ 0xBCD5, 0xBCD5, 0xBCD5 }, +{ 0xBCD6, 0xBCD6, 0xBCD6 }, +{ 0xBCD7, 0xBCD7, 0xBCD7 }, +{ 0xBCD8, 0xBCD8, 0xBCD8 }, +{ 0xBCD9, 0xBCD9, 0xBCD9 }, +{ 0xBCDA, 0xBCDA, 0xBCDA }, +{ 0xBCDB, 0xBCDB, 0xBCDB }, +{ 0xBCDC, 0xBCDC, 0xBCDC }, +{ 0xBCDD, 0xBCDD, 0xBCDD }, +{ 0xBCDE, 0xBCDE, 0xBCDE }, +{ 0xBCDF, 0xBCDF, 0xBCDF }, +{ 0xBCE0, 0xBCE0, 0xBCE0 }, +{ 0xBCE1, 0xBCE1, 0xBCE1 }, +{ 0xBCE2, 0xBCE2, 0xBCE2 }, +{ 0xBCE3, 0xBCE3, 0xBCE3 }, +{ 0xBCE4, 0xBCE4, 0xBCE4 }, +{ 0xBCE5, 0xBCE5, 0xBCE5 }, +{ 0xBCE6, 0xBCE6, 0xBCE6 }, +{ 0xBCE7, 0xBCE7, 0xBCE7 }, +{ 0xBCE8, 0xBCE8, 0xBCE8 }, +{ 0xBCE9, 0xBCE9, 0xBCE9 }, +{ 0xBCEA, 0xBCEA, 0xBCEA }, +{ 0xBCEB, 0xBCEB, 0xBCEB }, +{ 0xBCEC, 0xBCEC, 0xBCEC }, +{ 0xBCED, 0xBCED, 0xBCED }, +{ 0xBCEE, 0xBCEE, 0xBCEE }, +{ 0xBCEF, 0xBCEF, 0xBCEF }, +{ 0xBCF0, 0xBCF0, 0xBCF0 }, +{ 0xBCF1, 0xBCF1, 0xBCF1 }, +{ 0xBCF2, 0xBCF2, 0xBCF2 }, +{ 0xBCF3, 0xBCF3, 0xBCF3 }, +{ 0xBCF4, 0xBCF4, 0xBCF4 }, +{ 0xBCF5, 0xBCF5, 0xBCF5 }, +{ 0xBCF6, 0xBCF6, 0xBCF6 }, +{ 0xBCF7, 0xBCF7, 0xBCF7 }, +{ 0xBCF8, 0xBCF8, 0xBCF8 }, +{ 0xBCF9, 0xBCF9, 0xBCF9 }, +{ 0xBCFA, 0xBCFA, 0xBCFA }, +{ 0xBCFB, 0xBCFB, 0xBCFB }, +{ 0xBCFC, 0xBCFC, 0xBCFC }, +{ 0xBCFD, 0xBCFD, 0xBCFD }, +{ 0xBCFE, 0xBCFE, 0xBCFE }, +{ 0xBCFF, 0xBCFF, 0xBCFF }, +{ 0xBD00, 0xBD00, 0xBD00 }, +{ 0xBD01, 0xBD01, 0xBD01 }, +{ 0xBD02, 0xBD02, 0xBD02 }, +{ 0xBD03, 0xBD03, 0xBD03 }, +{ 0xBD04, 0xBD04, 0xBD04 }, +{ 0xBD05, 0xBD05, 0xBD05 }, +{ 0xBD06, 0xBD06, 0xBD06 }, +{ 0xBD07, 0xBD07, 0xBD07 }, +{ 0xBD08, 0xBD08, 0xBD08 }, +{ 0xBD09, 0xBD09, 0xBD09 }, +{ 0xBD0A, 0xBD0A, 0xBD0A }, +{ 0xBD0B, 0xBD0B, 0xBD0B }, +{ 0xBD0C, 0xBD0C, 0xBD0C }, +{ 0xBD0D, 0xBD0D, 0xBD0D }, +{ 0xBD0E, 0xBD0E, 0xBD0E }, +{ 0xBD0F, 0xBD0F, 0xBD0F }, +{ 0xBD10, 0xBD10, 0xBD10 }, +{ 0xBD11, 0xBD11, 0xBD11 }, +{ 0xBD12, 0xBD12, 0xBD12 }, +{ 0xBD13, 0xBD13, 0xBD13 }, +{ 0xBD14, 0xBD14, 0xBD14 }, +{ 0xBD15, 0xBD15, 0xBD15 }, +{ 0xBD16, 0xBD16, 0xBD16 }, +{ 0xBD17, 0xBD17, 0xBD17 }, +{ 0xBD18, 0xBD18, 0xBD18 }, +{ 0xBD19, 0xBD19, 0xBD19 }, +{ 0xBD1A, 0xBD1A, 0xBD1A }, +{ 0xBD1B, 0xBD1B, 0xBD1B }, +{ 0xBD1C, 0xBD1C, 0xBD1C }, +{ 0xBD1D, 0xBD1D, 0xBD1D }, +{ 0xBD1E, 0xBD1E, 0xBD1E }, +{ 0xBD1F, 0xBD1F, 0xBD1F }, +{ 0xBD20, 0xBD20, 0xBD20 }, +{ 0xBD21, 0xBD21, 0xBD21 }, +{ 0xBD22, 0xBD22, 0xBD22 }, +{ 0xBD23, 0xBD23, 0xBD23 }, +{ 0xBD24, 0xBD24, 0xBD24 }, +{ 0xBD25, 0xBD25, 0xBD25 }, +{ 0xBD26, 0xBD26, 0xBD26 }, +{ 0xBD27, 0xBD27, 0xBD27 }, +{ 0xBD28, 0xBD28, 0xBD28 }, +{ 0xBD29, 0xBD29, 0xBD29 }, +{ 0xBD2A, 0xBD2A, 0xBD2A }, +{ 0xBD2B, 0xBD2B, 0xBD2B }, +{ 0xBD2C, 0xBD2C, 0xBD2C }, +{ 0xBD2D, 0xBD2D, 0xBD2D }, +{ 0xBD2E, 0xBD2E, 0xBD2E }, +{ 0xBD2F, 0xBD2F, 0xBD2F }, +{ 0xBD30, 0xBD30, 0xBD30 }, +{ 0xBD31, 0xBD31, 0xBD31 }, +{ 0xBD32, 0xBD32, 0xBD32 }, +{ 0xBD33, 0xBD33, 0xBD33 }, +{ 0xBD34, 0xBD34, 0xBD34 }, +{ 0xBD35, 0xBD35, 0xBD35 }, +{ 0xBD36, 0xBD36, 0xBD36 }, +{ 0xBD37, 0xBD37, 0xBD37 }, +{ 0xBD38, 0xBD38, 0xBD38 }, +{ 0xBD39, 0xBD39, 0xBD39 }, +{ 0xBD3A, 0xBD3A, 0xBD3A }, +{ 0xBD3B, 0xBD3B, 0xBD3B }, +{ 0xBD3C, 0xBD3C, 0xBD3C }, +{ 0xBD3D, 0xBD3D, 0xBD3D }, +{ 0xBD3E, 0xBD3E, 0xBD3E }, +{ 0xBD3F, 0xBD3F, 0xBD3F }, +{ 0xBD40, 0xBD40, 0xBD40 }, +{ 0xBD41, 0xBD41, 0xBD41 }, +{ 0xBD42, 0xBD42, 0xBD42 }, +{ 0xBD43, 0xBD43, 0xBD43 }, +{ 0xBD44, 0xBD44, 0xBD44 }, +{ 0xBD45, 0xBD45, 0xBD45 }, +{ 0xBD46, 0xBD46, 0xBD46 }, +{ 0xBD47, 0xBD47, 0xBD47 }, +{ 0xBD48, 0xBD48, 0xBD48 }, +{ 0xBD49, 0xBD49, 0xBD49 }, +{ 0xBD4A, 0xBD4A, 0xBD4A }, +{ 0xBD4B, 0xBD4B, 0xBD4B }, +{ 0xBD4C, 0xBD4C, 0xBD4C }, +{ 0xBD4D, 0xBD4D, 0xBD4D }, +{ 0xBD4E, 0xBD4E, 0xBD4E }, +{ 0xBD4F, 0xBD4F, 0xBD4F }, +{ 0xBD50, 0xBD50, 0xBD50 }, +{ 0xBD51, 0xBD51, 0xBD51 }, +{ 0xBD52, 0xBD52, 0xBD52 }, +{ 0xBD53, 0xBD53, 0xBD53 }, +{ 0xBD54, 0xBD54, 0xBD54 }, +{ 0xBD55, 0xBD55, 0xBD55 }, +{ 0xBD56, 0xBD56, 0xBD56 }, +{ 0xBD57, 0xBD57, 0xBD57 }, +{ 0xBD58, 0xBD58, 0xBD58 }, +{ 0xBD59, 0xBD59, 0xBD59 }, +{ 0xBD5A, 0xBD5A, 0xBD5A }, +{ 0xBD5B, 0xBD5B, 0xBD5B }, +{ 0xBD5C, 0xBD5C, 0xBD5C }, +{ 0xBD5D, 0xBD5D, 0xBD5D }, +{ 0xBD5E, 0xBD5E, 0xBD5E }, +{ 0xBD5F, 0xBD5F, 0xBD5F }, +{ 0xBD60, 0xBD60, 0xBD60 }, +{ 0xBD61, 0xBD61, 0xBD61 }, +{ 0xBD62, 0xBD62, 0xBD62 }, +{ 0xBD63, 0xBD63, 0xBD63 }, +{ 0xBD64, 0xBD64, 0xBD64 }, +{ 0xBD65, 0xBD65, 0xBD65 }, +{ 0xBD66, 0xBD66, 0xBD66 }, +{ 0xBD67, 0xBD67, 0xBD67 }, +{ 0xBD68, 0xBD68, 0xBD68 }, +{ 0xBD69, 0xBD69, 0xBD69 }, +{ 0xBD6A, 0xBD6A, 0xBD6A }, +{ 0xBD6B, 0xBD6B, 0xBD6B }, +{ 0xBD6C, 0xBD6C, 0xBD6C }, +{ 0xBD6D, 0xBD6D, 0xBD6D }, +{ 0xBD6E, 0xBD6E, 0xBD6E }, +{ 0xBD6F, 0xBD6F, 0xBD6F }, +{ 0xBD70, 0xBD70, 0xBD70 }, +{ 0xBD71, 0xBD71, 0xBD71 }, +{ 0xBD72, 0xBD72, 0xBD72 }, +{ 0xBD73, 0xBD73, 0xBD73 }, +{ 0xBD74, 0xBD74, 0xBD74 }, +{ 0xBD75, 0xBD75, 0xBD75 }, +{ 0xBD76, 0xBD76, 0xBD76 }, +{ 0xBD77, 0xBD77, 0xBD77 }, +{ 0xBD78, 0xBD78, 0xBD78 }, +{ 0xBD79, 0xBD79, 0xBD79 }, +{ 0xBD7A, 0xBD7A, 0xBD7A }, +{ 0xBD7B, 0xBD7B, 0xBD7B }, +{ 0xBD7C, 0xBD7C, 0xBD7C }, +{ 0xBD7D, 0xBD7D, 0xBD7D }, +{ 0xBD7E, 0xBD7E, 0xBD7E }, +{ 0xBD7F, 0xBD7F, 0xBD7F }, +{ 0xBD80, 0xBD80, 0xBD80 }, +{ 0xBD81, 0xBD81, 0xBD81 }, +{ 0xBD82, 0xBD82, 0xBD82 }, +{ 0xBD83, 0xBD83, 0xBD83 }, +{ 0xBD84, 0xBD84, 0xBD84 }, +{ 0xBD85, 0xBD85, 0xBD85 }, +{ 0xBD86, 0xBD86, 0xBD86 }, +{ 0xBD87, 0xBD87, 0xBD87 }, +{ 0xBD88, 0xBD88, 0xBD88 }, +{ 0xBD89, 0xBD89, 0xBD89 }, +{ 0xBD8A, 0xBD8A, 0xBD8A }, +{ 0xBD8B, 0xBD8B, 0xBD8B }, +{ 0xBD8C, 0xBD8C, 0xBD8C }, +{ 0xBD8D, 0xBD8D, 0xBD8D }, +{ 0xBD8E, 0xBD8E, 0xBD8E }, +{ 0xBD8F, 0xBD8F, 0xBD8F }, +{ 0xBD90, 0xBD90, 0xBD90 }, +{ 0xBD91, 0xBD91, 0xBD91 }, +{ 0xBD92, 0xBD92, 0xBD92 }, +{ 0xBD93, 0xBD93, 0xBD93 }, +{ 0xBD94, 0xBD94, 0xBD94 }, +{ 0xBD95, 0xBD95, 0xBD95 }, +{ 0xBD96, 0xBD96, 0xBD96 }, +{ 0xBD97, 0xBD97, 0xBD97 }, +{ 0xBD98, 0xBD98, 0xBD98 }, +{ 0xBD99, 0xBD99, 0xBD99 }, +{ 0xBD9A, 0xBD9A, 0xBD9A }, +{ 0xBD9B, 0xBD9B, 0xBD9B }, +{ 0xBD9C, 0xBD9C, 0xBD9C }, +{ 0xBD9D, 0xBD9D, 0xBD9D }, +{ 0xBD9E, 0xBD9E, 0xBD9E }, +{ 0xBD9F, 0xBD9F, 0xBD9F }, +{ 0xBDA0, 0xBDA0, 0xBDA0 }, +{ 0xBDA1, 0xBDA1, 0xBDA1 }, +{ 0xBDA2, 0xBDA2, 0xBDA2 }, +{ 0xBDA3, 0xBDA3, 0xBDA3 }, +{ 0xBDA4, 0xBDA4, 0xBDA4 }, +{ 0xBDA5, 0xBDA5, 0xBDA5 }, +{ 0xBDA6, 0xBDA6, 0xBDA6 }, +{ 0xBDA7, 0xBDA7, 0xBDA7 }, +{ 0xBDA8, 0xBDA8, 0xBDA8 }, +{ 0xBDA9, 0xBDA9, 0xBDA9 }, +{ 0xBDAA, 0xBDAA, 0xBDAA }, +{ 0xBDAB, 0xBDAB, 0xBDAB }, +{ 0xBDAC, 0xBDAC, 0xBDAC }, +{ 0xBDAD, 0xBDAD, 0xBDAD }, +{ 0xBDAE, 0xBDAE, 0xBDAE }, +{ 0xBDAF, 0xBDAF, 0xBDAF }, +{ 0xBDB0, 0xBDB0, 0xBDB0 }, +{ 0xBDB1, 0xBDB1, 0xBDB1 }, +{ 0xBDB2, 0xBDB2, 0xBDB2 }, +{ 0xBDB3, 0xBDB3, 0xBDB3 }, +{ 0xBDB4, 0xBDB4, 0xBDB4 }, +{ 0xBDB5, 0xBDB5, 0xBDB5 }, +{ 0xBDB6, 0xBDB6, 0xBDB6 }, +{ 0xBDB7, 0xBDB7, 0xBDB7 }, +{ 0xBDB8, 0xBDB8, 0xBDB8 }, +{ 0xBDB9, 0xBDB9, 0xBDB9 }, +{ 0xBDBA, 0xBDBA, 0xBDBA }, +{ 0xBDBB, 0xBDBB, 0xBDBB }, +{ 0xBDBC, 0xBDBC, 0xBDBC }, +{ 0xBDBD, 0xBDBD, 0xBDBD }, +{ 0xBDBE, 0xBDBE, 0xBDBE }, +{ 0xBDBF, 0xBDBF, 0xBDBF }, +{ 0xBDC0, 0xBDC0, 0xBDC0 }, +{ 0xBDC1, 0xBDC1, 0xBDC1 }, +{ 0xBDC2, 0xBDC2, 0xBDC2 }, +{ 0xBDC3, 0xBDC3, 0xBDC3 }, +{ 0xBDC4, 0xBDC4, 0xBDC4 }, +{ 0xBDC5, 0xBDC5, 0xBDC5 }, +{ 0xBDC6, 0xBDC6, 0xBDC6 }, +{ 0xBDC7, 0xBDC7, 0xBDC7 }, +{ 0xBDC8, 0xBDC8, 0xBDC8 }, +{ 0xBDC9, 0xBDC9, 0xBDC9 }, +{ 0xBDCA, 0xBDCA, 0xBDCA }, +{ 0xBDCB, 0xBDCB, 0xBDCB }, +{ 0xBDCC, 0xBDCC, 0xBDCC }, +{ 0xBDCD, 0xBDCD, 0xBDCD }, +{ 0xBDCE, 0xBDCE, 0xBDCE }, +{ 0xBDCF, 0xBDCF, 0xBDCF }, +{ 0xBDD0, 0xBDD0, 0xBDD0 }, +{ 0xBDD1, 0xBDD1, 0xBDD1 }, +{ 0xBDD2, 0xBDD2, 0xBDD2 }, +{ 0xBDD3, 0xBDD3, 0xBDD3 }, +{ 0xBDD4, 0xBDD4, 0xBDD4 }, +{ 0xBDD5, 0xBDD5, 0xBDD5 }, +{ 0xBDD6, 0xBDD6, 0xBDD6 }, +{ 0xBDD7, 0xBDD7, 0xBDD7 }, +{ 0xBDD8, 0xBDD8, 0xBDD8 }, +{ 0xBDD9, 0xBDD9, 0xBDD9 }, +{ 0xBDDA, 0xBDDA, 0xBDDA }, +{ 0xBDDB, 0xBDDB, 0xBDDB }, +{ 0xBDDC, 0xBDDC, 0xBDDC }, +{ 0xBDDD, 0xBDDD, 0xBDDD }, +{ 0xBDDE, 0xBDDE, 0xBDDE }, +{ 0xBDDF, 0xBDDF, 0xBDDF }, +{ 0xBDE0, 0xBDE0, 0xBDE0 }, +{ 0xBDE1, 0xBDE1, 0xBDE1 }, +{ 0xBDE2, 0xBDE2, 0xBDE2 }, +{ 0xBDE3, 0xBDE3, 0xBDE3 }, +{ 0xBDE4, 0xBDE4, 0xBDE4 }, +{ 0xBDE5, 0xBDE5, 0xBDE5 }, +{ 0xBDE6, 0xBDE6, 0xBDE6 }, +{ 0xBDE7, 0xBDE7, 0xBDE7 }, +{ 0xBDE8, 0xBDE8, 0xBDE8 }, +{ 0xBDE9, 0xBDE9, 0xBDE9 }, +{ 0xBDEA, 0xBDEA, 0xBDEA }, +{ 0xBDEB, 0xBDEB, 0xBDEB }, +{ 0xBDEC, 0xBDEC, 0xBDEC }, +{ 0xBDED, 0xBDED, 0xBDED }, +{ 0xBDEE, 0xBDEE, 0xBDEE }, +{ 0xBDEF, 0xBDEF, 0xBDEF }, +{ 0xBDF0, 0xBDF0, 0xBDF0 }, +{ 0xBDF1, 0xBDF1, 0xBDF1 }, +{ 0xBDF2, 0xBDF2, 0xBDF2 }, +{ 0xBDF3, 0xBDF3, 0xBDF3 }, +{ 0xBDF4, 0xBDF4, 0xBDF4 }, +{ 0xBDF5, 0xBDF5, 0xBDF5 }, +{ 0xBDF6, 0xBDF6, 0xBDF6 }, +{ 0xBDF7, 0xBDF7, 0xBDF7 }, +{ 0xBDF8, 0xBDF8, 0xBDF8 }, +{ 0xBDF9, 0xBDF9, 0xBDF9 }, +{ 0xBDFA, 0xBDFA, 0xBDFA }, +{ 0xBDFB, 0xBDFB, 0xBDFB }, +{ 0xBDFC, 0xBDFC, 0xBDFC }, +{ 0xBDFD, 0xBDFD, 0xBDFD }, +{ 0xBDFE, 0xBDFE, 0xBDFE }, +{ 0xBDFF, 0xBDFF, 0xBDFF }, +{ 0xBE00, 0xBE00, 0xBE00 }, +{ 0xBE01, 0xBE01, 0xBE01 }, +{ 0xBE02, 0xBE02, 0xBE02 }, +{ 0xBE03, 0xBE03, 0xBE03 }, +{ 0xBE04, 0xBE04, 0xBE04 }, +{ 0xBE05, 0xBE05, 0xBE05 }, +{ 0xBE06, 0xBE06, 0xBE06 }, +{ 0xBE07, 0xBE07, 0xBE07 }, +{ 0xBE08, 0xBE08, 0xBE08 }, +{ 0xBE09, 0xBE09, 0xBE09 }, +{ 0xBE0A, 0xBE0A, 0xBE0A }, +{ 0xBE0B, 0xBE0B, 0xBE0B }, +{ 0xBE0C, 0xBE0C, 0xBE0C }, +{ 0xBE0D, 0xBE0D, 0xBE0D }, +{ 0xBE0E, 0xBE0E, 0xBE0E }, +{ 0xBE0F, 0xBE0F, 0xBE0F }, +{ 0xBE10, 0xBE10, 0xBE10 }, +{ 0xBE11, 0xBE11, 0xBE11 }, +{ 0xBE12, 0xBE12, 0xBE12 }, +{ 0xBE13, 0xBE13, 0xBE13 }, +{ 0xBE14, 0xBE14, 0xBE14 }, +{ 0xBE15, 0xBE15, 0xBE15 }, +{ 0xBE16, 0xBE16, 0xBE16 }, +{ 0xBE17, 0xBE17, 0xBE17 }, +{ 0xBE18, 0xBE18, 0xBE18 }, +{ 0xBE19, 0xBE19, 0xBE19 }, +{ 0xBE1A, 0xBE1A, 0xBE1A }, +{ 0xBE1B, 0xBE1B, 0xBE1B }, +{ 0xBE1C, 0xBE1C, 0xBE1C }, +{ 0xBE1D, 0xBE1D, 0xBE1D }, +{ 0xBE1E, 0xBE1E, 0xBE1E }, +{ 0xBE1F, 0xBE1F, 0xBE1F }, +{ 0xBE20, 0xBE20, 0xBE20 }, +{ 0xBE21, 0xBE21, 0xBE21 }, +{ 0xBE22, 0xBE22, 0xBE22 }, +{ 0xBE23, 0xBE23, 0xBE23 }, +{ 0xBE24, 0xBE24, 0xBE24 }, +{ 0xBE25, 0xBE25, 0xBE25 }, +{ 0xBE26, 0xBE26, 0xBE26 }, +{ 0xBE27, 0xBE27, 0xBE27 }, +{ 0xBE28, 0xBE28, 0xBE28 }, +{ 0xBE29, 0xBE29, 0xBE29 }, +{ 0xBE2A, 0xBE2A, 0xBE2A }, +{ 0xBE2B, 0xBE2B, 0xBE2B }, +{ 0xBE2C, 0xBE2C, 0xBE2C }, +{ 0xBE2D, 0xBE2D, 0xBE2D }, +{ 0xBE2E, 0xBE2E, 0xBE2E }, +{ 0xBE2F, 0xBE2F, 0xBE2F }, +{ 0xBE30, 0xBE30, 0xBE30 }, +{ 0xBE31, 0xBE31, 0xBE31 }, +{ 0xBE32, 0xBE32, 0xBE32 }, +{ 0xBE33, 0xBE33, 0xBE33 }, +{ 0xBE34, 0xBE34, 0xBE34 }, +{ 0xBE35, 0xBE35, 0xBE35 }, +{ 0xBE36, 0xBE36, 0xBE36 }, +{ 0xBE37, 0xBE37, 0xBE37 }, +{ 0xBE38, 0xBE38, 0xBE38 }, +{ 0xBE39, 0xBE39, 0xBE39 }, +{ 0xBE3A, 0xBE3A, 0xBE3A }, +{ 0xBE3B, 0xBE3B, 0xBE3B }, +{ 0xBE3C, 0xBE3C, 0xBE3C }, +{ 0xBE3D, 0xBE3D, 0xBE3D }, +{ 0xBE3E, 0xBE3E, 0xBE3E }, +{ 0xBE3F, 0xBE3F, 0xBE3F }, +{ 0xBE40, 0xBE40, 0xBE40 }, +{ 0xBE41, 0xBE41, 0xBE41 }, +{ 0xBE42, 0xBE42, 0xBE42 }, +{ 0xBE43, 0xBE43, 0xBE43 }, +{ 0xBE44, 0xBE44, 0xBE44 }, +{ 0xBE45, 0xBE45, 0xBE45 }, +{ 0xBE46, 0xBE46, 0xBE46 }, +{ 0xBE47, 0xBE47, 0xBE47 }, +{ 0xBE48, 0xBE48, 0xBE48 }, +{ 0xBE49, 0xBE49, 0xBE49 }, +{ 0xBE4A, 0xBE4A, 0xBE4A }, +{ 0xBE4B, 0xBE4B, 0xBE4B }, +{ 0xBE4C, 0xBE4C, 0xBE4C }, +{ 0xBE4D, 0xBE4D, 0xBE4D }, +{ 0xBE4E, 0xBE4E, 0xBE4E }, +{ 0xBE4F, 0xBE4F, 0xBE4F }, +{ 0xBE50, 0xBE50, 0xBE50 }, +{ 0xBE51, 0xBE51, 0xBE51 }, +{ 0xBE52, 0xBE52, 0xBE52 }, +{ 0xBE53, 0xBE53, 0xBE53 }, +{ 0xBE54, 0xBE54, 0xBE54 }, +{ 0xBE55, 0xBE55, 0xBE55 }, +{ 0xBE56, 0xBE56, 0xBE56 }, +{ 0xBE57, 0xBE57, 0xBE57 }, +{ 0xBE58, 0xBE58, 0xBE58 }, +{ 0xBE59, 0xBE59, 0xBE59 }, +{ 0xBE5A, 0xBE5A, 0xBE5A }, +{ 0xBE5B, 0xBE5B, 0xBE5B }, +{ 0xBE5C, 0xBE5C, 0xBE5C }, +{ 0xBE5D, 0xBE5D, 0xBE5D }, +{ 0xBE5E, 0xBE5E, 0xBE5E }, +{ 0xBE5F, 0xBE5F, 0xBE5F }, +{ 0xBE60, 0xBE60, 0xBE60 }, +{ 0xBE61, 0xBE61, 0xBE61 }, +{ 0xBE62, 0xBE62, 0xBE62 }, +{ 0xBE63, 0xBE63, 0xBE63 }, +{ 0xBE64, 0xBE64, 0xBE64 }, +{ 0xBE65, 0xBE65, 0xBE65 }, +{ 0xBE66, 0xBE66, 0xBE66 }, +{ 0xBE67, 0xBE67, 0xBE67 }, +{ 0xBE68, 0xBE68, 0xBE68 }, +{ 0xBE69, 0xBE69, 0xBE69 }, +{ 0xBE6A, 0xBE6A, 0xBE6A }, +{ 0xBE6B, 0xBE6B, 0xBE6B }, +{ 0xBE6C, 0xBE6C, 0xBE6C }, +{ 0xBE6D, 0xBE6D, 0xBE6D }, +{ 0xBE6E, 0xBE6E, 0xBE6E }, +{ 0xBE6F, 0xBE6F, 0xBE6F }, +{ 0xBE70, 0xBE70, 0xBE70 }, +{ 0xBE71, 0xBE71, 0xBE71 }, +{ 0xBE72, 0xBE72, 0xBE72 }, +{ 0xBE73, 0xBE73, 0xBE73 }, +{ 0xBE74, 0xBE74, 0xBE74 }, +{ 0xBE75, 0xBE75, 0xBE75 }, +{ 0xBE76, 0xBE76, 0xBE76 }, +{ 0xBE77, 0xBE77, 0xBE77 }, +{ 0xBE78, 0xBE78, 0xBE78 }, +{ 0xBE79, 0xBE79, 0xBE79 }, +{ 0xBE7A, 0xBE7A, 0xBE7A }, +{ 0xBE7B, 0xBE7B, 0xBE7B }, +{ 0xBE7C, 0xBE7C, 0xBE7C }, +{ 0xBE7D, 0xBE7D, 0xBE7D }, +{ 0xBE7E, 0xBE7E, 0xBE7E }, +{ 0xBE7F, 0xBE7F, 0xBE7F }, +{ 0xBE80, 0xBE80, 0xBE80 }, +{ 0xBE81, 0xBE81, 0xBE81 }, +{ 0xBE82, 0xBE82, 0xBE82 }, +{ 0xBE83, 0xBE83, 0xBE83 }, +{ 0xBE84, 0xBE84, 0xBE84 }, +{ 0xBE85, 0xBE85, 0xBE85 }, +{ 0xBE86, 0xBE86, 0xBE86 }, +{ 0xBE87, 0xBE87, 0xBE87 }, +{ 0xBE88, 0xBE88, 0xBE88 }, +{ 0xBE89, 0xBE89, 0xBE89 }, +{ 0xBE8A, 0xBE8A, 0xBE8A }, +{ 0xBE8B, 0xBE8B, 0xBE8B }, +{ 0xBE8C, 0xBE8C, 0xBE8C }, +{ 0xBE8D, 0xBE8D, 0xBE8D }, +{ 0xBE8E, 0xBE8E, 0xBE8E }, +{ 0xBE8F, 0xBE8F, 0xBE8F }, +{ 0xBE90, 0xBE90, 0xBE90 }, +{ 0xBE91, 0xBE91, 0xBE91 }, +{ 0xBE92, 0xBE92, 0xBE92 }, +{ 0xBE93, 0xBE93, 0xBE93 }, +{ 0xBE94, 0xBE94, 0xBE94 }, +{ 0xBE95, 0xBE95, 0xBE95 }, +{ 0xBE96, 0xBE96, 0xBE96 }, +{ 0xBE97, 0xBE97, 0xBE97 }, +{ 0xBE98, 0xBE98, 0xBE98 }, +{ 0xBE99, 0xBE99, 0xBE99 }, +{ 0xBE9A, 0xBE9A, 0xBE9A }, +{ 0xBE9B, 0xBE9B, 0xBE9B }, +{ 0xBE9C, 0xBE9C, 0xBE9C }, +{ 0xBE9D, 0xBE9D, 0xBE9D }, +{ 0xBE9E, 0xBE9E, 0xBE9E }, +{ 0xBE9F, 0xBE9F, 0xBE9F }, +{ 0xBEA0, 0xBEA0, 0xBEA0 }, +{ 0xBEA1, 0xBEA1, 0xBEA1 }, +{ 0xBEA2, 0xBEA2, 0xBEA2 }, +{ 0xBEA3, 0xBEA3, 0xBEA3 }, +{ 0xBEA4, 0xBEA4, 0xBEA4 }, +{ 0xBEA5, 0xBEA5, 0xBEA5 }, +{ 0xBEA6, 0xBEA6, 0xBEA6 }, +{ 0xBEA7, 0xBEA7, 0xBEA7 }, +{ 0xBEA8, 0xBEA8, 0xBEA8 }, +{ 0xBEA9, 0xBEA9, 0xBEA9 }, +{ 0xBEAA, 0xBEAA, 0xBEAA }, +{ 0xBEAB, 0xBEAB, 0xBEAB }, +{ 0xBEAC, 0xBEAC, 0xBEAC }, +{ 0xBEAD, 0xBEAD, 0xBEAD }, +{ 0xBEAE, 0xBEAE, 0xBEAE }, +{ 0xBEAF, 0xBEAF, 0xBEAF }, +{ 0xBEB0, 0xBEB0, 0xBEB0 }, +{ 0xBEB1, 0xBEB1, 0xBEB1 }, +{ 0xBEB2, 0xBEB2, 0xBEB2 }, +{ 0xBEB3, 0xBEB3, 0xBEB3 }, +{ 0xBEB4, 0xBEB4, 0xBEB4 }, +{ 0xBEB5, 0xBEB5, 0xBEB5 }, +{ 0xBEB6, 0xBEB6, 0xBEB6 }, +{ 0xBEB7, 0xBEB7, 0xBEB7 }, +{ 0xBEB8, 0xBEB8, 0xBEB8 }, +{ 0xBEB9, 0xBEB9, 0xBEB9 }, +{ 0xBEBA, 0xBEBA, 0xBEBA }, +{ 0xBEBB, 0xBEBB, 0xBEBB }, +{ 0xBEBC, 0xBEBC, 0xBEBC }, +{ 0xBEBD, 0xBEBD, 0xBEBD }, +{ 0xBEBE, 0xBEBE, 0xBEBE }, +{ 0xBEBF, 0xBEBF, 0xBEBF }, +{ 0xBEC0, 0xBEC0, 0xBEC0 }, +{ 0xBEC1, 0xBEC1, 0xBEC1 }, +{ 0xBEC2, 0xBEC2, 0xBEC2 }, +{ 0xBEC3, 0xBEC3, 0xBEC3 }, +{ 0xBEC4, 0xBEC4, 0xBEC4 }, +{ 0xBEC5, 0xBEC5, 0xBEC5 }, +{ 0xBEC6, 0xBEC6, 0xBEC6 }, +{ 0xBEC7, 0xBEC7, 0xBEC7 }, +{ 0xBEC8, 0xBEC8, 0xBEC8 }, +{ 0xBEC9, 0xBEC9, 0xBEC9 }, +{ 0xBECA, 0xBECA, 0xBECA }, +{ 0xBECB, 0xBECB, 0xBECB }, +{ 0xBECC, 0xBECC, 0xBECC }, +{ 0xBECD, 0xBECD, 0xBECD }, +{ 0xBECE, 0xBECE, 0xBECE }, +{ 0xBECF, 0xBECF, 0xBECF }, +{ 0xBED0, 0xBED0, 0xBED0 }, +{ 0xBED1, 0xBED1, 0xBED1 }, +{ 0xBED2, 0xBED2, 0xBED2 }, +{ 0xBED3, 0xBED3, 0xBED3 }, +{ 0xBED4, 0xBED4, 0xBED4 }, +{ 0xBED5, 0xBED5, 0xBED5 }, +{ 0xBED6, 0xBED6, 0xBED6 }, +{ 0xBED7, 0xBED7, 0xBED7 }, +{ 0xBED8, 0xBED8, 0xBED8 }, +{ 0xBED9, 0xBED9, 0xBED9 }, +{ 0xBEDA, 0xBEDA, 0xBEDA }, +{ 0xBEDB, 0xBEDB, 0xBEDB }, +{ 0xBEDC, 0xBEDC, 0xBEDC }, +{ 0xBEDD, 0xBEDD, 0xBEDD }, +{ 0xBEDE, 0xBEDE, 0xBEDE }, +{ 0xBEDF, 0xBEDF, 0xBEDF }, +{ 0xBEE0, 0xBEE0, 0xBEE0 }, +{ 0xBEE1, 0xBEE1, 0xBEE1 }, +{ 0xBEE2, 0xBEE2, 0xBEE2 }, +{ 0xBEE3, 0xBEE3, 0xBEE3 }, +{ 0xBEE4, 0xBEE4, 0xBEE4 }, +{ 0xBEE5, 0xBEE5, 0xBEE5 }, +{ 0xBEE6, 0xBEE6, 0xBEE6 }, +{ 0xBEE7, 0xBEE7, 0xBEE7 }, +{ 0xBEE8, 0xBEE8, 0xBEE8 }, +{ 0xBEE9, 0xBEE9, 0xBEE9 }, +{ 0xBEEA, 0xBEEA, 0xBEEA }, +{ 0xBEEB, 0xBEEB, 0xBEEB }, +{ 0xBEEC, 0xBEEC, 0xBEEC }, +{ 0xBEED, 0xBEED, 0xBEED }, +{ 0xBEEE, 0xBEEE, 0xBEEE }, +{ 0xBEEF, 0xBEEF, 0xBEEF }, +{ 0xBEF0, 0xBEF0, 0xBEF0 }, +{ 0xBEF1, 0xBEF1, 0xBEF1 }, +{ 0xBEF2, 0xBEF2, 0xBEF2 }, +{ 0xBEF3, 0xBEF3, 0xBEF3 }, +{ 0xBEF4, 0xBEF4, 0xBEF4 }, +{ 0xBEF5, 0xBEF5, 0xBEF5 }, +{ 0xBEF6, 0xBEF6, 0xBEF6 }, +{ 0xBEF7, 0xBEF7, 0xBEF7 }, +{ 0xBEF8, 0xBEF8, 0xBEF8 }, +{ 0xBEF9, 0xBEF9, 0xBEF9 }, +{ 0xBEFA, 0xBEFA, 0xBEFA }, +{ 0xBEFB, 0xBEFB, 0xBEFB }, +{ 0xBEFC, 0xBEFC, 0xBEFC }, +{ 0xBEFD, 0xBEFD, 0xBEFD }, +{ 0xBEFE, 0xBEFE, 0xBEFE }, +{ 0xBEFF, 0xBEFF, 0xBEFF }, +{ 0xBF00, 0xBF00, 0xBF00 }, +{ 0xBF01, 0xBF01, 0xBF01 }, +{ 0xBF02, 0xBF02, 0xBF02 }, +{ 0xBF03, 0xBF03, 0xBF03 }, +{ 0xBF04, 0xBF04, 0xBF04 }, +{ 0xBF05, 0xBF05, 0xBF05 }, +{ 0xBF06, 0xBF06, 0xBF06 }, +{ 0xBF07, 0xBF07, 0xBF07 }, +{ 0xBF08, 0xBF08, 0xBF08 }, +{ 0xBF09, 0xBF09, 0xBF09 }, +{ 0xBF0A, 0xBF0A, 0xBF0A }, +{ 0xBF0B, 0xBF0B, 0xBF0B }, +{ 0xBF0C, 0xBF0C, 0xBF0C }, +{ 0xBF0D, 0xBF0D, 0xBF0D }, +{ 0xBF0E, 0xBF0E, 0xBF0E }, +{ 0xBF0F, 0xBF0F, 0xBF0F }, +{ 0xBF10, 0xBF10, 0xBF10 }, +{ 0xBF11, 0xBF11, 0xBF11 }, +{ 0xBF12, 0xBF12, 0xBF12 }, +{ 0xBF13, 0xBF13, 0xBF13 }, +{ 0xBF14, 0xBF14, 0xBF14 }, +{ 0xBF15, 0xBF15, 0xBF15 }, +{ 0xBF16, 0xBF16, 0xBF16 }, +{ 0xBF17, 0xBF17, 0xBF17 }, +{ 0xBF18, 0xBF18, 0xBF18 }, +{ 0xBF19, 0xBF19, 0xBF19 }, +{ 0xBF1A, 0xBF1A, 0xBF1A }, +{ 0xBF1B, 0xBF1B, 0xBF1B }, +{ 0xBF1C, 0xBF1C, 0xBF1C }, +{ 0xBF1D, 0xBF1D, 0xBF1D }, +{ 0xBF1E, 0xBF1E, 0xBF1E }, +{ 0xBF1F, 0xBF1F, 0xBF1F }, +{ 0xBF20, 0xBF20, 0xBF20 }, +{ 0xBF21, 0xBF21, 0xBF21 }, +{ 0xBF22, 0xBF22, 0xBF22 }, +{ 0xBF23, 0xBF23, 0xBF23 }, +{ 0xBF24, 0xBF24, 0xBF24 }, +{ 0xBF25, 0xBF25, 0xBF25 }, +{ 0xBF26, 0xBF26, 0xBF26 }, +{ 0xBF27, 0xBF27, 0xBF27 }, +{ 0xBF28, 0xBF28, 0xBF28 }, +{ 0xBF29, 0xBF29, 0xBF29 }, +{ 0xBF2A, 0xBF2A, 0xBF2A }, +{ 0xBF2B, 0xBF2B, 0xBF2B }, +{ 0xBF2C, 0xBF2C, 0xBF2C }, +{ 0xBF2D, 0xBF2D, 0xBF2D }, +{ 0xBF2E, 0xBF2E, 0xBF2E }, +{ 0xBF2F, 0xBF2F, 0xBF2F }, +{ 0xBF30, 0xBF30, 0xBF30 }, +{ 0xBF31, 0xBF31, 0xBF31 }, +{ 0xBF32, 0xBF32, 0xBF32 }, +{ 0xBF33, 0xBF33, 0xBF33 }, +{ 0xBF34, 0xBF34, 0xBF34 }, +{ 0xBF35, 0xBF35, 0xBF35 }, +{ 0xBF36, 0xBF36, 0xBF36 }, +{ 0xBF37, 0xBF37, 0xBF37 }, +{ 0xBF38, 0xBF38, 0xBF38 }, +{ 0xBF39, 0xBF39, 0xBF39 }, +{ 0xBF3A, 0xBF3A, 0xBF3A }, +{ 0xBF3B, 0xBF3B, 0xBF3B }, +{ 0xBF3C, 0xBF3C, 0xBF3C }, +{ 0xBF3D, 0xBF3D, 0xBF3D }, +{ 0xBF3E, 0xBF3E, 0xBF3E }, +{ 0xBF3F, 0xBF3F, 0xBF3F }, +{ 0xBF40, 0xBF40, 0xBF40 }, +{ 0xBF41, 0xBF41, 0xBF41 }, +{ 0xBF42, 0xBF42, 0xBF42 }, +{ 0xBF43, 0xBF43, 0xBF43 }, +{ 0xBF44, 0xBF44, 0xBF44 }, +{ 0xBF45, 0xBF45, 0xBF45 }, +{ 0xBF46, 0xBF46, 0xBF46 }, +{ 0xBF47, 0xBF47, 0xBF47 }, +{ 0xBF48, 0xBF48, 0xBF48 }, +{ 0xBF49, 0xBF49, 0xBF49 }, +{ 0xBF4A, 0xBF4A, 0xBF4A }, +{ 0xBF4B, 0xBF4B, 0xBF4B }, +{ 0xBF4C, 0xBF4C, 0xBF4C }, +{ 0xBF4D, 0xBF4D, 0xBF4D }, +{ 0xBF4E, 0xBF4E, 0xBF4E }, +{ 0xBF4F, 0xBF4F, 0xBF4F }, +{ 0xBF50, 0xBF50, 0xBF50 }, +{ 0xBF51, 0xBF51, 0xBF51 }, +{ 0xBF52, 0xBF52, 0xBF52 }, +{ 0xBF53, 0xBF53, 0xBF53 }, +{ 0xBF54, 0xBF54, 0xBF54 }, +{ 0xBF55, 0xBF55, 0xBF55 }, +{ 0xBF56, 0xBF56, 0xBF56 }, +{ 0xBF57, 0xBF57, 0xBF57 }, +{ 0xBF58, 0xBF58, 0xBF58 }, +{ 0xBF59, 0xBF59, 0xBF59 }, +{ 0xBF5A, 0xBF5A, 0xBF5A }, +{ 0xBF5B, 0xBF5B, 0xBF5B }, +{ 0xBF5C, 0xBF5C, 0xBF5C }, +{ 0xBF5D, 0xBF5D, 0xBF5D }, +{ 0xBF5E, 0xBF5E, 0xBF5E }, +{ 0xBF5F, 0xBF5F, 0xBF5F }, +{ 0xBF60, 0xBF60, 0xBF60 }, +{ 0xBF61, 0xBF61, 0xBF61 }, +{ 0xBF62, 0xBF62, 0xBF62 }, +{ 0xBF63, 0xBF63, 0xBF63 }, +{ 0xBF64, 0xBF64, 0xBF64 }, +{ 0xBF65, 0xBF65, 0xBF65 }, +{ 0xBF66, 0xBF66, 0xBF66 }, +{ 0xBF67, 0xBF67, 0xBF67 }, +{ 0xBF68, 0xBF68, 0xBF68 }, +{ 0xBF69, 0xBF69, 0xBF69 }, +{ 0xBF6A, 0xBF6A, 0xBF6A }, +{ 0xBF6B, 0xBF6B, 0xBF6B }, +{ 0xBF6C, 0xBF6C, 0xBF6C }, +{ 0xBF6D, 0xBF6D, 0xBF6D }, +{ 0xBF6E, 0xBF6E, 0xBF6E }, +{ 0xBF6F, 0xBF6F, 0xBF6F }, +{ 0xBF70, 0xBF70, 0xBF70 }, +{ 0xBF71, 0xBF71, 0xBF71 }, +{ 0xBF72, 0xBF72, 0xBF72 }, +{ 0xBF73, 0xBF73, 0xBF73 }, +{ 0xBF74, 0xBF74, 0xBF74 }, +{ 0xBF75, 0xBF75, 0xBF75 }, +{ 0xBF76, 0xBF76, 0xBF76 }, +{ 0xBF77, 0xBF77, 0xBF77 }, +{ 0xBF78, 0xBF78, 0xBF78 }, +{ 0xBF79, 0xBF79, 0xBF79 }, +{ 0xBF7A, 0xBF7A, 0xBF7A }, +{ 0xBF7B, 0xBF7B, 0xBF7B }, +{ 0xBF7C, 0xBF7C, 0xBF7C }, +{ 0xBF7D, 0xBF7D, 0xBF7D }, +{ 0xBF7E, 0xBF7E, 0xBF7E }, +{ 0xBF7F, 0xBF7F, 0xBF7F }, +{ 0xBF80, 0xBF80, 0xBF80 }, +{ 0xBF81, 0xBF81, 0xBF81 }, +{ 0xBF82, 0xBF82, 0xBF82 }, +{ 0xBF83, 0xBF83, 0xBF83 }, +{ 0xBF84, 0xBF84, 0xBF84 }, +{ 0xBF85, 0xBF85, 0xBF85 }, +{ 0xBF86, 0xBF86, 0xBF86 }, +{ 0xBF87, 0xBF87, 0xBF87 }, +{ 0xBF88, 0xBF88, 0xBF88 }, +{ 0xBF89, 0xBF89, 0xBF89 }, +{ 0xBF8A, 0xBF8A, 0xBF8A }, +{ 0xBF8B, 0xBF8B, 0xBF8B }, +{ 0xBF8C, 0xBF8C, 0xBF8C }, +{ 0xBF8D, 0xBF8D, 0xBF8D }, +{ 0xBF8E, 0xBF8E, 0xBF8E }, +{ 0xBF8F, 0xBF8F, 0xBF8F }, +{ 0xBF90, 0xBF90, 0xBF90 }, +{ 0xBF91, 0xBF91, 0xBF91 }, +{ 0xBF92, 0xBF92, 0xBF92 }, +{ 0xBF93, 0xBF93, 0xBF93 }, +{ 0xBF94, 0xBF94, 0xBF94 }, +{ 0xBF95, 0xBF95, 0xBF95 }, +{ 0xBF96, 0xBF96, 0xBF96 }, +{ 0xBF97, 0xBF97, 0xBF97 }, +{ 0xBF98, 0xBF98, 0xBF98 }, +{ 0xBF99, 0xBF99, 0xBF99 }, +{ 0xBF9A, 0xBF9A, 0xBF9A }, +{ 0xBF9B, 0xBF9B, 0xBF9B }, +{ 0xBF9C, 0xBF9C, 0xBF9C }, +{ 0xBF9D, 0xBF9D, 0xBF9D }, +{ 0xBF9E, 0xBF9E, 0xBF9E }, +{ 0xBF9F, 0xBF9F, 0xBF9F }, +{ 0xBFA0, 0xBFA0, 0xBFA0 }, +{ 0xBFA1, 0xBFA1, 0xBFA1 }, +{ 0xBFA2, 0xBFA2, 0xBFA2 }, +{ 0xBFA3, 0xBFA3, 0xBFA3 }, +{ 0xBFA4, 0xBFA4, 0xBFA4 }, +{ 0xBFA5, 0xBFA5, 0xBFA5 }, +{ 0xBFA6, 0xBFA6, 0xBFA6 }, +{ 0xBFA7, 0xBFA7, 0xBFA7 }, +{ 0xBFA8, 0xBFA8, 0xBFA8 }, +{ 0xBFA9, 0xBFA9, 0xBFA9 }, +{ 0xBFAA, 0xBFAA, 0xBFAA }, +{ 0xBFAB, 0xBFAB, 0xBFAB }, +{ 0xBFAC, 0xBFAC, 0xBFAC }, +{ 0xBFAD, 0xBFAD, 0xBFAD }, +{ 0xBFAE, 0xBFAE, 0xBFAE }, +{ 0xBFAF, 0xBFAF, 0xBFAF }, +{ 0xBFB0, 0xBFB0, 0xBFB0 }, +{ 0xBFB1, 0xBFB1, 0xBFB1 }, +{ 0xBFB2, 0xBFB2, 0xBFB2 }, +{ 0xBFB3, 0xBFB3, 0xBFB3 }, +{ 0xBFB4, 0xBFB4, 0xBFB4 }, +{ 0xBFB5, 0xBFB5, 0xBFB5 }, +{ 0xBFB6, 0xBFB6, 0xBFB6 }, +{ 0xBFB7, 0xBFB7, 0xBFB7 }, +{ 0xBFB8, 0xBFB8, 0xBFB8 }, +{ 0xBFB9, 0xBFB9, 0xBFB9 }, +{ 0xBFBA, 0xBFBA, 0xBFBA }, +{ 0xBFBB, 0xBFBB, 0xBFBB }, +{ 0xBFBC, 0xBFBC, 0xBFBC }, +{ 0xBFBD, 0xBFBD, 0xBFBD }, +{ 0xBFBE, 0xBFBE, 0xBFBE }, +{ 0xBFBF, 0xBFBF, 0xBFBF }, +{ 0xBFC0, 0xBFC0, 0xBFC0 }, +{ 0xBFC1, 0xBFC1, 0xBFC1 }, +{ 0xBFC2, 0xBFC2, 0xBFC2 }, +{ 0xBFC3, 0xBFC3, 0xBFC3 }, +{ 0xBFC4, 0xBFC4, 0xBFC4 }, +{ 0xBFC5, 0xBFC5, 0xBFC5 }, +{ 0xBFC6, 0xBFC6, 0xBFC6 }, +{ 0xBFC7, 0xBFC7, 0xBFC7 }, +{ 0xBFC8, 0xBFC8, 0xBFC8 }, +{ 0xBFC9, 0xBFC9, 0xBFC9 }, +{ 0xBFCA, 0xBFCA, 0xBFCA }, +{ 0xBFCB, 0xBFCB, 0xBFCB }, +{ 0xBFCC, 0xBFCC, 0xBFCC }, +{ 0xBFCD, 0xBFCD, 0xBFCD }, +{ 0xBFCE, 0xBFCE, 0xBFCE }, +{ 0xBFCF, 0xBFCF, 0xBFCF }, +{ 0xBFD0, 0xBFD0, 0xBFD0 }, +{ 0xBFD1, 0xBFD1, 0xBFD1 }, +{ 0xBFD2, 0xBFD2, 0xBFD2 }, +{ 0xBFD3, 0xBFD3, 0xBFD3 }, +{ 0xBFD4, 0xBFD4, 0xBFD4 }, +{ 0xBFD5, 0xBFD5, 0xBFD5 }, +{ 0xBFD6, 0xBFD6, 0xBFD6 }, +{ 0xBFD7, 0xBFD7, 0xBFD7 }, +{ 0xBFD8, 0xBFD8, 0xBFD8 }, +{ 0xBFD9, 0xBFD9, 0xBFD9 }, +{ 0xBFDA, 0xBFDA, 0xBFDA }, +{ 0xBFDB, 0xBFDB, 0xBFDB }, +{ 0xBFDC, 0xBFDC, 0xBFDC }, +{ 0xBFDD, 0xBFDD, 0xBFDD }, +{ 0xBFDE, 0xBFDE, 0xBFDE }, +{ 0xBFDF, 0xBFDF, 0xBFDF }, +{ 0xBFE0, 0xBFE0, 0xBFE0 }, +{ 0xBFE1, 0xBFE1, 0xBFE1 }, +{ 0xBFE2, 0xBFE2, 0xBFE2 }, +{ 0xBFE3, 0xBFE3, 0xBFE3 }, +{ 0xBFE4, 0xBFE4, 0xBFE4 }, +{ 0xBFE5, 0xBFE5, 0xBFE5 }, +{ 0xBFE6, 0xBFE6, 0xBFE6 }, +{ 0xBFE7, 0xBFE7, 0xBFE7 }, +{ 0xBFE8, 0xBFE8, 0xBFE8 }, +{ 0xBFE9, 0xBFE9, 0xBFE9 }, +{ 0xBFEA, 0xBFEA, 0xBFEA }, +{ 0xBFEB, 0xBFEB, 0xBFEB }, +{ 0xBFEC, 0xBFEC, 0xBFEC }, +{ 0xBFED, 0xBFED, 0xBFED }, +{ 0xBFEE, 0xBFEE, 0xBFEE }, +{ 0xBFEF, 0xBFEF, 0xBFEF }, +{ 0xBFF0, 0xBFF0, 0xBFF0 }, +{ 0xBFF1, 0xBFF1, 0xBFF1 }, +{ 0xBFF2, 0xBFF2, 0xBFF2 }, +{ 0xBFF3, 0xBFF3, 0xBFF3 }, +{ 0xBFF4, 0xBFF4, 0xBFF4 }, +{ 0xBFF5, 0xBFF5, 0xBFF5 }, +{ 0xBFF6, 0xBFF6, 0xBFF6 }, +{ 0xBFF7, 0xBFF7, 0xBFF7 }, +{ 0xBFF8, 0xBFF8, 0xBFF8 }, +{ 0xBFF9, 0xBFF9, 0xBFF9 }, +{ 0xBFFA, 0xBFFA, 0xBFFA }, +{ 0xBFFB, 0xBFFB, 0xBFFB }, +{ 0xBFFC, 0xBFFC, 0xBFFC }, +{ 0xBFFD, 0xBFFD, 0xBFFD }, +{ 0xBFFE, 0xBFFE, 0xBFFE }, +{ 0xBFFF, 0xBFFF, 0xBFFF }, +{ 0xC000, 0xC000, 0xC000 }, +{ 0xC001, 0xC001, 0xC001 }, +{ 0xC002, 0xC002, 0xC002 }, +{ 0xC003, 0xC003, 0xC003 }, +{ 0xC004, 0xC004, 0xC004 }, +{ 0xC005, 0xC005, 0xC005 }, +{ 0xC006, 0xC006, 0xC006 }, +{ 0xC007, 0xC007, 0xC007 }, +{ 0xC008, 0xC008, 0xC008 }, +{ 0xC009, 0xC009, 0xC009 }, +{ 0xC00A, 0xC00A, 0xC00A }, +{ 0xC00B, 0xC00B, 0xC00B }, +{ 0xC00C, 0xC00C, 0xC00C }, +{ 0xC00D, 0xC00D, 0xC00D }, +{ 0xC00E, 0xC00E, 0xC00E }, +{ 0xC00F, 0xC00F, 0xC00F }, +{ 0xC010, 0xC010, 0xC010 }, +{ 0xC011, 0xC011, 0xC011 }, +{ 0xC012, 0xC012, 0xC012 }, +{ 0xC013, 0xC013, 0xC013 }, +{ 0xC014, 0xC014, 0xC014 }, +{ 0xC015, 0xC015, 0xC015 }, +{ 0xC016, 0xC016, 0xC016 }, +{ 0xC017, 0xC017, 0xC017 }, +{ 0xC018, 0xC018, 0xC018 }, +{ 0xC019, 0xC019, 0xC019 }, +{ 0xC01A, 0xC01A, 0xC01A }, +{ 0xC01B, 0xC01B, 0xC01B }, +{ 0xC01C, 0xC01C, 0xC01C }, +{ 0xC01D, 0xC01D, 0xC01D }, +{ 0xC01E, 0xC01E, 0xC01E }, +{ 0xC01F, 0xC01F, 0xC01F }, +{ 0xC020, 0xC020, 0xC020 }, +{ 0xC021, 0xC021, 0xC021 }, +{ 0xC022, 0xC022, 0xC022 }, +{ 0xC023, 0xC023, 0xC023 }, +{ 0xC024, 0xC024, 0xC024 }, +{ 0xC025, 0xC025, 0xC025 }, +{ 0xC026, 0xC026, 0xC026 }, +{ 0xC027, 0xC027, 0xC027 }, +{ 0xC028, 0xC028, 0xC028 }, +{ 0xC029, 0xC029, 0xC029 }, +{ 0xC02A, 0xC02A, 0xC02A }, +{ 0xC02B, 0xC02B, 0xC02B }, +{ 0xC02C, 0xC02C, 0xC02C }, +{ 0xC02D, 0xC02D, 0xC02D }, +{ 0xC02E, 0xC02E, 0xC02E }, +{ 0xC02F, 0xC02F, 0xC02F }, +{ 0xC030, 0xC030, 0xC030 }, +{ 0xC031, 0xC031, 0xC031 }, +{ 0xC032, 0xC032, 0xC032 }, +{ 0xC033, 0xC033, 0xC033 }, +{ 0xC034, 0xC034, 0xC034 }, +{ 0xC035, 0xC035, 0xC035 }, +{ 0xC036, 0xC036, 0xC036 }, +{ 0xC037, 0xC037, 0xC037 }, +{ 0xC038, 0xC038, 0xC038 }, +{ 0xC039, 0xC039, 0xC039 }, +{ 0xC03A, 0xC03A, 0xC03A }, +{ 0xC03B, 0xC03B, 0xC03B }, +{ 0xC03C, 0xC03C, 0xC03C }, +{ 0xC03D, 0xC03D, 0xC03D }, +{ 0xC03E, 0xC03E, 0xC03E }, +{ 0xC03F, 0xC03F, 0xC03F }, +{ 0xC040, 0xC040, 0xC040 }, +{ 0xC041, 0xC041, 0xC041 }, +{ 0xC042, 0xC042, 0xC042 }, +{ 0xC043, 0xC043, 0xC043 }, +{ 0xC044, 0xC044, 0xC044 }, +{ 0xC045, 0xC045, 0xC045 }, +{ 0xC046, 0xC046, 0xC046 }, +{ 0xC047, 0xC047, 0xC047 }, +{ 0xC048, 0xC048, 0xC048 }, +{ 0xC049, 0xC049, 0xC049 }, +{ 0xC04A, 0xC04A, 0xC04A }, +{ 0xC04B, 0xC04B, 0xC04B }, +{ 0xC04C, 0xC04C, 0xC04C }, +{ 0xC04D, 0xC04D, 0xC04D }, +{ 0xC04E, 0xC04E, 0xC04E }, +{ 0xC04F, 0xC04F, 0xC04F }, +{ 0xC050, 0xC050, 0xC050 }, +{ 0xC051, 0xC051, 0xC051 }, +{ 0xC052, 0xC052, 0xC052 }, +{ 0xC053, 0xC053, 0xC053 }, +{ 0xC054, 0xC054, 0xC054 }, +{ 0xC055, 0xC055, 0xC055 }, +{ 0xC056, 0xC056, 0xC056 }, +{ 0xC057, 0xC057, 0xC057 }, +{ 0xC058, 0xC058, 0xC058 }, +{ 0xC059, 0xC059, 0xC059 }, +{ 0xC05A, 0xC05A, 0xC05A }, +{ 0xC05B, 0xC05B, 0xC05B }, +{ 0xC05C, 0xC05C, 0xC05C }, +{ 0xC05D, 0xC05D, 0xC05D }, +{ 0xC05E, 0xC05E, 0xC05E }, +{ 0xC05F, 0xC05F, 0xC05F }, +{ 0xC060, 0xC060, 0xC060 }, +{ 0xC061, 0xC061, 0xC061 }, +{ 0xC062, 0xC062, 0xC062 }, +{ 0xC063, 0xC063, 0xC063 }, +{ 0xC064, 0xC064, 0xC064 }, +{ 0xC065, 0xC065, 0xC065 }, +{ 0xC066, 0xC066, 0xC066 }, +{ 0xC067, 0xC067, 0xC067 }, +{ 0xC068, 0xC068, 0xC068 }, +{ 0xC069, 0xC069, 0xC069 }, +{ 0xC06A, 0xC06A, 0xC06A }, +{ 0xC06B, 0xC06B, 0xC06B }, +{ 0xC06C, 0xC06C, 0xC06C }, +{ 0xC06D, 0xC06D, 0xC06D }, +{ 0xC06E, 0xC06E, 0xC06E }, +{ 0xC06F, 0xC06F, 0xC06F }, +{ 0xC070, 0xC070, 0xC070 }, +{ 0xC071, 0xC071, 0xC071 }, +{ 0xC072, 0xC072, 0xC072 }, +{ 0xC073, 0xC073, 0xC073 }, +{ 0xC074, 0xC074, 0xC074 }, +{ 0xC075, 0xC075, 0xC075 }, +{ 0xC076, 0xC076, 0xC076 }, +{ 0xC077, 0xC077, 0xC077 }, +{ 0xC078, 0xC078, 0xC078 }, +{ 0xC079, 0xC079, 0xC079 }, +{ 0xC07A, 0xC07A, 0xC07A }, +{ 0xC07B, 0xC07B, 0xC07B }, +{ 0xC07C, 0xC07C, 0xC07C }, +{ 0xC07D, 0xC07D, 0xC07D }, +{ 0xC07E, 0xC07E, 0xC07E }, +{ 0xC07F, 0xC07F, 0xC07F }, +{ 0xC080, 0xC080, 0xC080 }, +{ 0xC081, 0xC081, 0xC081 }, +{ 0xC082, 0xC082, 0xC082 }, +{ 0xC083, 0xC083, 0xC083 }, +{ 0xC084, 0xC084, 0xC084 }, +{ 0xC085, 0xC085, 0xC085 }, +{ 0xC086, 0xC086, 0xC086 }, +{ 0xC087, 0xC087, 0xC087 }, +{ 0xC088, 0xC088, 0xC088 }, +{ 0xC089, 0xC089, 0xC089 }, +{ 0xC08A, 0xC08A, 0xC08A }, +{ 0xC08B, 0xC08B, 0xC08B }, +{ 0xC08C, 0xC08C, 0xC08C }, +{ 0xC08D, 0xC08D, 0xC08D }, +{ 0xC08E, 0xC08E, 0xC08E }, +{ 0xC08F, 0xC08F, 0xC08F }, +{ 0xC090, 0xC090, 0xC090 }, +{ 0xC091, 0xC091, 0xC091 }, +{ 0xC092, 0xC092, 0xC092 }, +{ 0xC093, 0xC093, 0xC093 }, +{ 0xC094, 0xC094, 0xC094 }, +{ 0xC095, 0xC095, 0xC095 }, +{ 0xC096, 0xC096, 0xC096 }, +{ 0xC097, 0xC097, 0xC097 }, +{ 0xC098, 0xC098, 0xC098 }, +{ 0xC099, 0xC099, 0xC099 }, +{ 0xC09A, 0xC09A, 0xC09A }, +{ 0xC09B, 0xC09B, 0xC09B }, +{ 0xC09C, 0xC09C, 0xC09C }, +{ 0xC09D, 0xC09D, 0xC09D }, +{ 0xC09E, 0xC09E, 0xC09E }, +{ 0xC09F, 0xC09F, 0xC09F }, +{ 0xC0A0, 0xC0A0, 0xC0A0 }, +{ 0xC0A1, 0xC0A1, 0xC0A1 }, +{ 0xC0A2, 0xC0A2, 0xC0A2 }, +{ 0xC0A3, 0xC0A3, 0xC0A3 }, +{ 0xC0A4, 0xC0A4, 0xC0A4 }, +{ 0xC0A5, 0xC0A5, 0xC0A5 }, +{ 0xC0A6, 0xC0A6, 0xC0A6 }, +{ 0xC0A7, 0xC0A7, 0xC0A7 }, +{ 0xC0A8, 0xC0A8, 0xC0A8 }, +{ 0xC0A9, 0xC0A9, 0xC0A9 }, +{ 0xC0AA, 0xC0AA, 0xC0AA }, +{ 0xC0AB, 0xC0AB, 0xC0AB }, +{ 0xC0AC, 0xC0AC, 0xC0AC }, +{ 0xC0AD, 0xC0AD, 0xC0AD }, +{ 0xC0AE, 0xC0AE, 0xC0AE }, +{ 0xC0AF, 0xC0AF, 0xC0AF }, +{ 0xC0B0, 0xC0B0, 0xC0B0 }, +{ 0xC0B1, 0xC0B1, 0xC0B1 }, +{ 0xC0B2, 0xC0B2, 0xC0B2 }, +{ 0xC0B3, 0xC0B3, 0xC0B3 }, +{ 0xC0B4, 0xC0B4, 0xC0B4 }, +{ 0xC0B5, 0xC0B5, 0xC0B5 }, +{ 0xC0B6, 0xC0B6, 0xC0B6 }, +{ 0xC0B7, 0xC0B7, 0xC0B7 }, +{ 0xC0B8, 0xC0B8, 0xC0B8 }, +{ 0xC0B9, 0xC0B9, 0xC0B9 }, +{ 0xC0BA, 0xC0BA, 0xC0BA }, +{ 0xC0BB, 0xC0BB, 0xC0BB }, +{ 0xC0BC, 0xC0BC, 0xC0BC }, +{ 0xC0BD, 0xC0BD, 0xC0BD }, +{ 0xC0BE, 0xC0BE, 0xC0BE }, +{ 0xC0BF, 0xC0BF, 0xC0BF }, +{ 0xC0C0, 0xC0C0, 0xC0C0 }, +{ 0xC0C1, 0xC0C1, 0xC0C1 }, +{ 0xC0C2, 0xC0C2, 0xC0C2 }, +{ 0xC0C3, 0xC0C3, 0xC0C3 }, +{ 0xC0C4, 0xC0C4, 0xC0C4 }, +{ 0xC0C5, 0xC0C5, 0xC0C5 }, +{ 0xC0C6, 0xC0C6, 0xC0C6 }, +{ 0xC0C7, 0xC0C7, 0xC0C7 }, +{ 0xC0C8, 0xC0C8, 0xC0C8 }, +{ 0xC0C9, 0xC0C9, 0xC0C9 }, +{ 0xC0CA, 0xC0CA, 0xC0CA }, +{ 0xC0CB, 0xC0CB, 0xC0CB }, +{ 0xC0CC, 0xC0CC, 0xC0CC }, +{ 0xC0CD, 0xC0CD, 0xC0CD }, +{ 0xC0CE, 0xC0CE, 0xC0CE }, +{ 0xC0CF, 0xC0CF, 0xC0CF }, +{ 0xC0D0, 0xC0D0, 0xC0D0 }, +{ 0xC0D1, 0xC0D1, 0xC0D1 }, +{ 0xC0D2, 0xC0D2, 0xC0D2 }, +{ 0xC0D3, 0xC0D3, 0xC0D3 }, +{ 0xC0D4, 0xC0D4, 0xC0D4 }, +{ 0xC0D5, 0xC0D5, 0xC0D5 }, +{ 0xC0D6, 0xC0D6, 0xC0D6 }, +{ 0xC0D7, 0xC0D7, 0xC0D7 }, +{ 0xC0D8, 0xC0D8, 0xC0D8 }, +{ 0xC0D9, 0xC0D9, 0xC0D9 }, +{ 0xC0DA, 0xC0DA, 0xC0DA }, +{ 0xC0DB, 0xC0DB, 0xC0DB }, +{ 0xC0DC, 0xC0DC, 0xC0DC }, +{ 0xC0DD, 0xC0DD, 0xC0DD }, +{ 0xC0DE, 0xC0DE, 0xC0DE }, +{ 0xC0DF, 0xC0DF, 0xC0DF }, +{ 0xC0E0, 0xC0E0, 0xC0E0 }, +{ 0xC0E1, 0xC0E1, 0xC0E1 }, +{ 0xC0E2, 0xC0E2, 0xC0E2 }, +{ 0xC0E3, 0xC0E3, 0xC0E3 }, +{ 0xC0E4, 0xC0E4, 0xC0E4 }, +{ 0xC0E5, 0xC0E5, 0xC0E5 }, +{ 0xC0E6, 0xC0E6, 0xC0E6 }, +{ 0xC0E7, 0xC0E7, 0xC0E7 }, +{ 0xC0E8, 0xC0E8, 0xC0E8 }, +{ 0xC0E9, 0xC0E9, 0xC0E9 }, +{ 0xC0EA, 0xC0EA, 0xC0EA }, +{ 0xC0EB, 0xC0EB, 0xC0EB }, +{ 0xC0EC, 0xC0EC, 0xC0EC }, +{ 0xC0ED, 0xC0ED, 0xC0ED }, +{ 0xC0EE, 0xC0EE, 0xC0EE }, +{ 0xC0EF, 0xC0EF, 0xC0EF }, +{ 0xC0F0, 0xC0F0, 0xC0F0 }, +{ 0xC0F1, 0xC0F1, 0xC0F1 }, +{ 0xC0F2, 0xC0F2, 0xC0F2 }, +{ 0xC0F3, 0xC0F3, 0xC0F3 }, +{ 0xC0F4, 0xC0F4, 0xC0F4 }, +{ 0xC0F5, 0xC0F5, 0xC0F5 }, +{ 0xC0F6, 0xC0F6, 0xC0F6 }, +{ 0xC0F7, 0xC0F7, 0xC0F7 }, +{ 0xC0F8, 0xC0F8, 0xC0F8 }, +{ 0xC0F9, 0xC0F9, 0xC0F9 }, +{ 0xC0FA, 0xC0FA, 0xC0FA }, +{ 0xC0FB, 0xC0FB, 0xC0FB }, +{ 0xC0FC, 0xC0FC, 0xC0FC }, +{ 0xC0FD, 0xC0FD, 0xC0FD }, +{ 0xC0FE, 0xC0FE, 0xC0FE }, +{ 0xC0FF, 0xC0FF, 0xC0FF }, +{ 0xC100, 0xC100, 0xC100 }, +{ 0xC101, 0xC101, 0xC101 }, +{ 0xC102, 0xC102, 0xC102 }, +{ 0xC103, 0xC103, 0xC103 }, +{ 0xC104, 0xC104, 0xC104 }, +{ 0xC105, 0xC105, 0xC105 }, +{ 0xC106, 0xC106, 0xC106 }, +{ 0xC107, 0xC107, 0xC107 }, +{ 0xC108, 0xC108, 0xC108 }, +{ 0xC109, 0xC109, 0xC109 }, +{ 0xC10A, 0xC10A, 0xC10A }, +{ 0xC10B, 0xC10B, 0xC10B }, +{ 0xC10C, 0xC10C, 0xC10C }, +{ 0xC10D, 0xC10D, 0xC10D }, +{ 0xC10E, 0xC10E, 0xC10E }, +{ 0xC10F, 0xC10F, 0xC10F }, +{ 0xC110, 0xC110, 0xC110 }, +{ 0xC111, 0xC111, 0xC111 }, +{ 0xC112, 0xC112, 0xC112 }, +{ 0xC113, 0xC113, 0xC113 }, +{ 0xC114, 0xC114, 0xC114 }, +{ 0xC115, 0xC115, 0xC115 }, +{ 0xC116, 0xC116, 0xC116 }, +{ 0xC117, 0xC117, 0xC117 }, +{ 0xC118, 0xC118, 0xC118 }, +{ 0xC119, 0xC119, 0xC119 }, +{ 0xC11A, 0xC11A, 0xC11A }, +{ 0xC11B, 0xC11B, 0xC11B }, +{ 0xC11C, 0xC11C, 0xC11C }, +{ 0xC11D, 0xC11D, 0xC11D }, +{ 0xC11E, 0xC11E, 0xC11E }, +{ 0xC11F, 0xC11F, 0xC11F }, +{ 0xC120, 0xC120, 0xC120 }, +{ 0xC121, 0xC121, 0xC121 }, +{ 0xC122, 0xC122, 0xC122 }, +{ 0xC123, 0xC123, 0xC123 }, +{ 0xC124, 0xC124, 0xC124 }, +{ 0xC125, 0xC125, 0xC125 }, +{ 0xC126, 0xC126, 0xC126 }, +{ 0xC127, 0xC127, 0xC127 }, +{ 0xC128, 0xC128, 0xC128 }, +{ 0xC129, 0xC129, 0xC129 }, +{ 0xC12A, 0xC12A, 0xC12A }, +{ 0xC12B, 0xC12B, 0xC12B }, +{ 0xC12C, 0xC12C, 0xC12C }, +{ 0xC12D, 0xC12D, 0xC12D }, +{ 0xC12E, 0xC12E, 0xC12E }, +{ 0xC12F, 0xC12F, 0xC12F }, +{ 0xC130, 0xC130, 0xC130 }, +{ 0xC131, 0xC131, 0xC131 }, +{ 0xC132, 0xC132, 0xC132 }, +{ 0xC133, 0xC133, 0xC133 }, +{ 0xC134, 0xC134, 0xC134 }, +{ 0xC135, 0xC135, 0xC135 }, +{ 0xC136, 0xC136, 0xC136 }, +{ 0xC137, 0xC137, 0xC137 }, +{ 0xC138, 0xC138, 0xC138 }, +{ 0xC139, 0xC139, 0xC139 }, +{ 0xC13A, 0xC13A, 0xC13A }, +{ 0xC13B, 0xC13B, 0xC13B }, +{ 0xC13C, 0xC13C, 0xC13C }, +{ 0xC13D, 0xC13D, 0xC13D }, +{ 0xC13E, 0xC13E, 0xC13E }, +{ 0xC13F, 0xC13F, 0xC13F }, +{ 0xC140, 0xC140, 0xC140 }, +{ 0xC141, 0xC141, 0xC141 }, +{ 0xC142, 0xC142, 0xC142 }, +{ 0xC143, 0xC143, 0xC143 }, +{ 0xC144, 0xC144, 0xC144 }, +{ 0xC145, 0xC145, 0xC145 }, +{ 0xC146, 0xC146, 0xC146 }, +{ 0xC147, 0xC147, 0xC147 }, +{ 0xC148, 0xC148, 0xC148 }, +{ 0xC149, 0xC149, 0xC149 }, +{ 0xC14A, 0xC14A, 0xC14A }, +{ 0xC14B, 0xC14B, 0xC14B }, +{ 0xC14C, 0xC14C, 0xC14C }, +{ 0xC14D, 0xC14D, 0xC14D }, +{ 0xC14E, 0xC14E, 0xC14E }, +{ 0xC14F, 0xC14F, 0xC14F }, +{ 0xC150, 0xC150, 0xC150 }, +{ 0xC151, 0xC151, 0xC151 }, +{ 0xC152, 0xC152, 0xC152 }, +{ 0xC153, 0xC153, 0xC153 }, +{ 0xC154, 0xC154, 0xC154 }, +{ 0xC155, 0xC155, 0xC155 }, +{ 0xC156, 0xC156, 0xC156 }, +{ 0xC157, 0xC157, 0xC157 }, +{ 0xC158, 0xC158, 0xC158 }, +{ 0xC159, 0xC159, 0xC159 }, +{ 0xC15A, 0xC15A, 0xC15A }, +{ 0xC15B, 0xC15B, 0xC15B }, +{ 0xC15C, 0xC15C, 0xC15C }, +{ 0xC15D, 0xC15D, 0xC15D }, +{ 0xC15E, 0xC15E, 0xC15E }, +{ 0xC15F, 0xC15F, 0xC15F }, +{ 0xC160, 0xC160, 0xC160 }, +{ 0xC161, 0xC161, 0xC161 }, +{ 0xC162, 0xC162, 0xC162 }, +{ 0xC163, 0xC163, 0xC163 }, +{ 0xC164, 0xC164, 0xC164 }, +{ 0xC165, 0xC165, 0xC165 }, +{ 0xC166, 0xC166, 0xC166 }, +{ 0xC167, 0xC167, 0xC167 }, +{ 0xC168, 0xC168, 0xC168 }, +{ 0xC169, 0xC169, 0xC169 }, +{ 0xC16A, 0xC16A, 0xC16A }, +{ 0xC16B, 0xC16B, 0xC16B }, +{ 0xC16C, 0xC16C, 0xC16C }, +{ 0xC16D, 0xC16D, 0xC16D }, +{ 0xC16E, 0xC16E, 0xC16E }, +{ 0xC16F, 0xC16F, 0xC16F }, +{ 0xC170, 0xC170, 0xC170 }, +{ 0xC171, 0xC171, 0xC171 }, +{ 0xC172, 0xC172, 0xC172 }, +{ 0xC173, 0xC173, 0xC173 }, +{ 0xC174, 0xC174, 0xC174 }, +{ 0xC175, 0xC175, 0xC175 }, +{ 0xC176, 0xC176, 0xC176 }, +{ 0xC177, 0xC177, 0xC177 }, +{ 0xC178, 0xC178, 0xC178 }, +{ 0xC179, 0xC179, 0xC179 }, +{ 0xC17A, 0xC17A, 0xC17A }, +{ 0xC17B, 0xC17B, 0xC17B }, +{ 0xC17C, 0xC17C, 0xC17C }, +{ 0xC17D, 0xC17D, 0xC17D }, +{ 0xC17E, 0xC17E, 0xC17E }, +{ 0xC17F, 0xC17F, 0xC17F }, +{ 0xC180, 0xC180, 0xC180 }, +{ 0xC181, 0xC181, 0xC181 }, +{ 0xC182, 0xC182, 0xC182 }, +{ 0xC183, 0xC183, 0xC183 }, +{ 0xC184, 0xC184, 0xC184 }, +{ 0xC185, 0xC185, 0xC185 }, +{ 0xC186, 0xC186, 0xC186 }, +{ 0xC187, 0xC187, 0xC187 }, +{ 0xC188, 0xC188, 0xC188 }, +{ 0xC189, 0xC189, 0xC189 }, +{ 0xC18A, 0xC18A, 0xC18A }, +{ 0xC18B, 0xC18B, 0xC18B }, +{ 0xC18C, 0xC18C, 0xC18C }, +{ 0xC18D, 0xC18D, 0xC18D }, +{ 0xC18E, 0xC18E, 0xC18E }, +{ 0xC18F, 0xC18F, 0xC18F }, +{ 0xC190, 0xC190, 0xC190 }, +{ 0xC191, 0xC191, 0xC191 }, +{ 0xC192, 0xC192, 0xC192 }, +{ 0xC193, 0xC193, 0xC193 }, +{ 0xC194, 0xC194, 0xC194 }, +{ 0xC195, 0xC195, 0xC195 }, +{ 0xC196, 0xC196, 0xC196 }, +{ 0xC197, 0xC197, 0xC197 }, +{ 0xC198, 0xC198, 0xC198 }, +{ 0xC199, 0xC199, 0xC199 }, +{ 0xC19A, 0xC19A, 0xC19A }, +{ 0xC19B, 0xC19B, 0xC19B }, +{ 0xC19C, 0xC19C, 0xC19C }, +{ 0xC19D, 0xC19D, 0xC19D }, +{ 0xC19E, 0xC19E, 0xC19E }, +{ 0xC19F, 0xC19F, 0xC19F }, +{ 0xC1A0, 0xC1A0, 0xC1A0 }, +{ 0xC1A1, 0xC1A1, 0xC1A1 }, +{ 0xC1A2, 0xC1A2, 0xC1A2 }, +{ 0xC1A3, 0xC1A3, 0xC1A3 }, +{ 0xC1A4, 0xC1A4, 0xC1A4 }, +{ 0xC1A5, 0xC1A5, 0xC1A5 }, +{ 0xC1A6, 0xC1A6, 0xC1A6 }, +{ 0xC1A7, 0xC1A7, 0xC1A7 }, +{ 0xC1A8, 0xC1A8, 0xC1A8 }, +{ 0xC1A9, 0xC1A9, 0xC1A9 }, +{ 0xC1AA, 0xC1AA, 0xC1AA }, +{ 0xC1AB, 0xC1AB, 0xC1AB }, +{ 0xC1AC, 0xC1AC, 0xC1AC }, +{ 0xC1AD, 0xC1AD, 0xC1AD }, +{ 0xC1AE, 0xC1AE, 0xC1AE }, +{ 0xC1AF, 0xC1AF, 0xC1AF }, +{ 0xC1B0, 0xC1B0, 0xC1B0 }, +{ 0xC1B1, 0xC1B1, 0xC1B1 }, +{ 0xC1B2, 0xC1B2, 0xC1B2 }, +{ 0xC1B3, 0xC1B3, 0xC1B3 }, +{ 0xC1B4, 0xC1B4, 0xC1B4 }, +{ 0xC1B5, 0xC1B5, 0xC1B5 }, +{ 0xC1B6, 0xC1B6, 0xC1B6 }, +{ 0xC1B7, 0xC1B7, 0xC1B7 }, +{ 0xC1B8, 0xC1B8, 0xC1B8 }, +{ 0xC1B9, 0xC1B9, 0xC1B9 }, +{ 0xC1BA, 0xC1BA, 0xC1BA }, +{ 0xC1BB, 0xC1BB, 0xC1BB }, +{ 0xC1BC, 0xC1BC, 0xC1BC }, +{ 0xC1BD, 0xC1BD, 0xC1BD }, +{ 0xC1BE, 0xC1BE, 0xC1BE }, +{ 0xC1BF, 0xC1BF, 0xC1BF }, +{ 0xC1C0, 0xC1C0, 0xC1C0 }, +{ 0xC1C1, 0xC1C1, 0xC1C1 }, +{ 0xC1C2, 0xC1C2, 0xC1C2 }, +{ 0xC1C3, 0xC1C3, 0xC1C3 }, +{ 0xC1C4, 0xC1C4, 0xC1C4 }, +{ 0xC1C5, 0xC1C5, 0xC1C5 }, +{ 0xC1C6, 0xC1C6, 0xC1C6 }, +{ 0xC1C7, 0xC1C7, 0xC1C7 }, +{ 0xC1C8, 0xC1C8, 0xC1C8 }, +{ 0xC1C9, 0xC1C9, 0xC1C9 }, +{ 0xC1CA, 0xC1CA, 0xC1CA }, +{ 0xC1CB, 0xC1CB, 0xC1CB }, +{ 0xC1CC, 0xC1CC, 0xC1CC }, +{ 0xC1CD, 0xC1CD, 0xC1CD }, +{ 0xC1CE, 0xC1CE, 0xC1CE }, +{ 0xC1CF, 0xC1CF, 0xC1CF }, +{ 0xC1D0, 0xC1D0, 0xC1D0 }, +{ 0xC1D1, 0xC1D1, 0xC1D1 }, +{ 0xC1D2, 0xC1D2, 0xC1D2 }, +{ 0xC1D3, 0xC1D3, 0xC1D3 }, +{ 0xC1D4, 0xC1D4, 0xC1D4 }, +{ 0xC1D5, 0xC1D5, 0xC1D5 }, +{ 0xC1D6, 0xC1D6, 0xC1D6 }, +{ 0xC1D7, 0xC1D7, 0xC1D7 }, +{ 0xC1D8, 0xC1D8, 0xC1D8 }, +{ 0xC1D9, 0xC1D9, 0xC1D9 }, +{ 0xC1DA, 0xC1DA, 0xC1DA }, +{ 0xC1DB, 0xC1DB, 0xC1DB }, +{ 0xC1DC, 0xC1DC, 0xC1DC }, +{ 0xC1DD, 0xC1DD, 0xC1DD }, +{ 0xC1DE, 0xC1DE, 0xC1DE }, +{ 0xC1DF, 0xC1DF, 0xC1DF }, +{ 0xC1E0, 0xC1E0, 0xC1E0 }, +{ 0xC1E1, 0xC1E1, 0xC1E1 }, +{ 0xC1E2, 0xC1E2, 0xC1E2 }, +{ 0xC1E3, 0xC1E3, 0xC1E3 }, +{ 0xC1E4, 0xC1E4, 0xC1E4 }, +{ 0xC1E5, 0xC1E5, 0xC1E5 }, +{ 0xC1E6, 0xC1E6, 0xC1E6 }, +{ 0xC1E7, 0xC1E7, 0xC1E7 }, +{ 0xC1E8, 0xC1E8, 0xC1E8 }, +{ 0xC1E9, 0xC1E9, 0xC1E9 }, +{ 0xC1EA, 0xC1EA, 0xC1EA }, +{ 0xC1EB, 0xC1EB, 0xC1EB }, +{ 0xC1EC, 0xC1EC, 0xC1EC }, +{ 0xC1ED, 0xC1ED, 0xC1ED }, +{ 0xC1EE, 0xC1EE, 0xC1EE }, +{ 0xC1EF, 0xC1EF, 0xC1EF }, +{ 0xC1F0, 0xC1F0, 0xC1F0 }, +{ 0xC1F1, 0xC1F1, 0xC1F1 }, +{ 0xC1F2, 0xC1F2, 0xC1F2 }, +{ 0xC1F3, 0xC1F3, 0xC1F3 }, +{ 0xC1F4, 0xC1F4, 0xC1F4 }, +{ 0xC1F5, 0xC1F5, 0xC1F5 }, +{ 0xC1F6, 0xC1F6, 0xC1F6 }, +{ 0xC1F7, 0xC1F7, 0xC1F7 }, +{ 0xC1F8, 0xC1F8, 0xC1F8 }, +{ 0xC1F9, 0xC1F9, 0xC1F9 }, +{ 0xC1FA, 0xC1FA, 0xC1FA }, +{ 0xC1FB, 0xC1FB, 0xC1FB }, +{ 0xC1FC, 0xC1FC, 0xC1FC }, +{ 0xC1FD, 0xC1FD, 0xC1FD }, +{ 0xC1FE, 0xC1FE, 0xC1FE }, +{ 0xC1FF, 0xC1FF, 0xC1FF }, +{ 0xC200, 0xC200, 0xC200 }, +{ 0xC201, 0xC201, 0xC201 }, +{ 0xC202, 0xC202, 0xC202 }, +{ 0xC203, 0xC203, 0xC203 }, +{ 0xC204, 0xC204, 0xC204 }, +{ 0xC205, 0xC205, 0xC205 }, +{ 0xC206, 0xC206, 0xC206 }, +{ 0xC207, 0xC207, 0xC207 }, +{ 0xC208, 0xC208, 0xC208 }, +{ 0xC209, 0xC209, 0xC209 }, +{ 0xC20A, 0xC20A, 0xC20A }, +{ 0xC20B, 0xC20B, 0xC20B }, +{ 0xC20C, 0xC20C, 0xC20C }, +{ 0xC20D, 0xC20D, 0xC20D }, +{ 0xC20E, 0xC20E, 0xC20E }, +{ 0xC20F, 0xC20F, 0xC20F }, +{ 0xC210, 0xC210, 0xC210 }, +{ 0xC211, 0xC211, 0xC211 }, +{ 0xC212, 0xC212, 0xC212 }, +{ 0xC213, 0xC213, 0xC213 }, +{ 0xC214, 0xC214, 0xC214 }, +{ 0xC215, 0xC215, 0xC215 }, +{ 0xC216, 0xC216, 0xC216 }, +{ 0xC217, 0xC217, 0xC217 }, +{ 0xC218, 0xC218, 0xC218 }, +{ 0xC219, 0xC219, 0xC219 }, +{ 0xC21A, 0xC21A, 0xC21A }, +{ 0xC21B, 0xC21B, 0xC21B }, +{ 0xC21C, 0xC21C, 0xC21C }, +{ 0xC21D, 0xC21D, 0xC21D }, +{ 0xC21E, 0xC21E, 0xC21E }, +{ 0xC21F, 0xC21F, 0xC21F }, +{ 0xC220, 0xC220, 0xC220 }, +{ 0xC221, 0xC221, 0xC221 }, +{ 0xC222, 0xC222, 0xC222 }, +{ 0xC223, 0xC223, 0xC223 }, +{ 0xC224, 0xC224, 0xC224 }, +{ 0xC225, 0xC225, 0xC225 }, +{ 0xC226, 0xC226, 0xC226 }, +{ 0xC227, 0xC227, 0xC227 }, +{ 0xC228, 0xC228, 0xC228 }, +{ 0xC229, 0xC229, 0xC229 }, +{ 0xC22A, 0xC22A, 0xC22A }, +{ 0xC22B, 0xC22B, 0xC22B }, +{ 0xC22C, 0xC22C, 0xC22C }, +{ 0xC22D, 0xC22D, 0xC22D }, +{ 0xC22E, 0xC22E, 0xC22E }, +{ 0xC22F, 0xC22F, 0xC22F }, +{ 0xC230, 0xC230, 0xC230 }, +{ 0xC231, 0xC231, 0xC231 }, +{ 0xC232, 0xC232, 0xC232 }, +{ 0xC233, 0xC233, 0xC233 }, +{ 0xC234, 0xC234, 0xC234 }, +{ 0xC235, 0xC235, 0xC235 }, +{ 0xC236, 0xC236, 0xC236 }, +{ 0xC237, 0xC237, 0xC237 }, +{ 0xC238, 0xC238, 0xC238 }, +{ 0xC239, 0xC239, 0xC239 }, +{ 0xC23A, 0xC23A, 0xC23A }, +{ 0xC23B, 0xC23B, 0xC23B }, +{ 0xC23C, 0xC23C, 0xC23C }, +{ 0xC23D, 0xC23D, 0xC23D }, +{ 0xC23E, 0xC23E, 0xC23E }, +{ 0xC23F, 0xC23F, 0xC23F }, +{ 0xC240, 0xC240, 0xC240 }, +{ 0xC241, 0xC241, 0xC241 }, +{ 0xC242, 0xC242, 0xC242 }, +{ 0xC243, 0xC243, 0xC243 }, +{ 0xC244, 0xC244, 0xC244 }, +{ 0xC245, 0xC245, 0xC245 }, +{ 0xC246, 0xC246, 0xC246 }, +{ 0xC247, 0xC247, 0xC247 }, +{ 0xC248, 0xC248, 0xC248 }, +{ 0xC249, 0xC249, 0xC249 }, +{ 0xC24A, 0xC24A, 0xC24A }, +{ 0xC24B, 0xC24B, 0xC24B }, +{ 0xC24C, 0xC24C, 0xC24C }, +{ 0xC24D, 0xC24D, 0xC24D }, +{ 0xC24E, 0xC24E, 0xC24E }, +{ 0xC24F, 0xC24F, 0xC24F }, +{ 0xC250, 0xC250, 0xC250 }, +{ 0xC251, 0xC251, 0xC251 }, +{ 0xC252, 0xC252, 0xC252 }, +{ 0xC253, 0xC253, 0xC253 }, +{ 0xC254, 0xC254, 0xC254 }, +{ 0xC255, 0xC255, 0xC255 }, +{ 0xC256, 0xC256, 0xC256 }, +{ 0xC257, 0xC257, 0xC257 }, +{ 0xC258, 0xC258, 0xC258 }, +{ 0xC259, 0xC259, 0xC259 }, +{ 0xC25A, 0xC25A, 0xC25A }, +{ 0xC25B, 0xC25B, 0xC25B }, +{ 0xC25C, 0xC25C, 0xC25C }, +{ 0xC25D, 0xC25D, 0xC25D }, +{ 0xC25E, 0xC25E, 0xC25E }, +{ 0xC25F, 0xC25F, 0xC25F }, +{ 0xC260, 0xC260, 0xC260 }, +{ 0xC261, 0xC261, 0xC261 }, +{ 0xC262, 0xC262, 0xC262 }, +{ 0xC263, 0xC263, 0xC263 }, +{ 0xC264, 0xC264, 0xC264 }, +{ 0xC265, 0xC265, 0xC265 }, +{ 0xC266, 0xC266, 0xC266 }, +{ 0xC267, 0xC267, 0xC267 }, +{ 0xC268, 0xC268, 0xC268 }, +{ 0xC269, 0xC269, 0xC269 }, +{ 0xC26A, 0xC26A, 0xC26A }, +{ 0xC26B, 0xC26B, 0xC26B }, +{ 0xC26C, 0xC26C, 0xC26C }, +{ 0xC26D, 0xC26D, 0xC26D }, +{ 0xC26E, 0xC26E, 0xC26E }, +{ 0xC26F, 0xC26F, 0xC26F }, +{ 0xC270, 0xC270, 0xC270 }, +{ 0xC271, 0xC271, 0xC271 }, +{ 0xC272, 0xC272, 0xC272 }, +{ 0xC273, 0xC273, 0xC273 }, +{ 0xC274, 0xC274, 0xC274 }, +{ 0xC275, 0xC275, 0xC275 }, +{ 0xC276, 0xC276, 0xC276 }, +{ 0xC277, 0xC277, 0xC277 }, +{ 0xC278, 0xC278, 0xC278 }, +{ 0xC279, 0xC279, 0xC279 }, +{ 0xC27A, 0xC27A, 0xC27A }, +{ 0xC27B, 0xC27B, 0xC27B }, +{ 0xC27C, 0xC27C, 0xC27C }, +{ 0xC27D, 0xC27D, 0xC27D }, +{ 0xC27E, 0xC27E, 0xC27E }, +{ 0xC27F, 0xC27F, 0xC27F }, +{ 0xC280, 0xC280, 0xC280 }, +{ 0xC281, 0xC281, 0xC281 }, +{ 0xC282, 0xC282, 0xC282 }, +{ 0xC283, 0xC283, 0xC283 }, +{ 0xC284, 0xC284, 0xC284 }, +{ 0xC285, 0xC285, 0xC285 }, +{ 0xC286, 0xC286, 0xC286 }, +{ 0xC287, 0xC287, 0xC287 }, +{ 0xC288, 0xC288, 0xC288 }, +{ 0xC289, 0xC289, 0xC289 }, +{ 0xC28A, 0xC28A, 0xC28A }, +{ 0xC28B, 0xC28B, 0xC28B }, +{ 0xC28C, 0xC28C, 0xC28C }, +{ 0xC28D, 0xC28D, 0xC28D }, +{ 0xC28E, 0xC28E, 0xC28E }, +{ 0xC28F, 0xC28F, 0xC28F }, +{ 0xC290, 0xC290, 0xC290 }, +{ 0xC291, 0xC291, 0xC291 }, +{ 0xC292, 0xC292, 0xC292 }, +{ 0xC293, 0xC293, 0xC293 }, +{ 0xC294, 0xC294, 0xC294 }, +{ 0xC295, 0xC295, 0xC295 }, +{ 0xC296, 0xC296, 0xC296 }, +{ 0xC297, 0xC297, 0xC297 }, +{ 0xC298, 0xC298, 0xC298 }, +{ 0xC299, 0xC299, 0xC299 }, +{ 0xC29A, 0xC29A, 0xC29A }, +{ 0xC29B, 0xC29B, 0xC29B }, +{ 0xC29C, 0xC29C, 0xC29C }, +{ 0xC29D, 0xC29D, 0xC29D }, +{ 0xC29E, 0xC29E, 0xC29E }, +{ 0xC29F, 0xC29F, 0xC29F }, +{ 0xC2A0, 0xC2A0, 0xC2A0 }, +{ 0xC2A1, 0xC2A1, 0xC2A1 }, +{ 0xC2A2, 0xC2A2, 0xC2A2 }, +{ 0xC2A3, 0xC2A3, 0xC2A3 }, +{ 0xC2A4, 0xC2A4, 0xC2A4 }, +{ 0xC2A5, 0xC2A5, 0xC2A5 }, +{ 0xC2A6, 0xC2A6, 0xC2A6 }, +{ 0xC2A7, 0xC2A7, 0xC2A7 }, +{ 0xC2A8, 0xC2A8, 0xC2A8 }, +{ 0xC2A9, 0xC2A9, 0xC2A9 }, +{ 0xC2AA, 0xC2AA, 0xC2AA }, +{ 0xC2AB, 0xC2AB, 0xC2AB }, +{ 0xC2AC, 0xC2AC, 0xC2AC }, +{ 0xC2AD, 0xC2AD, 0xC2AD }, +{ 0xC2AE, 0xC2AE, 0xC2AE }, +{ 0xC2AF, 0xC2AF, 0xC2AF }, +{ 0xC2B0, 0xC2B0, 0xC2B0 }, +{ 0xC2B1, 0xC2B1, 0xC2B1 }, +{ 0xC2B2, 0xC2B2, 0xC2B2 }, +{ 0xC2B3, 0xC2B3, 0xC2B3 }, +{ 0xC2B4, 0xC2B4, 0xC2B4 }, +{ 0xC2B5, 0xC2B5, 0xC2B5 }, +{ 0xC2B6, 0xC2B6, 0xC2B6 }, +{ 0xC2B7, 0xC2B7, 0xC2B7 }, +{ 0xC2B8, 0xC2B8, 0xC2B8 }, +{ 0xC2B9, 0xC2B9, 0xC2B9 }, +{ 0xC2BA, 0xC2BA, 0xC2BA }, +{ 0xC2BB, 0xC2BB, 0xC2BB }, +{ 0xC2BC, 0xC2BC, 0xC2BC }, +{ 0xC2BD, 0xC2BD, 0xC2BD }, +{ 0xC2BE, 0xC2BE, 0xC2BE }, +{ 0xC2BF, 0xC2BF, 0xC2BF }, +{ 0xC2C0, 0xC2C0, 0xC2C0 }, +{ 0xC2C1, 0xC2C1, 0xC2C1 }, +{ 0xC2C2, 0xC2C2, 0xC2C2 }, +{ 0xC2C3, 0xC2C3, 0xC2C3 }, +{ 0xC2C4, 0xC2C4, 0xC2C4 }, +{ 0xC2C5, 0xC2C5, 0xC2C5 }, +{ 0xC2C6, 0xC2C6, 0xC2C6 }, +{ 0xC2C7, 0xC2C7, 0xC2C7 }, +{ 0xC2C8, 0xC2C8, 0xC2C8 }, +{ 0xC2C9, 0xC2C9, 0xC2C9 }, +{ 0xC2CA, 0xC2CA, 0xC2CA }, +{ 0xC2CB, 0xC2CB, 0xC2CB }, +{ 0xC2CC, 0xC2CC, 0xC2CC }, +{ 0xC2CD, 0xC2CD, 0xC2CD }, +{ 0xC2CE, 0xC2CE, 0xC2CE }, +{ 0xC2CF, 0xC2CF, 0xC2CF }, +{ 0xC2D0, 0xC2D0, 0xC2D0 }, +{ 0xC2D1, 0xC2D1, 0xC2D1 }, +{ 0xC2D2, 0xC2D2, 0xC2D2 }, +{ 0xC2D3, 0xC2D3, 0xC2D3 }, +{ 0xC2D4, 0xC2D4, 0xC2D4 }, +{ 0xC2D5, 0xC2D5, 0xC2D5 }, +{ 0xC2D6, 0xC2D6, 0xC2D6 }, +{ 0xC2D7, 0xC2D7, 0xC2D7 }, +{ 0xC2D8, 0xC2D8, 0xC2D8 }, +{ 0xC2D9, 0xC2D9, 0xC2D9 }, +{ 0xC2DA, 0xC2DA, 0xC2DA }, +{ 0xC2DB, 0xC2DB, 0xC2DB }, +{ 0xC2DC, 0xC2DC, 0xC2DC }, +{ 0xC2DD, 0xC2DD, 0xC2DD }, +{ 0xC2DE, 0xC2DE, 0xC2DE }, +{ 0xC2DF, 0xC2DF, 0xC2DF }, +{ 0xC2E0, 0xC2E0, 0xC2E0 }, +{ 0xC2E1, 0xC2E1, 0xC2E1 }, +{ 0xC2E2, 0xC2E2, 0xC2E2 }, +{ 0xC2E3, 0xC2E3, 0xC2E3 }, +{ 0xC2E4, 0xC2E4, 0xC2E4 }, +{ 0xC2E5, 0xC2E5, 0xC2E5 }, +{ 0xC2E6, 0xC2E6, 0xC2E6 }, +{ 0xC2E7, 0xC2E7, 0xC2E7 }, +{ 0xC2E8, 0xC2E8, 0xC2E8 }, +{ 0xC2E9, 0xC2E9, 0xC2E9 }, +{ 0xC2EA, 0xC2EA, 0xC2EA }, +{ 0xC2EB, 0xC2EB, 0xC2EB }, +{ 0xC2EC, 0xC2EC, 0xC2EC }, +{ 0xC2ED, 0xC2ED, 0xC2ED }, +{ 0xC2EE, 0xC2EE, 0xC2EE }, +{ 0xC2EF, 0xC2EF, 0xC2EF }, +{ 0xC2F0, 0xC2F0, 0xC2F0 }, +{ 0xC2F1, 0xC2F1, 0xC2F1 }, +{ 0xC2F2, 0xC2F2, 0xC2F2 }, +{ 0xC2F3, 0xC2F3, 0xC2F3 }, +{ 0xC2F4, 0xC2F4, 0xC2F4 }, +{ 0xC2F5, 0xC2F5, 0xC2F5 }, +{ 0xC2F6, 0xC2F6, 0xC2F6 }, +{ 0xC2F7, 0xC2F7, 0xC2F7 }, +{ 0xC2F8, 0xC2F8, 0xC2F8 }, +{ 0xC2F9, 0xC2F9, 0xC2F9 }, +{ 0xC2FA, 0xC2FA, 0xC2FA }, +{ 0xC2FB, 0xC2FB, 0xC2FB }, +{ 0xC2FC, 0xC2FC, 0xC2FC }, +{ 0xC2FD, 0xC2FD, 0xC2FD }, +{ 0xC2FE, 0xC2FE, 0xC2FE }, +{ 0xC2FF, 0xC2FF, 0xC2FF }, +{ 0xC300, 0xC300, 0xC300 }, +{ 0xC301, 0xC301, 0xC301 }, +{ 0xC302, 0xC302, 0xC302 }, +{ 0xC303, 0xC303, 0xC303 }, +{ 0xC304, 0xC304, 0xC304 }, +{ 0xC305, 0xC305, 0xC305 }, +{ 0xC306, 0xC306, 0xC306 }, +{ 0xC307, 0xC307, 0xC307 }, +{ 0xC308, 0xC308, 0xC308 }, +{ 0xC309, 0xC309, 0xC309 }, +{ 0xC30A, 0xC30A, 0xC30A }, +{ 0xC30B, 0xC30B, 0xC30B }, +{ 0xC30C, 0xC30C, 0xC30C }, +{ 0xC30D, 0xC30D, 0xC30D }, +{ 0xC30E, 0xC30E, 0xC30E }, +{ 0xC30F, 0xC30F, 0xC30F }, +{ 0xC310, 0xC310, 0xC310 }, +{ 0xC311, 0xC311, 0xC311 }, +{ 0xC312, 0xC312, 0xC312 }, +{ 0xC313, 0xC313, 0xC313 }, +{ 0xC314, 0xC314, 0xC314 }, +{ 0xC315, 0xC315, 0xC315 }, +{ 0xC316, 0xC316, 0xC316 }, +{ 0xC317, 0xC317, 0xC317 }, +{ 0xC318, 0xC318, 0xC318 }, +{ 0xC319, 0xC319, 0xC319 }, +{ 0xC31A, 0xC31A, 0xC31A }, +{ 0xC31B, 0xC31B, 0xC31B }, +{ 0xC31C, 0xC31C, 0xC31C }, +{ 0xC31D, 0xC31D, 0xC31D }, +{ 0xC31E, 0xC31E, 0xC31E }, +{ 0xC31F, 0xC31F, 0xC31F }, +{ 0xC320, 0xC320, 0xC320 }, +{ 0xC321, 0xC321, 0xC321 }, +{ 0xC322, 0xC322, 0xC322 }, +{ 0xC323, 0xC323, 0xC323 }, +{ 0xC324, 0xC324, 0xC324 }, +{ 0xC325, 0xC325, 0xC325 }, +{ 0xC326, 0xC326, 0xC326 }, +{ 0xC327, 0xC327, 0xC327 }, +{ 0xC328, 0xC328, 0xC328 }, +{ 0xC329, 0xC329, 0xC329 }, +{ 0xC32A, 0xC32A, 0xC32A }, +{ 0xC32B, 0xC32B, 0xC32B }, +{ 0xC32C, 0xC32C, 0xC32C }, +{ 0xC32D, 0xC32D, 0xC32D }, +{ 0xC32E, 0xC32E, 0xC32E }, +{ 0xC32F, 0xC32F, 0xC32F }, +{ 0xC330, 0xC330, 0xC330 }, +{ 0xC331, 0xC331, 0xC331 }, +{ 0xC332, 0xC332, 0xC332 }, +{ 0xC333, 0xC333, 0xC333 }, +{ 0xC334, 0xC334, 0xC334 }, +{ 0xC335, 0xC335, 0xC335 }, +{ 0xC336, 0xC336, 0xC336 }, +{ 0xC337, 0xC337, 0xC337 }, +{ 0xC338, 0xC338, 0xC338 }, +{ 0xC339, 0xC339, 0xC339 }, +{ 0xC33A, 0xC33A, 0xC33A }, +{ 0xC33B, 0xC33B, 0xC33B }, +{ 0xC33C, 0xC33C, 0xC33C }, +{ 0xC33D, 0xC33D, 0xC33D }, +{ 0xC33E, 0xC33E, 0xC33E }, +{ 0xC33F, 0xC33F, 0xC33F }, +{ 0xC340, 0xC340, 0xC340 }, +{ 0xC341, 0xC341, 0xC341 }, +{ 0xC342, 0xC342, 0xC342 }, +{ 0xC343, 0xC343, 0xC343 }, +{ 0xC344, 0xC344, 0xC344 }, +{ 0xC345, 0xC345, 0xC345 }, +{ 0xC346, 0xC346, 0xC346 }, +{ 0xC347, 0xC347, 0xC347 }, +{ 0xC348, 0xC348, 0xC348 }, +{ 0xC349, 0xC349, 0xC349 }, +{ 0xC34A, 0xC34A, 0xC34A }, +{ 0xC34B, 0xC34B, 0xC34B }, +{ 0xC34C, 0xC34C, 0xC34C }, +{ 0xC34D, 0xC34D, 0xC34D }, +{ 0xC34E, 0xC34E, 0xC34E }, +{ 0xC34F, 0xC34F, 0xC34F }, +{ 0xC350, 0xC350, 0xC350 }, +{ 0xC351, 0xC351, 0xC351 }, +{ 0xC352, 0xC352, 0xC352 }, +{ 0xC353, 0xC353, 0xC353 }, +{ 0xC354, 0xC354, 0xC354 }, +{ 0xC355, 0xC355, 0xC355 }, +{ 0xC356, 0xC356, 0xC356 }, +{ 0xC357, 0xC357, 0xC357 }, +{ 0xC358, 0xC358, 0xC358 }, +{ 0xC359, 0xC359, 0xC359 }, +{ 0xC35A, 0xC35A, 0xC35A }, +{ 0xC35B, 0xC35B, 0xC35B }, +{ 0xC35C, 0xC35C, 0xC35C }, +{ 0xC35D, 0xC35D, 0xC35D }, +{ 0xC35E, 0xC35E, 0xC35E }, +{ 0xC35F, 0xC35F, 0xC35F }, +{ 0xC360, 0xC360, 0xC360 }, +{ 0xC361, 0xC361, 0xC361 }, +{ 0xC362, 0xC362, 0xC362 }, +{ 0xC363, 0xC363, 0xC363 }, +{ 0xC364, 0xC364, 0xC364 }, +{ 0xC365, 0xC365, 0xC365 }, +{ 0xC366, 0xC366, 0xC366 }, +{ 0xC367, 0xC367, 0xC367 }, +{ 0xC368, 0xC368, 0xC368 }, +{ 0xC369, 0xC369, 0xC369 }, +{ 0xC36A, 0xC36A, 0xC36A }, +{ 0xC36B, 0xC36B, 0xC36B }, +{ 0xC36C, 0xC36C, 0xC36C }, +{ 0xC36D, 0xC36D, 0xC36D }, +{ 0xC36E, 0xC36E, 0xC36E }, +{ 0xC36F, 0xC36F, 0xC36F }, +{ 0xC370, 0xC370, 0xC370 }, +{ 0xC371, 0xC371, 0xC371 }, +{ 0xC372, 0xC372, 0xC372 }, +{ 0xC373, 0xC373, 0xC373 }, +{ 0xC374, 0xC374, 0xC374 }, +{ 0xC375, 0xC375, 0xC375 }, +{ 0xC376, 0xC376, 0xC376 }, +{ 0xC377, 0xC377, 0xC377 }, +{ 0xC378, 0xC378, 0xC378 }, +{ 0xC379, 0xC379, 0xC379 }, +{ 0xC37A, 0xC37A, 0xC37A }, +{ 0xC37B, 0xC37B, 0xC37B }, +{ 0xC37C, 0xC37C, 0xC37C }, +{ 0xC37D, 0xC37D, 0xC37D }, +{ 0xC37E, 0xC37E, 0xC37E }, +{ 0xC37F, 0xC37F, 0xC37F }, +{ 0xC380, 0xC380, 0xC380 }, +{ 0xC381, 0xC381, 0xC381 }, +{ 0xC382, 0xC382, 0xC382 }, +{ 0xC383, 0xC383, 0xC383 }, +{ 0xC384, 0xC384, 0xC384 }, +{ 0xC385, 0xC385, 0xC385 }, +{ 0xC386, 0xC386, 0xC386 }, +{ 0xC387, 0xC387, 0xC387 }, +{ 0xC388, 0xC388, 0xC388 }, +{ 0xC389, 0xC389, 0xC389 }, +{ 0xC38A, 0xC38A, 0xC38A }, +{ 0xC38B, 0xC38B, 0xC38B }, +{ 0xC38C, 0xC38C, 0xC38C }, +{ 0xC38D, 0xC38D, 0xC38D }, +{ 0xC38E, 0xC38E, 0xC38E }, +{ 0xC38F, 0xC38F, 0xC38F }, +{ 0xC390, 0xC390, 0xC390 }, +{ 0xC391, 0xC391, 0xC391 }, +{ 0xC392, 0xC392, 0xC392 }, +{ 0xC393, 0xC393, 0xC393 }, +{ 0xC394, 0xC394, 0xC394 }, +{ 0xC395, 0xC395, 0xC395 }, +{ 0xC396, 0xC396, 0xC396 }, +{ 0xC397, 0xC397, 0xC397 }, +{ 0xC398, 0xC398, 0xC398 }, +{ 0xC399, 0xC399, 0xC399 }, +{ 0xC39A, 0xC39A, 0xC39A }, +{ 0xC39B, 0xC39B, 0xC39B }, +{ 0xC39C, 0xC39C, 0xC39C }, +{ 0xC39D, 0xC39D, 0xC39D }, +{ 0xC39E, 0xC39E, 0xC39E }, +{ 0xC39F, 0xC39F, 0xC39F }, +{ 0xC3A0, 0xC3A0, 0xC3A0 }, +{ 0xC3A1, 0xC3A1, 0xC3A1 }, +{ 0xC3A2, 0xC3A2, 0xC3A2 }, +{ 0xC3A3, 0xC3A3, 0xC3A3 }, +{ 0xC3A4, 0xC3A4, 0xC3A4 }, +{ 0xC3A5, 0xC3A5, 0xC3A5 }, +{ 0xC3A6, 0xC3A6, 0xC3A6 }, +{ 0xC3A7, 0xC3A7, 0xC3A7 }, +{ 0xC3A8, 0xC3A8, 0xC3A8 }, +{ 0xC3A9, 0xC3A9, 0xC3A9 }, +{ 0xC3AA, 0xC3AA, 0xC3AA }, +{ 0xC3AB, 0xC3AB, 0xC3AB }, +{ 0xC3AC, 0xC3AC, 0xC3AC }, +{ 0xC3AD, 0xC3AD, 0xC3AD }, +{ 0xC3AE, 0xC3AE, 0xC3AE }, +{ 0xC3AF, 0xC3AF, 0xC3AF }, +{ 0xC3B0, 0xC3B0, 0xC3B0 }, +{ 0xC3B1, 0xC3B1, 0xC3B1 }, +{ 0xC3B2, 0xC3B2, 0xC3B2 }, +{ 0xC3B3, 0xC3B3, 0xC3B3 }, +{ 0xC3B4, 0xC3B4, 0xC3B4 }, +{ 0xC3B5, 0xC3B5, 0xC3B5 }, +{ 0xC3B6, 0xC3B6, 0xC3B6 }, +{ 0xC3B7, 0xC3B7, 0xC3B7 }, +{ 0xC3B8, 0xC3B8, 0xC3B8 }, +{ 0xC3B9, 0xC3B9, 0xC3B9 }, +{ 0xC3BA, 0xC3BA, 0xC3BA }, +{ 0xC3BB, 0xC3BB, 0xC3BB }, +{ 0xC3BC, 0xC3BC, 0xC3BC }, +{ 0xC3BD, 0xC3BD, 0xC3BD }, +{ 0xC3BE, 0xC3BE, 0xC3BE }, +{ 0xC3BF, 0xC3BF, 0xC3BF }, +{ 0xC3C0, 0xC3C0, 0xC3C0 }, +{ 0xC3C1, 0xC3C1, 0xC3C1 }, +{ 0xC3C2, 0xC3C2, 0xC3C2 }, +{ 0xC3C3, 0xC3C3, 0xC3C3 }, +{ 0xC3C4, 0xC3C4, 0xC3C4 }, +{ 0xC3C5, 0xC3C5, 0xC3C5 }, +{ 0xC3C6, 0xC3C6, 0xC3C6 }, +{ 0xC3C7, 0xC3C7, 0xC3C7 }, +{ 0xC3C8, 0xC3C8, 0xC3C8 }, +{ 0xC3C9, 0xC3C9, 0xC3C9 }, +{ 0xC3CA, 0xC3CA, 0xC3CA }, +{ 0xC3CB, 0xC3CB, 0xC3CB }, +{ 0xC3CC, 0xC3CC, 0xC3CC }, +{ 0xC3CD, 0xC3CD, 0xC3CD }, +{ 0xC3CE, 0xC3CE, 0xC3CE }, +{ 0xC3CF, 0xC3CF, 0xC3CF }, +{ 0xC3D0, 0xC3D0, 0xC3D0 }, +{ 0xC3D1, 0xC3D1, 0xC3D1 }, +{ 0xC3D2, 0xC3D2, 0xC3D2 }, +{ 0xC3D3, 0xC3D3, 0xC3D3 }, +{ 0xC3D4, 0xC3D4, 0xC3D4 }, +{ 0xC3D5, 0xC3D5, 0xC3D5 }, +{ 0xC3D6, 0xC3D6, 0xC3D6 }, +{ 0xC3D7, 0xC3D7, 0xC3D7 }, +{ 0xC3D8, 0xC3D8, 0xC3D8 }, +{ 0xC3D9, 0xC3D9, 0xC3D9 }, +{ 0xC3DA, 0xC3DA, 0xC3DA }, +{ 0xC3DB, 0xC3DB, 0xC3DB }, +{ 0xC3DC, 0xC3DC, 0xC3DC }, +{ 0xC3DD, 0xC3DD, 0xC3DD }, +{ 0xC3DE, 0xC3DE, 0xC3DE }, +{ 0xC3DF, 0xC3DF, 0xC3DF }, +{ 0xC3E0, 0xC3E0, 0xC3E0 }, +{ 0xC3E1, 0xC3E1, 0xC3E1 }, +{ 0xC3E2, 0xC3E2, 0xC3E2 }, +{ 0xC3E3, 0xC3E3, 0xC3E3 }, +{ 0xC3E4, 0xC3E4, 0xC3E4 }, +{ 0xC3E5, 0xC3E5, 0xC3E5 }, +{ 0xC3E6, 0xC3E6, 0xC3E6 }, +{ 0xC3E7, 0xC3E7, 0xC3E7 }, +{ 0xC3E8, 0xC3E8, 0xC3E8 }, +{ 0xC3E9, 0xC3E9, 0xC3E9 }, +{ 0xC3EA, 0xC3EA, 0xC3EA }, +{ 0xC3EB, 0xC3EB, 0xC3EB }, +{ 0xC3EC, 0xC3EC, 0xC3EC }, +{ 0xC3ED, 0xC3ED, 0xC3ED }, +{ 0xC3EE, 0xC3EE, 0xC3EE }, +{ 0xC3EF, 0xC3EF, 0xC3EF }, +{ 0xC3F0, 0xC3F0, 0xC3F0 }, +{ 0xC3F1, 0xC3F1, 0xC3F1 }, +{ 0xC3F2, 0xC3F2, 0xC3F2 }, +{ 0xC3F3, 0xC3F3, 0xC3F3 }, +{ 0xC3F4, 0xC3F4, 0xC3F4 }, +{ 0xC3F5, 0xC3F5, 0xC3F5 }, +{ 0xC3F6, 0xC3F6, 0xC3F6 }, +{ 0xC3F7, 0xC3F7, 0xC3F7 }, +{ 0xC3F8, 0xC3F8, 0xC3F8 }, +{ 0xC3F9, 0xC3F9, 0xC3F9 }, +{ 0xC3FA, 0xC3FA, 0xC3FA }, +{ 0xC3FB, 0xC3FB, 0xC3FB }, +{ 0xC3FC, 0xC3FC, 0xC3FC }, +{ 0xC3FD, 0xC3FD, 0xC3FD }, +{ 0xC3FE, 0xC3FE, 0xC3FE }, +{ 0xC3FF, 0xC3FF, 0xC3FF }, +{ 0xC400, 0xC400, 0xC400 }, +{ 0xC401, 0xC401, 0xC401 }, +{ 0xC402, 0xC402, 0xC402 }, +{ 0xC403, 0xC403, 0xC403 }, +{ 0xC404, 0xC404, 0xC404 }, +{ 0xC405, 0xC405, 0xC405 }, +{ 0xC406, 0xC406, 0xC406 }, +{ 0xC407, 0xC407, 0xC407 }, +{ 0xC408, 0xC408, 0xC408 }, +{ 0xC409, 0xC409, 0xC409 }, +{ 0xC40A, 0xC40A, 0xC40A }, +{ 0xC40B, 0xC40B, 0xC40B }, +{ 0xC40C, 0xC40C, 0xC40C }, +{ 0xC40D, 0xC40D, 0xC40D }, +{ 0xC40E, 0xC40E, 0xC40E }, +{ 0xC40F, 0xC40F, 0xC40F }, +{ 0xC410, 0xC410, 0xC410 }, +{ 0xC411, 0xC411, 0xC411 }, +{ 0xC412, 0xC412, 0xC412 }, +{ 0xC413, 0xC413, 0xC413 }, +{ 0xC414, 0xC414, 0xC414 }, +{ 0xC415, 0xC415, 0xC415 }, +{ 0xC416, 0xC416, 0xC416 }, +{ 0xC417, 0xC417, 0xC417 }, +{ 0xC418, 0xC418, 0xC418 }, +{ 0xC419, 0xC419, 0xC419 }, +{ 0xC41A, 0xC41A, 0xC41A }, +{ 0xC41B, 0xC41B, 0xC41B }, +{ 0xC41C, 0xC41C, 0xC41C }, +{ 0xC41D, 0xC41D, 0xC41D }, +{ 0xC41E, 0xC41E, 0xC41E }, +{ 0xC41F, 0xC41F, 0xC41F }, +{ 0xC420, 0xC420, 0xC420 }, +{ 0xC421, 0xC421, 0xC421 }, +{ 0xC422, 0xC422, 0xC422 }, +{ 0xC423, 0xC423, 0xC423 }, +{ 0xC424, 0xC424, 0xC424 }, +{ 0xC425, 0xC425, 0xC425 }, +{ 0xC426, 0xC426, 0xC426 }, +{ 0xC427, 0xC427, 0xC427 }, +{ 0xC428, 0xC428, 0xC428 }, +{ 0xC429, 0xC429, 0xC429 }, +{ 0xC42A, 0xC42A, 0xC42A }, +{ 0xC42B, 0xC42B, 0xC42B }, +{ 0xC42C, 0xC42C, 0xC42C }, +{ 0xC42D, 0xC42D, 0xC42D }, +{ 0xC42E, 0xC42E, 0xC42E }, +{ 0xC42F, 0xC42F, 0xC42F }, +{ 0xC430, 0xC430, 0xC430 }, +{ 0xC431, 0xC431, 0xC431 }, +{ 0xC432, 0xC432, 0xC432 }, +{ 0xC433, 0xC433, 0xC433 }, +{ 0xC434, 0xC434, 0xC434 }, +{ 0xC435, 0xC435, 0xC435 }, +{ 0xC436, 0xC436, 0xC436 }, +{ 0xC437, 0xC437, 0xC437 }, +{ 0xC438, 0xC438, 0xC438 }, +{ 0xC439, 0xC439, 0xC439 }, +{ 0xC43A, 0xC43A, 0xC43A }, +{ 0xC43B, 0xC43B, 0xC43B }, +{ 0xC43C, 0xC43C, 0xC43C }, +{ 0xC43D, 0xC43D, 0xC43D }, +{ 0xC43E, 0xC43E, 0xC43E }, +{ 0xC43F, 0xC43F, 0xC43F }, +{ 0xC440, 0xC440, 0xC440 }, +{ 0xC441, 0xC441, 0xC441 }, +{ 0xC442, 0xC442, 0xC442 }, +{ 0xC443, 0xC443, 0xC443 }, +{ 0xC444, 0xC444, 0xC444 }, +{ 0xC445, 0xC445, 0xC445 }, +{ 0xC446, 0xC446, 0xC446 }, +{ 0xC447, 0xC447, 0xC447 }, +{ 0xC448, 0xC448, 0xC448 }, +{ 0xC449, 0xC449, 0xC449 }, +{ 0xC44A, 0xC44A, 0xC44A }, +{ 0xC44B, 0xC44B, 0xC44B }, +{ 0xC44C, 0xC44C, 0xC44C }, +{ 0xC44D, 0xC44D, 0xC44D }, +{ 0xC44E, 0xC44E, 0xC44E }, +{ 0xC44F, 0xC44F, 0xC44F }, +{ 0xC450, 0xC450, 0xC450 }, +{ 0xC451, 0xC451, 0xC451 }, +{ 0xC452, 0xC452, 0xC452 }, +{ 0xC453, 0xC453, 0xC453 }, +{ 0xC454, 0xC454, 0xC454 }, +{ 0xC455, 0xC455, 0xC455 }, +{ 0xC456, 0xC456, 0xC456 }, +{ 0xC457, 0xC457, 0xC457 }, +{ 0xC458, 0xC458, 0xC458 }, +{ 0xC459, 0xC459, 0xC459 }, +{ 0xC45A, 0xC45A, 0xC45A }, +{ 0xC45B, 0xC45B, 0xC45B }, +{ 0xC45C, 0xC45C, 0xC45C }, +{ 0xC45D, 0xC45D, 0xC45D }, +{ 0xC45E, 0xC45E, 0xC45E }, +{ 0xC45F, 0xC45F, 0xC45F }, +{ 0xC460, 0xC460, 0xC460 }, +{ 0xC461, 0xC461, 0xC461 }, +{ 0xC462, 0xC462, 0xC462 }, +{ 0xC463, 0xC463, 0xC463 }, +{ 0xC464, 0xC464, 0xC464 }, +{ 0xC465, 0xC465, 0xC465 }, +{ 0xC466, 0xC466, 0xC466 }, +{ 0xC467, 0xC467, 0xC467 }, +{ 0xC468, 0xC468, 0xC468 }, +{ 0xC469, 0xC469, 0xC469 }, +{ 0xC46A, 0xC46A, 0xC46A }, +{ 0xC46B, 0xC46B, 0xC46B }, +{ 0xC46C, 0xC46C, 0xC46C }, +{ 0xC46D, 0xC46D, 0xC46D }, +{ 0xC46E, 0xC46E, 0xC46E }, +{ 0xC46F, 0xC46F, 0xC46F }, +{ 0xC470, 0xC470, 0xC470 }, +{ 0xC471, 0xC471, 0xC471 }, +{ 0xC472, 0xC472, 0xC472 }, +{ 0xC473, 0xC473, 0xC473 }, +{ 0xC474, 0xC474, 0xC474 }, +{ 0xC475, 0xC475, 0xC475 }, +{ 0xC476, 0xC476, 0xC476 }, +{ 0xC477, 0xC477, 0xC477 }, +{ 0xC478, 0xC478, 0xC478 }, +{ 0xC479, 0xC479, 0xC479 }, +{ 0xC47A, 0xC47A, 0xC47A }, +{ 0xC47B, 0xC47B, 0xC47B }, +{ 0xC47C, 0xC47C, 0xC47C }, +{ 0xC47D, 0xC47D, 0xC47D }, +{ 0xC47E, 0xC47E, 0xC47E }, +{ 0xC47F, 0xC47F, 0xC47F }, +{ 0xC480, 0xC480, 0xC480 }, +{ 0xC481, 0xC481, 0xC481 }, +{ 0xC482, 0xC482, 0xC482 }, +{ 0xC483, 0xC483, 0xC483 }, +{ 0xC484, 0xC484, 0xC484 }, +{ 0xC485, 0xC485, 0xC485 }, +{ 0xC486, 0xC486, 0xC486 }, +{ 0xC487, 0xC487, 0xC487 }, +{ 0xC488, 0xC488, 0xC488 }, +{ 0xC489, 0xC489, 0xC489 }, +{ 0xC48A, 0xC48A, 0xC48A }, +{ 0xC48B, 0xC48B, 0xC48B }, +{ 0xC48C, 0xC48C, 0xC48C }, +{ 0xC48D, 0xC48D, 0xC48D }, +{ 0xC48E, 0xC48E, 0xC48E }, +{ 0xC48F, 0xC48F, 0xC48F }, +{ 0xC490, 0xC490, 0xC490 }, +{ 0xC491, 0xC491, 0xC491 }, +{ 0xC492, 0xC492, 0xC492 }, +{ 0xC493, 0xC493, 0xC493 }, +{ 0xC494, 0xC494, 0xC494 }, +{ 0xC495, 0xC495, 0xC495 }, +{ 0xC496, 0xC496, 0xC496 }, +{ 0xC497, 0xC497, 0xC497 }, +{ 0xC498, 0xC498, 0xC498 }, +{ 0xC499, 0xC499, 0xC499 }, +{ 0xC49A, 0xC49A, 0xC49A }, +{ 0xC49B, 0xC49B, 0xC49B }, +{ 0xC49C, 0xC49C, 0xC49C }, +{ 0xC49D, 0xC49D, 0xC49D }, +{ 0xC49E, 0xC49E, 0xC49E }, +{ 0xC49F, 0xC49F, 0xC49F }, +{ 0xC4A0, 0xC4A0, 0xC4A0 }, +{ 0xC4A1, 0xC4A1, 0xC4A1 }, +{ 0xC4A2, 0xC4A2, 0xC4A2 }, +{ 0xC4A3, 0xC4A3, 0xC4A3 }, +{ 0xC4A4, 0xC4A4, 0xC4A4 }, +{ 0xC4A5, 0xC4A5, 0xC4A5 }, +{ 0xC4A6, 0xC4A6, 0xC4A6 }, +{ 0xC4A7, 0xC4A7, 0xC4A7 }, +{ 0xC4A8, 0xC4A8, 0xC4A8 }, +{ 0xC4A9, 0xC4A9, 0xC4A9 }, +{ 0xC4AA, 0xC4AA, 0xC4AA }, +{ 0xC4AB, 0xC4AB, 0xC4AB }, +{ 0xC4AC, 0xC4AC, 0xC4AC }, +{ 0xC4AD, 0xC4AD, 0xC4AD }, +{ 0xC4AE, 0xC4AE, 0xC4AE }, +{ 0xC4AF, 0xC4AF, 0xC4AF }, +{ 0xC4B0, 0xC4B0, 0xC4B0 }, +{ 0xC4B1, 0xC4B1, 0xC4B1 }, +{ 0xC4B2, 0xC4B2, 0xC4B2 }, +{ 0xC4B3, 0xC4B3, 0xC4B3 }, +{ 0xC4B4, 0xC4B4, 0xC4B4 }, +{ 0xC4B5, 0xC4B5, 0xC4B5 }, +{ 0xC4B6, 0xC4B6, 0xC4B6 }, +{ 0xC4B7, 0xC4B7, 0xC4B7 }, +{ 0xC4B8, 0xC4B8, 0xC4B8 }, +{ 0xC4B9, 0xC4B9, 0xC4B9 }, +{ 0xC4BA, 0xC4BA, 0xC4BA }, +{ 0xC4BB, 0xC4BB, 0xC4BB }, +{ 0xC4BC, 0xC4BC, 0xC4BC }, +{ 0xC4BD, 0xC4BD, 0xC4BD }, +{ 0xC4BE, 0xC4BE, 0xC4BE }, +{ 0xC4BF, 0xC4BF, 0xC4BF }, +{ 0xC4C0, 0xC4C0, 0xC4C0 }, +{ 0xC4C1, 0xC4C1, 0xC4C1 }, +{ 0xC4C2, 0xC4C2, 0xC4C2 }, +{ 0xC4C3, 0xC4C3, 0xC4C3 }, +{ 0xC4C4, 0xC4C4, 0xC4C4 }, +{ 0xC4C5, 0xC4C5, 0xC4C5 }, +{ 0xC4C6, 0xC4C6, 0xC4C6 }, +{ 0xC4C7, 0xC4C7, 0xC4C7 }, +{ 0xC4C8, 0xC4C8, 0xC4C8 }, +{ 0xC4C9, 0xC4C9, 0xC4C9 }, +{ 0xC4CA, 0xC4CA, 0xC4CA }, +{ 0xC4CB, 0xC4CB, 0xC4CB }, +{ 0xC4CC, 0xC4CC, 0xC4CC }, +{ 0xC4CD, 0xC4CD, 0xC4CD }, +{ 0xC4CE, 0xC4CE, 0xC4CE }, +{ 0xC4CF, 0xC4CF, 0xC4CF }, +{ 0xC4D0, 0xC4D0, 0xC4D0 }, +{ 0xC4D1, 0xC4D1, 0xC4D1 }, +{ 0xC4D2, 0xC4D2, 0xC4D2 }, +{ 0xC4D3, 0xC4D3, 0xC4D3 }, +{ 0xC4D4, 0xC4D4, 0xC4D4 }, +{ 0xC4D5, 0xC4D5, 0xC4D5 }, +{ 0xC4D6, 0xC4D6, 0xC4D6 }, +{ 0xC4D7, 0xC4D7, 0xC4D7 }, +{ 0xC4D8, 0xC4D8, 0xC4D8 }, +{ 0xC4D9, 0xC4D9, 0xC4D9 }, +{ 0xC4DA, 0xC4DA, 0xC4DA }, +{ 0xC4DB, 0xC4DB, 0xC4DB }, +{ 0xC4DC, 0xC4DC, 0xC4DC }, +{ 0xC4DD, 0xC4DD, 0xC4DD }, +{ 0xC4DE, 0xC4DE, 0xC4DE }, +{ 0xC4DF, 0xC4DF, 0xC4DF }, +{ 0xC4E0, 0xC4E0, 0xC4E0 }, +{ 0xC4E1, 0xC4E1, 0xC4E1 }, +{ 0xC4E2, 0xC4E2, 0xC4E2 }, +{ 0xC4E3, 0xC4E3, 0xC4E3 }, +{ 0xC4E4, 0xC4E4, 0xC4E4 }, +{ 0xC4E5, 0xC4E5, 0xC4E5 }, +{ 0xC4E6, 0xC4E6, 0xC4E6 }, +{ 0xC4E7, 0xC4E7, 0xC4E7 }, +{ 0xC4E8, 0xC4E8, 0xC4E8 }, +{ 0xC4E9, 0xC4E9, 0xC4E9 }, +{ 0xC4EA, 0xC4EA, 0xC4EA }, +{ 0xC4EB, 0xC4EB, 0xC4EB }, +{ 0xC4EC, 0xC4EC, 0xC4EC }, +{ 0xC4ED, 0xC4ED, 0xC4ED }, +{ 0xC4EE, 0xC4EE, 0xC4EE }, +{ 0xC4EF, 0xC4EF, 0xC4EF }, +{ 0xC4F0, 0xC4F0, 0xC4F0 }, +{ 0xC4F1, 0xC4F1, 0xC4F1 }, +{ 0xC4F2, 0xC4F2, 0xC4F2 }, +{ 0xC4F3, 0xC4F3, 0xC4F3 }, +{ 0xC4F4, 0xC4F4, 0xC4F4 }, +{ 0xC4F5, 0xC4F5, 0xC4F5 }, +{ 0xC4F6, 0xC4F6, 0xC4F6 }, +{ 0xC4F7, 0xC4F7, 0xC4F7 }, +{ 0xC4F8, 0xC4F8, 0xC4F8 }, +{ 0xC4F9, 0xC4F9, 0xC4F9 }, +{ 0xC4FA, 0xC4FA, 0xC4FA }, +{ 0xC4FB, 0xC4FB, 0xC4FB }, +{ 0xC4FC, 0xC4FC, 0xC4FC }, +{ 0xC4FD, 0xC4FD, 0xC4FD }, +{ 0xC4FE, 0xC4FE, 0xC4FE }, +{ 0xC4FF, 0xC4FF, 0xC4FF }, +{ 0xC500, 0xC500, 0xC500 }, +{ 0xC501, 0xC501, 0xC501 }, +{ 0xC502, 0xC502, 0xC502 }, +{ 0xC503, 0xC503, 0xC503 }, +{ 0xC504, 0xC504, 0xC504 }, +{ 0xC505, 0xC505, 0xC505 }, +{ 0xC506, 0xC506, 0xC506 }, +{ 0xC507, 0xC507, 0xC507 }, +{ 0xC508, 0xC508, 0xC508 }, +{ 0xC509, 0xC509, 0xC509 }, +{ 0xC50A, 0xC50A, 0xC50A }, +{ 0xC50B, 0xC50B, 0xC50B }, +{ 0xC50C, 0xC50C, 0xC50C }, +{ 0xC50D, 0xC50D, 0xC50D }, +{ 0xC50E, 0xC50E, 0xC50E }, +{ 0xC50F, 0xC50F, 0xC50F }, +{ 0xC510, 0xC510, 0xC510 }, +{ 0xC511, 0xC511, 0xC511 }, +{ 0xC512, 0xC512, 0xC512 }, +{ 0xC513, 0xC513, 0xC513 }, +{ 0xC514, 0xC514, 0xC514 }, +{ 0xC515, 0xC515, 0xC515 }, +{ 0xC516, 0xC516, 0xC516 }, +{ 0xC517, 0xC517, 0xC517 }, +{ 0xC518, 0xC518, 0xC518 }, +{ 0xC519, 0xC519, 0xC519 }, +{ 0xC51A, 0xC51A, 0xC51A }, +{ 0xC51B, 0xC51B, 0xC51B }, +{ 0xC51C, 0xC51C, 0xC51C }, +{ 0xC51D, 0xC51D, 0xC51D }, +{ 0xC51E, 0xC51E, 0xC51E }, +{ 0xC51F, 0xC51F, 0xC51F }, +{ 0xC520, 0xC520, 0xC520 }, +{ 0xC521, 0xC521, 0xC521 }, +{ 0xC522, 0xC522, 0xC522 }, +{ 0xC523, 0xC523, 0xC523 }, +{ 0xC524, 0xC524, 0xC524 }, +{ 0xC525, 0xC525, 0xC525 }, +{ 0xC526, 0xC526, 0xC526 }, +{ 0xC527, 0xC527, 0xC527 }, +{ 0xC528, 0xC528, 0xC528 }, +{ 0xC529, 0xC529, 0xC529 }, +{ 0xC52A, 0xC52A, 0xC52A }, +{ 0xC52B, 0xC52B, 0xC52B }, +{ 0xC52C, 0xC52C, 0xC52C }, +{ 0xC52D, 0xC52D, 0xC52D }, +{ 0xC52E, 0xC52E, 0xC52E }, +{ 0xC52F, 0xC52F, 0xC52F }, +{ 0xC530, 0xC530, 0xC530 }, +{ 0xC531, 0xC531, 0xC531 }, +{ 0xC532, 0xC532, 0xC532 }, +{ 0xC533, 0xC533, 0xC533 }, +{ 0xC534, 0xC534, 0xC534 }, +{ 0xC535, 0xC535, 0xC535 }, +{ 0xC536, 0xC536, 0xC536 }, +{ 0xC537, 0xC537, 0xC537 }, +{ 0xC538, 0xC538, 0xC538 }, +{ 0xC539, 0xC539, 0xC539 }, +{ 0xC53A, 0xC53A, 0xC53A }, +{ 0xC53B, 0xC53B, 0xC53B }, +{ 0xC53C, 0xC53C, 0xC53C }, +{ 0xC53D, 0xC53D, 0xC53D }, +{ 0xC53E, 0xC53E, 0xC53E }, +{ 0xC53F, 0xC53F, 0xC53F }, +{ 0xC540, 0xC540, 0xC540 }, +{ 0xC541, 0xC541, 0xC541 }, +{ 0xC542, 0xC542, 0xC542 }, +{ 0xC543, 0xC543, 0xC543 }, +{ 0xC544, 0xC544, 0xC544 }, +{ 0xC545, 0xC545, 0xC545 }, +{ 0xC546, 0xC546, 0xC546 }, +{ 0xC547, 0xC547, 0xC547 }, +{ 0xC548, 0xC548, 0xC548 }, +{ 0xC549, 0xC549, 0xC549 }, +{ 0xC54A, 0xC54A, 0xC54A }, +{ 0xC54B, 0xC54B, 0xC54B }, +{ 0xC54C, 0xC54C, 0xC54C }, +{ 0xC54D, 0xC54D, 0xC54D }, +{ 0xC54E, 0xC54E, 0xC54E }, +{ 0xC54F, 0xC54F, 0xC54F }, +{ 0xC550, 0xC550, 0xC550 }, +{ 0xC551, 0xC551, 0xC551 }, +{ 0xC552, 0xC552, 0xC552 }, +{ 0xC553, 0xC553, 0xC553 }, +{ 0xC554, 0xC554, 0xC554 }, +{ 0xC555, 0xC555, 0xC555 }, +{ 0xC556, 0xC556, 0xC556 }, +{ 0xC557, 0xC557, 0xC557 }, +{ 0xC558, 0xC558, 0xC558 }, +{ 0xC559, 0xC559, 0xC559 }, +{ 0xC55A, 0xC55A, 0xC55A }, +{ 0xC55B, 0xC55B, 0xC55B }, +{ 0xC55C, 0xC55C, 0xC55C }, +{ 0xC55D, 0xC55D, 0xC55D }, +{ 0xC55E, 0xC55E, 0xC55E }, +{ 0xC55F, 0xC55F, 0xC55F }, +{ 0xC560, 0xC560, 0xC560 }, +{ 0xC561, 0xC561, 0xC561 }, +{ 0xC562, 0xC562, 0xC562 }, +{ 0xC563, 0xC563, 0xC563 }, +{ 0xC564, 0xC564, 0xC564 }, +{ 0xC565, 0xC565, 0xC565 }, +{ 0xC566, 0xC566, 0xC566 }, +{ 0xC567, 0xC567, 0xC567 }, +{ 0xC568, 0xC568, 0xC568 }, +{ 0xC569, 0xC569, 0xC569 }, +{ 0xC56A, 0xC56A, 0xC56A }, +{ 0xC56B, 0xC56B, 0xC56B }, +{ 0xC56C, 0xC56C, 0xC56C }, +{ 0xC56D, 0xC56D, 0xC56D }, +{ 0xC56E, 0xC56E, 0xC56E }, +{ 0xC56F, 0xC56F, 0xC56F }, +{ 0xC570, 0xC570, 0xC570 }, +{ 0xC571, 0xC571, 0xC571 }, +{ 0xC572, 0xC572, 0xC572 }, +{ 0xC573, 0xC573, 0xC573 }, +{ 0xC574, 0xC574, 0xC574 }, +{ 0xC575, 0xC575, 0xC575 }, +{ 0xC576, 0xC576, 0xC576 }, +{ 0xC577, 0xC577, 0xC577 }, +{ 0xC578, 0xC578, 0xC578 }, +{ 0xC579, 0xC579, 0xC579 }, +{ 0xC57A, 0xC57A, 0xC57A }, +{ 0xC57B, 0xC57B, 0xC57B }, +{ 0xC57C, 0xC57C, 0xC57C }, +{ 0xC57D, 0xC57D, 0xC57D }, +{ 0xC57E, 0xC57E, 0xC57E }, +{ 0xC57F, 0xC57F, 0xC57F }, +{ 0xC580, 0xC580, 0xC580 }, +{ 0xC581, 0xC581, 0xC581 }, +{ 0xC582, 0xC582, 0xC582 }, +{ 0xC583, 0xC583, 0xC583 }, +{ 0xC584, 0xC584, 0xC584 }, +{ 0xC585, 0xC585, 0xC585 }, +{ 0xC586, 0xC586, 0xC586 }, +{ 0xC587, 0xC587, 0xC587 }, +{ 0xC588, 0xC588, 0xC588 }, +{ 0xC589, 0xC589, 0xC589 }, +{ 0xC58A, 0xC58A, 0xC58A }, +{ 0xC58B, 0xC58B, 0xC58B }, +{ 0xC58C, 0xC58C, 0xC58C }, +{ 0xC58D, 0xC58D, 0xC58D }, +{ 0xC58E, 0xC58E, 0xC58E }, +{ 0xC58F, 0xC58F, 0xC58F }, +{ 0xC590, 0xC590, 0xC590 }, +{ 0xC591, 0xC591, 0xC591 }, +{ 0xC592, 0xC592, 0xC592 }, +{ 0xC593, 0xC593, 0xC593 }, +{ 0xC594, 0xC594, 0xC594 }, +{ 0xC595, 0xC595, 0xC595 }, +{ 0xC596, 0xC596, 0xC596 }, +{ 0xC597, 0xC597, 0xC597 }, +{ 0xC598, 0xC598, 0xC598 }, +{ 0xC599, 0xC599, 0xC599 }, +{ 0xC59A, 0xC59A, 0xC59A }, +{ 0xC59B, 0xC59B, 0xC59B }, +{ 0xC59C, 0xC59C, 0xC59C }, +{ 0xC59D, 0xC59D, 0xC59D }, +{ 0xC59E, 0xC59E, 0xC59E }, +{ 0xC59F, 0xC59F, 0xC59F }, +{ 0xC5A0, 0xC5A0, 0xC5A0 }, +{ 0xC5A1, 0xC5A1, 0xC5A1 }, +{ 0xC5A2, 0xC5A2, 0xC5A2 }, +{ 0xC5A3, 0xC5A3, 0xC5A3 }, +{ 0xC5A4, 0xC5A4, 0xC5A4 }, +{ 0xC5A5, 0xC5A5, 0xC5A5 }, +{ 0xC5A6, 0xC5A6, 0xC5A6 }, +{ 0xC5A7, 0xC5A7, 0xC5A7 }, +{ 0xC5A8, 0xC5A8, 0xC5A8 }, +{ 0xC5A9, 0xC5A9, 0xC5A9 }, +{ 0xC5AA, 0xC5AA, 0xC5AA }, +{ 0xC5AB, 0xC5AB, 0xC5AB }, +{ 0xC5AC, 0xC5AC, 0xC5AC }, +{ 0xC5AD, 0xC5AD, 0xC5AD }, +{ 0xC5AE, 0xC5AE, 0xC5AE }, +{ 0xC5AF, 0xC5AF, 0xC5AF }, +{ 0xC5B0, 0xC5B0, 0xC5B0 }, +{ 0xC5B1, 0xC5B1, 0xC5B1 }, +{ 0xC5B2, 0xC5B2, 0xC5B2 }, +{ 0xC5B3, 0xC5B3, 0xC5B3 }, +{ 0xC5B4, 0xC5B4, 0xC5B4 }, +{ 0xC5B5, 0xC5B5, 0xC5B5 }, +{ 0xC5B6, 0xC5B6, 0xC5B6 }, +{ 0xC5B7, 0xC5B7, 0xC5B7 }, +{ 0xC5B8, 0xC5B8, 0xC5B8 }, +{ 0xC5B9, 0xC5B9, 0xC5B9 }, +{ 0xC5BA, 0xC5BA, 0xC5BA }, +{ 0xC5BB, 0xC5BB, 0xC5BB }, +{ 0xC5BC, 0xC5BC, 0xC5BC }, +{ 0xC5BD, 0xC5BD, 0xC5BD }, +{ 0xC5BE, 0xC5BE, 0xC5BE }, +{ 0xC5BF, 0xC5BF, 0xC5BF }, +{ 0xC5C0, 0xC5C0, 0xC5C0 }, +{ 0xC5C1, 0xC5C1, 0xC5C1 }, +{ 0xC5C2, 0xC5C2, 0xC5C2 }, +{ 0xC5C3, 0xC5C3, 0xC5C3 }, +{ 0xC5C4, 0xC5C4, 0xC5C4 }, +{ 0xC5C5, 0xC5C5, 0xC5C5 }, +{ 0xC5C6, 0xC5C6, 0xC5C6 }, +{ 0xC5C7, 0xC5C7, 0xC5C7 }, +{ 0xC5C8, 0xC5C8, 0xC5C8 }, +{ 0xC5C9, 0xC5C9, 0xC5C9 }, +{ 0xC5CA, 0xC5CA, 0xC5CA }, +{ 0xC5CB, 0xC5CB, 0xC5CB }, +{ 0xC5CC, 0xC5CC, 0xC5CC }, +{ 0xC5CD, 0xC5CD, 0xC5CD }, +{ 0xC5CE, 0xC5CE, 0xC5CE }, +{ 0xC5CF, 0xC5CF, 0xC5CF }, +{ 0xC5D0, 0xC5D0, 0xC5D0 }, +{ 0xC5D1, 0xC5D1, 0xC5D1 }, +{ 0xC5D2, 0xC5D2, 0xC5D2 }, +{ 0xC5D3, 0xC5D3, 0xC5D3 }, +{ 0xC5D4, 0xC5D4, 0xC5D4 }, +{ 0xC5D5, 0xC5D5, 0xC5D5 }, +{ 0xC5D6, 0xC5D6, 0xC5D6 }, +{ 0xC5D7, 0xC5D7, 0xC5D7 }, +{ 0xC5D8, 0xC5D8, 0xC5D8 }, +{ 0xC5D9, 0xC5D9, 0xC5D9 }, +{ 0xC5DA, 0xC5DA, 0xC5DA }, +{ 0xC5DB, 0xC5DB, 0xC5DB }, +{ 0xC5DC, 0xC5DC, 0xC5DC }, +{ 0xC5DD, 0xC5DD, 0xC5DD }, +{ 0xC5DE, 0xC5DE, 0xC5DE }, +{ 0xC5DF, 0xC5DF, 0xC5DF }, +{ 0xC5E0, 0xC5E0, 0xC5E0 }, +{ 0xC5E1, 0xC5E1, 0xC5E1 }, +{ 0xC5E2, 0xC5E2, 0xC5E2 }, +{ 0xC5E3, 0xC5E3, 0xC5E3 }, +{ 0xC5E4, 0xC5E4, 0xC5E4 }, +{ 0xC5E5, 0xC5E5, 0xC5E5 }, +{ 0xC5E6, 0xC5E6, 0xC5E6 }, +{ 0xC5E7, 0xC5E7, 0xC5E7 }, +{ 0xC5E8, 0xC5E8, 0xC5E8 }, +{ 0xC5E9, 0xC5E9, 0xC5E9 }, +{ 0xC5EA, 0xC5EA, 0xC5EA }, +{ 0xC5EB, 0xC5EB, 0xC5EB }, +{ 0xC5EC, 0xC5EC, 0xC5EC }, +{ 0xC5ED, 0xC5ED, 0xC5ED }, +{ 0xC5EE, 0xC5EE, 0xC5EE }, +{ 0xC5EF, 0xC5EF, 0xC5EF }, +{ 0xC5F0, 0xC5F0, 0xC5F0 }, +{ 0xC5F1, 0xC5F1, 0xC5F1 }, +{ 0xC5F2, 0xC5F2, 0xC5F2 }, +{ 0xC5F3, 0xC5F3, 0xC5F3 }, +{ 0xC5F4, 0xC5F4, 0xC5F4 }, +{ 0xC5F5, 0xC5F5, 0xC5F5 }, +{ 0xC5F6, 0xC5F6, 0xC5F6 }, +{ 0xC5F7, 0xC5F7, 0xC5F7 }, +{ 0xC5F8, 0xC5F8, 0xC5F8 }, +{ 0xC5F9, 0xC5F9, 0xC5F9 }, +{ 0xC5FA, 0xC5FA, 0xC5FA }, +{ 0xC5FB, 0xC5FB, 0xC5FB }, +{ 0xC5FC, 0xC5FC, 0xC5FC }, +{ 0xC5FD, 0xC5FD, 0xC5FD }, +{ 0xC5FE, 0xC5FE, 0xC5FE }, +{ 0xC5FF, 0xC5FF, 0xC5FF }, +{ 0xC600, 0xC600, 0xC600 }, +{ 0xC601, 0xC601, 0xC601 }, +{ 0xC602, 0xC602, 0xC602 }, +{ 0xC603, 0xC603, 0xC603 }, +{ 0xC604, 0xC604, 0xC604 }, +{ 0xC605, 0xC605, 0xC605 }, +{ 0xC606, 0xC606, 0xC606 }, +{ 0xC607, 0xC607, 0xC607 }, +{ 0xC608, 0xC608, 0xC608 }, +{ 0xC609, 0xC609, 0xC609 }, +{ 0xC60A, 0xC60A, 0xC60A }, +{ 0xC60B, 0xC60B, 0xC60B }, +{ 0xC60C, 0xC60C, 0xC60C }, +{ 0xC60D, 0xC60D, 0xC60D }, +{ 0xC60E, 0xC60E, 0xC60E }, +{ 0xC60F, 0xC60F, 0xC60F }, +{ 0xC610, 0xC610, 0xC610 }, +{ 0xC611, 0xC611, 0xC611 }, +{ 0xC612, 0xC612, 0xC612 }, +{ 0xC613, 0xC613, 0xC613 }, +{ 0xC614, 0xC614, 0xC614 }, +{ 0xC615, 0xC615, 0xC615 }, +{ 0xC616, 0xC616, 0xC616 }, +{ 0xC617, 0xC617, 0xC617 }, +{ 0xC618, 0xC618, 0xC618 }, +{ 0xC619, 0xC619, 0xC619 }, +{ 0xC61A, 0xC61A, 0xC61A }, +{ 0xC61B, 0xC61B, 0xC61B }, +{ 0xC61C, 0xC61C, 0xC61C }, +{ 0xC61D, 0xC61D, 0xC61D }, +{ 0xC61E, 0xC61E, 0xC61E }, +{ 0xC61F, 0xC61F, 0xC61F }, +{ 0xC620, 0xC620, 0xC620 }, +{ 0xC621, 0xC621, 0xC621 }, +{ 0xC622, 0xC622, 0xC622 }, +{ 0xC623, 0xC623, 0xC623 }, +{ 0xC624, 0xC624, 0xC624 }, +{ 0xC625, 0xC625, 0xC625 }, +{ 0xC626, 0xC626, 0xC626 }, +{ 0xC627, 0xC627, 0xC627 }, +{ 0xC628, 0xC628, 0xC628 }, +{ 0xC629, 0xC629, 0xC629 }, +{ 0xC62A, 0xC62A, 0xC62A }, +{ 0xC62B, 0xC62B, 0xC62B }, +{ 0xC62C, 0xC62C, 0xC62C }, +{ 0xC62D, 0xC62D, 0xC62D }, +{ 0xC62E, 0xC62E, 0xC62E }, +{ 0xC62F, 0xC62F, 0xC62F }, +{ 0xC630, 0xC630, 0xC630 }, +{ 0xC631, 0xC631, 0xC631 }, +{ 0xC632, 0xC632, 0xC632 }, +{ 0xC633, 0xC633, 0xC633 }, +{ 0xC634, 0xC634, 0xC634 }, +{ 0xC635, 0xC635, 0xC635 }, +{ 0xC636, 0xC636, 0xC636 }, +{ 0xC637, 0xC637, 0xC637 }, +{ 0xC638, 0xC638, 0xC638 }, +{ 0xC639, 0xC639, 0xC639 }, +{ 0xC63A, 0xC63A, 0xC63A }, +{ 0xC63B, 0xC63B, 0xC63B }, +{ 0xC63C, 0xC63C, 0xC63C }, +{ 0xC63D, 0xC63D, 0xC63D }, +{ 0xC63E, 0xC63E, 0xC63E }, +{ 0xC63F, 0xC63F, 0xC63F }, +{ 0xC640, 0xC640, 0xC640 }, +{ 0xC641, 0xC641, 0xC641 }, +{ 0xC642, 0xC642, 0xC642 }, +{ 0xC643, 0xC643, 0xC643 }, +{ 0xC644, 0xC644, 0xC644 }, +{ 0xC645, 0xC645, 0xC645 }, +{ 0xC646, 0xC646, 0xC646 }, +{ 0xC647, 0xC647, 0xC647 }, +{ 0xC648, 0xC648, 0xC648 }, +{ 0xC649, 0xC649, 0xC649 }, +{ 0xC64A, 0xC64A, 0xC64A }, +{ 0xC64B, 0xC64B, 0xC64B }, +{ 0xC64C, 0xC64C, 0xC64C }, +{ 0xC64D, 0xC64D, 0xC64D }, +{ 0xC64E, 0xC64E, 0xC64E }, +{ 0xC64F, 0xC64F, 0xC64F }, +{ 0xC650, 0xC650, 0xC650 }, +{ 0xC651, 0xC651, 0xC651 }, +{ 0xC652, 0xC652, 0xC652 }, +{ 0xC653, 0xC653, 0xC653 }, +{ 0xC654, 0xC654, 0xC654 }, +{ 0xC655, 0xC655, 0xC655 }, +{ 0xC656, 0xC656, 0xC656 }, +{ 0xC657, 0xC657, 0xC657 }, +{ 0xC658, 0xC658, 0xC658 }, +{ 0xC659, 0xC659, 0xC659 }, +{ 0xC65A, 0xC65A, 0xC65A }, +{ 0xC65B, 0xC65B, 0xC65B }, +{ 0xC65C, 0xC65C, 0xC65C }, +{ 0xC65D, 0xC65D, 0xC65D }, +{ 0xC65E, 0xC65E, 0xC65E }, +{ 0xC65F, 0xC65F, 0xC65F }, +{ 0xC660, 0xC660, 0xC660 }, +{ 0xC661, 0xC661, 0xC661 }, +{ 0xC662, 0xC662, 0xC662 }, +{ 0xC663, 0xC663, 0xC663 }, +{ 0xC664, 0xC664, 0xC664 }, +{ 0xC665, 0xC665, 0xC665 }, +{ 0xC666, 0xC666, 0xC666 }, +{ 0xC667, 0xC667, 0xC667 }, +{ 0xC668, 0xC668, 0xC668 }, +{ 0xC669, 0xC669, 0xC669 }, +{ 0xC66A, 0xC66A, 0xC66A }, +{ 0xC66B, 0xC66B, 0xC66B }, +{ 0xC66C, 0xC66C, 0xC66C }, +{ 0xC66D, 0xC66D, 0xC66D }, +{ 0xC66E, 0xC66E, 0xC66E }, +{ 0xC66F, 0xC66F, 0xC66F }, +{ 0xC670, 0xC670, 0xC670 }, +{ 0xC671, 0xC671, 0xC671 }, +{ 0xC672, 0xC672, 0xC672 }, +{ 0xC673, 0xC673, 0xC673 }, +{ 0xC674, 0xC674, 0xC674 }, +{ 0xC675, 0xC675, 0xC675 }, +{ 0xC676, 0xC676, 0xC676 }, +{ 0xC677, 0xC677, 0xC677 }, +{ 0xC678, 0xC678, 0xC678 }, +{ 0xC679, 0xC679, 0xC679 }, +{ 0xC67A, 0xC67A, 0xC67A }, +{ 0xC67B, 0xC67B, 0xC67B }, +{ 0xC67C, 0xC67C, 0xC67C }, +{ 0xC67D, 0xC67D, 0xC67D }, +{ 0xC67E, 0xC67E, 0xC67E }, +{ 0xC67F, 0xC67F, 0xC67F }, +{ 0xC680, 0xC680, 0xC680 }, +{ 0xC681, 0xC681, 0xC681 }, +{ 0xC682, 0xC682, 0xC682 }, +{ 0xC683, 0xC683, 0xC683 }, +{ 0xC684, 0xC684, 0xC684 }, +{ 0xC685, 0xC685, 0xC685 }, +{ 0xC686, 0xC686, 0xC686 }, +{ 0xC687, 0xC687, 0xC687 }, +{ 0xC688, 0xC688, 0xC688 }, +{ 0xC689, 0xC689, 0xC689 }, +{ 0xC68A, 0xC68A, 0xC68A }, +{ 0xC68B, 0xC68B, 0xC68B }, +{ 0xC68C, 0xC68C, 0xC68C }, +{ 0xC68D, 0xC68D, 0xC68D }, +{ 0xC68E, 0xC68E, 0xC68E }, +{ 0xC68F, 0xC68F, 0xC68F }, +{ 0xC690, 0xC690, 0xC690 }, +{ 0xC691, 0xC691, 0xC691 }, +{ 0xC692, 0xC692, 0xC692 }, +{ 0xC693, 0xC693, 0xC693 }, +{ 0xC694, 0xC694, 0xC694 }, +{ 0xC695, 0xC695, 0xC695 }, +{ 0xC696, 0xC696, 0xC696 }, +{ 0xC697, 0xC697, 0xC697 }, +{ 0xC698, 0xC698, 0xC698 }, +{ 0xC699, 0xC699, 0xC699 }, +{ 0xC69A, 0xC69A, 0xC69A }, +{ 0xC69B, 0xC69B, 0xC69B }, +{ 0xC69C, 0xC69C, 0xC69C }, +{ 0xC69D, 0xC69D, 0xC69D }, +{ 0xC69E, 0xC69E, 0xC69E }, +{ 0xC69F, 0xC69F, 0xC69F }, +{ 0xC6A0, 0xC6A0, 0xC6A0 }, +{ 0xC6A1, 0xC6A1, 0xC6A1 }, +{ 0xC6A2, 0xC6A2, 0xC6A2 }, +{ 0xC6A3, 0xC6A3, 0xC6A3 }, +{ 0xC6A4, 0xC6A4, 0xC6A4 }, +{ 0xC6A5, 0xC6A5, 0xC6A5 }, +{ 0xC6A6, 0xC6A6, 0xC6A6 }, +{ 0xC6A7, 0xC6A7, 0xC6A7 }, +{ 0xC6A8, 0xC6A8, 0xC6A8 }, +{ 0xC6A9, 0xC6A9, 0xC6A9 }, +{ 0xC6AA, 0xC6AA, 0xC6AA }, +{ 0xC6AB, 0xC6AB, 0xC6AB }, +{ 0xC6AC, 0xC6AC, 0xC6AC }, +{ 0xC6AD, 0xC6AD, 0xC6AD }, +{ 0xC6AE, 0xC6AE, 0xC6AE }, +{ 0xC6AF, 0xC6AF, 0xC6AF }, +{ 0xC6B0, 0xC6B0, 0xC6B0 }, +{ 0xC6B1, 0xC6B1, 0xC6B1 }, +{ 0xC6B2, 0xC6B2, 0xC6B2 }, +{ 0xC6B3, 0xC6B3, 0xC6B3 }, +{ 0xC6B4, 0xC6B4, 0xC6B4 }, +{ 0xC6B5, 0xC6B5, 0xC6B5 }, +{ 0xC6B6, 0xC6B6, 0xC6B6 }, +{ 0xC6B7, 0xC6B7, 0xC6B7 }, +{ 0xC6B8, 0xC6B8, 0xC6B8 }, +{ 0xC6B9, 0xC6B9, 0xC6B9 }, +{ 0xC6BA, 0xC6BA, 0xC6BA }, +{ 0xC6BB, 0xC6BB, 0xC6BB }, +{ 0xC6BC, 0xC6BC, 0xC6BC }, +{ 0xC6BD, 0xC6BD, 0xC6BD }, +{ 0xC6BE, 0xC6BE, 0xC6BE }, +{ 0xC6BF, 0xC6BF, 0xC6BF }, +{ 0xC6C0, 0xC6C0, 0xC6C0 }, +{ 0xC6C1, 0xC6C1, 0xC6C1 }, +{ 0xC6C2, 0xC6C2, 0xC6C2 }, +{ 0xC6C3, 0xC6C3, 0xC6C3 }, +{ 0xC6C4, 0xC6C4, 0xC6C4 }, +{ 0xC6C5, 0xC6C5, 0xC6C5 }, +{ 0xC6C6, 0xC6C6, 0xC6C6 }, +{ 0xC6C7, 0xC6C7, 0xC6C7 }, +{ 0xC6C8, 0xC6C8, 0xC6C8 }, +{ 0xC6C9, 0xC6C9, 0xC6C9 }, +{ 0xC6CA, 0xC6CA, 0xC6CA }, +{ 0xC6CB, 0xC6CB, 0xC6CB }, +{ 0xC6CC, 0xC6CC, 0xC6CC }, +{ 0xC6CD, 0xC6CD, 0xC6CD }, +{ 0xC6CE, 0xC6CE, 0xC6CE }, +{ 0xC6CF, 0xC6CF, 0xC6CF }, +{ 0xC6D0, 0xC6D0, 0xC6D0 }, +{ 0xC6D1, 0xC6D1, 0xC6D1 }, +{ 0xC6D2, 0xC6D2, 0xC6D2 }, +{ 0xC6D3, 0xC6D3, 0xC6D3 }, +{ 0xC6D4, 0xC6D4, 0xC6D4 }, +{ 0xC6D5, 0xC6D5, 0xC6D5 }, +{ 0xC6D6, 0xC6D6, 0xC6D6 }, +{ 0xC6D7, 0xC6D7, 0xC6D7 }, +{ 0xC6D8, 0xC6D8, 0xC6D8 }, +{ 0xC6D9, 0xC6D9, 0xC6D9 }, +{ 0xC6DA, 0xC6DA, 0xC6DA }, +{ 0xC6DB, 0xC6DB, 0xC6DB }, +{ 0xC6DC, 0xC6DC, 0xC6DC }, +{ 0xC6DD, 0xC6DD, 0xC6DD }, +{ 0xC6DE, 0xC6DE, 0xC6DE }, +{ 0xC6DF, 0xC6DF, 0xC6DF }, +{ 0xC6E0, 0xC6E0, 0xC6E0 }, +{ 0xC6E1, 0xC6E1, 0xC6E1 }, +{ 0xC6E2, 0xC6E2, 0xC6E2 }, +{ 0xC6E3, 0xC6E3, 0xC6E3 }, +{ 0xC6E4, 0xC6E4, 0xC6E4 }, +{ 0xC6E5, 0xC6E5, 0xC6E5 }, +{ 0xC6E6, 0xC6E6, 0xC6E6 }, +{ 0xC6E7, 0xC6E7, 0xC6E7 }, +{ 0xC6E8, 0xC6E8, 0xC6E8 }, +{ 0xC6E9, 0xC6E9, 0xC6E9 }, +{ 0xC6EA, 0xC6EA, 0xC6EA }, +{ 0xC6EB, 0xC6EB, 0xC6EB }, +{ 0xC6EC, 0xC6EC, 0xC6EC }, +{ 0xC6ED, 0xC6ED, 0xC6ED }, +{ 0xC6EE, 0xC6EE, 0xC6EE }, +{ 0xC6EF, 0xC6EF, 0xC6EF }, +{ 0xC6F0, 0xC6F0, 0xC6F0 }, +{ 0xC6F1, 0xC6F1, 0xC6F1 }, +{ 0xC6F2, 0xC6F2, 0xC6F2 }, +{ 0xC6F3, 0xC6F3, 0xC6F3 }, +{ 0xC6F4, 0xC6F4, 0xC6F4 }, +{ 0xC6F5, 0xC6F5, 0xC6F5 }, +{ 0xC6F6, 0xC6F6, 0xC6F6 }, +{ 0xC6F7, 0xC6F7, 0xC6F7 }, +{ 0xC6F8, 0xC6F8, 0xC6F8 }, +{ 0xC6F9, 0xC6F9, 0xC6F9 }, +{ 0xC6FA, 0xC6FA, 0xC6FA }, +{ 0xC6FB, 0xC6FB, 0xC6FB }, +{ 0xC6FC, 0xC6FC, 0xC6FC }, +{ 0xC6FD, 0xC6FD, 0xC6FD }, +{ 0xC6FE, 0xC6FE, 0xC6FE }, +{ 0xC6FF, 0xC6FF, 0xC6FF }, +{ 0xC700, 0xC700, 0xC700 }, +{ 0xC701, 0xC701, 0xC701 }, +{ 0xC702, 0xC702, 0xC702 }, +{ 0xC703, 0xC703, 0xC703 }, +{ 0xC704, 0xC704, 0xC704 }, +{ 0xC705, 0xC705, 0xC705 }, +{ 0xC706, 0xC706, 0xC706 }, +{ 0xC707, 0xC707, 0xC707 }, +{ 0xC708, 0xC708, 0xC708 }, +{ 0xC709, 0xC709, 0xC709 }, +{ 0xC70A, 0xC70A, 0xC70A }, +{ 0xC70B, 0xC70B, 0xC70B }, +{ 0xC70C, 0xC70C, 0xC70C }, +{ 0xC70D, 0xC70D, 0xC70D }, +{ 0xC70E, 0xC70E, 0xC70E }, +{ 0xC70F, 0xC70F, 0xC70F }, +{ 0xC710, 0xC710, 0xC710 }, +{ 0xC711, 0xC711, 0xC711 }, +{ 0xC712, 0xC712, 0xC712 }, +{ 0xC713, 0xC713, 0xC713 }, +{ 0xC714, 0xC714, 0xC714 }, +{ 0xC715, 0xC715, 0xC715 }, +{ 0xC716, 0xC716, 0xC716 }, +{ 0xC717, 0xC717, 0xC717 }, +{ 0xC718, 0xC718, 0xC718 }, +{ 0xC719, 0xC719, 0xC719 }, +{ 0xC71A, 0xC71A, 0xC71A }, +{ 0xC71B, 0xC71B, 0xC71B }, +{ 0xC71C, 0xC71C, 0xC71C }, +{ 0xC71D, 0xC71D, 0xC71D }, +{ 0xC71E, 0xC71E, 0xC71E }, +{ 0xC71F, 0xC71F, 0xC71F }, +{ 0xC720, 0xC720, 0xC720 }, +{ 0xC721, 0xC721, 0xC721 }, +{ 0xC722, 0xC722, 0xC722 }, +{ 0xC723, 0xC723, 0xC723 }, +{ 0xC724, 0xC724, 0xC724 }, +{ 0xC725, 0xC725, 0xC725 }, +{ 0xC726, 0xC726, 0xC726 }, +{ 0xC727, 0xC727, 0xC727 }, +{ 0xC728, 0xC728, 0xC728 }, +{ 0xC729, 0xC729, 0xC729 }, +{ 0xC72A, 0xC72A, 0xC72A }, +{ 0xC72B, 0xC72B, 0xC72B }, +{ 0xC72C, 0xC72C, 0xC72C }, +{ 0xC72D, 0xC72D, 0xC72D }, +{ 0xC72E, 0xC72E, 0xC72E }, +{ 0xC72F, 0xC72F, 0xC72F }, +{ 0xC730, 0xC730, 0xC730 }, +{ 0xC731, 0xC731, 0xC731 }, +{ 0xC732, 0xC732, 0xC732 }, +{ 0xC733, 0xC733, 0xC733 }, +{ 0xC734, 0xC734, 0xC734 }, +{ 0xC735, 0xC735, 0xC735 }, +{ 0xC736, 0xC736, 0xC736 }, +{ 0xC737, 0xC737, 0xC737 }, +{ 0xC738, 0xC738, 0xC738 }, +{ 0xC739, 0xC739, 0xC739 }, +{ 0xC73A, 0xC73A, 0xC73A }, +{ 0xC73B, 0xC73B, 0xC73B }, +{ 0xC73C, 0xC73C, 0xC73C }, +{ 0xC73D, 0xC73D, 0xC73D }, +{ 0xC73E, 0xC73E, 0xC73E }, +{ 0xC73F, 0xC73F, 0xC73F }, +{ 0xC740, 0xC740, 0xC740 }, +{ 0xC741, 0xC741, 0xC741 }, +{ 0xC742, 0xC742, 0xC742 }, +{ 0xC743, 0xC743, 0xC743 }, +{ 0xC744, 0xC744, 0xC744 }, +{ 0xC745, 0xC745, 0xC745 }, +{ 0xC746, 0xC746, 0xC746 }, +{ 0xC747, 0xC747, 0xC747 }, +{ 0xC748, 0xC748, 0xC748 }, +{ 0xC749, 0xC749, 0xC749 }, +{ 0xC74A, 0xC74A, 0xC74A }, +{ 0xC74B, 0xC74B, 0xC74B }, +{ 0xC74C, 0xC74C, 0xC74C }, +{ 0xC74D, 0xC74D, 0xC74D }, +{ 0xC74E, 0xC74E, 0xC74E }, +{ 0xC74F, 0xC74F, 0xC74F }, +{ 0xC750, 0xC750, 0xC750 }, +{ 0xC751, 0xC751, 0xC751 }, +{ 0xC752, 0xC752, 0xC752 }, +{ 0xC753, 0xC753, 0xC753 }, +{ 0xC754, 0xC754, 0xC754 }, +{ 0xC755, 0xC755, 0xC755 }, +{ 0xC756, 0xC756, 0xC756 }, +{ 0xC757, 0xC757, 0xC757 }, +{ 0xC758, 0xC758, 0xC758 }, +{ 0xC759, 0xC759, 0xC759 }, +{ 0xC75A, 0xC75A, 0xC75A }, +{ 0xC75B, 0xC75B, 0xC75B }, +{ 0xC75C, 0xC75C, 0xC75C }, +{ 0xC75D, 0xC75D, 0xC75D }, +{ 0xC75E, 0xC75E, 0xC75E }, +{ 0xC75F, 0xC75F, 0xC75F }, +{ 0xC760, 0xC760, 0xC760 }, +{ 0xC761, 0xC761, 0xC761 }, +{ 0xC762, 0xC762, 0xC762 }, +{ 0xC763, 0xC763, 0xC763 }, +{ 0xC764, 0xC764, 0xC764 }, +{ 0xC765, 0xC765, 0xC765 }, +{ 0xC766, 0xC766, 0xC766 }, +{ 0xC767, 0xC767, 0xC767 }, +{ 0xC768, 0xC768, 0xC768 }, +{ 0xC769, 0xC769, 0xC769 }, +{ 0xC76A, 0xC76A, 0xC76A }, +{ 0xC76B, 0xC76B, 0xC76B }, +{ 0xC76C, 0xC76C, 0xC76C }, +{ 0xC76D, 0xC76D, 0xC76D }, +{ 0xC76E, 0xC76E, 0xC76E }, +{ 0xC76F, 0xC76F, 0xC76F }, +{ 0xC770, 0xC770, 0xC770 }, +{ 0xC771, 0xC771, 0xC771 }, +{ 0xC772, 0xC772, 0xC772 }, +{ 0xC773, 0xC773, 0xC773 }, +{ 0xC774, 0xC774, 0xC774 }, +{ 0xC775, 0xC775, 0xC775 }, +{ 0xC776, 0xC776, 0xC776 }, +{ 0xC777, 0xC777, 0xC777 }, +{ 0xC778, 0xC778, 0xC778 }, +{ 0xC779, 0xC779, 0xC779 }, +{ 0xC77A, 0xC77A, 0xC77A }, +{ 0xC77B, 0xC77B, 0xC77B }, +{ 0xC77C, 0xC77C, 0xC77C }, +{ 0xC77D, 0xC77D, 0xC77D }, +{ 0xC77E, 0xC77E, 0xC77E }, +{ 0xC77F, 0xC77F, 0xC77F }, +{ 0xC780, 0xC780, 0xC780 }, +{ 0xC781, 0xC781, 0xC781 }, +{ 0xC782, 0xC782, 0xC782 }, +{ 0xC783, 0xC783, 0xC783 }, +{ 0xC784, 0xC784, 0xC784 }, +{ 0xC785, 0xC785, 0xC785 }, +{ 0xC786, 0xC786, 0xC786 }, +{ 0xC787, 0xC787, 0xC787 }, +{ 0xC788, 0xC788, 0xC788 }, +{ 0xC789, 0xC789, 0xC789 }, +{ 0xC78A, 0xC78A, 0xC78A }, +{ 0xC78B, 0xC78B, 0xC78B }, +{ 0xC78C, 0xC78C, 0xC78C }, +{ 0xC78D, 0xC78D, 0xC78D }, +{ 0xC78E, 0xC78E, 0xC78E }, +{ 0xC78F, 0xC78F, 0xC78F }, +{ 0xC790, 0xC790, 0xC790 }, +{ 0xC791, 0xC791, 0xC791 }, +{ 0xC792, 0xC792, 0xC792 }, +{ 0xC793, 0xC793, 0xC793 }, +{ 0xC794, 0xC794, 0xC794 }, +{ 0xC795, 0xC795, 0xC795 }, +{ 0xC796, 0xC796, 0xC796 }, +{ 0xC797, 0xC797, 0xC797 }, +{ 0xC798, 0xC798, 0xC798 }, +{ 0xC799, 0xC799, 0xC799 }, +{ 0xC79A, 0xC79A, 0xC79A }, +{ 0xC79B, 0xC79B, 0xC79B }, +{ 0xC79C, 0xC79C, 0xC79C }, +{ 0xC79D, 0xC79D, 0xC79D }, +{ 0xC79E, 0xC79E, 0xC79E }, +{ 0xC79F, 0xC79F, 0xC79F }, +{ 0xC7A0, 0xC7A0, 0xC7A0 }, +{ 0xC7A1, 0xC7A1, 0xC7A1 }, +{ 0xC7A2, 0xC7A2, 0xC7A2 }, +{ 0xC7A3, 0xC7A3, 0xC7A3 }, +{ 0xC7A4, 0xC7A4, 0xC7A4 }, +{ 0xC7A5, 0xC7A5, 0xC7A5 }, +{ 0xC7A6, 0xC7A6, 0xC7A6 }, +{ 0xC7A7, 0xC7A7, 0xC7A7 }, +{ 0xC7A8, 0xC7A8, 0xC7A8 }, +{ 0xC7A9, 0xC7A9, 0xC7A9 }, +{ 0xC7AA, 0xC7AA, 0xC7AA }, +{ 0xC7AB, 0xC7AB, 0xC7AB }, +{ 0xC7AC, 0xC7AC, 0xC7AC }, +{ 0xC7AD, 0xC7AD, 0xC7AD }, +{ 0xC7AE, 0xC7AE, 0xC7AE }, +{ 0xC7AF, 0xC7AF, 0xC7AF }, +{ 0xC7B0, 0xC7B0, 0xC7B0 }, +{ 0xC7B1, 0xC7B1, 0xC7B1 }, +{ 0xC7B2, 0xC7B2, 0xC7B2 }, +{ 0xC7B3, 0xC7B3, 0xC7B3 }, +{ 0xC7B4, 0xC7B4, 0xC7B4 }, +{ 0xC7B5, 0xC7B5, 0xC7B5 }, +{ 0xC7B6, 0xC7B6, 0xC7B6 }, +{ 0xC7B7, 0xC7B7, 0xC7B7 }, +{ 0xC7B8, 0xC7B8, 0xC7B8 }, +{ 0xC7B9, 0xC7B9, 0xC7B9 }, +{ 0xC7BA, 0xC7BA, 0xC7BA }, +{ 0xC7BB, 0xC7BB, 0xC7BB }, +{ 0xC7BC, 0xC7BC, 0xC7BC }, +{ 0xC7BD, 0xC7BD, 0xC7BD }, +{ 0xC7BE, 0xC7BE, 0xC7BE }, +{ 0xC7BF, 0xC7BF, 0xC7BF }, +{ 0xC7C0, 0xC7C0, 0xC7C0 }, +{ 0xC7C1, 0xC7C1, 0xC7C1 }, +{ 0xC7C2, 0xC7C2, 0xC7C2 }, +{ 0xC7C3, 0xC7C3, 0xC7C3 }, +{ 0xC7C4, 0xC7C4, 0xC7C4 }, +{ 0xC7C5, 0xC7C5, 0xC7C5 }, +{ 0xC7C6, 0xC7C6, 0xC7C6 }, +{ 0xC7C7, 0xC7C7, 0xC7C7 }, +{ 0xC7C8, 0xC7C8, 0xC7C8 }, +{ 0xC7C9, 0xC7C9, 0xC7C9 }, +{ 0xC7CA, 0xC7CA, 0xC7CA }, +{ 0xC7CB, 0xC7CB, 0xC7CB }, +{ 0xC7CC, 0xC7CC, 0xC7CC }, +{ 0xC7CD, 0xC7CD, 0xC7CD }, +{ 0xC7CE, 0xC7CE, 0xC7CE }, +{ 0xC7CF, 0xC7CF, 0xC7CF }, +{ 0xC7D0, 0xC7D0, 0xC7D0 }, +{ 0xC7D1, 0xC7D1, 0xC7D1 }, +{ 0xC7D2, 0xC7D2, 0xC7D2 }, +{ 0xC7D3, 0xC7D3, 0xC7D3 }, +{ 0xC7D4, 0xC7D4, 0xC7D4 }, +{ 0xC7D5, 0xC7D5, 0xC7D5 }, +{ 0xC7D6, 0xC7D6, 0xC7D6 }, +{ 0xC7D7, 0xC7D7, 0xC7D7 }, +{ 0xC7D8, 0xC7D8, 0xC7D8 }, +{ 0xC7D9, 0xC7D9, 0xC7D9 }, +{ 0xC7DA, 0xC7DA, 0xC7DA }, +{ 0xC7DB, 0xC7DB, 0xC7DB }, +{ 0xC7DC, 0xC7DC, 0xC7DC }, +{ 0xC7DD, 0xC7DD, 0xC7DD }, +{ 0xC7DE, 0xC7DE, 0xC7DE }, +{ 0xC7DF, 0xC7DF, 0xC7DF }, +{ 0xC7E0, 0xC7E0, 0xC7E0 }, +{ 0xC7E1, 0xC7E1, 0xC7E1 }, +{ 0xC7E2, 0xC7E2, 0xC7E2 }, +{ 0xC7E3, 0xC7E3, 0xC7E3 }, +{ 0xC7E4, 0xC7E4, 0xC7E4 }, +{ 0xC7E5, 0xC7E5, 0xC7E5 }, +{ 0xC7E6, 0xC7E6, 0xC7E6 }, +{ 0xC7E7, 0xC7E7, 0xC7E7 }, +{ 0xC7E8, 0xC7E8, 0xC7E8 }, +{ 0xC7E9, 0xC7E9, 0xC7E9 }, +{ 0xC7EA, 0xC7EA, 0xC7EA }, +{ 0xC7EB, 0xC7EB, 0xC7EB }, +{ 0xC7EC, 0xC7EC, 0xC7EC }, +{ 0xC7ED, 0xC7ED, 0xC7ED }, +{ 0xC7EE, 0xC7EE, 0xC7EE }, +{ 0xC7EF, 0xC7EF, 0xC7EF }, +{ 0xC7F0, 0xC7F0, 0xC7F0 }, +{ 0xC7F1, 0xC7F1, 0xC7F1 }, +{ 0xC7F2, 0xC7F2, 0xC7F2 }, +{ 0xC7F3, 0xC7F3, 0xC7F3 }, +{ 0xC7F4, 0xC7F4, 0xC7F4 }, +{ 0xC7F5, 0xC7F5, 0xC7F5 }, +{ 0xC7F6, 0xC7F6, 0xC7F6 }, +{ 0xC7F7, 0xC7F7, 0xC7F7 }, +{ 0xC7F8, 0xC7F8, 0xC7F8 }, +{ 0xC7F9, 0xC7F9, 0xC7F9 }, +{ 0xC7FA, 0xC7FA, 0xC7FA }, +{ 0xC7FB, 0xC7FB, 0xC7FB }, +{ 0xC7FC, 0xC7FC, 0xC7FC }, +{ 0xC7FD, 0xC7FD, 0xC7FD }, +{ 0xC7FE, 0xC7FE, 0xC7FE }, +{ 0xC7FF, 0xC7FF, 0xC7FF }, +{ 0xC800, 0xC800, 0xC800 }, +{ 0xC801, 0xC801, 0xC801 }, +{ 0xC802, 0xC802, 0xC802 }, +{ 0xC803, 0xC803, 0xC803 }, +{ 0xC804, 0xC804, 0xC804 }, +{ 0xC805, 0xC805, 0xC805 }, +{ 0xC806, 0xC806, 0xC806 }, +{ 0xC807, 0xC807, 0xC807 }, +{ 0xC808, 0xC808, 0xC808 }, +{ 0xC809, 0xC809, 0xC809 }, +{ 0xC80A, 0xC80A, 0xC80A }, +{ 0xC80B, 0xC80B, 0xC80B }, +{ 0xC80C, 0xC80C, 0xC80C }, +{ 0xC80D, 0xC80D, 0xC80D }, +{ 0xC80E, 0xC80E, 0xC80E }, +{ 0xC80F, 0xC80F, 0xC80F }, +{ 0xC810, 0xC810, 0xC810 }, +{ 0xC811, 0xC811, 0xC811 }, +{ 0xC812, 0xC812, 0xC812 }, +{ 0xC813, 0xC813, 0xC813 }, +{ 0xC814, 0xC814, 0xC814 }, +{ 0xC815, 0xC815, 0xC815 }, +{ 0xC816, 0xC816, 0xC816 }, +{ 0xC817, 0xC817, 0xC817 }, +{ 0xC818, 0xC818, 0xC818 }, +{ 0xC819, 0xC819, 0xC819 }, +{ 0xC81A, 0xC81A, 0xC81A }, +{ 0xC81B, 0xC81B, 0xC81B }, +{ 0xC81C, 0xC81C, 0xC81C }, +{ 0xC81D, 0xC81D, 0xC81D }, +{ 0xC81E, 0xC81E, 0xC81E }, +{ 0xC81F, 0xC81F, 0xC81F }, +{ 0xC820, 0xC820, 0xC820 }, +{ 0xC821, 0xC821, 0xC821 }, +{ 0xC822, 0xC822, 0xC822 }, +{ 0xC823, 0xC823, 0xC823 }, +{ 0xC824, 0xC824, 0xC824 }, +{ 0xC825, 0xC825, 0xC825 }, +{ 0xC826, 0xC826, 0xC826 }, +{ 0xC827, 0xC827, 0xC827 }, +{ 0xC828, 0xC828, 0xC828 }, +{ 0xC829, 0xC829, 0xC829 }, +{ 0xC82A, 0xC82A, 0xC82A }, +{ 0xC82B, 0xC82B, 0xC82B }, +{ 0xC82C, 0xC82C, 0xC82C }, +{ 0xC82D, 0xC82D, 0xC82D }, +{ 0xC82E, 0xC82E, 0xC82E }, +{ 0xC82F, 0xC82F, 0xC82F }, +{ 0xC830, 0xC830, 0xC830 }, +{ 0xC831, 0xC831, 0xC831 }, +{ 0xC832, 0xC832, 0xC832 }, +{ 0xC833, 0xC833, 0xC833 }, +{ 0xC834, 0xC834, 0xC834 }, +{ 0xC835, 0xC835, 0xC835 }, +{ 0xC836, 0xC836, 0xC836 }, +{ 0xC837, 0xC837, 0xC837 }, +{ 0xC838, 0xC838, 0xC838 }, +{ 0xC839, 0xC839, 0xC839 }, +{ 0xC83A, 0xC83A, 0xC83A }, +{ 0xC83B, 0xC83B, 0xC83B }, +{ 0xC83C, 0xC83C, 0xC83C }, +{ 0xC83D, 0xC83D, 0xC83D }, +{ 0xC83E, 0xC83E, 0xC83E }, +{ 0xC83F, 0xC83F, 0xC83F }, +{ 0xC840, 0xC840, 0xC840 }, +{ 0xC841, 0xC841, 0xC841 }, +{ 0xC842, 0xC842, 0xC842 }, +{ 0xC843, 0xC843, 0xC843 }, +{ 0xC844, 0xC844, 0xC844 }, +{ 0xC845, 0xC845, 0xC845 }, +{ 0xC846, 0xC846, 0xC846 }, +{ 0xC847, 0xC847, 0xC847 }, +{ 0xC848, 0xC848, 0xC848 }, +{ 0xC849, 0xC849, 0xC849 }, +{ 0xC84A, 0xC84A, 0xC84A }, +{ 0xC84B, 0xC84B, 0xC84B }, +{ 0xC84C, 0xC84C, 0xC84C }, +{ 0xC84D, 0xC84D, 0xC84D }, +{ 0xC84E, 0xC84E, 0xC84E }, +{ 0xC84F, 0xC84F, 0xC84F }, +{ 0xC850, 0xC850, 0xC850 }, +{ 0xC851, 0xC851, 0xC851 }, +{ 0xC852, 0xC852, 0xC852 }, +{ 0xC853, 0xC853, 0xC853 }, +{ 0xC854, 0xC854, 0xC854 }, +{ 0xC855, 0xC855, 0xC855 }, +{ 0xC856, 0xC856, 0xC856 }, +{ 0xC857, 0xC857, 0xC857 }, +{ 0xC858, 0xC858, 0xC858 }, +{ 0xC859, 0xC859, 0xC859 }, +{ 0xC85A, 0xC85A, 0xC85A }, +{ 0xC85B, 0xC85B, 0xC85B }, +{ 0xC85C, 0xC85C, 0xC85C }, +{ 0xC85D, 0xC85D, 0xC85D }, +{ 0xC85E, 0xC85E, 0xC85E }, +{ 0xC85F, 0xC85F, 0xC85F }, +{ 0xC860, 0xC860, 0xC860 }, +{ 0xC861, 0xC861, 0xC861 }, +{ 0xC862, 0xC862, 0xC862 }, +{ 0xC863, 0xC863, 0xC863 }, +{ 0xC864, 0xC864, 0xC864 }, +{ 0xC865, 0xC865, 0xC865 }, +{ 0xC866, 0xC866, 0xC866 }, +{ 0xC867, 0xC867, 0xC867 }, +{ 0xC868, 0xC868, 0xC868 }, +{ 0xC869, 0xC869, 0xC869 }, +{ 0xC86A, 0xC86A, 0xC86A }, +{ 0xC86B, 0xC86B, 0xC86B }, +{ 0xC86C, 0xC86C, 0xC86C }, +{ 0xC86D, 0xC86D, 0xC86D }, +{ 0xC86E, 0xC86E, 0xC86E }, +{ 0xC86F, 0xC86F, 0xC86F }, +{ 0xC870, 0xC870, 0xC870 }, +{ 0xC871, 0xC871, 0xC871 }, +{ 0xC872, 0xC872, 0xC872 }, +{ 0xC873, 0xC873, 0xC873 }, +{ 0xC874, 0xC874, 0xC874 }, +{ 0xC875, 0xC875, 0xC875 }, +{ 0xC876, 0xC876, 0xC876 }, +{ 0xC877, 0xC877, 0xC877 }, +{ 0xC878, 0xC878, 0xC878 }, +{ 0xC879, 0xC879, 0xC879 }, +{ 0xC87A, 0xC87A, 0xC87A }, +{ 0xC87B, 0xC87B, 0xC87B }, +{ 0xC87C, 0xC87C, 0xC87C }, +{ 0xC87D, 0xC87D, 0xC87D }, +{ 0xC87E, 0xC87E, 0xC87E }, +{ 0xC87F, 0xC87F, 0xC87F }, +{ 0xC880, 0xC880, 0xC880 }, +{ 0xC881, 0xC881, 0xC881 }, +{ 0xC882, 0xC882, 0xC882 }, +{ 0xC883, 0xC883, 0xC883 }, +{ 0xC884, 0xC884, 0xC884 }, +{ 0xC885, 0xC885, 0xC885 }, +{ 0xC886, 0xC886, 0xC886 }, +{ 0xC887, 0xC887, 0xC887 }, +{ 0xC888, 0xC888, 0xC888 }, +{ 0xC889, 0xC889, 0xC889 }, +{ 0xC88A, 0xC88A, 0xC88A }, +{ 0xC88B, 0xC88B, 0xC88B }, +{ 0xC88C, 0xC88C, 0xC88C }, +{ 0xC88D, 0xC88D, 0xC88D }, +{ 0xC88E, 0xC88E, 0xC88E }, +{ 0xC88F, 0xC88F, 0xC88F }, +{ 0xC890, 0xC890, 0xC890 }, +{ 0xC891, 0xC891, 0xC891 }, +{ 0xC892, 0xC892, 0xC892 }, +{ 0xC893, 0xC893, 0xC893 }, +{ 0xC894, 0xC894, 0xC894 }, +{ 0xC895, 0xC895, 0xC895 }, +{ 0xC896, 0xC896, 0xC896 }, +{ 0xC897, 0xC897, 0xC897 }, +{ 0xC898, 0xC898, 0xC898 }, +{ 0xC899, 0xC899, 0xC899 }, +{ 0xC89A, 0xC89A, 0xC89A }, +{ 0xC89B, 0xC89B, 0xC89B }, +{ 0xC89C, 0xC89C, 0xC89C }, +{ 0xC89D, 0xC89D, 0xC89D }, +{ 0xC89E, 0xC89E, 0xC89E }, +{ 0xC89F, 0xC89F, 0xC89F }, +{ 0xC8A0, 0xC8A0, 0xC8A0 }, +{ 0xC8A1, 0xC8A1, 0xC8A1 }, +{ 0xC8A2, 0xC8A2, 0xC8A2 }, +{ 0xC8A3, 0xC8A3, 0xC8A3 }, +{ 0xC8A4, 0xC8A4, 0xC8A4 }, +{ 0xC8A5, 0xC8A5, 0xC8A5 }, +{ 0xC8A6, 0xC8A6, 0xC8A6 }, +{ 0xC8A7, 0xC8A7, 0xC8A7 }, +{ 0xC8A8, 0xC8A8, 0xC8A8 }, +{ 0xC8A9, 0xC8A9, 0xC8A9 }, +{ 0xC8AA, 0xC8AA, 0xC8AA }, +{ 0xC8AB, 0xC8AB, 0xC8AB }, +{ 0xC8AC, 0xC8AC, 0xC8AC }, +{ 0xC8AD, 0xC8AD, 0xC8AD }, +{ 0xC8AE, 0xC8AE, 0xC8AE }, +{ 0xC8AF, 0xC8AF, 0xC8AF }, +{ 0xC8B0, 0xC8B0, 0xC8B0 }, +{ 0xC8B1, 0xC8B1, 0xC8B1 }, +{ 0xC8B2, 0xC8B2, 0xC8B2 }, +{ 0xC8B3, 0xC8B3, 0xC8B3 }, +{ 0xC8B4, 0xC8B4, 0xC8B4 }, +{ 0xC8B5, 0xC8B5, 0xC8B5 }, +{ 0xC8B6, 0xC8B6, 0xC8B6 }, +{ 0xC8B7, 0xC8B7, 0xC8B7 }, +{ 0xC8B8, 0xC8B8, 0xC8B8 }, +{ 0xC8B9, 0xC8B9, 0xC8B9 }, +{ 0xC8BA, 0xC8BA, 0xC8BA }, +{ 0xC8BB, 0xC8BB, 0xC8BB }, +{ 0xC8BC, 0xC8BC, 0xC8BC }, +{ 0xC8BD, 0xC8BD, 0xC8BD }, +{ 0xC8BE, 0xC8BE, 0xC8BE }, +{ 0xC8BF, 0xC8BF, 0xC8BF }, +{ 0xC8C0, 0xC8C0, 0xC8C0 }, +{ 0xC8C1, 0xC8C1, 0xC8C1 }, +{ 0xC8C2, 0xC8C2, 0xC8C2 }, +{ 0xC8C3, 0xC8C3, 0xC8C3 }, +{ 0xC8C4, 0xC8C4, 0xC8C4 }, +{ 0xC8C5, 0xC8C5, 0xC8C5 }, +{ 0xC8C6, 0xC8C6, 0xC8C6 }, +{ 0xC8C7, 0xC8C7, 0xC8C7 }, +{ 0xC8C8, 0xC8C8, 0xC8C8 }, +{ 0xC8C9, 0xC8C9, 0xC8C9 }, +{ 0xC8CA, 0xC8CA, 0xC8CA }, +{ 0xC8CB, 0xC8CB, 0xC8CB }, +{ 0xC8CC, 0xC8CC, 0xC8CC }, +{ 0xC8CD, 0xC8CD, 0xC8CD }, +{ 0xC8CE, 0xC8CE, 0xC8CE }, +{ 0xC8CF, 0xC8CF, 0xC8CF }, +{ 0xC8D0, 0xC8D0, 0xC8D0 }, +{ 0xC8D1, 0xC8D1, 0xC8D1 }, +{ 0xC8D2, 0xC8D2, 0xC8D2 }, +{ 0xC8D3, 0xC8D3, 0xC8D3 }, +{ 0xC8D4, 0xC8D4, 0xC8D4 }, +{ 0xC8D5, 0xC8D5, 0xC8D5 }, +{ 0xC8D6, 0xC8D6, 0xC8D6 }, +{ 0xC8D7, 0xC8D7, 0xC8D7 }, +{ 0xC8D8, 0xC8D8, 0xC8D8 }, +{ 0xC8D9, 0xC8D9, 0xC8D9 }, +{ 0xC8DA, 0xC8DA, 0xC8DA }, +{ 0xC8DB, 0xC8DB, 0xC8DB }, +{ 0xC8DC, 0xC8DC, 0xC8DC }, +{ 0xC8DD, 0xC8DD, 0xC8DD }, +{ 0xC8DE, 0xC8DE, 0xC8DE }, +{ 0xC8DF, 0xC8DF, 0xC8DF }, +{ 0xC8E0, 0xC8E0, 0xC8E0 }, +{ 0xC8E1, 0xC8E1, 0xC8E1 }, +{ 0xC8E2, 0xC8E2, 0xC8E2 }, +{ 0xC8E3, 0xC8E3, 0xC8E3 }, +{ 0xC8E4, 0xC8E4, 0xC8E4 }, +{ 0xC8E5, 0xC8E5, 0xC8E5 }, +{ 0xC8E6, 0xC8E6, 0xC8E6 }, +{ 0xC8E7, 0xC8E7, 0xC8E7 }, +{ 0xC8E8, 0xC8E8, 0xC8E8 }, +{ 0xC8E9, 0xC8E9, 0xC8E9 }, +{ 0xC8EA, 0xC8EA, 0xC8EA }, +{ 0xC8EB, 0xC8EB, 0xC8EB }, +{ 0xC8EC, 0xC8EC, 0xC8EC }, +{ 0xC8ED, 0xC8ED, 0xC8ED }, +{ 0xC8EE, 0xC8EE, 0xC8EE }, +{ 0xC8EF, 0xC8EF, 0xC8EF }, +{ 0xC8F0, 0xC8F0, 0xC8F0 }, +{ 0xC8F1, 0xC8F1, 0xC8F1 }, +{ 0xC8F2, 0xC8F2, 0xC8F2 }, +{ 0xC8F3, 0xC8F3, 0xC8F3 }, +{ 0xC8F4, 0xC8F4, 0xC8F4 }, +{ 0xC8F5, 0xC8F5, 0xC8F5 }, +{ 0xC8F6, 0xC8F6, 0xC8F6 }, +{ 0xC8F7, 0xC8F7, 0xC8F7 }, +{ 0xC8F8, 0xC8F8, 0xC8F8 }, +{ 0xC8F9, 0xC8F9, 0xC8F9 }, +{ 0xC8FA, 0xC8FA, 0xC8FA }, +{ 0xC8FB, 0xC8FB, 0xC8FB }, +{ 0xC8FC, 0xC8FC, 0xC8FC }, +{ 0xC8FD, 0xC8FD, 0xC8FD }, +{ 0xC8FE, 0xC8FE, 0xC8FE }, +{ 0xC8FF, 0xC8FF, 0xC8FF }, +{ 0xC900, 0xC900, 0xC900 }, +{ 0xC901, 0xC901, 0xC901 }, +{ 0xC902, 0xC902, 0xC902 }, +{ 0xC903, 0xC903, 0xC903 }, +{ 0xC904, 0xC904, 0xC904 }, +{ 0xC905, 0xC905, 0xC905 }, +{ 0xC906, 0xC906, 0xC906 }, +{ 0xC907, 0xC907, 0xC907 }, +{ 0xC908, 0xC908, 0xC908 }, +{ 0xC909, 0xC909, 0xC909 }, +{ 0xC90A, 0xC90A, 0xC90A }, +{ 0xC90B, 0xC90B, 0xC90B }, +{ 0xC90C, 0xC90C, 0xC90C }, +{ 0xC90D, 0xC90D, 0xC90D }, +{ 0xC90E, 0xC90E, 0xC90E }, +{ 0xC90F, 0xC90F, 0xC90F }, +{ 0xC910, 0xC910, 0xC910 }, +{ 0xC911, 0xC911, 0xC911 }, +{ 0xC912, 0xC912, 0xC912 }, +{ 0xC913, 0xC913, 0xC913 }, +{ 0xC914, 0xC914, 0xC914 }, +{ 0xC915, 0xC915, 0xC915 }, +{ 0xC916, 0xC916, 0xC916 }, +{ 0xC917, 0xC917, 0xC917 }, +{ 0xC918, 0xC918, 0xC918 }, +{ 0xC919, 0xC919, 0xC919 }, +{ 0xC91A, 0xC91A, 0xC91A }, +{ 0xC91B, 0xC91B, 0xC91B }, +{ 0xC91C, 0xC91C, 0xC91C }, +{ 0xC91D, 0xC91D, 0xC91D }, +{ 0xC91E, 0xC91E, 0xC91E }, +{ 0xC91F, 0xC91F, 0xC91F }, +{ 0xC920, 0xC920, 0xC920 }, +{ 0xC921, 0xC921, 0xC921 }, +{ 0xC922, 0xC922, 0xC922 }, +{ 0xC923, 0xC923, 0xC923 }, +{ 0xC924, 0xC924, 0xC924 }, +{ 0xC925, 0xC925, 0xC925 }, +{ 0xC926, 0xC926, 0xC926 }, +{ 0xC927, 0xC927, 0xC927 }, +{ 0xC928, 0xC928, 0xC928 }, +{ 0xC929, 0xC929, 0xC929 }, +{ 0xC92A, 0xC92A, 0xC92A }, +{ 0xC92B, 0xC92B, 0xC92B }, +{ 0xC92C, 0xC92C, 0xC92C }, +{ 0xC92D, 0xC92D, 0xC92D }, +{ 0xC92E, 0xC92E, 0xC92E }, +{ 0xC92F, 0xC92F, 0xC92F }, +{ 0xC930, 0xC930, 0xC930 }, +{ 0xC931, 0xC931, 0xC931 }, +{ 0xC932, 0xC932, 0xC932 }, +{ 0xC933, 0xC933, 0xC933 }, +{ 0xC934, 0xC934, 0xC934 }, +{ 0xC935, 0xC935, 0xC935 }, +{ 0xC936, 0xC936, 0xC936 }, +{ 0xC937, 0xC937, 0xC937 }, +{ 0xC938, 0xC938, 0xC938 }, +{ 0xC939, 0xC939, 0xC939 }, +{ 0xC93A, 0xC93A, 0xC93A }, +{ 0xC93B, 0xC93B, 0xC93B }, +{ 0xC93C, 0xC93C, 0xC93C }, +{ 0xC93D, 0xC93D, 0xC93D }, +{ 0xC93E, 0xC93E, 0xC93E }, +{ 0xC93F, 0xC93F, 0xC93F }, +{ 0xC940, 0xC940, 0xC940 }, +{ 0xC941, 0xC941, 0xC941 }, +{ 0xC942, 0xC942, 0xC942 }, +{ 0xC943, 0xC943, 0xC943 }, +{ 0xC944, 0xC944, 0xC944 }, +{ 0xC945, 0xC945, 0xC945 }, +{ 0xC946, 0xC946, 0xC946 }, +{ 0xC947, 0xC947, 0xC947 }, +{ 0xC948, 0xC948, 0xC948 }, +{ 0xC949, 0xC949, 0xC949 }, +{ 0xC94A, 0xC94A, 0xC94A }, +{ 0xC94B, 0xC94B, 0xC94B }, +{ 0xC94C, 0xC94C, 0xC94C }, +{ 0xC94D, 0xC94D, 0xC94D }, +{ 0xC94E, 0xC94E, 0xC94E }, +{ 0xC94F, 0xC94F, 0xC94F }, +{ 0xC950, 0xC950, 0xC950 }, +{ 0xC951, 0xC951, 0xC951 }, +{ 0xC952, 0xC952, 0xC952 }, +{ 0xC953, 0xC953, 0xC953 }, +{ 0xC954, 0xC954, 0xC954 }, +{ 0xC955, 0xC955, 0xC955 }, +{ 0xC956, 0xC956, 0xC956 }, +{ 0xC957, 0xC957, 0xC957 }, +{ 0xC958, 0xC958, 0xC958 }, +{ 0xC959, 0xC959, 0xC959 }, +{ 0xC95A, 0xC95A, 0xC95A }, +{ 0xC95B, 0xC95B, 0xC95B }, +{ 0xC95C, 0xC95C, 0xC95C }, +{ 0xC95D, 0xC95D, 0xC95D }, +{ 0xC95E, 0xC95E, 0xC95E }, +{ 0xC95F, 0xC95F, 0xC95F }, +{ 0xC960, 0xC960, 0xC960 }, +{ 0xC961, 0xC961, 0xC961 }, +{ 0xC962, 0xC962, 0xC962 }, +{ 0xC963, 0xC963, 0xC963 }, +{ 0xC964, 0xC964, 0xC964 }, +{ 0xC965, 0xC965, 0xC965 }, +{ 0xC966, 0xC966, 0xC966 }, +{ 0xC967, 0xC967, 0xC967 }, +{ 0xC968, 0xC968, 0xC968 }, +{ 0xC969, 0xC969, 0xC969 }, +{ 0xC96A, 0xC96A, 0xC96A }, +{ 0xC96B, 0xC96B, 0xC96B }, +{ 0xC96C, 0xC96C, 0xC96C }, +{ 0xC96D, 0xC96D, 0xC96D }, +{ 0xC96E, 0xC96E, 0xC96E }, +{ 0xC96F, 0xC96F, 0xC96F }, +{ 0xC970, 0xC970, 0xC970 }, +{ 0xC971, 0xC971, 0xC971 }, +{ 0xC972, 0xC972, 0xC972 }, +{ 0xC973, 0xC973, 0xC973 }, +{ 0xC974, 0xC974, 0xC974 }, +{ 0xC975, 0xC975, 0xC975 }, +{ 0xC976, 0xC976, 0xC976 }, +{ 0xC977, 0xC977, 0xC977 }, +{ 0xC978, 0xC978, 0xC978 }, +{ 0xC979, 0xC979, 0xC979 }, +{ 0xC97A, 0xC97A, 0xC97A }, +{ 0xC97B, 0xC97B, 0xC97B }, +{ 0xC97C, 0xC97C, 0xC97C }, +{ 0xC97D, 0xC97D, 0xC97D }, +{ 0xC97E, 0xC97E, 0xC97E }, +{ 0xC97F, 0xC97F, 0xC97F }, +{ 0xC980, 0xC980, 0xC980 }, +{ 0xC981, 0xC981, 0xC981 }, +{ 0xC982, 0xC982, 0xC982 }, +{ 0xC983, 0xC983, 0xC983 }, +{ 0xC984, 0xC984, 0xC984 }, +{ 0xC985, 0xC985, 0xC985 }, +{ 0xC986, 0xC986, 0xC986 }, +{ 0xC987, 0xC987, 0xC987 }, +{ 0xC988, 0xC988, 0xC988 }, +{ 0xC989, 0xC989, 0xC989 }, +{ 0xC98A, 0xC98A, 0xC98A }, +{ 0xC98B, 0xC98B, 0xC98B }, +{ 0xC98C, 0xC98C, 0xC98C }, +{ 0xC98D, 0xC98D, 0xC98D }, +{ 0xC98E, 0xC98E, 0xC98E }, +{ 0xC98F, 0xC98F, 0xC98F }, +{ 0xC990, 0xC990, 0xC990 }, +{ 0xC991, 0xC991, 0xC991 }, +{ 0xC992, 0xC992, 0xC992 }, +{ 0xC993, 0xC993, 0xC993 }, +{ 0xC994, 0xC994, 0xC994 }, +{ 0xC995, 0xC995, 0xC995 }, +{ 0xC996, 0xC996, 0xC996 }, +{ 0xC997, 0xC997, 0xC997 }, +{ 0xC998, 0xC998, 0xC998 }, +{ 0xC999, 0xC999, 0xC999 }, +{ 0xC99A, 0xC99A, 0xC99A }, +{ 0xC99B, 0xC99B, 0xC99B }, +{ 0xC99C, 0xC99C, 0xC99C }, +{ 0xC99D, 0xC99D, 0xC99D }, +{ 0xC99E, 0xC99E, 0xC99E }, +{ 0xC99F, 0xC99F, 0xC99F }, +{ 0xC9A0, 0xC9A0, 0xC9A0 }, +{ 0xC9A1, 0xC9A1, 0xC9A1 }, +{ 0xC9A2, 0xC9A2, 0xC9A2 }, +{ 0xC9A3, 0xC9A3, 0xC9A3 }, +{ 0xC9A4, 0xC9A4, 0xC9A4 }, +{ 0xC9A5, 0xC9A5, 0xC9A5 }, +{ 0xC9A6, 0xC9A6, 0xC9A6 }, +{ 0xC9A7, 0xC9A7, 0xC9A7 }, +{ 0xC9A8, 0xC9A8, 0xC9A8 }, +{ 0xC9A9, 0xC9A9, 0xC9A9 }, +{ 0xC9AA, 0xC9AA, 0xC9AA }, +{ 0xC9AB, 0xC9AB, 0xC9AB }, +{ 0xC9AC, 0xC9AC, 0xC9AC }, +{ 0xC9AD, 0xC9AD, 0xC9AD }, +{ 0xC9AE, 0xC9AE, 0xC9AE }, +{ 0xC9AF, 0xC9AF, 0xC9AF }, +{ 0xC9B0, 0xC9B0, 0xC9B0 }, +{ 0xC9B1, 0xC9B1, 0xC9B1 }, +{ 0xC9B2, 0xC9B2, 0xC9B2 }, +{ 0xC9B3, 0xC9B3, 0xC9B3 }, +{ 0xC9B4, 0xC9B4, 0xC9B4 }, +{ 0xC9B5, 0xC9B5, 0xC9B5 }, +{ 0xC9B6, 0xC9B6, 0xC9B6 }, +{ 0xC9B7, 0xC9B7, 0xC9B7 }, +{ 0xC9B8, 0xC9B8, 0xC9B8 }, +{ 0xC9B9, 0xC9B9, 0xC9B9 }, +{ 0xC9BA, 0xC9BA, 0xC9BA }, +{ 0xC9BB, 0xC9BB, 0xC9BB }, +{ 0xC9BC, 0xC9BC, 0xC9BC }, +{ 0xC9BD, 0xC9BD, 0xC9BD }, +{ 0xC9BE, 0xC9BE, 0xC9BE }, +{ 0xC9BF, 0xC9BF, 0xC9BF }, +{ 0xC9C0, 0xC9C0, 0xC9C0 }, +{ 0xC9C1, 0xC9C1, 0xC9C1 }, +{ 0xC9C2, 0xC9C2, 0xC9C2 }, +{ 0xC9C3, 0xC9C3, 0xC9C3 }, +{ 0xC9C4, 0xC9C4, 0xC9C4 }, +{ 0xC9C5, 0xC9C5, 0xC9C5 }, +{ 0xC9C6, 0xC9C6, 0xC9C6 }, +{ 0xC9C7, 0xC9C7, 0xC9C7 }, +{ 0xC9C8, 0xC9C8, 0xC9C8 }, +{ 0xC9C9, 0xC9C9, 0xC9C9 }, +{ 0xC9CA, 0xC9CA, 0xC9CA }, +{ 0xC9CB, 0xC9CB, 0xC9CB }, +{ 0xC9CC, 0xC9CC, 0xC9CC }, +{ 0xC9CD, 0xC9CD, 0xC9CD }, +{ 0xC9CE, 0xC9CE, 0xC9CE }, +{ 0xC9CF, 0xC9CF, 0xC9CF }, +{ 0xC9D0, 0xC9D0, 0xC9D0 }, +{ 0xC9D1, 0xC9D1, 0xC9D1 }, +{ 0xC9D2, 0xC9D2, 0xC9D2 }, +{ 0xC9D3, 0xC9D3, 0xC9D3 }, +{ 0xC9D4, 0xC9D4, 0xC9D4 }, +{ 0xC9D5, 0xC9D5, 0xC9D5 }, +{ 0xC9D6, 0xC9D6, 0xC9D6 }, +{ 0xC9D7, 0xC9D7, 0xC9D7 }, +{ 0xC9D8, 0xC9D8, 0xC9D8 }, +{ 0xC9D9, 0xC9D9, 0xC9D9 }, +{ 0xC9DA, 0xC9DA, 0xC9DA }, +{ 0xC9DB, 0xC9DB, 0xC9DB }, +{ 0xC9DC, 0xC9DC, 0xC9DC }, +{ 0xC9DD, 0xC9DD, 0xC9DD }, +{ 0xC9DE, 0xC9DE, 0xC9DE }, +{ 0xC9DF, 0xC9DF, 0xC9DF }, +{ 0xC9E0, 0xC9E0, 0xC9E0 }, +{ 0xC9E1, 0xC9E1, 0xC9E1 }, +{ 0xC9E2, 0xC9E2, 0xC9E2 }, +{ 0xC9E3, 0xC9E3, 0xC9E3 }, +{ 0xC9E4, 0xC9E4, 0xC9E4 }, +{ 0xC9E5, 0xC9E5, 0xC9E5 }, +{ 0xC9E6, 0xC9E6, 0xC9E6 }, +{ 0xC9E7, 0xC9E7, 0xC9E7 }, +{ 0xC9E8, 0xC9E8, 0xC9E8 }, +{ 0xC9E9, 0xC9E9, 0xC9E9 }, +{ 0xC9EA, 0xC9EA, 0xC9EA }, +{ 0xC9EB, 0xC9EB, 0xC9EB }, +{ 0xC9EC, 0xC9EC, 0xC9EC }, +{ 0xC9ED, 0xC9ED, 0xC9ED }, +{ 0xC9EE, 0xC9EE, 0xC9EE }, +{ 0xC9EF, 0xC9EF, 0xC9EF }, +{ 0xC9F0, 0xC9F0, 0xC9F0 }, +{ 0xC9F1, 0xC9F1, 0xC9F1 }, +{ 0xC9F2, 0xC9F2, 0xC9F2 }, +{ 0xC9F3, 0xC9F3, 0xC9F3 }, +{ 0xC9F4, 0xC9F4, 0xC9F4 }, +{ 0xC9F5, 0xC9F5, 0xC9F5 }, +{ 0xC9F6, 0xC9F6, 0xC9F6 }, +{ 0xC9F7, 0xC9F7, 0xC9F7 }, +{ 0xC9F8, 0xC9F8, 0xC9F8 }, +{ 0xC9F9, 0xC9F9, 0xC9F9 }, +{ 0xC9FA, 0xC9FA, 0xC9FA }, +{ 0xC9FB, 0xC9FB, 0xC9FB }, +{ 0xC9FC, 0xC9FC, 0xC9FC }, +{ 0xC9FD, 0xC9FD, 0xC9FD }, +{ 0xC9FE, 0xC9FE, 0xC9FE }, +{ 0xC9FF, 0xC9FF, 0xC9FF }, +{ 0xCA00, 0xCA00, 0xCA00 }, +{ 0xCA01, 0xCA01, 0xCA01 }, +{ 0xCA02, 0xCA02, 0xCA02 }, +{ 0xCA03, 0xCA03, 0xCA03 }, +{ 0xCA04, 0xCA04, 0xCA04 }, +{ 0xCA05, 0xCA05, 0xCA05 }, +{ 0xCA06, 0xCA06, 0xCA06 }, +{ 0xCA07, 0xCA07, 0xCA07 }, +{ 0xCA08, 0xCA08, 0xCA08 }, +{ 0xCA09, 0xCA09, 0xCA09 }, +{ 0xCA0A, 0xCA0A, 0xCA0A }, +{ 0xCA0B, 0xCA0B, 0xCA0B }, +{ 0xCA0C, 0xCA0C, 0xCA0C }, +{ 0xCA0D, 0xCA0D, 0xCA0D }, +{ 0xCA0E, 0xCA0E, 0xCA0E }, +{ 0xCA0F, 0xCA0F, 0xCA0F }, +{ 0xCA10, 0xCA10, 0xCA10 }, +{ 0xCA11, 0xCA11, 0xCA11 }, +{ 0xCA12, 0xCA12, 0xCA12 }, +{ 0xCA13, 0xCA13, 0xCA13 }, +{ 0xCA14, 0xCA14, 0xCA14 }, +{ 0xCA15, 0xCA15, 0xCA15 }, +{ 0xCA16, 0xCA16, 0xCA16 }, +{ 0xCA17, 0xCA17, 0xCA17 }, +{ 0xCA18, 0xCA18, 0xCA18 }, +{ 0xCA19, 0xCA19, 0xCA19 }, +{ 0xCA1A, 0xCA1A, 0xCA1A }, +{ 0xCA1B, 0xCA1B, 0xCA1B }, +{ 0xCA1C, 0xCA1C, 0xCA1C }, +{ 0xCA1D, 0xCA1D, 0xCA1D }, +{ 0xCA1E, 0xCA1E, 0xCA1E }, +{ 0xCA1F, 0xCA1F, 0xCA1F }, +{ 0xCA20, 0xCA20, 0xCA20 }, +{ 0xCA21, 0xCA21, 0xCA21 }, +{ 0xCA22, 0xCA22, 0xCA22 }, +{ 0xCA23, 0xCA23, 0xCA23 }, +{ 0xCA24, 0xCA24, 0xCA24 }, +{ 0xCA25, 0xCA25, 0xCA25 }, +{ 0xCA26, 0xCA26, 0xCA26 }, +{ 0xCA27, 0xCA27, 0xCA27 }, +{ 0xCA28, 0xCA28, 0xCA28 }, +{ 0xCA29, 0xCA29, 0xCA29 }, +{ 0xCA2A, 0xCA2A, 0xCA2A }, +{ 0xCA2B, 0xCA2B, 0xCA2B }, +{ 0xCA2C, 0xCA2C, 0xCA2C }, +{ 0xCA2D, 0xCA2D, 0xCA2D }, +{ 0xCA2E, 0xCA2E, 0xCA2E }, +{ 0xCA2F, 0xCA2F, 0xCA2F }, +{ 0xCA30, 0xCA30, 0xCA30 }, +{ 0xCA31, 0xCA31, 0xCA31 }, +{ 0xCA32, 0xCA32, 0xCA32 }, +{ 0xCA33, 0xCA33, 0xCA33 }, +{ 0xCA34, 0xCA34, 0xCA34 }, +{ 0xCA35, 0xCA35, 0xCA35 }, +{ 0xCA36, 0xCA36, 0xCA36 }, +{ 0xCA37, 0xCA37, 0xCA37 }, +{ 0xCA38, 0xCA38, 0xCA38 }, +{ 0xCA39, 0xCA39, 0xCA39 }, +{ 0xCA3A, 0xCA3A, 0xCA3A }, +{ 0xCA3B, 0xCA3B, 0xCA3B }, +{ 0xCA3C, 0xCA3C, 0xCA3C }, +{ 0xCA3D, 0xCA3D, 0xCA3D }, +{ 0xCA3E, 0xCA3E, 0xCA3E }, +{ 0xCA3F, 0xCA3F, 0xCA3F }, +{ 0xCA40, 0xCA40, 0xCA40 }, +{ 0xCA41, 0xCA41, 0xCA41 }, +{ 0xCA42, 0xCA42, 0xCA42 }, +{ 0xCA43, 0xCA43, 0xCA43 }, +{ 0xCA44, 0xCA44, 0xCA44 }, +{ 0xCA45, 0xCA45, 0xCA45 }, +{ 0xCA46, 0xCA46, 0xCA46 }, +{ 0xCA47, 0xCA47, 0xCA47 }, +{ 0xCA48, 0xCA48, 0xCA48 }, +{ 0xCA49, 0xCA49, 0xCA49 }, +{ 0xCA4A, 0xCA4A, 0xCA4A }, +{ 0xCA4B, 0xCA4B, 0xCA4B }, +{ 0xCA4C, 0xCA4C, 0xCA4C }, +{ 0xCA4D, 0xCA4D, 0xCA4D }, +{ 0xCA4E, 0xCA4E, 0xCA4E }, +{ 0xCA4F, 0xCA4F, 0xCA4F }, +{ 0xCA50, 0xCA50, 0xCA50 }, +{ 0xCA51, 0xCA51, 0xCA51 }, +{ 0xCA52, 0xCA52, 0xCA52 }, +{ 0xCA53, 0xCA53, 0xCA53 }, +{ 0xCA54, 0xCA54, 0xCA54 }, +{ 0xCA55, 0xCA55, 0xCA55 }, +{ 0xCA56, 0xCA56, 0xCA56 }, +{ 0xCA57, 0xCA57, 0xCA57 }, +{ 0xCA58, 0xCA58, 0xCA58 }, +{ 0xCA59, 0xCA59, 0xCA59 }, +{ 0xCA5A, 0xCA5A, 0xCA5A }, +{ 0xCA5B, 0xCA5B, 0xCA5B }, +{ 0xCA5C, 0xCA5C, 0xCA5C }, +{ 0xCA5D, 0xCA5D, 0xCA5D }, +{ 0xCA5E, 0xCA5E, 0xCA5E }, +{ 0xCA5F, 0xCA5F, 0xCA5F }, +{ 0xCA60, 0xCA60, 0xCA60 }, +{ 0xCA61, 0xCA61, 0xCA61 }, +{ 0xCA62, 0xCA62, 0xCA62 }, +{ 0xCA63, 0xCA63, 0xCA63 }, +{ 0xCA64, 0xCA64, 0xCA64 }, +{ 0xCA65, 0xCA65, 0xCA65 }, +{ 0xCA66, 0xCA66, 0xCA66 }, +{ 0xCA67, 0xCA67, 0xCA67 }, +{ 0xCA68, 0xCA68, 0xCA68 }, +{ 0xCA69, 0xCA69, 0xCA69 }, +{ 0xCA6A, 0xCA6A, 0xCA6A }, +{ 0xCA6B, 0xCA6B, 0xCA6B }, +{ 0xCA6C, 0xCA6C, 0xCA6C }, +{ 0xCA6D, 0xCA6D, 0xCA6D }, +{ 0xCA6E, 0xCA6E, 0xCA6E }, +{ 0xCA6F, 0xCA6F, 0xCA6F }, +{ 0xCA70, 0xCA70, 0xCA70 }, +{ 0xCA71, 0xCA71, 0xCA71 }, +{ 0xCA72, 0xCA72, 0xCA72 }, +{ 0xCA73, 0xCA73, 0xCA73 }, +{ 0xCA74, 0xCA74, 0xCA74 }, +{ 0xCA75, 0xCA75, 0xCA75 }, +{ 0xCA76, 0xCA76, 0xCA76 }, +{ 0xCA77, 0xCA77, 0xCA77 }, +{ 0xCA78, 0xCA78, 0xCA78 }, +{ 0xCA79, 0xCA79, 0xCA79 }, +{ 0xCA7A, 0xCA7A, 0xCA7A }, +{ 0xCA7B, 0xCA7B, 0xCA7B }, +{ 0xCA7C, 0xCA7C, 0xCA7C }, +{ 0xCA7D, 0xCA7D, 0xCA7D }, +{ 0xCA7E, 0xCA7E, 0xCA7E }, +{ 0xCA7F, 0xCA7F, 0xCA7F }, +{ 0xCA80, 0xCA80, 0xCA80 }, +{ 0xCA81, 0xCA81, 0xCA81 }, +{ 0xCA82, 0xCA82, 0xCA82 }, +{ 0xCA83, 0xCA83, 0xCA83 }, +{ 0xCA84, 0xCA84, 0xCA84 }, +{ 0xCA85, 0xCA85, 0xCA85 }, +{ 0xCA86, 0xCA86, 0xCA86 }, +{ 0xCA87, 0xCA87, 0xCA87 }, +{ 0xCA88, 0xCA88, 0xCA88 }, +{ 0xCA89, 0xCA89, 0xCA89 }, +{ 0xCA8A, 0xCA8A, 0xCA8A }, +{ 0xCA8B, 0xCA8B, 0xCA8B }, +{ 0xCA8C, 0xCA8C, 0xCA8C }, +{ 0xCA8D, 0xCA8D, 0xCA8D }, +{ 0xCA8E, 0xCA8E, 0xCA8E }, +{ 0xCA8F, 0xCA8F, 0xCA8F }, +{ 0xCA90, 0xCA90, 0xCA90 }, +{ 0xCA91, 0xCA91, 0xCA91 }, +{ 0xCA92, 0xCA92, 0xCA92 }, +{ 0xCA93, 0xCA93, 0xCA93 }, +{ 0xCA94, 0xCA94, 0xCA94 }, +{ 0xCA95, 0xCA95, 0xCA95 }, +{ 0xCA96, 0xCA96, 0xCA96 }, +{ 0xCA97, 0xCA97, 0xCA97 }, +{ 0xCA98, 0xCA98, 0xCA98 }, +{ 0xCA99, 0xCA99, 0xCA99 }, +{ 0xCA9A, 0xCA9A, 0xCA9A }, +{ 0xCA9B, 0xCA9B, 0xCA9B }, +{ 0xCA9C, 0xCA9C, 0xCA9C }, +{ 0xCA9D, 0xCA9D, 0xCA9D }, +{ 0xCA9E, 0xCA9E, 0xCA9E }, +{ 0xCA9F, 0xCA9F, 0xCA9F }, +{ 0xCAA0, 0xCAA0, 0xCAA0 }, +{ 0xCAA1, 0xCAA1, 0xCAA1 }, +{ 0xCAA2, 0xCAA2, 0xCAA2 }, +{ 0xCAA3, 0xCAA3, 0xCAA3 }, +{ 0xCAA4, 0xCAA4, 0xCAA4 }, +{ 0xCAA5, 0xCAA5, 0xCAA5 }, +{ 0xCAA6, 0xCAA6, 0xCAA6 }, +{ 0xCAA7, 0xCAA7, 0xCAA7 }, +{ 0xCAA8, 0xCAA8, 0xCAA8 }, +{ 0xCAA9, 0xCAA9, 0xCAA9 }, +{ 0xCAAA, 0xCAAA, 0xCAAA }, +{ 0xCAAB, 0xCAAB, 0xCAAB }, +{ 0xCAAC, 0xCAAC, 0xCAAC }, +{ 0xCAAD, 0xCAAD, 0xCAAD }, +{ 0xCAAE, 0xCAAE, 0xCAAE }, +{ 0xCAAF, 0xCAAF, 0xCAAF }, +{ 0xCAB0, 0xCAB0, 0xCAB0 }, +{ 0xCAB1, 0xCAB1, 0xCAB1 }, +{ 0xCAB2, 0xCAB2, 0xCAB2 }, +{ 0xCAB3, 0xCAB3, 0xCAB3 }, +{ 0xCAB4, 0xCAB4, 0xCAB4 }, +{ 0xCAB5, 0xCAB5, 0xCAB5 }, +{ 0xCAB6, 0xCAB6, 0xCAB6 }, +{ 0xCAB7, 0xCAB7, 0xCAB7 }, +{ 0xCAB8, 0xCAB8, 0xCAB8 }, +{ 0xCAB9, 0xCAB9, 0xCAB9 }, +{ 0xCABA, 0xCABA, 0xCABA }, +{ 0xCABB, 0xCABB, 0xCABB }, +{ 0xCABC, 0xCABC, 0xCABC }, +{ 0xCABD, 0xCABD, 0xCABD }, +{ 0xCABE, 0xCABE, 0xCABE }, +{ 0xCABF, 0xCABF, 0xCABF }, +{ 0xCAC0, 0xCAC0, 0xCAC0 }, +{ 0xCAC1, 0xCAC1, 0xCAC1 }, +{ 0xCAC2, 0xCAC2, 0xCAC2 }, +{ 0xCAC3, 0xCAC3, 0xCAC3 }, +{ 0xCAC4, 0xCAC4, 0xCAC4 }, +{ 0xCAC5, 0xCAC5, 0xCAC5 }, +{ 0xCAC6, 0xCAC6, 0xCAC6 }, +{ 0xCAC7, 0xCAC7, 0xCAC7 }, +{ 0xCAC8, 0xCAC8, 0xCAC8 }, +{ 0xCAC9, 0xCAC9, 0xCAC9 }, +{ 0xCACA, 0xCACA, 0xCACA }, +{ 0xCACB, 0xCACB, 0xCACB }, +{ 0xCACC, 0xCACC, 0xCACC }, +{ 0xCACD, 0xCACD, 0xCACD }, +{ 0xCACE, 0xCACE, 0xCACE }, +{ 0xCACF, 0xCACF, 0xCACF }, +{ 0xCAD0, 0xCAD0, 0xCAD0 }, +{ 0xCAD1, 0xCAD1, 0xCAD1 }, +{ 0xCAD2, 0xCAD2, 0xCAD2 }, +{ 0xCAD3, 0xCAD3, 0xCAD3 }, +{ 0xCAD4, 0xCAD4, 0xCAD4 }, +{ 0xCAD5, 0xCAD5, 0xCAD5 }, +{ 0xCAD6, 0xCAD6, 0xCAD6 }, +{ 0xCAD7, 0xCAD7, 0xCAD7 }, +{ 0xCAD8, 0xCAD8, 0xCAD8 }, +{ 0xCAD9, 0xCAD9, 0xCAD9 }, +{ 0xCADA, 0xCADA, 0xCADA }, +{ 0xCADB, 0xCADB, 0xCADB }, +{ 0xCADC, 0xCADC, 0xCADC }, +{ 0xCADD, 0xCADD, 0xCADD }, +{ 0xCADE, 0xCADE, 0xCADE }, +{ 0xCADF, 0xCADF, 0xCADF }, +{ 0xCAE0, 0xCAE0, 0xCAE0 }, +{ 0xCAE1, 0xCAE1, 0xCAE1 }, +{ 0xCAE2, 0xCAE2, 0xCAE2 }, +{ 0xCAE3, 0xCAE3, 0xCAE3 }, +{ 0xCAE4, 0xCAE4, 0xCAE4 }, +{ 0xCAE5, 0xCAE5, 0xCAE5 }, +{ 0xCAE6, 0xCAE6, 0xCAE6 }, +{ 0xCAE7, 0xCAE7, 0xCAE7 }, +{ 0xCAE8, 0xCAE8, 0xCAE8 }, +{ 0xCAE9, 0xCAE9, 0xCAE9 }, +{ 0xCAEA, 0xCAEA, 0xCAEA }, +{ 0xCAEB, 0xCAEB, 0xCAEB }, +{ 0xCAEC, 0xCAEC, 0xCAEC }, +{ 0xCAED, 0xCAED, 0xCAED }, +{ 0xCAEE, 0xCAEE, 0xCAEE }, +{ 0xCAEF, 0xCAEF, 0xCAEF }, +{ 0xCAF0, 0xCAF0, 0xCAF0 }, +{ 0xCAF1, 0xCAF1, 0xCAF1 }, +{ 0xCAF2, 0xCAF2, 0xCAF2 }, +{ 0xCAF3, 0xCAF3, 0xCAF3 }, +{ 0xCAF4, 0xCAF4, 0xCAF4 }, +{ 0xCAF5, 0xCAF5, 0xCAF5 }, +{ 0xCAF6, 0xCAF6, 0xCAF6 }, +{ 0xCAF7, 0xCAF7, 0xCAF7 }, +{ 0xCAF8, 0xCAF8, 0xCAF8 }, +{ 0xCAF9, 0xCAF9, 0xCAF9 }, +{ 0xCAFA, 0xCAFA, 0xCAFA }, +{ 0xCAFB, 0xCAFB, 0xCAFB }, +{ 0xCAFC, 0xCAFC, 0xCAFC }, +{ 0xCAFD, 0xCAFD, 0xCAFD }, +{ 0xCAFE, 0xCAFE, 0xCAFE }, +{ 0xCAFF, 0xCAFF, 0xCAFF }, +{ 0xCB00, 0xCB00, 0xCB00 }, +{ 0xCB01, 0xCB01, 0xCB01 }, +{ 0xCB02, 0xCB02, 0xCB02 }, +{ 0xCB03, 0xCB03, 0xCB03 }, +{ 0xCB04, 0xCB04, 0xCB04 }, +{ 0xCB05, 0xCB05, 0xCB05 }, +{ 0xCB06, 0xCB06, 0xCB06 }, +{ 0xCB07, 0xCB07, 0xCB07 }, +{ 0xCB08, 0xCB08, 0xCB08 }, +{ 0xCB09, 0xCB09, 0xCB09 }, +{ 0xCB0A, 0xCB0A, 0xCB0A }, +{ 0xCB0B, 0xCB0B, 0xCB0B }, +{ 0xCB0C, 0xCB0C, 0xCB0C }, +{ 0xCB0D, 0xCB0D, 0xCB0D }, +{ 0xCB0E, 0xCB0E, 0xCB0E }, +{ 0xCB0F, 0xCB0F, 0xCB0F }, +{ 0xCB10, 0xCB10, 0xCB10 }, +{ 0xCB11, 0xCB11, 0xCB11 }, +{ 0xCB12, 0xCB12, 0xCB12 }, +{ 0xCB13, 0xCB13, 0xCB13 }, +{ 0xCB14, 0xCB14, 0xCB14 }, +{ 0xCB15, 0xCB15, 0xCB15 }, +{ 0xCB16, 0xCB16, 0xCB16 }, +{ 0xCB17, 0xCB17, 0xCB17 }, +{ 0xCB18, 0xCB18, 0xCB18 }, +{ 0xCB19, 0xCB19, 0xCB19 }, +{ 0xCB1A, 0xCB1A, 0xCB1A }, +{ 0xCB1B, 0xCB1B, 0xCB1B }, +{ 0xCB1C, 0xCB1C, 0xCB1C }, +{ 0xCB1D, 0xCB1D, 0xCB1D }, +{ 0xCB1E, 0xCB1E, 0xCB1E }, +{ 0xCB1F, 0xCB1F, 0xCB1F }, +{ 0xCB20, 0xCB20, 0xCB20 }, +{ 0xCB21, 0xCB21, 0xCB21 }, +{ 0xCB22, 0xCB22, 0xCB22 }, +{ 0xCB23, 0xCB23, 0xCB23 }, +{ 0xCB24, 0xCB24, 0xCB24 }, +{ 0xCB25, 0xCB25, 0xCB25 }, +{ 0xCB26, 0xCB26, 0xCB26 }, +{ 0xCB27, 0xCB27, 0xCB27 }, +{ 0xCB28, 0xCB28, 0xCB28 }, +{ 0xCB29, 0xCB29, 0xCB29 }, +{ 0xCB2A, 0xCB2A, 0xCB2A }, +{ 0xCB2B, 0xCB2B, 0xCB2B }, +{ 0xCB2C, 0xCB2C, 0xCB2C }, +{ 0xCB2D, 0xCB2D, 0xCB2D }, +{ 0xCB2E, 0xCB2E, 0xCB2E }, +{ 0xCB2F, 0xCB2F, 0xCB2F }, +{ 0xCB30, 0xCB30, 0xCB30 }, +{ 0xCB31, 0xCB31, 0xCB31 }, +{ 0xCB32, 0xCB32, 0xCB32 }, +{ 0xCB33, 0xCB33, 0xCB33 }, +{ 0xCB34, 0xCB34, 0xCB34 }, +{ 0xCB35, 0xCB35, 0xCB35 }, +{ 0xCB36, 0xCB36, 0xCB36 }, +{ 0xCB37, 0xCB37, 0xCB37 }, +{ 0xCB38, 0xCB38, 0xCB38 }, +{ 0xCB39, 0xCB39, 0xCB39 }, +{ 0xCB3A, 0xCB3A, 0xCB3A }, +{ 0xCB3B, 0xCB3B, 0xCB3B }, +{ 0xCB3C, 0xCB3C, 0xCB3C }, +{ 0xCB3D, 0xCB3D, 0xCB3D }, +{ 0xCB3E, 0xCB3E, 0xCB3E }, +{ 0xCB3F, 0xCB3F, 0xCB3F }, +{ 0xCB40, 0xCB40, 0xCB40 }, +{ 0xCB41, 0xCB41, 0xCB41 }, +{ 0xCB42, 0xCB42, 0xCB42 }, +{ 0xCB43, 0xCB43, 0xCB43 }, +{ 0xCB44, 0xCB44, 0xCB44 }, +{ 0xCB45, 0xCB45, 0xCB45 }, +{ 0xCB46, 0xCB46, 0xCB46 }, +{ 0xCB47, 0xCB47, 0xCB47 }, +{ 0xCB48, 0xCB48, 0xCB48 }, +{ 0xCB49, 0xCB49, 0xCB49 }, +{ 0xCB4A, 0xCB4A, 0xCB4A }, +{ 0xCB4B, 0xCB4B, 0xCB4B }, +{ 0xCB4C, 0xCB4C, 0xCB4C }, +{ 0xCB4D, 0xCB4D, 0xCB4D }, +{ 0xCB4E, 0xCB4E, 0xCB4E }, +{ 0xCB4F, 0xCB4F, 0xCB4F }, +{ 0xCB50, 0xCB50, 0xCB50 }, +{ 0xCB51, 0xCB51, 0xCB51 }, +{ 0xCB52, 0xCB52, 0xCB52 }, +{ 0xCB53, 0xCB53, 0xCB53 }, +{ 0xCB54, 0xCB54, 0xCB54 }, +{ 0xCB55, 0xCB55, 0xCB55 }, +{ 0xCB56, 0xCB56, 0xCB56 }, +{ 0xCB57, 0xCB57, 0xCB57 }, +{ 0xCB58, 0xCB58, 0xCB58 }, +{ 0xCB59, 0xCB59, 0xCB59 }, +{ 0xCB5A, 0xCB5A, 0xCB5A }, +{ 0xCB5B, 0xCB5B, 0xCB5B }, +{ 0xCB5C, 0xCB5C, 0xCB5C }, +{ 0xCB5D, 0xCB5D, 0xCB5D }, +{ 0xCB5E, 0xCB5E, 0xCB5E }, +{ 0xCB5F, 0xCB5F, 0xCB5F }, +{ 0xCB60, 0xCB60, 0xCB60 }, +{ 0xCB61, 0xCB61, 0xCB61 }, +{ 0xCB62, 0xCB62, 0xCB62 }, +{ 0xCB63, 0xCB63, 0xCB63 }, +{ 0xCB64, 0xCB64, 0xCB64 }, +{ 0xCB65, 0xCB65, 0xCB65 }, +{ 0xCB66, 0xCB66, 0xCB66 }, +{ 0xCB67, 0xCB67, 0xCB67 }, +{ 0xCB68, 0xCB68, 0xCB68 }, +{ 0xCB69, 0xCB69, 0xCB69 }, +{ 0xCB6A, 0xCB6A, 0xCB6A }, +{ 0xCB6B, 0xCB6B, 0xCB6B }, +{ 0xCB6C, 0xCB6C, 0xCB6C }, +{ 0xCB6D, 0xCB6D, 0xCB6D }, +{ 0xCB6E, 0xCB6E, 0xCB6E }, +{ 0xCB6F, 0xCB6F, 0xCB6F }, +{ 0xCB70, 0xCB70, 0xCB70 }, +{ 0xCB71, 0xCB71, 0xCB71 }, +{ 0xCB72, 0xCB72, 0xCB72 }, +{ 0xCB73, 0xCB73, 0xCB73 }, +{ 0xCB74, 0xCB74, 0xCB74 }, +{ 0xCB75, 0xCB75, 0xCB75 }, +{ 0xCB76, 0xCB76, 0xCB76 }, +{ 0xCB77, 0xCB77, 0xCB77 }, +{ 0xCB78, 0xCB78, 0xCB78 }, +{ 0xCB79, 0xCB79, 0xCB79 }, +{ 0xCB7A, 0xCB7A, 0xCB7A }, +{ 0xCB7B, 0xCB7B, 0xCB7B }, +{ 0xCB7C, 0xCB7C, 0xCB7C }, +{ 0xCB7D, 0xCB7D, 0xCB7D }, +{ 0xCB7E, 0xCB7E, 0xCB7E }, +{ 0xCB7F, 0xCB7F, 0xCB7F }, +{ 0xCB80, 0xCB80, 0xCB80 }, +{ 0xCB81, 0xCB81, 0xCB81 }, +{ 0xCB82, 0xCB82, 0xCB82 }, +{ 0xCB83, 0xCB83, 0xCB83 }, +{ 0xCB84, 0xCB84, 0xCB84 }, +{ 0xCB85, 0xCB85, 0xCB85 }, +{ 0xCB86, 0xCB86, 0xCB86 }, +{ 0xCB87, 0xCB87, 0xCB87 }, +{ 0xCB88, 0xCB88, 0xCB88 }, +{ 0xCB89, 0xCB89, 0xCB89 }, +{ 0xCB8A, 0xCB8A, 0xCB8A }, +{ 0xCB8B, 0xCB8B, 0xCB8B }, +{ 0xCB8C, 0xCB8C, 0xCB8C }, +{ 0xCB8D, 0xCB8D, 0xCB8D }, +{ 0xCB8E, 0xCB8E, 0xCB8E }, +{ 0xCB8F, 0xCB8F, 0xCB8F }, +{ 0xCB90, 0xCB90, 0xCB90 }, +{ 0xCB91, 0xCB91, 0xCB91 }, +{ 0xCB92, 0xCB92, 0xCB92 }, +{ 0xCB93, 0xCB93, 0xCB93 }, +{ 0xCB94, 0xCB94, 0xCB94 }, +{ 0xCB95, 0xCB95, 0xCB95 }, +{ 0xCB96, 0xCB96, 0xCB96 }, +{ 0xCB97, 0xCB97, 0xCB97 }, +{ 0xCB98, 0xCB98, 0xCB98 }, +{ 0xCB99, 0xCB99, 0xCB99 }, +{ 0xCB9A, 0xCB9A, 0xCB9A }, +{ 0xCB9B, 0xCB9B, 0xCB9B }, +{ 0xCB9C, 0xCB9C, 0xCB9C }, +{ 0xCB9D, 0xCB9D, 0xCB9D }, +{ 0xCB9E, 0xCB9E, 0xCB9E }, +{ 0xCB9F, 0xCB9F, 0xCB9F }, +{ 0xCBA0, 0xCBA0, 0xCBA0 }, +{ 0xCBA1, 0xCBA1, 0xCBA1 }, +{ 0xCBA2, 0xCBA2, 0xCBA2 }, +{ 0xCBA3, 0xCBA3, 0xCBA3 }, +{ 0xCBA4, 0xCBA4, 0xCBA4 }, +{ 0xCBA5, 0xCBA5, 0xCBA5 }, +{ 0xCBA6, 0xCBA6, 0xCBA6 }, +{ 0xCBA7, 0xCBA7, 0xCBA7 }, +{ 0xCBA8, 0xCBA8, 0xCBA8 }, +{ 0xCBA9, 0xCBA9, 0xCBA9 }, +{ 0xCBAA, 0xCBAA, 0xCBAA }, +{ 0xCBAB, 0xCBAB, 0xCBAB }, +{ 0xCBAC, 0xCBAC, 0xCBAC }, +{ 0xCBAD, 0xCBAD, 0xCBAD }, +{ 0xCBAE, 0xCBAE, 0xCBAE }, +{ 0xCBAF, 0xCBAF, 0xCBAF }, +{ 0xCBB0, 0xCBB0, 0xCBB0 }, +{ 0xCBB1, 0xCBB1, 0xCBB1 }, +{ 0xCBB2, 0xCBB2, 0xCBB2 }, +{ 0xCBB3, 0xCBB3, 0xCBB3 }, +{ 0xCBB4, 0xCBB4, 0xCBB4 }, +{ 0xCBB5, 0xCBB5, 0xCBB5 }, +{ 0xCBB6, 0xCBB6, 0xCBB6 }, +{ 0xCBB7, 0xCBB7, 0xCBB7 }, +{ 0xCBB8, 0xCBB8, 0xCBB8 }, +{ 0xCBB9, 0xCBB9, 0xCBB9 }, +{ 0xCBBA, 0xCBBA, 0xCBBA }, +{ 0xCBBB, 0xCBBB, 0xCBBB }, +{ 0xCBBC, 0xCBBC, 0xCBBC }, +{ 0xCBBD, 0xCBBD, 0xCBBD }, +{ 0xCBBE, 0xCBBE, 0xCBBE }, +{ 0xCBBF, 0xCBBF, 0xCBBF }, +{ 0xCBC0, 0xCBC0, 0xCBC0 }, +{ 0xCBC1, 0xCBC1, 0xCBC1 }, +{ 0xCBC2, 0xCBC2, 0xCBC2 }, +{ 0xCBC3, 0xCBC3, 0xCBC3 }, +{ 0xCBC4, 0xCBC4, 0xCBC4 }, +{ 0xCBC5, 0xCBC5, 0xCBC5 }, +{ 0xCBC6, 0xCBC6, 0xCBC6 }, +{ 0xCBC7, 0xCBC7, 0xCBC7 }, +{ 0xCBC8, 0xCBC8, 0xCBC8 }, +{ 0xCBC9, 0xCBC9, 0xCBC9 }, +{ 0xCBCA, 0xCBCA, 0xCBCA }, +{ 0xCBCB, 0xCBCB, 0xCBCB }, +{ 0xCBCC, 0xCBCC, 0xCBCC }, +{ 0xCBCD, 0xCBCD, 0xCBCD }, +{ 0xCBCE, 0xCBCE, 0xCBCE }, +{ 0xCBCF, 0xCBCF, 0xCBCF }, +{ 0xCBD0, 0xCBD0, 0xCBD0 }, +{ 0xCBD1, 0xCBD1, 0xCBD1 }, +{ 0xCBD2, 0xCBD2, 0xCBD2 }, +{ 0xCBD3, 0xCBD3, 0xCBD3 }, +{ 0xCBD4, 0xCBD4, 0xCBD4 }, +{ 0xCBD5, 0xCBD5, 0xCBD5 }, +{ 0xCBD6, 0xCBD6, 0xCBD6 }, +{ 0xCBD7, 0xCBD7, 0xCBD7 }, +{ 0xCBD8, 0xCBD8, 0xCBD8 }, +{ 0xCBD9, 0xCBD9, 0xCBD9 }, +{ 0xCBDA, 0xCBDA, 0xCBDA }, +{ 0xCBDB, 0xCBDB, 0xCBDB }, +{ 0xCBDC, 0xCBDC, 0xCBDC }, +{ 0xCBDD, 0xCBDD, 0xCBDD }, +{ 0xCBDE, 0xCBDE, 0xCBDE }, +{ 0xCBDF, 0xCBDF, 0xCBDF }, +{ 0xCBE0, 0xCBE0, 0xCBE0 }, +{ 0xCBE1, 0xCBE1, 0xCBE1 }, +{ 0xCBE2, 0xCBE2, 0xCBE2 }, +{ 0xCBE3, 0xCBE3, 0xCBE3 }, +{ 0xCBE4, 0xCBE4, 0xCBE4 }, +{ 0xCBE5, 0xCBE5, 0xCBE5 }, +{ 0xCBE6, 0xCBE6, 0xCBE6 }, +{ 0xCBE7, 0xCBE7, 0xCBE7 }, +{ 0xCBE8, 0xCBE8, 0xCBE8 }, +{ 0xCBE9, 0xCBE9, 0xCBE9 }, +{ 0xCBEA, 0xCBEA, 0xCBEA }, +{ 0xCBEB, 0xCBEB, 0xCBEB }, +{ 0xCBEC, 0xCBEC, 0xCBEC }, +{ 0xCBED, 0xCBED, 0xCBED }, +{ 0xCBEE, 0xCBEE, 0xCBEE }, +{ 0xCBEF, 0xCBEF, 0xCBEF }, +{ 0xCBF0, 0xCBF0, 0xCBF0 }, +{ 0xCBF1, 0xCBF1, 0xCBF1 }, +{ 0xCBF2, 0xCBF2, 0xCBF2 }, +{ 0xCBF3, 0xCBF3, 0xCBF3 }, +{ 0xCBF4, 0xCBF4, 0xCBF4 }, +{ 0xCBF5, 0xCBF5, 0xCBF5 }, +{ 0xCBF6, 0xCBF6, 0xCBF6 }, +{ 0xCBF7, 0xCBF7, 0xCBF7 }, +{ 0xCBF8, 0xCBF8, 0xCBF8 }, +{ 0xCBF9, 0xCBF9, 0xCBF9 }, +{ 0xCBFA, 0xCBFA, 0xCBFA }, +{ 0xCBFB, 0xCBFB, 0xCBFB }, +{ 0xCBFC, 0xCBFC, 0xCBFC }, +{ 0xCBFD, 0xCBFD, 0xCBFD }, +{ 0xCBFE, 0xCBFE, 0xCBFE }, +{ 0xCBFF, 0xCBFF, 0xCBFF }, +{ 0xCC00, 0xCC00, 0xCC00 }, +{ 0xCC01, 0xCC01, 0xCC01 }, +{ 0xCC02, 0xCC02, 0xCC02 }, +{ 0xCC03, 0xCC03, 0xCC03 }, +{ 0xCC04, 0xCC04, 0xCC04 }, +{ 0xCC05, 0xCC05, 0xCC05 }, +{ 0xCC06, 0xCC06, 0xCC06 }, +{ 0xCC07, 0xCC07, 0xCC07 }, +{ 0xCC08, 0xCC08, 0xCC08 }, +{ 0xCC09, 0xCC09, 0xCC09 }, +{ 0xCC0A, 0xCC0A, 0xCC0A }, +{ 0xCC0B, 0xCC0B, 0xCC0B }, +{ 0xCC0C, 0xCC0C, 0xCC0C }, +{ 0xCC0D, 0xCC0D, 0xCC0D }, +{ 0xCC0E, 0xCC0E, 0xCC0E }, +{ 0xCC0F, 0xCC0F, 0xCC0F }, +{ 0xCC10, 0xCC10, 0xCC10 }, +{ 0xCC11, 0xCC11, 0xCC11 }, +{ 0xCC12, 0xCC12, 0xCC12 }, +{ 0xCC13, 0xCC13, 0xCC13 }, +{ 0xCC14, 0xCC14, 0xCC14 }, +{ 0xCC15, 0xCC15, 0xCC15 }, +{ 0xCC16, 0xCC16, 0xCC16 }, +{ 0xCC17, 0xCC17, 0xCC17 }, +{ 0xCC18, 0xCC18, 0xCC18 }, +{ 0xCC19, 0xCC19, 0xCC19 }, +{ 0xCC1A, 0xCC1A, 0xCC1A }, +{ 0xCC1B, 0xCC1B, 0xCC1B }, +{ 0xCC1C, 0xCC1C, 0xCC1C }, +{ 0xCC1D, 0xCC1D, 0xCC1D }, +{ 0xCC1E, 0xCC1E, 0xCC1E }, +{ 0xCC1F, 0xCC1F, 0xCC1F }, +{ 0xCC20, 0xCC20, 0xCC20 }, +{ 0xCC21, 0xCC21, 0xCC21 }, +{ 0xCC22, 0xCC22, 0xCC22 }, +{ 0xCC23, 0xCC23, 0xCC23 }, +{ 0xCC24, 0xCC24, 0xCC24 }, +{ 0xCC25, 0xCC25, 0xCC25 }, +{ 0xCC26, 0xCC26, 0xCC26 }, +{ 0xCC27, 0xCC27, 0xCC27 }, +{ 0xCC28, 0xCC28, 0xCC28 }, +{ 0xCC29, 0xCC29, 0xCC29 }, +{ 0xCC2A, 0xCC2A, 0xCC2A }, +{ 0xCC2B, 0xCC2B, 0xCC2B }, +{ 0xCC2C, 0xCC2C, 0xCC2C }, +{ 0xCC2D, 0xCC2D, 0xCC2D }, +{ 0xCC2E, 0xCC2E, 0xCC2E }, +{ 0xCC2F, 0xCC2F, 0xCC2F }, +{ 0xCC30, 0xCC30, 0xCC30 }, +{ 0xCC31, 0xCC31, 0xCC31 }, +{ 0xCC32, 0xCC32, 0xCC32 }, +{ 0xCC33, 0xCC33, 0xCC33 }, +{ 0xCC34, 0xCC34, 0xCC34 }, +{ 0xCC35, 0xCC35, 0xCC35 }, +{ 0xCC36, 0xCC36, 0xCC36 }, +{ 0xCC37, 0xCC37, 0xCC37 }, +{ 0xCC38, 0xCC38, 0xCC38 }, +{ 0xCC39, 0xCC39, 0xCC39 }, +{ 0xCC3A, 0xCC3A, 0xCC3A }, +{ 0xCC3B, 0xCC3B, 0xCC3B }, +{ 0xCC3C, 0xCC3C, 0xCC3C }, +{ 0xCC3D, 0xCC3D, 0xCC3D }, +{ 0xCC3E, 0xCC3E, 0xCC3E }, +{ 0xCC3F, 0xCC3F, 0xCC3F }, +{ 0xCC40, 0xCC40, 0xCC40 }, +{ 0xCC41, 0xCC41, 0xCC41 }, +{ 0xCC42, 0xCC42, 0xCC42 }, +{ 0xCC43, 0xCC43, 0xCC43 }, +{ 0xCC44, 0xCC44, 0xCC44 }, +{ 0xCC45, 0xCC45, 0xCC45 }, +{ 0xCC46, 0xCC46, 0xCC46 }, +{ 0xCC47, 0xCC47, 0xCC47 }, +{ 0xCC48, 0xCC48, 0xCC48 }, +{ 0xCC49, 0xCC49, 0xCC49 }, +{ 0xCC4A, 0xCC4A, 0xCC4A }, +{ 0xCC4B, 0xCC4B, 0xCC4B }, +{ 0xCC4C, 0xCC4C, 0xCC4C }, +{ 0xCC4D, 0xCC4D, 0xCC4D }, +{ 0xCC4E, 0xCC4E, 0xCC4E }, +{ 0xCC4F, 0xCC4F, 0xCC4F }, +{ 0xCC50, 0xCC50, 0xCC50 }, +{ 0xCC51, 0xCC51, 0xCC51 }, +{ 0xCC52, 0xCC52, 0xCC52 }, +{ 0xCC53, 0xCC53, 0xCC53 }, +{ 0xCC54, 0xCC54, 0xCC54 }, +{ 0xCC55, 0xCC55, 0xCC55 }, +{ 0xCC56, 0xCC56, 0xCC56 }, +{ 0xCC57, 0xCC57, 0xCC57 }, +{ 0xCC58, 0xCC58, 0xCC58 }, +{ 0xCC59, 0xCC59, 0xCC59 }, +{ 0xCC5A, 0xCC5A, 0xCC5A }, +{ 0xCC5B, 0xCC5B, 0xCC5B }, +{ 0xCC5C, 0xCC5C, 0xCC5C }, +{ 0xCC5D, 0xCC5D, 0xCC5D }, +{ 0xCC5E, 0xCC5E, 0xCC5E }, +{ 0xCC5F, 0xCC5F, 0xCC5F }, +{ 0xCC60, 0xCC60, 0xCC60 }, +{ 0xCC61, 0xCC61, 0xCC61 }, +{ 0xCC62, 0xCC62, 0xCC62 }, +{ 0xCC63, 0xCC63, 0xCC63 }, +{ 0xCC64, 0xCC64, 0xCC64 }, +{ 0xCC65, 0xCC65, 0xCC65 }, +{ 0xCC66, 0xCC66, 0xCC66 }, +{ 0xCC67, 0xCC67, 0xCC67 }, +{ 0xCC68, 0xCC68, 0xCC68 }, +{ 0xCC69, 0xCC69, 0xCC69 }, +{ 0xCC6A, 0xCC6A, 0xCC6A }, +{ 0xCC6B, 0xCC6B, 0xCC6B }, +{ 0xCC6C, 0xCC6C, 0xCC6C }, +{ 0xCC6D, 0xCC6D, 0xCC6D }, +{ 0xCC6E, 0xCC6E, 0xCC6E }, +{ 0xCC6F, 0xCC6F, 0xCC6F }, +{ 0xCC70, 0xCC70, 0xCC70 }, +{ 0xCC71, 0xCC71, 0xCC71 }, +{ 0xCC72, 0xCC72, 0xCC72 }, +{ 0xCC73, 0xCC73, 0xCC73 }, +{ 0xCC74, 0xCC74, 0xCC74 }, +{ 0xCC75, 0xCC75, 0xCC75 }, +{ 0xCC76, 0xCC76, 0xCC76 }, +{ 0xCC77, 0xCC77, 0xCC77 }, +{ 0xCC78, 0xCC78, 0xCC78 }, +{ 0xCC79, 0xCC79, 0xCC79 }, +{ 0xCC7A, 0xCC7A, 0xCC7A }, +{ 0xCC7B, 0xCC7B, 0xCC7B }, +{ 0xCC7C, 0xCC7C, 0xCC7C }, +{ 0xCC7D, 0xCC7D, 0xCC7D }, +{ 0xCC7E, 0xCC7E, 0xCC7E }, +{ 0xCC7F, 0xCC7F, 0xCC7F }, +{ 0xCC80, 0xCC80, 0xCC80 }, +{ 0xCC81, 0xCC81, 0xCC81 }, +{ 0xCC82, 0xCC82, 0xCC82 }, +{ 0xCC83, 0xCC83, 0xCC83 }, +{ 0xCC84, 0xCC84, 0xCC84 }, +{ 0xCC85, 0xCC85, 0xCC85 }, +{ 0xCC86, 0xCC86, 0xCC86 }, +{ 0xCC87, 0xCC87, 0xCC87 }, +{ 0xCC88, 0xCC88, 0xCC88 }, +{ 0xCC89, 0xCC89, 0xCC89 }, +{ 0xCC8A, 0xCC8A, 0xCC8A }, +{ 0xCC8B, 0xCC8B, 0xCC8B }, +{ 0xCC8C, 0xCC8C, 0xCC8C }, +{ 0xCC8D, 0xCC8D, 0xCC8D }, +{ 0xCC8E, 0xCC8E, 0xCC8E }, +{ 0xCC8F, 0xCC8F, 0xCC8F }, +{ 0xCC90, 0xCC90, 0xCC90 }, +{ 0xCC91, 0xCC91, 0xCC91 }, +{ 0xCC92, 0xCC92, 0xCC92 }, +{ 0xCC93, 0xCC93, 0xCC93 }, +{ 0xCC94, 0xCC94, 0xCC94 }, +{ 0xCC95, 0xCC95, 0xCC95 }, +{ 0xCC96, 0xCC96, 0xCC96 }, +{ 0xCC97, 0xCC97, 0xCC97 }, +{ 0xCC98, 0xCC98, 0xCC98 }, +{ 0xCC99, 0xCC99, 0xCC99 }, +{ 0xCC9A, 0xCC9A, 0xCC9A }, +{ 0xCC9B, 0xCC9B, 0xCC9B }, +{ 0xCC9C, 0xCC9C, 0xCC9C }, +{ 0xCC9D, 0xCC9D, 0xCC9D }, +{ 0xCC9E, 0xCC9E, 0xCC9E }, +{ 0xCC9F, 0xCC9F, 0xCC9F }, +{ 0xCCA0, 0xCCA0, 0xCCA0 }, +{ 0xCCA1, 0xCCA1, 0xCCA1 }, +{ 0xCCA2, 0xCCA2, 0xCCA2 }, +{ 0xCCA3, 0xCCA3, 0xCCA3 }, +{ 0xCCA4, 0xCCA4, 0xCCA4 }, +{ 0xCCA5, 0xCCA5, 0xCCA5 }, +{ 0xCCA6, 0xCCA6, 0xCCA6 }, +{ 0xCCA7, 0xCCA7, 0xCCA7 }, +{ 0xCCA8, 0xCCA8, 0xCCA8 }, +{ 0xCCA9, 0xCCA9, 0xCCA9 }, +{ 0xCCAA, 0xCCAA, 0xCCAA }, +{ 0xCCAB, 0xCCAB, 0xCCAB }, +{ 0xCCAC, 0xCCAC, 0xCCAC }, +{ 0xCCAD, 0xCCAD, 0xCCAD }, +{ 0xCCAE, 0xCCAE, 0xCCAE }, +{ 0xCCAF, 0xCCAF, 0xCCAF }, +{ 0xCCB0, 0xCCB0, 0xCCB0 }, +{ 0xCCB1, 0xCCB1, 0xCCB1 }, +{ 0xCCB2, 0xCCB2, 0xCCB2 }, +{ 0xCCB3, 0xCCB3, 0xCCB3 }, +{ 0xCCB4, 0xCCB4, 0xCCB4 }, +{ 0xCCB5, 0xCCB5, 0xCCB5 }, +{ 0xCCB6, 0xCCB6, 0xCCB6 }, +{ 0xCCB7, 0xCCB7, 0xCCB7 }, +{ 0xCCB8, 0xCCB8, 0xCCB8 }, +{ 0xCCB9, 0xCCB9, 0xCCB9 }, +{ 0xCCBA, 0xCCBA, 0xCCBA }, +{ 0xCCBB, 0xCCBB, 0xCCBB }, +{ 0xCCBC, 0xCCBC, 0xCCBC }, +{ 0xCCBD, 0xCCBD, 0xCCBD }, +{ 0xCCBE, 0xCCBE, 0xCCBE }, +{ 0xCCBF, 0xCCBF, 0xCCBF }, +{ 0xCCC0, 0xCCC0, 0xCCC0 }, +{ 0xCCC1, 0xCCC1, 0xCCC1 }, +{ 0xCCC2, 0xCCC2, 0xCCC2 }, +{ 0xCCC3, 0xCCC3, 0xCCC3 }, +{ 0xCCC4, 0xCCC4, 0xCCC4 }, +{ 0xCCC5, 0xCCC5, 0xCCC5 }, +{ 0xCCC6, 0xCCC6, 0xCCC6 }, +{ 0xCCC7, 0xCCC7, 0xCCC7 }, +{ 0xCCC8, 0xCCC8, 0xCCC8 }, +{ 0xCCC9, 0xCCC9, 0xCCC9 }, +{ 0xCCCA, 0xCCCA, 0xCCCA }, +{ 0xCCCB, 0xCCCB, 0xCCCB }, +{ 0xCCCC, 0xCCCC, 0xCCCC }, +{ 0xCCCD, 0xCCCD, 0xCCCD }, +{ 0xCCCE, 0xCCCE, 0xCCCE }, +{ 0xCCCF, 0xCCCF, 0xCCCF }, +{ 0xCCD0, 0xCCD0, 0xCCD0 }, +{ 0xCCD1, 0xCCD1, 0xCCD1 }, +{ 0xCCD2, 0xCCD2, 0xCCD2 }, +{ 0xCCD3, 0xCCD3, 0xCCD3 }, +{ 0xCCD4, 0xCCD4, 0xCCD4 }, +{ 0xCCD5, 0xCCD5, 0xCCD5 }, +{ 0xCCD6, 0xCCD6, 0xCCD6 }, +{ 0xCCD7, 0xCCD7, 0xCCD7 }, +{ 0xCCD8, 0xCCD8, 0xCCD8 }, +{ 0xCCD9, 0xCCD9, 0xCCD9 }, +{ 0xCCDA, 0xCCDA, 0xCCDA }, +{ 0xCCDB, 0xCCDB, 0xCCDB }, +{ 0xCCDC, 0xCCDC, 0xCCDC }, +{ 0xCCDD, 0xCCDD, 0xCCDD }, +{ 0xCCDE, 0xCCDE, 0xCCDE }, +{ 0xCCDF, 0xCCDF, 0xCCDF }, +{ 0xCCE0, 0xCCE0, 0xCCE0 }, +{ 0xCCE1, 0xCCE1, 0xCCE1 }, +{ 0xCCE2, 0xCCE2, 0xCCE2 }, +{ 0xCCE3, 0xCCE3, 0xCCE3 }, +{ 0xCCE4, 0xCCE4, 0xCCE4 }, +{ 0xCCE5, 0xCCE5, 0xCCE5 }, +{ 0xCCE6, 0xCCE6, 0xCCE6 }, +{ 0xCCE7, 0xCCE7, 0xCCE7 }, +{ 0xCCE8, 0xCCE8, 0xCCE8 }, +{ 0xCCE9, 0xCCE9, 0xCCE9 }, +{ 0xCCEA, 0xCCEA, 0xCCEA }, +{ 0xCCEB, 0xCCEB, 0xCCEB }, +{ 0xCCEC, 0xCCEC, 0xCCEC }, +{ 0xCCED, 0xCCED, 0xCCED }, +{ 0xCCEE, 0xCCEE, 0xCCEE }, +{ 0xCCEF, 0xCCEF, 0xCCEF }, +{ 0xCCF0, 0xCCF0, 0xCCF0 }, +{ 0xCCF1, 0xCCF1, 0xCCF1 }, +{ 0xCCF2, 0xCCF2, 0xCCF2 }, +{ 0xCCF3, 0xCCF3, 0xCCF3 }, +{ 0xCCF4, 0xCCF4, 0xCCF4 }, +{ 0xCCF5, 0xCCF5, 0xCCF5 }, +{ 0xCCF6, 0xCCF6, 0xCCF6 }, +{ 0xCCF7, 0xCCF7, 0xCCF7 }, +{ 0xCCF8, 0xCCF8, 0xCCF8 }, +{ 0xCCF9, 0xCCF9, 0xCCF9 }, +{ 0xCCFA, 0xCCFA, 0xCCFA }, +{ 0xCCFB, 0xCCFB, 0xCCFB }, +{ 0xCCFC, 0xCCFC, 0xCCFC }, +{ 0xCCFD, 0xCCFD, 0xCCFD }, +{ 0xCCFE, 0xCCFE, 0xCCFE }, +{ 0xCCFF, 0xCCFF, 0xCCFF }, +{ 0xCD00, 0xCD00, 0xCD00 }, +{ 0xCD01, 0xCD01, 0xCD01 }, +{ 0xCD02, 0xCD02, 0xCD02 }, +{ 0xCD03, 0xCD03, 0xCD03 }, +{ 0xCD04, 0xCD04, 0xCD04 }, +{ 0xCD05, 0xCD05, 0xCD05 }, +{ 0xCD06, 0xCD06, 0xCD06 }, +{ 0xCD07, 0xCD07, 0xCD07 }, +{ 0xCD08, 0xCD08, 0xCD08 }, +{ 0xCD09, 0xCD09, 0xCD09 }, +{ 0xCD0A, 0xCD0A, 0xCD0A }, +{ 0xCD0B, 0xCD0B, 0xCD0B }, +{ 0xCD0C, 0xCD0C, 0xCD0C }, +{ 0xCD0D, 0xCD0D, 0xCD0D }, +{ 0xCD0E, 0xCD0E, 0xCD0E }, +{ 0xCD0F, 0xCD0F, 0xCD0F }, +{ 0xCD10, 0xCD10, 0xCD10 }, +{ 0xCD11, 0xCD11, 0xCD11 }, +{ 0xCD12, 0xCD12, 0xCD12 }, +{ 0xCD13, 0xCD13, 0xCD13 }, +{ 0xCD14, 0xCD14, 0xCD14 }, +{ 0xCD15, 0xCD15, 0xCD15 }, +{ 0xCD16, 0xCD16, 0xCD16 }, +{ 0xCD17, 0xCD17, 0xCD17 }, +{ 0xCD18, 0xCD18, 0xCD18 }, +{ 0xCD19, 0xCD19, 0xCD19 }, +{ 0xCD1A, 0xCD1A, 0xCD1A }, +{ 0xCD1B, 0xCD1B, 0xCD1B }, +{ 0xCD1C, 0xCD1C, 0xCD1C }, +{ 0xCD1D, 0xCD1D, 0xCD1D }, +{ 0xCD1E, 0xCD1E, 0xCD1E }, +{ 0xCD1F, 0xCD1F, 0xCD1F }, +{ 0xCD20, 0xCD20, 0xCD20 }, +{ 0xCD21, 0xCD21, 0xCD21 }, +{ 0xCD22, 0xCD22, 0xCD22 }, +{ 0xCD23, 0xCD23, 0xCD23 }, +{ 0xCD24, 0xCD24, 0xCD24 }, +{ 0xCD25, 0xCD25, 0xCD25 }, +{ 0xCD26, 0xCD26, 0xCD26 }, +{ 0xCD27, 0xCD27, 0xCD27 }, +{ 0xCD28, 0xCD28, 0xCD28 }, +{ 0xCD29, 0xCD29, 0xCD29 }, +{ 0xCD2A, 0xCD2A, 0xCD2A }, +{ 0xCD2B, 0xCD2B, 0xCD2B }, +{ 0xCD2C, 0xCD2C, 0xCD2C }, +{ 0xCD2D, 0xCD2D, 0xCD2D }, +{ 0xCD2E, 0xCD2E, 0xCD2E }, +{ 0xCD2F, 0xCD2F, 0xCD2F }, +{ 0xCD30, 0xCD30, 0xCD30 }, +{ 0xCD31, 0xCD31, 0xCD31 }, +{ 0xCD32, 0xCD32, 0xCD32 }, +{ 0xCD33, 0xCD33, 0xCD33 }, +{ 0xCD34, 0xCD34, 0xCD34 }, +{ 0xCD35, 0xCD35, 0xCD35 }, +{ 0xCD36, 0xCD36, 0xCD36 }, +{ 0xCD37, 0xCD37, 0xCD37 }, +{ 0xCD38, 0xCD38, 0xCD38 }, +{ 0xCD39, 0xCD39, 0xCD39 }, +{ 0xCD3A, 0xCD3A, 0xCD3A }, +{ 0xCD3B, 0xCD3B, 0xCD3B }, +{ 0xCD3C, 0xCD3C, 0xCD3C }, +{ 0xCD3D, 0xCD3D, 0xCD3D }, +{ 0xCD3E, 0xCD3E, 0xCD3E }, +{ 0xCD3F, 0xCD3F, 0xCD3F }, +{ 0xCD40, 0xCD40, 0xCD40 }, +{ 0xCD41, 0xCD41, 0xCD41 }, +{ 0xCD42, 0xCD42, 0xCD42 }, +{ 0xCD43, 0xCD43, 0xCD43 }, +{ 0xCD44, 0xCD44, 0xCD44 }, +{ 0xCD45, 0xCD45, 0xCD45 }, +{ 0xCD46, 0xCD46, 0xCD46 }, +{ 0xCD47, 0xCD47, 0xCD47 }, +{ 0xCD48, 0xCD48, 0xCD48 }, +{ 0xCD49, 0xCD49, 0xCD49 }, +{ 0xCD4A, 0xCD4A, 0xCD4A }, +{ 0xCD4B, 0xCD4B, 0xCD4B }, +{ 0xCD4C, 0xCD4C, 0xCD4C }, +{ 0xCD4D, 0xCD4D, 0xCD4D }, +{ 0xCD4E, 0xCD4E, 0xCD4E }, +{ 0xCD4F, 0xCD4F, 0xCD4F }, +{ 0xCD50, 0xCD50, 0xCD50 }, +{ 0xCD51, 0xCD51, 0xCD51 }, +{ 0xCD52, 0xCD52, 0xCD52 }, +{ 0xCD53, 0xCD53, 0xCD53 }, +{ 0xCD54, 0xCD54, 0xCD54 }, +{ 0xCD55, 0xCD55, 0xCD55 }, +{ 0xCD56, 0xCD56, 0xCD56 }, +{ 0xCD57, 0xCD57, 0xCD57 }, +{ 0xCD58, 0xCD58, 0xCD58 }, +{ 0xCD59, 0xCD59, 0xCD59 }, +{ 0xCD5A, 0xCD5A, 0xCD5A }, +{ 0xCD5B, 0xCD5B, 0xCD5B }, +{ 0xCD5C, 0xCD5C, 0xCD5C }, +{ 0xCD5D, 0xCD5D, 0xCD5D }, +{ 0xCD5E, 0xCD5E, 0xCD5E }, +{ 0xCD5F, 0xCD5F, 0xCD5F }, +{ 0xCD60, 0xCD60, 0xCD60 }, +{ 0xCD61, 0xCD61, 0xCD61 }, +{ 0xCD62, 0xCD62, 0xCD62 }, +{ 0xCD63, 0xCD63, 0xCD63 }, +{ 0xCD64, 0xCD64, 0xCD64 }, +{ 0xCD65, 0xCD65, 0xCD65 }, +{ 0xCD66, 0xCD66, 0xCD66 }, +{ 0xCD67, 0xCD67, 0xCD67 }, +{ 0xCD68, 0xCD68, 0xCD68 }, +{ 0xCD69, 0xCD69, 0xCD69 }, +{ 0xCD6A, 0xCD6A, 0xCD6A }, +{ 0xCD6B, 0xCD6B, 0xCD6B }, +{ 0xCD6C, 0xCD6C, 0xCD6C }, +{ 0xCD6D, 0xCD6D, 0xCD6D }, +{ 0xCD6E, 0xCD6E, 0xCD6E }, +{ 0xCD6F, 0xCD6F, 0xCD6F }, +{ 0xCD70, 0xCD70, 0xCD70 }, +{ 0xCD71, 0xCD71, 0xCD71 }, +{ 0xCD72, 0xCD72, 0xCD72 }, +{ 0xCD73, 0xCD73, 0xCD73 }, +{ 0xCD74, 0xCD74, 0xCD74 }, +{ 0xCD75, 0xCD75, 0xCD75 }, +{ 0xCD76, 0xCD76, 0xCD76 }, +{ 0xCD77, 0xCD77, 0xCD77 }, +{ 0xCD78, 0xCD78, 0xCD78 }, +{ 0xCD79, 0xCD79, 0xCD79 }, +{ 0xCD7A, 0xCD7A, 0xCD7A }, +{ 0xCD7B, 0xCD7B, 0xCD7B }, +{ 0xCD7C, 0xCD7C, 0xCD7C }, +{ 0xCD7D, 0xCD7D, 0xCD7D }, +{ 0xCD7E, 0xCD7E, 0xCD7E }, +{ 0xCD7F, 0xCD7F, 0xCD7F }, +{ 0xCD80, 0xCD80, 0xCD80 }, +{ 0xCD81, 0xCD81, 0xCD81 }, +{ 0xCD82, 0xCD82, 0xCD82 }, +{ 0xCD83, 0xCD83, 0xCD83 }, +{ 0xCD84, 0xCD84, 0xCD84 }, +{ 0xCD85, 0xCD85, 0xCD85 }, +{ 0xCD86, 0xCD86, 0xCD86 }, +{ 0xCD87, 0xCD87, 0xCD87 }, +{ 0xCD88, 0xCD88, 0xCD88 }, +{ 0xCD89, 0xCD89, 0xCD89 }, +{ 0xCD8A, 0xCD8A, 0xCD8A }, +{ 0xCD8B, 0xCD8B, 0xCD8B }, +{ 0xCD8C, 0xCD8C, 0xCD8C }, +{ 0xCD8D, 0xCD8D, 0xCD8D }, +{ 0xCD8E, 0xCD8E, 0xCD8E }, +{ 0xCD8F, 0xCD8F, 0xCD8F }, +{ 0xCD90, 0xCD90, 0xCD90 }, +{ 0xCD91, 0xCD91, 0xCD91 }, +{ 0xCD92, 0xCD92, 0xCD92 }, +{ 0xCD93, 0xCD93, 0xCD93 }, +{ 0xCD94, 0xCD94, 0xCD94 }, +{ 0xCD95, 0xCD95, 0xCD95 }, +{ 0xCD96, 0xCD96, 0xCD96 }, +{ 0xCD97, 0xCD97, 0xCD97 }, +{ 0xCD98, 0xCD98, 0xCD98 }, +{ 0xCD99, 0xCD99, 0xCD99 }, +{ 0xCD9A, 0xCD9A, 0xCD9A }, +{ 0xCD9B, 0xCD9B, 0xCD9B }, +{ 0xCD9C, 0xCD9C, 0xCD9C }, +{ 0xCD9D, 0xCD9D, 0xCD9D }, +{ 0xCD9E, 0xCD9E, 0xCD9E }, +{ 0xCD9F, 0xCD9F, 0xCD9F }, +{ 0xCDA0, 0xCDA0, 0xCDA0 }, +{ 0xCDA1, 0xCDA1, 0xCDA1 }, +{ 0xCDA2, 0xCDA2, 0xCDA2 }, +{ 0xCDA3, 0xCDA3, 0xCDA3 }, +{ 0xCDA4, 0xCDA4, 0xCDA4 }, +{ 0xCDA5, 0xCDA5, 0xCDA5 }, +{ 0xCDA6, 0xCDA6, 0xCDA6 }, +{ 0xCDA7, 0xCDA7, 0xCDA7 }, +{ 0xCDA8, 0xCDA8, 0xCDA8 }, +{ 0xCDA9, 0xCDA9, 0xCDA9 }, +{ 0xCDAA, 0xCDAA, 0xCDAA }, +{ 0xCDAB, 0xCDAB, 0xCDAB }, +{ 0xCDAC, 0xCDAC, 0xCDAC }, +{ 0xCDAD, 0xCDAD, 0xCDAD }, +{ 0xCDAE, 0xCDAE, 0xCDAE }, +{ 0xCDAF, 0xCDAF, 0xCDAF }, +{ 0xCDB0, 0xCDB0, 0xCDB0 }, +{ 0xCDB1, 0xCDB1, 0xCDB1 }, +{ 0xCDB2, 0xCDB2, 0xCDB2 }, +{ 0xCDB3, 0xCDB3, 0xCDB3 }, +{ 0xCDB4, 0xCDB4, 0xCDB4 }, +{ 0xCDB5, 0xCDB5, 0xCDB5 }, +{ 0xCDB6, 0xCDB6, 0xCDB6 }, +{ 0xCDB7, 0xCDB7, 0xCDB7 }, +{ 0xCDB8, 0xCDB8, 0xCDB8 }, +{ 0xCDB9, 0xCDB9, 0xCDB9 }, +{ 0xCDBA, 0xCDBA, 0xCDBA }, +{ 0xCDBB, 0xCDBB, 0xCDBB }, +{ 0xCDBC, 0xCDBC, 0xCDBC }, +{ 0xCDBD, 0xCDBD, 0xCDBD }, +{ 0xCDBE, 0xCDBE, 0xCDBE }, +{ 0xCDBF, 0xCDBF, 0xCDBF }, +{ 0xCDC0, 0xCDC0, 0xCDC0 }, +{ 0xCDC1, 0xCDC1, 0xCDC1 }, +{ 0xCDC2, 0xCDC2, 0xCDC2 }, +{ 0xCDC3, 0xCDC3, 0xCDC3 }, +{ 0xCDC4, 0xCDC4, 0xCDC4 }, +{ 0xCDC5, 0xCDC5, 0xCDC5 }, +{ 0xCDC6, 0xCDC6, 0xCDC6 }, +{ 0xCDC7, 0xCDC7, 0xCDC7 }, +{ 0xCDC8, 0xCDC8, 0xCDC8 }, +{ 0xCDC9, 0xCDC9, 0xCDC9 }, +{ 0xCDCA, 0xCDCA, 0xCDCA }, +{ 0xCDCB, 0xCDCB, 0xCDCB }, +{ 0xCDCC, 0xCDCC, 0xCDCC }, +{ 0xCDCD, 0xCDCD, 0xCDCD }, +{ 0xCDCE, 0xCDCE, 0xCDCE }, +{ 0xCDCF, 0xCDCF, 0xCDCF }, +{ 0xCDD0, 0xCDD0, 0xCDD0 }, +{ 0xCDD1, 0xCDD1, 0xCDD1 }, +{ 0xCDD2, 0xCDD2, 0xCDD2 }, +{ 0xCDD3, 0xCDD3, 0xCDD3 }, +{ 0xCDD4, 0xCDD4, 0xCDD4 }, +{ 0xCDD5, 0xCDD5, 0xCDD5 }, +{ 0xCDD6, 0xCDD6, 0xCDD6 }, +{ 0xCDD7, 0xCDD7, 0xCDD7 }, +{ 0xCDD8, 0xCDD8, 0xCDD8 }, +{ 0xCDD9, 0xCDD9, 0xCDD9 }, +{ 0xCDDA, 0xCDDA, 0xCDDA }, +{ 0xCDDB, 0xCDDB, 0xCDDB }, +{ 0xCDDC, 0xCDDC, 0xCDDC }, +{ 0xCDDD, 0xCDDD, 0xCDDD }, +{ 0xCDDE, 0xCDDE, 0xCDDE }, +{ 0xCDDF, 0xCDDF, 0xCDDF }, +{ 0xCDE0, 0xCDE0, 0xCDE0 }, +{ 0xCDE1, 0xCDE1, 0xCDE1 }, +{ 0xCDE2, 0xCDE2, 0xCDE2 }, +{ 0xCDE3, 0xCDE3, 0xCDE3 }, +{ 0xCDE4, 0xCDE4, 0xCDE4 }, +{ 0xCDE5, 0xCDE5, 0xCDE5 }, +{ 0xCDE6, 0xCDE6, 0xCDE6 }, +{ 0xCDE7, 0xCDE7, 0xCDE7 }, +{ 0xCDE8, 0xCDE8, 0xCDE8 }, +{ 0xCDE9, 0xCDE9, 0xCDE9 }, +{ 0xCDEA, 0xCDEA, 0xCDEA }, +{ 0xCDEB, 0xCDEB, 0xCDEB }, +{ 0xCDEC, 0xCDEC, 0xCDEC }, +{ 0xCDED, 0xCDED, 0xCDED }, +{ 0xCDEE, 0xCDEE, 0xCDEE }, +{ 0xCDEF, 0xCDEF, 0xCDEF }, +{ 0xCDF0, 0xCDF0, 0xCDF0 }, +{ 0xCDF1, 0xCDF1, 0xCDF1 }, +{ 0xCDF2, 0xCDF2, 0xCDF2 }, +{ 0xCDF3, 0xCDF3, 0xCDF3 }, +{ 0xCDF4, 0xCDF4, 0xCDF4 }, +{ 0xCDF5, 0xCDF5, 0xCDF5 }, +{ 0xCDF6, 0xCDF6, 0xCDF6 }, +{ 0xCDF7, 0xCDF7, 0xCDF7 }, +{ 0xCDF8, 0xCDF8, 0xCDF8 }, +{ 0xCDF9, 0xCDF9, 0xCDF9 }, +{ 0xCDFA, 0xCDFA, 0xCDFA }, +{ 0xCDFB, 0xCDFB, 0xCDFB }, +{ 0xCDFC, 0xCDFC, 0xCDFC }, +{ 0xCDFD, 0xCDFD, 0xCDFD }, +{ 0xCDFE, 0xCDFE, 0xCDFE }, +{ 0xCDFF, 0xCDFF, 0xCDFF }, +{ 0xCE00, 0xCE00, 0xCE00 }, +{ 0xCE01, 0xCE01, 0xCE01 }, +{ 0xCE02, 0xCE02, 0xCE02 }, +{ 0xCE03, 0xCE03, 0xCE03 }, +{ 0xCE04, 0xCE04, 0xCE04 }, +{ 0xCE05, 0xCE05, 0xCE05 }, +{ 0xCE06, 0xCE06, 0xCE06 }, +{ 0xCE07, 0xCE07, 0xCE07 }, +{ 0xCE08, 0xCE08, 0xCE08 }, +{ 0xCE09, 0xCE09, 0xCE09 }, +{ 0xCE0A, 0xCE0A, 0xCE0A }, +{ 0xCE0B, 0xCE0B, 0xCE0B }, +{ 0xCE0C, 0xCE0C, 0xCE0C }, +{ 0xCE0D, 0xCE0D, 0xCE0D }, +{ 0xCE0E, 0xCE0E, 0xCE0E }, +{ 0xCE0F, 0xCE0F, 0xCE0F }, +{ 0xCE10, 0xCE10, 0xCE10 }, +{ 0xCE11, 0xCE11, 0xCE11 }, +{ 0xCE12, 0xCE12, 0xCE12 }, +{ 0xCE13, 0xCE13, 0xCE13 }, +{ 0xCE14, 0xCE14, 0xCE14 }, +{ 0xCE15, 0xCE15, 0xCE15 }, +{ 0xCE16, 0xCE16, 0xCE16 }, +{ 0xCE17, 0xCE17, 0xCE17 }, +{ 0xCE18, 0xCE18, 0xCE18 }, +{ 0xCE19, 0xCE19, 0xCE19 }, +{ 0xCE1A, 0xCE1A, 0xCE1A }, +{ 0xCE1B, 0xCE1B, 0xCE1B }, +{ 0xCE1C, 0xCE1C, 0xCE1C }, +{ 0xCE1D, 0xCE1D, 0xCE1D }, +{ 0xCE1E, 0xCE1E, 0xCE1E }, +{ 0xCE1F, 0xCE1F, 0xCE1F }, +{ 0xCE20, 0xCE20, 0xCE20 }, +{ 0xCE21, 0xCE21, 0xCE21 }, +{ 0xCE22, 0xCE22, 0xCE22 }, +{ 0xCE23, 0xCE23, 0xCE23 }, +{ 0xCE24, 0xCE24, 0xCE24 }, +{ 0xCE25, 0xCE25, 0xCE25 }, +{ 0xCE26, 0xCE26, 0xCE26 }, +{ 0xCE27, 0xCE27, 0xCE27 }, +{ 0xCE28, 0xCE28, 0xCE28 }, +{ 0xCE29, 0xCE29, 0xCE29 }, +{ 0xCE2A, 0xCE2A, 0xCE2A }, +{ 0xCE2B, 0xCE2B, 0xCE2B }, +{ 0xCE2C, 0xCE2C, 0xCE2C }, +{ 0xCE2D, 0xCE2D, 0xCE2D }, +{ 0xCE2E, 0xCE2E, 0xCE2E }, +{ 0xCE2F, 0xCE2F, 0xCE2F }, +{ 0xCE30, 0xCE30, 0xCE30 }, +{ 0xCE31, 0xCE31, 0xCE31 }, +{ 0xCE32, 0xCE32, 0xCE32 }, +{ 0xCE33, 0xCE33, 0xCE33 }, +{ 0xCE34, 0xCE34, 0xCE34 }, +{ 0xCE35, 0xCE35, 0xCE35 }, +{ 0xCE36, 0xCE36, 0xCE36 }, +{ 0xCE37, 0xCE37, 0xCE37 }, +{ 0xCE38, 0xCE38, 0xCE38 }, +{ 0xCE39, 0xCE39, 0xCE39 }, +{ 0xCE3A, 0xCE3A, 0xCE3A }, +{ 0xCE3B, 0xCE3B, 0xCE3B }, +{ 0xCE3C, 0xCE3C, 0xCE3C }, +{ 0xCE3D, 0xCE3D, 0xCE3D }, +{ 0xCE3E, 0xCE3E, 0xCE3E }, +{ 0xCE3F, 0xCE3F, 0xCE3F }, +{ 0xCE40, 0xCE40, 0xCE40 }, +{ 0xCE41, 0xCE41, 0xCE41 }, +{ 0xCE42, 0xCE42, 0xCE42 }, +{ 0xCE43, 0xCE43, 0xCE43 }, +{ 0xCE44, 0xCE44, 0xCE44 }, +{ 0xCE45, 0xCE45, 0xCE45 }, +{ 0xCE46, 0xCE46, 0xCE46 }, +{ 0xCE47, 0xCE47, 0xCE47 }, +{ 0xCE48, 0xCE48, 0xCE48 }, +{ 0xCE49, 0xCE49, 0xCE49 }, +{ 0xCE4A, 0xCE4A, 0xCE4A }, +{ 0xCE4B, 0xCE4B, 0xCE4B }, +{ 0xCE4C, 0xCE4C, 0xCE4C }, +{ 0xCE4D, 0xCE4D, 0xCE4D }, +{ 0xCE4E, 0xCE4E, 0xCE4E }, +{ 0xCE4F, 0xCE4F, 0xCE4F }, +{ 0xCE50, 0xCE50, 0xCE50 }, +{ 0xCE51, 0xCE51, 0xCE51 }, +{ 0xCE52, 0xCE52, 0xCE52 }, +{ 0xCE53, 0xCE53, 0xCE53 }, +{ 0xCE54, 0xCE54, 0xCE54 }, +{ 0xCE55, 0xCE55, 0xCE55 }, +{ 0xCE56, 0xCE56, 0xCE56 }, +{ 0xCE57, 0xCE57, 0xCE57 }, +{ 0xCE58, 0xCE58, 0xCE58 }, +{ 0xCE59, 0xCE59, 0xCE59 }, +{ 0xCE5A, 0xCE5A, 0xCE5A }, +{ 0xCE5B, 0xCE5B, 0xCE5B }, +{ 0xCE5C, 0xCE5C, 0xCE5C }, +{ 0xCE5D, 0xCE5D, 0xCE5D }, +{ 0xCE5E, 0xCE5E, 0xCE5E }, +{ 0xCE5F, 0xCE5F, 0xCE5F }, +{ 0xCE60, 0xCE60, 0xCE60 }, +{ 0xCE61, 0xCE61, 0xCE61 }, +{ 0xCE62, 0xCE62, 0xCE62 }, +{ 0xCE63, 0xCE63, 0xCE63 }, +{ 0xCE64, 0xCE64, 0xCE64 }, +{ 0xCE65, 0xCE65, 0xCE65 }, +{ 0xCE66, 0xCE66, 0xCE66 }, +{ 0xCE67, 0xCE67, 0xCE67 }, +{ 0xCE68, 0xCE68, 0xCE68 }, +{ 0xCE69, 0xCE69, 0xCE69 }, +{ 0xCE6A, 0xCE6A, 0xCE6A }, +{ 0xCE6B, 0xCE6B, 0xCE6B }, +{ 0xCE6C, 0xCE6C, 0xCE6C }, +{ 0xCE6D, 0xCE6D, 0xCE6D }, +{ 0xCE6E, 0xCE6E, 0xCE6E }, +{ 0xCE6F, 0xCE6F, 0xCE6F }, +{ 0xCE70, 0xCE70, 0xCE70 }, +{ 0xCE71, 0xCE71, 0xCE71 }, +{ 0xCE72, 0xCE72, 0xCE72 }, +{ 0xCE73, 0xCE73, 0xCE73 }, +{ 0xCE74, 0xCE74, 0xCE74 }, +{ 0xCE75, 0xCE75, 0xCE75 }, +{ 0xCE76, 0xCE76, 0xCE76 }, +{ 0xCE77, 0xCE77, 0xCE77 }, +{ 0xCE78, 0xCE78, 0xCE78 }, +{ 0xCE79, 0xCE79, 0xCE79 }, +{ 0xCE7A, 0xCE7A, 0xCE7A }, +{ 0xCE7B, 0xCE7B, 0xCE7B }, +{ 0xCE7C, 0xCE7C, 0xCE7C }, +{ 0xCE7D, 0xCE7D, 0xCE7D }, +{ 0xCE7E, 0xCE7E, 0xCE7E }, +{ 0xCE7F, 0xCE7F, 0xCE7F }, +{ 0xCE80, 0xCE80, 0xCE80 }, +{ 0xCE81, 0xCE81, 0xCE81 }, +{ 0xCE82, 0xCE82, 0xCE82 }, +{ 0xCE83, 0xCE83, 0xCE83 }, +{ 0xCE84, 0xCE84, 0xCE84 }, +{ 0xCE85, 0xCE85, 0xCE85 }, +{ 0xCE86, 0xCE86, 0xCE86 }, +{ 0xCE87, 0xCE87, 0xCE87 }, +{ 0xCE88, 0xCE88, 0xCE88 }, +{ 0xCE89, 0xCE89, 0xCE89 }, +{ 0xCE8A, 0xCE8A, 0xCE8A }, +{ 0xCE8B, 0xCE8B, 0xCE8B }, +{ 0xCE8C, 0xCE8C, 0xCE8C }, +{ 0xCE8D, 0xCE8D, 0xCE8D }, +{ 0xCE8E, 0xCE8E, 0xCE8E }, +{ 0xCE8F, 0xCE8F, 0xCE8F }, +{ 0xCE90, 0xCE90, 0xCE90 }, +{ 0xCE91, 0xCE91, 0xCE91 }, +{ 0xCE92, 0xCE92, 0xCE92 }, +{ 0xCE93, 0xCE93, 0xCE93 }, +{ 0xCE94, 0xCE94, 0xCE94 }, +{ 0xCE95, 0xCE95, 0xCE95 }, +{ 0xCE96, 0xCE96, 0xCE96 }, +{ 0xCE97, 0xCE97, 0xCE97 }, +{ 0xCE98, 0xCE98, 0xCE98 }, +{ 0xCE99, 0xCE99, 0xCE99 }, +{ 0xCE9A, 0xCE9A, 0xCE9A }, +{ 0xCE9B, 0xCE9B, 0xCE9B }, +{ 0xCE9C, 0xCE9C, 0xCE9C }, +{ 0xCE9D, 0xCE9D, 0xCE9D }, +{ 0xCE9E, 0xCE9E, 0xCE9E }, +{ 0xCE9F, 0xCE9F, 0xCE9F }, +{ 0xCEA0, 0xCEA0, 0xCEA0 }, +{ 0xCEA1, 0xCEA1, 0xCEA1 }, +{ 0xCEA2, 0xCEA2, 0xCEA2 }, +{ 0xCEA3, 0xCEA3, 0xCEA3 }, +{ 0xCEA4, 0xCEA4, 0xCEA4 }, +{ 0xCEA5, 0xCEA5, 0xCEA5 }, +{ 0xCEA6, 0xCEA6, 0xCEA6 }, +{ 0xCEA7, 0xCEA7, 0xCEA7 }, +{ 0xCEA8, 0xCEA8, 0xCEA8 }, +{ 0xCEA9, 0xCEA9, 0xCEA9 }, +{ 0xCEAA, 0xCEAA, 0xCEAA }, +{ 0xCEAB, 0xCEAB, 0xCEAB }, +{ 0xCEAC, 0xCEAC, 0xCEAC }, +{ 0xCEAD, 0xCEAD, 0xCEAD }, +{ 0xCEAE, 0xCEAE, 0xCEAE }, +{ 0xCEAF, 0xCEAF, 0xCEAF }, +{ 0xCEB0, 0xCEB0, 0xCEB0 }, +{ 0xCEB1, 0xCEB1, 0xCEB1 }, +{ 0xCEB2, 0xCEB2, 0xCEB2 }, +{ 0xCEB3, 0xCEB3, 0xCEB3 }, +{ 0xCEB4, 0xCEB4, 0xCEB4 }, +{ 0xCEB5, 0xCEB5, 0xCEB5 }, +{ 0xCEB6, 0xCEB6, 0xCEB6 }, +{ 0xCEB7, 0xCEB7, 0xCEB7 }, +{ 0xCEB8, 0xCEB8, 0xCEB8 }, +{ 0xCEB9, 0xCEB9, 0xCEB9 }, +{ 0xCEBA, 0xCEBA, 0xCEBA }, +{ 0xCEBB, 0xCEBB, 0xCEBB }, +{ 0xCEBC, 0xCEBC, 0xCEBC }, +{ 0xCEBD, 0xCEBD, 0xCEBD }, +{ 0xCEBE, 0xCEBE, 0xCEBE }, +{ 0xCEBF, 0xCEBF, 0xCEBF }, +{ 0xCEC0, 0xCEC0, 0xCEC0 }, +{ 0xCEC1, 0xCEC1, 0xCEC1 }, +{ 0xCEC2, 0xCEC2, 0xCEC2 }, +{ 0xCEC3, 0xCEC3, 0xCEC3 }, +{ 0xCEC4, 0xCEC4, 0xCEC4 }, +{ 0xCEC5, 0xCEC5, 0xCEC5 }, +{ 0xCEC6, 0xCEC6, 0xCEC6 }, +{ 0xCEC7, 0xCEC7, 0xCEC7 }, +{ 0xCEC8, 0xCEC8, 0xCEC8 }, +{ 0xCEC9, 0xCEC9, 0xCEC9 }, +{ 0xCECA, 0xCECA, 0xCECA }, +{ 0xCECB, 0xCECB, 0xCECB }, +{ 0xCECC, 0xCECC, 0xCECC }, +{ 0xCECD, 0xCECD, 0xCECD }, +{ 0xCECE, 0xCECE, 0xCECE }, +{ 0xCECF, 0xCECF, 0xCECF }, +{ 0xCED0, 0xCED0, 0xCED0 }, +{ 0xCED1, 0xCED1, 0xCED1 }, +{ 0xCED2, 0xCED2, 0xCED2 }, +{ 0xCED3, 0xCED3, 0xCED3 }, +{ 0xCED4, 0xCED4, 0xCED4 }, +{ 0xCED5, 0xCED5, 0xCED5 }, +{ 0xCED6, 0xCED6, 0xCED6 }, +{ 0xCED7, 0xCED7, 0xCED7 }, +{ 0xCED8, 0xCED8, 0xCED8 }, +{ 0xCED9, 0xCED9, 0xCED9 }, +{ 0xCEDA, 0xCEDA, 0xCEDA }, +{ 0xCEDB, 0xCEDB, 0xCEDB }, +{ 0xCEDC, 0xCEDC, 0xCEDC }, +{ 0xCEDD, 0xCEDD, 0xCEDD }, +{ 0xCEDE, 0xCEDE, 0xCEDE }, +{ 0xCEDF, 0xCEDF, 0xCEDF }, +{ 0xCEE0, 0xCEE0, 0xCEE0 }, +{ 0xCEE1, 0xCEE1, 0xCEE1 }, +{ 0xCEE2, 0xCEE2, 0xCEE2 }, +{ 0xCEE3, 0xCEE3, 0xCEE3 }, +{ 0xCEE4, 0xCEE4, 0xCEE4 }, +{ 0xCEE5, 0xCEE5, 0xCEE5 }, +{ 0xCEE6, 0xCEE6, 0xCEE6 }, +{ 0xCEE7, 0xCEE7, 0xCEE7 }, +{ 0xCEE8, 0xCEE8, 0xCEE8 }, +{ 0xCEE9, 0xCEE9, 0xCEE9 }, +{ 0xCEEA, 0xCEEA, 0xCEEA }, +{ 0xCEEB, 0xCEEB, 0xCEEB }, +{ 0xCEEC, 0xCEEC, 0xCEEC }, +{ 0xCEED, 0xCEED, 0xCEED }, +{ 0xCEEE, 0xCEEE, 0xCEEE }, +{ 0xCEEF, 0xCEEF, 0xCEEF }, +{ 0xCEF0, 0xCEF0, 0xCEF0 }, +{ 0xCEF1, 0xCEF1, 0xCEF1 }, +{ 0xCEF2, 0xCEF2, 0xCEF2 }, +{ 0xCEF3, 0xCEF3, 0xCEF3 }, +{ 0xCEF4, 0xCEF4, 0xCEF4 }, +{ 0xCEF5, 0xCEF5, 0xCEF5 }, +{ 0xCEF6, 0xCEF6, 0xCEF6 }, +{ 0xCEF7, 0xCEF7, 0xCEF7 }, +{ 0xCEF8, 0xCEF8, 0xCEF8 }, +{ 0xCEF9, 0xCEF9, 0xCEF9 }, +{ 0xCEFA, 0xCEFA, 0xCEFA }, +{ 0xCEFB, 0xCEFB, 0xCEFB }, +{ 0xCEFC, 0xCEFC, 0xCEFC }, +{ 0xCEFD, 0xCEFD, 0xCEFD }, +{ 0xCEFE, 0xCEFE, 0xCEFE }, +{ 0xCEFF, 0xCEFF, 0xCEFF }, +{ 0xCF00, 0xCF00, 0xCF00 }, +{ 0xCF01, 0xCF01, 0xCF01 }, +{ 0xCF02, 0xCF02, 0xCF02 }, +{ 0xCF03, 0xCF03, 0xCF03 }, +{ 0xCF04, 0xCF04, 0xCF04 }, +{ 0xCF05, 0xCF05, 0xCF05 }, +{ 0xCF06, 0xCF06, 0xCF06 }, +{ 0xCF07, 0xCF07, 0xCF07 }, +{ 0xCF08, 0xCF08, 0xCF08 }, +{ 0xCF09, 0xCF09, 0xCF09 }, +{ 0xCF0A, 0xCF0A, 0xCF0A }, +{ 0xCF0B, 0xCF0B, 0xCF0B }, +{ 0xCF0C, 0xCF0C, 0xCF0C }, +{ 0xCF0D, 0xCF0D, 0xCF0D }, +{ 0xCF0E, 0xCF0E, 0xCF0E }, +{ 0xCF0F, 0xCF0F, 0xCF0F }, +{ 0xCF10, 0xCF10, 0xCF10 }, +{ 0xCF11, 0xCF11, 0xCF11 }, +{ 0xCF12, 0xCF12, 0xCF12 }, +{ 0xCF13, 0xCF13, 0xCF13 }, +{ 0xCF14, 0xCF14, 0xCF14 }, +{ 0xCF15, 0xCF15, 0xCF15 }, +{ 0xCF16, 0xCF16, 0xCF16 }, +{ 0xCF17, 0xCF17, 0xCF17 }, +{ 0xCF18, 0xCF18, 0xCF18 }, +{ 0xCF19, 0xCF19, 0xCF19 }, +{ 0xCF1A, 0xCF1A, 0xCF1A }, +{ 0xCF1B, 0xCF1B, 0xCF1B }, +{ 0xCF1C, 0xCF1C, 0xCF1C }, +{ 0xCF1D, 0xCF1D, 0xCF1D }, +{ 0xCF1E, 0xCF1E, 0xCF1E }, +{ 0xCF1F, 0xCF1F, 0xCF1F }, +{ 0xCF20, 0xCF20, 0xCF20 }, +{ 0xCF21, 0xCF21, 0xCF21 }, +{ 0xCF22, 0xCF22, 0xCF22 }, +{ 0xCF23, 0xCF23, 0xCF23 }, +{ 0xCF24, 0xCF24, 0xCF24 }, +{ 0xCF25, 0xCF25, 0xCF25 }, +{ 0xCF26, 0xCF26, 0xCF26 }, +{ 0xCF27, 0xCF27, 0xCF27 }, +{ 0xCF28, 0xCF28, 0xCF28 }, +{ 0xCF29, 0xCF29, 0xCF29 }, +{ 0xCF2A, 0xCF2A, 0xCF2A }, +{ 0xCF2B, 0xCF2B, 0xCF2B }, +{ 0xCF2C, 0xCF2C, 0xCF2C }, +{ 0xCF2D, 0xCF2D, 0xCF2D }, +{ 0xCF2E, 0xCF2E, 0xCF2E }, +{ 0xCF2F, 0xCF2F, 0xCF2F }, +{ 0xCF30, 0xCF30, 0xCF30 }, +{ 0xCF31, 0xCF31, 0xCF31 }, +{ 0xCF32, 0xCF32, 0xCF32 }, +{ 0xCF33, 0xCF33, 0xCF33 }, +{ 0xCF34, 0xCF34, 0xCF34 }, +{ 0xCF35, 0xCF35, 0xCF35 }, +{ 0xCF36, 0xCF36, 0xCF36 }, +{ 0xCF37, 0xCF37, 0xCF37 }, +{ 0xCF38, 0xCF38, 0xCF38 }, +{ 0xCF39, 0xCF39, 0xCF39 }, +{ 0xCF3A, 0xCF3A, 0xCF3A }, +{ 0xCF3B, 0xCF3B, 0xCF3B }, +{ 0xCF3C, 0xCF3C, 0xCF3C }, +{ 0xCF3D, 0xCF3D, 0xCF3D }, +{ 0xCF3E, 0xCF3E, 0xCF3E }, +{ 0xCF3F, 0xCF3F, 0xCF3F }, +{ 0xCF40, 0xCF40, 0xCF40 }, +{ 0xCF41, 0xCF41, 0xCF41 }, +{ 0xCF42, 0xCF42, 0xCF42 }, +{ 0xCF43, 0xCF43, 0xCF43 }, +{ 0xCF44, 0xCF44, 0xCF44 }, +{ 0xCF45, 0xCF45, 0xCF45 }, +{ 0xCF46, 0xCF46, 0xCF46 }, +{ 0xCF47, 0xCF47, 0xCF47 }, +{ 0xCF48, 0xCF48, 0xCF48 }, +{ 0xCF49, 0xCF49, 0xCF49 }, +{ 0xCF4A, 0xCF4A, 0xCF4A }, +{ 0xCF4B, 0xCF4B, 0xCF4B }, +{ 0xCF4C, 0xCF4C, 0xCF4C }, +{ 0xCF4D, 0xCF4D, 0xCF4D }, +{ 0xCF4E, 0xCF4E, 0xCF4E }, +{ 0xCF4F, 0xCF4F, 0xCF4F }, +{ 0xCF50, 0xCF50, 0xCF50 }, +{ 0xCF51, 0xCF51, 0xCF51 }, +{ 0xCF52, 0xCF52, 0xCF52 }, +{ 0xCF53, 0xCF53, 0xCF53 }, +{ 0xCF54, 0xCF54, 0xCF54 }, +{ 0xCF55, 0xCF55, 0xCF55 }, +{ 0xCF56, 0xCF56, 0xCF56 }, +{ 0xCF57, 0xCF57, 0xCF57 }, +{ 0xCF58, 0xCF58, 0xCF58 }, +{ 0xCF59, 0xCF59, 0xCF59 }, +{ 0xCF5A, 0xCF5A, 0xCF5A }, +{ 0xCF5B, 0xCF5B, 0xCF5B }, +{ 0xCF5C, 0xCF5C, 0xCF5C }, +{ 0xCF5D, 0xCF5D, 0xCF5D }, +{ 0xCF5E, 0xCF5E, 0xCF5E }, +{ 0xCF5F, 0xCF5F, 0xCF5F }, +{ 0xCF60, 0xCF60, 0xCF60 }, +{ 0xCF61, 0xCF61, 0xCF61 }, +{ 0xCF62, 0xCF62, 0xCF62 }, +{ 0xCF63, 0xCF63, 0xCF63 }, +{ 0xCF64, 0xCF64, 0xCF64 }, +{ 0xCF65, 0xCF65, 0xCF65 }, +{ 0xCF66, 0xCF66, 0xCF66 }, +{ 0xCF67, 0xCF67, 0xCF67 }, +{ 0xCF68, 0xCF68, 0xCF68 }, +{ 0xCF69, 0xCF69, 0xCF69 }, +{ 0xCF6A, 0xCF6A, 0xCF6A }, +{ 0xCF6B, 0xCF6B, 0xCF6B }, +{ 0xCF6C, 0xCF6C, 0xCF6C }, +{ 0xCF6D, 0xCF6D, 0xCF6D }, +{ 0xCF6E, 0xCF6E, 0xCF6E }, +{ 0xCF6F, 0xCF6F, 0xCF6F }, +{ 0xCF70, 0xCF70, 0xCF70 }, +{ 0xCF71, 0xCF71, 0xCF71 }, +{ 0xCF72, 0xCF72, 0xCF72 }, +{ 0xCF73, 0xCF73, 0xCF73 }, +{ 0xCF74, 0xCF74, 0xCF74 }, +{ 0xCF75, 0xCF75, 0xCF75 }, +{ 0xCF76, 0xCF76, 0xCF76 }, +{ 0xCF77, 0xCF77, 0xCF77 }, +{ 0xCF78, 0xCF78, 0xCF78 }, +{ 0xCF79, 0xCF79, 0xCF79 }, +{ 0xCF7A, 0xCF7A, 0xCF7A }, +{ 0xCF7B, 0xCF7B, 0xCF7B }, +{ 0xCF7C, 0xCF7C, 0xCF7C }, +{ 0xCF7D, 0xCF7D, 0xCF7D }, +{ 0xCF7E, 0xCF7E, 0xCF7E }, +{ 0xCF7F, 0xCF7F, 0xCF7F }, +{ 0xCF80, 0xCF80, 0xCF80 }, +{ 0xCF81, 0xCF81, 0xCF81 }, +{ 0xCF82, 0xCF82, 0xCF82 }, +{ 0xCF83, 0xCF83, 0xCF83 }, +{ 0xCF84, 0xCF84, 0xCF84 }, +{ 0xCF85, 0xCF85, 0xCF85 }, +{ 0xCF86, 0xCF86, 0xCF86 }, +{ 0xCF87, 0xCF87, 0xCF87 }, +{ 0xCF88, 0xCF88, 0xCF88 }, +{ 0xCF89, 0xCF89, 0xCF89 }, +{ 0xCF8A, 0xCF8A, 0xCF8A }, +{ 0xCF8B, 0xCF8B, 0xCF8B }, +{ 0xCF8C, 0xCF8C, 0xCF8C }, +{ 0xCF8D, 0xCF8D, 0xCF8D }, +{ 0xCF8E, 0xCF8E, 0xCF8E }, +{ 0xCF8F, 0xCF8F, 0xCF8F }, +{ 0xCF90, 0xCF90, 0xCF90 }, +{ 0xCF91, 0xCF91, 0xCF91 }, +{ 0xCF92, 0xCF92, 0xCF92 }, +{ 0xCF93, 0xCF93, 0xCF93 }, +{ 0xCF94, 0xCF94, 0xCF94 }, +{ 0xCF95, 0xCF95, 0xCF95 }, +{ 0xCF96, 0xCF96, 0xCF96 }, +{ 0xCF97, 0xCF97, 0xCF97 }, +{ 0xCF98, 0xCF98, 0xCF98 }, +{ 0xCF99, 0xCF99, 0xCF99 }, +{ 0xCF9A, 0xCF9A, 0xCF9A }, +{ 0xCF9B, 0xCF9B, 0xCF9B }, +{ 0xCF9C, 0xCF9C, 0xCF9C }, +{ 0xCF9D, 0xCF9D, 0xCF9D }, +{ 0xCF9E, 0xCF9E, 0xCF9E }, +{ 0xCF9F, 0xCF9F, 0xCF9F }, +{ 0xCFA0, 0xCFA0, 0xCFA0 }, +{ 0xCFA1, 0xCFA1, 0xCFA1 }, +{ 0xCFA2, 0xCFA2, 0xCFA2 }, +{ 0xCFA3, 0xCFA3, 0xCFA3 }, +{ 0xCFA4, 0xCFA4, 0xCFA4 }, +{ 0xCFA5, 0xCFA5, 0xCFA5 }, +{ 0xCFA6, 0xCFA6, 0xCFA6 }, +{ 0xCFA7, 0xCFA7, 0xCFA7 }, +{ 0xCFA8, 0xCFA8, 0xCFA8 }, +{ 0xCFA9, 0xCFA9, 0xCFA9 }, +{ 0xCFAA, 0xCFAA, 0xCFAA }, +{ 0xCFAB, 0xCFAB, 0xCFAB }, +{ 0xCFAC, 0xCFAC, 0xCFAC }, +{ 0xCFAD, 0xCFAD, 0xCFAD }, +{ 0xCFAE, 0xCFAE, 0xCFAE }, +{ 0xCFAF, 0xCFAF, 0xCFAF }, +{ 0xCFB0, 0xCFB0, 0xCFB0 }, +{ 0xCFB1, 0xCFB1, 0xCFB1 }, +{ 0xCFB2, 0xCFB2, 0xCFB2 }, +{ 0xCFB3, 0xCFB3, 0xCFB3 }, +{ 0xCFB4, 0xCFB4, 0xCFB4 }, +{ 0xCFB5, 0xCFB5, 0xCFB5 }, +{ 0xCFB6, 0xCFB6, 0xCFB6 }, +{ 0xCFB7, 0xCFB7, 0xCFB7 }, +{ 0xCFB8, 0xCFB8, 0xCFB8 }, +{ 0xCFB9, 0xCFB9, 0xCFB9 }, +{ 0xCFBA, 0xCFBA, 0xCFBA }, +{ 0xCFBB, 0xCFBB, 0xCFBB }, +{ 0xCFBC, 0xCFBC, 0xCFBC }, +{ 0xCFBD, 0xCFBD, 0xCFBD }, +{ 0xCFBE, 0xCFBE, 0xCFBE }, +{ 0xCFBF, 0xCFBF, 0xCFBF }, +{ 0xCFC0, 0xCFC0, 0xCFC0 }, +{ 0xCFC1, 0xCFC1, 0xCFC1 }, +{ 0xCFC2, 0xCFC2, 0xCFC2 }, +{ 0xCFC3, 0xCFC3, 0xCFC3 }, +{ 0xCFC4, 0xCFC4, 0xCFC4 }, +{ 0xCFC5, 0xCFC5, 0xCFC5 }, +{ 0xCFC6, 0xCFC6, 0xCFC6 }, +{ 0xCFC7, 0xCFC7, 0xCFC7 }, +{ 0xCFC8, 0xCFC8, 0xCFC8 }, +{ 0xCFC9, 0xCFC9, 0xCFC9 }, +{ 0xCFCA, 0xCFCA, 0xCFCA }, +{ 0xCFCB, 0xCFCB, 0xCFCB }, +{ 0xCFCC, 0xCFCC, 0xCFCC }, +{ 0xCFCD, 0xCFCD, 0xCFCD }, +{ 0xCFCE, 0xCFCE, 0xCFCE }, +{ 0xCFCF, 0xCFCF, 0xCFCF }, +{ 0xCFD0, 0xCFD0, 0xCFD0 }, +{ 0xCFD1, 0xCFD1, 0xCFD1 }, +{ 0xCFD2, 0xCFD2, 0xCFD2 }, +{ 0xCFD3, 0xCFD3, 0xCFD3 }, +{ 0xCFD4, 0xCFD4, 0xCFD4 }, +{ 0xCFD5, 0xCFD5, 0xCFD5 }, +{ 0xCFD6, 0xCFD6, 0xCFD6 }, +{ 0xCFD7, 0xCFD7, 0xCFD7 }, +{ 0xCFD8, 0xCFD8, 0xCFD8 }, +{ 0xCFD9, 0xCFD9, 0xCFD9 }, +{ 0xCFDA, 0xCFDA, 0xCFDA }, +{ 0xCFDB, 0xCFDB, 0xCFDB }, +{ 0xCFDC, 0xCFDC, 0xCFDC }, +{ 0xCFDD, 0xCFDD, 0xCFDD }, +{ 0xCFDE, 0xCFDE, 0xCFDE }, +{ 0xCFDF, 0xCFDF, 0xCFDF }, +{ 0xCFE0, 0xCFE0, 0xCFE0 }, +{ 0xCFE1, 0xCFE1, 0xCFE1 }, +{ 0xCFE2, 0xCFE2, 0xCFE2 }, +{ 0xCFE3, 0xCFE3, 0xCFE3 }, +{ 0xCFE4, 0xCFE4, 0xCFE4 }, +{ 0xCFE5, 0xCFE5, 0xCFE5 }, +{ 0xCFE6, 0xCFE6, 0xCFE6 }, +{ 0xCFE7, 0xCFE7, 0xCFE7 }, +{ 0xCFE8, 0xCFE8, 0xCFE8 }, +{ 0xCFE9, 0xCFE9, 0xCFE9 }, +{ 0xCFEA, 0xCFEA, 0xCFEA }, +{ 0xCFEB, 0xCFEB, 0xCFEB }, +{ 0xCFEC, 0xCFEC, 0xCFEC }, +{ 0xCFED, 0xCFED, 0xCFED }, +{ 0xCFEE, 0xCFEE, 0xCFEE }, +{ 0xCFEF, 0xCFEF, 0xCFEF }, +{ 0xCFF0, 0xCFF0, 0xCFF0 }, +{ 0xCFF1, 0xCFF1, 0xCFF1 }, +{ 0xCFF2, 0xCFF2, 0xCFF2 }, +{ 0xCFF3, 0xCFF3, 0xCFF3 }, +{ 0xCFF4, 0xCFF4, 0xCFF4 }, +{ 0xCFF5, 0xCFF5, 0xCFF5 }, +{ 0xCFF6, 0xCFF6, 0xCFF6 }, +{ 0xCFF7, 0xCFF7, 0xCFF7 }, +{ 0xCFF8, 0xCFF8, 0xCFF8 }, +{ 0xCFF9, 0xCFF9, 0xCFF9 }, +{ 0xCFFA, 0xCFFA, 0xCFFA }, +{ 0xCFFB, 0xCFFB, 0xCFFB }, +{ 0xCFFC, 0xCFFC, 0xCFFC }, +{ 0xCFFD, 0xCFFD, 0xCFFD }, +{ 0xCFFE, 0xCFFE, 0xCFFE }, +{ 0xCFFF, 0xCFFF, 0xCFFF }, +{ 0xD000, 0xD000, 0xD000 }, +{ 0xD001, 0xD001, 0xD001 }, +{ 0xD002, 0xD002, 0xD002 }, +{ 0xD003, 0xD003, 0xD003 }, +{ 0xD004, 0xD004, 0xD004 }, +{ 0xD005, 0xD005, 0xD005 }, +{ 0xD006, 0xD006, 0xD006 }, +{ 0xD007, 0xD007, 0xD007 }, +{ 0xD008, 0xD008, 0xD008 }, +{ 0xD009, 0xD009, 0xD009 }, +{ 0xD00A, 0xD00A, 0xD00A }, +{ 0xD00B, 0xD00B, 0xD00B }, +{ 0xD00C, 0xD00C, 0xD00C }, +{ 0xD00D, 0xD00D, 0xD00D }, +{ 0xD00E, 0xD00E, 0xD00E }, +{ 0xD00F, 0xD00F, 0xD00F }, +{ 0xD010, 0xD010, 0xD010 }, +{ 0xD011, 0xD011, 0xD011 }, +{ 0xD012, 0xD012, 0xD012 }, +{ 0xD013, 0xD013, 0xD013 }, +{ 0xD014, 0xD014, 0xD014 }, +{ 0xD015, 0xD015, 0xD015 }, +{ 0xD016, 0xD016, 0xD016 }, +{ 0xD017, 0xD017, 0xD017 }, +{ 0xD018, 0xD018, 0xD018 }, +{ 0xD019, 0xD019, 0xD019 }, +{ 0xD01A, 0xD01A, 0xD01A }, +{ 0xD01B, 0xD01B, 0xD01B }, +{ 0xD01C, 0xD01C, 0xD01C }, +{ 0xD01D, 0xD01D, 0xD01D }, +{ 0xD01E, 0xD01E, 0xD01E }, +{ 0xD01F, 0xD01F, 0xD01F }, +{ 0xD020, 0xD020, 0xD020 }, +{ 0xD021, 0xD021, 0xD021 }, +{ 0xD022, 0xD022, 0xD022 }, +{ 0xD023, 0xD023, 0xD023 }, +{ 0xD024, 0xD024, 0xD024 }, +{ 0xD025, 0xD025, 0xD025 }, +{ 0xD026, 0xD026, 0xD026 }, +{ 0xD027, 0xD027, 0xD027 }, +{ 0xD028, 0xD028, 0xD028 }, +{ 0xD029, 0xD029, 0xD029 }, +{ 0xD02A, 0xD02A, 0xD02A }, +{ 0xD02B, 0xD02B, 0xD02B }, +{ 0xD02C, 0xD02C, 0xD02C }, +{ 0xD02D, 0xD02D, 0xD02D }, +{ 0xD02E, 0xD02E, 0xD02E }, +{ 0xD02F, 0xD02F, 0xD02F }, +{ 0xD030, 0xD030, 0xD030 }, +{ 0xD031, 0xD031, 0xD031 }, +{ 0xD032, 0xD032, 0xD032 }, +{ 0xD033, 0xD033, 0xD033 }, +{ 0xD034, 0xD034, 0xD034 }, +{ 0xD035, 0xD035, 0xD035 }, +{ 0xD036, 0xD036, 0xD036 }, +{ 0xD037, 0xD037, 0xD037 }, +{ 0xD038, 0xD038, 0xD038 }, +{ 0xD039, 0xD039, 0xD039 }, +{ 0xD03A, 0xD03A, 0xD03A }, +{ 0xD03B, 0xD03B, 0xD03B }, +{ 0xD03C, 0xD03C, 0xD03C }, +{ 0xD03D, 0xD03D, 0xD03D }, +{ 0xD03E, 0xD03E, 0xD03E }, +{ 0xD03F, 0xD03F, 0xD03F }, +{ 0xD040, 0xD040, 0xD040 }, +{ 0xD041, 0xD041, 0xD041 }, +{ 0xD042, 0xD042, 0xD042 }, +{ 0xD043, 0xD043, 0xD043 }, +{ 0xD044, 0xD044, 0xD044 }, +{ 0xD045, 0xD045, 0xD045 }, +{ 0xD046, 0xD046, 0xD046 }, +{ 0xD047, 0xD047, 0xD047 }, +{ 0xD048, 0xD048, 0xD048 }, +{ 0xD049, 0xD049, 0xD049 }, +{ 0xD04A, 0xD04A, 0xD04A }, +{ 0xD04B, 0xD04B, 0xD04B }, +{ 0xD04C, 0xD04C, 0xD04C }, +{ 0xD04D, 0xD04D, 0xD04D }, +{ 0xD04E, 0xD04E, 0xD04E }, +{ 0xD04F, 0xD04F, 0xD04F }, +{ 0xD050, 0xD050, 0xD050 }, +{ 0xD051, 0xD051, 0xD051 }, +{ 0xD052, 0xD052, 0xD052 }, +{ 0xD053, 0xD053, 0xD053 }, +{ 0xD054, 0xD054, 0xD054 }, +{ 0xD055, 0xD055, 0xD055 }, +{ 0xD056, 0xD056, 0xD056 }, +{ 0xD057, 0xD057, 0xD057 }, +{ 0xD058, 0xD058, 0xD058 }, +{ 0xD059, 0xD059, 0xD059 }, +{ 0xD05A, 0xD05A, 0xD05A }, +{ 0xD05B, 0xD05B, 0xD05B }, +{ 0xD05C, 0xD05C, 0xD05C }, +{ 0xD05D, 0xD05D, 0xD05D }, +{ 0xD05E, 0xD05E, 0xD05E }, +{ 0xD05F, 0xD05F, 0xD05F }, +{ 0xD060, 0xD060, 0xD060 }, +{ 0xD061, 0xD061, 0xD061 }, +{ 0xD062, 0xD062, 0xD062 }, +{ 0xD063, 0xD063, 0xD063 }, +{ 0xD064, 0xD064, 0xD064 }, +{ 0xD065, 0xD065, 0xD065 }, +{ 0xD066, 0xD066, 0xD066 }, +{ 0xD067, 0xD067, 0xD067 }, +{ 0xD068, 0xD068, 0xD068 }, +{ 0xD069, 0xD069, 0xD069 }, +{ 0xD06A, 0xD06A, 0xD06A }, +{ 0xD06B, 0xD06B, 0xD06B }, +{ 0xD06C, 0xD06C, 0xD06C }, +{ 0xD06D, 0xD06D, 0xD06D }, +{ 0xD06E, 0xD06E, 0xD06E }, +{ 0xD06F, 0xD06F, 0xD06F }, +{ 0xD070, 0xD070, 0xD070 }, +{ 0xD071, 0xD071, 0xD071 }, +{ 0xD072, 0xD072, 0xD072 }, +{ 0xD073, 0xD073, 0xD073 }, +{ 0xD074, 0xD074, 0xD074 }, +{ 0xD075, 0xD075, 0xD075 }, +{ 0xD076, 0xD076, 0xD076 }, +{ 0xD077, 0xD077, 0xD077 }, +{ 0xD078, 0xD078, 0xD078 }, +{ 0xD079, 0xD079, 0xD079 }, +{ 0xD07A, 0xD07A, 0xD07A }, +{ 0xD07B, 0xD07B, 0xD07B }, +{ 0xD07C, 0xD07C, 0xD07C }, +{ 0xD07D, 0xD07D, 0xD07D }, +{ 0xD07E, 0xD07E, 0xD07E }, +{ 0xD07F, 0xD07F, 0xD07F }, +{ 0xD080, 0xD080, 0xD080 }, +{ 0xD081, 0xD081, 0xD081 }, +{ 0xD082, 0xD082, 0xD082 }, +{ 0xD083, 0xD083, 0xD083 }, +{ 0xD084, 0xD084, 0xD084 }, +{ 0xD085, 0xD085, 0xD085 }, +{ 0xD086, 0xD086, 0xD086 }, +{ 0xD087, 0xD087, 0xD087 }, +{ 0xD088, 0xD088, 0xD088 }, +{ 0xD089, 0xD089, 0xD089 }, +{ 0xD08A, 0xD08A, 0xD08A }, +{ 0xD08B, 0xD08B, 0xD08B }, +{ 0xD08C, 0xD08C, 0xD08C }, +{ 0xD08D, 0xD08D, 0xD08D }, +{ 0xD08E, 0xD08E, 0xD08E }, +{ 0xD08F, 0xD08F, 0xD08F }, +{ 0xD090, 0xD090, 0xD090 }, +{ 0xD091, 0xD091, 0xD091 }, +{ 0xD092, 0xD092, 0xD092 }, +{ 0xD093, 0xD093, 0xD093 }, +{ 0xD094, 0xD094, 0xD094 }, +{ 0xD095, 0xD095, 0xD095 }, +{ 0xD096, 0xD096, 0xD096 }, +{ 0xD097, 0xD097, 0xD097 }, +{ 0xD098, 0xD098, 0xD098 }, +{ 0xD099, 0xD099, 0xD099 }, +{ 0xD09A, 0xD09A, 0xD09A }, +{ 0xD09B, 0xD09B, 0xD09B }, +{ 0xD09C, 0xD09C, 0xD09C }, +{ 0xD09D, 0xD09D, 0xD09D }, +{ 0xD09E, 0xD09E, 0xD09E }, +{ 0xD09F, 0xD09F, 0xD09F }, +{ 0xD0A0, 0xD0A0, 0xD0A0 }, +{ 0xD0A1, 0xD0A1, 0xD0A1 }, +{ 0xD0A2, 0xD0A2, 0xD0A2 }, +{ 0xD0A3, 0xD0A3, 0xD0A3 }, +{ 0xD0A4, 0xD0A4, 0xD0A4 }, +{ 0xD0A5, 0xD0A5, 0xD0A5 }, +{ 0xD0A6, 0xD0A6, 0xD0A6 }, +{ 0xD0A7, 0xD0A7, 0xD0A7 }, +{ 0xD0A8, 0xD0A8, 0xD0A8 }, +{ 0xD0A9, 0xD0A9, 0xD0A9 }, +{ 0xD0AA, 0xD0AA, 0xD0AA }, +{ 0xD0AB, 0xD0AB, 0xD0AB }, +{ 0xD0AC, 0xD0AC, 0xD0AC }, +{ 0xD0AD, 0xD0AD, 0xD0AD }, +{ 0xD0AE, 0xD0AE, 0xD0AE }, +{ 0xD0AF, 0xD0AF, 0xD0AF }, +{ 0xD0B0, 0xD0B0, 0xD0B0 }, +{ 0xD0B1, 0xD0B1, 0xD0B1 }, +{ 0xD0B2, 0xD0B2, 0xD0B2 }, +{ 0xD0B3, 0xD0B3, 0xD0B3 }, +{ 0xD0B4, 0xD0B4, 0xD0B4 }, +{ 0xD0B5, 0xD0B5, 0xD0B5 }, +{ 0xD0B6, 0xD0B6, 0xD0B6 }, +{ 0xD0B7, 0xD0B7, 0xD0B7 }, +{ 0xD0B8, 0xD0B8, 0xD0B8 }, +{ 0xD0B9, 0xD0B9, 0xD0B9 }, +{ 0xD0BA, 0xD0BA, 0xD0BA }, +{ 0xD0BB, 0xD0BB, 0xD0BB }, +{ 0xD0BC, 0xD0BC, 0xD0BC }, +{ 0xD0BD, 0xD0BD, 0xD0BD }, +{ 0xD0BE, 0xD0BE, 0xD0BE }, +{ 0xD0BF, 0xD0BF, 0xD0BF }, +{ 0xD0C0, 0xD0C0, 0xD0C0 }, +{ 0xD0C1, 0xD0C1, 0xD0C1 }, +{ 0xD0C2, 0xD0C2, 0xD0C2 }, +{ 0xD0C3, 0xD0C3, 0xD0C3 }, +{ 0xD0C4, 0xD0C4, 0xD0C4 }, +{ 0xD0C5, 0xD0C5, 0xD0C5 }, +{ 0xD0C6, 0xD0C6, 0xD0C6 }, +{ 0xD0C7, 0xD0C7, 0xD0C7 }, +{ 0xD0C8, 0xD0C8, 0xD0C8 }, +{ 0xD0C9, 0xD0C9, 0xD0C9 }, +{ 0xD0CA, 0xD0CA, 0xD0CA }, +{ 0xD0CB, 0xD0CB, 0xD0CB }, +{ 0xD0CC, 0xD0CC, 0xD0CC }, +{ 0xD0CD, 0xD0CD, 0xD0CD }, +{ 0xD0CE, 0xD0CE, 0xD0CE }, +{ 0xD0CF, 0xD0CF, 0xD0CF }, +{ 0xD0D0, 0xD0D0, 0xD0D0 }, +{ 0xD0D1, 0xD0D1, 0xD0D1 }, +{ 0xD0D2, 0xD0D2, 0xD0D2 }, +{ 0xD0D3, 0xD0D3, 0xD0D3 }, +{ 0xD0D4, 0xD0D4, 0xD0D4 }, +{ 0xD0D5, 0xD0D5, 0xD0D5 }, +{ 0xD0D6, 0xD0D6, 0xD0D6 }, +{ 0xD0D7, 0xD0D7, 0xD0D7 }, +{ 0xD0D8, 0xD0D8, 0xD0D8 }, +{ 0xD0D9, 0xD0D9, 0xD0D9 }, +{ 0xD0DA, 0xD0DA, 0xD0DA }, +{ 0xD0DB, 0xD0DB, 0xD0DB }, +{ 0xD0DC, 0xD0DC, 0xD0DC }, +{ 0xD0DD, 0xD0DD, 0xD0DD }, +{ 0xD0DE, 0xD0DE, 0xD0DE }, +{ 0xD0DF, 0xD0DF, 0xD0DF }, +{ 0xD0E0, 0xD0E0, 0xD0E0 }, +{ 0xD0E1, 0xD0E1, 0xD0E1 }, +{ 0xD0E2, 0xD0E2, 0xD0E2 }, +{ 0xD0E3, 0xD0E3, 0xD0E3 }, +{ 0xD0E4, 0xD0E4, 0xD0E4 }, +{ 0xD0E5, 0xD0E5, 0xD0E5 }, +{ 0xD0E6, 0xD0E6, 0xD0E6 }, +{ 0xD0E7, 0xD0E7, 0xD0E7 }, +{ 0xD0E8, 0xD0E8, 0xD0E8 }, +{ 0xD0E9, 0xD0E9, 0xD0E9 }, +{ 0xD0EA, 0xD0EA, 0xD0EA }, +{ 0xD0EB, 0xD0EB, 0xD0EB }, +{ 0xD0EC, 0xD0EC, 0xD0EC }, +{ 0xD0ED, 0xD0ED, 0xD0ED }, +{ 0xD0EE, 0xD0EE, 0xD0EE }, +{ 0xD0EF, 0xD0EF, 0xD0EF }, +{ 0xD0F0, 0xD0F0, 0xD0F0 }, +{ 0xD0F1, 0xD0F1, 0xD0F1 }, +{ 0xD0F2, 0xD0F2, 0xD0F2 }, +{ 0xD0F3, 0xD0F3, 0xD0F3 }, +{ 0xD0F4, 0xD0F4, 0xD0F4 }, +{ 0xD0F5, 0xD0F5, 0xD0F5 }, +{ 0xD0F6, 0xD0F6, 0xD0F6 }, +{ 0xD0F7, 0xD0F7, 0xD0F7 }, +{ 0xD0F8, 0xD0F8, 0xD0F8 }, +{ 0xD0F9, 0xD0F9, 0xD0F9 }, +{ 0xD0FA, 0xD0FA, 0xD0FA }, +{ 0xD0FB, 0xD0FB, 0xD0FB }, +{ 0xD0FC, 0xD0FC, 0xD0FC }, +{ 0xD0FD, 0xD0FD, 0xD0FD }, +{ 0xD0FE, 0xD0FE, 0xD0FE }, +{ 0xD0FF, 0xD0FF, 0xD0FF }, +{ 0xD100, 0xD100, 0xD100 }, +{ 0xD101, 0xD101, 0xD101 }, +{ 0xD102, 0xD102, 0xD102 }, +{ 0xD103, 0xD103, 0xD103 }, +{ 0xD104, 0xD104, 0xD104 }, +{ 0xD105, 0xD105, 0xD105 }, +{ 0xD106, 0xD106, 0xD106 }, +{ 0xD107, 0xD107, 0xD107 }, +{ 0xD108, 0xD108, 0xD108 }, +{ 0xD109, 0xD109, 0xD109 }, +{ 0xD10A, 0xD10A, 0xD10A }, +{ 0xD10B, 0xD10B, 0xD10B }, +{ 0xD10C, 0xD10C, 0xD10C }, +{ 0xD10D, 0xD10D, 0xD10D }, +{ 0xD10E, 0xD10E, 0xD10E }, +{ 0xD10F, 0xD10F, 0xD10F }, +{ 0xD110, 0xD110, 0xD110 }, +{ 0xD111, 0xD111, 0xD111 }, +{ 0xD112, 0xD112, 0xD112 }, +{ 0xD113, 0xD113, 0xD113 }, +{ 0xD114, 0xD114, 0xD114 }, +{ 0xD115, 0xD115, 0xD115 }, +{ 0xD116, 0xD116, 0xD116 }, +{ 0xD117, 0xD117, 0xD117 }, +{ 0xD118, 0xD118, 0xD118 }, +{ 0xD119, 0xD119, 0xD119 }, +{ 0xD11A, 0xD11A, 0xD11A }, +{ 0xD11B, 0xD11B, 0xD11B }, +{ 0xD11C, 0xD11C, 0xD11C }, +{ 0xD11D, 0xD11D, 0xD11D }, +{ 0xD11E, 0xD11E, 0xD11E }, +{ 0xD11F, 0xD11F, 0xD11F }, +{ 0xD120, 0xD120, 0xD120 }, +{ 0xD121, 0xD121, 0xD121 }, +{ 0xD122, 0xD122, 0xD122 }, +{ 0xD123, 0xD123, 0xD123 }, +{ 0xD124, 0xD124, 0xD124 }, +{ 0xD125, 0xD125, 0xD125 }, +{ 0xD126, 0xD126, 0xD126 }, +{ 0xD127, 0xD127, 0xD127 }, +{ 0xD128, 0xD128, 0xD128 }, +{ 0xD129, 0xD129, 0xD129 }, +{ 0xD12A, 0xD12A, 0xD12A }, +{ 0xD12B, 0xD12B, 0xD12B }, +{ 0xD12C, 0xD12C, 0xD12C }, +{ 0xD12D, 0xD12D, 0xD12D }, +{ 0xD12E, 0xD12E, 0xD12E }, +{ 0xD12F, 0xD12F, 0xD12F }, +{ 0xD130, 0xD130, 0xD130 }, +{ 0xD131, 0xD131, 0xD131 }, +{ 0xD132, 0xD132, 0xD132 }, +{ 0xD133, 0xD133, 0xD133 }, +{ 0xD134, 0xD134, 0xD134 }, +{ 0xD135, 0xD135, 0xD135 }, +{ 0xD136, 0xD136, 0xD136 }, +{ 0xD137, 0xD137, 0xD137 }, +{ 0xD138, 0xD138, 0xD138 }, +{ 0xD139, 0xD139, 0xD139 }, +{ 0xD13A, 0xD13A, 0xD13A }, +{ 0xD13B, 0xD13B, 0xD13B }, +{ 0xD13C, 0xD13C, 0xD13C }, +{ 0xD13D, 0xD13D, 0xD13D }, +{ 0xD13E, 0xD13E, 0xD13E }, +{ 0xD13F, 0xD13F, 0xD13F }, +{ 0xD140, 0xD140, 0xD140 }, +{ 0xD141, 0xD141, 0xD141 }, +{ 0xD142, 0xD142, 0xD142 }, +{ 0xD143, 0xD143, 0xD143 }, +{ 0xD144, 0xD144, 0xD144 }, +{ 0xD145, 0xD145, 0xD145 }, +{ 0xD146, 0xD146, 0xD146 }, +{ 0xD147, 0xD147, 0xD147 }, +{ 0xD148, 0xD148, 0xD148 }, +{ 0xD149, 0xD149, 0xD149 }, +{ 0xD14A, 0xD14A, 0xD14A }, +{ 0xD14B, 0xD14B, 0xD14B }, +{ 0xD14C, 0xD14C, 0xD14C }, +{ 0xD14D, 0xD14D, 0xD14D }, +{ 0xD14E, 0xD14E, 0xD14E }, +{ 0xD14F, 0xD14F, 0xD14F }, +{ 0xD150, 0xD150, 0xD150 }, +{ 0xD151, 0xD151, 0xD151 }, +{ 0xD152, 0xD152, 0xD152 }, +{ 0xD153, 0xD153, 0xD153 }, +{ 0xD154, 0xD154, 0xD154 }, +{ 0xD155, 0xD155, 0xD155 }, +{ 0xD156, 0xD156, 0xD156 }, +{ 0xD157, 0xD157, 0xD157 }, +{ 0xD158, 0xD158, 0xD158 }, +{ 0xD159, 0xD159, 0xD159 }, +{ 0xD15A, 0xD15A, 0xD15A }, +{ 0xD15B, 0xD15B, 0xD15B }, +{ 0xD15C, 0xD15C, 0xD15C }, +{ 0xD15D, 0xD15D, 0xD15D }, +{ 0xD15E, 0xD15E, 0xD15E }, +{ 0xD15F, 0xD15F, 0xD15F }, +{ 0xD160, 0xD160, 0xD160 }, +{ 0xD161, 0xD161, 0xD161 }, +{ 0xD162, 0xD162, 0xD162 }, +{ 0xD163, 0xD163, 0xD163 }, +{ 0xD164, 0xD164, 0xD164 }, +{ 0xD165, 0xD165, 0xD165 }, +{ 0xD166, 0xD166, 0xD166 }, +{ 0xD167, 0xD167, 0xD167 }, +{ 0xD168, 0xD168, 0xD168 }, +{ 0xD169, 0xD169, 0xD169 }, +{ 0xD16A, 0xD16A, 0xD16A }, +{ 0xD16B, 0xD16B, 0xD16B }, +{ 0xD16C, 0xD16C, 0xD16C }, +{ 0xD16D, 0xD16D, 0xD16D }, +{ 0xD16E, 0xD16E, 0xD16E }, +{ 0xD16F, 0xD16F, 0xD16F }, +{ 0xD170, 0xD170, 0xD170 }, +{ 0xD171, 0xD171, 0xD171 }, +{ 0xD172, 0xD172, 0xD172 }, +{ 0xD173, 0xD173, 0xD173 }, +{ 0xD174, 0xD174, 0xD174 }, +{ 0xD175, 0xD175, 0xD175 }, +{ 0xD176, 0xD176, 0xD176 }, +{ 0xD177, 0xD177, 0xD177 }, +{ 0xD178, 0xD178, 0xD178 }, +{ 0xD179, 0xD179, 0xD179 }, +{ 0xD17A, 0xD17A, 0xD17A }, +{ 0xD17B, 0xD17B, 0xD17B }, +{ 0xD17C, 0xD17C, 0xD17C }, +{ 0xD17D, 0xD17D, 0xD17D }, +{ 0xD17E, 0xD17E, 0xD17E }, +{ 0xD17F, 0xD17F, 0xD17F }, +{ 0xD180, 0xD180, 0xD180 }, +{ 0xD181, 0xD181, 0xD181 }, +{ 0xD182, 0xD182, 0xD182 }, +{ 0xD183, 0xD183, 0xD183 }, +{ 0xD184, 0xD184, 0xD184 }, +{ 0xD185, 0xD185, 0xD185 }, +{ 0xD186, 0xD186, 0xD186 }, +{ 0xD187, 0xD187, 0xD187 }, +{ 0xD188, 0xD188, 0xD188 }, +{ 0xD189, 0xD189, 0xD189 }, +{ 0xD18A, 0xD18A, 0xD18A }, +{ 0xD18B, 0xD18B, 0xD18B }, +{ 0xD18C, 0xD18C, 0xD18C }, +{ 0xD18D, 0xD18D, 0xD18D }, +{ 0xD18E, 0xD18E, 0xD18E }, +{ 0xD18F, 0xD18F, 0xD18F }, +{ 0xD190, 0xD190, 0xD190 }, +{ 0xD191, 0xD191, 0xD191 }, +{ 0xD192, 0xD192, 0xD192 }, +{ 0xD193, 0xD193, 0xD193 }, +{ 0xD194, 0xD194, 0xD194 }, +{ 0xD195, 0xD195, 0xD195 }, +{ 0xD196, 0xD196, 0xD196 }, +{ 0xD197, 0xD197, 0xD197 }, +{ 0xD198, 0xD198, 0xD198 }, +{ 0xD199, 0xD199, 0xD199 }, +{ 0xD19A, 0xD19A, 0xD19A }, +{ 0xD19B, 0xD19B, 0xD19B }, +{ 0xD19C, 0xD19C, 0xD19C }, +{ 0xD19D, 0xD19D, 0xD19D }, +{ 0xD19E, 0xD19E, 0xD19E }, +{ 0xD19F, 0xD19F, 0xD19F }, +{ 0xD1A0, 0xD1A0, 0xD1A0 }, +{ 0xD1A1, 0xD1A1, 0xD1A1 }, +{ 0xD1A2, 0xD1A2, 0xD1A2 }, +{ 0xD1A3, 0xD1A3, 0xD1A3 }, +{ 0xD1A4, 0xD1A4, 0xD1A4 }, +{ 0xD1A5, 0xD1A5, 0xD1A5 }, +{ 0xD1A6, 0xD1A6, 0xD1A6 }, +{ 0xD1A7, 0xD1A7, 0xD1A7 }, +{ 0xD1A8, 0xD1A8, 0xD1A8 }, +{ 0xD1A9, 0xD1A9, 0xD1A9 }, +{ 0xD1AA, 0xD1AA, 0xD1AA }, +{ 0xD1AB, 0xD1AB, 0xD1AB }, +{ 0xD1AC, 0xD1AC, 0xD1AC }, +{ 0xD1AD, 0xD1AD, 0xD1AD }, +{ 0xD1AE, 0xD1AE, 0xD1AE }, +{ 0xD1AF, 0xD1AF, 0xD1AF }, +{ 0xD1B0, 0xD1B0, 0xD1B0 }, +{ 0xD1B1, 0xD1B1, 0xD1B1 }, +{ 0xD1B2, 0xD1B2, 0xD1B2 }, +{ 0xD1B3, 0xD1B3, 0xD1B3 }, +{ 0xD1B4, 0xD1B4, 0xD1B4 }, +{ 0xD1B5, 0xD1B5, 0xD1B5 }, +{ 0xD1B6, 0xD1B6, 0xD1B6 }, +{ 0xD1B7, 0xD1B7, 0xD1B7 }, +{ 0xD1B8, 0xD1B8, 0xD1B8 }, +{ 0xD1B9, 0xD1B9, 0xD1B9 }, +{ 0xD1BA, 0xD1BA, 0xD1BA }, +{ 0xD1BB, 0xD1BB, 0xD1BB }, +{ 0xD1BC, 0xD1BC, 0xD1BC }, +{ 0xD1BD, 0xD1BD, 0xD1BD }, +{ 0xD1BE, 0xD1BE, 0xD1BE }, +{ 0xD1BF, 0xD1BF, 0xD1BF }, +{ 0xD1C0, 0xD1C0, 0xD1C0 }, +{ 0xD1C1, 0xD1C1, 0xD1C1 }, +{ 0xD1C2, 0xD1C2, 0xD1C2 }, +{ 0xD1C3, 0xD1C3, 0xD1C3 }, +{ 0xD1C4, 0xD1C4, 0xD1C4 }, +{ 0xD1C5, 0xD1C5, 0xD1C5 }, +{ 0xD1C6, 0xD1C6, 0xD1C6 }, +{ 0xD1C7, 0xD1C7, 0xD1C7 }, +{ 0xD1C8, 0xD1C8, 0xD1C8 }, +{ 0xD1C9, 0xD1C9, 0xD1C9 }, +{ 0xD1CA, 0xD1CA, 0xD1CA }, +{ 0xD1CB, 0xD1CB, 0xD1CB }, +{ 0xD1CC, 0xD1CC, 0xD1CC }, +{ 0xD1CD, 0xD1CD, 0xD1CD }, +{ 0xD1CE, 0xD1CE, 0xD1CE }, +{ 0xD1CF, 0xD1CF, 0xD1CF }, +{ 0xD1D0, 0xD1D0, 0xD1D0 }, +{ 0xD1D1, 0xD1D1, 0xD1D1 }, +{ 0xD1D2, 0xD1D2, 0xD1D2 }, +{ 0xD1D3, 0xD1D3, 0xD1D3 }, +{ 0xD1D4, 0xD1D4, 0xD1D4 }, +{ 0xD1D5, 0xD1D5, 0xD1D5 }, +{ 0xD1D6, 0xD1D6, 0xD1D6 }, +{ 0xD1D7, 0xD1D7, 0xD1D7 }, +{ 0xD1D8, 0xD1D8, 0xD1D8 }, +{ 0xD1D9, 0xD1D9, 0xD1D9 }, +{ 0xD1DA, 0xD1DA, 0xD1DA }, +{ 0xD1DB, 0xD1DB, 0xD1DB }, +{ 0xD1DC, 0xD1DC, 0xD1DC }, +{ 0xD1DD, 0xD1DD, 0xD1DD }, +{ 0xD1DE, 0xD1DE, 0xD1DE }, +{ 0xD1DF, 0xD1DF, 0xD1DF }, +{ 0xD1E0, 0xD1E0, 0xD1E0 }, +{ 0xD1E1, 0xD1E1, 0xD1E1 }, +{ 0xD1E2, 0xD1E2, 0xD1E2 }, +{ 0xD1E3, 0xD1E3, 0xD1E3 }, +{ 0xD1E4, 0xD1E4, 0xD1E4 }, +{ 0xD1E5, 0xD1E5, 0xD1E5 }, +{ 0xD1E6, 0xD1E6, 0xD1E6 }, +{ 0xD1E7, 0xD1E7, 0xD1E7 }, +{ 0xD1E8, 0xD1E8, 0xD1E8 }, +{ 0xD1E9, 0xD1E9, 0xD1E9 }, +{ 0xD1EA, 0xD1EA, 0xD1EA }, +{ 0xD1EB, 0xD1EB, 0xD1EB }, +{ 0xD1EC, 0xD1EC, 0xD1EC }, +{ 0xD1ED, 0xD1ED, 0xD1ED }, +{ 0xD1EE, 0xD1EE, 0xD1EE }, +{ 0xD1EF, 0xD1EF, 0xD1EF }, +{ 0xD1F0, 0xD1F0, 0xD1F0 }, +{ 0xD1F1, 0xD1F1, 0xD1F1 }, +{ 0xD1F2, 0xD1F2, 0xD1F2 }, +{ 0xD1F3, 0xD1F3, 0xD1F3 }, +{ 0xD1F4, 0xD1F4, 0xD1F4 }, +{ 0xD1F5, 0xD1F5, 0xD1F5 }, +{ 0xD1F6, 0xD1F6, 0xD1F6 }, +{ 0xD1F7, 0xD1F7, 0xD1F7 }, +{ 0xD1F8, 0xD1F8, 0xD1F8 }, +{ 0xD1F9, 0xD1F9, 0xD1F9 }, +{ 0xD1FA, 0xD1FA, 0xD1FA }, +{ 0xD1FB, 0xD1FB, 0xD1FB }, +{ 0xD1FC, 0xD1FC, 0xD1FC }, +{ 0xD1FD, 0xD1FD, 0xD1FD }, +{ 0xD1FE, 0xD1FE, 0xD1FE }, +{ 0xD1FF, 0xD1FF, 0xD1FF }, +{ 0xD200, 0xD200, 0xD200 }, +{ 0xD201, 0xD201, 0xD201 }, +{ 0xD202, 0xD202, 0xD202 }, +{ 0xD203, 0xD203, 0xD203 }, +{ 0xD204, 0xD204, 0xD204 }, +{ 0xD205, 0xD205, 0xD205 }, +{ 0xD206, 0xD206, 0xD206 }, +{ 0xD207, 0xD207, 0xD207 }, +{ 0xD208, 0xD208, 0xD208 }, +{ 0xD209, 0xD209, 0xD209 }, +{ 0xD20A, 0xD20A, 0xD20A }, +{ 0xD20B, 0xD20B, 0xD20B }, +{ 0xD20C, 0xD20C, 0xD20C }, +{ 0xD20D, 0xD20D, 0xD20D }, +{ 0xD20E, 0xD20E, 0xD20E }, +{ 0xD20F, 0xD20F, 0xD20F }, +{ 0xD210, 0xD210, 0xD210 }, +{ 0xD211, 0xD211, 0xD211 }, +{ 0xD212, 0xD212, 0xD212 }, +{ 0xD213, 0xD213, 0xD213 }, +{ 0xD214, 0xD214, 0xD214 }, +{ 0xD215, 0xD215, 0xD215 }, +{ 0xD216, 0xD216, 0xD216 }, +{ 0xD217, 0xD217, 0xD217 }, +{ 0xD218, 0xD218, 0xD218 }, +{ 0xD219, 0xD219, 0xD219 }, +{ 0xD21A, 0xD21A, 0xD21A }, +{ 0xD21B, 0xD21B, 0xD21B }, +{ 0xD21C, 0xD21C, 0xD21C }, +{ 0xD21D, 0xD21D, 0xD21D }, +{ 0xD21E, 0xD21E, 0xD21E }, +{ 0xD21F, 0xD21F, 0xD21F }, +{ 0xD220, 0xD220, 0xD220 }, +{ 0xD221, 0xD221, 0xD221 }, +{ 0xD222, 0xD222, 0xD222 }, +{ 0xD223, 0xD223, 0xD223 }, +{ 0xD224, 0xD224, 0xD224 }, +{ 0xD225, 0xD225, 0xD225 }, +{ 0xD226, 0xD226, 0xD226 }, +{ 0xD227, 0xD227, 0xD227 }, +{ 0xD228, 0xD228, 0xD228 }, +{ 0xD229, 0xD229, 0xD229 }, +{ 0xD22A, 0xD22A, 0xD22A }, +{ 0xD22B, 0xD22B, 0xD22B }, +{ 0xD22C, 0xD22C, 0xD22C }, +{ 0xD22D, 0xD22D, 0xD22D }, +{ 0xD22E, 0xD22E, 0xD22E }, +{ 0xD22F, 0xD22F, 0xD22F }, +{ 0xD230, 0xD230, 0xD230 }, +{ 0xD231, 0xD231, 0xD231 }, +{ 0xD232, 0xD232, 0xD232 }, +{ 0xD233, 0xD233, 0xD233 }, +{ 0xD234, 0xD234, 0xD234 }, +{ 0xD235, 0xD235, 0xD235 }, +{ 0xD236, 0xD236, 0xD236 }, +{ 0xD237, 0xD237, 0xD237 }, +{ 0xD238, 0xD238, 0xD238 }, +{ 0xD239, 0xD239, 0xD239 }, +{ 0xD23A, 0xD23A, 0xD23A }, +{ 0xD23B, 0xD23B, 0xD23B }, +{ 0xD23C, 0xD23C, 0xD23C }, +{ 0xD23D, 0xD23D, 0xD23D }, +{ 0xD23E, 0xD23E, 0xD23E }, +{ 0xD23F, 0xD23F, 0xD23F }, +{ 0xD240, 0xD240, 0xD240 }, +{ 0xD241, 0xD241, 0xD241 }, +{ 0xD242, 0xD242, 0xD242 }, +{ 0xD243, 0xD243, 0xD243 }, +{ 0xD244, 0xD244, 0xD244 }, +{ 0xD245, 0xD245, 0xD245 }, +{ 0xD246, 0xD246, 0xD246 }, +{ 0xD247, 0xD247, 0xD247 }, +{ 0xD248, 0xD248, 0xD248 }, +{ 0xD249, 0xD249, 0xD249 }, +{ 0xD24A, 0xD24A, 0xD24A }, +{ 0xD24B, 0xD24B, 0xD24B }, +{ 0xD24C, 0xD24C, 0xD24C }, +{ 0xD24D, 0xD24D, 0xD24D }, +{ 0xD24E, 0xD24E, 0xD24E }, +{ 0xD24F, 0xD24F, 0xD24F }, +{ 0xD250, 0xD250, 0xD250 }, +{ 0xD251, 0xD251, 0xD251 }, +{ 0xD252, 0xD252, 0xD252 }, +{ 0xD253, 0xD253, 0xD253 }, +{ 0xD254, 0xD254, 0xD254 }, +{ 0xD255, 0xD255, 0xD255 }, +{ 0xD256, 0xD256, 0xD256 }, +{ 0xD257, 0xD257, 0xD257 }, +{ 0xD258, 0xD258, 0xD258 }, +{ 0xD259, 0xD259, 0xD259 }, +{ 0xD25A, 0xD25A, 0xD25A }, +{ 0xD25B, 0xD25B, 0xD25B }, +{ 0xD25C, 0xD25C, 0xD25C }, +{ 0xD25D, 0xD25D, 0xD25D }, +{ 0xD25E, 0xD25E, 0xD25E }, +{ 0xD25F, 0xD25F, 0xD25F }, +{ 0xD260, 0xD260, 0xD260 }, +{ 0xD261, 0xD261, 0xD261 }, +{ 0xD262, 0xD262, 0xD262 }, +{ 0xD263, 0xD263, 0xD263 }, +{ 0xD264, 0xD264, 0xD264 }, +{ 0xD265, 0xD265, 0xD265 }, +{ 0xD266, 0xD266, 0xD266 }, +{ 0xD267, 0xD267, 0xD267 }, +{ 0xD268, 0xD268, 0xD268 }, +{ 0xD269, 0xD269, 0xD269 }, +{ 0xD26A, 0xD26A, 0xD26A }, +{ 0xD26B, 0xD26B, 0xD26B }, +{ 0xD26C, 0xD26C, 0xD26C }, +{ 0xD26D, 0xD26D, 0xD26D }, +{ 0xD26E, 0xD26E, 0xD26E }, +{ 0xD26F, 0xD26F, 0xD26F }, +{ 0xD270, 0xD270, 0xD270 }, +{ 0xD271, 0xD271, 0xD271 }, +{ 0xD272, 0xD272, 0xD272 }, +{ 0xD273, 0xD273, 0xD273 }, +{ 0xD274, 0xD274, 0xD274 }, +{ 0xD275, 0xD275, 0xD275 }, +{ 0xD276, 0xD276, 0xD276 }, +{ 0xD277, 0xD277, 0xD277 }, +{ 0xD278, 0xD278, 0xD278 }, +{ 0xD279, 0xD279, 0xD279 }, +{ 0xD27A, 0xD27A, 0xD27A }, +{ 0xD27B, 0xD27B, 0xD27B }, +{ 0xD27C, 0xD27C, 0xD27C }, +{ 0xD27D, 0xD27D, 0xD27D }, +{ 0xD27E, 0xD27E, 0xD27E }, +{ 0xD27F, 0xD27F, 0xD27F }, +{ 0xD280, 0xD280, 0xD280 }, +{ 0xD281, 0xD281, 0xD281 }, +{ 0xD282, 0xD282, 0xD282 }, +{ 0xD283, 0xD283, 0xD283 }, +{ 0xD284, 0xD284, 0xD284 }, +{ 0xD285, 0xD285, 0xD285 }, +{ 0xD286, 0xD286, 0xD286 }, +{ 0xD287, 0xD287, 0xD287 }, +{ 0xD288, 0xD288, 0xD288 }, +{ 0xD289, 0xD289, 0xD289 }, +{ 0xD28A, 0xD28A, 0xD28A }, +{ 0xD28B, 0xD28B, 0xD28B }, +{ 0xD28C, 0xD28C, 0xD28C }, +{ 0xD28D, 0xD28D, 0xD28D }, +{ 0xD28E, 0xD28E, 0xD28E }, +{ 0xD28F, 0xD28F, 0xD28F }, +{ 0xD290, 0xD290, 0xD290 }, +{ 0xD291, 0xD291, 0xD291 }, +{ 0xD292, 0xD292, 0xD292 }, +{ 0xD293, 0xD293, 0xD293 }, +{ 0xD294, 0xD294, 0xD294 }, +{ 0xD295, 0xD295, 0xD295 }, +{ 0xD296, 0xD296, 0xD296 }, +{ 0xD297, 0xD297, 0xD297 }, +{ 0xD298, 0xD298, 0xD298 }, +{ 0xD299, 0xD299, 0xD299 }, +{ 0xD29A, 0xD29A, 0xD29A }, +{ 0xD29B, 0xD29B, 0xD29B }, +{ 0xD29C, 0xD29C, 0xD29C }, +{ 0xD29D, 0xD29D, 0xD29D }, +{ 0xD29E, 0xD29E, 0xD29E }, +{ 0xD29F, 0xD29F, 0xD29F }, +{ 0xD2A0, 0xD2A0, 0xD2A0 }, +{ 0xD2A1, 0xD2A1, 0xD2A1 }, +{ 0xD2A2, 0xD2A2, 0xD2A2 }, +{ 0xD2A3, 0xD2A3, 0xD2A3 }, +{ 0xD2A4, 0xD2A4, 0xD2A4 }, +{ 0xD2A5, 0xD2A5, 0xD2A5 }, +{ 0xD2A6, 0xD2A6, 0xD2A6 }, +{ 0xD2A7, 0xD2A7, 0xD2A7 }, +{ 0xD2A8, 0xD2A8, 0xD2A8 }, +{ 0xD2A9, 0xD2A9, 0xD2A9 }, +{ 0xD2AA, 0xD2AA, 0xD2AA }, +{ 0xD2AB, 0xD2AB, 0xD2AB }, +{ 0xD2AC, 0xD2AC, 0xD2AC }, +{ 0xD2AD, 0xD2AD, 0xD2AD }, +{ 0xD2AE, 0xD2AE, 0xD2AE }, +{ 0xD2AF, 0xD2AF, 0xD2AF }, +{ 0xD2B0, 0xD2B0, 0xD2B0 }, +{ 0xD2B1, 0xD2B1, 0xD2B1 }, +{ 0xD2B2, 0xD2B2, 0xD2B2 }, +{ 0xD2B3, 0xD2B3, 0xD2B3 }, +{ 0xD2B4, 0xD2B4, 0xD2B4 }, +{ 0xD2B5, 0xD2B5, 0xD2B5 }, +{ 0xD2B6, 0xD2B6, 0xD2B6 }, +{ 0xD2B7, 0xD2B7, 0xD2B7 }, +{ 0xD2B8, 0xD2B8, 0xD2B8 }, +{ 0xD2B9, 0xD2B9, 0xD2B9 }, +{ 0xD2BA, 0xD2BA, 0xD2BA }, +{ 0xD2BB, 0xD2BB, 0xD2BB }, +{ 0xD2BC, 0xD2BC, 0xD2BC }, +{ 0xD2BD, 0xD2BD, 0xD2BD }, +{ 0xD2BE, 0xD2BE, 0xD2BE }, +{ 0xD2BF, 0xD2BF, 0xD2BF }, +{ 0xD2C0, 0xD2C0, 0xD2C0 }, +{ 0xD2C1, 0xD2C1, 0xD2C1 }, +{ 0xD2C2, 0xD2C2, 0xD2C2 }, +{ 0xD2C3, 0xD2C3, 0xD2C3 }, +{ 0xD2C4, 0xD2C4, 0xD2C4 }, +{ 0xD2C5, 0xD2C5, 0xD2C5 }, +{ 0xD2C6, 0xD2C6, 0xD2C6 }, +{ 0xD2C7, 0xD2C7, 0xD2C7 }, +{ 0xD2C8, 0xD2C8, 0xD2C8 }, +{ 0xD2C9, 0xD2C9, 0xD2C9 }, +{ 0xD2CA, 0xD2CA, 0xD2CA }, +{ 0xD2CB, 0xD2CB, 0xD2CB }, +{ 0xD2CC, 0xD2CC, 0xD2CC }, +{ 0xD2CD, 0xD2CD, 0xD2CD }, +{ 0xD2CE, 0xD2CE, 0xD2CE }, +{ 0xD2CF, 0xD2CF, 0xD2CF }, +{ 0xD2D0, 0xD2D0, 0xD2D0 }, +{ 0xD2D1, 0xD2D1, 0xD2D1 }, +{ 0xD2D2, 0xD2D2, 0xD2D2 }, +{ 0xD2D3, 0xD2D3, 0xD2D3 }, +{ 0xD2D4, 0xD2D4, 0xD2D4 }, +{ 0xD2D5, 0xD2D5, 0xD2D5 }, +{ 0xD2D6, 0xD2D6, 0xD2D6 }, +{ 0xD2D7, 0xD2D7, 0xD2D7 }, +{ 0xD2D8, 0xD2D8, 0xD2D8 }, +{ 0xD2D9, 0xD2D9, 0xD2D9 }, +{ 0xD2DA, 0xD2DA, 0xD2DA }, +{ 0xD2DB, 0xD2DB, 0xD2DB }, +{ 0xD2DC, 0xD2DC, 0xD2DC }, +{ 0xD2DD, 0xD2DD, 0xD2DD }, +{ 0xD2DE, 0xD2DE, 0xD2DE }, +{ 0xD2DF, 0xD2DF, 0xD2DF }, +{ 0xD2E0, 0xD2E0, 0xD2E0 }, +{ 0xD2E1, 0xD2E1, 0xD2E1 }, +{ 0xD2E2, 0xD2E2, 0xD2E2 }, +{ 0xD2E3, 0xD2E3, 0xD2E3 }, +{ 0xD2E4, 0xD2E4, 0xD2E4 }, +{ 0xD2E5, 0xD2E5, 0xD2E5 }, +{ 0xD2E6, 0xD2E6, 0xD2E6 }, +{ 0xD2E7, 0xD2E7, 0xD2E7 }, +{ 0xD2E8, 0xD2E8, 0xD2E8 }, +{ 0xD2E9, 0xD2E9, 0xD2E9 }, +{ 0xD2EA, 0xD2EA, 0xD2EA }, +{ 0xD2EB, 0xD2EB, 0xD2EB }, +{ 0xD2EC, 0xD2EC, 0xD2EC }, +{ 0xD2ED, 0xD2ED, 0xD2ED }, +{ 0xD2EE, 0xD2EE, 0xD2EE }, +{ 0xD2EF, 0xD2EF, 0xD2EF }, +{ 0xD2F0, 0xD2F0, 0xD2F0 }, +{ 0xD2F1, 0xD2F1, 0xD2F1 }, +{ 0xD2F2, 0xD2F2, 0xD2F2 }, +{ 0xD2F3, 0xD2F3, 0xD2F3 }, +{ 0xD2F4, 0xD2F4, 0xD2F4 }, +{ 0xD2F5, 0xD2F5, 0xD2F5 }, +{ 0xD2F6, 0xD2F6, 0xD2F6 }, +{ 0xD2F7, 0xD2F7, 0xD2F7 }, +{ 0xD2F8, 0xD2F8, 0xD2F8 }, +{ 0xD2F9, 0xD2F9, 0xD2F9 }, +{ 0xD2FA, 0xD2FA, 0xD2FA }, +{ 0xD2FB, 0xD2FB, 0xD2FB }, +{ 0xD2FC, 0xD2FC, 0xD2FC }, +{ 0xD2FD, 0xD2FD, 0xD2FD }, +{ 0xD2FE, 0xD2FE, 0xD2FE }, +{ 0xD2FF, 0xD2FF, 0xD2FF }, +{ 0xD300, 0xD300, 0xD300 }, +{ 0xD301, 0xD301, 0xD301 }, +{ 0xD302, 0xD302, 0xD302 }, +{ 0xD303, 0xD303, 0xD303 }, +{ 0xD304, 0xD304, 0xD304 }, +{ 0xD305, 0xD305, 0xD305 }, +{ 0xD306, 0xD306, 0xD306 }, +{ 0xD307, 0xD307, 0xD307 }, +{ 0xD308, 0xD308, 0xD308 }, +{ 0xD309, 0xD309, 0xD309 }, +{ 0xD30A, 0xD30A, 0xD30A }, +{ 0xD30B, 0xD30B, 0xD30B }, +{ 0xD30C, 0xD30C, 0xD30C }, +{ 0xD30D, 0xD30D, 0xD30D }, +{ 0xD30E, 0xD30E, 0xD30E }, +{ 0xD30F, 0xD30F, 0xD30F }, +{ 0xD310, 0xD310, 0xD310 }, +{ 0xD311, 0xD311, 0xD311 }, +{ 0xD312, 0xD312, 0xD312 }, +{ 0xD313, 0xD313, 0xD313 }, +{ 0xD314, 0xD314, 0xD314 }, +{ 0xD315, 0xD315, 0xD315 }, +{ 0xD316, 0xD316, 0xD316 }, +{ 0xD317, 0xD317, 0xD317 }, +{ 0xD318, 0xD318, 0xD318 }, +{ 0xD319, 0xD319, 0xD319 }, +{ 0xD31A, 0xD31A, 0xD31A }, +{ 0xD31B, 0xD31B, 0xD31B }, +{ 0xD31C, 0xD31C, 0xD31C }, +{ 0xD31D, 0xD31D, 0xD31D }, +{ 0xD31E, 0xD31E, 0xD31E }, +{ 0xD31F, 0xD31F, 0xD31F }, +{ 0xD320, 0xD320, 0xD320 }, +{ 0xD321, 0xD321, 0xD321 }, +{ 0xD322, 0xD322, 0xD322 }, +{ 0xD323, 0xD323, 0xD323 }, +{ 0xD324, 0xD324, 0xD324 }, +{ 0xD325, 0xD325, 0xD325 }, +{ 0xD326, 0xD326, 0xD326 }, +{ 0xD327, 0xD327, 0xD327 }, +{ 0xD328, 0xD328, 0xD328 }, +{ 0xD329, 0xD329, 0xD329 }, +{ 0xD32A, 0xD32A, 0xD32A }, +{ 0xD32B, 0xD32B, 0xD32B }, +{ 0xD32C, 0xD32C, 0xD32C }, +{ 0xD32D, 0xD32D, 0xD32D }, +{ 0xD32E, 0xD32E, 0xD32E }, +{ 0xD32F, 0xD32F, 0xD32F }, +{ 0xD330, 0xD330, 0xD330 }, +{ 0xD331, 0xD331, 0xD331 }, +{ 0xD332, 0xD332, 0xD332 }, +{ 0xD333, 0xD333, 0xD333 }, +{ 0xD334, 0xD334, 0xD334 }, +{ 0xD335, 0xD335, 0xD335 }, +{ 0xD336, 0xD336, 0xD336 }, +{ 0xD337, 0xD337, 0xD337 }, +{ 0xD338, 0xD338, 0xD338 }, +{ 0xD339, 0xD339, 0xD339 }, +{ 0xD33A, 0xD33A, 0xD33A }, +{ 0xD33B, 0xD33B, 0xD33B }, +{ 0xD33C, 0xD33C, 0xD33C }, +{ 0xD33D, 0xD33D, 0xD33D }, +{ 0xD33E, 0xD33E, 0xD33E }, +{ 0xD33F, 0xD33F, 0xD33F }, +{ 0xD340, 0xD340, 0xD340 }, +{ 0xD341, 0xD341, 0xD341 }, +{ 0xD342, 0xD342, 0xD342 }, +{ 0xD343, 0xD343, 0xD343 }, +{ 0xD344, 0xD344, 0xD344 }, +{ 0xD345, 0xD345, 0xD345 }, +{ 0xD346, 0xD346, 0xD346 }, +{ 0xD347, 0xD347, 0xD347 }, +{ 0xD348, 0xD348, 0xD348 }, +{ 0xD349, 0xD349, 0xD349 }, +{ 0xD34A, 0xD34A, 0xD34A }, +{ 0xD34B, 0xD34B, 0xD34B }, +{ 0xD34C, 0xD34C, 0xD34C }, +{ 0xD34D, 0xD34D, 0xD34D }, +{ 0xD34E, 0xD34E, 0xD34E }, +{ 0xD34F, 0xD34F, 0xD34F }, +{ 0xD350, 0xD350, 0xD350 }, +{ 0xD351, 0xD351, 0xD351 }, +{ 0xD352, 0xD352, 0xD352 }, +{ 0xD353, 0xD353, 0xD353 }, +{ 0xD354, 0xD354, 0xD354 }, +{ 0xD355, 0xD355, 0xD355 }, +{ 0xD356, 0xD356, 0xD356 }, +{ 0xD357, 0xD357, 0xD357 }, +{ 0xD358, 0xD358, 0xD358 }, +{ 0xD359, 0xD359, 0xD359 }, +{ 0xD35A, 0xD35A, 0xD35A }, +{ 0xD35B, 0xD35B, 0xD35B }, +{ 0xD35C, 0xD35C, 0xD35C }, +{ 0xD35D, 0xD35D, 0xD35D }, +{ 0xD35E, 0xD35E, 0xD35E }, +{ 0xD35F, 0xD35F, 0xD35F }, +{ 0xD360, 0xD360, 0xD360 }, +{ 0xD361, 0xD361, 0xD361 }, +{ 0xD362, 0xD362, 0xD362 }, +{ 0xD363, 0xD363, 0xD363 }, +{ 0xD364, 0xD364, 0xD364 }, +{ 0xD365, 0xD365, 0xD365 }, +{ 0xD366, 0xD366, 0xD366 }, +{ 0xD367, 0xD367, 0xD367 }, +{ 0xD368, 0xD368, 0xD368 }, +{ 0xD369, 0xD369, 0xD369 }, +{ 0xD36A, 0xD36A, 0xD36A }, +{ 0xD36B, 0xD36B, 0xD36B }, +{ 0xD36C, 0xD36C, 0xD36C }, +{ 0xD36D, 0xD36D, 0xD36D }, +{ 0xD36E, 0xD36E, 0xD36E }, +{ 0xD36F, 0xD36F, 0xD36F }, +{ 0xD370, 0xD370, 0xD370 }, +{ 0xD371, 0xD371, 0xD371 }, +{ 0xD372, 0xD372, 0xD372 }, +{ 0xD373, 0xD373, 0xD373 }, +{ 0xD374, 0xD374, 0xD374 }, +{ 0xD375, 0xD375, 0xD375 }, +{ 0xD376, 0xD376, 0xD376 }, +{ 0xD377, 0xD377, 0xD377 }, +{ 0xD378, 0xD378, 0xD378 }, +{ 0xD379, 0xD379, 0xD379 }, +{ 0xD37A, 0xD37A, 0xD37A }, +{ 0xD37B, 0xD37B, 0xD37B }, +{ 0xD37C, 0xD37C, 0xD37C }, +{ 0xD37D, 0xD37D, 0xD37D }, +{ 0xD37E, 0xD37E, 0xD37E }, +{ 0xD37F, 0xD37F, 0xD37F }, +{ 0xD380, 0xD380, 0xD380 }, +{ 0xD381, 0xD381, 0xD381 }, +{ 0xD382, 0xD382, 0xD382 }, +{ 0xD383, 0xD383, 0xD383 }, +{ 0xD384, 0xD384, 0xD384 }, +{ 0xD385, 0xD385, 0xD385 }, +{ 0xD386, 0xD386, 0xD386 }, +{ 0xD387, 0xD387, 0xD387 }, +{ 0xD388, 0xD388, 0xD388 }, +{ 0xD389, 0xD389, 0xD389 }, +{ 0xD38A, 0xD38A, 0xD38A }, +{ 0xD38B, 0xD38B, 0xD38B }, +{ 0xD38C, 0xD38C, 0xD38C }, +{ 0xD38D, 0xD38D, 0xD38D }, +{ 0xD38E, 0xD38E, 0xD38E }, +{ 0xD38F, 0xD38F, 0xD38F }, +{ 0xD390, 0xD390, 0xD390 }, +{ 0xD391, 0xD391, 0xD391 }, +{ 0xD392, 0xD392, 0xD392 }, +{ 0xD393, 0xD393, 0xD393 }, +{ 0xD394, 0xD394, 0xD394 }, +{ 0xD395, 0xD395, 0xD395 }, +{ 0xD396, 0xD396, 0xD396 }, +{ 0xD397, 0xD397, 0xD397 }, +{ 0xD398, 0xD398, 0xD398 }, +{ 0xD399, 0xD399, 0xD399 }, +{ 0xD39A, 0xD39A, 0xD39A }, +{ 0xD39B, 0xD39B, 0xD39B }, +{ 0xD39C, 0xD39C, 0xD39C }, +{ 0xD39D, 0xD39D, 0xD39D }, +{ 0xD39E, 0xD39E, 0xD39E }, +{ 0xD39F, 0xD39F, 0xD39F }, +{ 0xD3A0, 0xD3A0, 0xD3A0 }, +{ 0xD3A1, 0xD3A1, 0xD3A1 }, +{ 0xD3A2, 0xD3A2, 0xD3A2 }, +{ 0xD3A3, 0xD3A3, 0xD3A3 }, +{ 0xD3A4, 0xD3A4, 0xD3A4 }, +{ 0xD3A5, 0xD3A5, 0xD3A5 }, +{ 0xD3A6, 0xD3A6, 0xD3A6 }, +{ 0xD3A7, 0xD3A7, 0xD3A7 }, +{ 0xD3A8, 0xD3A8, 0xD3A8 }, +{ 0xD3A9, 0xD3A9, 0xD3A9 }, +{ 0xD3AA, 0xD3AA, 0xD3AA }, +{ 0xD3AB, 0xD3AB, 0xD3AB }, +{ 0xD3AC, 0xD3AC, 0xD3AC }, +{ 0xD3AD, 0xD3AD, 0xD3AD }, +{ 0xD3AE, 0xD3AE, 0xD3AE }, +{ 0xD3AF, 0xD3AF, 0xD3AF }, +{ 0xD3B0, 0xD3B0, 0xD3B0 }, +{ 0xD3B1, 0xD3B1, 0xD3B1 }, +{ 0xD3B2, 0xD3B2, 0xD3B2 }, +{ 0xD3B3, 0xD3B3, 0xD3B3 }, +{ 0xD3B4, 0xD3B4, 0xD3B4 }, +{ 0xD3B5, 0xD3B5, 0xD3B5 }, +{ 0xD3B6, 0xD3B6, 0xD3B6 }, +{ 0xD3B7, 0xD3B7, 0xD3B7 }, +{ 0xD3B8, 0xD3B8, 0xD3B8 }, +{ 0xD3B9, 0xD3B9, 0xD3B9 }, +{ 0xD3BA, 0xD3BA, 0xD3BA }, +{ 0xD3BB, 0xD3BB, 0xD3BB }, +{ 0xD3BC, 0xD3BC, 0xD3BC }, +{ 0xD3BD, 0xD3BD, 0xD3BD }, +{ 0xD3BE, 0xD3BE, 0xD3BE }, +{ 0xD3BF, 0xD3BF, 0xD3BF }, +{ 0xD3C0, 0xD3C0, 0xD3C0 }, +{ 0xD3C1, 0xD3C1, 0xD3C1 }, +{ 0xD3C2, 0xD3C2, 0xD3C2 }, +{ 0xD3C3, 0xD3C3, 0xD3C3 }, +{ 0xD3C4, 0xD3C4, 0xD3C4 }, +{ 0xD3C5, 0xD3C5, 0xD3C5 }, +{ 0xD3C6, 0xD3C6, 0xD3C6 }, +{ 0xD3C7, 0xD3C7, 0xD3C7 }, +{ 0xD3C8, 0xD3C8, 0xD3C8 }, +{ 0xD3C9, 0xD3C9, 0xD3C9 }, +{ 0xD3CA, 0xD3CA, 0xD3CA }, +{ 0xD3CB, 0xD3CB, 0xD3CB }, +{ 0xD3CC, 0xD3CC, 0xD3CC }, +{ 0xD3CD, 0xD3CD, 0xD3CD }, +{ 0xD3CE, 0xD3CE, 0xD3CE }, +{ 0xD3CF, 0xD3CF, 0xD3CF }, +{ 0xD3D0, 0xD3D0, 0xD3D0 }, +{ 0xD3D1, 0xD3D1, 0xD3D1 }, +{ 0xD3D2, 0xD3D2, 0xD3D2 }, +{ 0xD3D3, 0xD3D3, 0xD3D3 }, +{ 0xD3D4, 0xD3D4, 0xD3D4 }, +{ 0xD3D5, 0xD3D5, 0xD3D5 }, +{ 0xD3D6, 0xD3D6, 0xD3D6 }, +{ 0xD3D7, 0xD3D7, 0xD3D7 }, +{ 0xD3D8, 0xD3D8, 0xD3D8 }, +{ 0xD3D9, 0xD3D9, 0xD3D9 }, +{ 0xD3DA, 0xD3DA, 0xD3DA }, +{ 0xD3DB, 0xD3DB, 0xD3DB }, +{ 0xD3DC, 0xD3DC, 0xD3DC }, +{ 0xD3DD, 0xD3DD, 0xD3DD }, +{ 0xD3DE, 0xD3DE, 0xD3DE }, +{ 0xD3DF, 0xD3DF, 0xD3DF }, +{ 0xD3E0, 0xD3E0, 0xD3E0 }, +{ 0xD3E1, 0xD3E1, 0xD3E1 }, +{ 0xD3E2, 0xD3E2, 0xD3E2 }, +{ 0xD3E3, 0xD3E3, 0xD3E3 }, +{ 0xD3E4, 0xD3E4, 0xD3E4 }, +{ 0xD3E5, 0xD3E5, 0xD3E5 }, +{ 0xD3E6, 0xD3E6, 0xD3E6 }, +{ 0xD3E7, 0xD3E7, 0xD3E7 }, +{ 0xD3E8, 0xD3E8, 0xD3E8 }, +{ 0xD3E9, 0xD3E9, 0xD3E9 }, +{ 0xD3EA, 0xD3EA, 0xD3EA }, +{ 0xD3EB, 0xD3EB, 0xD3EB }, +{ 0xD3EC, 0xD3EC, 0xD3EC }, +{ 0xD3ED, 0xD3ED, 0xD3ED }, +{ 0xD3EE, 0xD3EE, 0xD3EE }, +{ 0xD3EF, 0xD3EF, 0xD3EF }, +{ 0xD3F0, 0xD3F0, 0xD3F0 }, +{ 0xD3F1, 0xD3F1, 0xD3F1 }, +{ 0xD3F2, 0xD3F2, 0xD3F2 }, +{ 0xD3F3, 0xD3F3, 0xD3F3 }, +{ 0xD3F4, 0xD3F4, 0xD3F4 }, +{ 0xD3F5, 0xD3F5, 0xD3F5 }, +{ 0xD3F6, 0xD3F6, 0xD3F6 }, +{ 0xD3F7, 0xD3F7, 0xD3F7 }, +{ 0xD3F8, 0xD3F8, 0xD3F8 }, +{ 0xD3F9, 0xD3F9, 0xD3F9 }, +{ 0xD3FA, 0xD3FA, 0xD3FA }, +{ 0xD3FB, 0xD3FB, 0xD3FB }, +{ 0xD3FC, 0xD3FC, 0xD3FC }, +{ 0xD3FD, 0xD3FD, 0xD3FD }, +{ 0xD3FE, 0xD3FE, 0xD3FE }, +{ 0xD3FF, 0xD3FF, 0xD3FF }, +{ 0xD400, 0xD400, 0xD400 }, +{ 0xD401, 0xD401, 0xD401 }, +{ 0xD402, 0xD402, 0xD402 }, +{ 0xD403, 0xD403, 0xD403 }, +{ 0xD404, 0xD404, 0xD404 }, +{ 0xD405, 0xD405, 0xD405 }, +{ 0xD406, 0xD406, 0xD406 }, +{ 0xD407, 0xD407, 0xD407 }, +{ 0xD408, 0xD408, 0xD408 }, +{ 0xD409, 0xD409, 0xD409 }, +{ 0xD40A, 0xD40A, 0xD40A }, +{ 0xD40B, 0xD40B, 0xD40B }, +{ 0xD40C, 0xD40C, 0xD40C }, +{ 0xD40D, 0xD40D, 0xD40D }, +{ 0xD40E, 0xD40E, 0xD40E }, +{ 0xD40F, 0xD40F, 0xD40F }, +{ 0xD410, 0xD410, 0xD410 }, +{ 0xD411, 0xD411, 0xD411 }, +{ 0xD412, 0xD412, 0xD412 }, +{ 0xD413, 0xD413, 0xD413 }, +{ 0xD414, 0xD414, 0xD414 }, +{ 0xD415, 0xD415, 0xD415 }, +{ 0xD416, 0xD416, 0xD416 }, +{ 0xD417, 0xD417, 0xD417 }, +{ 0xD418, 0xD418, 0xD418 }, +{ 0xD419, 0xD419, 0xD419 }, +{ 0xD41A, 0xD41A, 0xD41A }, +{ 0xD41B, 0xD41B, 0xD41B }, +{ 0xD41C, 0xD41C, 0xD41C }, +{ 0xD41D, 0xD41D, 0xD41D }, +{ 0xD41E, 0xD41E, 0xD41E }, +{ 0xD41F, 0xD41F, 0xD41F }, +{ 0xD420, 0xD420, 0xD420 }, +{ 0xD421, 0xD421, 0xD421 }, +{ 0xD422, 0xD422, 0xD422 }, +{ 0xD423, 0xD423, 0xD423 }, +{ 0xD424, 0xD424, 0xD424 }, +{ 0xD425, 0xD425, 0xD425 }, +{ 0xD426, 0xD426, 0xD426 }, +{ 0xD427, 0xD427, 0xD427 }, +{ 0xD428, 0xD428, 0xD428 }, +{ 0xD429, 0xD429, 0xD429 }, +{ 0xD42A, 0xD42A, 0xD42A }, +{ 0xD42B, 0xD42B, 0xD42B }, +{ 0xD42C, 0xD42C, 0xD42C }, +{ 0xD42D, 0xD42D, 0xD42D }, +{ 0xD42E, 0xD42E, 0xD42E }, +{ 0xD42F, 0xD42F, 0xD42F }, +{ 0xD430, 0xD430, 0xD430 }, +{ 0xD431, 0xD431, 0xD431 }, +{ 0xD432, 0xD432, 0xD432 }, +{ 0xD433, 0xD433, 0xD433 }, +{ 0xD434, 0xD434, 0xD434 }, +{ 0xD435, 0xD435, 0xD435 }, +{ 0xD436, 0xD436, 0xD436 }, +{ 0xD437, 0xD437, 0xD437 }, +{ 0xD438, 0xD438, 0xD438 }, +{ 0xD439, 0xD439, 0xD439 }, +{ 0xD43A, 0xD43A, 0xD43A }, +{ 0xD43B, 0xD43B, 0xD43B }, +{ 0xD43C, 0xD43C, 0xD43C }, +{ 0xD43D, 0xD43D, 0xD43D }, +{ 0xD43E, 0xD43E, 0xD43E }, +{ 0xD43F, 0xD43F, 0xD43F }, +{ 0xD440, 0xD440, 0xD440 }, +{ 0xD441, 0xD441, 0xD441 }, +{ 0xD442, 0xD442, 0xD442 }, +{ 0xD443, 0xD443, 0xD443 }, +{ 0xD444, 0xD444, 0xD444 }, +{ 0xD445, 0xD445, 0xD445 }, +{ 0xD446, 0xD446, 0xD446 }, +{ 0xD447, 0xD447, 0xD447 }, +{ 0xD448, 0xD448, 0xD448 }, +{ 0xD449, 0xD449, 0xD449 }, +{ 0xD44A, 0xD44A, 0xD44A }, +{ 0xD44B, 0xD44B, 0xD44B }, +{ 0xD44C, 0xD44C, 0xD44C }, +{ 0xD44D, 0xD44D, 0xD44D }, +{ 0xD44E, 0xD44E, 0xD44E }, +{ 0xD44F, 0xD44F, 0xD44F }, +{ 0xD450, 0xD450, 0xD450 }, +{ 0xD451, 0xD451, 0xD451 }, +{ 0xD452, 0xD452, 0xD452 }, +{ 0xD453, 0xD453, 0xD453 }, +{ 0xD454, 0xD454, 0xD454 }, +{ 0xD455, 0xD455, 0xD455 }, +{ 0xD456, 0xD456, 0xD456 }, +{ 0xD457, 0xD457, 0xD457 }, +{ 0xD458, 0xD458, 0xD458 }, +{ 0xD459, 0xD459, 0xD459 }, +{ 0xD45A, 0xD45A, 0xD45A }, +{ 0xD45B, 0xD45B, 0xD45B }, +{ 0xD45C, 0xD45C, 0xD45C }, +{ 0xD45D, 0xD45D, 0xD45D }, +{ 0xD45E, 0xD45E, 0xD45E }, +{ 0xD45F, 0xD45F, 0xD45F }, +{ 0xD460, 0xD460, 0xD460 }, +{ 0xD461, 0xD461, 0xD461 }, +{ 0xD462, 0xD462, 0xD462 }, +{ 0xD463, 0xD463, 0xD463 }, +{ 0xD464, 0xD464, 0xD464 }, +{ 0xD465, 0xD465, 0xD465 }, +{ 0xD466, 0xD466, 0xD466 }, +{ 0xD467, 0xD467, 0xD467 }, +{ 0xD468, 0xD468, 0xD468 }, +{ 0xD469, 0xD469, 0xD469 }, +{ 0xD46A, 0xD46A, 0xD46A }, +{ 0xD46B, 0xD46B, 0xD46B }, +{ 0xD46C, 0xD46C, 0xD46C }, +{ 0xD46D, 0xD46D, 0xD46D }, +{ 0xD46E, 0xD46E, 0xD46E }, +{ 0xD46F, 0xD46F, 0xD46F }, +{ 0xD470, 0xD470, 0xD470 }, +{ 0xD471, 0xD471, 0xD471 }, +{ 0xD472, 0xD472, 0xD472 }, +{ 0xD473, 0xD473, 0xD473 }, +{ 0xD474, 0xD474, 0xD474 }, +{ 0xD475, 0xD475, 0xD475 }, +{ 0xD476, 0xD476, 0xD476 }, +{ 0xD477, 0xD477, 0xD477 }, +{ 0xD478, 0xD478, 0xD478 }, +{ 0xD479, 0xD479, 0xD479 }, +{ 0xD47A, 0xD47A, 0xD47A }, +{ 0xD47B, 0xD47B, 0xD47B }, +{ 0xD47C, 0xD47C, 0xD47C }, +{ 0xD47D, 0xD47D, 0xD47D }, +{ 0xD47E, 0xD47E, 0xD47E }, +{ 0xD47F, 0xD47F, 0xD47F }, +{ 0xD480, 0xD480, 0xD480 }, +{ 0xD481, 0xD481, 0xD481 }, +{ 0xD482, 0xD482, 0xD482 }, +{ 0xD483, 0xD483, 0xD483 }, +{ 0xD484, 0xD484, 0xD484 }, +{ 0xD485, 0xD485, 0xD485 }, +{ 0xD486, 0xD486, 0xD486 }, +{ 0xD487, 0xD487, 0xD487 }, +{ 0xD488, 0xD488, 0xD488 }, +{ 0xD489, 0xD489, 0xD489 }, +{ 0xD48A, 0xD48A, 0xD48A }, +{ 0xD48B, 0xD48B, 0xD48B }, +{ 0xD48C, 0xD48C, 0xD48C }, +{ 0xD48D, 0xD48D, 0xD48D }, +{ 0xD48E, 0xD48E, 0xD48E }, +{ 0xD48F, 0xD48F, 0xD48F }, +{ 0xD490, 0xD490, 0xD490 }, +{ 0xD491, 0xD491, 0xD491 }, +{ 0xD492, 0xD492, 0xD492 }, +{ 0xD493, 0xD493, 0xD493 }, +{ 0xD494, 0xD494, 0xD494 }, +{ 0xD495, 0xD495, 0xD495 }, +{ 0xD496, 0xD496, 0xD496 }, +{ 0xD497, 0xD497, 0xD497 }, +{ 0xD498, 0xD498, 0xD498 }, +{ 0xD499, 0xD499, 0xD499 }, +{ 0xD49A, 0xD49A, 0xD49A }, +{ 0xD49B, 0xD49B, 0xD49B }, +{ 0xD49C, 0xD49C, 0xD49C }, +{ 0xD49D, 0xD49D, 0xD49D }, +{ 0xD49E, 0xD49E, 0xD49E }, +{ 0xD49F, 0xD49F, 0xD49F }, +{ 0xD4A0, 0xD4A0, 0xD4A0 }, +{ 0xD4A1, 0xD4A1, 0xD4A1 }, +{ 0xD4A2, 0xD4A2, 0xD4A2 }, +{ 0xD4A3, 0xD4A3, 0xD4A3 }, +{ 0xD4A4, 0xD4A4, 0xD4A4 }, +{ 0xD4A5, 0xD4A5, 0xD4A5 }, +{ 0xD4A6, 0xD4A6, 0xD4A6 }, +{ 0xD4A7, 0xD4A7, 0xD4A7 }, +{ 0xD4A8, 0xD4A8, 0xD4A8 }, +{ 0xD4A9, 0xD4A9, 0xD4A9 }, +{ 0xD4AA, 0xD4AA, 0xD4AA }, +{ 0xD4AB, 0xD4AB, 0xD4AB }, +{ 0xD4AC, 0xD4AC, 0xD4AC }, +{ 0xD4AD, 0xD4AD, 0xD4AD }, +{ 0xD4AE, 0xD4AE, 0xD4AE }, +{ 0xD4AF, 0xD4AF, 0xD4AF }, +{ 0xD4B0, 0xD4B0, 0xD4B0 }, +{ 0xD4B1, 0xD4B1, 0xD4B1 }, +{ 0xD4B2, 0xD4B2, 0xD4B2 }, +{ 0xD4B3, 0xD4B3, 0xD4B3 }, +{ 0xD4B4, 0xD4B4, 0xD4B4 }, +{ 0xD4B5, 0xD4B5, 0xD4B5 }, +{ 0xD4B6, 0xD4B6, 0xD4B6 }, +{ 0xD4B7, 0xD4B7, 0xD4B7 }, +{ 0xD4B8, 0xD4B8, 0xD4B8 }, +{ 0xD4B9, 0xD4B9, 0xD4B9 }, +{ 0xD4BA, 0xD4BA, 0xD4BA }, +{ 0xD4BB, 0xD4BB, 0xD4BB }, +{ 0xD4BC, 0xD4BC, 0xD4BC }, +{ 0xD4BD, 0xD4BD, 0xD4BD }, +{ 0xD4BE, 0xD4BE, 0xD4BE }, +{ 0xD4BF, 0xD4BF, 0xD4BF }, +{ 0xD4C0, 0xD4C0, 0xD4C0 }, +{ 0xD4C1, 0xD4C1, 0xD4C1 }, +{ 0xD4C2, 0xD4C2, 0xD4C2 }, +{ 0xD4C3, 0xD4C3, 0xD4C3 }, +{ 0xD4C4, 0xD4C4, 0xD4C4 }, +{ 0xD4C5, 0xD4C5, 0xD4C5 }, +{ 0xD4C6, 0xD4C6, 0xD4C6 }, +{ 0xD4C7, 0xD4C7, 0xD4C7 }, +{ 0xD4C8, 0xD4C8, 0xD4C8 }, +{ 0xD4C9, 0xD4C9, 0xD4C9 }, +{ 0xD4CA, 0xD4CA, 0xD4CA }, +{ 0xD4CB, 0xD4CB, 0xD4CB }, +{ 0xD4CC, 0xD4CC, 0xD4CC }, +{ 0xD4CD, 0xD4CD, 0xD4CD }, +{ 0xD4CE, 0xD4CE, 0xD4CE }, +{ 0xD4CF, 0xD4CF, 0xD4CF }, +{ 0xD4D0, 0xD4D0, 0xD4D0 }, +{ 0xD4D1, 0xD4D1, 0xD4D1 }, +{ 0xD4D2, 0xD4D2, 0xD4D2 }, +{ 0xD4D3, 0xD4D3, 0xD4D3 }, +{ 0xD4D4, 0xD4D4, 0xD4D4 }, +{ 0xD4D5, 0xD4D5, 0xD4D5 }, +{ 0xD4D6, 0xD4D6, 0xD4D6 }, +{ 0xD4D7, 0xD4D7, 0xD4D7 }, +{ 0xD4D8, 0xD4D8, 0xD4D8 }, +{ 0xD4D9, 0xD4D9, 0xD4D9 }, +{ 0xD4DA, 0xD4DA, 0xD4DA }, +{ 0xD4DB, 0xD4DB, 0xD4DB }, +{ 0xD4DC, 0xD4DC, 0xD4DC }, +{ 0xD4DD, 0xD4DD, 0xD4DD }, +{ 0xD4DE, 0xD4DE, 0xD4DE }, +{ 0xD4DF, 0xD4DF, 0xD4DF }, +{ 0xD4E0, 0xD4E0, 0xD4E0 }, +{ 0xD4E1, 0xD4E1, 0xD4E1 }, +{ 0xD4E2, 0xD4E2, 0xD4E2 }, +{ 0xD4E3, 0xD4E3, 0xD4E3 }, +{ 0xD4E4, 0xD4E4, 0xD4E4 }, +{ 0xD4E5, 0xD4E5, 0xD4E5 }, +{ 0xD4E6, 0xD4E6, 0xD4E6 }, +{ 0xD4E7, 0xD4E7, 0xD4E7 }, +{ 0xD4E8, 0xD4E8, 0xD4E8 }, +{ 0xD4E9, 0xD4E9, 0xD4E9 }, +{ 0xD4EA, 0xD4EA, 0xD4EA }, +{ 0xD4EB, 0xD4EB, 0xD4EB }, +{ 0xD4EC, 0xD4EC, 0xD4EC }, +{ 0xD4ED, 0xD4ED, 0xD4ED }, +{ 0xD4EE, 0xD4EE, 0xD4EE }, +{ 0xD4EF, 0xD4EF, 0xD4EF }, +{ 0xD4F0, 0xD4F0, 0xD4F0 }, +{ 0xD4F1, 0xD4F1, 0xD4F1 }, +{ 0xD4F2, 0xD4F2, 0xD4F2 }, +{ 0xD4F3, 0xD4F3, 0xD4F3 }, +{ 0xD4F4, 0xD4F4, 0xD4F4 }, +{ 0xD4F5, 0xD4F5, 0xD4F5 }, +{ 0xD4F6, 0xD4F6, 0xD4F6 }, +{ 0xD4F7, 0xD4F7, 0xD4F7 }, +{ 0xD4F8, 0xD4F8, 0xD4F8 }, +{ 0xD4F9, 0xD4F9, 0xD4F9 }, +{ 0xD4FA, 0xD4FA, 0xD4FA }, +{ 0xD4FB, 0xD4FB, 0xD4FB }, +{ 0xD4FC, 0xD4FC, 0xD4FC }, +{ 0xD4FD, 0xD4FD, 0xD4FD }, +{ 0xD4FE, 0xD4FE, 0xD4FE }, +{ 0xD4FF, 0xD4FF, 0xD4FF }, +{ 0xD500, 0xD500, 0xD500 }, +{ 0xD501, 0xD501, 0xD501 }, +{ 0xD502, 0xD502, 0xD502 }, +{ 0xD503, 0xD503, 0xD503 }, +{ 0xD504, 0xD504, 0xD504 }, +{ 0xD505, 0xD505, 0xD505 }, +{ 0xD506, 0xD506, 0xD506 }, +{ 0xD507, 0xD507, 0xD507 }, +{ 0xD508, 0xD508, 0xD508 }, +{ 0xD509, 0xD509, 0xD509 }, +{ 0xD50A, 0xD50A, 0xD50A }, +{ 0xD50B, 0xD50B, 0xD50B }, +{ 0xD50C, 0xD50C, 0xD50C }, +{ 0xD50D, 0xD50D, 0xD50D }, +{ 0xD50E, 0xD50E, 0xD50E }, +{ 0xD50F, 0xD50F, 0xD50F }, +{ 0xD510, 0xD510, 0xD510 }, +{ 0xD511, 0xD511, 0xD511 }, +{ 0xD512, 0xD512, 0xD512 }, +{ 0xD513, 0xD513, 0xD513 }, +{ 0xD514, 0xD514, 0xD514 }, +{ 0xD515, 0xD515, 0xD515 }, +{ 0xD516, 0xD516, 0xD516 }, +{ 0xD517, 0xD517, 0xD517 }, +{ 0xD518, 0xD518, 0xD518 }, +{ 0xD519, 0xD519, 0xD519 }, +{ 0xD51A, 0xD51A, 0xD51A }, +{ 0xD51B, 0xD51B, 0xD51B }, +{ 0xD51C, 0xD51C, 0xD51C }, +{ 0xD51D, 0xD51D, 0xD51D }, +{ 0xD51E, 0xD51E, 0xD51E }, +{ 0xD51F, 0xD51F, 0xD51F }, +{ 0xD520, 0xD520, 0xD520 }, +{ 0xD521, 0xD521, 0xD521 }, +{ 0xD522, 0xD522, 0xD522 }, +{ 0xD523, 0xD523, 0xD523 }, +{ 0xD524, 0xD524, 0xD524 }, +{ 0xD525, 0xD525, 0xD525 }, +{ 0xD526, 0xD526, 0xD526 }, +{ 0xD527, 0xD527, 0xD527 }, +{ 0xD528, 0xD528, 0xD528 }, +{ 0xD529, 0xD529, 0xD529 }, +{ 0xD52A, 0xD52A, 0xD52A }, +{ 0xD52B, 0xD52B, 0xD52B }, +{ 0xD52C, 0xD52C, 0xD52C }, +{ 0xD52D, 0xD52D, 0xD52D }, +{ 0xD52E, 0xD52E, 0xD52E }, +{ 0xD52F, 0xD52F, 0xD52F }, +{ 0xD530, 0xD530, 0xD530 }, +{ 0xD531, 0xD531, 0xD531 }, +{ 0xD532, 0xD532, 0xD532 }, +{ 0xD533, 0xD533, 0xD533 }, +{ 0xD534, 0xD534, 0xD534 }, +{ 0xD535, 0xD535, 0xD535 }, +{ 0xD536, 0xD536, 0xD536 }, +{ 0xD537, 0xD537, 0xD537 }, +{ 0xD538, 0xD538, 0xD538 }, +{ 0xD539, 0xD539, 0xD539 }, +{ 0xD53A, 0xD53A, 0xD53A }, +{ 0xD53B, 0xD53B, 0xD53B }, +{ 0xD53C, 0xD53C, 0xD53C }, +{ 0xD53D, 0xD53D, 0xD53D }, +{ 0xD53E, 0xD53E, 0xD53E }, +{ 0xD53F, 0xD53F, 0xD53F }, +{ 0xD540, 0xD540, 0xD540 }, +{ 0xD541, 0xD541, 0xD541 }, +{ 0xD542, 0xD542, 0xD542 }, +{ 0xD543, 0xD543, 0xD543 }, +{ 0xD544, 0xD544, 0xD544 }, +{ 0xD545, 0xD545, 0xD545 }, +{ 0xD546, 0xD546, 0xD546 }, +{ 0xD547, 0xD547, 0xD547 }, +{ 0xD548, 0xD548, 0xD548 }, +{ 0xD549, 0xD549, 0xD549 }, +{ 0xD54A, 0xD54A, 0xD54A }, +{ 0xD54B, 0xD54B, 0xD54B }, +{ 0xD54C, 0xD54C, 0xD54C }, +{ 0xD54D, 0xD54D, 0xD54D }, +{ 0xD54E, 0xD54E, 0xD54E }, +{ 0xD54F, 0xD54F, 0xD54F }, +{ 0xD550, 0xD550, 0xD550 }, +{ 0xD551, 0xD551, 0xD551 }, +{ 0xD552, 0xD552, 0xD552 }, +{ 0xD553, 0xD553, 0xD553 }, +{ 0xD554, 0xD554, 0xD554 }, +{ 0xD555, 0xD555, 0xD555 }, +{ 0xD556, 0xD556, 0xD556 }, +{ 0xD557, 0xD557, 0xD557 }, +{ 0xD558, 0xD558, 0xD558 }, +{ 0xD559, 0xD559, 0xD559 }, +{ 0xD55A, 0xD55A, 0xD55A }, +{ 0xD55B, 0xD55B, 0xD55B }, +{ 0xD55C, 0xD55C, 0xD55C }, +{ 0xD55D, 0xD55D, 0xD55D }, +{ 0xD55E, 0xD55E, 0xD55E }, +{ 0xD55F, 0xD55F, 0xD55F }, +{ 0xD560, 0xD560, 0xD560 }, +{ 0xD561, 0xD561, 0xD561 }, +{ 0xD562, 0xD562, 0xD562 }, +{ 0xD563, 0xD563, 0xD563 }, +{ 0xD564, 0xD564, 0xD564 }, +{ 0xD565, 0xD565, 0xD565 }, +{ 0xD566, 0xD566, 0xD566 }, +{ 0xD567, 0xD567, 0xD567 }, +{ 0xD568, 0xD568, 0xD568 }, +{ 0xD569, 0xD569, 0xD569 }, +{ 0xD56A, 0xD56A, 0xD56A }, +{ 0xD56B, 0xD56B, 0xD56B }, +{ 0xD56C, 0xD56C, 0xD56C }, +{ 0xD56D, 0xD56D, 0xD56D }, +{ 0xD56E, 0xD56E, 0xD56E }, +{ 0xD56F, 0xD56F, 0xD56F }, +{ 0xD570, 0xD570, 0xD570 }, +{ 0xD571, 0xD571, 0xD571 }, +{ 0xD572, 0xD572, 0xD572 }, +{ 0xD573, 0xD573, 0xD573 }, +{ 0xD574, 0xD574, 0xD574 }, +{ 0xD575, 0xD575, 0xD575 }, +{ 0xD576, 0xD576, 0xD576 }, +{ 0xD577, 0xD577, 0xD577 }, +{ 0xD578, 0xD578, 0xD578 }, +{ 0xD579, 0xD579, 0xD579 }, +{ 0xD57A, 0xD57A, 0xD57A }, +{ 0xD57B, 0xD57B, 0xD57B }, +{ 0xD57C, 0xD57C, 0xD57C }, +{ 0xD57D, 0xD57D, 0xD57D }, +{ 0xD57E, 0xD57E, 0xD57E }, +{ 0xD57F, 0xD57F, 0xD57F }, +{ 0xD580, 0xD580, 0xD580 }, +{ 0xD581, 0xD581, 0xD581 }, +{ 0xD582, 0xD582, 0xD582 }, +{ 0xD583, 0xD583, 0xD583 }, +{ 0xD584, 0xD584, 0xD584 }, +{ 0xD585, 0xD585, 0xD585 }, +{ 0xD586, 0xD586, 0xD586 }, +{ 0xD587, 0xD587, 0xD587 }, +{ 0xD588, 0xD588, 0xD588 }, +{ 0xD589, 0xD589, 0xD589 }, +{ 0xD58A, 0xD58A, 0xD58A }, +{ 0xD58B, 0xD58B, 0xD58B }, +{ 0xD58C, 0xD58C, 0xD58C }, +{ 0xD58D, 0xD58D, 0xD58D }, +{ 0xD58E, 0xD58E, 0xD58E }, +{ 0xD58F, 0xD58F, 0xD58F }, +{ 0xD590, 0xD590, 0xD590 }, +{ 0xD591, 0xD591, 0xD591 }, +{ 0xD592, 0xD592, 0xD592 }, +{ 0xD593, 0xD593, 0xD593 }, +{ 0xD594, 0xD594, 0xD594 }, +{ 0xD595, 0xD595, 0xD595 }, +{ 0xD596, 0xD596, 0xD596 }, +{ 0xD597, 0xD597, 0xD597 }, +{ 0xD598, 0xD598, 0xD598 }, +{ 0xD599, 0xD599, 0xD599 }, +{ 0xD59A, 0xD59A, 0xD59A }, +{ 0xD59B, 0xD59B, 0xD59B }, +{ 0xD59C, 0xD59C, 0xD59C }, +{ 0xD59D, 0xD59D, 0xD59D }, +{ 0xD59E, 0xD59E, 0xD59E }, +{ 0xD59F, 0xD59F, 0xD59F }, +{ 0xD5A0, 0xD5A0, 0xD5A0 }, +{ 0xD5A1, 0xD5A1, 0xD5A1 }, +{ 0xD5A2, 0xD5A2, 0xD5A2 }, +{ 0xD5A3, 0xD5A3, 0xD5A3 }, +{ 0xD5A4, 0xD5A4, 0xD5A4 }, +{ 0xD5A5, 0xD5A5, 0xD5A5 }, +{ 0xD5A6, 0xD5A6, 0xD5A6 }, +{ 0xD5A7, 0xD5A7, 0xD5A7 }, +{ 0xD5A8, 0xD5A8, 0xD5A8 }, +{ 0xD5A9, 0xD5A9, 0xD5A9 }, +{ 0xD5AA, 0xD5AA, 0xD5AA }, +{ 0xD5AB, 0xD5AB, 0xD5AB }, +{ 0xD5AC, 0xD5AC, 0xD5AC }, +{ 0xD5AD, 0xD5AD, 0xD5AD }, +{ 0xD5AE, 0xD5AE, 0xD5AE }, +{ 0xD5AF, 0xD5AF, 0xD5AF }, +{ 0xD5B0, 0xD5B0, 0xD5B0 }, +{ 0xD5B1, 0xD5B1, 0xD5B1 }, +{ 0xD5B2, 0xD5B2, 0xD5B2 }, +{ 0xD5B3, 0xD5B3, 0xD5B3 }, +{ 0xD5B4, 0xD5B4, 0xD5B4 }, +{ 0xD5B5, 0xD5B5, 0xD5B5 }, +{ 0xD5B6, 0xD5B6, 0xD5B6 }, +{ 0xD5B7, 0xD5B7, 0xD5B7 }, +{ 0xD5B8, 0xD5B8, 0xD5B8 }, +{ 0xD5B9, 0xD5B9, 0xD5B9 }, +{ 0xD5BA, 0xD5BA, 0xD5BA }, +{ 0xD5BB, 0xD5BB, 0xD5BB }, +{ 0xD5BC, 0xD5BC, 0xD5BC }, +{ 0xD5BD, 0xD5BD, 0xD5BD }, +{ 0xD5BE, 0xD5BE, 0xD5BE }, +{ 0xD5BF, 0xD5BF, 0xD5BF }, +{ 0xD5C0, 0xD5C0, 0xD5C0 }, +{ 0xD5C1, 0xD5C1, 0xD5C1 }, +{ 0xD5C2, 0xD5C2, 0xD5C2 }, +{ 0xD5C3, 0xD5C3, 0xD5C3 }, +{ 0xD5C4, 0xD5C4, 0xD5C4 }, +{ 0xD5C5, 0xD5C5, 0xD5C5 }, +{ 0xD5C6, 0xD5C6, 0xD5C6 }, +{ 0xD5C7, 0xD5C7, 0xD5C7 }, +{ 0xD5C8, 0xD5C8, 0xD5C8 }, +{ 0xD5C9, 0xD5C9, 0xD5C9 }, +{ 0xD5CA, 0xD5CA, 0xD5CA }, +{ 0xD5CB, 0xD5CB, 0xD5CB }, +{ 0xD5CC, 0xD5CC, 0xD5CC }, +{ 0xD5CD, 0xD5CD, 0xD5CD }, +{ 0xD5CE, 0xD5CE, 0xD5CE }, +{ 0xD5CF, 0xD5CF, 0xD5CF }, +{ 0xD5D0, 0xD5D0, 0xD5D0 }, +{ 0xD5D1, 0xD5D1, 0xD5D1 }, +{ 0xD5D2, 0xD5D2, 0xD5D2 }, +{ 0xD5D3, 0xD5D3, 0xD5D3 }, +{ 0xD5D4, 0xD5D4, 0xD5D4 }, +{ 0xD5D5, 0xD5D5, 0xD5D5 }, +{ 0xD5D6, 0xD5D6, 0xD5D6 }, +{ 0xD5D7, 0xD5D7, 0xD5D7 }, +{ 0xD5D8, 0xD5D8, 0xD5D8 }, +{ 0xD5D9, 0xD5D9, 0xD5D9 }, +{ 0xD5DA, 0xD5DA, 0xD5DA }, +{ 0xD5DB, 0xD5DB, 0xD5DB }, +{ 0xD5DC, 0xD5DC, 0xD5DC }, +{ 0xD5DD, 0xD5DD, 0xD5DD }, +{ 0xD5DE, 0xD5DE, 0xD5DE }, +{ 0xD5DF, 0xD5DF, 0xD5DF }, +{ 0xD5E0, 0xD5E0, 0xD5E0 }, +{ 0xD5E1, 0xD5E1, 0xD5E1 }, +{ 0xD5E2, 0xD5E2, 0xD5E2 }, +{ 0xD5E3, 0xD5E3, 0xD5E3 }, +{ 0xD5E4, 0xD5E4, 0xD5E4 }, +{ 0xD5E5, 0xD5E5, 0xD5E5 }, +{ 0xD5E6, 0xD5E6, 0xD5E6 }, +{ 0xD5E7, 0xD5E7, 0xD5E7 }, +{ 0xD5E8, 0xD5E8, 0xD5E8 }, +{ 0xD5E9, 0xD5E9, 0xD5E9 }, +{ 0xD5EA, 0xD5EA, 0xD5EA }, +{ 0xD5EB, 0xD5EB, 0xD5EB }, +{ 0xD5EC, 0xD5EC, 0xD5EC }, +{ 0xD5ED, 0xD5ED, 0xD5ED }, +{ 0xD5EE, 0xD5EE, 0xD5EE }, +{ 0xD5EF, 0xD5EF, 0xD5EF }, +{ 0xD5F0, 0xD5F0, 0xD5F0 }, +{ 0xD5F1, 0xD5F1, 0xD5F1 }, +{ 0xD5F2, 0xD5F2, 0xD5F2 }, +{ 0xD5F3, 0xD5F3, 0xD5F3 }, +{ 0xD5F4, 0xD5F4, 0xD5F4 }, +{ 0xD5F5, 0xD5F5, 0xD5F5 }, +{ 0xD5F6, 0xD5F6, 0xD5F6 }, +{ 0xD5F7, 0xD5F7, 0xD5F7 }, +{ 0xD5F8, 0xD5F8, 0xD5F8 }, +{ 0xD5F9, 0xD5F9, 0xD5F9 }, +{ 0xD5FA, 0xD5FA, 0xD5FA }, +{ 0xD5FB, 0xD5FB, 0xD5FB }, +{ 0xD5FC, 0xD5FC, 0xD5FC }, +{ 0xD5FD, 0xD5FD, 0xD5FD }, +{ 0xD5FE, 0xD5FE, 0xD5FE }, +{ 0xD5FF, 0xD5FF, 0xD5FF }, +{ 0xD600, 0xD600, 0xD600 }, +{ 0xD601, 0xD601, 0xD601 }, +{ 0xD602, 0xD602, 0xD602 }, +{ 0xD603, 0xD603, 0xD603 }, +{ 0xD604, 0xD604, 0xD604 }, +{ 0xD605, 0xD605, 0xD605 }, +{ 0xD606, 0xD606, 0xD606 }, +{ 0xD607, 0xD607, 0xD607 }, +{ 0xD608, 0xD608, 0xD608 }, +{ 0xD609, 0xD609, 0xD609 }, +{ 0xD60A, 0xD60A, 0xD60A }, +{ 0xD60B, 0xD60B, 0xD60B }, +{ 0xD60C, 0xD60C, 0xD60C }, +{ 0xD60D, 0xD60D, 0xD60D }, +{ 0xD60E, 0xD60E, 0xD60E }, +{ 0xD60F, 0xD60F, 0xD60F }, +{ 0xD610, 0xD610, 0xD610 }, +{ 0xD611, 0xD611, 0xD611 }, +{ 0xD612, 0xD612, 0xD612 }, +{ 0xD613, 0xD613, 0xD613 }, +{ 0xD614, 0xD614, 0xD614 }, +{ 0xD615, 0xD615, 0xD615 }, +{ 0xD616, 0xD616, 0xD616 }, +{ 0xD617, 0xD617, 0xD617 }, +{ 0xD618, 0xD618, 0xD618 }, +{ 0xD619, 0xD619, 0xD619 }, +{ 0xD61A, 0xD61A, 0xD61A }, +{ 0xD61B, 0xD61B, 0xD61B }, +{ 0xD61C, 0xD61C, 0xD61C }, +{ 0xD61D, 0xD61D, 0xD61D }, +{ 0xD61E, 0xD61E, 0xD61E }, +{ 0xD61F, 0xD61F, 0xD61F }, +{ 0xD620, 0xD620, 0xD620 }, +{ 0xD621, 0xD621, 0xD621 }, +{ 0xD622, 0xD622, 0xD622 }, +{ 0xD623, 0xD623, 0xD623 }, +{ 0xD624, 0xD624, 0xD624 }, +{ 0xD625, 0xD625, 0xD625 }, +{ 0xD626, 0xD626, 0xD626 }, +{ 0xD627, 0xD627, 0xD627 }, +{ 0xD628, 0xD628, 0xD628 }, +{ 0xD629, 0xD629, 0xD629 }, +{ 0xD62A, 0xD62A, 0xD62A }, +{ 0xD62B, 0xD62B, 0xD62B }, +{ 0xD62C, 0xD62C, 0xD62C }, +{ 0xD62D, 0xD62D, 0xD62D }, +{ 0xD62E, 0xD62E, 0xD62E }, +{ 0xD62F, 0xD62F, 0xD62F }, +{ 0xD630, 0xD630, 0xD630 }, +{ 0xD631, 0xD631, 0xD631 }, +{ 0xD632, 0xD632, 0xD632 }, +{ 0xD633, 0xD633, 0xD633 }, +{ 0xD634, 0xD634, 0xD634 }, +{ 0xD635, 0xD635, 0xD635 }, +{ 0xD636, 0xD636, 0xD636 }, +{ 0xD637, 0xD637, 0xD637 }, +{ 0xD638, 0xD638, 0xD638 }, +{ 0xD639, 0xD639, 0xD639 }, +{ 0xD63A, 0xD63A, 0xD63A }, +{ 0xD63B, 0xD63B, 0xD63B }, +{ 0xD63C, 0xD63C, 0xD63C }, +{ 0xD63D, 0xD63D, 0xD63D }, +{ 0xD63E, 0xD63E, 0xD63E }, +{ 0xD63F, 0xD63F, 0xD63F }, +{ 0xD640, 0xD640, 0xD640 }, +{ 0xD641, 0xD641, 0xD641 }, +{ 0xD642, 0xD642, 0xD642 }, +{ 0xD643, 0xD643, 0xD643 }, +{ 0xD644, 0xD644, 0xD644 }, +{ 0xD645, 0xD645, 0xD645 }, +{ 0xD646, 0xD646, 0xD646 }, +{ 0xD647, 0xD647, 0xD647 }, +{ 0xD648, 0xD648, 0xD648 }, +{ 0xD649, 0xD649, 0xD649 }, +{ 0xD64A, 0xD64A, 0xD64A }, +{ 0xD64B, 0xD64B, 0xD64B }, +{ 0xD64C, 0xD64C, 0xD64C }, +{ 0xD64D, 0xD64D, 0xD64D }, +{ 0xD64E, 0xD64E, 0xD64E }, +{ 0xD64F, 0xD64F, 0xD64F }, +{ 0xD650, 0xD650, 0xD650 }, +{ 0xD651, 0xD651, 0xD651 }, +{ 0xD652, 0xD652, 0xD652 }, +{ 0xD653, 0xD653, 0xD653 }, +{ 0xD654, 0xD654, 0xD654 }, +{ 0xD655, 0xD655, 0xD655 }, +{ 0xD656, 0xD656, 0xD656 }, +{ 0xD657, 0xD657, 0xD657 }, +{ 0xD658, 0xD658, 0xD658 }, +{ 0xD659, 0xD659, 0xD659 }, +{ 0xD65A, 0xD65A, 0xD65A }, +{ 0xD65B, 0xD65B, 0xD65B }, +{ 0xD65C, 0xD65C, 0xD65C }, +{ 0xD65D, 0xD65D, 0xD65D }, +{ 0xD65E, 0xD65E, 0xD65E }, +{ 0xD65F, 0xD65F, 0xD65F }, +{ 0xD660, 0xD660, 0xD660 }, +{ 0xD661, 0xD661, 0xD661 }, +{ 0xD662, 0xD662, 0xD662 }, +{ 0xD663, 0xD663, 0xD663 }, +{ 0xD664, 0xD664, 0xD664 }, +{ 0xD665, 0xD665, 0xD665 }, +{ 0xD666, 0xD666, 0xD666 }, +{ 0xD667, 0xD667, 0xD667 }, +{ 0xD668, 0xD668, 0xD668 }, +{ 0xD669, 0xD669, 0xD669 }, +{ 0xD66A, 0xD66A, 0xD66A }, +{ 0xD66B, 0xD66B, 0xD66B }, +{ 0xD66C, 0xD66C, 0xD66C }, +{ 0xD66D, 0xD66D, 0xD66D }, +{ 0xD66E, 0xD66E, 0xD66E }, +{ 0xD66F, 0xD66F, 0xD66F }, +{ 0xD670, 0xD670, 0xD670 }, +{ 0xD671, 0xD671, 0xD671 }, +{ 0xD672, 0xD672, 0xD672 }, +{ 0xD673, 0xD673, 0xD673 }, +{ 0xD674, 0xD674, 0xD674 }, +{ 0xD675, 0xD675, 0xD675 }, +{ 0xD676, 0xD676, 0xD676 }, +{ 0xD677, 0xD677, 0xD677 }, +{ 0xD678, 0xD678, 0xD678 }, +{ 0xD679, 0xD679, 0xD679 }, +{ 0xD67A, 0xD67A, 0xD67A }, +{ 0xD67B, 0xD67B, 0xD67B }, +{ 0xD67C, 0xD67C, 0xD67C }, +{ 0xD67D, 0xD67D, 0xD67D }, +{ 0xD67E, 0xD67E, 0xD67E }, +{ 0xD67F, 0xD67F, 0xD67F }, +{ 0xD680, 0xD680, 0xD680 }, +{ 0xD681, 0xD681, 0xD681 }, +{ 0xD682, 0xD682, 0xD682 }, +{ 0xD683, 0xD683, 0xD683 }, +{ 0xD684, 0xD684, 0xD684 }, +{ 0xD685, 0xD685, 0xD685 }, +{ 0xD686, 0xD686, 0xD686 }, +{ 0xD687, 0xD687, 0xD687 }, +{ 0xD688, 0xD688, 0xD688 }, +{ 0xD689, 0xD689, 0xD689 }, +{ 0xD68A, 0xD68A, 0xD68A }, +{ 0xD68B, 0xD68B, 0xD68B }, +{ 0xD68C, 0xD68C, 0xD68C }, +{ 0xD68D, 0xD68D, 0xD68D }, +{ 0xD68E, 0xD68E, 0xD68E }, +{ 0xD68F, 0xD68F, 0xD68F }, +{ 0xD690, 0xD690, 0xD690 }, +{ 0xD691, 0xD691, 0xD691 }, +{ 0xD692, 0xD692, 0xD692 }, +{ 0xD693, 0xD693, 0xD693 }, +{ 0xD694, 0xD694, 0xD694 }, +{ 0xD695, 0xD695, 0xD695 }, +{ 0xD696, 0xD696, 0xD696 }, +{ 0xD697, 0xD697, 0xD697 }, +{ 0xD698, 0xD698, 0xD698 }, +{ 0xD699, 0xD699, 0xD699 }, +{ 0xD69A, 0xD69A, 0xD69A }, +{ 0xD69B, 0xD69B, 0xD69B }, +{ 0xD69C, 0xD69C, 0xD69C }, +{ 0xD69D, 0xD69D, 0xD69D }, +{ 0xD69E, 0xD69E, 0xD69E }, +{ 0xD69F, 0xD69F, 0xD69F }, +{ 0xD6A0, 0xD6A0, 0xD6A0 }, +{ 0xD6A1, 0xD6A1, 0xD6A1 }, +{ 0xD6A2, 0xD6A2, 0xD6A2 }, +{ 0xD6A3, 0xD6A3, 0xD6A3 }, +{ 0xD6A4, 0xD6A4, 0xD6A4 }, +{ 0xD6A5, 0xD6A5, 0xD6A5 }, +{ 0xD6A6, 0xD6A6, 0xD6A6 }, +{ 0xD6A7, 0xD6A7, 0xD6A7 }, +{ 0xD6A8, 0xD6A8, 0xD6A8 }, +{ 0xD6A9, 0xD6A9, 0xD6A9 }, +{ 0xD6AA, 0xD6AA, 0xD6AA }, +{ 0xD6AB, 0xD6AB, 0xD6AB }, +{ 0xD6AC, 0xD6AC, 0xD6AC }, +{ 0xD6AD, 0xD6AD, 0xD6AD }, +{ 0xD6AE, 0xD6AE, 0xD6AE }, +{ 0xD6AF, 0xD6AF, 0xD6AF }, +{ 0xD6B0, 0xD6B0, 0xD6B0 }, +{ 0xD6B1, 0xD6B1, 0xD6B1 }, +{ 0xD6B2, 0xD6B2, 0xD6B2 }, +{ 0xD6B3, 0xD6B3, 0xD6B3 }, +{ 0xD6B4, 0xD6B4, 0xD6B4 }, +{ 0xD6B5, 0xD6B5, 0xD6B5 }, +{ 0xD6B6, 0xD6B6, 0xD6B6 }, +{ 0xD6B7, 0xD6B7, 0xD6B7 }, +{ 0xD6B8, 0xD6B8, 0xD6B8 }, +{ 0xD6B9, 0xD6B9, 0xD6B9 }, +{ 0xD6BA, 0xD6BA, 0xD6BA }, +{ 0xD6BB, 0xD6BB, 0xD6BB }, +{ 0xD6BC, 0xD6BC, 0xD6BC }, +{ 0xD6BD, 0xD6BD, 0xD6BD }, +{ 0xD6BE, 0xD6BE, 0xD6BE }, +{ 0xD6BF, 0xD6BF, 0xD6BF }, +{ 0xD6C0, 0xD6C0, 0xD6C0 }, +{ 0xD6C1, 0xD6C1, 0xD6C1 }, +{ 0xD6C2, 0xD6C2, 0xD6C2 }, +{ 0xD6C3, 0xD6C3, 0xD6C3 }, +{ 0xD6C4, 0xD6C4, 0xD6C4 }, +{ 0xD6C5, 0xD6C5, 0xD6C5 }, +{ 0xD6C6, 0xD6C6, 0xD6C6 }, +{ 0xD6C7, 0xD6C7, 0xD6C7 }, +{ 0xD6C8, 0xD6C8, 0xD6C8 }, +{ 0xD6C9, 0xD6C9, 0xD6C9 }, +{ 0xD6CA, 0xD6CA, 0xD6CA }, +{ 0xD6CB, 0xD6CB, 0xD6CB }, +{ 0xD6CC, 0xD6CC, 0xD6CC }, +{ 0xD6CD, 0xD6CD, 0xD6CD }, +{ 0xD6CE, 0xD6CE, 0xD6CE }, +{ 0xD6CF, 0xD6CF, 0xD6CF }, +{ 0xD6D0, 0xD6D0, 0xD6D0 }, +{ 0xD6D1, 0xD6D1, 0xD6D1 }, +{ 0xD6D2, 0xD6D2, 0xD6D2 }, +{ 0xD6D3, 0xD6D3, 0xD6D3 }, +{ 0xD6D4, 0xD6D4, 0xD6D4 }, +{ 0xD6D5, 0xD6D5, 0xD6D5 }, +{ 0xD6D6, 0xD6D6, 0xD6D6 }, +{ 0xD6D7, 0xD6D7, 0xD6D7 }, +{ 0xD6D8, 0xD6D8, 0xD6D8 }, +{ 0xD6D9, 0xD6D9, 0xD6D9 }, +{ 0xD6DA, 0xD6DA, 0xD6DA }, +{ 0xD6DB, 0xD6DB, 0xD6DB }, +{ 0xD6DC, 0xD6DC, 0xD6DC }, +{ 0xD6DD, 0xD6DD, 0xD6DD }, +{ 0xD6DE, 0xD6DE, 0xD6DE }, +{ 0xD6DF, 0xD6DF, 0xD6DF }, +{ 0xD6E0, 0xD6E0, 0xD6E0 }, +{ 0xD6E1, 0xD6E1, 0xD6E1 }, +{ 0xD6E2, 0xD6E2, 0xD6E2 }, +{ 0xD6E3, 0xD6E3, 0xD6E3 }, +{ 0xD6E4, 0xD6E4, 0xD6E4 }, +{ 0xD6E5, 0xD6E5, 0xD6E5 }, +{ 0xD6E6, 0xD6E6, 0xD6E6 }, +{ 0xD6E7, 0xD6E7, 0xD6E7 }, +{ 0xD6E8, 0xD6E8, 0xD6E8 }, +{ 0xD6E9, 0xD6E9, 0xD6E9 }, +{ 0xD6EA, 0xD6EA, 0xD6EA }, +{ 0xD6EB, 0xD6EB, 0xD6EB }, +{ 0xD6EC, 0xD6EC, 0xD6EC }, +{ 0xD6ED, 0xD6ED, 0xD6ED }, +{ 0xD6EE, 0xD6EE, 0xD6EE }, +{ 0xD6EF, 0xD6EF, 0xD6EF }, +{ 0xD6F0, 0xD6F0, 0xD6F0 }, +{ 0xD6F1, 0xD6F1, 0xD6F1 }, +{ 0xD6F2, 0xD6F2, 0xD6F2 }, +{ 0xD6F3, 0xD6F3, 0xD6F3 }, +{ 0xD6F4, 0xD6F4, 0xD6F4 }, +{ 0xD6F5, 0xD6F5, 0xD6F5 }, +{ 0xD6F6, 0xD6F6, 0xD6F6 }, +{ 0xD6F7, 0xD6F7, 0xD6F7 }, +{ 0xD6F8, 0xD6F8, 0xD6F8 }, +{ 0xD6F9, 0xD6F9, 0xD6F9 }, +{ 0xD6FA, 0xD6FA, 0xD6FA }, +{ 0xD6FB, 0xD6FB, 0xD6FB }, +{ 0xD6FC, 0xD6FC, 0xD6FC }, +{ 0xD6FD, 0xD6FD, 0xD6FD }, +{ 0xD6FE, 0xD6FE, 0xD6FE }, +{ 0xD6FF, 0xD6FF, 0xD6FF }, +{ 0xD700, 0xD700, 0xD700 }, +{ 0xD701, 0xD701, 0xD701 }, +{ 0xD702, 0xD702, 0xD702 }, +{ 0xD703, 0xD703, 0xD703 }, +{ 0xD704, 0xD704, 0xD704 }, +{ 0xD705, 0xD705, 0xD705 }, +{ 0xD706, 0xD706, 0xD706 }, +{ 0xD707, 0xD707, 0xD707 }, +{ 0xD708, 0xD708, 0xD708 }, +{ 0xD709, 0xD709, 0xD709 }, +{ 0xD70A, 0xD70A, 0xD70A }, +{ 0xD70B, 0xD70B, 0xD70B }, +{ 0xD70C, 0xD70C, 0xD70C }, +{ 0xD70D, 0xD70D, 0xD70D }, +{ 0xD70E, 0xD70E, 0xD70E }, +{ 0xD70F, 0xD70F, 0xD70F }, +{ 0xD710, 0xD710, 0xD710 }, +{ 0xD711, 0xD711, 0xD711 }, +{ 0xD712, 0xD712, 0xD712 }, +{ 0xD713, 0xD713, 0xD713 }, +{ 0xD714, 0xD714, 0xD714 }, +{ 0xD715, 0xD715, 0xD715 }, +{ 0xD716, 0xD716, 0xD716 }, +{ 0xD717, 0xD717, 0xD717 }, +{ 0xD718, 0xD718, 0xD718 }, +{ 0xD719, 0xD719, 0xD719 }, +{ 0xD71A, 0xD71A, 0xD71A }, +{ 0xD71B, 0xD71B, 0xD71B }, +{ 0xD71C, 0xD71C, 0xD71C }, +{ 0xD71D, 0xD71D, 0xD71D }, +{ 0xD71E, 0xD71E, 0xD71E }, +{ 0xD71F, 0xD71F, 0xD71F }, +{ 0xD720, 0xD720, 0xD720 }, +{ 0xD721, 0xD721, 0xD721 }, +{ 0xD722, 0xD722, 0xD722 }, +{ 0xD723, 0xD723, 0xD723 }, +{ 0xD724, 0xD724, 0xD724 }, +{ 0xD725, 0xD725, 0xD725 }, +{ 0xD726, 0xD726, 0xD726 }, +{ 0xD727, 0xD727, 0xD727 }, +{ 0xD728, 0xD728, 0xD728 }, +{ 0xD729, 0xD729, 0xD729 }, +{ 0xD72A, 0xD72A, 0xD72A }, +{ 0xD72B, 0xD72B, 0xD72B }, +{ 0xD72C, 0xD72C, 0xD72C }, +{ 0xD72D, 0xD72D, 0xD72D }, +{ 0xD72E, 0xD72E, 0xD72E }, +{ 0xD72F, 0xD72F, 0xD72F }, +{ 0xD730, 0xD730, 0xD730 }, +{ 0xD731, 0xD731, 0xD731 }, +{ 0xD732, 0xD732, 0xD732 }, +{ 0xD733, 0xD733, 0xD733 }, +{ 0xD734, 0xD734, 0xD734 }, +{ 0xD735, 0xD735, 0xD735 }, +{ 0xD736, 0xD736, 0xD736 }, +{ 0xD737, 0xD737, 0xD737 }, +{ 0xD738, 0xD738, 0xD738 }, +{ 0xD739, 0xD739, 0xD739 }, +{ 0xD73A, 0xD73A, 0xD73A }, +{ 0xD73B, 0xD73B, 0xD73B }, +{ 0xD73C, 0xD73C, 0xD73C }, +{ 0xD73D, 0xD73D, 0xD73D }, +{ 0xD73E, 0xD73E, 0xD73E }, +{ 0xD73F, 0xD73F, 0xD73F }, +{ 0xD740, 0xD740, 0xD740 }, +{ 0xD741, 0xD741, 0xD741 }, +{ 0xD742, 0xD742, 0xD742 }, +{ 0xD743, 0xD743, 0xD743 }, +{ 0xD744, 0xD744, 0xD744 }, +{ 0xD745, 0xD745, 0xD745 }, +{ 0xD746, 0xD746, 0xD746 }, +{ 0xD747, 0xD747, 0xD747 }, +{ 0xD748, 0xD748, 0xD748 }, +{ 0xD749, 0xD749, 0xD749 }, +{ 0xD74A, 0xD74A, 0xD74A }, +{ 0xD74B, 0xD74B, 0xD74B }, +{ 0xD74C, 0xD74C, 0xD74C }, +{ 0xD74D, 0xD74D, 0xD74D }, +{ 0xD74E, 0xD74E, 0xD74E }, +{ 0xD74F, 0xD74F, 0xD74F }, +{ 0xD750, 0xD750, 0xD750 }, +{ 0xD751, 0xD751, 0xD751 }, +{ 0xD752, 0xD752, 0xD752 }, +{ 0xD753, 0xD753, 0xD753 }, +{ 0xD754, 0xD754, 0xD754 }, +{ 0xD755, 0xD755, 0xD755 }, +{ 0xD756, 0xD756, 0xD756 }, +{ 0xD757, 0xD757, 0xD757 }, +{ 0xD758, 0xD758, 0xD758 }, +{ 0xD759, 0xD759, 0xD759 }, +{ 0xD75A, 0xD75A, 0xD75A }, +{ 0xD75B, 0xD75B, 0xD75B }, +{ 0xD75C, 0xD75C, 0xD75C }, +{ 0xD75D, 0xD75D, 0xD75D }, +{ 0xD75E, 0xD75E, 0xD75E }, +{ 0xD75F, 0xD75F, 0xD75F }, +{ 0xD760, 0xD760, 0xD760 }, +{ 0xD761, 0xD761, 0xD761 }, +{ 0xD762, 0xD762, 0xD762 }, +{ 0xD763, 0xD763, 0xD763 }, +{ 0xD764, 0xD764, 0xD764 }, +{ 0xD765, 0xD765, 0xD765 }, +{ 0xD766, 0xD766, 0xD766 }, +{ 0xD767, 0xD767, 0xD767 }, +{ 0xD768, 0xD768, 0xD768 }, +{ 0xD769, 0xD769, 0xD769 }, +{ 0xD76A, 0xD76A, 0xD76A }, +{ 0xD76B, 0xD76B, 0xD76B }, +{ 0xD76C, 0xD76C, 0xD76C }, +{ 0xD76D, 0xD76D, 0xD76D }, +{ 0xD76E, 0xD76E, 0xD76E }, +{ 0xD76F, 0xD76F, 0xD76F }, +{ 0xD770, 0xD770, 0xD770 }, +{ 0xD771, 0xD771, 0xD771 }, +{ 0xD772, 0xD772, 0xD772 }, +{ 0xD773, 0xD773, 0xD773 }, +{ 0xD774, 0xD774, 0xD774 }, +{ 0xD775, 0xD775, 0xD775 }, +{ 0xD776, 0xD776, 0xD776 }, +{ 0xD777, 0xD777, 0xD777 }, +{ 0xD778, 0xD778, 0xD778 }, +{ 0xD779, 0xD779, 0xD779 }, +{ 0xD77A, 0xD77A, 0xD77A }, +{ 0xD77B, 0xD77B, 0xD77B }, +{ 0xD77C, 0xD77C, 0xD77C }, +{ 0xD77D, 0xD77D, 0xD77D }, +{ 0xD77E, 0xD77E, 0xD77E }, +{ 0xD77F, 0xD77F, 0xD77F }, +{ 0xD780, 0xD780, 0xD780 }, +{ 0xD781, 0xD781, 0xD781 }, +{ 0xD782, 0xD782, 0xD782 }, +{ 0xD783, 0xD783, 0xD783 }, +{ 0xD784, 0xD784, 0xD784 }, +{ 0xD785, 0xD785, 0xD785 }, +{ 0xD786, 0xD786, 0xD786 }, +{ 0xD787, 0xD787, 0xD787 }, +{ 0xD788, 0xD788, 0xD788 }, +{ 0xD789, 0xD789, 0xD789 }, +{ 0xD78A, 0xD78A, 0xD78A }, +{ 0xD78B, 0xD78B, 0xD78B }, +{ 0xD78C, 0xD78C, 0xD78C }, +{ 0xD78D, 0xD78D, 0xD78D }, +{ 0xD78E, 0xD78E, 0xD78E }, +{ 0xD78F, 0xD78F, 0xD78F }, +{ 0xD790, 0xD790, 0xD790 }, +{ 0xD791, 0xD791, 0xD791 }, +{ 0xD792, 0xD792, 0xD792 }, +{ 0xD793, 0xD793, 0xD793 }, +{ 0xD794, 0xD794, 0xD794 }, +{ 0xD795, 0xD795, 0xD795 }, +{ 0xD796, 0xD796, 0xD796 }, +{ 0xD797, 0xD797, 0xD797 }, +{ 0xD798, 0xD798, 0xD798 }, +{ 0xD799, 0xD799, 0xD799 }, +{ 0xD79A, 0xD79A, 0xD79A }, +{ 0xD79B, 0xD79B, 0xD79B }, +{ 0xD79C, 0xD79C, 0xD79C }, +{ 0xD79D, 0xD79D, 0xD79D }, +{ 0xD79E, 0xD79E, 0xD79E }, +{ 0xD79F, 0xD79F, 0xD79F }, +{ 0xD7A0, 0xD7A0, 0xD7A0 }, +{ 0xD7A1, 0xD7A1, 0xD7A1 }, +{ 0xD7A2, 0xD7A2, 0xD7A2 }, +{ 0xD7A3, 0xD7A3, 0xD7A3 }, +{ 0xF900, 0xF900, 0xF900 }, +{ 0xF901, 0xF901, 0xF901 }, +{ 0xF902, 0xF902, 0xF902 }, +{ 0xF903, 0xF903, 0xF903 }, +{ 0xF904, 0xF904, 0xF904 }, +{ 0xF905, 0xF905, 0xF905 }, +{ 0xF906, 0xF906, 0xF906 }, +{ 0xF907, 0xF907, 0xF907 }, +{ 0xF908, 0xF908, 0xF908 }, +{ 0xF909, 0xF909, 0xF909 }, +{ 0xF90A, 0xF90A, 0xF90A }, +{ 0xF90B, 0xF90B, 0xF90B }, +{ 0xF90C, 0xF90C, 0xF90C }, +{ 0xF90D, 0xF90D, 0xF90D }, +{ 0xF90E, 0xF90E, 0xF90E }, +{ 0xF90F, 0xF90F, 0xF90F }, +{ 0xF910, 0xF910, 0xF910 }, +{ 0xF911, 0xF911, 0xF911 }, +{ 0xF912, 0xF912, 0xF912 }, +{ 0xF913, 0xF913, 0xF913 }, +{ 0xF914, 0xF914, 0xF914 }, +{ 0xF915, 0xF915, 0xF915 }, +{ 0xF916, 0xF916, 0xF916 }, +{ 0xF917, 0xF917, 0xF917 }, +{ 0xF918, 0xF918, 0xF918 }, +{ 0xF919, 0xF919, 0xF919 }, +{ 0xF91A, 0xF91A, 0xF91A }, +{ 0xF91B, 0xF91B, 0xF91B }, +{ 0xF91C, 0xF91C, 0xF91C }, +{ 0xF91D, 0xF91D, 0xF91D }, +{ 0xF91E, 0xF91E, 0xF91E }, +{ 0xF91F, 0xF91F, 0xF91F }, +{ 0xF920, 0xF920, 0xF920 }, +{ 0xF921, 0xF921, 0xF921 }, +{ 0xF922, 0xF922, 0xF922 }, +{ 0xF923, 0xF923, 0xF923 }, +{ 0xF924, 0xF924, 0xF924 }, +{ 0xF925, 0xF925, 0xF925 }, +{ 0xF926, 0xF926, 0xF926 }, +{ 0xF927, 0xF927, 0xF927 }, +{ 0xF928, 0xF928, 0xF928 }, +{ 0xF929, 0xF929, 0xF929 }, +{ 0xF92A, 0xF92A, 0xF92A }, +{ 0xF92B, 0xF92B, 0xF92B }, +{ 0xF92C, 0xF92C, 0xF92C }, +{ 0xF92D, 0xF92D, 0xF92D }, +{ 0xF92E, 0xF92E, 0xF92E }, +{ 0xF92F, 0xF92F, 0xF92F }, +{ 0xF930, 0xF930, 0xF930 }, +{ 0xF931, 0xF931, 0xF931 }, +{ 0xF932, 0xF932, 0xF932 }, +{ 0xF933, 0xF933, 0xF933 }, +{ 0xF934, 0xF934, 0xF934 }, +{ 0xF935, 0xF935, 0xF935 }, +{ 0xF936, 0xF936, 0xF936 }, +{ 0xF937, 0xF937, 0xF937 }, +{ 0xF938, 0xF938, 0xF938 }, +{ 0xF939, 0xF939, 0xF939 }, +{ 0xF93A, 0xF93A, 0xF93A }, +{ 0xF93B, 0xF93B, 0xF93B }, +{ 0xF93C, 0xF93C, 0xF93C }, +{ 0xF93D, 0xF93D, 0xF93D }, +{ 0xF93E, 0xF93E, 0xF93E }, +{ 0xF93F, 0xF93F, 0xF93F }, +{ 0xF940, 0xF940, 0xF940 }, +{ 0xF941, 0xF941, 0xF941 }, +{ 0xF942, 0xF942, 0xF942 }, +{ 0xF943, 0xF943, 0xF943 }, +{ 0xF944, 0xF944, 0xF944 }, +{ 0xF945, 0xF945, 0xF945 }, +{ 0xF946, 0xF946, 0xF946 }, +{ 0xF947, 0xF947, 0xF947 }, +{ 0xF948, 0xF948, 0xF948 }, +{ 0xF949, 0xF949, 0xF949 }, +{ 0xF94A, 0xF94A, 0xF94A }, +{ 0xF94B, 0xF94B, 0xF94B }, +{ 0xF94C, 0xF94C, 0xF94C }, +{ 0xF94D, 0xF94D, 0xF94D }, +{ 0xF94E, 0xF94E, 0xF94E }, +{ 0xF94F, 0xF94F, 0xF94F }, +{ 0xF950, 0xF950, 0xF950 }, +{ 0xF951, 0xF951, 0xF951 }, +{ 0xF952, 0xF952, 0xF952 }, +{ 0xF953, 0xF953, 0xF953 }, +{ 0xF954, 0xF954, 0xF954 }, +{ 0xF955, 0xF955, 0xF955 }, +{ 0xF956, 0xF956, 0xF956 }, +{ 0xF957, 0xF957, 0xF957 }, +{ 0xF958, 0xF958, 0xF958 }, +{ 0xF959, 0xF959, 0xF959 }, +{ 0xF95A, 0xF95A, 0xF95A }, +{ 0xF95B, 0xF95B, 0xF95B }, +{ 0xF95C, 0xF95C, 0xF95C }, +{ 0xF95D, 0xF95D, 0xF95D }, +{ 0xF95E, 0xF95E, 0xF95E }, +{ 0xF95F, 0xF95F, 0xF95F }, +{ 0xF960, 0xF960, 0xF960 }, +{ 0xF961, 0xF961, 0xF961 }, +{ 0xF962, 0xF962, 0xF962 }, +{ 0xF963, 0xF963, 0xF963 }, +{ 0xF964, 0xF964, 0xF964 }, +{ 0xF965, 0xF965, 0xF965 }, +{ 0xF966, 0xF966, 0xF966 }, +{ 0xF967, 0xF967, 0xF967 }, +{ 0xF968, 0xF968, 0xF968 }, +{ 0xF969, 0xF969, 0xF969 }, +{ 0xF96A, 0xF96A, 0xF96A }, +{ 0xF96B, 0xF96B, 0xF96B }, +{ 0xF96C, 0xF96C, 0xF96C }, +{ 0xF96D, 0xF96D, 0xF96D }, +{ 0xF96E, 0xF96E, 0xF96E }, +{ 0xF96F, 0xF96F, 0xF96F }, +{ 0xF970, 0xF970, 0xF970 }, +{ 0xF971, 0xF971, 0xF971 }, +{ 0xF972, 0xF972, 0xF972 }, +{ 0xF973, 0xF973, 0xF973 }, +{ 0xF974, 0xF974, 0xF974 }, +{ 0xF975, 0xF975, 0xF975 }, +{ 0xF976, 0xF976, 0xF976 }, +{ 0xF977, 0xF977, 0xF977 }, +{ 0xF978, 0xF978, 0xF978 }, +{ 0xF979, 0xF979, 0xF979 }, +{ 0xF97A, 0xF97A, 0xF97A }, +{ 0xF97B, 0xF97B, 0xF97B }, +{ 0xF97C, 0xF97C, 0xF97C }, +{ 0xF97D, 0xF97D, 0xF97D }, +{ 0xF97E, 0xF97E, 0xF97E }, +{ 0xF97F, 0xF97F, 0xF97F }, +{ 0xF980, 0xF980, 0xF980 }, +{ 0xF981, 0xF981, 0xF981 }, +{ 0xF982, 0xF982, 0xF982 }, +{ 0xF983, 0xF983, 0xF983 }, +{ 0xF984, 0xF984, 0xF984 }, +{ 0xF985, 0xF985, 0xF985 }, +{ 0xF986, 0xF986, 0xF986 }, +{ 0xF987, 0xF987, 0xF987 }, +{ 0xF988, 0xF988, 0xF988 }, +{ 0xF989, 0xF989, 0xF989 }, +{ 0xF98A, 0xF98A, 0xF98A }, +{ 0xF98B, 0xF98B, 0xF98B }, +{ 0xF98C, 0xF98C, 0xF98C }, +{ 0xF98D, 0xF98D, 0xF98D }, +{ 0xF98E, 0xF98E, 0xF98E }, +{ 0xF98F, 0xF98F, 0xF98F }, +{ 0xF990, 0xF990, 0xF990 }, +{ 0xF991, 0xF991, 0xF991 }, +{ 0xF992, 0xF992, 0xF992 }, +{ 0xF993, 0xF993, 0xF993 }, +{ 0xF994, 0xF994, 0xF994 }, +{ 0xF995, 0xF995, 0xF995 }, +{ 0xF996, 0xF996, 0xF996 }, +{ 0xF997, 0xF997, 0xF997 }, +{ 0xF998, 0xF998, 0xF998 }, +{ 0xF999, 0xF999, 0xF999 }, +{ 0xF99A, 0xF99A, 0xF99A }, +{ 0xF99B, 0xF99B, 0xF99B }, +{ 0xF99C, 0xF99C, 0xF99C }, +{ 0xF99D, 0xF99D, 0xF99D }, +{ 0xF99E, 0xF99E, 0xF99E }, +{ 0xF99F, 0xF99F, 0xF99F }, +{ 0xF9A0, 0xF9A0, 0xF9A0 }, +{ 0xF9A1, 0xF9A1, 0xF9A1 }, +{ 0xF9A2, 0xF9A2, 0xF9A2 }, +{ 0xF9A3, 0xF9A3, 0xF9A3 }, +{ 0xF9A4, 0xF9A4, 0xF9A4 }, +{ 0xF9A5, 0xF9A5, 0xF9A5 }, +{ 0xF9A6, 0xF9A6, 0xF9A6 }, +{ 0xF9A7, 0xF9A7, 0xF9A7 }, +{ 0xF9A8, 0xF9A8, 0xF9A8 }, +{ 0xF9A9, 0xF9A9, 0xF9A9 }, +{ 0xF9AA, 0xF9AA, 0xF9AA }, +{ 0xF9AB, 0xF9AB, 0xF9AB }, +{ 0xF9AC, 0xF9AC, 0xF9AC }, +{ 0xF9AD, 0xF9AD, 0xF9AD }, +{ 0xF9AE, 0xF9AE, 0xF9AE }, +{ 0xF9AF, 0xF9AF, 0xF9AF }, +{ 0xF9B0, 0xF9B0, 0xF9B0 }, +{ 0xF9B1, 0xF9B1, 0xF9B1 }, +{ 0xF9B2, 0xF9B2, 0xF9B2 }, +{ 0xF9B3, 0xF9B3, 0xF9B3 }, +{ 0xF9B4, 0xF9B4, 0xF9B4 }, +{ 0xF9B5, 0xF9B5, 0xF9B5 }, +{ 0xF9B6, 0xF9B6, 0xF9B6 }, +{ 0xF9B7, 0xF9B7, 0xF9B7 }, +{ 0xF9B8, 0xF9B8, 0xF9B8 }, +{ 0xF9B9, 0xF9B9, 0xF9B9 }, +{ 0xF9BA, 0xF9BA, 0xF9BA }, +{ 0xF9BB, 0xF9BB, 0xF9BB }, +{ 0xF9BC, 0xF9BC, 0xF9BC }, +{ 0xF9BD, 0xF9BD, 0xF9BD }, +{ 0xF9BE, 0xF9BE, 0xF9BE }, +{ 0xF9BF, 0xF9BF, 0xF9BF }, +{ 0xF9C0, 0xF9C0, 0xF9C0 }, +{ 0xF9C1, 0xF9C1, 0xF9C1 }, +{ 0xF9C2, 0xF9C2, 0xF9C2 }, +{ 0xF9C3, 0xF9C3, 0xF9C3 }, +{ 0xF9C4, 0xF9C4, 0xF9C4 }, +{ 0xF9C5, 0xF9C5, 0xF9C5 }, +{ 0xF9C6, 0xF9C6, 0xF9C6 }, +{ 0xF9C7, 0xF9C7, 0xF9C7 }, +{ 0xF9C8, 0xF9C8, 0xF9C8 }, +{ 0xF9C9, 0xF9C9, 0xF9C9 }, +{ 0xF9CA, 0xF9CA, 0xF9CA }, +{ 0xF9CB, 0xF9CB, 0xF9CB }, +{ 0xF9CC, 0xF9CC, 0xF9CC }, +{ 0xF9CD, 0xF9CD, 0xF9CD }, +{ 0xF9CE, 0xF9CE, 0xF9CE }, +{ 0xF9CF, 0xF9CF, 0xF9CF }, +{ 0xF9D0, 0xF9D0, 0xF9D0 }, +{ 0xF9D1, 0xF9D1, 0xF9D1 }, +{ 0xF9D2, 0xF9D2, 0xF9D2 }, +{ 0xF9D3, 0xF9D3, 0xF9D3 }, +{ 0xF9D4, 0xF9D4, 0xF9D4 }, +{ 0xF9D5, 0xF9D5, 0xF9D5 }, +{ 0xF9D6, 0xF9D6, 0xF9D6 }, +{ 0xF9D7, 0xF9D7, 0xF9D7 }, +{ 0xF9D8, 0xF9D8, 0xF9D8 }, +{ 0xF9D9, 0xF9D9, 0xF9D9 }, +{ 0xF9DA, 0xF9DA, 0xF9DA }, +{ 0xF9DB, 0xF9DB, 0xF9DB }, +{ 0xF9DC, 0xF9DC, 0xF9DC }, +{ 0xF9DD, 0xF9DD, 0xF9DD }, +{ 0xF9DE, 0xF9DE, 0xF9DE }, +{ 0xF9DF, 0xF9DF, 0xF9DF }, +{ 0xF9E0, 0xF9E0, 0xF9E0 }, +{ 0xF9E1, 0xF9E1, 0xF9E1 }, +{ 0xF9E2, 0xF9E2, 0xF9E2 }, +{ 0xF9E3, 0xF9E3, 0xF9E3 }, +{ 0xF9E4, 0xF9E4, 0xF9E4 }, +{ 0xF9E5, 0xF9E5, 0xF9E5 }, +{ 0xF9E6, 0xF9E6, 0xF9E6 }, +{ 0xF9E7, 0xF9E7, 0xF9E7 }, +{ 0xF9E8, 0xF9E8, 0xF9E8 }, +{ 0xF9E9, 0xF9E9, 0xF9E9 }, +{ 0xF9EA, 0xF9EA, 0xF9EA }, +{ 0xF9EB, 0xF9EB, 0xF9EB }, +{ 0xF9EC, 0xF9EC, 0xF9EC }, +{ 0xF9ED, 0xF9ED, 0xF9ED }, +{ 0xF9EE, 0xF9EE, 0xF9EE }, +{ 0xF9EF, 0xF9EF, 0xF9EF }, +{ 0xF9F0, 0xF9F0, 0xF9F0 }, +{ 0xF9F1, 0xF9F1, 0xF9F1 }, +{ 0xF9F2, 0xF9F2, 0xF9F2 }, +{ 0xF9F3, 0xF9F3, 0xF9F3 }, +{ 0xF9F4, 0xF9F4, 0xF9F4 }, +{ 0xF9F5, 0xF9F5, 0xF9F5 }, +{ 0xF9F6, 0xF9F6, 0xF9F6 }, +{ 0xF9F7, 0xF9F7, 0xF9F7 }, +{ 0xF9F8, 0xF9F8, 0xF9F8 }, +{ 0xF9F9, 0xF9F9, 0xF9F9 }, +{ 0xF9FA, 0xF9FA, 0xF9FA }, +{ 0xF9FB, 0xF9FB, 0xF9FB }, +{ 0xF9FC, 0xF9FC, 0xF9FC }, +{ 0xF9FD, 0xF9FD, 0xF9FD }, +{ 0xF9FE, 0xF9FE, 0xF9FE }, +{ 0xF9FF, 0xF9FF, 0xF9FF }, +{ 0xFA00, 0xFA00, 0xFA00 }, +{ 0xFA01, 0xFA01, 0xFA01 }, +{ 0xFA02, 0xFA02, 0xFA02 }, +{ 0xFA03, 0xFA03, 0xFA03 }, +{ 0xFA04, 0xFA04, 0xFA04 }, +{ 0xFA05, 0xFA05, 0xFA05 }, +{ 0xFA06, 0xFA06, 0xFA06 }, +{ 0xFA07, 0xFA07, 0xFA07 }, +{ 0xFA08, 0xFA08, 0xFA08 }, +{ 0xFA09, 0xFA09, 0xFA09 }, +{ 0xFA0A, 0xFA0A, 0xFA0A }, +{ 0xFA0B, 0xFA0B, 0xFA0B }, +{ 0xFA0C, 0xFA0C, 0xFA0C }, +{ 0xFA0D, 0xFA0D, 0xFA0D }, +{ 0xFA0E, 0xFA0E, 0xFA0E }, +{ 0xFA0F, 0xFA0F, 0xFA0F }, +{ 0xFA10, 0xFA10, 0xFA10 }, +{ 0xFA11, 0xFA11, 0xFA11 }, +{ 0xFA12, 0xFA12, 0xFA12 }, +{ 0xFA13, 0xFA13, 0xFA13 }, +{ 0xFA14, 0xFA14, 0xFA14 }, +{ 0xFA15, 0xFA15, 0xFA15 }, +{ 0xFA16, 0xFA16, 0xFA16 }, +{ 0xFA17, 0xFA17, 0xFA17 }, +{ 0xFA18, 0xFA18, 0xFA18 }, +{ 0xFA19, 0xFA19, 0xFA19 }, +{ 0xFA1A, 0xFA1A, 0xFA1A }, +{ 0xFA1B, 0xFA1B, 0xFA1B }, +{ 0xFA1C, 0xFA1C, 0xFA1C }, +{ 0xFA1D, 0xFA1D, 0xFA1D }, +{ 0xFA1E, 0xFA1E, 0xFA1E }, +{ 0xFA1F, 0xFA1F, 0xFA1F }, +{ 0xFA20, 0xFA20, 0xFA20 }, +{ 0xFA21, 0xFA21, 0xFA21 }, +{ 0xFA22, 0xFA22, 0xFA22 }, +{ 0xFA23, 0xFA23, 0xFA23 }, +{ 0xFA24, 0xFA24, 0xFA24 }, +{ 0xFA25, 0xFA25, 0xFA25 }, +{ 0xFA26, 0xFA26, 0xFA26 }, +{ 0xFA27, 0xFA27, 0xFA27 }, +{ 0xFA28, 0xFA28, 0xFA28 }, +{ 0xFA29, 0xFA29, 0xFA29 }, +{ 0xFA2A, 0xFA2A, 0xFA2A }, +{ 0xFA2B, 0xFA2B, 0xFA2B }, +{ 0xFA2C, 0xFA2C, 0xFA2C }, +{ 0xFA2D, 0xFA2D, 0xFA2D }, +{ 0xFA30, 0xFA30, 0xFA30 }, +{ 0xFA31, 0xFA31, 0xFA31 }, +{ 0xFA32, 0xFA32, 0xFA32 }, +{ 0xFA33, 0xFA33, 0xFA33 }, +{ 0xFA34, 0xFA34, 0xFA34 }, +{ 0xFA35, 0xFA35, 0xFA35 }, +{ 0xFA36, 0xFA36, 0xFA36 }, +{ 0xFA37, 0xFA37, 0xFA37 }, +{ 0xFA38, 0xFA38, 0xFA38 }, +{ 0xFA39, 0xFA39, 0xFA39 }, +{ 0xFA3A, 0xFA3A, 0xFA3A }, +{ 0xFA3B, 0xFA3B, 0xFA3B }, +{ 0xFA3C, 0xFA3C, 0xFA3C }, +{ 0xFA3D, 0xFA3D, 0xFA3D }, +{ 0xFA3E, 0xFA3E, 0xFA3E }, +{ 0xFA3F, 0xFA3F, 0xFA3F }, +{ 0xFA40, 0xFA40, 0xFA40 }, +{ 0xFA41, 0xFA41, 0xFA41 }, +{ 0xFA42, 0xFA42, 0xFA42 }, +{ 0xFA43, 0xFA43, 0xFA43 }, +{ 0xFA44, 0xFA44, 0xFA44 }, +{ 0xFA45, 0xFA45, 0xFA45 }, +{ 0xFA46, 0xFA46, 0xFA46 }, +{ 0xFA47, 0xFA47, 0xFA47 }, +{ 0xFA48, 0xFA48, 0xFA48 }, +{ 0xFA49, 0xFA49, 0xFA49 }, +{ 0xFA4A, 0xFA4A, 0xFA4A }, +{ 0xFA4B, 0xFA4B, 0xFA4B }, +{ 0xFA4C, 0xFA4C, 0xFA4C }, +{ 0xFA4D, 0xFA4D, 0xFA4D }, +{ 0xFA4E, 0xFA4E, 0xFA4E }, +{ 0xFA4F, 0xFA4F, 0xFA4F }, +{ 0xFA50, 0xFA50, 0xFA50 }, +{ 0xFA51, 0xFA51, 0xFA51 }, +{ 0xFA52, 0xFA52, 0xFA52 }, +{ 0xFA53, 0xFA53, 0xFA53 }, +{ 0xFA54, 0xFA54, 0xFA54 }, +{ 0xFA55, 0xFA55, 0xFA55 }, +{ 0xFA56, 0xFA56, 0xFA56 }, +{ 0xFA57, 0xFA57, 0xFA57 }, +{ 0xFA58, 0xFA58, 0xFA58 }, +{ 0xFA59, 0xFA59, 0xFA59 }, +{ 0xFA5A, 0xFA5A, 0xFA5A }, +{ 0xFA5B, 0xFA5B, 0xFA5B }, +{ 0xFA5C, 0xFA5C, 0xFA5C }, +{ 0xFA5D, 0xFA5D, 0xFA5D }, +{ 0xFA5E, 0xFA5E, 0xFA5E }, +{ 0xFA5F, 0xFA5F, 0xFA5F }, +{ 0xFA60, 0xFA60, 0xFA60 }, +{ 0xFA61, 0xFA61, 0xFA61 }, +{ 0xFA62, 0xFA62, 0xFA62 }, +{ 0xFA63, 0xFA63, 0xFA63 }, +{ 0xFA64, 0xFA64, 0xFA64 }, +{ 0xFA65, 0xFA65, 0xFA65 }, +{ 0xFA66, 0xFA66, 0xFA66 }, +{ 0xFA67, 0xFA67, 0xFA67 }, +{ 0xFA68, 0xFA68, 0xFA68 }, +{ 0xFA69, 0xFA69, 0xFA69 }, +{ 0xFA6A, 0xFA6A, 0xFA6A }, +{ 0xFA70, 0xFA70, 0xFA70 }, +{ 0xFA71, 0xFA71, 0xFA71 }, +{ 0xFA72, 0xFA72, 0xFA72 }, +{ 0xFA73, 0xFA73, 0xFA73 }, +{ 0xFA74, 0xFA74, 0xFA74 }, +{ 0xFA75, 0xFA75, 0xFA75 }, +{ 0xFA76, 0xFA76, 0xFA76 }, +{ 0xFA77, 0xFA77, 0xFA77 }, +{ 0xFA78, 0xFA78, 0xFA78 }, +{ 0xFA79, 0xFA79, 0xFA79 }, +{ 0xFA7A, 0xFA7A, 0xFA7A }, +{ 0xFA7B, 0xFA7B, 0xFA7B }, +{ 0xFA7C, 0xFA7C, 0xFA7C }, +{ 0xFA7D, 0xFA7D, 0xFA7D }, +{ 0xFA7E, 0xFA7E, 0xFA7E }, +{ 0xFA7F, 0xFA7F, 0xFA7F }, +{ 0xFA80, 0xFA80, 0xFA80 }, +{ 0xFA81, 0xFA81, 0xFA81 }, +{ 0xFA82, 0xFA82, 0xFA82 }, +{ 0xFA83, 0xFA83, 0xFA83 }, +{ 0xFA84, 0xFA84, 0xFA84 }, +{ 0xFA85, 0xFA85, 0xFA85 }, +{ 0xFA86, 0xFA86, 0xFA86 }, +{ 0xFA87, 0xFA87, 0xFA87 }, +{ 0xFA88, 0xFA88, 0xFA88 }, +{ 0xFA89, 0xFA89, 0xFA89 }, +{ 0xFA8A, 0xFA8A, 0xFA8A }, +{ 0xFA8B, 0xFA8B, 0xFA8B }, +{ 0xFA8C, 0xFA8C, 0xFA8C }, +{ 0xFA8D, 0xFA8D, 0xFA8D }, +{ 0xFA8E, 0xFA8E, 0xFA8E }, +{ 0xFA8F, 0xFA8F, 0xFA8F }, +{ 0xFA90, 0xFA90, 0xFA90 }, +{ 0xFA91, 0xFA91, 0xFA91 }, +{ 0xFA92, 0xFA92, 0xFA92 }, +{ 0xFA93, 0xFA93, 0xFA93 }, +{ 0xFA94, 0xFA94, 0xFA94 }, +{ 0xFA95, 0xFA95, 0xFA95 }, +{ 0xFA96, 0xFA96, 0xFA96 }, +{ 0xFA97, 0xFA97, 0xFA97 }, +{ 0xFA98, 0xFA98, 0xFA98 }, +{ 0xFA99, 0xFA99, 0xFA99 }, +{ 0xFA9A, 0xFA9A, 0xFA9A }, +{ 0xFA9B, 0xFA9B, 0xFA9B }, +{ 0xFA9C, 0xFA9C, 0xFA9C }, +{ 0xFA9D, 0xFA9D, 0xFA9D }, +{ 0xFA9E, 0xFA9E, 0xFA9E }, +{ 0xFA9F, 0xFA9F, 0xFA9F }, +{ 0xFAA0, 0xFAA0, 0xFAA0 }, +{ 0xFAA1, 0xFAA1, 0xFAA1 }, +{ 0xFAA2, 0xFAA2, 0xFAA2 }, +{ 0xFAA3, 0xFAA3, 0xFAA3 }, +{ 0xFAA4, 0xFAA4, 0xFAA4 }, +{ 0xFAA5, 0xFAA5, 0xFAA5 }, +{ 0xFAA6, 0xFAA6, 0xFAA6 }, +{ 0xFAA7, 0xFAA7, 0xFAA7 }, +{ 0xFAA8, 0xFAA8, 0xFAA8 }, +{ 0xFAA9, 0xFAA9, 0xFAA9 }, +{ 0xFAAA, 0xFAAA, 0xFAAA }, +{ 0xFAAB, 0xFAAB, 0xFAAB }, +{ 0xFAAC, 0xFAAC, 0xFAAC }, +{ 0xFAAD, 0xFAAD, 0xFAAD }, +{ 0xFAAE, 0xFAAE, 0xFAAE }, +{ 0xFAAF, 0xFAAF, 0xFAAF }, +{ 0xFAB0, 0xFAB0, 0xFAB0 }, +{ 0xFAB1, 0xFAB1, 0xFAB1 }, +{ 0xFAB2, 0xFAB2, 0xFAB2 }, +{ 0xFAB3, 0xFAB3, 0xFAB3 }, +{ 0xFAB4, 0xFAB4, 0xFAB4 }, +{ 0xFAB5, 0xFAB5, 0xFAB5 }, +{ 0xFAB6, 0xFAB6, 0xFAB6 }, +{ 0xFAB7, 0xFAB7, 0xFAB7 }, +{ 0xFAB8, 0xFAB8, 0xFAB8 }, +{ 0xFAB9, 0xFAB9, 0xFAB9 }, +{ 0xFABA, 0xFABA, 0xFABA }, +{ 0xFABB, 0xFABB, 0xFABB }, +{ 0xFABC, 0xFABC, 0xFABC }, +{ 0xFABD, 0xFABD, 0xFABD }, +{ 0xFABE, 0xFABE, 0xFABE }, +{ 0xFABF, 0xFABF, 0xFABF }, +{ 0xFAC0, 0xFAC0, 0xFAC0 }, +{ 0xFAC1, 0xFAC1, 0xFAC1 }, +{ 0xFAC2, 0xFAC2, 0xFAC2 }, +{ 0xFAC3, 0xFAC3, 0xFAC3 }, +{ 0xFAC4, 0xFAC4, 0xFAC4 }, +{ 0xFAC5, 0xFAC5, 0xFAC5 }, +{ 0xFAC6, 0xFAC6, 0xFAC6 }, +{ 0xFAC7, 0xFAC7, 0xFAC7 }, +{ 0xFAC8, 0xFAC8, 0xFAC8 }, +{ 0xFAC9, 0xFAC9, 0xFAC9 }, +{ 0xFACA, 0xFACA, 0xFACA }, +{ 0xFACB, 0xFACB, 0xFACB }, +{ 0xFACC, 0xFACC, 0xFACC }, +{ 0xFACD, 0xFACD, 0xFACD }, +{ 0xFACE, 0xFACE, 0xFACE }, +{ 0xFACF, 0xFACF, 0xFACF }, +{ 0xFAD0, 0xFAD0, 0xFAD0 }, +{ 0xFAD1, 0xFAD1, 0xFAD1 }, +{ 0xFAD2, 0xFAD2, 0xFAD2 }, +{ 0xFAD3, 0xFAD3, 0xFAD3 }, +{ 0xFAD4, 0xFAD4, 0xFAD4 }, +{ 0xFAD5, 0xFAD5, 0xFAD5 }, +{ 0xFAD6, 0xFAD6, 0xFAD6 }, +{ 0xFAD7, 0xFAD7, 0xFAD7 }, +{ 0xFAD8, 0xFAD8, 0xFAD8 }, +{ 0xFAD9, 0xFAD9, 0xFAD9 }, +{ 0xFB00, 0xFB00, 0xFB00 }, +{ 0xFB01, 0xFB01, 0xFB01 }, +{ 0xFB02, 0xFB02, 0xFB02 }, +{ 0xFB03, 0xFB03, 0xFB03 }, +{ 0xFB04, 0xFB04, 0xFB04 }, +{ 0xFB05, 0xFB05, 0xFB05 }, +{ 0xFB06, 0xFB06, 0xFB06 }, +{ 0xFB13, 0xFB13, 0xFB13 }, +{ 0xFB14, 0xFB14, 0xFB14 }, +{ 0xFB15, 0xFB15, 0xFB15 }, +{ 0xFB16, 0xFB16, 0xFB16 }, +{ 0xFB17, 0xFB17, 0xFB17 }, +{ 0xFB1D, 0xFB1D, 0xFB1D }, +{ 0xFB1E, 0xFB1E, 0xFB1E }, +{ 0xFB1F, 0xFB1F, 0xFB1F }, +{ 0xFB20, 0xFB20, 0xFB20 }, +{ 0xFB21, 0xFB21, 0xFB21 }, +{ 0xFB22, 0xFB22, 0xFB22 }, +{ 0xFB23, 0xFB23, 0xFB23 }, +{ 0xFB24, 0xFB24, 0xFB24 }, +{ 0xFB25, 0xFB25, 0xFB25 }, +{ 0xFB26, 0xFB26, 0xFB26 }, +{ 0xFB27, 0xFB27, 0xFB27 }, +{ 0xFB28, 0xFB28, 0xFB28 }, +{ 0xFB2A, 0xFB2A, 0xFB2A }, +{ 0xFB2B, 0xFB2B, 0xFB2B }, +{ 0xFB2C, 0xFB2C, 0xFB2C }, +{ 0xFB2D, 0xFB2D, 0xFB2D }, +{ 0xFB2E, 0xFB2E, 0xFB2E }, +{ 0xFB2F, 0xFB2F, 0xFB2F }, +{ 0xFB30, 0xFB30, 0xFB30 }, +{ 0xFB31, 0xFB31, 0xFB31 }, +{ 0xFB32, 0xFB32, 0xFB32 }, +{ 0xFB33, 0xFB33, 0xFB33 }, +{ 0xFB34, 0xFB34, 0xFB34 }, +{ 0xFB35, 0xFB35, 0xFB35 }, +{ 0xFB36, 0xFB36, 0xFB36 }, +{ 0xFB38, 0xFB38, 0xFB38 }, +{ 0xFB39, 0xFB39, 0xFB39 }, +{ 0xFB3A, 0xFB3A, 0xFB3A }, +{ 0xFB3B, 0xFB3B, 0xFB3B }, +{ 0xFB3C, 0xFB3C, 0xFB3C }, +{ 0xFB3E, 0xFB3E, 0xFB3E }, +{ 0xFB40, 0xFB40, 0xFB40 }, +{ 0xFB41, 0xFB41, 0xFB41 }, +{ 0xFB43, 0xFB43, 0xFB43 }, +{ 0xFB44, 0xFB44, 0xFB44 }, +{ 0xFB46, 0xFB46, 0xFB46 }, +{ 0xFB47, 0xFB47, 0xFB47 }, +{ 0xFB48, 0xFB48, 0xFB48 }, +{ 0xFB49, 0xFB49, 0xFB49 }, +{ 0xFB4A, 0xFB4A, 0xFB4A }, +{ 0xFB4B, 0xFB4B, 0xFB4B }, +{ 0xFB4C, 0xFB4C, 0xFB4C }, +{ 0xFB4D, 0xFB4D, 0xFB4D }, +{ 0xFB4E, 0xFB4E, 0xFB4E }, +{ 0xFB4F, 0xFB4F, 0xFB4F }, +{ 0xFB50, 0xFB50, 0xFB50 }, +{ 0xFB51, 0xFB51, 0xFB51 }, +{ 0xFB52, 0xFB52, 0xFB52 }, +{ 0xFB53, 0xFB53, 0xFB53 }, +{ 0xFB54, 0xFB54, 0xFB54 }, +{ 0xFB55, 0xFB55, 0xFB55 }, +{ 0xFB56, 0xFB56, 0xFB56 }, +{ 0xFB57, 0xFB57, 0xFB57 }, +{ 0xFB58, 0xFB58, 0xFB58 }, +{ 0xFB59, 0xFB59, 0xFB59 }, +{ 0xFB5A, 0xFB5A, 0xFB5A }, +{ 0xFB5B, 0xFB5B, 0xFB5B }, +{ 0xFB5C, 0xFB5C, 0xFB5C }, +{ 0xFB5D, 0xFB5D, 0xFB5D }, +{ 0xFB5E, 0xFB5E, 0xFB5E }, +{ 0xFB5F, 0xFB5F, 0xFB5F }, +{ 0xFB60, 0xFB60, 0xFB60 }, +{ 0xFB61, 0xFB61, 0xFB61 }, +{ 0xFB62, 0xFB62, 0xFB62 }, +{ 0xFB63, 0xFB63, 0xFB63 }, +{ 0xFB64, 0xFB64, 0xFB64 }, +{ 0xFB65, 0xFB65, 0xFB65 }, +{ 0xFB66, 0xFB66, 0xFB66 }, +{ 0xFB67, 0xFB67, 0xFB67 }, +{ 0xFB68, 0xFB68, 0xFB68 }, +{ 0xFB69, 0xFB69, 0xFB69 }, +{ 0xFB6A, 0xFB6A, 0xFB6A }, +{ 0xFB6B, 0xFB6B, 0xFB6B }, +{ 0xFB6C, 0xFB6C, 0xFB6C }, +{ 0xFB6D, 0xFB6D, 0xFB6D }, +{ 0xFB6E, 0xFB6E, 0xFB6E }, +{ 0xFB6F, 0xFB6F, 0xFB6F }, +{ 0xFB70, 0xFB70, 0xFB70 }, +{ 0xFB71, 0xFB71, 0xFB71 }, +{ 0xFB72, 0xFB72, 0xFB72 }, +{ 0xFB73, 0xFB73, 0xFB73 }, +{ 0xFB74, 0xFB74, 0xFB74 }, +{ 0xFB75, 0xFB75, 0xFB75 }, +{ 0xFB76, 0xFB76, 0xFB76 }, +{ 0xFB77, 0xFB77, 0xFB77 }, +{ 0xFB78, 0xFB78, 0xFB78 }, +{ 0xFB79, 0xFB79, 0xFB79 }, +{ 0xFB7A, 0xFB7A, 0xFB7A }, +{ 0xFB7B, 0xFB7B, 0xFB7B }, +{ 0xFB7C, 0xFB7C, 0xFB7C }, +{ 0xFB7D, 0xFB7D, 0xFB7D }, +{ 0xFB7E, 0xFB7E, 0xFB7E }, +{ 0xFB7F, 0xFB7F, 0xFB7F }, +{ 0xFB80, 0xFB80, 0xFB80 }, +{ 0xFB81, 0xFB81, 0xFB81 }, +{ 0xFB82, 0xFB82, 0xFB82 }, +{ 0xFB83, 0xFB83, 0xFB83 }, +{ 0xFB84, 0xFB84, 0xFB84 }, +{ 0xFB85, 0xFB85, 0xFB85 }, +{ 0xFB86, 0xFB86, 0xFB86 }, +{ 0xFB87, 0xFB87, 0xFB87 }, +{ 0xFB88, 0xFB88, 0xFB88 }, +{ 0xFB89, 0xFB89, 0xFB89 }, +{ 0xFB8A, 0xFB8A, 0xFB8A }, +{ 0xFB8B, 0xFB8B, 0xFB8B }, +{ 0xFB8C, 0xFB8C, 0xFB8C }, +{ 0xFB8D, 0xFB8D, 0xFB8D }, +{ 0xFB8E, 0xFB8E, 0xFB8E }, +{ 0xFB8F, 0xFB8F, 0xFB8F }, +{ 0xFB90, 0xFB90, 0xFB90 }, +{ 0xFB91, 0xFB91, 0xFB91 }, +{ 0xFB92, 0xFB92, 0xFB92 }, +{ 0xFB93, 0xFB93, 0xFB93 }, +{ 0xFB94, 0xFB94, 0xFB94 }, +{ 0xFB95, 0xFB95, 0xFB95 }, +{ 0xFB96, 0xFB96, 0xFB96 }, +{ 0xFB97, 0xFB97, 0xFB97 }, +{ 0xFB98, 0xFB98, 0xFB98 }, +{ 0xFB99, 0xFB99, 0xFB99 }, +{ 0xFB9A, 0xFB9A, 0xFB9A }, +{ 0xFB9B, 0xFB9B, 0xFB9B }, +{ 0xFB9C, 0xFB9C, 0xFB9C }, +{ 0xFB9D, 0xFB9D, 0xFB9D }, +{ 0xFB9E, 0xFB9E, 0xFB9E }, +{ 0xFB9F, 0xFB9F, 0xFB9F }, +{ 0xFBA0, 0xFBA0, 0xFBA0 }, +{ 0xFBA1, 0xFBA1, 0xFBA1 }, +{ 0xFBA2, 0xFBA2, 0xFBA2 }, +{ 0xFBA3, 0xFBA3, 0xFBA3 }, +{ 0xFBA4, 0xFBA4, 0xFBA4 }, +{ 0xFBA5, 0xFBA5, 0xFBA5 }, +{ 0xFBA6, 0xFBA6, 0xFBA6 }, +{ 0xFBA7, 0xFBA7, 0xFBA7 }, +{ 0xFBA8, 0xFBA8, 0xFBA8 }, +{ 0xFBA9, 0xFBA9, 0xFBA9 }, +{ 0xFBAA, 0xFBAA, 0xFBAA }, +{ 0xFBAB, 0xFBAB, 0xFBAB }, +{ 0xFBAC, 0xFBAC, 0xFBAC }, +{ 0xFBAD, 0xFBAD, 0xFBAD }, +{ 0xFBAE, 0xFBAE, 0xFBAE }, +{ 0xFBAF, 0xFBAF, 0xFBAF }, +{ 0xFBB0, 0xFBB0, 0xFBB0 }, +{ 0xFBB1, 0xFBB1, 0xFBB1 }, +{ 0xFBD3, 0xFBD3, 0xFBD3 }, +{ 0xFBD4, 0xFBD4, 0xFBD4 }, +{ 0xFBD5, 0xFBD5, 0xFBD5 }, +{ 0xFBD6, 0xFBD6, 0xFBD6 }, +{ 0xFBD7, 0xFBD7, 0xFBD7 }, +{ 0xFBD8, 0xFBD8, 0xFBD8 }, +{ 0xFBD9, 0xFBD9, 0xFBD9 }, +{ 0xFBDA, 0xFBDA, 0xFBDA }, +{ 0xFBDB, 0xFBDB, 0xFBDB }, +{ 0xFBDC, 0xFBDC, 0xFBDC }, +{ 0xFBDD, 0xFBDD, 0xFBDD }, +{ 0xFBDE, 0xFBDE, 0xFBDE }, +{ 0xFBDF, 0xFBDF, 0xFBDF }, +{ 0xFBE0, 0xFBE0, 0xFBE0 }, +{ 0xFBE1, 0xFBE1, 0xFBE1 }, +{ 0xFBE2, 0xFBE2, 0xFBE2 }, +{ 0xFBE3, 0xFBE3, 0xFBE3 }, +{ 0xFBE4, 0xFBE4, 0xFBE4 }, +{ 0xFBE5, 0xFBE5, 0xFBE5 }, +{ 0xFBE6, 0xFBE6, 0xFBE6 }, +{ 0xFBE7, 0xFBE7, 0xFBE7 }, +{ 0xFBE8, 0xFBE8, 0xFBE8 }, +{ 0xFBE9, 0xFBE9, 0xFBE9 }, +{ 0xFBEA, 0xFBEA, 0xFBEA }, +{ 0xFBEB, 0xFBEB, 0xFBEB }, +{ 0xFBEC, 0xFBEC, 0xFBEC }, +{ 0xFBED, 0xFBED, 0xFBED }, +{ 0xFBEE, 0xFBEE, 0xFBEE }, +{ 0xFBEF, 0xFBEF, 0xFBEF }, +{ 0xFBF0, 0xFBF0, 0xFBF0 }, +{ 0xFBF1, 0xFBF1, 0xFBF1 }, +{ 0xFBF2, 0xFBF2, 0xFBF2 }, +{ 0xFBF3, 0xFBF3, 0xFBF3 }, +{ 0xFBF4, 0xFBF4, 0xFBF4 }, +{ 0xFBF5, 0xFBF5, 0xFBF5 }, +{ 0xFBF6, 0xFBF6, 0xFBF6 }, +{ 0xFBF7, 0xFBF7, 0xFBF7 }, +{ 0xFBF8, 0xFBF8, 0xFBF8 }, +{ 0xFBF9, 0xFBF9, 0xFBF9 }, +{ 0xFBFA, 0xFBFA, 0xFBFA }, +{ 0xFBFB, 0xFBFB, 0xFBFB }, +{ 0xFBFC, 0xFBFC, 0xFBFC }, +{ 0xFBFD, 0xFBFD, 0xFBFD }, +{ 0xFBFE, 0xFBFE, 0xFBFE }, +{ 0xFBFF, 0xFBFF, 0xFBFF }, +{ 0xFC00, 0xFC00, 0xFC00 }, +{ 0xFC01, 0xFC01, 0xFC01 }, +{ 0xFC02, 0xFC02, 0xFC02 }, +{ 0xFC03, 0xFC03, 0xFC03 }, +{ 0xFC04, 0xFC04, 0xFC04 }, +{ 0xFC05, 0xFC05, 0xFC05 }, +{ 0xFC06, 0xFC06, 0xFC06 }, +{ 0xFC07, 0xFC07, 0xFC07 }, +{ 0xFC08, 0xFC08, 0xFC08 }, +{ 0xFC09, 0xFC09, 0xFC09 }, +{ 0xFC0A, 0xFC0A, 0xFC0A }, +{ 0xFC0B, 0xFC0B, 0xFC0B }, +{ 0xFC0C, 0xFC0C, 0xFC0C }, +{ 0xFC0D, 0xFC0D, 0xFC0D }, +{ 0xFC0E, 0xFC0E, 0xFC0E }, +{ 0xFC0F, 0xFC0F, 0xFC0F }, +{ 0xFC10, 0xFC10, 0xFC10 }, +{ 0xFC11, 0xFC11, 0xFC11 }, +{ 0xFC12, 0xFC12, 0xFC12 }, +{ 0xFC13, 0xFC13, 0xFC13 }, +{ 0xFC14, 0xFC14, 0xFC14 }, +{ 0xFC15, 0xFC15, 0xFC15 }, +{ 0xFC16, 0xFC16, 0xFC16 }, +{ 0xFC17, 0xFC17, 0xFC17 }, +{ 0xFC18, 0xFC18, 0xFC18 }, +{ 0xFC19, 0xFC19, 0xFC19 }, +{ 0xFC1A, 0xFC1A, 0xFC1A }, +{ 0xFC1B, 0xFC1B, 0xFC1B }, +{ 0xFC1C, 0xFC1C, 0xFC1C }, +{ 0xFC1D, 0xFC1D, 0xFC1D }, +{ 0xFC1E, 0xFC1E, 0xFC1E }, +{ 0xFC1F, 0xFC1F, 0xFC1F }, +{ 0xFC20, 0xFC20, 0xFC20 }, +{ 0xFC21, 0xFC21, 0xFC21 }, +{ 0xFC22, 0xFC22, 0xFC22 }, +{ 0xFC23, 0xFC23, 0xFC23 }, +{ 0xFC24, 0xFC24, 0xFC24 }, +{ 0xFC25, 0xFC25, 0xFC25 }, +{ 0xFC26, 0xFC26, 0xFC26 }, +{ 0xFC27, 0xFC27, 0xFC27 }, +{ 0xFC28, 0xFC28, 0xFC28 }, +{ 0xFC29, 0xFC29, 0xFC29 }, +{ 0xFC2A, 0xFC2A, 0xFC2A }, +{ 0xFC2B, 0xFC2B, 0xFC2B }, +{ 0xFC2C, 0xFC2C, 0xFC2C }, +{ 0xFC2D, 0xFC2D, 0xFC2D }, +{ 0xFC2E, 0xFC2E, 0xFC2E }, +{ 0xFC2F, 0xFC2F, 0xFC2F }, +{ 0xFC30, 0xFC30, 0xFC30 }, +{ 0xFC31, 0xFC31, 0xFC31 }, +{ 0xFC32, 0xFC32, 0xFC32 }, +{ 0xFC33, 0xFC33, 0xFC33 }, +{ 0xFC34, 0xFC34, 0xFC34 }, +{ 0xFC35, 0xFC35, 0xFC35 }, +{ 0xFC36, 0xFC36, 0xFC36 }, +{ 0xFC37, 0xFC37, 0xFC37 }, +{ 0xFC38, 0xFC38, 0xFC38 }, +{ 0xFC39, 0xFC39, 0xFC39 }, +{ 0xFC3A, 0xFC3A, 0xFC3A }, +{ 0xFC3B, 0xFC3B, 0xFC3B }, +{ 0xFC3C, 0xFC3C, 0xFC3C }, +{ 0xFC3D, 0xFC3D, 0xFC3D }, +{ 0xFC3E, 0xFC3E, 0xFC3E }, +{ 0xFC3F, 0xFC3F, 0xFC3F }, +{ 0xFC40, 0xFC40, 0xFC40 }, +{ 0xFC41, 0xFC41, 0xFC41 }, +{ 0xFC42, 0xFC42, 0xFC42 }, +{ 0xFC43, 0xFC43, 0xFC43 }, +{ 0xFC44, 0xFC44, 0xFC44 }, +{ 0xFC45, 0xFC45, 0xFC45 }, +{ 0xFC46, 0xFC46, 0xFC46 }, +{ 0xFC47, 0xFC47, 0xFC47 }, +{ 0xFC48, 0xFC48, 0xFC48 }, +{ 0xFC49, 0xFC49, 0xFC49 }, +{ 0xFC4A, 0xFC4A, 0xFC4A }, +{ 0xFC4B, 0xFC4B, 0xFC4B }, +{ 0xFC4C, 0xFC4C, 0xFC4C }, +{ 0xFC4D, 0xFC4D, 0xFC4D }, +{ 0xFC4E, 0xFC4E, 0xFC4E }, +{ 0xFC4F, 0xFC4F, 0xFC4F }, +{ 0xFC50, 0xFC50, 0xFC50 }, +{ 0xFC51, 0xFC51, 0xFC51 }, +{ 0xFC52, 0xFC52, 0xFC52 }, +{ 0xFC53, 0xFC53, 0xFC53 }, +{ 0xFC54, 0xFC54, 0xFC54 }, +{ 0xFC55, 0xFC55, 0xFC55 }, +{ 0xFC56, 0xFC56, 0xFC56 }, +{ 0xFC57, 0xFC57, 0xFC57 }, +{ 0xFC58, 0xFC58, 0xFC58 }, +{ 0xFC59, 0xFC59, 0xFC59 }, +{ 0xFC5A, 0xFC5A, 0xFC5A }, +{ 0xFC5B, 0xFC5B, 0xFC5B }, +{ 0xFC5C, 0xFC5C, 0xFC5C }, +{ 0xFC5D, 0xFC5D, 0xFC5D }, +{ 0xFC5E, 0xFC5E, 0xFC5E }, +{ 0xFC5F, 0xFC5F, 0xFC5F }, +{ 0xFC60, 0xFC60, 0xFC60 }, +{ 0xFC61, 0xFC61, 0xFC61 }, +{ 0xFC62, 0xFC62, 0xFC62 }, +{ 0xFC63, 0xFC63, 0xFC63 }, +{ 0xFC64, 0xFC64, 0xFC64 }, +{ 0xFC65, 0xFC65, 0xFC65 }, +{ 0xFC66, 0xFC66, 0xFC66 }, +{ 0xFC67, 0xFC67, 0xFC67 }, +{ 0xFC68, 0xFC68, 0xFC68 }, +{ 0xFC69, 0xFC69, 0xFC69 }, +{ 0xFC6A, 0xFC6A, 0xFC6A }, +{ 0xFC6B, 0xFC6B, 0xFC6B }, +{ 0xFC6C, 0xFC6C, 0xFC6C }, +{ 0xFC6D, 0xFC6D, 0xFC6D }, +{ 0xFC6E, 0xFC6E, 0xFC6E }, +{ 0xFC6F, 0xFC6F, 0xFC6F }, +{ 0xFC70, 0xFC70, 0xFC70 }, +{ 0xFC71, 0xFC71, 0xFC71 }, +{ 0xFC72, 0xFC72, 0xFC72 }, +{ 0xFC73, 0xFC73, 0xFC73 }, +{ 0xFC74, 0xFC74, 0xFC74 }, +{ 0xFC75, 0xFC75, 0xFC75 }, +{ 0xFC76, 0xFC76, 0xFC76 }, +{ 0xFC77, 0xFC77, 0xFC77 }, +{ 0xFC78, 0xFC78, 0xFC78 }, +{ 0xFC79, 0xFC79, 0xFC79 }, +{ 0xFC7A, 0xFC7A, 0xFC7A }, +{ 0xFC7B, 0xFC7B, 0xFC7B }, +{ 0xFC7C, 0xFC7C, 0xFC7C }, +{ 0xFC7D, 0xFC7D, 0xFC7D }, +{ 0xFC7E, 0xFC7E, 0xFC7E }, +{ 0xFC7F, 0xFC7F, 0xFC7F }, +{ 0xFC80, 0xFC80, 0xFC80 }, +{ 0xFC81, 0xFC81, 0xFC81 }, +{ 0xFC82, 0xFC82, 0xFC82 }, +{ 0xFC83, 0xFC83, 0xFC83 }, +{ 0xFC84, 0xFC84, 0xFC84 }, +{ 0xFC85, 0xFC85, 0xFC85 }, +{ 0xFC86, 0xFC86, 0xFC86 }, +{ 0xFC87, 0xFC87, 0xFC87 }, +{ 0xFC88, 0xFC88, 0xFC88 }, +{ 0xFC89, 0xFC89, 0xFC89 }, +{ 0xFC8A, 0xFC8A, 0xFC8A }, +{ 0xFC8B, 0xFC8B, 0xFC8B }, +{ 0xFC8C, 0xFC8C, 0xFC8C }, +{ 0xFC8D, 0xFC8D, 0xFC8D }, +{ 0xFC8E, 0xFC8E, 0xFC8E }, +{ 0xFC8F, 0xFC8F, 0xFC8F }, +{ 0xFC90, 0xFC90, 0xFC90 }, +{ 0xFC91, 0xFC91, 0xFC91 }, +{ 0xFC92, 0xFC92, 0xFC92 }, +{ 0xFC93, 0xFC93, 0xFC93 }, +{ 0xFC94, 0xFC94, 0xFC94 }, +{ 0xFC95, 0xFC95, 0xFC95 }, +{ 0xFC96, 0xFC96, 0xFC96 }, +{ 0xFC97, 0xFC97, 0xFC97 }, +{ 0xFC98, 0xFC98, 0xFC98 }, +{ 0xFC99, 0xFC99, 0xFC99 }, +{ 0xFC9A, 0xFC9A, 0xFC9A }, +{ 0xFC9B, 0xFC9B, 0xFC9B }, +{ 0xFC9C, 0xFC9C, 0xFC9C }, +{ 0xFC9D, 0xFC9D, 0xFC9D }, +{ 0xFC9E, 0xFC9E, 0xFC9E }, +{ 0xFC9F, 0xFC9F, 0xFC9F }, +{ 0xFCA0, 0xFCA0, 0xFCA0 }, +{ 0xFCA1, 0xFCA1, 0xFCA1 }, +{ 0xFCA2, 0xFCA2, 0xFCA2 }, +{ 0xFCA3, 0xFCA3, 0xFCA3 }, +{ 0xFCA4, 0xFCA4, 0xFCA4 }, +{ 0xFCA5, 0xFCA5, 0xFCA5 }, +{ 0xFCA6, 0xFCA6, 0xFCA6 }, +{ 0xFCA7, 0xFCA7, 0xFCA7 }, +{ 0xFCA8, 0xFCA8, 0xFCA8 }, +{ 0xFCA9, 0xFCA9, 0xFCA9 }, +{ 0xFCAA, 0xFCAA, 0xFCAA }, +{ 0xFCAB, 0xFCAB, 0xFCAB }, +{ 0xFCAC, 0xFCAC, 0xFCAC }, +{ 0xFCAD, 0xFCAD, 0xFCAD }, +{ 0xFCAE, 0xFCAE, 0xFCAE }, +{ 0xFCAF, 0xFCAF, 0xFCAF }, +{ 0xFCB0, 0xFCB0, 0xFCB0 }, +{ 0xFCB1, 0xFCB1, 0xFCB1 }, +{ 0xFCB2, 0xFCB2, 0xFCB2 }, +{ 0xFCB3, 0xFCB3, 0xFCB3 }, +{ 0xFCB4, 0xFCB4, 0xFCB4 }, +{ 0xFCB5, 0xFCB5, 0xFCB5 }, +{ 0xFCB6, 0xFCB6, 0xFCB6 }, +{ 0xFCB7, 0xFCB7, 0xFCB7 }, +{ 0xFCB8, 0xFCB8, 0xFCB8 }, +{ 0xFCB9, 0xFCB9, 0xFCB9 }, +{ 0xFCBA, 0xFCBA, 0xFCBA }, +{ 0xFCBB, 0xFCBB, 0xFCBB }, +{ 0xFCBC, 0xFCBC, 0xFCBC }, +{ 0xFCBD, 0xFCBD, 0xFCBD }, +{ 0xFCBE, 0xFCBE, 0xFCBE }, +{ 0xFCBF, 0xFCBF, 0xFCBF }, +{ 0xFCC0, 0xFCC0, 0xFCC0 }, +{ 0xFCC1, 0xFCC1, 0xFCC1 }, +{ 0xFCC2, 0xFCC2, 0xFCC2 }, +{ 0xFCC3, 0xFCC3, 0xFCC3 }, +{ 0xFCC4, 0xFCC4, 0xFCC4 }, +{ 0xFCC5, 0xFCC5, 0xFCC5 }, +{ 0xFCC6, 0xFCC6, 0xFCC6 }, +{ 0xFCC7, 0xFCC7, 0xFCC7 }, +{ 0xFCC8, 0xFCC8, 0xFCC8 }, +{ 0xFCC9, 0xFCC9, 0xFCC9 }, +{ 0xFCCA, 0xFCCA, 0xFCCA }, +{ 0xFCCB, 0xFCCB, 0xFCCB }, +{ 0xFCCC, 0xFCCC, 0xFCCC }, +{ 0xFCCD, 0xFCCD, 0xFCCD }, +{ 0xFCCE, 0xFCCE, 0xFCCE }, +{ 0xFCCF, 0xFCCF, 0xFCCF }, +{ 0xFCD0, 0xFCD0, 0xFCD0 }, +{ 0xFCD1, 0xFCD1, 0xFCD1 }, +{ 0xFCD2, 0xFCD2, 0xFCD2 }, +{ 0xFCD3, 0xFCD3, 0xFCD3 }, +{ 0xFCD4, 0xFCD4, 0xFCD4 }, +{ 0xFCD5, 0xFCD5, 0xFCD5 }, +{ 0xFCD6, 0xFCD6, 0xFCD6 }, +{ 0xFCD7, 0xFCD7, 0xFCD7 }, +{ 0xFCD8, 0xFCD8, 0xFCD8 }, +{ 0xFCD9, 0xFCD9, 0xFCD9 }, +{ 0xFCDA, 0xFCDA, 0xFCDA }, +{ 0xFCDB, 0xFCDB, 0xFCDB }, +{ 0xFCDC, 0xFCDC, 0xFCDC }, +{ 0xFCDD, 0xFCDD, 0xFCDD }, +{ 0xFCDE, 0xFCDE, 0xFCDE }, +{ 0xFCDF, 0xFCDF, 0xFCDF }, +{ 0xFCE0, 0xFCE0, 0xFCE0 }, +{ 0xFCE1, 0xFCE1, 0xFCE1 }, +{ 0xFCE2, 0xFCE2, 0xFCE2 }, +{ 0xFCE3, 0xFCE3, 0xFCE3 }, +{ 0xFCE4, 0xFCE4, 0xFCE4 }, +{ 0xFCE5, 0xFCE5, 0xFCE5 }, +{ 0xFCE6, 0xFCE6, 0xFCE6 }, +{ 0xFCE7, 0xFCE7, 0xFCE7 }, +{ 0xFCE8, 0xFCE8, 0xFCE8 }, +{ 0xFCE9, 0xFCE9, 0xFCE9 }, +{ 0xFCEA, 0xFCEA, 0xFCEA }, +{ 0xFCEB, 0xFCEB, 0xFCEB }, +{ 0xFCEC, 0xFCEC, 0xFCEC }, +{ 0xFCED, 0xFCED, 0xFCED }, +{ 0xFCEE, 0xFCEE, 0xFCEE }, +{ 0xFCEF, 0xFCEF, 0xFCEF }, +{ 0xFCF0, 0xFCF0, 0xFCF0 }, +{ 0xFCF1, 0xFCF1, 0xFCF1 }, +{ 0xFCF2, 0xFCF2, 0xFCF2 }, +{ 0xFCF3, 0xFCF3, 0xFCF3 }, +{ 0xFCF4, 0xFCF4, 0xFCF4 }, +{ 0xFCF5, 0xFCF5, 0xFCF5 }, +{ 0xFCF6, 0xFCF6, 0xFCF6 }, +{ 0xFCF7, 0xFCF7, 0xFCF7 }, +{ 0xFCF8, 0xFCF8, 0xFCF8 }, +{ 0xFCF9, 0xFCF9, 0xFCF9 }, +{ 0xFCFA, 0xFCFA, 0xFCFA }, +{ 0xFCFB, 0xFCFB, 0xFCFB }, +{ 0xFCFC, 0xFCFC, 0xFCFC }, +{ 0xFCFD, 0xFCFD, 0xFCFD }, +{ 0xFCFE, 0xFCFE, 0xFCFE }, +{ 0xFCFF, 0xFCFF, 0xFCFF }, +{ 0xFD00, 0xFD00, 0xFD00 }, +{ 0xFD01, 0xFD01, 0xFD01 }, +{ 0xFD02, 0xFD02, 0xFD02 }, +{ 0xFD03, 0xFD03, 0xFD03 }, +{ 0xFD04, 0xFD04, 0xFD04 }, +{ 0xFD05, 0xFD05, 0xFD05 }, +{ 0xFD06, 0xFD06, 0xFD06 }, +{ 0xFD07, 0xFD07, 0xFD07 }, +{ 0xFD08, 0xFD08, 0xFD08 }, +{ 0xFD09, 0xFD09, 0xFD09 }, +{ 0xFD0A, 0xFD0A, 0xFD0A }, +{ 0xFD0B, 0xFD0B, 0xFD0B }, +{ 0xFD0C, 0xFD0C, 0xFD0C }, +{ 0xFD0D, 0xFD0D, 0xFD0D }, +{ 0xFD0E, 0xFD0E, 0xFD0E }, +{ 0xFD0F, 0xFD0F, 0xFD0F }, +{ 0xFD10, 0xFD10, 0xFD10 }, +{ 0xFD11, 0xFD11, 0xFD11 }, +{ 0xFD12, 0xFD12, 0xFD12 }, +{ 0xFD13, 0xFD13, 0xFD13 }, +{ 0xFD14, 0xFD14, 0xFD14 }, +{ 0xFD15, 0xFD15, 0xFD15 }, +{ 0xFD16, 0xFD16, 0xFD16 }, +{ 0xFD17, 0xFD17, 0xFD17 }, +{ 0xFD18, 0xFD18, 0xFD18 }, +{ 0xFD19, 0xFD19, 0xFD19 }, +{ 0xFD1A, 0xFD1A, 0xFD1A }, +{ 0xFD1B, 0xFD1B, 0xFD1B }, +{ 0xFD1C, 0xFD1C, 0xFD1C }, +{ 0xFD1D, 0xFD1D, 0xFD1D }, +{ 0xFD1E, 0xFD1E, 0xFD1E }, +{ 0xFD1F, 0xFD1F, 0xFD1F }, +{ 0xFD20, 0xFD20, 0xFD20 }, +{ 0xFD21, 0xFD21, 0xFD21 }, +{ 0xFD22, 0xFD22, 0xFD22 }, +{ 0xFD23, 0xFD23, 0xFD23 }, +{ 0xFD24, 0xFD24, 0xFD24 }, +{ 0xFD25, 0xFD25, 0xFD25 }, +{ 0xFD26, 0xFD26, 0xFD26 }, +{ 0xFD27, 0xFD27, 0xFD27 }, +{ 0xFD28, 0xFD28, 0xFD28 }, +{ 0xFD29, 0xFD29, 0xFD29 }, +{ 0xFD2A, 0xFD2A, 0xFD2A }, +{ 0xFD2B, 0xFD2B, 0xFD2B }, +{ 0xFD2C, 0xFD2C, 0xFD2C }, +{ 0xFD2D, 0xFD2D, 0xFD2D }, +{ 0xFD2E, 0xFD2E, 0xFD2E }, +{ 0xFD2F, 0xFD2F, 0xFD2F }, +{ 0xFD30, 0xFD30, 0xFD30 }, +{ 0xFD31, 0xFD31, 0xFD31 }, +{ 0xFD32, 0xFD32, 0xFD32 }, +{ 0xFD33, 0xFD33, 0xFD33 }, +{ 0xFD34, 0xFD34, 0xFD34 }, +{ 0xFD35, 0xFD35, 0xFD35 }, +{ 0xFD36, 0xFD36, 0xFD36 }, +{ 0xFD37, 0xFD37, 0xFD37 }, +{ 0xFD38, 0xFD38, 0xFD38 }, +{ 0xFD39, 0xFD39, 0xFD39 }, +{ 0xFD3A, 0xFD3A, 0xFD3A }, +{ 0xFD3B, 0xFD3B, 0xFD3B }, +{ 0xFD3C, 0xFD3C, 0xFD3C }, +{ 0xFD3D, 0xFD3D, 0xFD3D }, +{ 0xFD50, 0xFD50, 0xFD50 }, +{ 0xFD51, 0xFD51, 0xFD51 }, +{ 0xFD52, 0xFD52, 0xFD52 }, +{ 0xFD53, 0xFD53, 0xFD53 }, +{ 0xFD54, 0xFD54, 0xFD54 }, +{ 0xFD55, 0xFD55, 0xFD55 }, +{ 0xFD56, 0xFD56, 0xFD56 }, +{ 0xFD57, 0xFD57, 0xFD57 }, +{ 0xFD58, 0xFD58, 0xFD58 }, +{ 0xFD59, 0xFD59, 0xFD59 }, +{ 0xFD5A, 0xFD5A, 0xFD5A }, +{ 0xFD5B, 0xFD5B, 0xFD5B }, +{ 0xFD5C, 0xFD5C, 0xFD5C }, +{ 0xFD5D, 0xFD5D, 0xFD5D }, +{ 0xFD5E, 0xFD5E, 0xFD5E }, +{ 0xFD5F, 0xFD5F, 0xFD5F }, +{ 0xFD60, 0xFD60, 0xFD60 }, +{ 0xFD61, 0xFD61, 0xFD61 }, +{ 0xFD62, 0xFD62, 0xFD62 }, +{ 0xFD63, 0xFD63, 0xFD63 }, +{ 0xFD64, 0xFD64, 0xFD64 }, +{ 0xFD65, 0xFD65, 0xFD65 }, +{ 0xFD66, 0xFD66, 0xFD66 }, +{ 0xFD67, 0xFD67, 0xFD67 }, +{ 0xFD68, 0xFD68, 0xFD68 }, +{ 0xFD69, 0xFD69, 0xFD69 }, +{ 0xFD6A, 0xFD6A, 0xFD6A }, +{ 0xFD6B, 0xFD6B, 0xFD6B }, +{ 0xFD6C, 0xFD6C, 0xFD6C }, +{ 0xFD6D, 0xFD6D, 0xFD6D }, +{ 0xFD6E, 0xFD6E, 0xFD6E }, +{ 0xFD6F, 0xFD6F, 0xFD6F }, +{ 0xFD70, 0xFD70, 0xFD70 }, +{ 0xFD71, 0xFD71, 0xFD71 }, +{ 0xFD72, 0xFD72, 0xFD72 }, +{ 0xFD73, 0xFD73, 0xFD73 }, +{ 0xFD74, 0xFD74, 0xFD74 }, +{ 0xFD75, 0xFD75, 0xFD75 }, +{ 0xFD76, 0xFD76, 0xFD76 }, +{ 0xFD77, 0xFD77, 0xFD77 }, +{ 0xFD78, 0xFD78, 0xFD78 }, +{ 0xFD79, 0xFD79, 0xFD79 }, +{ 0xFD7A, 0xFD7A, 0xFD7A }, +{ 0xFD7B, 0xFD7B, 0xFD7B }, +{ 0xFD7C, 0xFD7C, 0xFD7C }, +{ 0xFD7D, 0xFD7D, 0xFD7D }, +{ 0xFD7E, 0xFD7E, 0xFD7E }, +{ 0xFD7F, 0xFD7F, 0xFD7F }, +{ 0xFD80, 0xFD80, 0xFD80 }, +{ 0xFD81, 0xFD81, 0xFD81 }, +{ 0xFD82, 0xFD82, 0xFD82 }, +{ 0xFD83, 0xFD83, 0xFD83 }, +{ 0xFD84, 0xFD84, 0xFD84 }, +{ 0xFD85, 0xFD85, 0xFD85 }, +{ 0xFD86, 0xFD86, 0xFD86 }, +{ 0xFD87, 0xFD87, 0xFD87 }, +{ 0xFD88, 0xFD88, 0xFD88 }, +{ 0xFD89, 0xFD89, 0xFD89 }, +{ 0xFD8A, 0xFD8A, 0xFD8A }, +{ 0xFD8B, 0xFD8B, 0xFD8B }, +{ 0xFD8C, 0xFD8C, 0xFD8C }, +{ 0xFD8D, 0xFD8D, 0xFD8D }, +{ 0xFD8E, 0xFD8E, 0xFD8E }, +{ 0xFD8F, 0xFD8F, 0xFD8F }, +{ 0xFD92, 0xFD92, 0xFD92 }, +{ 0xFD93, 0xFD93, 0xFD93 }, +{ 0xFD94, 0xFD94, 0xFD94 }, +{ 0xFD95, 0xFD95, 0xFD95 }, +{ 0xFD96, 0xFD96, 0xFD96 }, +{ 0xFD97, 0xFD97, 0xFD97 }, +{ 0xFD98, 0xFD98, 0xFD98 }, +{ 0xFD99, 0xFD99, 0xFD99 }, +{ 0xFD9A, 0xFD9A, 0xFD9A }, +{ 0xFD9B, 0xFD9B, 0xFD9B }, +{ 0xFD9C, 0xFD9C, 0xFD9C }, +{ 0xFD9D, 0xFD9D, 0xFD9D }, +{ 0xFD9E, 0xFD9E, 0xFD9E }, +{ 0xFD9F, 0xFD9F, 0xFD9F }, +{ 0xFDA0, 0xFDA0, 0xFDA0 }, +{ 0xFDA1, 0xFDA1, 0xFDA1 }, +{ 0xFDA2, 0xFDA2, 0xFDA2 }, +{ 0xFDA3, 0xFDA3, 0xFDA3 }, +{ 0xFDA4, 0xFDA4, 0xFDA4 }, +{ 0xFDA5, 0xFDA5, 0xFDA5 }, +{ 0xFDA6, 0xFDA6, 0xFDA6 }, +{ 0xFDA7, 0xFDA7, 0xFDA7 }, +{ 0xFDA8, 0xFDA8, 0xFDA8 }, +{ 0xFDA9, 0xFDA9, 0xFDA9 }, +{ 0xFDAA, 0xFDAA, 0xFDAA }, +{ 0xFDAB, 0xFDAB, 0xFDAB }, +{ 0xFDAC, 0xFDAC, 0xFDAC }, +{ 0xFDAD, 0xFDAD, 0xFDAD }, +{ 0xFDAE, 0xFDAE, 0xFDAE }, +{ 0xFDAF, 0xFDAF, 0xFDAF }, +{ 0xFDB0, 0xFDB0, 0xFDB0 }, +{ 0xFDB1, 0xFDB1, 0xFDB1 }, +{ 0xFDB2, 0xFDB2, 0xFDB2 }, +{ 0xFDB3, 0xFDB3, 0xFDB3 }, +{ 0xFDB4, 0xFDB4, 0xFDB4 }, +{ 0xFDB5, 0xFDB5, 0xFDB5 }, +{ 0xFDB6, 0xFDB6, 0xFDB6 }, +{ 0xFDB7, 0xFDB7, 0xFDB7 }, +{ 0xFDB8, 0xFDB8, 0xFDB8 }, +{ 0xFDB9, 0xFDB9, 0xFDB9 }, +{ 0xFDBA, 0xFDBA, 0xFDBA }, +{ 0xFDBB, 0xFDBB, 0xFDBB }, +{ 0xFDBC, 0xFDBC, 0xFDBC }, +{ 0xFDBD, 0xFDBD, 0xFDBD }, +{ 0xFDBE, 0xFDBE, 0xFDBE }, +{ 0xFDBF, 0xFDBF, 0xFDBF }, +{ 0xFDC0, 0xFDC0, 0xFDC0 }, +{ 0xFDC1, 0xFDC1, 0xFDC1 }, +{ 0xFDC2, 0xFDC2, 0xFDC2 }, +{ 0xFDC3, 0xFDC3, 0xFDC3 }, +{ 0xFDC4, 0xFDC4, 0xFDC4 }, +{ 0xFDC5, 0xFDC5, 0xFDC5 }, +{ 0xFDC6, 0xFDC6, 0xFDC6 }, +{ 0xFDC7, 0xFDC7, 0xFDC7 }, +{ 0xFDF0, 0xFDF0, 0xFDF0 }, +{ 0xFDF1, 0xFDF1, 0xFDF1 }, +{ 0xFDF2, 0xFDF2, 0xFDF2 }, +{ 0xFDF3, 0xFDF3, 0xFDF3 }, +{ 0xFDF4, 0xFDF4, 0xFDF4 }, +{ 0xFDF5, 0xFDF5, 0xFDF5 }, +{ 0xFDF6, 0xFDF6, 0xFDF6 }, +{ 0xFDF7, 0xFDF7, 0xFDF7 }, +{ 0xFDF8, 0xFDF8, 0xFDF8 }, +{ 0xFDF9, 0xFDF9, 0xFDF9 }, +{ 0xFDFA, 0xFDFA, 0xFDFA }, +{ 0xFDFB, 0xFDFB, 0xFDFB }, +{ 0xFE00, 0xFE00, 0xFE00 }, +{ 0xFE01, 0xFE01, 0xFE01 }, +{ 0xFE02, 0xFE02, 0xFE02 }, +{ 0xFE03, 0xFE03, 0xFE03 }, +{ 0xFE04, 0xFE04, 0xFE04 }, +{ 0xFE05, 0xFE05, 0xFE05 }, +{ 0xFE06, 0xFE06, 0xFE06 }, +{ 0xFE07, 0xFE07, 0xFE07 }, +{ 0xFE08, 0xFE08, 0xFE08 }, +{ 0xFE09, 0xFE09, 0xFE09 }, +{ 0xFE0A, 0xFE0A, 0xFE0A }, +{ 0xFE0B, 0xFE0B, 0xFE0B }, +{ 0xFE0C, 0xFE0C, 0xFE0C }, +{ 0xFE0D, 0xFE0D, 0xFE0D }, +{ 0xFE0E, 0xFE0E, 0xFE0E }, +{ 0xFE0F, 0xFE0F, 0xFE0F }, +{ 0xFE20, 0xFE20, 0xFE20 }, +{ 0xFE21, 0xFE21, 0xFE21 }, +{ 0xFE22, 0xFE22, 0xFE22 }, +{ 0xFE23, 0xFE23, 0xFE23 }, +{ 0xFE70, 0xFE70, 0xFE70 }, +{ 0xFE71, 0xFE71, 0xFE71 }, +{ 0xFE72, 0xFE72, 0xFE72 }, +{ 0xFE73, 0xFE73, 0xFE73 }, +{ 0xFE74, 0xFE74, 0xFE74 }, +{ 0xFE76, 0xFE76, 0xFE76 }, +{ 0xFE77, 0xFE77, 0xFE77 }, +{ 0xFE78, 0xFE78, 0xFE78 }, +{ 0xFE79, 0xFE79, 0xFE79 }, +{ 0xFE7A, 0xFE7A, 0xFE7A }, +{ 0xFE7B, 0xFE7B, 0xFE7B }, +{ 0xFE7C, 0xFE7C, 0xFE7C }, +{ 0xFE7D, 0xFE7D, 0xFE7D }, +{ 0xFE7E, 0xFE7E, 0xFE7E }, +{ 0xFE7F, 0xFE7F, 0xFE7F }, +{ 0xFE80, 0xFE80, 0xFE80 }, +{ 0xFE81, 0xFE81, 0xFE81 }, +{ 0xFE82, 0xFE82, 0xFE82 }, +{ 0xFE83, 0xFE83, 0xFE83 }, +{ 0xFE84, 0xFE84, 0xFE84 }, +{ 0xFE85, 0xFE85, 0xFE85 }, +{ 0xFE86, 0xFE86, 0xFE86 }, +{ 0xFE87, 0xFE87, 0xFE87 }, +{ 0xFE88, 0xFE88, 0xFE88 }, +{ 0xFE89, 0xFE89, 0xFE89 }, +{ 0xFE8A, 0xFE8A, 0xFE8A }, +{ 0xFE8B, 0xFE8B, 0xFE8B }, +{ 0xFE8C, 0xFE8C, 0xFE8C }, +{ 0xFE8D, 0xFE8D, 0xFE8D }, +{ 0xFE8E, 0xFE8E, 0xFE8E }, +{ 0xFE8F, 0xFE8F, 0xFE8F }, +{ 0xFE90, 0xFE90, 0xFE90 }, +{ 0xFE91, 0xFE91, 0xFE91 }, +{ 0xFE92, 0xFE92, 0xFE92 }, +{ 0xFE93, 0xFE93, 0xFE93 }, +{ 0xFE94, 0xFE94, 0xFE94 }, +{ 0xFE95, 0xFE95, 0xFE95 }, +{ 0xFE96, 0xFE96, 0xFE96 }, +{ 0xFE97, 0xFE97, 0xFE97 }, +{ 0xFE98, 0xFE98, 0xFE98 }, +{ 0xFE99, 0xFE99, 0xFE99 }, +{ 0xFE9A, 0xFE9A, 0xFE9A }, +{ 0xFE9B, 0xFE9B, 0xFE9B }, +{ 0xFE9C, 0xFE9C, 0xFE9C }, +{ 0xFE9D, 0xFE9D, 0xFE9D }, +{ 0xFE9E, 0xFE9E, 0xFE9E }, +{ 0xFE9F, 0xFE9F, 0xFE9F }, +{ 0xFEA0, 0xFEA0, 0xFEA0 }, +{ 0xFEA1, 0xFEA1, 0xFEA1 }, +{ 0xFEA2, 0xFEA2, 0xFEA2 }, +{ 0xFEA3, 0xFEA3, 0xFEA3 }, +{ 0xFEA4, 0xFEA4, 0xFEA4 }, +{ 0xFEA5, 0xFEA5, 0xFEA5 }, +{ 0xFEA6, 0xFEA6, 0xFEA6 }, +{ 0xFEA7, 0xFEA7, 0xFEA7 }, +{ 0xFEA8, 0xFEA8, 0xFEA8 }, +{ 0xFEA9, 0xFEA9, 0xFEA9 }, +{ 0xFEAA, 0xFEAA, 0xFEAA }, +{ 0xFEAB, 0xFEAB, 0xFEAB }, +{ 0xFEAC, 0xFEAC, 0xFEAC }, +{ 0xFEAD, 0xFEAD, 0xFEAD }, +{ 0xFEAE, 0xFEAE, 0xFEAE }, +{ 0xFEAF, 0xFEAF, 0xFEAF }, +{ 0xFEB0, 0xFEB0, 0xFEB0 }, +{ 0xFEB1, 0xFEB1, 0xFEB1 }, +{ 0xFEB2, 0xFEB2, 0xFEB2 }, +{ 0xFEB3, 0xFEB3, 0xFEB3 }, +{ 0xFEB4, 0xFEB4, 0xFEB4 }, +{ 0xFEB5, 0xFEB5, 0xFEB5 }, +{ 0xFEB6, 0xFEB6, 0xFEB6 }, +{ 0xFEB7, 0xFEB7, 0xFEB7 }, +{ 0xFEB8, 0xFEB8, 0xFEB8 }, +{ 0xFEB9, 0xFEB9, 0xFEB9 }, +{ 0xFEBA, 0xFEBA, 0xFEBA }, +{ 0xFEBB, 0xFEBB, 0xFEBB }, +{ 0xFEBC, 0xFEBC, 0xFEBC }, +{ 0xFEBD, 0xFEBD, 0xFEBD }, +{ 0xFEBE, 0xFEBE, 0xFEBE }, +{ 0xFEBF, 0xFEBF, 0xFEBF }, +{ 0xFEC0, 0xFEC0, 0xFEC0 }, +{ 0xFEC1, 0xFEC1, 0xFEC1 }, +{ 0xFEC2, 0xFEC2, 0xFEC2 }, +{ 0xFEC3, 0xFEC3, 0xFEC3 }, +{ 0xFEC4, 0xFEC4, 0xFEC4 }, +{ 0xFEC5, 0xFEC5, 0xFEC5 }, +{ 0xFEC6, 0xFEC6, 0xFEC6 }, +{ 0xFEC7, 0xFEC7, 0xFEC7 }, +{ 0xFEC8, 0xFEC8, 0xFEC8 }, +{ 0xFEC9, 0xFEC9, 0xFEC9 }, +{ 0xFECA, 0xFECA, 0xFECA }, +{ 0xFECB, 0xFECB, 0xFECB }, +{ 0xFECC, 0xFECC, 0xFECC }, +{ 0xFECD, 0xFECD, 0xFECD }, +{ 0xFECE, 0xFECE, 0xFECE }, +{ 0xFECF, 0xFECF, 0xFECF }, +{ 0xFED0, 0xFED0, 0xFED0 }, +{ 0xFED1, 0xFED1, 0xFED1 }, +{ 0xFED2, 0xFED2, 0xFED2 }, +{ 0xFED3, 0xFED3, 0xFED3 }, +{ 0xFED4, 0xFED4, 0xFED4 }, +{ 0xFED5, 0xFED5, 0xFED5 }, +{ 0xFED6, 0xFED6, 0xFED6 }, +{ 0xFED7, 0xFED7, 0xFED7 }, +{ 0xFED8, 0xFED8, 0xFED8 }, +{ 0xFED9, 0xFED9, 0xFED9 }, +{ 0xFEDA, 0xFEDA, 0xFEDA }, +{ 0xFEDB, 0xFEDB, 0xFEDB }, +{ 0xFEDC, 0xFEDC, 0xFEDC }, +{ 0xFEDD, 0xFEDD, 0xFEDD }, +{ 0xFEDE, 0xFEDE, 0xFEDE }, +{ 0xFEDF, 0xFEDF, 0xFEDF }, +{ 0xFEE0, 0xFEE0, 0xFEE0 }, +{ 0xFEE1, 0xFEE1, 0xFEE1 }, +{ 0xFEE2, 0xFEE2, 0xFEE2 }, +{ 0xFEE3, 0xFEE3, 0xFEE3 }, +{ 0xFEE4, 0xFEE4, 0xFEE4 }, +{ 0xFEE5, 0xFEE5, 0xFEE5 }, +{ 0xFEE6, 0xFEE6, 0xFEE6 }, +{ 0xFEE7, 0xFEE7, 0xFEE7 }, +{ 0xFEE8, 0xFEE8, 0xFEE8 }, +{ 0xFEE9, 0xFEE9, 0xFEE9 }, +{ 0xFEEA, 0xFEEA, 0xFEEA }, +{ 0xFEEB, 0xFEEB, 0xFEEB }, +{ 0xFEEC, 0xFEEC, 0xFEEC }, +{ 0xFEED, 0xFEED, 0xFEED }, +{ 0xFEEE, 0xFEEE, 0xFEEE }, +{ 0xFEEF, 0xFEEF, 0xFEEF }, +{ 0xFEF0, 0xFEF0, 0xFEF0 }, +{ 0xFEF1, 0xFEF1, 0xFEF1 }, +{ 0xFEF2, 0xFEF2, 0xFEF2 }, +{ 0xFEF3, 0xFEF3, 0xFEF3 }, +{ 0xFEF4, 0xFEF4, 0xFEF4 }, +{ 0xFEF5, 0xFEF5, 0xFEF5 }, +{ 0xFEF6, 0xFEF6, 0xFEF6 }, +{ 0xFEF7, 0xFEF7, 0xFEF7 }, +{ 0xFEF8, 0xFEF8, 0xFEF8 }, +{ 0xFEF9, 0xFEF9, 0xFEF9 }, +{ 0xFEFA, 0xFEFA, 0xFEFA }, +{ 0xFEFB, 0xFEFB, 0xFEFB }, +{ 0xFEFC, 0xFEFC, 0xFEFC }, +{ 0xFF21, 0xFF21, 0xFF41 }, +{ 0xFF22, 0xFF22, 0xFF42 }, +{ 0xFF23, 0xFF23, 0xFF43 }, +{ 0xFF24, 0xFF24, 0xFF44 }, +{ 0xFF25, 0xFF25, 0xFF45 }, +{ 0xFF26, 0xFF26, 0xFF46 }, +{ 0xFF27, 0xFF27, 0xFF47 }, +{ 0xFF28, 0xFF28, 0xFF48 }, +{ 0xFF29, 0xFF29, 0xFF49 }, +{ 0xFF2A, 0xFF2A, 0xFF4A }, +{ 0xFF2B, 0xFF2B, 0xFF4B }, +{ 0xFF2C, 0xFF2C, 0xFF4C }, +{ 0xFF2D, 0xFF2D, 0xFF4D }, +{ 0xFF2E, 0xFF2E, 0xFF4E }, +{ 0xFF2F, 0xFF2F, 0xFF4F }, +{ 0xFF30, 0xFF30, 0xFF50 }, +{ 0xFF31, 0xFF31, 0xFF51 }, +{ 0xFF32, 0xFF32, 0xFF52 }, +{ 0xFF33, 0xFF33, 0xFF53 }, +{ 0xFF34, 0xFF34, 0xFF54 }, +{ 0xFF35, 0xFF35, 0xFF55 }, +{ 0xFF36, 0xFF36, 0xFF56 }, +{ 0xFF37, 0xFF37, 0xFF57 }, +{ 0xFF38, 0xFF38, 0xFF58 }, +{ 0xFF39, 0xFF39, 0xFF59 }, +{ 0xFF3A, 0xFF3A, 0xFF5A }, +{ 0xFF41, 0xFF21, 0xFF41 }, +{ 0xFF42, 0xFF22, 0xFF42 }, +{ 0xFF43, 0xFF23, 0xFF43 }, +{ 0xFF44, 0xFF24, 0xFF44 }, +{ 0xFF45, 0xFF25, 0xFF45 }, +{ 0xFF46, 0xFF26, 0xFF46 }, +{ 0xFF47, 0xFF27, 0xFF47 }, +{ 0xFF48, 0xFF28, 0xFF48 }, +{ 0xFF49, 0xFF29, 0xFF49 }, +{ 0xFF4A, 0xFF2A, 0xFF4A }, +{ 0xFF4B, 0xFF2B, 0xFF4B }, +{ 0xFF4C, 0xFF2C, 0xFF4C }, +{ 0xFF4D, 0xFF2D, 0xFF4D }, +{ 0xFF4E, 0xFF2E, 0xFF4E }, +{ 0xFF4F, 0xFF2F, 0xFF4F }, +{ 0xFF50, 0xFF30, 0xFF50 }, +{ 0xFF51, 0xFF31, 0xFF51 }, +{ 0xFF52, 0xFF32, 0xFF52 }, +{ 0xFF53, 0xFF33, 0xFF53 }, +{ 0xFF54, 0xFF34, 0xFF54 }, +{ 0xFF55, 0xFF35, 0xFF55 }, +{ 0xFF56, 0xFF36, 0xFF56 }, +{ 0xFF57, 0xFF37, 0xFF57 }, +{ 0xFF58, 0xFF38, 0xFF58 }, +{ 0xFF59, 0xFF39, 0xFF59 }, +{ 0xFF5A, 0xFF3A, 0xFF5A }, +{ 0xFF66, 0xFF66, 0xFF66 }, +{ 0xFF67, 0xFF67, 0xFF67 }, +{ 0xFF68, 0xFF68, 0xFF68 }, +{ 0xFF69, 0xFF69, 0xFF69 }, +{ 0xFF6A, 0xFF6A, 0xFF6A }, +{ 0xFF6B, 0xFF6B, 0xFF6B }, +{ 0xFF6C, 0xFF6C, 0xFF6C }, +{ 0xFF6D, 0xFF6D, 0xFF6D }, +{ 0xFF6E, 0xFF6E, 0xFF6E }, +{ 0xFF6F, 0xFF6F, 0xFF6F }, +{ 0xFF70, 0xFF70, 0xFF70 }, +{ 0xFF71, 0xFF71, 0xFF71 }, +{ 0xFF72, 0xFF72, 0xFF72 }, +{ 0xFF73, 0xFF73, 0xFF73 }, +{ 0xFF74, 0xFF74, 0xFF74 }, +{ 0xFF75, 0xFF75, 0xFF75 }, +{ 0xFF76, 0xFF76, 0xFF76 }, +{ 0xFF77, 0xFF77, 0xFF77 }, +{ 0xFF78, 0xFF78, 0xFF78 }, +{ 0xFF79, 0xFF79, 0xFF79 }, +{ 0xFF7A, 0xFF7A, 0xFF7A }, +{ 0xFF7B, 0xFF7B, 0xFF7B }, +{ 0xFF7C, 0xFF7C, 0xFF7C }, +{ 0xFF7D, 0xFF7D, 0xFF7D }, +{ 0xFF7E, 0xFF7E, 0xFF7E }, +{ 0xFF7F, 0xFF7F, 0xFF7F }, +{ 0xFF80, 0xFF80, 0xFF80 }, +{ 0xFF81, 0xFF81, 0xFF81 }, +{ 0xFF82, 0xFF82, 0xFF82 }, +{ 0xFF83, 0xFF83, 0xFF83 }, +{ 0xFF84, 0xFF84, 0xFF84 }, +{ 0xFF85, 0xFF85, 0xFF85 }, +{ 0xFF86, 0xFF86, 0xFF86 }, +{ 0xFF87, 0xFF87, 0xFF87 }, +{ 0xFF88, 0xFF88, 0xFF88 }, +{ 0xFF89, 0xFF89, 0xFF89 }, +{ 0xFF8A, 0xFF8A, 0xFF8A }, +{ 0xFF8B, 0xFF8B, 0xFF8B }, +{ 0xFF8C, 0xFF8C, 0xFF8C }, +{ 0xFF8D, 0xFF8D, 0xFF8D }, +{ 0xFF8E, 0xFF8E, 0xFF8E }, +{ 0xFF8F, 0xFF8F, 0xFF8F }, +{ 0xFF90, 0xFF90, 0xFF90 }, +{ 0xFF91, 0xFF91, 0xFF91 }, +{ 0xFF92, 0xFF92, 0xFF92 }, +{ 0xFF93, 0xFF93, 0xFF93 }, +{ 0xFF94, 0xFF94, 0xFF94 }, +{ 0xFF95, 0xFF95, 0xFF95 }, +{ 0xFF96, 0xFF96, 0xFF96 }, +{ 0xFF97, 0xFF97, 0xFF97 }, +{ 0xFF98, 0xFF98, 0xFF98 }, +{ 0xFF99, 0xFF99, 0xFF99 }, +{ 0xFF9A, 0xFF9A, 0xFF9A }, +{ 0xFF9B, 0xFF9B, 0xFF9B }, +{ 0xFF9C, 0xFF9C, 0xFF9C }, +{ 0xFF9D, 0xFF9D, 0xFF9D }, +{ 0xFF9E, 0xFF9E, 0xFF9E }, +{ 0xFF9F, 0xFF9F, 0xFF9F }, +{ 0xFFA0, 0xFFA0, 0xFFA0 }, +{ 0xFFA1, 0xFFA1, 0xFFA1 }, +{ 0xFFA2, 0xFFA2, 0xFFA2 }, +{ 0xFFA3, 0xFFA3, 0xFFA3 }, +{ 0xFFA4, 0xFFA4, 0xFFA4 }, +{ 0xFFA5, 0xFFA5, 0xFFA5 }, +{ 0xFFA6, 0xFFA6, 0xFFA6 }, +{ 0xFFA7, 0xFFA7, 0xFFA7 }, +{ 0xFFA8, 0xFFA8, 0xFFA8 }, +{ 0xFFA9, 0xFFA9, 0xFFA9 }, +{ 0xFFAA, 0xFFAA, 0xFFAA }, +{ 0xFFAB, 0xFFAB, 0xFFAB }, +{ 0xFFAC, 0xFFAC, 0xFFAC }, +{ 0xFFAD, 0xFFAD, 0xFFAD }, +{ 0xFFAE, 0xFFAE, 0xFFAE }, +{ 0xFFAF, 0xFFAF, 0xFFAF }, +{ 0xFFB0, 0xFFB0, 0xFFB0 }, +{ 0xFFB1, 0xFFB1, 0xFFB1 }, +{ 0xFFB2, 0xFFB2, 0xFFB2 }, +{ 0xFFB3, 0xFFB3, 0xFFB3 }, +{ 0xFFB4, 0xFFB4, 0xFFB4 }, +{ 0xFFB5, 0xFFB5, 0xFFB5 }, +{ 0xFFB6, 0xFFB6, 0xFFB6 }, +{ 0xFFB7, 0xFFB7, 0xFFB7 }, +{ 0xFFB8, 0xFFB8, 0xFFB8 }, +{ 0xFFB9, 0xFFB9, 0xFFB9 }, +{ 0xFFBA, 0xFFBA, 0xFFBA }, +{ 0xFFBB, 0xFFBB, 0xFFBB }, +{ 0xFFBC, 0xFFBC, 0xFFBC }, +{ 0xFFBD, 0xFFBD, 0xFFBD }, +{ 0xFFBE, 0xFFBE, 0xFFBE }, +{ 0xFFC2, 0xFFC2, 0xFFC2 }, +{ 0xFFC3, 0xFFC3, 0xFFC3 }, +{ 0xFFC4, 0xFFC4, 0xFFC4 }, +{ 0xFFC5, 0xFFC5, 0xFFC5 }, +{ 0xFFC6, 0xFFC6, 0xFFC6 }, +{ 0xFFC7, 0xFFC7, 0xFFC7 }, +{ 0xFFCA, 0xFFCA, 0xFFCA }, +{ 0xFFCB, 0xFFCB, 0xFFCB }, +{ 0xFFCC, 0xFFCC, 0xFFCC }, +{ 0xFFCD, 0xFFCD, 0xFFCD }, +{ 0xFFCE, 0xFFCE, 0xFFCE }, +{ 0xFFCF, 0xFFCF, 0xFFCF }, +{ 0xFFD2, 0xFFD2, 0xFFD2 }, +{ 0xFFD3, 0xFFD3, 0xFFD3 }, +{ 0xFFD4, 0xFFD4, 0xFFD4 }, +{ 0xFFD5, 0xFFD5, 0xFFD5 }, +{ 0xFFD6, 0xFFD6, 0xFFD6 }, +{ 0xFFD7, 0xFFD7, 0xFFD7 }, +{ 0xFFDA, 0xFFDA, 0xFFDA }, +{ 0xFFDB, 0xFFDB, 0xFFDB }, +{ 0xFFDC, 0xFFDC, 0xFFDC } +}; diff --git a/src/myspell/w_char.hxx b/src/myspell/w_char.hxx new file mode 100644 index 0000000..3719dd3 --- /dev/null +++ b/src/myspell/w_char.hxx @@ -0,0 +1,21 @@ +#ifndef __WCHARHXX__ +#define __WCHARHXX__ + +#ifndef GCC +typedef struct { +#else +typedef struct __attribute__ ((packed)) { +#endif + unsigned char l; + unsigned char h; +} w_char; + +// two character arrays +struct replentry { + char * pattern; + char * pattern2; + bool start; + bool end; +}; + +#endif diff --git a/src/prefix.c b/src/prefix.c new file mode 100644 index 0000000..ea35542 --- /dev/null +++ b/src/prefix.c @@ -0,0 +1,696 @@ +/* + * BinReloc - a library for creating relocatable executables + * Written by: Hongli Lai + * http://autopackage.org/ + * + * This source code is public domain. You can relicense this code + * under whatever license you want. + * + * See http://autopackage.org/docs/binreloc/ for + * more information and how to use this. + */ + +#ifndef __BINRELOC_C__ +#define __BINRELOC_C__ + +#ifdef ENABLE_BINRELOC + #include + #include + #include +#endif /* ENABLE_BINRELOC */ +#include +#include +#include +#include +#include "prefix.h" + +G_BEGIN_DECLS + + +/** @internal + * Find the canonical filename of the executable. Returns the filename + * (which must be freed) or NULL on error. If the parameter 'error' is + * not NULL, the error code will be stored there, if an error occured. + */ +static char * +_br_find_exe (GbrInitError *error) +{ +#ifndef ENABLE_BINRELOC + if (error) + *error = GBR_INIT_ERROR_DISABLED; + return NULL; +#else + char *path, *path2, *line, *result; + size_t buf_size; + ssize_t size; + struct stat stat_buf; + FILE *f; + + /* Read from /proc/self/exe (symlink) */ + if (sizeof (path) > SSIZE_MAX) + buf_size = SSIZE_MAX - 1; + else + buf_size = PATH_MAX - 1; + path = (char *) g_try_malloc (buf_size); + if (path == NULL) { + /* Cannot allocate memory. */ + if (error) + *error = GBR_INIT_ERROR_NOMEM; + return NULL; + } + path2 = (char *) g_try_malloc (buf_size); + if (path2 == NULL) { + /* Cannot allocate memory. */ + if (error) + *error = GBR_INIT_ERROR_NOMEM; + g_free (path); + return NULL; + } + + strncpy (path2, "/proc/self/exe", buf_size - 1); + + while (1) { + int i; + + size = readlink (path2, path, buf_size - 1); + if (size == -1) { + /* Error. */ + g_free (path2); + break; + } + + /* readlink() success. */ + path[size] = '\0'; + + /* Check whether the symlink's target is also a symlink. + * We want to get the final target. */ + i = stat (path, &stat_buf); + if (i == -1) { + /* Error. */ + g_free (path2); + break; + } + + /* stat() success. */ + if (!S_ISLNK (stat_buf.st_mode)) { + /* path is not a symlink. Done. */ + g_free (path2); + return path; + } + + /* path is a symlink. Continue loop and resolve this. */ + strncpy (path, path2, buf_size - 1); + } + + + /* readlink() or stat() failed; this can happen when the program is + * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */ + + buf_size = PATH_MAX + 128; + line = (char *) g_try_realloc (path, buf_size); + if (line == NULL) { + /* Cannot allocate memory. */ + g_free (path); + if (error) + *error = GBR_INIT_ERROR_NOMEM; + return NULL; + } + + f = fopen ("/proc/self/maps", "r"); + if (f == NULL) { + g_free (line); + if (error) + *error = GBR_INIT_ERROR_OPEN_MAPS; + return NULL; + } + + /* The first entry should be the executable name. */ + result = fgets (line, (int) buf_size, f); + if (result == NULL) { + fclose (f); + g_free (line); + if (error) + *error = GBR_INIT_ERROR_READ_MAPS; + return NULL; + } + + /* Get rid of newline character. */ + buf_size = strlen (line); + if (buf_size <= 0) { + /* Huh? An empty string? */ + fclose (f); + g_free (line); + if (error) + *error = GBR_INIT_ERROR_INVALID_MAPS; + return NULL; + } + if (line[buf_size - 1] == 10) + line[buf_size - 1] = 0; + + /* Extract the filename; it is always an absolute path. */ + path = strchr (line, '/'); + + /* Sanity check. */ + if (strstr (line, " r-xp ") == NULL || path == NULL) { + fclose (f); + g_free (line); + if (error) + *error = GBR_INIT_ERROR_INVALID_MAPS; + return NULL; + } + + path = g_strdup (path); + g_free (line); + fclose (f); + return path; +#endif /* ENABLE_BINRELOC */ +} + + +/** @internal + * Find the canonical filename of the executable which owns symbol. + * Returns a filename which must be freed, or NULL on error. + */ +static char * +_br_find_exe_for_symbol (const void *symbol, GbrInitError *error) +{ +#ifndef ENABLE_BINRELOC + if (error) + *error = GBR_INIT_ERROR_DISABLED; + return (char *) NULL; +#else + #define SIZE PATH_MAX + 100 + FILE *f; + size_t address_string_len; + char *address_string, line[SIZE], *found; + + if (symbol == NULL) + return (char *) NULL; + + f = fopen ("/proc/self/maps", "r"); + if (f == NULL) + return (char *) NULL; + + address_string_len = 4; + address_string = (char *) g_try_malloc (address_string_len); + found = (char *) NULL; + + while (!feof (f)) { + char *start_addr, *end_addr, *end_addr_end, *file; + void *start_addr_p, *end_addr_p; + size_t len; + + if (fgets (line, SIZE, f) == NULL) + break; + + /* Sanity check. */ + if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL) + continue; + + /* Parse line. */ + start_addr = line; + end_addr = strchr (line, '-'); + file = strchr (line, '/'); + + /* More sanity check. */ + if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-')) + continue; + + end_addr[0] = '\0'; + end_addr++; + end_addr_end = strchr (end_addr, ' '); + if (end_addr_end == NULL) + continue; + + end_addr_end[0] = '\0'; + len = strlen (file); + if (len == 0) + continue; + if (file[len - 1] == '\n') + file[len - 1] = '\0'; + + /* Get rid of "(deleted)" from the filename. */ + len = strlen (file); + if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0) + file[len - 10] = '\0'; + + /* I don't know whether this can happen but better safe than sorry. */ + len = strlen (start_addr); + if (len != strlen (end_addr)) + continue; + + + /* Transform the addresses into a string in the form of 0xdeadbeef, + * then transform that into a pointer. */ + if (address_string_len < len + 3) { + address_string_len = len + 3; + address_string = (char *) g_try_realloc (address_string, address_string_len); + } + + memcpy (address_string, "0x", 2); + memcpy (address_string + 2, start_addr, len); + address_string[2 + len] = '\0'; + sscanf (address_string, "%p", &start_addr_p); + + memcpy (address_string, "0x", 2); + memcpy (address_string + 2, end_addr, len); + address_string[2 + len] = '\0'; + sscanf (address_string, "%p", &end_addr_p); + + + if (symbol >= start_addr_p && symbol < end_addr_p) { + found = file; + break; + } + } + + g_free (address_string); + fclose (f); + + if (found == NULL) + return (char *) NULL; + else + return g_strdup (found); +#endif /* ENABLE_BINRELOC */ +} + + +static gchar *exe = NULL; + +static void set_gerror (GError **error, GbrInitError errcode); + + +/** Initialize the BinReloc library (for applications). + * + * This function must be called before using any other BinReloc functions. + * It attempts to locate the application's canonical filename. + * + * @note If you want to use BinReloc for a library, then you should call + * gbr_init_lib() instead. + * + * @param error If BinReloc failed to initialize, then the error report will + * be stored in this variable. Set to NULL if you don't want an + * error report. See the #GbrInitError for a list of error + * codes. + * + * @returns TRUE on success, FALSE if BinReloc failed to initialize. + */ +gboolean +gbr_init (GError **error) +{ + GbrInitError errcode; + + /* Locate the application's filename. */ + exe = _br_find_exe (&errcode); + if (exe != NULL) + /* Success! */ + return TRUE; + else { + /* Failed :-( */ + set_gerror (error, errcode); + return FALSE; + } +} + + +/** Initialize the BinReloc library (for libraries). + * + * This function must be called before using any other BinReloc functions. + * It attempts to locate the calling library's canonical filename. + * + * @note The BinReloc source code MUST be included in your library, or this + * function won't work correctly. + * + * @returns TRUE on success, FALSE if a filename cannot be found. + */ +gboolean +gbr_init_lib (GError **error) +{ + GbrInitError errcode; + + exe = _br_find_exe_for_symbol ((const void *) "", &errcode); + if (exe != NULL) + /* Success! */ + return TRUE; + else { + /* Failed :-( */ + set_gerror (error, errcode); + return exe != NULL; + } +} + + +static void +set_gerror (GError **error, GbrInitError errcode) +{ + const gchar *error_message; + + if (error == NULL) + return; + + switch (errcode) { + case GBR_INIT_ERROR_NOMEM: + error_message = "Cannot allocate memory."; + break; + case GBR_INIT_ERROR_OPEN_MAPS: + error_message = "Unable to open /proc/self/maps for reading."; + break; + case GBR_INIT_ERROR_READ_MAPS: + error_message = "Unable to read from /proc/self/maps."; + break; + case GBR_INIT_ERROR_INVALID_MAPS: + error_message = "The file format of /proc/self/maps is invalid."; + break; + case GBR_INIT_ERROR_DISABLED: + error_message = "Binary relocation support is disabled."; + break; + default: + error_message = "Unknown error."; + break; + }; + g_set_error (error, g_quark_from_static_string ("GBinReloc"), + errcode, "%s", error_message); +} + + +/** Find the canonical filename of the current application. + * + * @param default_exe A default filename which will be used as fallback. + * @returns A string containing the application's canonical filename, + * which must be freed when no longer necessary. If BinReloc is + * not initialized, or if the initialization function failed, + * then a copy of default_exe will be returned. If default_exe + * is NULL, then NULL will be returned. + */ +gchar * +gbr_find_exe (const gchar *default_exe) +{ + if (exe == NULL) { + /* BinReloc is not initialized. */ + if (default_exe != NULL) + return g_strdup (default_exe); + else + return NULL; + } + return g_strdup (exe); +} + + +/** Locate the directory in which the current application is installed. + * + * The prefix is generated by the following pseudo-code evaluation: + * \code + * dirname(exename) + * \endcode + * + * @param default_dir A default directory which will used as fallback. + * @return A string containing the directory, which must be freed when no + * longer necessary. If BinReloc is not initialized, or if the + * initialization function failed, then a copy of default_dir + * will be returned. If default_dir is NULL, then NULL will be + * returned. + */ +gchar * +gbr_find_exe_dir (const gchar *default_dir) +{ + if (exe == NULL) { + /* BinReloc not initialized. */ + if (default_dir != NULL) + return g_strdup (default_dir); + else + return NULL; + } + + return g_path_get_dirname (exe); +} + + +/** Locate the prefix in which the current application is installed. + * + * The prefix is generated by the following pseudo-code evaluation: + * \code + * dirname(dirname(exename)) + * \endcode + * + * @param default_prefix A default prefix which will used as fallback. + * @return A string containing the prefix, which must be freed when no + * longer necessary. If BinReloc is not initialized, or if the + * initialization function failed, then a copy of default_prefix + * will be returned. If default_prefix is NULL, then NULL will be + * returned. + */ +gchar * +gbr_find_prefix (const gchar *default_prefix) +{ + gchar *dir1, *dir2; + + if (exe == NULL) { + /* BinReloc not initialized. */ + if (default_prefix != NULL) + return g_strdup (default_prefix); + else + return NULL; + } + + dir1 = g_path_get_dirname (exe); + dir2 = g_path_get_dirname (dir1); + g_free (dir1); + return dir2; +} + + +/** Locate the application's binary folder. + * + * The path is generated by the following pseudo-code evaluation: + * \code + * prefix + "/bin" + * \endcode + * + * @param default_bin_dir A default path which will used as fallback. + * @return A string containing the bin folder's path, which must be freed when + * no longer necessary. If BinReloc is not initialized, or if the + * initialization function failed, then a copy of default_bin_dir will + * be returned. If default_bin_dir is NULL, then NULL will be returned. + */ +gchar * +gbr_find_bin_dir (const gchar *default_bin_dir) +{ + gchar *prefix, *dir; + + prefix = gbr_find_prefix (NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_bin_dir != NULL) + return g_strdup (default_bin_dir); + else + return NULL; + } + + dir = g_build_filename (prefix, "bin", NULL); + g_free (prefix); + return dir; +} + + +/** Locate the application's superuser binary folder. + * + * The path is generated by the following pseudo-code evaluation: + * \code + * prefix + "/sbin" + * \endcode + * + * @param default_sbin_dir A default path which will used as fallback. + * @return A string containing the sbin folder's path, which must be freed when + * no longer necessary. If BinReloc is not initialized, or if the + * initialization function failed, then a copy of default_sbin_dir will + * be returned. If default_bin_dir is NULL, then NULL will be returned. + */ +gchar * +gbr_find_sbin_dir (const gchar *default_sbin_dir) +{ + gchar *prefix, *dir; + + prefix = gbr_find_prefix (NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_sbin_dir != NULL) + return g_strdup (default_sbin_dir); + else + return NULL; + } + + dir = g_build_filename (prefix, "sbin", NULL); + g_free (prefix); + return dir; +} + + +/** Locate the application's data folder. + * + * The path is generated by the following pseudo-code evaluation: + * \code + * prefix + "/share" + * \endcode + * + * @param default_data_dir A default path which will used as fallback. + * @return A string containing the data folder's path, which must be freed when + * no longer necessary. If BinReloc is not initialized, or if the + * initialization function failed, then a copy of default_data_dir + * will be returned. If default_data_dir is NULL, then NULL will be + * returned. + */ +gchar * +gbr_find_data_dir (const gchar *default_data_dir) +{ + gchar *prefix, *dir; + + prefix = gbr_find_prefix (NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_data_dir != NULL) + return g_strdup (default_data_dir); + else + return NULL; + } + + dir = g_build_filename (prefix, "share", NULL); + g_free (prefix); + return dir; +} + + +/** Locate the application's localization folder. + * + * The path is generated by the following pseudo-code evaluation: + * \code + * prefix + "/share/locale" + * \endcode + * + * @param default_locale_dir A default path which will used as fallback. + * @return A string containing the localization folder's path, which must be freed when + * no longer necessary. If BinReloc is not initialized, or if the + * initialization function failed, then a copy of default_locale_dir will be returned. + * If default_locale_dir is NULL, then NULL will be returned. + */ +gchar * +gbr_find_locale_dir (const gchar *default_locale_dir) +{ + gchar *data_dir, *dir; + + data_dir = gbr_find_data_dir (NULL); + if (data_dir == NULL) { + /* BinReloc not initialized. */ + if (default_locale_dir != NULL) + return g_strdup (default_locale_dir); + else + return NULL; + } + + dir = g_build_filename (data_dir, "locale", NULL); + g_free (data_dir); + return dir; +} + + +/** Locate the application's library folder. + * + * The path is generated by the following pseudo-code evaluation: + * \code + * prefix + "/lib" + * \endcode + * + * @param default_lib_dir A default path which will used as fallback. + * @return A string containing the library folder's path, which must be freed when + * no longer necessary. If BinReloc is not initialized, or if the + * initialization function failed, then a copy of default_lib_dir will be returned. + * If default_lib_dir is NULL, then NULL will be returned. + */ +gchar * +gbr_find_lib_dir (const gchar *default_lib_dir) +{ + gchar *prefix, *dir; + + prefix = gbr_find_prefix (NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_lib_dir != NULL) + return g_strdup (default_lib_dir); + else + return NULL; + } + + dir = g_build_filename (prefix, "lib", NULL); + g_free (prefix); + return dir; +} + + +/** Locate the application's libexec folder. + * + * The path is generated by the following pseudo-code evaluation: + * \code + * prefix + "/libexec" + * \endcode + * + * @param default_libexec_dir A default path which will used as fallback. + * @return A string containing the libexec folder's path, which must be freed when + * no longer necessary. If BinReloc is not initialized, or if the initialization + * function failed, then a copy of default_libexec_dir will be returned. + * If default_libexec_dir is NULL, then NULL will be returned. + */ +gchar * +gbr_find_libexec_dir (const gchar *default_libexec_dir) +{ + gchar *prefix, *dir; + + prefix = gbr_find_prefix (NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_libexec_dir != NULL) + return g_strdup (default_libexec_dir); + else + return NULL; + } + + dir = g_build_filename (prefix, "libexec", NULL); + g_free (prefix); + return dir; +} + + +/** Locate the application's configuration files folder. + * + * The path is generated by the following pseudo-code evaluation: + * \code + * prefix + "/etc" + * \endcode + * + * @param default_etc_dir A default path which will used as fallback. + * @return A string containing the etc folder's path, which must be freed when + * no longer necessary. If BinReloc is not initialized, or if the initialization + * function failed, then a copy of default_etc_dir will be returned. + * If default_etc_dir is NULL, then NULL will be returned. + */ +gchar * +gbr_find_etc_dir (const gchar *default_etc_dir) +{ + gchar *prefix, *dir; + + prefix = gbr_find_prefix (NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_etc_dir != NULL) + return g_strdup (default_etc_dir); + else + return NULL; + } + + dir = g_build_filename (prefix, "etc", NULL); + g_free (prefix); + return dir; +} + + +G_END_DECLS + +#endif /* __BINRELOC_C__ */ diff --git a/src/prefix.h b/src/prefix.h new file mode 100644 index 0000000..55d5829 --- /dev/null +++ b/src/prefix.h @@ -0,0 +1,68 @@ +/* + * BinReloc - a library for creating relocatable executables + * Written by: Hongli Lai + * http://autopackage.org/ + * + * This source code is public domain. You can relicense this code + * under whatever license you want. + * + * See http://autopackage.org/docs/binreloc/ for + * more information and how to use this. + */ + +#ifndef __BINRELOC_H__ +#define __BINRELOC_H__ + +#include + +G_BEGIN_DECLS + + +/** These error codes can be returned by br_init(), br_init_lib(), gbr_init() or gbr_init_lib(). */ +typedef enum { + /** Cannot allocate memory. */ + GBR_INIT_ERROR_NOMEM, + /** Unable to open /proc/self/maps; see errno for details. */ + GBR_INIT_ERROR_OPEN_MAPS, + /** Unable to read from /proc/self/maps; see errno for details. */ + GBR_INIT_ERROR_READ_MAPS, + /** The file format of /proc/self/maps is invalid; kernel bug? */ + GBR_INIT_ERROR_INVALID_MAPS, + /** BinReloc is disabled (the ENABLE_BINRELOC macro is not defined). */ + GBR_INIT_ERROR_DISABLED +} GbrInitError; + + +#ifndef BINRELOC_RUNNING_DOXYGEN +/* Mangle symbol names to avoid symbol collisions with other ELF objects. */ + #define gbr_find_exe qYFU3719188448765_gbr_find_exe + #define gbr_find_exe_dir qYFU3719188448765_gbr_find_exe_dir + #define gbr_find_prefix qYFU3719188448765_gbr_find_prefix + #define gbr_find_bin_dir qYFU3719188448765_gbr_find_bin_dir + #define gbr_find_sbin_dir qYFU3719188448765_gbr_find_sbin_dir + #define gbr_find_data_dir qYFU3719188448765_gbr_find_data_dir + #define gbr_find_locale_dir qYFU3719188448765_gbr_find_locale_dir + #define gbr_find_lib_dir qYFU3719188448765_gbr_find_lib_dir + #define gbr_find_libexec_dir qYFU3719188448765_gbr_find_libexec_dir + #define gbr_find_etc_dir qYFU3719188448765_gbr_find_etc_dir + + +#endif +gboolean gbr_init (GError **error); +gboolean gbr_init_lib (GError **error); + +gchar *gbr_find_exe (const gchar *default_exe); +gchar *gbr_find_exe_dir (const gchar *default_dir); +gchar *gbr_find_prefix (const gchar *default_prefix); +gchar *gbr_find_bin_dir (const gchar *default_bin_dir); +gchar *gbr_find_sbin_dir (const gchar *default_sbin_dir); +gchar *gbr_find_data_dir (const gchar *default_data_dir); +gchar *gbr_find_locale_dir (const gchar *default_locale_dir); +gchar *gbr_find_lib_dir (const gchar *default_lib_dir); +gchar *gbr_find_libexec_dir (const gchar *default_libexec_dir); +gchar *gbr_find_etc_dir (const gchar *default_etc_dir); + + +G_END_DECLS + +#endif /* __BINRELOC_H__ */ diff --git a/src/pwl.c b/src/pwl.c new file mode 100644 index 0000000..bd5155c --- /dev/null +++ b/src/pwl.c @@ -0,0 +1,1259 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003, 2004 Dom Lachowicz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/** + * + * This file implements personal word list (PWL) dictionaries in the + * type EnchantPWL. + * + * Under the hood, a PWL is stored as a Trie. Checking strings for + * correctness and making suggestions is done by traversing the Trie + * while allowing a certain number of miss-steps. Due to the prefix + * compression of the Trie, this allows all strings in the PWL within + * a given edit distance of the target word to be enumerated quite + * efficiently. + * + * Ideas for the future: + * + * - Order the suggestions first by edit distance, then by + * soundex key, to put the most similar sounding suggestions + * at the front of the list. Would need a "soundex" that is + * general enough to handle languages other than English. + * + * - iterative deepending to find suggestions, rather than a straight + * search to depth three. + * + */ + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif + +#include +#include +#include "enchant-provider.h" + +#include "pwl.h" + +#if defined(_MSC_VER) +#pragma warning(disable: 4996) /* The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name. */ +#endif + +#if defined(HAVE_FLOCK) || defined(HAVE_LOCKF) +#include +#include +#endif /* HAVE_FLOCK || HAVE_LOCKF */ + +#define ENCHANT_PWL_MAX_ERRORS 3 +#define ENCHANT_PWL_MAX_SUGGS 15 + +/* A PWL dictionary is stored as a Trie-like data structure EnchantTrie. + * The EnchantTrie datatype is completely recursive - all child nodes + * are simply EnchantTrie pointers. This means that all functions + * that potentially modify a trie need to return the modified trie, + * as additional memory may have been allocated. + * + * The empty trie is simply the null pointer. If a trie contains + * a single string, it is recorded in the "value" attribute and + * "subtries" is set to NULL. When two or more strings are contained, + * "value" is NULL and "subtries" is a GHashTable mapping the first + * character of each string to the subtrie containing the remainder of + * that string. + * + * All strings stored in the Trie are assumed to be in UTF format. + * Branching is done on unicode characters, not individual bytes. + */ +typedef struct str_enchant_trie EnchantTrie; +struct str_enchant_trie +{ + char* value; /* final string found under this node */ + GHashTable* subtries; /* Maps characters to subtries */ +}; + +struct str_enchant_pwl +{ + EnchantTrie* trie; + char * filename; + time_t file_changed; + GHashTable *words_in_trie; +}; + +/* Special Trie node indicating the end of a string */ +static EnchantTrie n_EOSTrie; +static EnchantTrie* EOSTrie = &n_EOSTrie; + +/* mode for searching trie */ +typedef enum enum_matcher_mode EnchantTrieMatcherMode; +enum enum_matcher_mode +{ + case_sensitive, + case_insensitive +}; + +/* The EnchantTrieMatcher structure maintains the state necessary to + * search for matching strings within an EnchantTrie. It includes a + * callback function which will be called with each matching string + * as it is found. The arguments to this function are: + * + * - a freshly-allocated copy of the matching string, which must + * be freed by external code + * - the EnchantTrieMatcher object, giving the context of the match + * (e.g. number of errors) + */ +typedef struct str_enchant_trie_matcher EnchantTrieMatcher; +struct str_enchant_trie_matcher +{ + int num_errors; /* Num errors encountered so far. */ + int max_errors; /* Max errors before search should terminate */ + + char* word; /* Word being searched for */ + ssize_t word_pos; /* Current position in the word */ + + char* path; /* Path taken through the trie so far */ + ssize_t path_len; /* Length of allocated path string */ + ssize_t path_pos; /* Current end pos of path string */ + + EnchantTrieMatcherMode mode; + + void (*cbfunc)(char*,EnchantTrieMatcher*); /* callback func */ + void* cbdata; /* Private data for use by callback func */ +}; + +/* To allow the list of suggestions to be built up an item at a time, + * its state is maintained in an EnchantSuggList object. + */ +typedef struct str_enchant_sugg_list +{ + char** suggs; + int* sugg_errs; + size_t n_suggs; +} EnchantSuggList; + +/* + * Function Prototypes + */ + +static void enchant_pwl_add_to_trie(EnchantPWL *pwl, + const char *const word, size_t len); +static void enchant_pwl_refresh_from_file(EnchantPWL* pwl); +static void enchant_pwl_check_cb(char* match,EnchantTrieMatcher* matcher); +static void enchant_pwl_suggest_cb(char* match,EnchantTrieMatcher* matcher); +static EnchantTrie* enchant_trie_init(void); +static void enchant_trie_free(EnchantTrie* trie); +static void enchant_trie_free_cb(void*,void*,void*); +static EnchantTrie* enchant_trie_insert(EnchantTrie* trie,const char *const word); +static void enchant_trie_remove(EnchantTrie* trie,const char *const word); +static void enchant_trie_find_matches(EnchantTrie* trie,EnchantTrieMatcher *matcher); +static void enchant_trie_find_matches_cb(void* keyV,void* subtrieV,void* matcherV); +static EnchantTrieMatcher* enchant_trie_matcher_init(const char* const word, size_t len, + int maxerrs, + EnchantTrieMatcherMode mode, + void(*cbfunc)(char*,EnchantTrieMatcher*), + void* cbdata); +static void enchant_trie_matcher_free(EnchantTrieMatcher* matcher); +static void enchant_trie_matcher_pushpath(EnchantTrieMatcher* matcher,char* newchars); +static void enchant_trie_matcher_poppath(EnchantTrieMatcher* matcher,int num); + +static int edit_dist(const char* word1, const char* word2); + +static void +enchant_lock_file (FILE * f) +{ +#if defined(HAVE_FLOCK) + flock (fileno (f), LOCK_EX); +#elif defined(HAVE_LOCKF) + lockf (fileno (f), F_LOCK, 0); +#elif defined(_WIN32) + OVERLAPPED overlapped; + + overlapped.Offset = 0; + overlapped.OffsetHigh = 0; + overlapped.hEvent = NULL; + if (!LockFileEx ((HANDLE) _get_osfhandle (fileno (f)), LOCKFILE_EXCLUSIVE_LOCK, 0, 0, 0x80000000, &overlapped)) + g_warning ("Could not lock file\n"); +#else + /* TODO: UNIX fcntl. This race condition probably isn't too bad. */ +#endif /* HAVE_FLOCK */ +} + +static void +enchant_unlock_file (FILE * f) +{ +#if defined(HAVE_FLOCK) + flock (fileno (f), LOCK_UN); +#elif defined(HAVE_LOCKF) + lockf (fileno (f), F_ULOCK, 0); +#elif defined(_WIN32) + OVERLAPPED overlapped; + + overlapped.Offset = 0; + overlapped.OffsetHigh = 0; + overlapped.hEvent = NULL; + if (!UnlockFileEx ((HANDLE) _get_osfhandle (fileno (f)), 0, 0, 0x80000000, &overlapped)) + g_warning ("Could not unlock file\n"); +#else + /* TODO: UNIX fcntl. This race condition probably isn't too bad. */ +#endif /* HAVE_FLOCK */ +} + +/** + * enchant_pwl_init + * + * Returns: a new PWL object used to store/check/suggest words. + */ +EnchantPWL* enchant_pwl_init(void) +{ + EnchantPWL *pwl; + + pwl = g_new0(EnchantPWL, 1); + pwl->words_in_trie = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + return pwl; +} + +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif + +/** + * enchant_pwl_init_with_file + * + * Returns: a new PWL object used to store/check/suggest words + * or NULL if the file cannot be opened or created + */ +EnchantPWL* enchant_pwl_init_with_file(const char * file) +{ + FILE* fd; + EnchantPWL *pwl; + + g_return_val_if_fail (file != NULL, NULL); + + fd = enchant_fopen(file, "ab+"); + if(fd == NULL) + { + return NULL; + } + fclose(fd); + pwl = enchant_pwl_init(); + pwl->filename = g_strdup(file); + pwl->file_changed = 0; + + enchant_pwl_refresh_from_file(pwl); + return pwl; +} + +static void enchant_pwl_refresh_from_file(EnchantPWL* pwl) +{ + char buffer[BUFSIZ]; + char* line; + size_t line_number = 1; + FILE *f; + struct stat stats; + + if(!pwl->filename) + return; + + if(g_stat(pwl->filename, &stats)!=0) + return; /*presumably I won't be able to open the file either*/ + + if(pwl->file_changed == stats.st_mtime) + return; /*nothing changed since last read*/ + + enchant_trie_free(pwl->trie); + pwl->trie = NULL; + g_hash_table_destroy (pwl->words_in_trie); + pwl->words_in_trie = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + f = enchant_fopen(pwl->filename, "r"); + if (!f) + return; + + pwl->file_changed = stats.st_mtime; + + enchant_lock_file (f); + + for (;NULL != (fgets (buffer, sizeof (buffer), f));++line_number) + { + const gunichar BOM = 0xfeff; + size_t l; + + line = buffer; + if(line_number == 1 && BOM == g_utf8_get_char(line)) + line = g_utf8_next_char(line); + + l = strlen(line)-1; + if (line[l]=='\n') + line[l] = '\0'; + else if(!feof(f)) /* ignore lines longer than BUFSIZ. */ + { + g_warning ("Line too long (ignored) in %s at line:%u\n", pwl->filename, line_number); + while (NULL != (fgets (buffer, sizeof (buffer), f))) + { + if (line[strlen(buffer)-1]=='\n') + break; + } + continue; + } + + if( line[0] && line[0] != '#') + { + if(g_utf8_validate(line, -1, NULL)) + enchant_pwl_add_to_trie(pwl, line, strlen(line)); + else + g_warning ("Bad UTF-8 sequence in %s at line:%u\n", pwl->filename, line_number); + } + } + + enchant_unlock_file (f); + fclose (f); +} + +void enchant_pwl_free(EnchantPWL *pwl) +{ + enchant_trie_free(pwl->trie); + g_free(pwl->filename); + g_hash_table_destroy (pwl->words_in_trie); + g_free(pwl); +} + +static void enchant_pwl_add_to_trie(EnchantPWL *pwl, + const char *const word, size_t len) +{ + char * normalized_word; + + normalized_word = g_utf8_normalize (word, len, G_NORMALIZE_NFD); + if(NULL != g_hash_table_lookup (pwl->words_in_trie, normalized_word)) { + g_free (normalized_word); + return; + } + + g_hash_table_insert (pwl->words_in_trie, normalized_word, g_strndup(word,len)); + + pwl->trie = enchant_trie_insert(pwl->trie, normalized_word); +} + +static void enchant_pwl_remove_from_trie(EnchantPWL *pwl, + const char *const word, size_t len) +{ + char * normalized_word = g_utf8_normalize (word, len, G_NORMALIZE_NFD); + + if( g_hash_table_remove (pwl->words_in_trie, normalized_word) ) + { + enchant_trie_remove(pwl->trie, normalized_word); + if(pwl->trie && pwl->trie->subtries == NULL && pwl->trie->value == NULL) + pwl->trie = NULL; /* make trie empty if has no content */ + } + + g_free(normalized_word); +} + +void enchant_pwl_add(EnchantPWL *pwl, + const char *const word, size_t len) +{ + enchant_pwl_refresh_from_file(pwl); + + enchant_pwl_add_to_trie(pwl, word, len); + + if (pwl->filename != NULL) + { + FILE *f; + + f = enchant_fopen(pwl->filename, "a"); + if (f) + { + struct stat stats; + + enchant_lock_file (f); + if(g_stat(pwl->filename, &stats)==0) + pwl->file_changed = stats.st_mtime; + + /* we write the new line first since we can't guarantee + that the file was terminated by a new line before + and we are just appending to the end of the file */ + fwrite ("\n", sizeof(char), 1, f); + fwrite (word, sizeof(char), len, f); + enchant_unlock_file (f); + fclose (f); + } + } +} + +void enchant_pwl_remove(EnchantPWL *pwl, + const char *const word, size_t len) +{ + if(enchant_pwl_check(pwl, word, len) == 1) + return; + + enchant_pwl_refresh_from_file(pwl); + + enchant_pwl_remove_from_trie(pwl, word, len); + + if (pwl->filename) + { + char * contents; + size_t length; + + FILE *f; + + if(!g_file_get_contents(pwl->filename, &contents, &length, NULL)) + return; + + f = enchant_fopen(pwl->filename, "wb"); /*binary because g_file_get_contents reads binary*/ + if (f) + { + const gunichar BOM = 0xfeff; + char * filestart, *searchstart, *needle; + char * key; + struct stat stats; + + enchant_lock_file (f); + key = g_strndup(word, len); + + if(BOM == g_utf8_get_char(contents)) + { + filestart = g_utf8_next_char(contents); + fwrite (contents, sizeof(char), filestart-contents, f); + } + else + filestart = contents; + + searchstart = filestart; + for(;;) + { + /*find word*/ + needle = strstr(searchstart, key); + if(needle == NULL) + { + fwrite (searchstart, sizeof(char), length - (searchstart - contents), f); + break; + } + else + { + char* foundend = needle+len; + if((needle == filestart || contents[needle-contents-1] == '\n' || contents[needle-contents-1] == '\r') && + (foundend == contents + length || *foundend == '\n' || *foundend == '\r')) + { + fwrite (searchstart, sizeof(char), needle - searchstart, f); + searchstart = foundend; + while (*searchstart == '\n' || *searchstart == '\r') + ++searchstart; + } + else { + fwrite (searchstart, sizeof(char), needle - searchstart+1, f); + searchstart = needle+1; + } + } + } + g_free(key); + + if(g_stat(pwl->filename, &stats)==0) + pwl->file_changed = stats.st_mtime; + + enchant_unlock_file (f); + + fclose (f); + } + g_free(contents); + } +} + +static int enchant_pwl_contains(EnchantPWL *pwl, const char *const word, size_t len) +{ + EnchantTrieMatcher* matcher; + int count = 0; + + matcher = enchant_trie_matcher_init(word,len,0,case_sensitive,enchant_pwl_check_cb, + &count); + enchant_trie_find_matches(pwl->trie,matcher); + enchant_trie_matcher_free(matcher); + + return (count == 0 ? 0 : 1); +} + +static int enchant_is_all_caps(const char*const word, size_t len) +{ + const char* it; + int hasCap = 0; + + g_return_val_if_fail (word && *word, 0); + + for(it = word; it < word + len; it = g_utf8_next_char(it)) + { + GUnicodeType type = g_unichar_type(g_utf8_get_char(it)); + switch(type) + { + case G_UNICODE_UPPERCASE_LETTER: + hasCap = 1; + break; + case G_UNICODE_TITLECASE_LETTER: + case G_UNICODE_LOWERCASE_LETTER: + return 0; + + case G_UNICODE_CONTROL: + case G_UNICODE_FORMAT: + case G_UNICODE_UNASSIGNED: + case G_UNICODE_PRIVATE_USE: + case G_UNICODE_SURROGATE: + case G_UNICODE_MODIFIER_LETTER: + case G_UNICODE_OTHER_LETTER: + case G_UNICODE_COMBINING_MARK: + case G_UNICODE_ENCLOSING_MARK: + case G_UNICODE_NON_SPACING_MARK: + case G_UNICODE_DECIMAL_NUMBER: + case G_UNICODE_LETTER_NUMBER: + case G_UNICODE_OTHER_NUMBER: + case G_UNICODE_CONNECT_PUNCTUATION: + case G_UNICODE_DASH_PUNCTUATION: + case G_UNICODE_CLOSE_PUNCTUATION: + case G_UNICODE_FINAL_PUNCTUATION: + case G_UNICODE_INITIAL_PUNCTUATION: + case G_UNICODE_OTHER_PUNCTUATION: + case G_UNICODE_OPEN_PUNCTUATION: + case G_UNICODE_CURRENCY_SYMBOL: + case G_UNICODE_MODIFIER_SYMBOL: + case G_UNICODE_MATH_SYMBOL: + case G_UNICODE_OTHER_SYMBOL: + case G_UNICODE_LINE_SEPARATOR: + case G_UNICODE_PARAGRAPH_SEPARATOR: + case G_UNICODE_SPACE_SEPARATOR: + default: + break; + } + } + + return hasCap; +} + +static int enchant_is_title_case(const char*const word, size_t len) +{ + gunichar ch; + GUnicodeType type; + const char* it = word; + + g_return_val_if_fail (word && *word, 0); + + ch = g_utf8_get_char(it); + + type = g_unichar_type(ch); + if(type != G_UNICODE_UPPERCASE_LETTER && type != G_UNICODE_TITLECASE_LETTER) + return 0; + + if(ch != g_unichar_totitle(ch) ) + return 0; + + for(it = g_utf8_next_char(it); it < word + len; it = g_utf8_next_char(it)) + { + type = g_unichar_type(g_utf8_get_char(it)); + if(type == G_UNICODE_UPPERCASE_LETTER || type == G_UNICODE_TITLECASE_LETTER) + return 0; + } + return 1; +} + +static gchar* enchant_utf8_strtitle(const gchar*str, gssize len) +{ + gunichar title_case_char; + gchar* result; + gchar* upperStr, * upperTail, * lowerTail; + gchar title_case_utf8[7]; + gint utf8len; + + upperStr = g_utf8_strup(str, len); /* for locale sensitive casing */ + + title_case_char = g_unichar_totitle(g_utf8_get_char(upperStr)); + + utf8len = g_unichar_to_utf8(title_case_char, title_case_utf8); + title_case_utf8[utf8len] = '\0'; + + upperTail = g_utf8_next_char(upperStr); + lowerTail = g_utf8_strdown(upperTail, -1); + + result = g_strconcat(title_case_utf8, + lowerTail, + NULL); + + g_free(upperStr); + g_free(lowerTail); + + return result; +} + +int enchant_pwl_check(EnchantPWL *pwl, const char *const word, size_t len) +{ + int exists = 0; + int isAllCaps = 0; + + enchant_pwl_refresh_from_file(pwl); + + exists = enchant_pwl_contains(pwl, word, len); + + if(exists) + return 0; + + if(enchant_is_title_case(word, len) || (isAllCaps = enchant_is_all_caps(word, len))) + { + char * lower_case_word = g_utf8_strdown(word, len); + exists = enchant_pwl_contains(pwl, lower_case_word, strlen(lower_case_word)); + g_free(lower_case_word); + if(exists) + return 0; + + if(isAllCaps) + { + char * title_case_word = enchant_utf8_strtitle(word, len); + exists = enchant_pwl_contains(pwl, title_case_word, strlen(title_case_word)); + g_free(title_case_word); + if(exists) + return 0; + } + } + + return 1; /* not found */ +} + +/* matcher callback when a match is found*/ +static void enchant_pwl_check_cb(char* match,EnchantTrieMatcher* matcher) +{ + g_free(match); + (*((int*)(matcher->cbdata)))++; +} + +static void enchant_pwl_case_and_denormalize_suggestions(EnchantPWL *pwl, + const char *const word, size_t len, + EnchantSuggList* suggs_list) +{ + size_t i; + gchar* (*utf8_case_convert_function)(const gchar*str, gssize len); + + if(enchant_is_title_case(word, len)) + utf8_case_convert_function = enchant_utf8_strtitle; + else if (enchant_is_all_caps(word, len)) + utf8_case_convert_function = g_utf8_strup; + else + utf8_case_convert_function = NULL; + + for(i = 0; i < suggs_list->n_suggs; ++i) + { + gchar* cased_suggestion; + gchar* suggestion; + size_t suggestion_len; + + suggestion = g_hash_table_lookup (pwl->words_in_trie, suggs_list->suggs[i]); + suggestion_len = strlen(suggestion); + + if(utf8_case_convert_function && + !enchant_is_all_caps(suggestion, suggestion_len)) + { + cased_suggestion = utf8_case_convert_function(suggestion, suggestion_len); + } + else + { + cased_suggestion = g_strndup(suggestion, suggestion_len); + } + + g_free(suggs_list->suggs[i]); + suggs_list->suggs[i] = cased_suggestion; + } +} + +static int best_distance(const char*const*const suggs, const char *const word, size_t len) +{ + int best_dist; + const char*const* sugg_it; + char* normalized_word; + + normalized_word = g_utf8_normalize (word, len, G_NORMALIZE_NFD); + best_dist = g_utf8_strlen(normalized_word, -1); + + if(suggs) + { + for(sugg_it = suggs; *sugg_it; ++sugg_it) + { + char* normalized_sugg; + int dist; + + normalized_sugg = g_utf8_normalize (*sugg_it, -1, G_NORMALIZE_NFD); + + dist = edit_dist(normalized_word, normalized_sugg); + g_free(normalized_sugg); + if (dist < best_dist) + best_dist = dist; + } + } + + g_free(normalized_word); + return best_dist; +} + +/* gives the best set of suggestions from pwl that are at least as good as the + * given suggs (if suggs == NULL just best from pwl) */ +char** enchant_pwl_suggest(EnchantPWL *pwl,const char *const word, + size_t len, const char*const*const suggs, size_t* out_n_suggs) +{ + EnchantTrieMatcher* matcher; + EnchantSuggList sugg_list; + int max_dist; + + max_dist = suggs? best_distance(suggs, word, len) : ENCHANT_PWL_MAX_ERRORS; + if(max_dist > ENCHANT_PWL_MAX_ERRORS) + max_dist = ENCHANT_PWL_MAX_ERRORS; + + enchant_pwl_refresh_from_file(pwl); + + sugg_list.suggs = g_new0(char*,ENCHANT_PWL_MAX_SUGGS+1); + sugg_list.sugg_errs = g_new0(int,ENCHANT_PWL_MAX_SUGGS); + sugg_list.n_suggs = 0; + + matcher = enchant_trie_matcher_init(word,len, max_dist, + case_insensitive, + enchant_pwl_suggest_cb, + &sugg_list); + enchant_trie_find_matches(pwl->trie,matcher); + enchant_trie_matcher_free(matcher); + + g_free(sugg_list.sugg_errs); + sugg_list.suggs[sugg_list.n_suggs] = NULL; + (*out_n_suggs) = sugg_list.n_suggs; + + enchant_pwl_case_and_denormalize_suggestions(pwl, word, len, &sugg_list); + + return sugg_list.suggs; +} + +/* matcher callback when a match is found*/ +static void enchant_pwl_suggest_cb(char* match,EnchantTrieMatcher* matcher) +{ + EnchantSuggList* sugg_list; + size_t loc, i; + int changes = 0; /* num words added to list */ + + sugg_list = (EnchantSuggList*)(matcher->cbdata); + + /* only get best errors so adapt */ + if(matcher->num_errors < matcher->max_errors) + matcher->max_errors = matcher->num_errors; + + + /* Find appropriate location in the array, if any */ + /* In future, this could be done using binary search... */ + for(loc=0; loc < sugg_list->n_suggs; loc++) { + /* Better than an existing suggestion, so stop */ + if(sugg_list->sugg_errs[loc] > matcher->num_errors) { + break; + } + /* Already in the list with better score, just return */ + if(strcmp(match,sugg_list->suggs[loc])==0) { + g_free(match); + return; + } + } + /* If it's not going to fit, just throw it away */ + if(loc >= ENCHANT_PWL_MAX_SUGGS) { + g_free(match); + return; + } + + changes++; + + /* Remove all elements with worse score */ + for(i=loc; i < sugg_list->n_suggs; i++){ + g_free(sugg_list->suggs[i]); + changes--; + } + + sugg_list->suggs[loc] = match; + sugg_list->sugg_errs[loc] = matcher->num_errors; + sugg_list->n_suggs = sugg_list->n_suggs + changes; + +} + +void enchant_pwl_free_string_list(EnchantPWL *pwl, char** string_list) +{ + g_strfreev(string_list); +} + +static EnchantTrie* enchant_trie_init(void) +{ + EnchantTrie* trie; + + trie = g_new(EnchantTrie,1); + trie->value = NULL; + trie->subtries = NULL; + + return trie; +} + +static void enchant_trie_free(EnchantTrie* trie) +{ + /* Dont ever free NULL, or the EOSTrie pointer */ + if(trie == NULL || trie == EOSTrie) { + return; + } + + /* Because we have not set a destroy function for the hashtable + * (to make code cleaner below), we need to explicitly free all + * subtries with a callback function. + */ + if (trie->subtries != NULL) { + g_hash_table_foreach(trie->subtries,enchant_trie_free_cb,NULL); + g_hash_table_destroy(trie->subtries); + } + + if (trie->value != NULL) { + g_free(trie->value); + } + + g_free(trie); +} + +static void enchant_trie_free_cb(void* key, void* value, void* data) +{ + enchant_trie_free((EnchantTrie*) value); +} + +static EnchantTrie* enchant_trie_insert(EnchantTrie* trie,const char *const word) +{ + char *tmpWord; + ssize_t nxtCh = 0; + EnchantTrie* subtrie; + + if (trie == NULL) { + trie = enchant_trie_init(); + } + + if (trie->value == NULL) { + if (trie->subtries == NULL) { + /* When single word, store in trie->value */ + trie->value = g_strdup(word); + } else { + /* Store multiple words in subtries */ + if (word[0] == '\0') { + /* Mark end-of-string with special node */ + tmpWord = g_strdup(""); + g_hash_table_insert(trie->subtries, + tmpWord,EOSTrie); + } else { + nxtCh = (ssize_t)(g_utf8_next_char(word)-word); + tmpWord = g_strndup(word,nxtCh); + subtrie = g_hash_table_lookup(trie->subtries, + tmpWord); + subtrie = enchant_trie_insert(subtrie, + (word+nxtCh)); + g_hash_table_insert(trie->subtries, + tmpWord,subtrie); + } + } + } else { + /* Create new hastable for subtries, and reinsert */ + trie->subtries = g_hash_table_new_full(g_str_hash, + g_str_equal,g_free, NULL); + tmpWord = trie->value; + trie->value = NULL; + enchant_trie_insert(trie,tmpWord); + enchant_trie_insert(trie,word); + g_free(tmpWord); + } + + return trie; +} + +#if !GLIB_CHECK_VERSION(2,14,0) +static void grab_keys (gpointer key, + gpointer value, + gpointer user_data) +{ + GList **l = user_data; + *l = g_list_prepend (*l, key); +} + +static GList* g_hash_table_get_keys (GHashTable *hash_table) +{ + GList *l = NULL; + g_hash_table_foreach (hash_table, grab_keys, &l); + return l; +} +#endif + +static void enchant_trie_remove(EnchantTrie* trie,const char *const word) +{ + char *tmpWord; + ssize_t nxtCh = 0; + EnchantTrie* subtrie; + + if (trie == NULL) + return; + + if (trie->value == NULL) { + if (trie->subtries != NULL) { + /* Store multiple words in subtries */ + if (word[0] == '\0') { + /* Mark end-of-string with special node */ + g_hash_table_remove(trie->subtries, ""); + } else { + nxtCh = (ssize_t)(g_utf8_next_char(word)-word); + tmpWord = g_strndup(word,nxtCh); + subtrie = g_hash_table_lookup(trie->subtries, + tmpWord); + enchant_trie_remove(subtrie, + (word+nxtCh)); + + if(subtrie->subtries == NULL && subtrie->value == NULL) + g_hash_table_remove(trie->subtries, tmpWord); + + g_free(tmpWord); + } + + if(g_hash_table_size(trie->subtries) == 1) + { + char* key; + GList* keys = g_hash_table_get_keys(trie->subtries); + key = (char*) keys->data; + subtrie = g_hash_table_lookup(trie->subtries, key); + + /* only remove trie nodes that have values by propogating these up */ + if(subtrie->value) + { + trie->value = g_strconcat(key, subtrie->value, NULL); + enchant_trie_free(subtrie); + g_hash_table_destroy(trie->subtries); + trie->subtries = NULL; + } + + g_list_free(keys); + } + } + } else { + if(strcmp(trie->value, word) == 0) + { + g_free(trie->value); + trie->value = NULL; + } + } +} + +static EnchantTrie* enchant_trie_get_subtrie(EnchantTrie* trie, + EnchantTrieMatcher* matcher, + char** nxtChS) +{ + EnchantTrie* subtrie; + + if(trie->subtries == NULL || *nxtChS == NULL) + return NULL; + + subtrie = g_hash_table_lookup(trie->subtries,*nxtChS); + if(subtrie == NULL && matcher->mode == case_insensitive) { + char* nxtChSUp = g_utf8_strup(*nxtChS, -1); /* we ignore the title case scenario since that will give us an edit_distance of one which is acceptable since this mode is used for suggestions*/ + g_free(*nxtChS); + *nxtChS = nxtChSUp; + subtrie = g_hash_table_lookup(trie->subtries,nxtChSUp); + } + return subtrie; +} + +static void enchant_trie_find_matches(EnchantTrie* trie,EnchantTrieMatcher *matcher) +{ + int errs = 0; + ssize_t nxtChI = 0, oldPos = 0; + char* nxtChS = NULL; + EnchantTrie* subtrie = NULL; + + g_return_if_fail(matcher); + + /* Can't match in the empty trie */ + if(trie == NULL) { + return; + } + + /* Bail out if over the error limits */ + if(matcher->num_errors > matcher->max_errors){ + return; + } + + /* If the end of a string has been reached, no point recursing */ + if (trie == EOSTrie) { + size_t word_len = strlen(matcher->word); + errs = matcher->num_errors; + if((ssize_t)word_len > matcher->word_pos) { + matcher->num_errors = errs + word_len - matcher->word_pos; + } + if (matcher->num_errors <= matcher->max_errors) { + matcher->cbfunc(g_strdup(matcher->path),matcher); + } + matcher->num_errors = errs; + return; + } + + /* If there is a value, just check it, no recursion */ + if (trie->value != NULL) { + gchar* value; + errs = matcher->num_errors; + value = trie->value; + if(matcher->mode == case_insensitive) + { + value = g_utf8_strdown(value, -1); + } + matcher->num_errors = errs + edit_dist(value, + &(matcher->word[matcher->word_pos])); + if(matcher->mode == case_insensitive) + { + g_free(value); + } + + if (matcher->num_errors <= matcher->max_errors) { + matcher->cbfunc(g_strconcat(matcher->path, + trie->value,NULL), + matcher); + } + matcher->num_errors = errs; + return; + } + + nxtChI = (ssize_t)(g_utf8_next_char(&matcher->word[matcher->word_pos]) - matcher->word); + nxtChS = g_strndup(&matcher->word[matcher->word_pos], + (nxtChI - matcher->word_pos)); + + /* Precisely match the first character, and recurse */ + subtrie = enchant_trie_get_subtrie(trie, matcher, &nxtChS); + + if (subtrie != NULL) { + enchant_trie_matcher_pushpath(matcher,nxtChS); + oldPos = matcher->word_pos; + matcher->word_pos = nxtChI; + enchant_trie_find_matches(subtrie,matcher); + matcher->word_pos = oldPos; + enchant_trie_matcher_poppath(matcher,strlen(nxtChS)); + } + + g_free(nxtChS); + + matcher->num_errors++; + if (matcher->word[matcher->word_pos] != '\0') { + /* Match on inserting word[0] */ + oldPos = matcher->word_pos; + matcher->word_pos = nxtChI; + enchant_trie_find_matches(trie,matcher); + matcher->word_pos = oldPos; + } + /* for each subtrie, match on delete or substitute word[0] or transpose word[0] and word[1] */ + g_hash_table_foreach(trie->subtries, + enchant_trie_find_matches_cb, + matcher); + matcher->num_errors--; +} + +static void enchant_trie_find_matches_cb(void* keyV,void* subtrieV,void* matcherV) +{ + char* key, *key2; + EnchantTrie* subtrie, *subtrie2; + EnchantTrieMatcher* matcher; + ssize_t nxtChI, oldPos; + + key = (char*) keyV; + subtrie = (EnchantTrie*) subtrieV; + matcher = (EnchantTrieMatcher*) matcherV; + + nxtChI = (ssize_t) (g_utf8_next_char(&matcher->word[matcher->word_pos]) - matcher->word); + + /* Dont handle actual matches, that's already done */ + if (strncmp(key,&matcher->word[matcher->word_pos],nxtChI-matcher->word_pos) == 0) { + return; + } + + enchant_trie_matcher_pushpath(matcher,key); + + /* Match on deleting word[0] */ + enchant_trie_find_matches(subtrie,matcher); + /* Match on substituting word[0] */ + oldPos = matcher->word_pos; + matcher->word_pos = nxtChI; + enchant_trie_find_matches(subtrie,matcher); + + enchant_trie_matcher_poppath(matcher,strlen(key)); + + /* Match on transposing word[0] and word[1] */ + key2 = g_strndup(&matcher->word[oldPos],nxtChI-oldPos); + subtrie2 = enchant_trie_get_subtrie(subtrie, matcher, &key2); + + if(subtrie2 != NULL) { + nxtChI = (ssize_t) (g_utf8_next_char(&matcher->word[matcher->word_pos]) - matcher->word); + if (strncmp(key,&matcher->word[matcher->word_pos],nxtChI-matcher->word_pos) == 0) { + matcher->word_pos = nxtChI; + enchant_trie_matcher_pushpath(matcher,key); + enchant_trie_matcher_pushpath(matcher,key2); + + enchant_trie_find_matches(subtrie2,matcher); + enchant_trie_matcher_poppath(matcher,strlen(key2)); + enchant_trie_matcher_poppath(matcher,strlen(key)); +} + } + + g_free(key2); + + matcher->word_pos = oldPos; +} + +static EnchantTrieMatcher* enchant_trie_matcher_init(const char* const word, + size_t len, + int maxerrs, + EnchantTrieMatcherMode mode, + void(*cbfunc)(char*,EnchantTrieMatcher*), + void* cbdata) +{ + EnchantTrieMatcher* matcher; + char * normalized_word, * pattern; + + normalized_word = g_utf8_normalize (word, len, G_NORMALIZE_NFD); + len = strlen(normalized_word); + + if(mode == case_insensitive) + { + pattern = g_utf8_strdown (normalized_word, len); + g_free(normalized_word); + } + else + pattern = normalized_word; + + matcher = g_new(EnchantTrieMatcher,1); + matcher->num_errors = 0; + matcher->max_errors = maxerrs; + matcher->word = pattern; + matcher->word_pos = 0; + matcher->path = g_new0(char,len+maxerrs+1); + matcher->path[0] = '\0'; + matcher->path_len = len+maxerrs+1; + matcher->path_pos = 0; + matcher->mode = mode; + matcher->cbfunc = cbfunc; + matcher->cbdata = cbdata; + + return matcher; +} + +static void enchant_trie_matcher_free(EnchantTrieMatcher* matcher) +{ + g_free(matcher->word); + g_free(matcher->path); + g_free(matcher); +} + +static void enchant_trie_matcher_pushpath(EnchantTrieMatcher* matcher,char* newchars) +{ + ssize_t len, i; + + len = strlen(newchars); + if(matcher->path_pos + len >= matcher->path_len) { + matcher->path_len = matcher->path_len + len + 10; + matcher->path = g_renew(char,matcher->path,matcher->path_len); + } + + for(i = 0; i < len; i++) { + matcher->path[matcher->path_pos + i] = newchars[i]; + } + matcher->path_pos = matcher->path_pos + len; + matcher->path[matcher->path_pos] = '\0'; +} + +static void enchant_trie_matcher_poppath(EnchantTrieMatcher* matcher,int num) +{ + g_return_if_fail(matcher->path_pos >= 0); + matcher->path_pos = matcher->path_pos - num; + if(matcher->path_pos < 0) { + matcher->path_pos = 0; + } + matcher->path[matcher->path_pos] = '\0'; +} + +static int edit_dist(const char* utf8word1, const char* utf8word2) +{ + gunichar * word1, * word2; + glong len1, len2, cost, i, j; + int v1, v2, v3, v4; + int* table; + + word1 = g_utf8_to_ucs4_fast(utf8word1, -1, &len1); + word2 = g_utf8_to_ucs4_fast(utf8word2, -1, &len2); + + table = g_new0(int,(len1+1)*(len2+1)); + + /* Initialise outer rows of table */ + for (i=0; i < len1 + 1; i++) { + table[i*(len2+1)] = i; + } + for (j=0; j < len2 + 1; j++) { + table[j] = j; + } + + /* Fill in table in dynamic programming style */ + for (i=1; i < len1+1; i++){ + for(j=1; j < len2+1; j++) { + if(word1[i-1] == word2[j-1]) { + cost = 0; + } else { + cost = 1; + } + v1 = table[(i-1)*(len2+1)+j] + 1; + v2 = table[i*(len2+1)+(j-1)] + 1; + v3 = table[(i-1)*(len2+1)+(j-1)] + cost; + + if(i > 1 && j > 1 && word1[i-1] == word2[j-2] && word1[i-2] == word2[j-1]) { + v4 = table[(i-2)*(len2+1)+(j-2)] + cost; + if(v4 < v1) + v1 = v4; + } + + if (v1 < v2 && v1 < v3) { + cost = v1; + } else if (v2 < v3) { + cost = v2; + } else { + cost = v3; + } + table[i*(len2+1)+j] = cost; + } + } + + cost = table[len1*(len2+1) + len2]; + g_free(word1); + g_free(word2); + g_free(table); + return cost; +} + + + diff --git a/src/pwl.h b/src/pwl.h new file mode 100644 index 0000000..47f9e9e --- /dev/null +++ b/src/pwl.h @@ -0,0 +1,59 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003 Dom Lachowicz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#ifndef PWL_H +#define PWL_H + +#include "enchant.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct str_enchant_pwl EnchantPWL; + +/* Create and initialise a new, empty PWL */ +EnchantPWL* enchant_pwl_init(void); +EnchantPWL* enchant_pwl_init_with_file(const char * file); + +void enchant_pwl_add(EnchantPWL * me, const char *const word, size_t len); +void enchant_pwl_remove(EnchantPWL * me, const char *const word, size_t len); +int enchant_pwl_check(EnchantPWL * me,const char *const word, size_t len); +/*gives the best set of suggestions from pwl that are at least as good as the given suggs*/ +char** enchant_pwl_suggest(EnchantPWL *me,const char *const word, + size_t len, const char*const*const suggs, size_t* out_n_suggs); +void enchant_pwl_free(EnchantPWL* me); +void enchant_pwl_free_string_list(EnchantPWL* me, char** string_list); + +#ifdef __cplusplus +} +#endif + +#endif /* PWL_H */ diff --git a/src/uspell/.cvsignore b/src/uspell/.cvsignore new file mode 100644 index 0000000..dfae835 --- /dev/null +++ b/src/uspell/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +libenchant_uspell.la +uspell_provider.lo +.deps +.libs diff --git a/src/uspell/Makefile.am b/src/uspell/Makefile.am new file mode 100644 index 0000000..5dc85c4 --- /dev/null +++ b/src/uspell/Makefile.am @@ -0,0 +1,15 @@ +if WITH_USPELL +target_lib = libenchant_uspell.la +else +target_lib = +endif + +INCLUDES=-I$(top_srcdir)/src $(ENCHANT_CFLAGS) $(USPELL_CFLAGS) $(CXX_WARN_CFLAGS) -D_ENCHANT_BUILD=1 + +uspell_LTLIBRARIES = $(target_lib) +uspelldir= $(libdir)/enchant + +libenchant_uspell_la_LIBADD= $(ENCHANT_LIBS) $(USPELL_LIBS) $(top_builddir)/src/libenchant.la +libenchant_uspell_la_LDFLAGS = -module -avoid-version -no-undefined +libenchant_uspell_la_SOURCES = uspell_provider.cpp +libenchant_uspell_lalibdir=$(libdir)/enchant diff --git a/src/uspell/README b/src/uspell/README new file mode 100644 index 0000000..1337bb7 --- /dev/null +++ b/src/uspell/README @@ -0,0 +1,6 @@ +"Raphael Finkel" is the primary author of Uspell. + +Uspell can be gotten from AbiWord's CVS. Instructions for anoncvs checkout +are here: + +http://www.abisource.com/developers/ diff --git a/src/uspell/uspell_provider.cpp b/src/uspell/uspell_provider.cpp new file mode 100644 index 0000000..0b3cf80 --- /dev/null +++ b/src/uspell/uspell_provider.cpp @@ -0,0 +1,481 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003 Dom Lachowicz, Raphael Finkel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + + +#include +#include +#include + +#include +#include + +#include + +#include "enchant.h" +#include "enchant-provider.h" + +#include +#include +#include + +ENCHANT_PLUGIN_DECLARE("Uspell") + +static const size_t MAXALTERNATIVE = 20; // we won't return more than this number of suggestions +static const size_t MAXCHARS = 100; // maximum number of bytes of utf8 or chars of UCS4 in a word + +static GSList * +uspell_checker_get_dictionary_dirs (EnchantBroker * broker) +{ + GSList *dirs = NULL; + + { + GSList *config_dirs, *iter; + + config_dirs = enchant_get_user_config_dirs (); + + for (iter = config_dirs; iter; iter = iter->next) + { + dirs = g_slist_append (dirs, g_build_filename ((const gchar *)iter->data, + "uspell", NULL)); + } + + g_slist_foreach (config_dirs, (GFunc)g_free, NULL); + g_slist_free (config_dirs); + } + + { + const gchar* const * system_data_dirs = g_get_system_data_dirs (); + const gchar* const * iter; + + for (iter = system_data_dirs; *iter; iter++) + { + dirs = g_slist_append (dirs, g_build_filename (*iter, "uspell", "dicts", NULL)); + } + } + + /* until I work out how to link the modules against enchant in MacOSX - fjf + */ +#ifndef XP_TARGET_COCOA + char * uspell_prefix = NULL; + + /* Look for explicitly set registry values */ + uspell_prefix = enchant_get_registry_value ("Uspell", "Data_Dir"); + if (uspell_prefix) + dirs = g_slist_append (dirs, uspell_prefix); + + /* Dynamically locate library and search for modules relative to it. */ + char * enchant_prefix = enchant_get_prefix_dir(); + if(enchant_prefix) + { + uspell_prefix = g_build_filename(enchant_prefix, "share", "enchant", "uspell", NULL); + g_free(enchant_prefix); + dirs = g_slist_append (dirs, uspell_prefix); + } +#endif + +#ifdef ENCHANT_USPELL_DICT_DIR + dirs = g_slist_append (dirs, g_strdup (ENCHANT_USPELL_DICT_DIR)); +#endif + + { + GSList *config_dirs, *iter; + + config_dirs = enchant_get_dirs_from_param (broker, "enchant.uspell.dictionary.path"); + + for (iter = config_dirs; iter; iter = iter->next) + { + dirs = g_slist_append (dirs, g_strdup ((const gchar *)iter->data)); + } + + g_slist_foreach (config_dirs, (GFunc)g_free, NULL); + g_slist_free (config_dirs); + } + + return dirs; +} + +static int +uspell_dict_check (EnchantDict * me, const char *const word, size_t len) +{ + uSpell *manager; + wide_t buf1[MAXCHARS], buf2[MAXCHARS], *curBuf, *otherBuf, *tmpBuf; + utf8_t myWord[MAXCHARS]; + int length; + + if (len >= MAXCHARS) + return 1; // too long; can't be right + memcpy(reinterpret_cast(myWord), word, len); + myWord[len] = 0; + curBuf = buf1; + otherBuf = buf2; + manager = reinterpret_cast(me->user_data); + + length = utf8_wide(curBuf, myWord, MAXCHARS); + if (manager->isSpelledRight(curBuf, length)) { + return 0; // correct the first time + } + if (manager->theFlags & uSpell::upperLower) { + toUpper(otherBuf, curBuf, length); + if (manager->isSpelledRight(otherBuf, length)) { + manager->acceptWord(myWord); + return 0; // correct if converted to all upper case + } + tmpBuf = curBuf; + curBuf = otherBuf; + otherBuf = tmpBuf; + } + if (manager->theFlags & uSpell::hasComposition) { + unPrecompose(otherBuf, &length, curBuf, length); + if (manager->isSpelledRight(otherBuf, length)) { + manager->acceptWord(myWord); + return 0; // correct if precomposed characters expanded, all upper + } + tmpBuf = curBuf; + curBuf = otherBuf; + otherBuf = tmpBuf; + } + if (manager->theFlags & uSpell::hasCompounds) { + if (manager->isSpelledRightMultiple(curBuf, length)) { + manager->acceptWord(myWord); + return 0; // correct as two words. Not right for all languages. + } + } + return 1; +} + +static char ** +uspell_dict_suggest (EnchantDict * me, const char *const word, + size_t len, size_t * out_n_suggs) +{ + uSpell *manager; + utf8_t myWord[MAXCHARS]; + + char **sugg_arr = NULL; + const utf8_t *sugg; + wide_t buf[MAXCHARS]; + int length; + unsigned int i; + utf8_t **list; + + if (len >= MAXCHARS) // no suggestions; the word is outlandish + return NULL; + memcpy(reinterpret_cast(myWord), word, len); + myWord[len] = 0; + manager = reinterpret_cast(me->user_data); + + list = reinterpret_cast( + calloc(sizeof(char *), MAXALTERNATIVE)); + length = utf8_wide(buf, myWord, MAXCHARS); + *out_n_suggs = manager->showAlternatives(buf, length, + list, MAXALTERNATIVE); + + if (*out_n_suggs) + { + sugg_arr = g_new0 (char *, *out_n_suggs + 1); + for (i = 0; i < *out_n_suggs; i++) + { + sugg = list[i]; + if (sugg) + sugg_arr[i] = + g_strdup (reinterpret_cast(sugg)); + free(list[i]); + } + } + free(list); + return sugg_arr; +} // uspell_dict_suggest + +static void +uspell_dict_add_to_session (EnchantDict * me, const char *const word, + size_t len) +{ + uSpell *manager; + wide_t buf[MAXCHARS]; + utf8_t myWord[MAXCHARS]; + int length, index; + + manager = reinterpret_cast(me->user_data); + + manager->acceptWord((const utf8_t *)word); + if (len >= MAXCHARS) + return; // too long; can't reasonably convert + // see if we want to acceptWord(uppercase(myWord)) + if (!(manager->theFlags & uSpell::upperLower)) return; // non-case language + length = utf8_wide(buf, (const utf8_t *)word, MAXCHARS); + for (index = 0; index < length; index++) { + if (g_unichar_isupper(buf[index])) return; // case-sensitive word + buf[index] = g_unichar_toupper(buf[index]); + } + wide_utf8(myWord, MAXCHARS, buf, length); + manager->acceptWord(myWord); +} // uspell_dict_add_to_session + +typedef struct { + const char * language_tag; + const char * corresponding_uspell_file_name; + int language_flags; +} Mapping; + +static const Mapping mapping [] = { + {"he", "hebrew", 0}, + {"he_IL", "hebrew", 0}, + {"yi", "yiddish", uSpell::hasComposition}, + {"en_US", "american", uSpell::upperLower}, +}; + +static const size_t n_mappings = (sizeof(mapping)/sizeof(mapping[0])); + +static void +s_buildHashNames (std::vector & names, EnchantBroker * broker, const char * tag) +{ + names.clear (); + + size_t mapIndex; + + for (mapIndex = 0; mapIndex < n_mappings; mapIndex++) { + if (!strcmp(tag, mapping[mapIndex].language_tag)) + break; + } + + if (mapIndex < n_mappings) { + + GSList *dirs, *iter; + char * dict = g_strdup_printf ("%s.uspell.dat", + mapping[mapIndex].corresponding_uspell_file_name); + + dirs = uspell_checker_get_dictionary_dirs (broker); + + for (iter = dirs; iter; iter = iter->next) + { + char *tmp; + + tmp = g_build_filename ((const char *)iter->data, dict, NULL); + names.push_back (tmp); + g_free (tmp); + } + + g_slist_foreach (dirs, (GFunc)g_free, NULL); + g_slist_free (dirs); + } +} + +static uSpell * +uspell_request_dict (const char * base, const char * mapping, const int flags) +{ + char *fileName, *transName, *filePart, *transPart; + + uSpell *manager; + + if (!base) + return NULL; + + filePart = g_strconcat(mapping, ".uspell.dat", NULL); + transPart = g_strconcat(mapping, ".uspell.trans", NULL); + fileName = g_build_filename (base, filePart, NULL); + transName = g_build_filename (base, transPart, NULL); + g_free(filePart); + g_free(transPart); + + try { + manager = new uSpell(fileName, transName, flags); + } + catch (...) { + manager = NULL; + } + + g_free (fileName); + g_free (transName); + + return manager; +} + +static uSpell * +uspell_request_manager (const char * dir, size_t mapIndex) +{ + uSpell * manager = NULL; + + manager = uspell_request_dict (dir, + mapping[mapIndex].corresponding_uspell_file_name, + mapping[mapIndex].language_flags); + + if (!manager) return NULL; + + // look for a supplementary private dictionary + const char *config_dir = g_get_user_config_dir(); + if (config_dir) { + gchar * auxFileName, * transPart; + transPart = g_strconcat (mapping[mapIndex].language_tag, ".dic", NULL); + auxFileName = g_build_filename (config_dir, transPart, NULL); + g_free (transPart); + + (void) manager->assimilateFile (auxFileName); + g_free (auxFileName); + } + + return manager; +} + +extern "C" { + +ENCHANT_MODULE_EXPORT (EnchantProvider *) + init_enchant_provider (void); + +static EnchantDict * +uspell_provider_request_dict (EnchantProvider * me, const char *const tag) +{ + EnchantDict *dict = NULL; + uSpell *manager = NULL; + size_t mapIndex; + bool found = false; + + GSList *dirs, *iter; + + for (mapIndex = 0; mapIndex < n_mappings && !found; mapIndex++) { + if (!strcmp(tag, mapping[mapIndex].language_tag)) + break; + } + + if (!found) + return NULL; + + dirs = uspell_checker_get_dictionary_dirs (me->owner); + + for (iter = dirs; iter && !manager; iter = iter->next) + { + manager = uspell_request_manager ((const char *)iter->data, mapIndex); + } + + g_slist_foreach (dirs, (GFunc)g_free, NULL); + g_slist_free (dirs); + + if (!manager) + return NULL; + + dict = g_new0 (EnchantDict, 1); + dict->user_data = manager; + dict->check = uspell_dict_check; + dict->suggest = uspell_dict_suggest; + dict->add_to_session = uspell_dict_add_to_session; + // don't use personal, session - let higher level implement that + + return dict; +} + +static int +uspell_provider_dictionary_exists (struct str_enchant_provider * me, + const char *const tag) +{ + std::vector names; + + s_buildHashNames (names, me->owner, tag); + for (size_t i = 0; i < names.size(); i++) { + if (g_file_test (names[i].c_str(), G_FILE_TEST_EXISTS)) + return 1; + } + + return 0; +} + +static void +uspell_provider_dispose_dict (EnchantProvider * me, EnchantDict * dict) +{ + uSpell *manager = reinterpret_cast(dict->user_data); + delete manager; + + g_free (dict); +} + +static char ** +uspell_provider_list_dictionaries (EnchantProvider * me, + size_t * out_n_dicts) +{ + size_t i, nb; + + nb = 0; + for (i = 0; i < n_mappings; i++) + if (uspell_provider_dictionary_exists (me, mapping[i].language_tag)) + nb++; + + *out_n_dicts = nb; + if (nb == 0) + return NULL; + + char ** out_dicts = g_new0 (char *, nb + 1); + for (i = 0; i < n_mappings; i++) + if (uspell_provider_dictionary_exists (me, mapping[i].language_tag)) + out_dicts[i] = g_strdup (mapping[i].language_tag); + + return out_dicts; +} + +static void +uspell_provider_free_string_list (EnchantProvider * me, char **str_list) +{ + g_strfreev (str_list); +} + +static void +uspell_provider_dispose (EnchantProvider * me) +{ + g_free (me); +} + +static const char * +uspell_provider_identify (EnchantProvider * me) +{ + return "uspell"; +} + +static const char * +uspell_provider_describe (EnchantProvider * me) +{ + return "Uspell Provider"; +} + +EnchantProvider * +init_enchant_provider (void) +{ + EnchantProvider *provider; + + provider = g_new0 (EnchantProvider, 1); + provider->dispose = uspell_provider_dispose; + provider->request_dict = uspell_provider_request_dict; + provider->dispose_dict = uspell_provider_dispose_dict; + provider->dictionary_exists = uspell_provider_dictionary_exists; + provider->identify = uspell_provider_identify; + provider->describe = uspell_provider_describe; + provider->list_dicts = uspell_provider_list_dictionaries; + provider->free_string_list = uspell_provider_free_string_list; + + return provider; +} + +} + diff --git a/src/voikko/Makefile.am b/src/voikko/Makefile.am new file mode 100644 index 0000000..e12cbc0 --- /dev/null +++ b/src/voikko/Makefile.am @@ -0,0 +1,15 @@ +if WITH_VOIKKO +target_lib = libenchant_voikko.la +else +target_lib = +endif + +INCLUDES=-I$(top_srcdir)/src $(ENCHANT_CFLAGS) $(CC_WARN_CFLAGS) @VOIKKO_INC@ -D_ENCHANT_BUILD=1 + +voikko_LTLIBRARIES = $(target_lib) +voikkodir= $(libdir)/enchant + +libenchant_voikko_la_LIBADD= $(ENCHANT_LIBS) $(VOIKKO_LIBS) $(top_builddir)/src/libenchant.la +libenchant_voikko_la_LDFLAGS = -module -avoid-version -no-undefined +libenchant_voikko_la_SOURCES = voikko_provider.c +libenchant_voikko_lalibdir=$(libdir)/enchant diff --git a/src/voikko/voikko_provider.c b/src/voikko/voikko_provider.c new file mode 100644 index 0000000..3cd2756 --- /dev/null +++ b/src/voikko/voikko_provider.c @@ -0,0 +1,201 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003,2004 Dom Lachowicz + * 2006-2007 Harri Pitkänen + * 2006 Anssi Hannula + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#include +#include +#include + +#include +#include + +#include "enchant.h" +#include "enchant-provider.h" + +/** + * Voikko is a Finnish spell checker. More information is available from: + * + * http://voikko.sourceforge.net/ + */ +ENCHANT_PLUGIN_DECLARE("Voikko") + +static int +voikko_dict_check (EnchantDict * me, const char *const word, size_t len) +{ + int result; + int voikko_handle; + + voikko_handle = (long) me->user_data; + result = voikko_spell_cstr(voikko_handle, word); + if (result == VOIKKO_SPELL_FAILED) + return 1; + else if (result == VOIKKO_SPELL_OK) + return 0; + else + return -1; +} + +static char ** +voikko_dict_suggest (EnchantDict * me, const char *const word, + size_t len, size_t * out_n_suggs) +{ + char **sugg_arr; + int voikko_handle; + + voikko_handle = (long) me->user_data; + sugg_arr = voikko_suggest_cstr(voikko_handle, word); + if (sugg_arr == NULL) + return NULL; + for (*out_n_suggs = 0; sugg_arr[*out_n_suggs] != NULL; (*out_n_suggs)++); + return sugg_arr; +} + +static EnchantDict * +voikko_provider_request_dict (EnchantProvider * me, const char *const tag) +{ + EnchantDict *dict; + const char * voikko_error; + int voikko_handle; + + /* Only Finnish is supported at the moment */ + if (strncmp(tag, "fi_FI", 6) != 0 && strncmp(tag, "fi", 3) != 0) + return NULL; + + voikko_error = voikko_init(&voikko_handle, "fi_FI", 0); + if (voikko_error) { + enchant_provider_set_error(me, voikko_error); + return NULL; + } + + dict = g_new0 (EnchantDict, 1); + dict->user_data = (void *)(long) voikko_handle; + dict->check = voikko_dict_check; + dict->suggest = voikko_dict_suggest; + + return dict; +} + +static void +voikko_provider_dispose_dict (EnchantProvider * me, EnchantDict * dict) +{ + voikko_terminate((long) dict->user_data); + g_free (dict); +} + +static int +voikko_provider_dictionary_exists (struct str_enchant_provider * me, + const char *const tag) +{ + int voikko_handle; + + /* Only Finnish is supported */ + if (strncmp(tag, "fi", 3) != 0) + return 0; + + /* Check that a dictionary is actually available */ + if (voikko_init(&voikko_handle, "fi_FI", 0) == NULL) { + voikko_terminate(voikko_handle); + return 1; + } + else return 0; +} + + +static char ** +voikko_provider_list_dicts (EnchantProvider * me, + size_t * out_n_dicts) +{ + char ** out_list = NULL; + int voikko_handle; + *out_n_dicts = 0; + + if (voikko_init(&voikko_handle, "fi_FI", 0) == NULL) { + voikko_terminate(voikko_handle); + *out_n_dicts = 1; + out_list = g_new0 (char *, *out_n_dicts + 1); + out_list[0] = g_strdup("fi"); + } + + return out_list; +} + +static void +voikko_provider_free_string_list (EnchantProvider * me, char **str_list) +{ + g_strfreev (str_list); +} + +static void +voikko_provider_dispose (EnchantProvider * me) +{ + g_free (me); +} + +static const char * +voikko_provider_identify (EnchantProvider * me) +{ + return "voikko"; +} + +static const char * +voikko_provider_describe (EnchantProvider * me) +{ + return "Voikko Provider"; +} + +#ifdef __cplusplus +extern "C" { +#endif + +ENCHANT_MODULE_EXPORT (EnchantProvider *) + init_enchant_provider (void); + +EnchantProvider * +init_enchant_provider (void) +{ + EnchantProvider *provider; + + provider = g_new0 (EnchantProvider, 1); + provider->dispose = voikko_provider_dispose; + provider->request_dict = voikko_provider_request_dict; + provider->dispose_dict = voikko_provider_dispose_dict; + provider->dictionary_exists = voikko_provider_dictionary_exists; + provider->identify = voikko_provider_identify; + provider->describe = voikko_provider_describe; + provider->list_dicts = voikko_provider_list_dicts; + provider->free_string_list = voikko_provider_free_string_list; + + return provider; +} + +#ifdef __cplusplus +} +#endif diff --git a/src/zemberek/Makefile.am b/src/zemberek/Makefile.am new file mode 100644 index 0000000..7fd90dd --- /dev/null +++ b/src/zemberek/Makefile.am @@ -0,0 +1,21 @@ +if WITH_ZEMBEREK +target_lib = libenchant_zemberek.la +else +target_lib = +endif + +INCLUDES=-I$(top_srcdir)/src $(ENCHANT_CFLAGS) $(ZEMBEREK_CFLAGS) $(CXX_WARN_CFLAGS) -D_ENCHANT_BUILD=1 + +zemberek_LTLIBRARIES = $(target_lib) +zemberekdir= $(libdir)/enchant + +libenchant_zemberek_lalibdir=$(libdir)/enchant +libenchant_zemberek_la_LIBADD= $(ZEMBEREK_LIBS) $(top_builddir)/src/libenchant.la +libenchant_zemberek_la_LDFLAGS = -avoid-version -no-undefined + +libenchant_zemberek_la_SOURCES =\ + zemberek.cpp \ + zemberek.h \ + zemberek_provider.cpp + + diff --git a/src/zemberek/zemberek.cpp b/src/zemberek/zemberek.cpp new file mode 100644 index 0000000..cf14ad6 --- /dev/null +++ b/src/zemberek/zemberek.cpp @@ -0,0 +1,124 @@ +/* Copyright (C) 2006 Barış Metin + * Copyright (C) 2007 Serkan Kaba + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#include "zemberek.h" + +bool zemberek_service_is_running () +{ + DBusGConnection *connection; + DBusGProxy *proxy; + + GError *Error = NULL; + g_type_init (); + + connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, + &Error); + if (connection == NULL) { + g_error_free (Error); + return false; + } + proxy = dbus_g_proxy_new_for_name_owner (connection, + "net.zemberekserver.server.dbus", + "/net/zemberekserver/server/dbus/ZemberekDbus", + "net.zemberekserver.server.dbus.ZemberekDbusInterface", + &Error); + + dbus_g_connection_unref (connection); + if (proxy == NULL) { + return false; + } + + g_object_unref (proxy); + return true; +} + +Zemberek::Zemberek() + : connection(NULL), proxy(NULL) +{ + GError *Error = NULL; + g_type_init (); + + connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, + &Error); + if (connection == NULL) { + g_error_free (Error); + throw "couldn't connect to the system bus"; + } + proxy = dbus_g_proxy_new_for_name (connection, + "net.zemberekserver.server.dbus", + "/net/zemberekserver/server/dbus/ZemberekDbus", + "net.zemberekserver.server.dbus.ZemberekDbusInterface"); + + if (proxy == NULL) { + throw "couldn't connect to the Zemberek service"; + } +} + + +Zemberek::~Zemberek() +{ + if(proxy) + g_object_unref (proxy); + if(connection) + dbus_g_connection_unref (connection); +} + + +int Zemberek::checkWord(const char* word) const +{ + gboolean result; + GError *Error = NULL; + if (!dbus_g_proxy_call (proxy, "kelimeDenetle", &Error, + G_TYPE_STRING,word,G_TYPE_INVALID, + G_TYPE_BOOLEAN, &result, G_TYPE_INVALID)) { + g_error_free (Error); + return -1; + } + else { + if (result) + return 0; + else + return 1; + } +} + + +char** Zemberek::suggestWord(const char* word, size_t *out_n_suggs) +{ + char** suggs; + GError *Error = NULL; + if (!dbus_g_proxy_call (proxy, "oner", &Error, + G_TYPE_STRING,word,G_TYPE_INVALID, + G_TYPE_STRV, &suggs,G_TYPE_INVALID)) { + g_error_free (Error); + return NULL; + } + *out_n_suggs = g_strv_length(suggs); + return suggs; +} diff --git a/src/zemberek/zemberek.h b/src/zemberek/zemberek.h new file mode 100644 index 0000000..53dbf6e --- /dev/null +++ b/src/zemberek/zemberek.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2006 Barış Metin + * Copyright (C) 2007 Serkan Kaba + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#ifndef ZEMBEREK_H +#define ZEMBEREK_H + +#include +#include + +class Zemberek +{ +public: + Zemberek(); + ~Zemberek(); + + int checkWord(const char* word) const; + char** suggestWord(const char* word, size_t *out_n_suggs); + +private: + DBusGConnection *connection; + DBusGProxy *proxy; +}; + +bool zemberek_service_is_running (); +#endif diff --git a/src/zemberek/zemberek_provider.cpp b/src/zemberek/zemberek_provider.cpp new file mode 100644 index 0000000..d1f528d --- /dev/null +++ b/src/zemberek/zemberek_provider.cpp @@ -0,0 +1,156 @@ +/* Copyright (C) 2006 Barış Metin + * Copyright (C) 2007 Serkan Kaba + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#include +#include + +#include "enchant.h" +#include "enchant-provider.h" + +#include "zemberek.h" + +ENCHANT_PLUGIN_DECLARE("Zemberek") + +extern "C" { + +ENCHANT_MODULE_EXPORT(EnchantProvider *) + init_enchant_provider(void); + +static int +zemberek_dict_check (EnchantDict * me, const char *const word, size_t len) +{ + Zemberek *checker; + checker = (Zemberek *) me->user_data; + return checker->checkWord(word); +} + +static char** +zemberek_dict_suggest (EnchantDict * me, const char *const word, + size_t len, size_t * out_n_suggs) +{ + Zemberek *checker; + checker = (Zemberek *) me->user_data; + return checker->suggestWord (word, out_n_suggs); +} + +static void +zemberek_provider_dispose(EnchantProvider *me) +{ + g_free(me); +} + +static EnchantDict* +zemberek_provider_request_dict(EnchantProvider *me, const char *tag) +{ + if (!((strcmp(tag, "tr") == 0) || (strncmp(tag, "tr_", 3) == 0))) + return NULL; // only handle turkish + + try + { + Zemberek* checker = new Zemberek(); + + EnchantDict* dict = g_new0(EnchantDict, 1); + dict->user_data = (void *) checker; + dict->check = zemberek_dict_check; + dict->suggest = zemberek_dict_suggest; + + return dict; + } + catch(...) + { + // will fail if zemberek service isn't running + return NULL; + } +} + +static void +zemberek_provider_dispose_dict (EnchantProvider * me, EnchantDict * dict) +{ + Zemberek *checker; + checker = (Zemberek *) dict->user_data; + delete checker; + g_free (dict); +} + +static const char * +zemberek_provider_identify (EnchantProvider * me) +{ + return "zemberek"; +} + +static const char * +zemberek_provider_describe (EnchantProvider * me) +{ + return "Zemberek Provider"; +} + +static void +zemberek_provider_free_string_list (EnchantProvider * me, char **str_list) +{ + g_strfreev (str_list); +} + +static char ** +zemberek_provider_list_dicts (EnchantProvider * me, + size_t * out_n_dicts) +{ + if (!zemberek_service_is_running ()) + { + *out_n_dicts = 0; + return NULL; + } + else + { + char ** out_list = NULL; + *out_n_dicts = 1; + out_list = g_new0 (char *, 2); + out_list[0] = g_strdup ("tr"); + + return out_list; + } +} + +EnchantProvider * +init_enchant_provider(void) +{ + EnchantProvider *provider; + + provider = g_new0(EnchantProvider, 1); + provider->dispose = zemberek_provider_dispose; + provider->request_dict = zemberek_provider_request_dict; + provider->dispose_dict = zemberek_provider_dispose_dict; + provider->identify = zemberek_provider_identify; + provider->describe = zemberek_provider_describe; + provider->list_dicts = zemberek_provider_list_dicts; + provider->free_string_list = zemberek_provider_free_string_list; + + return provider; +} + +} diff --git a/tests/.cvsignore b/tests/.cvsignore new file mode 100644 index 0000000..076f585 --- /dev/null +++ b/tests/.cvsignore @@ -0,0 +1,8 @@ +Makefile.in +Makefile +test-enchant +test-enchantxx +enchant-lsmod +enchant +.deps +.libs diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..0980415 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,35 @@ +INCLUDES=-I$(top_srcdir)/src $(ENCHANT_CFLAGS) + +EXTRA_DIST=test.pwl ispell.in + +DEPS= $(top_builddir)/src/libenchant.la +ldadd= $(top_builddir)/src/libenchant.la $(ENCHANT_LIBS) + +if WITH_CXX +cxx_progs = test-enchantxx +else +cxx_progs = +endif + +noinst_PROGRAMS = test-enchant $(cxx_progs) +bin_PROGRAMS= enchant-lsmod enchant + +test_enchant_SOURCES = test-enchant.c +test_enchant_LDFLAGS = +test_enchant_DEPENDENCIES = $(DEPS) +test_enchant_LDADD = $(ldadd) + +enchant_SOURCES = enchant-ispell.c +enchant_LDFLAGS = +enchant_DEPENDENCIES = $(DEPS) +enchant_LDADD = $(ldadd) + +test_enchantxx_SOURCES = test-enchantxx.cpp +test_enchantxx_LDFLAGS = +test_enchantxx_DEPENDENCIES = $(DEPS) +test_enchantxx_LDADD = $(ldadd) + +enchant_lsmod_SOURCES = enchant-lsmod.c +enchant_lsmod_LDFLAGS = +enchant_lsmod_DEPENDENCIES = $(DEPS) +enchant_lsmod_LDADD = $(ldadd) diff --git a/tests/enchant-ispell.c b/tests/enchant-ispell.c new file mode 100644 index 0000000..9c5f1c7 --- /dev/null +++ b/tests/enchant-ispell.c @@ -0,0 +1,549 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003 Dom Lachowicz + * 2007 Hannu Väisänen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * the non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/** + * This is a rough approximation of an "ispell compatibility mode" + * for Enchant. + * + * Modified in 2007 to work when called from emacs which + * calls a spelling program (e.g. enchant) like this + * + * enchant -a -m -d dictionary + */ + +#include +#include +#include +#include +#include + +#include "enchant.h" +#include "enchant-provider.h" + +/* word has to be bigger than this to be checked */ +#define MIN_WORD_LENGTH 1 + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include + +static char charset[15] = "CP437"; +#endif + +typedef enum + { + MODE_NONE, + MODE_VERSION, + MODE_A, + MODE_L + } IspellMode_t; + +static void +print_version (FILE * to) +{ + fprintf (to, "@(#) International Ispell Version 3.1.20 (but really Enchant %s)\n", VERSION); + fflush (to); +} + +static void +print_help (FILE * to, const char * prog) +{ + fprintf (to, "Usage: %s [options] -a|-d dict|-l|-L|-m|-v[v]|\n", prog); + fprintf (to, "\t-a lists alternatives.\n"); + fprintf (to, "\t-d dict uses dictionary .\n"); + fprintf (to, "\t-l lists misspellings.\n"); + fprintf (to, "\t-m is ignored.\n"); + fprintf (to, "\t-L displays line numbers.\n"); + fprintf (to, "\t-v displays program version.\n"); +} + +static gboolean +consume_line (FILE * in, GString * str) +{ + int ch; + gsize bytes_read, bytes_written; + gchar * utf; + gboolean ret = TRUE; + + g_string_truncate (str, 0); + + while (ret && (ch = fgetc (in)) != EOF) { + if (ch == '\r') + continue; + else { + g_string_append_c (str, ch); + if (ch == '\n') + ret = FALSE; + } + } + + if (str->len) { +#ifdef WIN32 + utf = g_convert(str->str, str->len, "UTF-8", charset, &bytes_read, &bytes_written, NULL); +#else + utf = g_locale_to_utf8 (str->str, str->len, &bytes_read, &bytes_written, NULL); +#endif + + if (utf) { + g_string_assign (str, utf); + g_free (utf); + } + /* Else str->str stays the same. we'll assume that it's + already utf8 and glib is just being stupid. */ + } + + return ret; +} + +static void +print_utf (FILE * out, const char * str) +{ + gsize bytes_read, bytes_written; + gchar * native; + + native = g_locale_from_utf8 (str, -1, &bytes_read, &bytes_written, NULL); + if (native) { + fwrite (native, 1, bytes_written, out); + g_free (native); + } else { + /* We'll assume that it's already utf8 and glib is just being stupid. */ + fwrite (str, 1, strlen (str), out); + } +} + +static void +do_mode_a (FILE * out, EnchantDict * dict, GString * word, size_t start_pos, size_t lineCount) +{ + size_t n_suggs; + char ** suggs; + + if (word->len <= MIN_WORD_LENGTH || enchant_dict_check (dict, word->str, word->len) == 0) { + if (lineCount) + fprintf (out, "* %u\n", (unsigned int)lineCount); + else + fwrite ("*\n", 1, 2, out); + } + else { + suggs = enchant_dict_suggest (dict, word->str, + word->len, &n_suggs); + if (!n_suggs || !suggs) { + fwrite ("# ", 1, 2, out); + if (lineCount) + fprintf (out, "%u ", (unsigned int)lineCount); + print_utf (out, word->str); + fprintf (out, " %u\n", (unsigned int)start_pos); + } + else { + size_t i = 0; + + fwrite ("& ", 1, 2, out); + if (lineCount) + fprintf (out, "%u ", (unsigned int)lineCount); + print_utf (out, word->str); + fprintf (out, " %u %u:", (unsigned int)n_suggs, (unsigned int)start_pos); + + for (i = 0; i < n_suggs; i++) { + fprintf (out, " "); + print_utf (out, suggs[i]); + + if (i != (n_suggs - 1)) + fwrite (",", 1, 1, out); + else + fwrite ("\n", 1, 1, out); + } + + enchant_dict_free_string_list (dict, suggs); + } + } +} + +static void +do_mode_l (FILE * out, EnchantDict * dict, GString * word, size_t lineCount) +{ + if (enchant_dict_check (dict, word->str, word->len) != 0) { + if (lineCount) + fprintf (out, "%u ", (unsigned int)lineCount); + print_utf (out, word->str); + fwrite ("\n", 1, 1, out); + } +} + + +static int +is_word_char (gunichar uc, size_t n) +{ + GUnicodeType type; + + type = g_unichar_type(uc); + + switch (type) { + case G_UNICODE_MODIFIER_LETTER: + case G_UNICODE_LOWERCASE_LETTER: + case G_UNICODE_TITLECASE_LETTER: + case G_UNICODE_UPPERCASE_LETTER: + case G_UNICODE_OTHER_LETTER: + case G_UNICODE_COMBINING_MARK: + case G_UNICODE_ENCLOSING_MARK: + case G_UNICODE_NON_SPACING_MARK: + case G_UNICODE_DECIMAL_NUMBER: + case G_UNICODE_LETTER_NUMBER: + case G_UNICODE_OTHER_NUMBER: + case G_UNICODE_CONNECT_PUNCTUATION: + return 1; /* Enchant 1.3.0 defines word chars like this. */ + + case G_UNICODE_CONTROL: + case G_UNICODE_FORMAT: + case G_UNICODE_UNASSIGNED: + case G_UNICODE_PRIVATE_USE: + case G_UNICODE_SURROGATE: + case G_UNICODE_DASH_PUNCTUATION: + case G_UNICODE_CLOSE_PUNCTUATION: + case G_UNICODE_FINAL_PUNCTUATION: + case G_UNICODE_INITIAL_PUNCTUATION: + case G_UNICODE_OTHER_PUNCTUATION: + case G_UNICODE_OPEN_PUNCTUATION: + case G_UNICODE_CURRENCY_SYMBOL: + case G_UNICODE_MODIFIER_SYMBOL: + case G_UNICODE_MATH_SYMBOL: + case G_UNICODE_OTHER_SYMBOL: + case G_UNICODE_LINE_SEPARATOR: + case G_UNICODE_PARAGRAPH_SEPARATOR: + case G_UNICODE_SPACE_SEPARATOR: + default: + if ((n > 0) && (uc == g_utf8_get_char("'"))) { + return 1; /** Char ' is accepted only within a word. */ + } + else if ((n > 0) && (type == G_UNICODE_DASH_PUNCTUATION)) { + return 1; /* hyphens only accepted within a word. */ + } + + return 0; + } +} + + +typedef struct lang_map { + const char *ispell; + const char *enchant; +} LangMap; + + +/* Maps ispell language codes to enchant language codes. */ +/* The list is partially taken from src/ispell/ispell_checker.cpp. */ +static const LangMap lingua[] = { + {"american", "en_US"}, + {"brazilian", "pt_BR"}, + {"british", "en_GB"}, + {"bulgarian", "bg"}, + {"catala", "ca"}, + {"catalan", "ca"}, + {"danish", "da"}, + {"dansk", "da"}, + {"deutsch", "de"}, + {"dutch", "nl"}, + {"ellhnika", "el"}, + {"espanol", "es"}, + {"esperanto", "eo"}, + {"estonian", "et"}, + {"faeroese", "fo"}, + {"finnish", "fi"}, + {"francais", "fr"}, + {"french", "fr"}, + {"galician", "gl"}, + {"german", "de"}, + {"hungarian", "hu"}, + {"interlingua", "ia"}, + {"irish", "ga"}, + {"italian", "it"}, + {"latin", "la"}, + {"lietuviu", "lt"}, + {"lithuanian", "lt"}, + {"mlatin", "la"}, + {"nederlands", "nl"}, + {"norsk", "no"}, + {"norwegian", "no"}, + {"nynorsk", "nn"}, + {"polish", "pl"}, + {"portugues", "pt"}, + {"portuguese", "pt"}, + {"russian", "ru"}, + {"sardinian", "sc"}, + {"slovak", "sk"}, + {"slovenian", "sl"}, + {"slovensko", "sl"}, + {"spanish", "es"}, + {"suomi", "fi"}, /* For Emacs/Voikko/tmispell compatibility. */ + {"svenska", "sv"}, + {"swedish", "sv"}, + {"swiss", "de_CH"}, + {"ukrainian", "uk"}, + {"yiddish-yivo", "yi"}, + {NULL, NULL} /* Last item must be {NULL, NULL}. */ +}; + + +/* Converts ispell language code to enchant language code. */ +static gchar * +convert_language_code (gchar *code) +{ + size_t i; + for (i = 0; lingua[i].ispell; i++) { + if (!strcmp(code,lingua[i].ispell)) { + /* We must call g_strdup() because the calling program g_free()s the result. */ + return g_strdup (lingua[i].enchant); + } + } + /* Let's call g_strdup() here too! */ + return g_strdup (code); +} + + +/* Splits a line into a set of (word,word_position) touples. */ +static GSList * +tokenize_line (GString * line) +{ + GSList * tokens = NULL; + char *utf = (char *) line->str; + + GString * word; + + gunichar uc; + size_t cur_pos = 0; + size_t start_pos = 0; + word = g_string_new (NULL); + + while (cur_pos < line->len && *utf) { + int i; + /* Skip non-word characters. */ + cur_pos = g_utf8_pointer_to_offset ((const char*)line->str, utf); + uc = g_utf8_get_char (utf); + while (cur_pos < line->len && *utf && !is_word_char(uc,0)) { + utf = g_utf8_next_char (utf); + uc = g_utf8_get_char (utf); + cur_pos = g_utf8_pointer_to_offset ((const char*)line->str, utf); + } + start_pos = cur_pos; + + /* Skip over word. */ + while (cur_pos < line->len && *utf && is_word_char(uc,1)) { + g_string_append_unichar (word, uc); + utf = g_utf8_next_char (utf); + uc = g_utf8_get_char (utf); + cur_pos = g_utf8_pointer_to_offset ((const char*)line->str, utf); + } + + /* Do not accept one or more ' at the end of the word. */ + i = word->len-1; + while ((i >= 0) && (word->str[i] == '\'')) { + g_string_truncate (word, i); + i--; + } + + /* Save (word, position) touple. */ + if (word->len) { + tokens = g_slist_append (tokens, g_string_new_len (word->str, word->len)); + tokens = g_slist_append (tokens, GINT_TO_POINTER(start_pos)); + g_string_truncate (word, 0); + } + } + g_string_free (word, TRUE); + + return tokens; +} + +static int +parse_file (FILE * in, FILE * out, IspellMode_t mode, int countLines, gchar *dictionary) +{ + EnchantBroker * broker; + EnchantDict * dict; + + GString * str, * word = NULL; + GSList * tokens, *token_ptr; + gchar * lang; + size_t pos, lineCount = 0; + + gboolean was_last_line = FALSE, corrected_something = FALSE; + + if (mode == MODE_A) + print_version (out); + + if (dictionary) { + lang = convert_language_code (dictionary); + } + else { + lang = enchant_get_user_language(); + if(!lang) + return 1; + } + + /* Enchant will get rid of useless trailing garbage like de_DE@euro or de_DE.ISO-8859-15 */ + + broker = enchant_broker_init (); + dict = enchant_broker_request_dict (broker, lang); + + if (!dict) { + fprintf (stderr, "Couldn't create a dictionary for %s\n", lang); + g_free (lang); + enchant_broker_free (broker); + return 1; + } + + g_free (lang); + + str = g_string_new (NULL); + + while (!was_last_line) { + was_last_line = consume_line (in, str); + + if (countLines) + lineCount++; + + if (str->len) { + corrected_something = FALSE; + token_ptr = tokens = tokenize_line (str); + while (tokens != NULL) { + corrected_something = TRUE; + + word = (GString *)tokens->data; + tokens = tokens->next; + pos = GPOINTER_TO_INT(tokens->data); + tokens = tokens->next; + + if (mode == MODE_A) + do_mode_a (out, dict, word, pos, lineCount); + else if (mode == MODE_L) + do_mode_l (out, dict, word, lineCount); + + g_string_free(word, TRUE); + } + if (token_ptr) + g_slist_free (token_ptr); + } + + if (mode == MODE_A && corrected_something) { + fwrite ("\n", 1, 1, out); + } + g_string_truncate (str, 0); + fflush (out); + } + + enchant_broker_free_dict (broker, dict); + enchant_broker_free (broker); + + g_string_free (str, TRUE); + + return 0; +} + +int main (int argc, char ** argv) +{ + IspellMode_t mode = MODE_NONE; + + char * file = NULL; + int i, rval = 0; + + FILE * fp = stdin; + + int countLines = 0; + gchar *dictionary = 0; /* -d dictionary */ + + /* Initialize system locale */ + setlocale(LC_ALL, ""); + +#ifdef WIN32 + /* Workaround about glib's "locale" not being the set C locale */ + if (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) != FILE_TYPE_CHAR) { + sprintf_s(charset,15,"CP%u",GetACP()); + } else { + sprintf_s(charset,15,"CP%u",GetConsoleCP()); + } +#endif + + for (i = 1; i < argc; i++) { + char * arg = argv[i]; + if (arg[0] == '-') { + if (strlen (arg) == 2) { + /* It seems that the first one of these that is specified gets precedence. */ + if (arg[1] == 'a' && MODE_NONE == mode) + mode = MODE_A; + else if (arg[1] == 'l' && MODE_NONE == mode) + mode = MODE_L; + else if (arg[1] == 'v' && MODE_NONE == mode) + mode = MODE_VERSION; + else if (arg[1] == 'L' && MODE_NONE == mode) + countLines = 1; + else if (arg[1] == 'm') + ; /* Ignore. Emacs calls ispell with '-m'. */ + else if (arg[1] == 'd') { + i++; + dictionary = argv[i]; /* Emacs calls ispell with '-d dictionary'. */ + } + } + else if ((strlen (arg) == 3) && (arg[1] == 'v') && (arg[2] == 'v')) { + mode = MODE_VERSION; /* Emacs (or ispell.el) calls [ai]spell with '-vv'. */ + } + else if (arg[1] == 'd') { + dictionary = arg + 2; /* Accept "-ddictionary", i.e. no space between -d and dictionary. */ + } + else if (strlen (arg) > 2) { + fprintf (stderr, "-%c does not take any parameters.\n", arg[1]); + exit(1); + } + else + file = arg; + } + else + file = arg; + } + + if (mode == MODE_VERSION) { + print_version (stdout); + } + else if (mode == MODE_NONE && !file) { + print_help (stdout, argv[0]); + } + else { + if (file) { + fp = enchant_fopen (file, "rb"); + if (!fp) { + fprintf (stderr, "Error: Could not open the file \"%s\" for reading.\n", file); + exit (1); + } + } + + rval = parse_file (fp, stdout, mode, countLines, dictionary); + + if (file) + fclose (fp); + } + + return rval; +} diff --git a/tests/enchant-lsmod.c b/tests/enchant-lsmod.c new file mode 100644 index 0000000..6bed5c2 --- /dev/null +++ b/tests/enchant-lsmod.c @@ -0,0 +1,145 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003 Dom Lachowicz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * the non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#include +#include +#include +#include + +#include "enchant.h" +#include "enchant-provider.h" + +static void +describe_dict (const char * const lang_tag, + const char * const provider_name, + const char * const provider_desc, + const char * const provider_file, + void * user_data) +{ + FILE * out = (FILE *)user_data; + fprintf (out, "%s (%s)\n", lang_tag, provider_name); +} + +static void +enumerate_providers (const char * name, + const char * desc, + const char * file, + void * user_data) +{ + FILE * out = (FILE *)user_data; + fprintf (out, "%s (%s)\n", name, desc); +} + +static void +enumerate_dicts (const char * const lang_tag, + const char * const provider_name, + const char * const provider_desc, + const char * const provider_file, + void * user_data) +{ + FILE * out = (FILE *)user_data; + fprintf (out, "%s (%s)\n", lang_tag, provider_name); +} + +int +main (int argc, char **argv) +{ + EnchantBroker *broker; + EnchantDict *dict; + char * lang_tag = NULL; + + int mode = 0, i; + + for (i = 1; i < argc; i++) { + if (!strcmp (argv[i], "-lang")) { + if (i < (argc - 1)) { + lang_tag = g_strdup (argv[++i]); + } else { + lang_tag = enchant_get_user_language(); + + if (!lang_tag || !strcmp (lang_tag, "C")) { + if (lang_tag) /* lang might be "C" */ + g_free (lang_tag); + lang_tag = g_strdup ("en"); + } + } + mode = 1; + } else if (!strcmp (argv[i], "-h") || !strcmp (argv[i], "-?") || !strcmp(argv[i], "-help")) { + printf ("%s [-lang [language_tag]] [-list-dicts] [-h] [-v]\n", argv[0]); + if (lang_tag) + g_free (lang_tag); + return 0; + } else if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-version")) { + printf ("%s %s\n", argv[0], VERSION); + if (lang_tag) + g_free (lang_tag); + return 0; + } else if (!strcmp (argv[i], "-list-dicts")) { + mode = 2; + } + } + + broker = enchant_broker_init (); + + if (mode == 0) { + enchant_broker_describe (broker, enumerate_providers, stdout); + } else if (mode == 1) { + + if (!lang_tag) { + printf ("Error: language tag not specified and environment variable $LANG not set.\n"); + enchant_broker_free (broker); + return 1; + } + + dict = enchant_broker_request_dict (broker, lang_tag); + + if (!dict) { + printf ("No dictionary available for '%s'.\n", lang_tag); + + if (lang_tag) + g_free (lang_tag); + + enchant_broker_free (broker); + return 1; + } else { + enchant_dict_describe (dict, describe_dict, stdout); + enchant_broker_free_dict (broker, dict); + } + } else if (mode == 2) { + enchant_broker_list_dicts (broker, enumerate_dicts, stdout); + } + + if (lang_tag) + g_free (lang_tag); + + enchant_broker_free (broker); + + return 0; +} diff --git a/tests/ispell.in b/tests/ispell.in new file mode 100755 index 0000000..0346c09 --- /dev/null +++ b/tests/ispell.in @@ -0,0 +1,25 @@ +#!/bin/sh + +# Ispell compatibility script for Enchant + +exec_prefix=@prefix@ +bindir=${exec_prefix}/bin +command="" + +for p +do + case $p in + -a|-A|-l|-c|-e*|-v*|-D) command=$p ;; + -* ) ;; + * ) command=${command:="-"} ;; + esac +done + +case $command in +-A|-c|-e*|-d) echo "Enchant does not support the $command mode.";; +-a|-l|-v* ) exec ${bindir}/enchant "$@" ;; +"-" ) exec ${bindir}/enchant "$@" ;; +* ) echo "Ispell compatibility script for Enchant." + echo "Usage: $0 [options] -a|-l|-v[v]|" + exit 1 ;; +esac diff --git a/tests/test-enchant.c b/tests/test-enchant.c new file mode 100644 index 0000000..29d1e87 --- /dev/null +++ b/tests/test-enchant.c @@ -0,0 +1,157 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003 Dom Lachowicz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * the non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#include +#include +#include +#include "enchant.h" + +static void +enumerate_providers_fn (const char * const name, + const char * const desc, + const char * const file, + void * ud) +{ + printf ("%s: '%s' (%s)\n", name, desc, file); +} + +static void +describe_dict_fn (const char * const lang, + const char * const name, + const char * const desc, + const char * const file, + void * ud) +{ + printf ("%s: %s '%s' (%s)\n", lang, name, desc, file); +} + +static const char * print_found(EnchantDict *dict, const char * word) +{ + if (enchant_dict_check (dict, word, -1) == 0) + return "found"; + return "not found"; +} + +static void +run_dict_tests (EnchantDict * dict) +{ + char **suggs; + size_t n_suggs; + size_t i, j; + + const char *check_checks[] = { "hello", "helllo" }; + const char *sugg_checks[] = { "helllo", "taag" }; + + for (i = 0; i < (sizeof (check_checks) / sizeof (check_checks[0])); i++) + { + printf ("enchant_dict_check (%s): %s\n", check_checks[i], + print_found(dict, check_checks[i])); + } + + for (i = 0; i < (sizeof (sugg_checks) / sizeof (sugg_checks[0])); i++) + { + suggs = + enchant_dict_suggest (dict, sugg_checks[i], strlen (sugg_checks[i]), + &n_suggs); + + printf ("enchant_dict_suggest(%s): %d\n", sugg_checks[i], n_suggs); + for (j = 0; j < n_suggs; j++) + { + printf ("\t=>%s\n", suggs[j]); + } + + if (suggs && n_suggs) + enchant_dict_free_string_list (dict, suggs); + } + + printf ("Adding 'helllo' to session\n"); + enchant_dict_add_to_session (dict, "helllo", 6); + for (i = 0; i < (sizeof (check_checks) / sizeof (check_checks[0])); i++) + { + printf ("enchant_dict_check (%s): %s\n", check_checks[i], + print_found(dict, check_checks[i])); + } + + printf ("Adding 'helllo' to personal\n"); + enchant_dict_add (dict, "helllo", 6); + for (i = 0; i < (sizeof (check_checks) / sizeof (check_checks[0])); i++) + { + printf ("enchant_dict_check (%s): %s\n", check_checks[i], + print_found(dict, check_checks[i])); + } +} + +int +main (int argc, char **argv) +{ + EnchantBroker *broker; + EnchantDict *dict; + const char * err; + + broker = enchant_broker_init (); + + dict = enchant_broker_request_dict (broker, "en_US"); + + if (!dict) + { + err = enchant_broker_get_error (broker); + if (err) + fprintf (stderr, "Couldn't create dictionary for en_US: %s\n", err); + else + fprintf (stderr, "Couldn't create dictionary for en_US\n"); + } + else + { + enchant_dict_describe (dict, describe_dict_fn, NULL); + run_dict_tests (dict); + enchant_broker_free_dict (broker, dict); + } + + dict = enchant_broker_request_pwl_dict (broker, "test.pwl"); + if (!dict) + { + err = enchant_broker_get_error (broker); + if (err) + fprintf (stderr, "Couldn't create personal wordlist dictionary: %s\n", err); + else + fprintf (stderr, "Couldn't create personal wordlist dictionary\n"); + } + else + { + enchant_dict_describe (dict, describe_dict_fn, NULL); + run_dict_tests (dict); + enchant_broker_free_dict (broker, dict); + } + + enchant_broker_describe (broker, enumerate_providers_fn, NULL); + enchant_broker_free (broker); + + return 0; +} diff --git a/tests/test-enchantxx.cpp b/tests/test-enchantxx.cpp new file mode 100644 index 0000000..22e4e1b --- /dev/null +++ b/tests/test-enchantxx.cpp @@ -0,0 +1,125 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003 Dom Lachowicz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * the non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU Lesser General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#include +#include +#include +#include "enchant.h" +#include "enchant++.h" + +static void +enumerate_providers_fn (const char * const name, + const char * const desc, + const char * const file, + void * ud) +{ + printf ("%s: '%s' (%s)\n", name, desc, file); +} + +static void +describe_dict (enchant::Dict * dict) +{ + printf ("%s: %s '%s' (%s)\n", dict->get_lang().c_str(), dict->get_provider_name().c_str(), + dict->get_provider_desc().c_str(), dict->get_provider_file().c_str()); +} + +static void +run_dict_tests (enchant::Dict * dict) +{ + std::vector suggs; + size_t i, j; + + const char *check_checks[] = { "hello", "helllo" }; + const char *sugg_checks[] = { "helllo", "taag" }; + + for (i = 0; i < (sizeof (check_checks) / sizeof (check_checks[0])); i++) + { + printf ("enchant_dict_check (%s): %d\n", check_checks[i], + dict->check (check_checks[i]) == false); + } + + for (i = 0; i < (sizeof (sugg_checks) / sizeof (sugg_checks[0])); i++) + { + dict->suggest (sugg_checks[i], suggs); + + printf ("enchant_dict_suggest(%s): %d\n", sugg_checks[i], suggs.size()); + for (j = 0; j < suggs.size(); j++) + { + printf ("\t=>%s\n", suggs[j].c_str()); + } + } + + printf ("Adding 'helllo' to session\n"); + dict->add_to_session ("helllo"); + for (i = 0; i < (sizeof (check_checks) / sizeof (check_checks[0])); i++) + { + printf ("enchant_dict_check (%s): %d\n", check_checks[i], + dict->check (check_checks[i]) == false); + } + +#if 0 + printf ("Adding 'helllo' to personal\n"); + dict->add_to_pwl ("helllo"); + for (i = 0; i < (sizeof (check_checks) / sizeof (check_checks[0])); i++) + { + printf ("enchant_dict_check (%s): %d\n", check_checks[i], + dict->check (check_checks[i]) == false); + } +#endif +} + +int +main (int argc, char **argv) +{ + enchant::Broker *broker; + enchant::Dict *dict; + + broker = enchant::Broker::instance (); + + try { + dict = broker->request_dict ("en_US"); + describe_dict (dict); + run_dict_tests (dict); + delete dict; + + // test personal wordlist dictionaries + dict = broker->request_pwl_dict ("test.pwl"); + describe_dict (dict); + run_dict_tests (dict); + delete dict; + } catch (enchant::Exception & ex) { + fprintf (stderr, "Couldn't create dictionary for en_US: %s\n", ex.what()); + return 1; + } + + broker->describe (enumerate_providers_fn); + + return 0; +} diff --git a/tests/test.pwl b/tests/test.pwl new file mode 100644 index 0000000..c6089a7 --- /dev/null +++ b/tests/test.pwl @@ -0,0 +1,2 @@ +hello +tag diff --git a/unittests/Enchant.Net.Tests/BrokerTests.cs b/unittests/Enchant.Net.Tests/BrokerTests.cs new file mode 100644 index 0000000..a53afd0 --- /dev/null +++ b/unittests/Enchant.Net.Tests/BrokerTests.cs @@ -0,0 +1,363 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Win32; +using NUnit.Framework; + +namespace Enchant.Tests +{ + [TestFixture] + public class BrokerTests + { + #region Setup/Teardown + + [SetUp] + public void Setup() + { + oldRegistryValue = (string) + Registry.GetValue(@"HKEY_CURRENT_USER\Software\Enchant\Config", "Data_Dir", null); + tempdir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + + Registry.SetValue(@"HKEY_CURRENT_USER\Software\Enchant\Config", "Data_Dir", tempdir, RegistryValueKind.String); + } + + [TearDown] + public void Teardown() + { + if (oldRegistryValue == null) + { + Registry.CurrentUser.OpenSubKey("Software").OpenSubKey("Enchant").OpenSubKey("Config", true).DeleteValue( + "Data_Dir"); + } + else + { + Registry.SetValue(@"HKEY_CURRENT_USER\Software\Enchant\Config", + "Data_Dir", + oldRegistryValue, + RegistryValueKind.String); + } + while (Directory.Exists(tempdir)) + { + Directory.Delete(tempdir, true); + } + } + + #endregion + + private string tempdir; + private string oldRegistryValue; + + [TestFixtureSetUp] + public void FixtureSetup() + { + string providerDir = Path.Combine(Path.Combine( + Directory.GetCurrentDirectory(), "lib"), "enchant"); + if (!Directory.Exists(providerDir)) + { + Directory.CreateDirectory(providerDir); + } + File.Copy("libenchant_ispell.dll", + Path.Combine(providerDir, "libenchant_ispell.dll"), true); + File.Copy("libenchant_myspell.dll", + Path.Combine(providerDir, "libenchant_myspell.dll"), true); + InstallDictionary("myspell", new string[] { "en_US.aff", "en_US.dic" }); + } + + static private void InstallDictionary(string provider, IEnumerable files) + { + string dictionarySourceDir = + Path.Combine(Path.Combine(Path.Combine(Path.Combine(Path.Combine(Path.Combine( + Directory.GetCurrentDirectory(), ".."), ".."), + "lib"), "share"), + "enchant"), provider); + + string dictionaryDestDir = Path.Combine(Path.Combine(Path.Combine( + Directory.GetCurrentDirectory(), "share"), "enchant"), + provider); + + if (!Directory.Exists(dictionaryDestDir)) + { + Directory.CreateDirectory(dictionaryDestDir); + } + + foreach (string file in files) + { + File.Copy(Path.Combine(dictionarySourceDir, file), + Path.Combine(dictionaryDestDir, file), true); + + } + } + + [TestFixtureTearDown] + public void FixtureTearDown() + { + Directory.Delete(Path.Combine(Directory.GetCurrentDirectory(), "lib"), true); + Directory.Delete(Path.Combine(Directory.GetCurrentDirectory(), "share"), true); + } + + [Test] + public void Construct() + { + Assert.IsNotNull(new Broker()); + } + + [Test] + // this test may fail because it picks up dictionaries from the + // open office installation present on the machine + public void Dictionaries() + { + using (Broker broker = new Broker()) + { + IEnumerable dictionaries = broker.Dictionaries; + Assert.IsNotNull(dictionaries); + int count = 0; + foreach (DictionaryInfo info in dictionaries) + { + Console.WriteLine("Language:{0}\tName:{1}\tDescription:{2}\tFile:{3}", + info.Language, + info.Provider.Name, + info.Provider.Description, + info.Provider.File); + Assert.IsNotEmpty(info.Language); + Assert.IsNotEmpty(info.Provider.Name); + Assert.IsNotEmpty(info.Provider.Description); + Assert.IsNotEmpty(info.Provider.File); + ++count; + } + Assert.AreEqual(1, count); + } + } + + [Test] + public void DictionaryExists() + { + using (Broker broker = new Broker()) + { + Assert.IsFalse(broker.DictionaryExists("qaa")); + Assert.IsTrue(broker.DictionaryExists("en_US")); + } + } + + [Test] + public void Providers() + { + using (Broker broker = new Broker()) + { + IEnumerable providers = broker.Providers; + Assert.IsNotNull(providers); + int count = 0; + foreach (ProviderInfo info in providers) + { + Console.WriteLine("Name:{0}\tDescription:{1}\tFile:{2}", + info.Name, + info.Description, + info.File); + Assert.IsNotEmpty(info.Name); + Assert.IsNotEmpty(info.Description); + Assert.IsNotEmpty(info.File); + ++count; + } + Assert.AreEqual(2, count); + } + } + + [Test] + public void RequestDictionary() + { + using (Broker broker = new Broker()) + { + Dictionary dictionary = broker.RequestDictionary("en_US"); + Assert.IsNotNull(dictionary); + } + } + + [Test] + public void RequestDictionary_CachingEnabled_DictionaryReRequested_SameReference() + { + using (Broker broker = new Broker()) + { + broker.CacheDictionaries = true; + Dictionary dictionaryFirstRequest = broker.RequestDictionary("en_US"); + Dictionary dictionarySecondRequest = broker.RequestDictionary("en_US"); + + Assert.AreSame(dictionaryFirstRequest, dictionarySecondRequest); + } + } + + [Test] + public void RequestDictionary_CachingEnabled_DictionaryDisposedThenReRequested_DifferentReference() + { + using (Broker broker = new Broker()) + { + broker.CacheDictionaries = true; + Dictionary dictionaryFirstRequest; + using (dictionaryFirstRequest = broker.RequestDictionary("en_US")) {} + Dictionary dictionarySecondRequest = broker.RequestDictionary("en_US"); + + Assert.AreNotSame(dictionaryFirstRequest, dictionarySecondRequest); + } + } + + [Test] + public void RequestDictionary_CachingDisabled_DictionaryReRequested_DifferentReference() + { + using (Broker broker = new Broker()) + { + broker.CacheDictionaries = false; + Dictionary dictionaryFirstRequest = broker.RequestDictionary("en_US"); + Dictionary dictionarySecondRequest = broker.RequestDictionary("en_US"); + + Assert.AreNotSame(dictionaryFirstRequest, dictionarySecondRequest); + } + } + + [Test] + public void RequestDictionary_CachingDisabled_DictionaryDisposedThenReRequested_DifferentReference() + { + using (Broker broker = new Broker()) + { + broker.CacheDictionaries = false; + Dictionary dictionaryFirstRequest; + using (dictionaryFirstRequest = broker.RequestDictionary("en_US")) + { + } + Dictionary dictionarySecondRequest = broker.RequestDictionary("en_US"); + + Assert.AreNotSame(dictionaryFirstRequest, dictionarySecondRequest); + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Dispose_UseDictionaryAfterBrokerDisposed_Throws() + { + Dictionary dictionary; + using (Broker broker = new Broker()) + { + dictionary = broker.RequestDictionary("en_US"); + } + DictionaryInfo info = dictionary.Information; + } + + [Test] + [ExpectedException(typeof (ApplicationException))] + public void RequestDictionary_DictionaryDoesNotExist_Throws() + { + using (Broker broker = new Broker()) + { + broker.RequestDictionary("qaa"); + } + } + + [Test] + public void RequestPwlDictionary() + { + string filename = Path.GetTempFileName(); + using (Broker broker = new Broker()) + { + using (Dictionary dictionary = broker.RequestPwlDictionary(filename)) + { + Assert.IsNotNull(dictionary); + File.Delete(filename); + } + } + } + + [Test] + public void SetOrdering() + { + using (Broker broker = new Broker()) + { + broker.SetOrdering("en_US", "aspell, myspell, ispell"); + } + } + + [Test] + public void DictionaryKeepsBrokerAlive() + { + WeakReference brokerReference; + Dictionary dictionary = GetDictionaryAllowingBrokerToGoOutOfScope(out brokerReference); + GC.Collect(); + GC.WaitForPendingFinalizers(); + Assert.IsTrue(brokerReference.IsAlive); + GC.KeepAlive(dictionary); + } + + private static Dictionary GetDictionaryAllowingBrokerToGoOutOfScope(out WeakReference brokerReference) + { + Broker broker = new Broker(); + brokerReference = new WeakReference(broker); + return broker.RequestDictionary("en_US"); + } + + [Test] + public void Finalize_DictionaryGoesOutOfScope_Finalized() + { + using (Broker broker = new Broker()) + { + broker.CacheDictionaries = true; + WeakReference dictionaryReference = GetDictionaryReference(broker); + GC.Collect(); + GC.WaitForPendingFinalizers(); + Assert.IsFalse(dictionaryReference.IsAlive); + } + } + + //this will allow the dictionary object to go out of scope + private static WeakReference GetDictionaryReference(Broker broker) + { + Dictionary dictionary = broker.RequestDictionary("en_US"); + return new WeakReference(dictionary); + } + + [Test] + public void Default_ReturnsNonNull() + { + Assert.IsNotNull(Broker.Default); + } + + [Test] + public void Default_CalledTwice_ReturnsSame() + { + Assert.AreSame(Broker.Default, Broker.Default); + } + + [Test] + public void Default_Disposed_ReturnsNonNull() + { + Broker.Default.Dispose(); + Assert.IsNotNull(Broker.Default); + } + + [Test] + public void Default_Disposed_ReturnsNewObject() + { + Broker originalBroker = Broker.Default; + originalBroker.Dispose(); + + Assert.AreNotSame(originalBroker, Broker.Default); + } + + } +} diff --git a/unittests/Enchant.Net.Tests/DictionaryTests.cs b/unittests/Enchant.Net.Tests/DictionaryTests.cs new file mode 100644 index 0000000..9f7bc39 --- /dev/null +++ b/unittests/Enchant.Net.Tests/DictionaryTests.cs @@ -0,0 +1,219 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using Microsoft.Win32; +using NUnit.Framework; + +namespace Enchant.Tests +{ + [TestFixture] + public class DictionaryTests + { + #region Setup/Teardown + + [SetUp] + public void Setup() + { + oldRegistryValue = (string) Registry.GetValue(@"HKEY_CURRENT_USER\Software\Enchant\Config", "Data_Dir", null); + tempdir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + + Registry.SetValue(@"HKEY_CURRENT_USER\Software\Enchant\Config", "Data_Dir", tempdir, RegistryValueKind.String); + broker = new Broker(); + dictionary = broker.RequestDictionary("en_US"); + } + + [TearDown] + public void Teardown() + { + if (oldRegistryValue == null) + { + Registry.CurrentUser.OpenSubKey("Software").OpenSubKey("Enchant").OpenSubKey("Config", true).DeleteValue( + "Data_Dir"); + } + else + { + Registry.SetValue(@"HKEY_CURRENT_USER\Software\Enchant\Config", + "Data_Dir", + oldRegistryValue, + RegistryValueKind.String); + } + + dictionary.Dispose(); + broker.Dispose(); + while (Directory.Exists(tempdir)) + { + Directory.Delete(tempdir, true); + } + } + + #endregion + + private Broker broker; + private Dictionary dictionary; + private string tempdir; + private string oldRegistryValue; + + [TestFixtureSetUp] + public void FixtureSetup() + { + string providerDir = Path.Combine(Path.Combine( + Directory.GetCurrentDirectory(), "lib"), "enchant"); + if (!Directory.Exists(providerDir)) + { + Directory.CreateDirectory(providerDir); + } + File.Copy("libenchant_ispell.dll", + Path.Combine(providerDir, "libenchant_ispell.dll"), true); + File.Copy("libenchant_myspell.dll", + Path.Combine(providerDir, "libenchant_myspell.dll"), true); + InstallDictionary("myspell", new string[]{"en_US.aff", "en_US.dic"}); + } + + static private void InstallDictionary(string provider, IEnumerable files) + { + string dictionarySourceDir = + Path.Combine(Path.Combine(Path.Combine(Path.Combine(Path.Combine(Path.Combine( + Directory.GetCurrentDirectory(), ".."), ".."), + "lib"), "share"), + "enchant"), provider); + + string dictionaryDestDir = Path.Combine(Path.Combine(Path.Combine( + Directory.GetCurrentDirectory(), "share"), "enchant"), + provider); + + if (!Directory.Exists(dictionaryDestDir)) + { + Directory.CreateDirectory(dictionaryDestDir); + } + + foreach (string file in files) + { + File.Copy(Path.Combine(dictionarySourceDir, file), + Path.Combine(dictionaryDestDir, file), true); + + } + } + + [TestFixtureTearDown] + public void FixtureTearDown() + { + Directory.Delete(Path.Combine(Directory.GetCurrentDirectory(), "lib"), true); + Directory.Delete(Path.Combine(Directory.GetCurrentDirectory(), "share"), true); + } + + [Test] + public void Add() + { + Assert.IsFalse(dictionary.Check("Googled")); + dictionary.Add("Googled"); + Assert.IsTrue(dictionary.Check("Googled")); + } + + [Test] + public void AddToSession(string word) + { + Assert.IsFalse(dictionary.Check("list")); + dictionary.AddToSession("list"); + Assert.IsTrue(dictionary.Check("list")); + } + + [Test] + public void Check() + { + Assert.IsTrue(dictionary.Check("hello")); + Assert.IsFalse(dictionary.Check("helo")); + } + + [Test] + public void Information() + { + DictionaryInfo info = dictionary.Information; + Console.WriteLine("Language:{0}\tName:{1}\tDescription:{2}\tFile:{3}", + info.Language, + info.Provider.Name, + info.Provider.Description, + info.Provider.File); + Assert.AreEqual("en_US", info.Language); + Assert.IsNotEmpty(info.Provider.Name); + Assert.IsNotEmpty(info.Provider.Description); + Assert.IsNotEmpty(info.Provider.File); + } + + [Test] + public void IsAdded() + { + Assert.IsFalse(dictionary.IsAdded("list")); + dictionary.AddToSession("list"); + Assert.IsTrue(dictionary.IsAdded("list")); + } + + [Test] + public void IsRemoved() + { + Assert.IsFalse(dictionary.IsRemoved("list")); + dictionary.RemoveFromSession("list"); + Assert.IsTrue(dictionary.IsRemoved("list")); + } + + [Test] + public void Remove() + { + Assert.IsTrue(dictionary.Check("world")); + dictionary.Remove("world"); + Assert.IsFalse(dictionary.Check("world")); + } + + [Test] + public void RemoveFromSession() + { + Assert.IsTrue(dictionary.Check("hello")); + dictionary.RemoveFromSession("hello"); + Assert.IsFalse(dictionary.Check("hello")); + } + + [Test] + public void StoreReplacement() + { + dictionary.StoreReplacement("theirs", "their's"); + } + + [Test] + public void Suggest() + { + List suggestions = new List(dictionary.Suggest("helo")); + Assert.Contains("hello", suggestions); + } + + [Test] + public void Dispose_Called_SendsDisposedEvent() + { + bool disposedEventCalled = false; + dictionary.Disposed += delegate + { disposedEventCalled = true; }; + dictionary.Dispose(); + Assert.IsTrue(disposedEventCalled); + } + } +} \ No newline at end of file diff --git a/unittests/Enchant.Net.Tests/Enchant.Net.Tests.csproj b/unittests/Enchant.Net.Tests/Enchant.Net.Tests.csproj new file mode 100644 index 0000000..cbe87c8 --- /dev/null +++ b/unittests/Enchant.Net.Tests/Enchant.Net.Tests.csproj @@ -0,0 +1,59 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {518F02B2-A542-44B3-A10D-CA8144268077} + Library + Properties + Enchant.Tests + Enchant.Net.Tests + + + true + full + false + ..\..\bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\bin\Release\ + TRACE + prompt + 4 + + + + False + + + + + + + + + + + {249A514E-7212-485B-86D0-E0ACB8967A4C} + Enchant.Net + + + + + + + + + \ No newline at end of file diff --git a/unittests/Enchant.Net.Tests/Properties/AssemblyInfo.cs b/unittests/Enchant.Net.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..10452a6 --- /dev/null +++ b/unittests/Enchant.Net.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Enchant.Net.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Enchant.Net.Tests")] +[assembly: AssemblyCopyright("Copyright © 2007 Eric Scott Albright")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d3f8e92e-e8be-4be3-94ef-2205d88abad5")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/unittests/EnchantBrokerTestFixture.h b/unittests/EnchantBrokerTestFixture.h new file mode 100644 index 0000000..dbfda60 --- /dev/null +++ b/unittests/EnchantBrokerTestFixture.h @@ -0,0 +1,313 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#ifndef __ENCHANTBROKERTESTFIXTURE +#define __ENCHANTBROKERTESTFIXTURE + +#if defined(_MSC_VER) +#pragma once +#pragma warning(push) +#pragma warning(disable:4505) //unreferenced local function has been removed +#pragma warning(disable: 4996) //The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name. +#endif + +#include "EnchantTestFixture.h" +#include "mock_provider/mock_provider.h" +#include +#include +#include + +#if !defined(_WIN32) +#if !defined(O_BINARY) +#define O_BINARY 0 // Posix systems don'd distinguish between text and binary so define O_BINARY for compatibility +#endif +#endif + +////////////////////////////////////// +// Mock provider functions +static void +MockProviderDispose(EnchantProvider *me) +{ + g_free(me); +} + +static int +MockEnGbAndQaaProviderDictionaryExists (EnchantProvider *, + const char *const tag) +{ + return (strcmp(tag, "en_GB")==0 || strcmp(tag, "qaa") ==0); +} + + +static EnchantDict* +MockEnGbAndQaaProviderRequestDictionary(EnchantProvider * me, const char *tag) +{ + EnchantDict *dict = NULL; + + if(MockEnGbAndQaaProviderDictionaryExists(me, tag)){ + dict = g_new0 (EnchantDict, 1); + } + return dict; +} + +static void +MockProviderDisposeDictionary (EnchantProvider *, EnchantDict * dict) +{ + g_free(dict); +} + +static const char * +MockProviderIdentify (EnchantProvider *) +{ + return "mock"; +} + +static const char * +MockProviderDescribe (EnchantProvider *) +{ + return "Mock Provider"; +} + +static char ** +MockEnGbAndQaaProviderListDictionaries (EnchantProvider *, + size_t * out_n_dicts) +{ + *out_n_dicts = 2; + char** out_list = g_new0 (char *, *out_n_dicts + 1); + out_list[0] = g_strdup ("en_GB"); + out_list[1] = g_strdup ("qaa"); + + return out_list; +} + +static char ** +MockEnGbProviderListDictionaries (EnchantProvider *, + size_t * out_n_dicts) +{ + *out_n_dicts = 1; + char** out_list = g_new0 (char *, *out_n_dicts + 1); + out_list[0] = g_strdup ("en_GB"); + + return out_list; +} + +static void +MockProviderFreeStringList (EnchantProvider *, char **str_list) +{ + g_strfreev (str_list); +} + +typedef void (*SET_CONFIGURE)(ConfigureHook); + +struct EnchantBrokerTestFixture : EnchantTestFixture +{ + EnchantBroker* _broker; + HMODULE hModule, hModule2; + + //Setup + EnchantBrokerTestFixture(ConfigureHook userConfiguration=NULL, + ConfigureHook user2Configuration=NULL, + bool includeNullProviders = false) + { + userMockProviderConfiguration = userConfiguration; + userMockProvider2Configuration = user2Configuration; + +#if _WIN32 + CopyProvider("libenchant_mock_provider", "libenchant_mock_provider"); + hModule = LoadLibrary(L"lib\\enchant\\libenchant_mock_provider.dll"); + if(hModule!=NULL){ + SET_CONFIGURE sc = (SET_CONFIGURE) GetProcAddress(hModule, "set_configure"); + (sc)(ConfigureMockProvider); + } + + hModule2 = NULL; + if(user2Configuration != NULL){ + CopyProvider("libenchant_mock_provider", "libenchant_mock_provider2"); + hModule2 = LoadLibrary(L"lib\\enchant\\libenchant_mock_provider2.dll"); + if(hModule2!=NULL){ + SET_CONFIGURE sc = (SET_CONFIGURE) GetProcAddress(hModule2, "set_configure"); + (sc)(ConfigureMockProvider2); + } + } +#else + // What to do? +#endif + + if(includeNullProviders){ + CopyProvider("libenchant_mock_provider", "null_provider"); + CopyProvider("libenchant_mock_provider", "null_identify"); + CopyProvider("libenchant_mock_provider", "null_describe"); + CopyProvider("libenchant", "libenchant"); //not a provider + } + + SetUserRegistryConfigDir(GetTempUserEnchantDir()); + InitializeBroker(); + } + + //Teardown + ~EnchantBrokerTestFixture(){ +#if _WIN32 + if(hModule!=NULL){ + FreeLibrary(hModule); + } + if(hModule2!=NULL){ + FreeLibrary(hModule2); + } +#endif + + if(_broker){ + enchant_broker_free (_broker); + } + while(!pwlFilenames.empty()) + { + DeleteFile(pwlFilenames.top()); + pwlFilenames.pop(); + } + } + + void InitializeBroker() + { + _broker = enchant_broker_init (); + } + + void CopyProvider(const std::string& sourceProviderName, const std::string& destinationProviderName) + { + std::string sourceName = sourceProviderName +"."+ G_MODULE_SUFFIX; + std::string destinationName = destinationProviderName + "." +G_MODULE_SUFFIX; + + std::string destinationDir = AddToPath(AddToPath(GetDirectoryOfThisModule(), "lib"),"enchant"); + + CreateDirectory(destinationDir); + + std::string destinationPath = AddToPath(destinationDir, destinationName); + + gchar * contents; + gsize length; + if(g_file_get_contents(AddToPath(GetDirectoryOfThisModule(), sourceName).c_str(), + &contents, + &length, + NULL)){ + g_file_set_contents(destinationPath.c_str(), + contents, + length, + NULL); + } + } + + + EnchantProvider* GetMockProvider(){ + return mock_provider; + } + + void SetErrorOnMockProvider(const std::string& error) + { + EnchantProvider* provider = GetMockProvider(); + if(provider != NULL) + { + enchant_provider_set_error(provider, error.c_str()); + } + } + + EnchantDict* RequestDictionary(const std::string& tag){ + return enchant_broker_request_dict(_broker, tag.c_str()); + } + + EnchantDict* RequestPersonalDictionary() + { + std::string pwlFileName = GetTemporaryFilename("epwl"); + CreateFile(pwlFileName); + pwlFilenames.push(pwlFileName); + return enchant_broker_request_pwl_dict(_broker, pwlFileName.c_str()); + } + + std::string GetLastPersonalDictionaryFileName() + { + return pwlFilenames.top(); + } + + + void FreeDictionary(EnchantDict* dictionary){ + if(dictionary != NULL){ + enchant_broker_free_dict(_broker, dictionary); + } + } + + private: + std::stack pwlFilenames; + static EnchantProvider * mock_provider; + static ConfigureHook userMockProviderConfiguration; + static ConfigureHook userMockProvider2Configuration; + static void ConfigureMockProvider (EnchantProvider * me, const char * dir_name) + { + mock_provider = me; + if(userMockProviderConfiguration){ + userMockProviderConfiguration(me, dir_name); + } + } + + static void ConfigureMockProvider2 (EnchantProvider * me, const char * dir_name) + { + mock_provider = me; + if(userMockProvider2Configuration){ + userMockProvider2Configuration(me, dir_name); + } + } +}; + +struct DictionaryDescription +{ + std::string LanguageTag; + std::string Name; + std::string Description; + std::string DllFile; + DictionaryDescription() + {} + DictionaryDescription(const char* const language_tag, + const char * const provider_name, + const char * const provider_desc, + const char * const provider_file): + LanguageTag(language_tag), + Name(provider_name), + Description(provider_desc), + DllFile(provider_file) + {} + + DictionaryDescription(const DictionaryDescription& d): + LanguageTag(d.LanguageTag), + Name(d.Name), + Description(d.Description), + DllFile(d.DllFile) + {} + + bool DataIsComplete() + { + return (LanguageTag.length() && + Name.length() && + Description.length() && + DllFile.length()); + } +}; + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif diff --git a/unittests/EnchantDictionaryTestFixture.h b/unittests/EnchantDictionaryTestFixture.h new file mode 100644 index 0000000..a31d299 --- /dev/null +++ b/unittests/EnchantDictionaryTestFixture.h @@ -0,0 +1,352 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#ifndef __ENCHANTDICTIONARYTESTFIXTURE +#define __ENCHANTDICTIONARYTESTFIXTURE + +#if defined(_MSC_VER) +#pragma once +#pragma warning(push) +#pragma warning(disable: 4996) //The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name. +#endif + +#include "../EnchantBrokerTestFixture.h" +#include +#include +#include +#include +#include + +static EnchantDict* +MockProviderRequestEmptyMockDictionary(EnchantProvider *, const char *) +{ + EnchantDict* dict = g_new0(EnchantDict, 1); + dict->user_data = NULL; + dict->check = NULL; + dict->suggest = NULL; + dict->add_to_personal = NULL; + dict->add_to_session = NULL; + dict->store_replacement = NULL; + + return dict; +} + +static void EmptyDictionary_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = MockProviderRequestEmptyMockDictionary; + me->dispose_dict = MockProviderDisposeDictionary; +} + + +static char** +MockDictionarySuggest (EnchantDict * , + const char *const word, + size_t len, + size_t * out_n_suggs) +{ + char **sugg_arr = NULL; + + *out_n_suggs = 4; + + sugg_arr = g_new0 (char *, *out_n_suggs + 1); + for(size_t i=0; i<*out_n_suggs;++i){ + if(len == -1) { + sugg_arr[i] = g_strdup (word); + } + else { + sugg_arr[i] = g_strndup (word, (gsize)len); + } + sugg_arr[i][0] = 'a' + (char)i; + } + return sugg_arr; +} + +static EnchantDict* +MockProviderRequestBasicMockDictionary(EnchantProvider *me, const char *tag) +{ + + EnchantDict* dict = MockProviderRequestEmptyMockDictionary(me, tag); + dict->suggest = MockDictionarySuggest; + return dict; +} + + +static void BasicDictionary_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = MockProviderRequestBasicMockDictionary; + me->dispose_dict = MockProviderDisposeDictionary; + me->free_string_list = MockProviderFreeStringList; +} + +struct EnchantDictionaryTestFixture : EnchantBrokerTestFixture +{ + EnchantDict* _dict; + EnchantDict* _pwl; + std::string _pwlFileName; + std::string origLangEnv; + bool hasLangEnv; + std::string languageTag; + + //Setup + EnchantDictionaryTestFixture(ConfigureHook userConfiguration=BasicDictionary_ProviderConfiguration, const std::string& languageTag="qaa"): + EnchantBrokerTestFixture(userConfiguration), + languageTag(languageTag) + { + InitializeTestDictionary(); + _pwl = RequestPersonalDictionary(); + _pwlFileName = GetLastPersonalDictionaryFileName(); + + bool hasLangEnv = (g_getenv("LANG") != NULL); + if(hasLangEnv) + { + origLangEnv = std::string(g_getenv("LANG")); + } + } + + //Teardown + ~EnchantDictionaryTestFixture(){ + FreeTestDictionary(); + FreeDictionary(_pwl); + ResetLocale(); + } + + void SetLocale(const std::string& locale) + { + g_setenv("LANG", locale.c_str(), TRUE); + } + + void ResetLocale() + { + if(hasLangEnv) + { + g_setenv("LANG", origLangEnv.c_str(), TRUE); + } + else{ + g_unsetenv("LANG"); + } + } + + void ReloadTestDictionary() + { + FreeTestDictionary(); + InitializeTestDictionary(); + } + + void InitializeTestDictionary() + { + _dict = enchant_broker_request_dict(_broker, languageTag.c_str()); + } + + void FreeTestDictionary() + { + FreeDictionary(_dict); + } + + bool PersonalWordListFileHasContents() + { + return FileHasContents(GetPersonalDictFileName()); + } + + bool ExcludeFileHasContents() + { + return FileHasContents(GetExcludeDictFileName()); + } + + bool BrokerPWLFileHasContents() + { + return FileHasContents(_pwlFileName); + } + + bool FileHasContents(const std::string & filename) + { + bool hasContents = false; + int fd = g_open(filename.c_str(), O_RDONLY, S_IREAD ); + if(fd == -1){ + return false; + } + + char c; + while(read(fd, &c, 1) == 1 && !hasContents){ + switch(c) + { + case '\n': + case '\r': + case ' ': + break; + default: + hasContents = true; + } + } + close(fd); + + return hasContents; + } + + std::string GetPersonalDictFileName(){ + return AddToPath(GetTempUserEnchantDir(), "qaa.dic"); + } + + std::string GetExcludeDictFileName(){ + return AddToPath(GetTempUserEnchantDir(), "qaa.exc"); + } + + void SetErrorOnMockDictionary(const std::string& error) + { + enchant_dict_set_error(_dict, error.c_str()); + } + + void FreeStringList(char** list) + { + if(list) + { + enchant_dict_free_string_list(_dict, list); + } + } + + bool IsWordInSession(const std::string& word){ + return enchant_dict_is_in_session(_dict, word.c_str(), word.size())!=0; + } + + bool IsWordInDictionary(const std::string& word){ + return enchant_dict_check(_dict, word.c_str(), word.size())==0; + } + + void RemoveWordFromDictionary(const std::string& word) + { + enchant_dict_remove(_dict, word.c_str(), word.size()); + } + + void AddWordToDictionary(const std::string& word) + { + enchant_dict_add(_dict, word.c_str(), word.size()); + } + + void AddWordsToDictionary(const std::vector& sWords) + { + for(std::vector::const_iterator itWord = sWords.begin(); + itWord != sWords.end(); + ++itWord){ + AddWordToDictionary(*itWord); + } + } + + void ExternalAddWordToDictionary(const std::string& word) + { + ExternalAddWordToFile(word, GetPersonalDictFileName()); + } + + void ExternalAddWordToExclude(const std::string& word) + { + ExternalAddWordToFile(word, GetExcludeDictFileName()); + } + + static void ExternalAddWordToFile(const std::string& word, const std::string& filename) + { + Sleep(1000); // FAT systems have a 2 second resolution + // NTFS is appreciably faster but no specs on what it is exactly + // c runtime library's time_t has a 1 second resolution + FILE * f = g_fopen(filename.c_str(), "at"); + if(f) + { + fputc('\n', f); + fputs(word.c_str(), f); + fclose(f); + } + } + + void ExternalAddNewLineToDictionary() + { + Sleep(1000); // FAT systems have a 2 second resolution + // NTFS is appreciably faster but no specs on what it is exactly + // c runtime library's time_t has a 1 second resolution + FILE * f = g_fopen(GetPersonalDictFileName().c_str(), "at"); + if(f) + { + fputc('\n', f); + fclose(f); + } + } + + void ExternalAddWordsToDictionary(const std::vector& sWords) + { + Sleep(1000); // FAT systems have a 2 second resolution + // NTFS is appreciably faster but no specs on what it is exactly + // c runtime library's time_t has a 1 second resolution + FILE * f = g_fopen(GetPersonalDictFileName().c_str(), "at"); + if(f) + { + for(std::vector::const_iterator + itWord = sWords.begin(); + itWord != sWords.end(); ++itWord) + { + if(itWord != sWords.begin()){ + fputc('\n', f); + } + fputs(itWord->c_str(), f); + } + fclose(f); + } + } + + std::vector GetExpectedSuggestions(const std::string& s, size_t begin = 0) + { + size_t cSuggestions; + char** expectedSuggestions = MockDictionarySuggest (_dict, + s.c_str(), + s.size(), + &cSuggestions); + + std::vector result; + if(expectedSuggestions != NULL && begin < cSuggestions){ + result.insert(result.begin(), expectedSuggestions+begin, expectedSuggestions+cSuggestions); + FreeStringList(expectedSuggestions); + } + + return result; + } + + + std::vector GetSuggestionsFromWord(const std::string& word) + { + std::vector result; + + size_t cSuggestions; + char** suggestions = enchant_dict_suggest(_dict, word.c_str(), word.size(), &cSuggestions); + + if(suggestions != NULL){ + result.insert(result.begin(), suggestions, suggestions+cSuggestions); + } + + FreeStringList(suggestions); + + return result; + } + + std::vector GetSuggestions(const std::string& s) + { + return GetSuggestionsFromWord("helo"); + } +}; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif diff --git a/unittests/EnchantTestFixture.h b/unittests/EnchantTestFixture.h new file mode 100644 index 0000000..7a0c6ab --- /dev/null +++ b/unittests/EnchantTestFixture.h @@ -0,0 +1,450 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#ifndef __ENCHANTTESTFIXTURE +#define __ENCHANTTESTFIXTURE + +#if defined(_MSC_VER) +#pragma once +#endif + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif +#include +#include +#include +#include + +struct EnchantTestFixture +{ + std::string savedRegistryHomeDir; + std::string savedUserRegistryModuleDir; + std::string savedMachineRegistryModuleDir; + std::string savedUserRegistryConfigDir; + std::string savedMachineRegistryConfigDir; + + //Setup + EnchantTestFixture() + { + CleanUpFiles(); //just in case we stopped the process in the middle. + MoveEnchantUserFilesOutOfTheWay(); + +#ifdef _WIN32 + savedRegistryHomeDir = GetRegistryHomeDir(); + ClearRegistryHomeDir(); + + savedUserRegistryModuleDir = GetUserRegistryModuleDir(); + ClearUserRegistryModuleDir(); + + savedMachineRegistryModuleDir = GetMachineRegistryModuleDir(); + ClearMachineRegistryModuleDir(); + + savedUserRegistryConfigDir = GetUserRegistryConfigDir(); + ClearUserRegistryConfigDir(); + + savedMachineRegistryConfigDir = GetMachineRegistryConfigDir(); + ClearMachineRegistryConfigDir(); +#endif + } + + //Teardown + ~EnchantTestFixture(){ +#ifdef _WIN32 + if(savedRegistryHomeDir.empty()) { + ClearRegistryHomeDir(); + } + else { + SetRegistryHomeDir(savedRegistryHomeDir); + } + + if(savedUserRegistryModuleDir.empty()) { + ClearUserRegistryModuleDir(); + } + else{ + SetUserRegistryModuleDir(savedUserRegistryModuleDir); + } + + if(savedMachineRegistryModuleDir.empty()) + { + ClearMachineRegistryModuleDir(); + } + else{ + SetMachineRegistryModuleDir(savedMachineRegistryModuleDir); + } + + if(savedUserRegistryConfigDir.empty()) + { + ClearUserRegistryConfigDir(); + } + else{ + SetUserRegistryConfigDir(savedUserRegistryConfigDir); + } + + if(savedMachineRegistryConfigDir.empty()) { + ClearMachineRegistryConfigDir(); + } + else { + SetMachineRegistryConfigDir(savedMachineRegistryConfigDir); + } + +#endif + RestoreEnchantUserFiles(); + CleanUpFiles(); + } + void CleanUpFiles() + { + //clean up personal dictionaries from home dir + DeleteDirAndFiles(GetTempUserEnchantDir()); + DeleteDirAndFiles(AddToPath(GetDirectoryOfThisModule(), "lib")); + DeleteDirAndFiles(AddToPath(GetDirectoryOfThisModule(), "share")); + } + + std::string GetTempUserEnchantDir() + { + return GetEnchantHomeDirFromBase(GetDirectoryOfThisModule()); + } + + void MoveEnchantUserFilesOutOfTheWay() + { + GetFilesOutOfTheWay(GetEnchantHomeDirFromBase(g_get_user_config_dir())); + GetFilesOutOfTheWay(GetEnchantHomeDirFromBase(g_get_home_dir())); + } + + void RestoreEnchantUserFiles() + { + RestoreFiles(GetEnchantHomeDirFromBase(g_get_user_config_dir())); + RestoreFiles(GetEnchantHomeDirFromBase(g_get_home_dir())); + } + +#define OUT_OF_THE_WAY ".real"; + + void GetFilesOutOfTheWay(const std::string& dir) + { + std::string toTheSideDir = dir + OUT_OF_THE_WAY; + + if(DirExists(dir) && !DirExists(toTheSideDir)) + { + MoveDir(dir, toTheSideDir); + } + + DeleteDirAndFiles(dir); + } + + void RestoreFiles(const std::string& dir) + { + std::string toTheSideDir = dir + OUT_OF_THE_WAY; + if(DirExists(toTheSideDir)) + { + DeleteDirAndFiles(dir); + MoveDir(toTheSideDir, dir); + } + } + + std::string GetEnchantConfigDir() + { + return AddToPath(AddToPath(GetDirectoryOfThisModule(), "share"), "enchant"); + } + + static bool DirExists(const std::string& dir) + { + return g_file_test(dir.c_str(), G_FILE_TEST_IS_DIR) != 0; + } + + static void MoveDir(const std::string& from, const std::string& to) + { + int result = g_rename(from.c_str(), to.c_str()); + if(result!= 0) + { + perror("failed (will retry)"); + // try once more. + result = g_rename(from.c_str(), to.c_str()); + } + assert(result == 0); + if(result!= 0) + { + perror("failed (giving up)"); + } + } + + static void DeleteDirAndFiles(const std::string& dir) + { + GDir* gdir = g_dir_open(dir.c_str(), 0, NULL); + if(gdir != NULL) + { + const gchar* filename; + for(;;){ + filename = g_dir_read_name(gdir); + if(filename == NULL) + { + break; + } + std::string filepath = AddToPath(dir, filename); + if(g_file_test(filepath.c_str(), G_FILE_TEST_IS_DIR)){ + DeleteDirAndFiles(filepath); + } + else { + DeleteFile(filepath); + } + } + g_dir_close(gdir); + } + g_rmdir(dir.c_str()); + } + + static std::string GetTemporaryFilename(const std::string & prefix){ + char* tempFileName = tempnam(".", prefix.c_str()); + std::string temp(tempFileName); + free(tempFileName); + return temp; + } + + static void CreateDirectory(const std::string& filepath) + { + g_mkdir_with_parents(filepath.c_str(), S_IREAD | S_IWRITE | S_IEXEC); + } + static void CreateFile(const std::string& filepath) + { + int fh = g_creat(filepath.c_str(), _S_IREAD | _S_IWRITE); + if(fh != -1) { + close(fh); + } + } + static void DeleteFile(const std::string& filepath) + { + if(FileExists(filepath)){ + g_remove(filepath.c_str()); + } + } + static bool FileExists(const std::string& filepath) + { + return(g_access(filepath.c_str(), 0)==0); + } + + std::string Convert(const std::wstring & ws) + { + gchar* str = g_utf16_to_utf8((gunichar2*)ws.c_str(), (glong)ws.length(), NULL, NULL, NULL); + std::string s(str); + g_free(str); + return s; + } + + std::wstring Convert(const std::string & s) + { + gunichar2* str = g_utf8_to_utf16(s.c_str(), (glong)s.length(), NULL, NULL, NULL); + std::wstring ws((wchar_t*)str); + g_free(str); + return ws; + } + + std::string GetDirectoryOfThisModule() + { + std::string result; +#if defined(_WIN32) + // should be in the same directory as our test fixture + WCHAR szFilename[MAX_PATH]; + GetModuleFileName(NULL, (LPWSTR) &szFilename, sizeof(szFilename)); + PathRemoveFileSpec((LPWSTR)&szFilename); + + result = Convert(szFilename); +#elif defined(ENABLE_BINRELOC) + gchar* prefix = gbr_find_prefix(NULL); + result = std::string(prefix); + g_free(prefix); +#endif + return result; + } + + std::string GetRegistryHomeDir() + { + return Convert(GetRegistryValue(HKEY_CURRENT_USER, L"Software\\Enchant\\Config", L"Home_Dir")); + } + + void SetRegistryHomeDir(const std::string & dir) + { + SetRegistryValue(HKEY_CURRENT_USER, L"Software\\Enchant\\Config", L"Home_Dir", dir); + } + + void ClearRegistryHomeDir() + { + ClearRegistryValue(HKEY_CURRENT_USER, L"Software\\Enchant\\Config", L"Home_Dir"); + } + + std::string GetUserRegistryModuleDir() + { + return Convert(GetRegistryValue(HKEY_CURRENT_USER, L"Software\\Enchant\\Config", L"Module_Dir")); + + } + + void SetUserRegistryModuleDir(const std::string & dir) + { + SetRegistryValue(HKEY_CURRENT_USER, L"Software\\Enchant\\Config", L"Module_Dir", dir); + } + + void ClearUserRegistryModuleDir() + { + ClearRegistryValue(HKEY_CURRENT_USER, L"Software\\Enchant\\Config", L"Module_Dir"); + } + + std::string GetMachineRegistryModuleDir() + { + return Convert(GetRegistryValue(HKEY_LOCAL_MACHINE, L"Software\\Enchant\\Config", L"Module_Dir")); + + } + + void SetMachineRegistryModuleDir(const std::string & dir) + { + SetRegistryValue(HKEY_LOCAL_MACHINE, L"Software\\Enchant\\Config", L"Module_Dir", dir); + } + + void ClearMachineRegistryModuleDir() + { + ClearRegistryValue(HKEY_LOCAL_MACHINE, L"Software\\Enchant\\Config", L"Module_Dir"); + } + + std::string GetUserRegistryConfigDir() + { + return Convert(GetRegistryValue(HKEY_CURRENT_USER, L"Software\\Enchant\\Config", L"Data_Dir")); + } + + void SetUserRegistryConfigDir(const std::string & dir) + { + SetRegistryValue(HKEY_CURRENT_USER, L"Software\\Enchant\\Config", L"Data_Dir", dir); + } + + void ClearUserRegistryConfigDir() + { + ClearRegistryValue(HKEY_CURRENT_USER, L"Software\\Enchant\\Config", L"Data_Dir"); + } + + std::string GetMachineRegistryConfigDir() + { + return Convert(GetRegistryValue(HKEY_LOCAL_MACHINE, L"Software\\Enchant\\Config", L"Data_Dir")); + } + + void SetMachineRegistryConfigDir(const std::string & dir) + { + SetRegistryValue(HKEY_LOCAL_MACHINE, L"Software\\Enchant\\Config", L"Data_Dir", dir); + } + + void ClearMachineRegistryConfigDir() + { + ClearRegistryValue(HKEY_LOCAL_MACHINE, L"Software\\Enchant\\Config", L"Data_Dir"); + } + + std::wstring GetRegistryValue(HKEY baseKey, const std::wstring& key, const std::wstring& valueName) + { + std::wstring result; + + WCHAR data[2048]; + DWORD dwSize = sizeof(data) * sizeof(WCHAR); + HKEY hkey; + + if(RegOpenKeyEx(baseKey, + key.c_str(), + 0, KEY_WRITE, &hkey) == ERROR_SUCCESS) + { + if(RegQueryValueEx(hkey, + valueName.c_str(), + 0, + NULL, + (LPBYTE)&data, &dwSize) + == ERROR_SUCCESS){ + result = std::wstring(data,dwSize); + } + } + return result; + } + + void SetRegistryValue(HKEY baseKey, + const std::wstring& key, + const std::wstring& valueName, + const std::string& value) + { + SetRegistryValue(baseKey, key, valueName, Convert(value)); + } + void SetRegistryValue(HKEY baseKey, + const std::wstring& key, + const std::wstring& valueName, + const std::wstring& value) + { + HKEY hkey; + if(RegCreateKeyEx(baseKey, + key.c_str(), + 0, NULL, + REG_OPTION_NON_VOLATILE, + KEY_WRITE, + NULL, &hkey, + NULL) == ERROR_SUCCESS) + { + RegSetValueEx(hkey, + valueName.c_str(), + 0, + REG_SZ, + (LPBYTE)value.c_str(), + (DWORD)((value.length()+1)*sizeof(wchar_t)) ); + + RegCloseKey(hkey); + } + } + + void ClearRegistryValue(HKEY baseKey, + const std::wstring& key, + const std::wstring& valueName) + { + HKEY hkey; + if(RegOpenKeyEx(baseKey, + key.c_str(), + 0, KEY_WRITE, &hkey) == ERROR_SUCCESS) + { + RegDeleteValue(hkey, valueName.c_str()); + RegCloseKey(hkey); + } + } + + + static std::string AddToPath(const std::string & path, const std::string & fileOrDirName) + { + std::string result; + gchar* filename = g_build_filename(path.c_str(), fileOrDirName.c_str(), NULL); + result = std::string(filename); + g_free(filename); + + return result; + } + + static std::string GetEnchantHomeDirFromBase(const std::string& basePath) + { +#ifdef XP_TARGET_COCOA + return AddToPath(AddToPath(AddToPath(basePath,"Library"), + "Application Support"), + "Enchant"); +#elif defined(_WIN32) + return AddToPath(basePath,"enchant"); +#else + return AddToPath(basePath,".enchant"); +#endif + } +}; + +#endif diff --git a/unittests/Readme.txt b/unittests/Readme.txt new file mode 100644 index 0000000..e3a6319 --- /dev/null +++ b/unittests/Readme.txt @@ -0,0 +1,3 @@ +Enchant Unit Tests are written using the UnitTest++ framework. + +See http://unittest-cpp.sourceforge.net/ for more details. diff --git a/unittests/broker/enchant_broker_describe_tests.cpp b/unittests/broker/enchant_broker_describe_tests.cpp new file mode 100644 index 0000000..95114b2 --- /dev/null +++ b/unittests/broker/enchant_broker_describe_tests.cpp @@ -0,0 +1,328 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include +#include +#include +#include "../EnchantBrokerTestFixture.h" + +struct ProviderDescription +{ + std::string Name; + std::string Description; + std::string DllFile; + ProviderDescription(const char * const provider_name, + const char * const provider_desc, + const char * const provider_dll_file): + Name(provider_name), + Description(provider_desc), + DllFile(provider_dll_file) + {} + + bool DataIsComplete() const + { + return (Name.length() && + Description.length() && + DllFile.length()); + } +}; + +void EnchantBrokerDescribeCallback (const char * const provider_name, + const char * const provider_desc, + const char * const provider_dll_file, + void * user_data) +{ + std::vector* providerList = reinterpret_cast*>(user_data); + + providerList->push_back(ProviderDescription(provider_name, provider_desc, provider_dll_file)); +} + +static void * global_user_data; +void EnchantBrokerDescribeAssignUserDataToStaticCallback (const char * const, + const char * const, + const char * const, + void * user_data) +{ + global_user_data = user_data; +} + +struct EnchantBrokerNoProvidersTestFixture : EnchantTestFixture +{ + //Setup + EnchantBrokerNoProvidersTestFixture() + { + _broker = enchant_broker_init (); + } + + //Teardown + ~EnchantBrokerNoProvidersTestFixture() + { + enchant_broker_free (_broker); + } + + EnchantBroker* _broker; + std::vector _providerList; +}; + +static const char * +MockProvider2Identify (EnchantProvider *) +{ + return "mock2"; +} + +static void Provider2Configuration (EnchantProvider * me, const char *) +{ + me->identify = MockProvider2Identify; + me->describe = MockProviderDescribe; +} + +struct EnchantBrokerDescribeTestFixtureBase : EnchantBrokerTestFixture +{ + std::vector _providerList; + + //Setup + EnchantBrokerDescribeTestFixtureBase(ConfigureHook userConfiguration): + EnchantBrokerTestFixture(userConfiguration, Provider2Configuration, true) + { } +}; + +static void List_Providers_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->identify = MockProviderIdentify; + me->describe = MockProviderDescribe; +} + +struct EnchantBrokerDescribeTestFixture : EnchantBrokerDescribeTestFixtureBase +{ + //Setup + EnchantBrokerDescribeTestFixture(): + EnchantBrokerDescribeTestFixtureBase(List_Providers_ProviderConfiguration) + { + global_user_data = NULL; + } +}; + + +static void List_Providers_ProviderConfigurationNoIdentify (EnchantProvider * me, const char *) +{ + me->identify = NULL; +} + +struct EnchantBrokerDescribe_ProviderLacksIdentify_TestFixture : EnchantBrokerDescribeTestFixtureBase +{ + //Setup + EnchantBrokerDescribe_ProviderLacksIdentify_TestFixture(): + EnchantBrokerDescribeTestFixtureBase(List_Providers_ProviderConfigurationNoIdentify) + { + global_user_data = NULL; + } +}; + +static void List_Providers_ProviderConfigurationNoDescribe (EnchantProvider * me, const char *) +{ + me->describe = NULL; +} + +struct EnchantBrokerDescribe_ProviderLacksDescribe_TestFixture : EnchantBrokerDescribeTestFixtureBase +{ + //Setup + EnchantBrokerDescribe_ProviderLacksDescribe_TestFixture(): + EnchantBrokerDescribeTestFixtureBase(List_Providers_ProviderConfigurationNoDescribe) + { + global_user_data = NULL; + } +}; + +static const char * +MockProviderIllegalUtf8 (EnchantProvider *) +{ + return "\xa5\xf1\x08"; +} + +static void List_Providers_ProviderConfigurationInvalidIdentify (EnchantProvider * me, const char *) +{ + me->identify = MockProviderIllegalUtf8; +} + +struct EnchantBrokerDescribe_ProviderHasInvalidUtf8Identify_TestFixture : EnchantBrokerDescribeTestFixtureBase +{ + //Setup + EnchantBrokerDescribe_ProviderHasInvalidUtf8Identify_TestFixture(): + EnchantBrokerDescribeTestFixtureBase(List_Providers_ProviderConfigurationInvalidIdentify) + { + global_user_data = NULL; + } +}; + +static void List_Providers_ProviderConfigurationInvalidDescribe (EnchantProvider * me, const char *) +{ + me->describe = MockProviderIllegalUtf8; +} + +struct EnchantBrokerDescribe_ProviderHasInvalidUtf8Describe_TestFixture : EnchantBrokerDescribeTestFixtureBase +{ + //Setup + EnchantBrokerDescribe_ProviderHasInvalidUtf8Describe_TestFixture(): + EnchantBrokerDescribeTestFixtureBase(List_Providers_ProviderConfigurationInvalidDescribe) + { + global_user_data = NULL; + } +}; + +/** + * enchant_broker_describe + * @broker: A non-null #EnchantBroker + * @fn: A non-null #EnchantBrokerDescribeFn + * @user_data: Optional user-data + * + * Enumerates the Enchant providers and tells + * you some rudimentary information about them. + */ + + +/* + * Providers are discovered by probing first in the .enchant directory + * in the user's home directory. + * [on windows in the enchant directory in the user's Application Data + * directory] + * + * The user's provider directory on windows can be overridden using the registry + * setting HKEY_CURRENT_USER\Software\Enchant\Config\Data_Dir + * + * Then from the module directory (that libenchant is in). + * + * The module directory can be overridden using the registry setting + * HKEY_CURRENT_USER\Software\Enchant\Config\Module_Dir + * or HKEY_LOCAL_MACHINE\Software\Enchant\Config\Module_Dir + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +TEST_FIXTURE(EnchantBrokerNoProvidersTestFixture, + EnchantBrokerDescribe_NoProviders) +{ + enchant_broker_describe(_broker, EnchantBrokerDescribeCallback, &_providerList); + CHECK_EQUAL((unsigned int)0, _providerList.size()); +} + +TEST_FIXTURE(EnchantBrokerDescribeTestFixture, + EnchantBrokerDescribe_ProvidersInUserDirectoryTakePrecedenceOverProvidersInSystem) +{ + enchant_broker_describe(_broker, EnchantBrokerDescribeCallback, &_providerList); + CHECK_EQUAL((unsigned int)2, _providerList.size()); + CHECK_EQUAL(2, std::count_if(_providerList.begin(), + _providerList.end(), + std::mem_fun_ref(&ProviderDescription::DataIsComplete))); +} + +TEST_FIXTURE(EnchantBrokerDescribeTestFixture, + EnchantBrokerDescribe_NullUserData_PassedThroughToCallback) +{ + global_user_data = "hello world"; + + enchant_broker_describe(_broker, + EnchantBrokerDescribeAssignUserDataToStaticCallback, + NULL); + CHECK_EQUAL((void*)NULL, global_user_data); +} + +TEST_FIXTURE(EnchantBrokerDescribeTestFixture, + EnchantBrokerDescribe_NonNullUserData_PassedThroughToCallback) +{ + char* data = "some data"; + global_user_data = NULL; + enchant_broker_describe(_broker, + EnchantBrokerDescribeAssignUserDataToStaticCallback, + data); + CHECK(global_user_data); + + if(global_user_data){ + CHECK_EQUAL(data, (char*)global_user_data); + } +} + +TEST_FIXTURE(EnchantBrokerDescribeTestFixture, + EnchantBrokerDescribe_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockProvider("something bad happened"); + + enchant_broker_describe(_broker, EnchantBrokerDescribeCallback, &_providerList); + + CHECK_EQUAL((void*)NULL, (void*)enchant_broker_get_error(_broker)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantBrokerDescribeTestFixture, + EnchantBrokerDescribe_NullBroker_DoNothing) +{ + enchant_broker_describe(NULL, EnchantBrokerDescribeCallback, &_providerList); + CHECK_EQUAL((void*)NULL, global_user_data); +} + +TEST_FIXTURE(EnchantBrokerDescribeTestFixture, + EnchantBrokerDescribe_NullDescribeFunction_DoNothing) +{ + enchant_broker_describe(_broker, NULL, &_providerList); + CHECK_EQUAL((void*)NULL, global_user_data); +} + +TEST_FIXTURE(EnchantBrokerDescribe_ProviderLacksIdentify_TestFixture, + EnchantBrokerDescribe_ProviderLacksIdentify_NotLoaded) +{ + enchant_broker_describe(_broker, EnchantBrokerDescribeCallback, &_providerList); + CHECK_EQUAL((unsigned int)1, _providerList.size()); + CHECK_EQUAL(1, std::count_if(_providerList.begin(), + _providerList.end(), + std::mem_fun_ref(&ProviderDescription::DataIsComplete))); +} + +TEST_FIXTURE(EnchantBrokerDescribe_ProviderLacksDescribe_TestFixture, + EnchantBrokerDescribe_ProviderLacksDescribe_NotLoaded) +{ + enchant_broker_describe(_broker, EnchantBrokerDescribeCallback, &_providerList); + CHECK_EQUAL((unsigned int)1, _providerList.size()); + CHECK_EQUAL(1, std::count_if(_providerList.begin(), + _providerList.end(), + std::mem_fun_ref(&ProviderDescription::DataIsComplete))); +} + +TEST_FIXTURE(EnchantBrokerDescribe_ProviderHasInvalidUtf8Describe_TestFixture, + EnchantBrokerDescribe_ProviderHasInvalidUtf8Describe_NotLoaded) +{ + enchant_broker_describe(_broker, EnchantBrokerDescribeCallback, &_providerList); + CHECK_EQUAL((unsigned int)1, _providerList.size()); + CHECK_EQUAL(1, std::count_if(_providerList.begin(), + _providerList.end(), + std::mem_fun_ref(&ProviderDescription::DataIsComplete))); +} + +TEST_FIXTURE(EnchantBrokerDescribe_ProviderHasInvalidUtf8Identify_TestFixture, + EnchantBrokerDescribe_ProviderHasInvalidUtf8Identify_NotLoaded) +{ + enchant_broker_describe(_broker, EnchantBrokerDescribeCallback, &_providerList); + CHECK_EQUAL((unsigned int)1, _providerList.size()); + CHECK_EQUAL(1, std::count_if(_providerList.begin(), + _providerList.end(), + std::mem_fun_ref(&ProviderDescription::DataIsComplete))); +} diff --git a/unittests/broker/enchant_broker_dict_exists_tests.cpp b/unittests/broker/enchant_broker_dict_exists_tests.cpp new file mode 100644 index 0000000..819bb12 --- /dev/null +++ b/unittests/broker/enchant_broker_dict_exists_tests.cpp @@ -0,0 +1,148 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantBrokerTestFixture.h" + +static int dictionaryExistsCalled; +static int DoesDictionaryExist (EnchantProvider * me, const char *const tag) +{ + dictionaryExistsCalled++; + return MockEnGbAndQaaProviderDictionaryExists(me, tag); +} + +static void Dictionary_Exists_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->dictionary_exists=DoesDictionaryExist; +} + +struct EnchantBrokerDictExists_ProviderImplementsDictionaryExist_TestFixture : EnchantBrokerTestFixture{ + //Setup + EnchantBrokerDictExists_ProviderImplementsDictionaryExist_TestFixture(): + EnchantBrokerTestFixture(Dictionary_Exists_ProviderConfiguration) + { dictionaryExistsCalled=0; } +}; + +#define EnchantBrokerDictExistsTestFixture EnchantBrokerDictExists_ProviderImplementsDictionaryExist_TestFixture +#define DictionaryExistsMethodCalled dictionaryExistsCalled +#include "enchant_broker_dict_exists_tests.i" + +static int listDictionariesCalled; +static char** ListDictionaries (EnchantProvider * me, size_t * out_n_dicts) +{ + listDictionariesCalled++; + + return MockEnGbAndQaaProviderListDictionaries(me, out_n_dicts); +} + +static void List_Dictionaries_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->list_dicts=ListDictionaries; + me->free_string_list = MockProviderFreeStringList; +} + +struct EnchantBrokerDictExists_ProviderImplementsListDictionaries_TestFixture : EnchantBrokerTestFixture +{ + //Setup + EnchantBrokerDictExists_ProviderImplementsListDictionaries_TestFixture(): + EnchantBrokerTestFixture(List_Dictionaries_ProviderConfiguration) + { + listDictionariesCalled = 0; + } + +}; + +#undef EnchantBrokerDictExistsTestFixture +#define EnchantBrokerDictExistsTestFixture EnchantBrokerDictExists_ProviderImplementsListDictionaries_TestFixture +#undef DictionaryExistsMethodCalled +#define DictionaryExistsMethodCalled listDictionariesCalled +#include "enchant_broker_dict_exists_tests.i" + +static int requestDictionaryCalled; +static EnchantDict * RequestDictionary (EnchantProvider *me, const char *tag) +{ + requestDictionaryCalled++; + return MockEnGbAndQaaProviderRequestDictionary(me, tag); +} + +static void Request_Dictionary_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = RequestDictionary; + me->dispose_dict = MockProviderDisposeDictionary; +} + +struct EnchantBrokerDictExists_ProviderImplementsRequestDictionary_TestFixture : EnchantBrokerTestFixture +{ + //Setup + EnchantBrokerDictExists_ProviderImplementsRequestDictionary_TestFixture(): + EnchantBrokerTestFixture(Request_Dictionary_ProviderConfiguration) + { + requestDictionaryCalled = 0; + } +}; + +#undef EnchantBrokerDictExistsTestFixture +#define EnchantBrokerDictExistsTestFixture EnchantBrokerDictExists_ProviderImplementsRequestDictionary_TestFixture +#undef DictionaryExistsMethodCalled +#define DictionaryExistsMethodCalled requestDictionaryCalled +#include "enchant_broker_dict_exists_tests.i" + +static void ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = RequestDictionary; + me->dispose_dict = MockProviderDisposeDictionary; + me->list_dicts = ListDictionaries; + me->free_string_list = MockProviderFreeStringList; + me->dictionary_exists = DoesDictionaryExist; +} + +struct EnchantBrokerDictExists_ProviderImplementsAll_TestFixture : EnchantBrokerTestFixture +{ + //Setup + EnchantBrokerDictExists_ProviderImplementsAll_TestFixture(): + EnchantBrokerTestFixture(ProviderConfiguration) + { + listDictionariesCalled = 0; + requestDictionaryCalled = 0; + dictionaryExistsCalled = 0; + } +}; + +TEST_FIXTURE(EnchantBrokerDictExists_ProviderImplementsAll_TestFixture, + EnchantBrokerDictExists_CalledWhenDictionaryIsInUse_DoesNotCallAnyMethods_GetsCachedResult) +{ + EnchantDict* dict = enchant_broker_request_dict(_broker, "en-GB"); + requestDictionaryCalled = 0; + + enchant_broker_dict_exists(_broker, "en-GB"); + CHECK_EQUAL(0,listDictionariesCalled); + CHECK_EQUAL(0,requestDictionaryCalled); + CHECK_EQUAL(0,dictionaryExistsCalled); + + enchant_broker_free_dict(_broker, dict); +} + +TEST_FIXTURE(EnchantBrokerTestFixture, + EnchantBrokerDictExists_ProviderImplementsNoMethods_0) +{ + CHECK_EQUAL(0, enchant_broker_dict_exists(_broker, "en_GB")); +} diff --git a/unittests/broker/enchant_broker_dict_exists_tests.i b/unittests/broker/enchant_broker_dict_exists_tests.i new file mode 100644 index 0000000..326197f --- /dev/null +++ b/unittests/broker/enchant_broker_dict_exists_tests.i @@ -0,0 +1,150 @@ +#ifndef EnchantBrokerDictExistsTestFixture +#error EnchantBrokerDictExistsTestFixture must be defined as the testfixture class to run these tests against +#endif + +#ifndef DictionaryExistsMethodCalled +#error DictionaryExistsMethodCalled must be defined as the variable that is set when the method is called +#endif + +/** + * enchant_broker_dict_exists + * @broker: A non-null #EnchantBroker + * @tag: The non-null language tag you wish to request a dictionary for ("en_US", "de_DE", ...) + * + * language tags are normalized by stripping leading and trailing whitespace, + * removing '@' and any trailing characters, removing '.' and any trailing characters, + * and replacing '-' with '_' + * + * If no providers have the given language tag, enchant will fallback to the language code + * prefix (everything before the first "_") + * + * Return existance of the requested dictionary (1 == true, 0 == false) + */ + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_DictDoesNotExist_FalseOnlyAsksOnce) +{ + CHECK_EQUAL(0, enchant_broker_dict_exists(_broker, "en")); + CHECK_EQUAL(1,DictionaryExistsMethodCalled); +} + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_DictExists_TrueAsksOnce) +{ + CHECK_EQUAL(1, enchant_broker_dict_exists(_broker, "en_GB")); + CHECK_EQUAL(1,DictionaryExistsMethodCalled); +} + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_BaseDictExists_TrueAsksTwice) +{ + CHECK_EQUAL(1, enchant_broker_dict_exists(_broker, "qaa_CA")); + CHECK_EQUAL(2,DictionaryExistsMethodCalled); +} + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_ExtraStuffAfterSecondUnderscore_NotStrippedForComparison) +{ + CHECK_EQUAL(0, enchant_broker_dict_exists(_broker, "en_GB_special")); +} + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_WhitespaceSurroundingLanguageTag_Removed) +{ + CHECK_EQUAL(1, enchant_broker_dict_exists(_broker, "\n\r en_GB \t\f")); +} + +/* Vertical tab is not considered to be whitespace in glib! + See bug# 59388 http://bugzilla.gnome.org/show_bug.cgi?id=59388 +*/ +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_VerticalTabSurroundingLanguageTag_NotRemoved) +{ + CHECK_EQUAL(0, enchant_broker_dict_exists(_broker, "\ven_GB")); + CHECK_EQUAL(0, enchant_broker_dict_exists(_broker, "en_GB\v")); +} + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_AtSignInLanguageTag_RemovesToTail) +{ + CHECK_EQUAL(1, enchant_broker_dict_exists(_broker, "en_GB@euro")); +} + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_NothingLeftAfterNormalizingLanguageTag_DoesNotCallProvider) +{ + CHECK_EQUAL(0, enchant_broker_dict_exists(_broker, "@euro")); + CHECK_EQUAL(0,DictionaryExistsMethodCalled); +} + + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_PeriodInLanguageTag_RemovesToTail) +{ + CHECK_EQUAL(1, enchant_broker_dict_exists(_broker, "en_GB.UTF-8")); +} + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_HyphensInLanguageTag_SubstitutedWithUnderscore) +{ + CHECK_EQUAL(1, enchant_broker_dict_exists(_broker, "en-GB")); +} + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_CalledWhenDictionaryIsNotInUse_MakesCall) +{ + DictionaryExistsMethodCalled = false; + enchant_broker_dict_exists(_broker, "en-GB"); + CHECK_EQUAL(1,DictionaryExistsMethodCalled); +} + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_DifferentCase_Finds) +{ + CHECK_EQUAL(1, enchant_broker_dict_exists(_broker, "EN_gb")); +} + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_DifferentCase_NoRegion_Finds) +{ + CHECK_EQUAL(1, enchant_broker_dict_exists(_broker, "QAA")); +} + + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockProvider("something bad happened"); + + enchant_broker_dict_exists(_broker, "en_US"); + + CHECK_EQUAL((void*)NULL, (void*)enchant_broker_get_error(_broker)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_NullBroker_0) +{ + CHECK_EQUAL(0, enchant_broker_dict_exists (NULL, "en_US")); +} + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_NullTag_0) +{ + CHECK_EQUAL(0, enchant_broker_dict_exists (_broker, NULL)); +} + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_EmptyTag_0) +{ + CHECK_EQUAL(0, enchant_broker_dict_exists (_broker, "")); +} + +TEST_FIXTURE(EnchantBrokerDictExistsTestFixture, + EnchantBrokerDictExists_InvalidTag_0_ErrorSet) +{ + CHECK_EQUAL(0, enchant_broker_dict_exists (_broker, "en~US")); + CHECK(NULL != enchant_broker_get_error(_broker)); +} diff --git a/unittests/broker/enchant_broker_free_dict_tests.cpp b/unittests/broker/enchant_broker_free_dict_tests.cpp new file mode 100644 index 0000000..78eeb09 --- /dev/null +++ b/unittests/broker/enchant_broker_free_dict_tests.cpp @@ -0,0 +1,143 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantBrokerTestFixture.h" +/** + * enchant_broker_free_dict + * @broker: A non-null #EnchantBroker + * @dict: A non-null #EnchantDict + * + * Releases the dictionary when you are done using it + * + * Must only be called once per dictionary request + */ + +EnchantDict* dictionaryToBeFreed=NULL; +static void +DisposeDictionary (EnchantProvider *me, EnchantDict * dict) +{ + dictionaryToBeFreed = dict; + MockProviderDisposeDictionary(me, dict); +} + +static void ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = MockEnGbAndQaaProviderRequestDictionary; + me->dispose_dict = DisposeDictionary; +} + +struct EnchantBrokerFreeDictTestFixture: EnchantBrokerTestFixture +{ + //Setup + EnchantBrokerFreeDictTestFixture():EnchantBrokerTestFixture(ProviderConfiguration) + { + _dictionary = RequestDictionary("en-GB"); + dictionaryToBeFreed=NULL; + } + + //Teardown + ~EnchantBrokerFreeDictTestFixture() + { + FreeDictionary(_dictionary); + } + + EnchantDict* _dictionary; +}; + +static void NoDisposeProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = MockEnGbAndQaaProviderRequestDictionary; +} + +struct EnchantBrokerFreeDictNoDisposeTestFixture: EnchantBrokerTestFixture +{ + //Setup + EnchantBrokerFreeDictNoDisposeTestFixture():EnchantBrokerTestFixture(NoDisposeProviderConfiguration) + { + _dictionary = enchant_broker_request_dict(_broker, "en-GB"); + dictionaryToBeFreed=NULL; + } + + //Teardown + ~EnchantBrokerFreeDictNoDisposeTestFixture(){ + FreeDictionary(_dictionary); + } + + EnchantDict* _dictionary; +}; + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST_FIXTURE(EnchantBrokerFreeDictTestFixture, + EnchantBrokerFreeDict) +{ + enchant_broker_free_dict(_broker, _dictionary); + CHECK_EQUAL(_dictionary, dictionaryToBeFreed); + _dictionary = NULL; +} + +TEST_FIXTURE(EnchantBrokerFreeDictTestFixture, + EnchantBrokerFreeDict_HasPreviousError_ErrorCleared) +{ + + SetErrorOnMockProvider("something bad happened"); + + enchant_broker_free_dict(_broker, _dictionary); + _dictionary = NULL; + + CHECK_EQUAL((void*)NULL, (void*)enchant_broker_get_error(_broker)); +} + +TEST_FIXTURE(EnchantBrokerFreeDictTestFixture, + EnchantBrokerFreeDict_HasTwoInstances_NoCrash) +{ + EnchantDict* dictionary1 = enchant_broker_request_dict(_broker, "en-GB"); + EnchantDict* dictionary2 = enchant_broker_request_dict(_broker, "en-GB"); + enchant_broker_free_dict(_broker, dictionary1); + enchant_broker_free_dict(_broker, dictionary2); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantBrokerFreeDictTestFixture, + EnchantBrokerFreeDict_NullBroker_DoNothing) +{ + enchant_broker_free_dict(NULL, _dictionary); + CHECK_EQUAL((EnchantDict*)NULL, dictionaryToBeFreed); +} + +TEST_FIXTURE(EnchantBrokerFreeDictTestFixture, + EnchantBrokerFreeDict_NullDict_DoNothing) +{ + enchant_broker_free_dict(_broker, NULL); + CHECK_EQUAL((EnchantDict*)NULL, dictionaryToBeFreed); +} + +TEST_FIXTURE(EnchantBrokerFreeDictNoDisposeTestFixture, + EnchantBrokerFreeDict_NotOnProvider_DoNothing) +{ + testResults_; + enchant_broker_free_dict(_broker, _dictionary); + _dictionary = NULL; +} diff --git a/unittests/broker/enchant_broker_free_tests.cpp b/unittests/broker/enchant_broker_free_tests.cpp new file mode 100644 index 0000000..eec9257 --- /dev/null +++ b/unittests/broker/enchant_broker_free_tests.cpp @@ -0,0 +1,128 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantBrokerTestFixture.h" + +/** + * enchant_broker_free + * @broker: A non-null #EnchantBroker + * + * Destroys the broker object + * + * Free must only be called once! + */ + +bool disposeWasCalled; +static void +Dispose (EnchantProvider *me) +{ + disposeWasCalled = true; + MockProviderDispose(me); +} + +bool disposeDictionaryCalled; +static void +DisposeDictionary (EnchantProvider *me, EnchantDict * dict) +{ + disposeDictionaryCalled = true; + MockProviderDisposeDictionary(me, dict); +} + +static void ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->dispose = Dispose; + me->request_dict = MockEnGbAndQaaProviderRequestDictionary; + me->dispose_dict = DisposeDictionary; +} + +struct EnchantBrokerFreeTestFixture: EnchantBrokerTestFixture +{ + //Setup + EnchantBrokerFreeTestFixture():EnchantBrokerTestFixture(ProviderConfiguration) + { + disposeWasCalled = false; + disposeDictionaryCalled = false; + } +}; + + +static void NoDisposeProviderConfiguration (EnchantProvider * me, const char *) +{ + me->dispose = NULL; +} + +struct EnchantBrokerFreeNoDisposeTestFixture: EnchantBrokerTestFixture +{ + //Setup + EnchantBrokerFreeNoDisposeTestFixture():EnchantBrokerTestFixture(NoDisposeProviderConfiguration) + { } +}; + + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST(EnchantBrokerFree) +{ + testResults_; + EnchantBroker* broker = enchant_broker_init (); + enchant_broker_free(broker); + broker = NULL; +} + +TEST_FIXTURE(EnchantBrokerFreeTestFixture, + EnchantBrokerFree_DisposesProviders) +{ + enchant_broker_free(_broker); + CHECK(disposeWasCalled); + _broker = NULL; +} + +TEST_FIXTURE(EnchantBrokerFreeNoDisposeTestFixture, + EnchantBrokerFree_ProviderLacksDispose) +{ + testResults_; + enchant_broker_free(_broker); + _broker = NULL; +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantBrokerFreeTestFixture, + EnchantBrokerFree_NullBroker_DoNothing) +{ + enchant_broker_free(NULL); + CHECK(!disposeWasCalled); +} + +TEST_FIXTURE(EnchantBrokerFreeTestFixture, + EnchantBrokerFree_DictionaryNotFreed_FreesDictionary) +{ + EnchantDict* dict = enchant_broker_request_dict(_broker, "en_GB"); + CHECK(dict); + CHECK(!disposeDictionaryCalled); + + enchant_broker_free(_broker); + _broker = NULL; + CHECK(disposeDictionaryCalled); +} diff --git a/unittests/broker/enchant_broker_get_error_tests.cpp b/unittests/broker/enchant_broker_get_error_tests.cpp new file mode 100644 index 0000000..7c55cca --- /dev/null +++ b/unittests/broker/enchant_broker_get_error_tests.cpp @@ -0,0 +1,65 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantBrokerTestFixture.h" + +/** + * enchant_broker_get_error + * @broker: A non-null broker + * + * Returns a const char string or NULL describing the last exception. + * WARNING: error is transient and is likely cleared as soon as the + * next broker operation happens + */ + + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST_FIXTURE(EnchantBrokerTestFixture, + EnchantBrokerGetError_HasPreviousError_Error) +{ + std::string errorMessage("something bad happened"); + SetErrorOnMockProvider(errorMessage); + + char * result = enchant_broker_get_error(_broker); + CHECK(result); + if(result) + { + CHECK_EQUAL(errorMessage.c_str(), result); + } +} + +TEST_FIXTURE(EnchantBrokerTestFixture, + EnchantBrokerGetError_NoPreviousError_Null) +{ + CHECK_EQUAL((void*)NULL, (void*)enchant_broker_get_error(_broker)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions + +TEST(EnchantBrokerGetError_NullBroker_Null) +{ + CHECK_EQUAL((void*)NULL, (void*)enchant_broker_get_error(NULL)); +} \ No newline at end of file diff --git a/unittests/broker/enchant_broker_init_tests.cpp b/unittests/broker/enchant_broker_init_tests.cpp new file mode 100644 index 0000000..0da684e --- /dev/null +++ b/unittests/broker/enchant_broker_init_tests.cpp @@ -0,0 +1,40 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include + +/** + * enchant_broker_init + * + * Returns: A new broker object capable of requesting + * dictionaries from + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST(EnchantBrokerInit_CreatesObject) +{ + EnchantBroker *broker = enchant_broker_init(); + CHECK(broker); + enchant_broker_free(broker); +} diff --git a/unittests/broker/enchant_broker_list_dicts_tests.cpp b/unittests/broker/enchant_broker_list_dicts_tests.cpp new file mode 100644 index 0000000..c542a1c --- /dev/null +++ b/unittests/broker/enchant_broker_list_dicts_tests.cpp @@ -0,0 +1,268 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantBrokerTestFixture.h" +#include + + +void EnchantDictionaryDescribeCallback (const char * const lang_tag, + const char * const provider_name, + const char * const provider_desc, + const char * const provider_file, + void * user_data) +{ + std::vector* dictionaryList = reinterpret_cast*>(user_data); + dictionaryList->push_back(DictionaryDescription(lang_tag, provider_name, provider_desc, provider_file)); +} + +static void * global_user_data; +void EnchantDictionaryDescribeAssignUserDataToStaticCallback (const char * const, + const char * const, + const char * const, + const char * const, + void * user_data) +{ + global_user_data = user_data; +} + +static bool freeStringListCalled; +static void FreeStringList (EnchantProvider * me, char **str_list) +{ + freeStringListCalled = true; + + return MockProviderFreeStringList(me, str_list); +} + +static bool listDictionariesCalled; +static char** ListDictionaries (EnchantProvider * me, size_t * out_n_dicts) +{ + listDictionariesCalled = true; + + return MockEnGbProviderListDictionaries(me, out_n_dicts); +} + +struct EnchantBrokerListDictionaries_TestFixtureBase : EnchantBrokerTestFixture +{ + //Setup + EnchantBrokerListDictionaries_TestFixtureBase(ConfigureHook userConfiguration): + EnchantBrokerTestFixture(userConfiguration) + { + listDictionariesCalled=false; + freeStringListCalled=false; + global_user_data = NULL; + } + std::vector _dictionaryList; +}; + +static void List_Dictionaries_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->list_dicts=ListDictionaries; + me->free_string_list = FreeStringList; +} + + +struct EnchantBrokerListDictionaries_TestFixture : EnchantBrokerListDictionaries_TestFixtureBase +{ + //Setup + EnchantBrokerListDictionaries_TestFixture(): + EnchantBrokerListDictionaries_TestFixtureBase(List_Dictionaries_ProviderConfiguration) + { } +}; + +static void List_Dictionaries_ProviderConfigurationLacksFreeStringList (EnchantProvider * me, const char *) +{ + me->list_dicts=ListDictionaries; + me->free_string_list = NULL; +} + +struct EnchantBrokerListDictionaries_ProviderLacksFreeStringList_TestFixture : EnchantBrokerListDictionaries_TestFixtureBase +{ + //Setup + EnchantBrokerListDictionaries_ProviderLacksFreeStringList_TestFixture(): + EnchantBrokerListDictionaries_TestFixtureBase(List_Dictionaries_ProviderConfigurationLacksFreeStringList) + { } +}; + +static char ** +InvalidTagEnGbListDictionaries (EnchantProvider *, + size_t * out_n_dicts) +{ + *out_n_dicts = 2; + char** out_list = g_new0 (char *, *out_n_dicts + 1); + out_list[0] = g_strdup ("en_GB"); + out_list[1] = g_strdup ("en-GB"); + + return out_list; +} + +static void List_Dictionaries_InvalidTag_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->list_dicts=InvalidTagEnGbListDictionaries; + me->free_string_list = FreeStringList; +} + +struct EnchantBrokerListDictionaries_ProviderReturnsInvalidTag_TestFixture : EnchantBrokerListDictionaries_TestFixtureBase +{ + //Setup + EnchantBrokerListDictionaries_ProviderReturnsInvalidTag_TestFixture(): + EnchantBrokerListDictionaries_TestFixtureBase(List_Dictionaries_InvalidTag_ProviderConfiguration) + { } +}; + + +static char ** +DuplicateEnGbListDictionaries (EnchantProvider *, + size_t * out_n_dicts) +{ + *out_n_dicts = 2; + char** out_list = g_new0 (char *, *out_n_dicts + 1); + out_list[0] = g_strdup ("en_GB"); + out_list[1] = g_strdup ("en_GB"); + + return out_list; +} + +static void List_Dictionaries_ProviderConfigurationDuplicateTags (EnchantProvider * me, const char *) +{ + me->list_dicts=DuplicateEnGbListDictionaries; + me->free_string_list = MockProviderFreeStringList; +} + +struct EnchantBrokerListDictionaries_ProviderDuplicateTags_TestFixture : EnchantBrokerListDictionaries_TestFixtureBase +{ + //Setup + EnchantBrokerListDictionaries_ProviderDuplicateTags_TestFixture(): + EnchantBrokerListDictionaries_TestFixtureBase(List_Dictionaries_ProviderConfigurationDuplicateTags) + { } +}; + +/** + * enchant_broker_list_dicts + * @broker: A non-null #EnchantBroker + * @fn: A non-null #EnchantDictDescribeFn + * @user_data: Optional user-data + * + * Enumerates the dictionaries available from + * all Enchant providers. + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture, + EnchantBrokerListDictionaries_UserDataNotNull_PassedThrough) +{ + char* userData = "some user data"; + enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeAssignUserDataToStaticCallback, userData); + CHECK_EQUAL(userData, global_user_data); +} + +TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture, + EnchantBrokerListDictionaries_UserDataNull_PassedThrough) +{ + enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeAssignUserDataToStaticCallback, NULL); + CHECK_EQUAL((void*)NULL, global_user_data); +} + +TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture, + EnchantBrokerListDictionaries_listDictionariesCalledOnProvider) +{ + enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeCallback, &_dictionaryList); + CHECK(listDictionariesCalled); +} + +TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture, + EnchantBrokerListDictionaries_freeStringListCalledOnProvider) +{ + enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeCallback, &_dictionaryList); + CHECK(freeStringListCalled); +} + +TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture, + EnchantBrokerListDictionaries_dictionaryListHasDictionariesFromProvider) +{ + enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeCallback, &_dictionaryList); + CHECK_EQUAL((unsigned int)1, _dictionaryList.size()); + if(_dictionaryList.size()>0){ + CHECK(_dictionaryList[0].DataIsComplete()); + CHECK_EQUAL(std::string("en_GB"), _dictionaryList[0].LanguageTag); + CHECK_EQUAL(std::string("mock"), _dictionaryList[0].Name); + CHECK_EQUAL(std::string("Mock Provider"), _dictionaryList[0].Description); + } +} + +TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture, + EnchantBrokerListDictionaries_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockProvider("something bad happened"); + + enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeCallback, &_dictionaryList); + + CHECK_EQUAL((void*)NULL, (void*)enchant_broker_get_error(_broker)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture, + EnchantBrokerListDictionaries_NullBroker_DoNotCallDescribeFunction) +{ + enchant_broker_list_dicts(NULL, EnchantDictionaryDescribeAssignUserDataToStaticCallback, NULL); + CHECK(!listDictionariesCalled); +} + +TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture, + EnchantBrokerListDictionaries_NullCallback_DoNotCallDescribeFunction) +{ + enchant_broker_list_dicts(_broker, NULL, NULL); + CHECK(!listDictionariesCalled); +} + +TEST_FIXTURE(EnchantBrokerTestFixture, + EnchantBrokerListDictionaries_ProviderLacksListDictionaries_CallbackNeverCalled) +{ + void* userData = "some user data"; + enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeAssignUserDataToStaticCallback, userData); + CHECK_EQUAL((void*)NULL, global_user_data); +} + +TEST_FIXTURE(EnchantBrokerListDictionaries_ProviderDuplicateTags_TestFixture, + EnchantBrokerListDictionaries_ProviderDuplicateTags_CallbackCalledOnlyOncePerUniqueTag) +{ + enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeCallback, &_dictionaryList); + CHECK_EQUAL((unsigned int)1, _dictionaryList.size()); +} + +TEST_FIXTURE(EnchantBrokerListDictionaries_ProviderLacksFreeStringList_TestFixture, + EnchantBrokerListDictionaries_freeStringListCalledOnProvider) +{ + enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeCallback, &_dictionaryList); + CHECK(!freeStringListCalled); +} + + +TEST_FIXTURE(EnchantBrokerListDictionaries_ProviderReturnsInvalidTag_TestFixture, + EnchantBrokerListDictionaries_ProviderReturnsInvalidTag) +{ + enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeCallback, &_dictionaryList); + CHECK_EQUAL((unsigned int)1, _dictionaryList.size()); +} \ No newline at end of file diff --git a/unittests/broker/enchant_broker_request_dict_tests.cpp b/unittests/broker/enchant_broker_request_dict_tests.cpp new file mode 100644 index 0000000..7affd19 --- /dev/null +++ b/unittests/broker/enchant_broker_request_dict_tests.cpp @@ -0,0 +1,225 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantBrokerTestFixture.h" + +static bool requestDictionaryCalled; +static EnchantDict * RequestDictionary (EnchantProvider *me, const char *tag) +{ + requestDictionaryCalled = true; + return MockEnGbAndQaaProviderRequestDictionary(me, tag); +} + +static void Request_Dictionary_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = RequestDictionary; + me->dispose_dict = MockProviderDisposeDictionary; +} + +struct EnchantBrokerRequestDictionary_TestFixture : EnchantBrokerTestFixture +{ + //Setup + EnchantBrokerRequestDictionary_TestFixture(): + EnchantBrokerTestFixture(Request_Dictionary_ProviderConfiguration) + { + _dict = NULL; + requestDictionaryCalled = false; + } + + //Teardown + ~EnchantBrokerRequestDictionary_TestFixture() + { + FreeDictionary(_dict); + } + + EnchantDict* _dict; +}; + +/** + * enchant_broker_request_dict + * @broker: A non-null #EnchantBroker + * @tag: The non-null language tag you wish to request a dictionary for ("en_US", "de_DE", ...) + * + * Returns: An #EnchantDict, or %null if no suitable dictionary could be found. + */ + + + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_ProviderHas_CallsProvider) +{ + _dict = enchant_broker_request_dict(_broker, "en_GB"); + CHECK(_dict); + CHECK(requestDictionaryCalled); +} + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_CalledTwice_CallsProviderOnceReturnsSame) +{ + _dict = enchant_broker_request_dict(_broker, "en_GB"); + requestDictionaryCalled = false; + EnchantDict* dict = enchant_broker_request_dict(_broker, "en_GB"); + CHECK(!requestDictionaryCalled); + + CHECK_EQUAL(_dict, dict); +} + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_ProviderDoesNotHave_CallsProvider) +{ + _dict = enchant_broker_request_dict(_broker, "en"); + CHECK_EQUAL((void*)NULL, (void*)_dict); + CHECK(requestDictionaryCalled); +} + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_ProviderHasBase_CallsProvider) +{ + _dict = enchant_broker_request_dict(_broker, "qaa_CA"); + CHECK(_dict); + CHECK(requestDictionaryCalled); +} + + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_WhitespaceSurroundingLanguageTag_Removed) +{ + _dict = enchant_broker_request_dict(_broker, "\n\r en_GB \t\f"); + CHECK(_dict); +} + +/* Vertical tab is not considered to be whitespace in glib! + See bug# 59388 http://bugzilla.gnome.org/show_bug.cgi?id=59388 +*/ +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_VerticalTabBeforeLanguageTag_NotRemoved) +{ + _dict = enchant_broker_request_dict(_broker, "\ven_GB"); + CHECK_EQUAL((void*)NULL, (void*)_dict); +} + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_VerticalTabAfterLanguageTag_NotRemoved) +{ + _dict = enchant_broker_request_dict(_broker, "en_GB\v"); + CHECK_EQUAL((void*)NULL, (void*)_dict); +} + + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_AtSignInLanguageTag_RemovesToTail) +{ + _dict = enchant_broker_request_dict(_broker, "en_GB@euro"); + CHECK(_dict); +} + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_PeriodInLanguageTag_RemovesToTail) +{ + _dict = enchant_broker_request_dict(_broker, "en_GB.UTF-8"); + CHECK(_dict); +} + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_HyphensInLanguageTag_SubstitutedWithUnderscore) +{ + _dict = enchant_broker_request_dict(_broker, "en-GB"); + CHECK(_dict); +} + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_DifferentCase_Finds) +{ + _dict = enchant_broker_request_dict(_broker, "En_gb"); + CHECK(_dict); +} + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_DifferentCase_NoRegion_Finds) +{ + _dict = enchant_broker_request_dict(_broker, "QAA"); + CHECK(_dict); +} + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockProvider("something bad happened"); + + _dict = enchant_broker_request_dict(_broker, "en-GB"); + + CHECK_EQUAL((void*)NULL, (void*)enchant_broker_get_error(_broker)); +} + +// ordering of providers for request is tested by enchant_broker_set_ordering tests + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_NullBroker_NULL) +{ + _dict = enchant_broker_request_dict(NULL, "en_GB"); + + CHECK_EQUAL((void*)NULL, (void*)_dict); + CHECK(!requestDictionaryCalled); +} + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_NullLanguageTag_NULL) +{ + _dict = enchant_broker_request_dict(_broker, NULL); + + CHECK_EQUAL((void*)NULL, (void*)_dict); + CHECK(!requestDictionaryCalled); +} + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_EmptyLanguageTag_NULL) +{ + _dict = enchant_broker_request_dict(_broker, ""); + + CHECK_EQUAL((void*)NULL, _dict); + CHECK(!requestDictionaryCalled); + +} + +TEST_FIXTURE(EnchantBrokerRequestDictionary_TestFixture, + EnchantBrokerRequestDictionary_InvalidTag_NULL_ErrorSet) +{ + _dict = enchant_broker_request_dict(_broker, "en~US"); + CHECK_EQUAL((void*)NULL, _dict); + CHECK(NULL != enchant_broker_get_error(_broker)); +} + +TEST_FIXTURE(EnchantBrokerTestFixture, + EnchantBrokerRequestDictionary_ProviderLacksListDictionaries_CallbackNeverCalled) +{ + requestDictionaryCalled = false; + EnchantDict* dict = enchant_broker_request_dict(_broker, "en_GB"); + + CHECK_EQUAL((void*)NULL, dict); + CHECK(!requestDictionaryCalled); +} diff --git a/unittests/broker/enchant_broker_request_pwl_dict_tests.cpp b/unittests/broker/enchant_broker_request_pwl_dict_tests.cpp new file mode 100644 index 0000000..a4f11c5 --- /dev/null +++ b/unittests/broker/enchant_broker_request_pwl_dict_tests.cpp @@ -0,0 +1,138 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantBrokerTestFixture.h" + +struct EnchantBrokerRequestPwlDictionary_TestFixture : EnchantBrokerTestFixture +{ + //Setup + EnchantBrokerRequestPwlDictionary_TestFixture() + { + _dict = NULL; + _pwlFile = GetTemporaryFilename("epwl"); + } + + //Teardown + ~EnchantBrokerRequestPwlDictionary_TestFixture() + { + FreeDictionary(_dict); + DeleteFile(_pwlFile); + } + + EnchantDict* _dict; + std::string _pwlFile; +}; + +/** + * enchant_broker_request_pwl_dict + * + * PWL is a personal wordlist file, 1 entry per line + * + * Returns: + */ + + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST_FIXTURE(EnchantBrokerRequestPwlDictionary_TestFixture, + EnchantBrokerRequestPwlDictionary_FileExists) +{ + CreateFile(_pwlFile); + _dict = enchant_broker_request_pwl_dict(_broker, _pwlFile.c_str()); + CHECK(_dict); +} + +TEST_FIXTURE(EnchantBrokerRequestPwlDictionary_TestFixture, + EnchantBrokerRequestPwlDictionary_FileDoesNotExist_SucceedsCreatesFile) +{ + _dict = enchant_broker_request_pwl_dict(_broker, _pwlFile.c_str()); + CHECK(FileExists(_pwlFile)); + CHECK(_dict); +} + +TEST_FIXTURE(EnchantBrokerRequestPwlDictionary_TestFixture, + EnchantBrokerRequestPwlDictionary_CalledTwice_ReturnsSame) +{ + _dict = enchant_broker_request_pwl_dict(_broker, _pwlFile.c_str()); + EnchantDict* dict = enchant_broker_request_pwl_dict(_broker, _pwlFile.c_str()); + CHECK_EQUAL(_dict, dict); +} + +TEST_FIXTURE(EnchantBrokerRequestPwlDictionary_TestFixture, + EnchantBrokerRequestPwlDictionary_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockProvider("something bad happened"); + + _dict = enchant_broker_request_pwl_dict(_broker, _pwlFile.c_str()); + + CHECK_EQUAL((void*)NULL, (void*)enchant_broker_get_error(_broker)); +} + + + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantBrokerRequestPwlDictionary_TestFixture, + EnchantBrokerRequestPwlDictionary_NullBroker_NULL) +{ + _dict = enchant_broker_request_pwl_dict(NULL, _pwlFile.c_str()); + + CHECK_EQUAL((void*)NULL, (void*)_dict); +} + +TEST_FIXTURE(EnchantBrokerRequestPwlDictionary_TestFixture, + EnchantBrokerRequestPwlDictionary_NullFilename_NULL) +{ + _dict = enchant_broker_request_pwl_dict(_broker, NULL); + + CHECK_EQUAL((void*)NULL, (void*)_dict); +} + +TEST_FIXTURE(EnchantBrokerRequestPwlDictionary_TestFixture, + EnchantBrokerRequestPwlDictionary_EmptyFilename_NULL) +{ + _dict = enchant_broker_request_pwl_dict(_broker, ""); + + CHECK_EQUAL((void*)NULL, _dict); +} + +TEST_FIXTURE(EnchantBrokerRequestPwlDictionary_TestFixture, + EnchantBrokerRequestPwlDictionary_IllegalFilename_NULL) +{ + // Colon is illegal for Windows and Mac but okay for Linux; +#if defined(XP_TARGET_COCOA) || defined(_WIN32) + _dict = enchant_broker_request_pwl_dict(_broker, ":"); + CHECK(!_dict); + CHECK((void*)enchant_broker_get_error(_broker)); +#endif +} + +#if defined(_WIN32) +TEST_FIXTURE(EnchantBrokerRequestPwlDictionary_TestFixture, + EnchantBrokerRequestPwlDictionary_IllegalUtf8InFilename_NULL) +{ + _dict = enchant_broker_request_pwl_dict(_broker, "abc\xa5\xf1\x08"); + CHECK(!_dict); +} +#endif diff --git a/unittests/broker/enchant_broker_set_ordering_tests.cpp b/unittests/broker/enchant_broker_set_ordering_tests.cpp new file mode 100644 index 0000000..086a479 --- /dev/null +++ b/unittests/broker/enchant_broker_set_ordering_tests.cpp @@ -0,0 +1,577 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include +#include "../EnchantBrokerTestFixture.h" + +static bool mock1ProviderRequestDictionaryCalled; +static bool mock2ProviderRequestDictionaryCalled; + +static EnchantDict * RequestDictionary1 (EnchantProvider *me, const char *tag) +{ + mock1ProviderRequestDictionaryCalled = true; + return MockEnGbAndQaaProviderRequestDictionary(me, tag); +} +static EnchantDict * RequestDictionary2 (EnchantProvider *me, const char *tag) +{ + mock2ProviderRequestDictionaryCalled = true; + return MockEnGbAndQaaProviderRequestDictionary(me, tag); +} +static const char * +MockProvider1Identify (EnchantProvider *) +{ + return "mock1"; +} + +static const char * +MockProvider2Identify (EnchantProvider *) +{ + return "mock2"; +} +static void Request_Dictionary_ProviderConfiguration1 (EnchantProvider * me, const char *) +{ + me->request_dict = RequestDictionary1; + me->dispose_dict = MockProviderDisposeDictionary; + me->identify = MockProvider1Identify; +} +static void Request_Dictionary_ProviderConfiguration2 (EnchantProvider * me, const char *) +{ + me->request_dict = RequestDictionary2; + me->dispose_dict = MockProviderDisposeDictionary; + me->identify = MockProvider2Identify; +} + + +enum ProviderOrder { + Mock1ThenMock2=1, + Mock2ThenMock1=2, + ErrorBothCalled=3, + ErrorNeitherCalled=4 +}; + +struct EnchantBrokerSetOrdering_TestFixture : EnchantBrokerTestFixture +{ + //Setup + EnchantBrokerSetOrdering_TestFixture(): + EnchantBrokerTestFixture(Request_Dictionary_ProviderConfiguration1,Request_Dictionary_ProviderConfiguration2) + + { + mock1ProviderRequestDictionaryCalled = false; + mock2ProviderRequestDictionaryCalled = false; + } + + ProviderOrder GetProviderOrder(const std::string & languageTag) + { + ProviderOrder result = ErrorBothCalled; + + EnchantDict* dict = enchant_broker_request_dict(_broker, languageTag.c_str()); + + if(mock2ProviderRequestDictionaryCalled && !mock1ProviderRequestDictionaryCalled) + { + result = Mock2ThenMock1; + } + else if(mock1ProviderRequestDictionaryCalled && !mock2ProviderRequestDictionaryCalled) + { + result = Mock1ThenMock2; + } + else if(!mock2ProviderRequestDictionaryCalled && !mock1ProviderRequestDictionaryCalled) + { + result = ErrorNeitherCalled; + } + + FreeDictionary(dict); + + mock1ProviderRequestDictionaryCalled = false; + mock2ProviderRequestDictionaryCalled = false; + + return result; + } +}; + +struct EnchantBrokerFileSetOrdering_TestFixture: EnchantBrokerSetOrdering_TestFixture +{ + std::string _tempPath; + + EnchantBrokerFileSetOrdering_TestFixture() + { + if(_broker){ // this is now freed so that individual tests can set up the file state they + // need before calling _broker = enchant_broker_init (); + enchant_broker_free (_broker); + _broker = NULL; + } + _tempPath = GetTemporaryFilename(""); + } + + ~EnchantBrokerFileSetOrdering_TestFixture() + { + DeleteDirAndFiles(_tempPath); + } + + static void WriteStringToOrderingFile(const std::string& path, const std::string& contents) + { + CreateDirectory(path); + std::ofstream file(AddToPath(path, "enchant.ordering").c_str()); + file << contents; + } + +}; + + +/** + * enchant_broker_set_ordering + * @broker: A non-null #EnchantBroker + * @tag: A non-null language tag (en_US) + * @ordering: A non-null ordering (aspell,myspell,ispell,uspell,hspell) + * + * Declares a preference of dictionaries to use for the language + * described/referred to by @tag. The ordering is a comma delimited + * list of provider names. As a special exception, the "*" tag can + * be used as a language tag to declare a default ordering for any + * language that does not explictly declare an ordering. + */ + + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_AsteriskForLanguage_SetsDefaultOrdering) +{ + enchant_broker_set_ordering(_broker, "*", "mock2,mock1"); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("qaa")); + + enchant_broker_set_ordering(_broker, "*", "mock1,mock2"); + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("qaa")); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_NoSpaces) +{ + enchant_broker_set_ordering(_broker, "qaa", "mock2,mock1"); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("qaa")); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_WhitespaceAroundProvider) +{ + enchant_broker_set_ordering(_broker, "qaa", "\n\f\t\r mock2\n \f\t\r,mock1"); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("qaa")); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_WhitespaceAroundProviderAfterComma) +{ + enchant_broker_set_ordering(_broker, "qaa", "aspell,\n\f\t \rmock2\n\f \r\t,mock1"); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("qaa")); +} + +/* Vertical tab is not considered to be whitespace in glib! + See bug# 59388 http://bugzilla.gnome.org/show_bug.cgi?id=59388 +*/ +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_VerticalTabSurroundingProvider_NotRemoved) +{ + enchant_broker_set_ordering(_broker, "qaa", "\vmock2,mock1"); + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("qaa")); + + enchant_broker_set_ordering(_broker, "qaa", "mock2\v,mock1"); + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("qaa")); + + enchant_broker_set_ordering(_broker, "qaa", "aspell,\vmock2,mock1"); + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("qaa")); + + enchant_broker_set_ordering(_broker, "qaa", "aspell,mock2\v,mock1"); + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("qaa")); + +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_SpecificLanguage_SetsOrderingForSpecific) +{ + enchant_broker_set_ordering(_broker, "qaa", "mock2,mock1"); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("qaa")); + + enchant_broker_set_ordering(_broker, "qaa", "mock1,mock2"); + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("qaa")); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_UnknownProvider_Ignored) +{ + enchant_broker_set_ordering(_broker, "qaa", "unknown,mock2,mock1"); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("qaa")); +} + + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_WhitespaceSurroundingLanguageTag_Removed) +{ + enchant_broker_set_ordering(_broker, "\n\r qaa \t\f", "mock2,mock1"); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("qaa")); +} + +/* Vertical tab is not considered to be whitespace in glib! + See bug# 59388 http://bugzilla.gnome.org/show_bug.cgi?id=59388 +*/ +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_VerticalTabPreceedingLanguageTag_NotRemoved) +{ + enchant_broker_set_ordering(_broker, "*", "mock1,mock2"); + enchant_broker_set_ordering(_broker, "\vqaa", "mock2,mock1"); + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("qaa")); +} + +/* Vertical tab is not considered to be whitespace in glib! + See bug# 59388 http://bugzilla.gnome.org/show_bug.cgi?id=59388 +*/ +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_VerticalTabFollowingLanguageTag_NotRemoved) +{ + enchant_broker_set_ordering(_broker, "*", "mock1,mock2"); + enchant_broker_set_ordering(_broker, "qaa\v", "mock2,mock1"); + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("qaa")); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_AtSignInLanguageTag_RemovesToTail) +{ + enchant_broker_set_ordering(_broker, "en_GB@euro", "mock2,mock1"); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerDictExists_PeriodInLanguageTag_RemovesToTail) +{ + enchant_broker_set_ordering(_broker, "en_GB.UTF-8", "unknown,mock2,mock1"); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_HyphensInLanguageTag_SubstitutedWithUnderscore) +{ + enchant_broker_set_ordering(_broker, "en-GB", "mock2,mock1"); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_DifferentCaseInLanguageTag_SubstitutedWithCorrectCase) +{ + enchant_broker_set_ordering(_broker, "EN-gb", "mock2,mock1"); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_DifferentCaseInLanguageTagNoRegion_SubstitutedWithCorrectCase) +{ + enchant_broker_set_ordering(_broker, "QAA", "mock2,mock1"); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("qaa")); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockProvider("something bad happened"); + + enchant_broker_set_ordering(_broker, "qaa", "mock2,mock1"); + + CHECK_EQUAL((void*)NULL, (void*)enchant_broker_get_error(_broker)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_NullBroker_DoNothing) +{ + enchant_broker_set_ordering(_broker, "qaa", "mock2,mock1"); + enchant_broker_set_ordering(NULL, "qaa", "mock1,mock2"); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("qaa")); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_NullLanguageTag_DoNothing) +{ + enchant_broker_set_ordering(_broker, "*", "mock2,mock1"); + enchant_broker_set_ordering(_broker, NULL, "mock1,mock2"); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("qaa")); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_EmptyAfterNormalization_DoNothing) +{ + testResults_; + enchant_broker_set_ordering(_broker, " @ ", "mock1,mock2"); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_EmptyLanguageTag_DoNothing) +{ + testResults_; + enchant_broker_set_ordering(_broker, "", "aspell,myspell,ispell"); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_NullOrdering_DoNothing) +{ + testResults_; + enchant_broker_set_ordering(_broker, "en_GB", NULL); +} + +TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, + EnchantBrokerSetOrdering_EmptyOrdering_DoNothing) +{ + testResults_; + enchant_broker_set_ordering(_broker, "en_GB", ""); +} + + +/* + * Ordering can also be set in enchant.ordering file: + * Language_Tag : Provider1, Provider2, ProviderN + * + * The enchant.ordering file is discovered by first looking in the user's + * config directory, then in the .enchant directory in the user's home directory. + * + * The user's config directory is located at share/enchant + * in the module directory (that libenchant is in) + * but it can be overridden using the registry setting + * HKEY_CURRENT_USER\Software\Enchant\Config\Data_Dir + * or HKEY_LOCAL_MACHINE\Software\Enchant\Config\Data_Dir + * + * The user's home directory on windows can be overridden using the registry + * setting HKEY_CURRENT_USER\Software\Enchant\Config\Home_Dir + * + * The module directory can be overridden using the registry setting + * HKEY_CURRENT_USER\Software\Enchant\Config\Module_Dir + * or HKEY_LOCAL_MACHINE\Software\Enchant\Config\Module_Dir + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, +EnchantBrokerFileOrderingMock1ThenMock2_DefaultConfigDirectory) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),"en_GB:mock1,mock2"); + InitializeBroker(); + + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, +EnchantBrokerFileOrderingMock2ThenMock1_DefaultConfigDirectory) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),"en_GB:mock2,mock1"); + InitializeBroker(); + + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, +EnchantBrokerFileOrderingMock1ThenMock2_UserOverriddenConfigDirectory) +{ + WriteStringToOrderingFile(_tempPath, "en_GB:mock1,mock2"); + SetUserRegistryConfigDir(_tempPath); + InitializeBroker(); + + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrderingMock2ThenMock1_UserOverriddenConfigDirectory) +{ + WriteStringToOrderingFile(_tempPath, "en_GB:mock2,mock1"); + SetUserRegistryConfigDir(_tempPath); + InitializeBroker(); + + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrderingMock1ThenMock2_MachineOverriddenConfigDirectory) +{ + WriteStringToOrderingFile(_tempPath, "en_GB:mock1,mock2"); + SetMachineRegistryConfigDir(_tempPath); + InitializeBroker(); + + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrderingMock2ThenMock1_MachineOverriddenConfigDirectory) +{ + WriteStringToOrderingFile(_tempPath, "en_GB:mock2,mock1"); + SetMachineRegistryConfigDir(_tempPath); + InitializeBroker(); + + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("en_GB")); +} + +// if config registry "" global from share/enchant +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrdering_OverriddenConfigDirectoryIsBlank_UsesSettingsFromDefaultConfigDirectory_Mock1Then2) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),"en_GB:mock1,mock2"); + SetMachineRegistryConfigDir(""); + InitializeBroker(); + + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrdering_OverriddenConfigDirectoryIsBlank_UsesSettingsFromDefaultConfigDirectory_Mock2Then1) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),"en_GB:mock2,mock1"); + SetMachineRegistryConfigDir(""); + InitializeBroker(); + + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrdering_ExtraSpacesAndTabs_Mock1Then2) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),"\t en_GB\f \t:\r\t mock1\t , \tmock2\t "); + InitializeBroker(); + + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrdering_ExtraSpaces_Mock2Then1) +{ + WriteStringToOrderingFile(GetEnchantConfigDir()," \ten_GB\t \f:\r \tmock2 \t,\t mock1 \t"); + InitializeBroker(); + + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrdering_ExtraNewLines_Mock1Then2) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),"\nen_GB:mock1,mock2\n\nqaa:mock2,mock1\n*:mock2\n"); + InitializeBroker(); + + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrdering_ExtraNewLines_Mock2Then1) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),"\nen_GB:mock2,mock1\n\nqaa:mock2,mock1\n*:mock2\n"); + InitializeBroker(); + + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("en_GB")); +} + + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrdering_HomeAndGlobal_HomeMergedWithGlobal_HomeTakesPrecedence_Mock1Then2) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),"en_GB:mock2,mock1\nqaa:mock1,mock2"); + WriteStringToOrderingFile(_tempPath, "en_GB:mock1,mock2"); + SetUserRegistryConfigDir(_tempPath); + + InitializeBroker(); + + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("en_GB")); + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("qaa")); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrdering_HomeAndGlobal_HomeMergedWithGlobal_HomeTakesPrecedence_Mock2Then1) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),"en_GB:mock1,mock2\nqaa:mock2,mock1"); + WriteStringToOrderingFile(_tempPath, "en_GB:mock2,mock1"); + SetUserRegistryConfigDir(_tempPath); + + InitializeBroker(); + + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("en_GB")); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("qaa")); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrdering_EmptyFile) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),""); + + InitializeBroker(); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrdering_NoLanguageTag) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),":mock1,mock2"); + + InitializeBroker(); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrdering_NoColon) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),"en_GB mock1,mock2"); + + InitializeBroker(); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrdering_NoProviders_DoesNotOverridePreviousOrdering_Mock1Then2) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),"en_GB:"); + WriteStringToOrderingFile(_tempPath, "en_GB:mock1,mock2"); + SetUserRegistryConfigDir(_tempPath); + + InitializeBroker(); + + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, + EnchantBrokerFileOrdering_NoProviders_DoesNotOverridePreviousOrdering_Mock2Then1) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),"en_GB:"); + WriteStringToOrderingFile(_tempPath, "en_GB:mock2,mock1"); + SetUserRegistryConfigDir(_tempPath); + + InitializeBroker(); + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, +EnchantBrokerFileOrdering_ListedTwice_LastTakesPrecedence_Mock1ThenMock2) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),"en_GB:mock2,mock1\nen_GB:mock1,mock2"); + InitializeBroker(); + + CHECK_EQUAL(Mock1ThenMock2, GetProviderOrder("en_GB")); +} + +TEST_FIXTURE(EnchantBrokerFileSetOrdering_TestFixture, +EnchantBrokerFileOrdering_ListedTwice_LastTakesPrecedence_Mock2ThenMock1) +{ + WriteStringToOrderingFile(GetEnchantConfigDir(),"en_GB:mock1,mock2\nen_GB:mock2,mock1"); + InitializeBroker(); + + CHECK_EQUAL(Mock2ThenMock1, GetProviderOrder("en_GB")); +} diff --git a/unittests/dictionary/enchant_dict_add_tests.cpp b/unittests/dictionary/enchant_dict_add_tests.cpp new file mode 100644 index 0000000..aab3c6a --- /dev/null +++ b/unittests/dictionary/enchant_dict_add_tests.cpp @@ -0,0 +1,234 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantDictionaryTestFixture.h" + +static bool addToPersonalCalled; +static std::string wordToAdd; + +static void +MockDictionaryAddToPersonal (EnchantDict * dict, const char *const word, size_t len) +{ + dict; + addToPersonalCalled = true; + wordToAdd = std::string(word, len); +} + +static EnchantDict* MockProviderRequestAddToPersonalMockDictionary(EnchantProvider * me, const char *tag) +{ + + EnchantDict* dict = MockProviderRequestEmptyMockDictionary(me, tag); + dict->add_to_personal = MockDictionaryAddToPersonal; + return dict; +} + +static void DictionaryAddToPersonal_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = MockProviderRequestAddToPersonalMockDictionary; + me->dispose_dict = MockProviderDisposeDictionary; +} + +struct EnchantDictionaryAdd_TestFixture : EnchantDictionaryTestFixture +{ + //Setup + EnchantDictionaryAdd_TestFixture(): + EnchantDictionaryTestFixture(DictionaryAddToPersonal_ProviderConfiguration) + { + addToPersonalCalled = false; + } +}; + +struct EnchantDictionaryAddToPersonalNotImplemented_TestFixture : EnchantDictionaryTestFixture +{ + //Setup + EnchantDictionaryAddToPersonalNotImplemented_TestFixture(): + EnchantDictionaryTestFixture(EmptyDictionary_ProviderConfiguration) + { + addToPersonalCalled = false; + } +}; + +/** + * enchant_dict_add + * @dict: A non-null #EnchantDict + * @word: The non-null word you wish to add to your personal dictionary, in UTF-8 encoding + * @len: The byte length of @word, or -1 for strlen (@word) + * + * Remarks: if the word exists in the exclude dictionary, it will be removed from the + * exclude dictionary + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_WordExistsInDictionary) +{ + enchant_dict_add(_dict, "hello", -1); + CHECK(IsWordInDictionary("hello")); +} + +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_WordExistsInSession) +{ + enchant_dict_add(_dict, "hello", -1); + CHECK(IsWordInSession("hello")); +} + +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_WordAddedToEnchantPwlFile) +{ + CHECK(!PersonalWordListFileHasContents()); + enchant_dict_add(_dict, "hello", -1); + CHECK(PersonalWordListFileHasContents()); +} + +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_PassedOnToProvider_LenComputed) +{ + enchant_dict_add(_dict, "hello", -1); + CHECK(addToPersonalCalled); + CHECK_EQUAL(std::string("hello"), wordToAdd); +} + +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_PassedOnToProvider_LenSpecified) +{ + enchant_dict_add(_dict, "hellodisregard me", 5); + CHECK(addToPersonalCalled); + CHECK_EQUAL(std::string("hello"), wordToAdd); +} + + +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_WordExistsInSession_StillCallsProvider) +{ + enchant_dict_add_to_session(_dict, "session", -1); + CHECK(!addToPersonalCalled); + + enchant_dict_add(_dict, "session", -1); + CHECK(addToPersonalCalled); + CHECK_EQUAL(std::string("session"), wordToAdd); +} + +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_WordExistsInPersonal_StillCallsProvider) +{ + enchant_dict_add(_dict, "personal", -1); + addToPersonalCalled=false; + wordToAdd = std::string(); + + enchant_dict_add(_dict, "personal", -1); + CHECK(addToPersonalCalled); + CHECK_EQUAL(std::string("personal"), wordToAdd); +} + +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_WordExistsInExclude_RemovedFromExcludeAddedToPersonal) +{ + enchant_dict_remove(_dict, "personal", -1); + CHECK(ExcludeFileHasContents()); + enchant_dict_add(_dict, "personal", -1); + CHECK(!ExcludeFileHasContents()); + CHECK(PersonalWordListFileHasContents()); +} + +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_InBrokerPwl) +{ + enchant_dict_add(_pwl, "personal", -1); + CHECK(!addToPersonalCalled); + CHECK(!PersonalWordListFileHasContents()); +} + +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_IsPermanent) +{ + enchant_dict_add(_dict, "hello", -1); + CHECK(IsWordInDictionary("hello")); + + ReloadTestDictionary(); + + CHECK(IsWordInDictionary("hello")); +} + + +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockDictionary("something bad happened"); + + enchant_dict_add(_dict, "hello", -1); + CHECK_EQUAL((void*)NULL, (void*)enchant_dict_get_error(_dict)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_NullDictionary_NotAdded) +{ + enchant_dict_add(NULL, "hello", -1); + CHECK(!addToPersonalCalled); +} + +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_NullWord_NotAdded) +{ + enchant_dict_add(_dict, NULL, -1); + CHECK(!addToPersonalCalled); +} + +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_EmptyWord_NotAdded) +{ + enchant_dict_add(_dict, "", -1); + CHECK(!addToPersonalCalled); +} + +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_WordSize0_NotAdded) +{ + enchant_dict_add(_dict, "hello", 0); + CHECK(!addToPersonalCalled); +} + +TEST_FIXTURE(EnchantDictionaryAdd_TestFixture, + EnchantDictionaryAdd_InvalidUtf8Word_NotAdded) +{ + enchant_dict_add(_dict, "\xa5\xf1\x08", -1); + CHECK(!addToPersonalCalled); +} + +TEST_FIXTURE(EnchantDictionaryAddToPersonalNotImplemented_TestFixture, + EnchantDictionaryAddToPersonalNotImplemented_WordAddedToEnchantPwlFile) +{ + CHECK(!PersonalWordListFileHasContents()); + enchant_dict_add(_dict, "hello", -1); + CHECK(PersonalWordListFileHasContents()); +} + +TEST_FIXTURE(EnchantDictionaryAddToPersonalNotImplemented_TestFixture, + EnchantDictionaryAddToPersonalNotImplemented_NotPassedOnToProvider) +{ + enchant_dict_add(_dict, "hello", -1); + CHECK(!addToPersonalCalled); +} diff --git a/unittests/dictionary/enchant_dict_add_to_session_tests.cpp b/unittests/dictionary/enchant_dict_add_to_session_tests.cpp new file mode 100644 index 0000000..a62aad8 --- /dev/null +++ b/unittests/dictionary/enchant_dict_add_to_session_tests.cpp @@ -0,0 +1,221 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantDictionaryTestFixture.h" + +static bool addToSessionCalled; +static std::string wordToAdd; + +static void +MockDictionaryAddToSession (EnchantDict * dict, const char *const word, size_t len) +{ + dict; + addToSessionCalled = true; + wordToAdd = std::string(word, len); +} + +static EnchantDict* MockProviderRequestAddToSessionMockDictionary(EnchantProvider * me, const char *tag) +{ + + EnchantDict* dict = MockProviderRequestEmptyMockDictionary(me, tag); + dict->add_to_session = MockDictionaryAddToSession; + return dict; +} + +static void DictionaryAddToSession_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = MockProviderRequestAddToSessionMockDictionary; + me->dispose_dict = MockProviderDisposeDictionary; +} + +struct EnchantDictionaryAddToSession_TestFixture : EnchantDictionaryTestFixture +{ + //Setup + EnchantDictionaryAddToSession_TestFixture(): + EnchantDictionaryTestFixture(DictionaryAddToSession_ProviderConfiguration) + { + addToSessionCalled = false; + } +}; + +struct EnchantDictionaryAddToSessionNotImplemented_TestFixture : EnchantDictionaryTestFixture +{ + //Setup + EnchantDictionaryAddToSessionNotImplemented_TestFixture(): + EnchantDictionaryTestFixture(EmptyDictionary_ProviderConfiguration) + { + addToSessionCalled = false; + } +}; +/** + * enchant_dict_add_to_session + * @dict: A non-null #EnchantDict + * @word: The non-null word you wish to add to this spell-checking session, in UTF-8 encoding + * @len: The byte length of @word, or -1 for strlen (@word) + * + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_WordNotAddedToEnchantPwlFile) +{ + enchant_dict_add_to_session(_dict, "hello", -1); + CHECK(!PersonalWordListFileHasContents()); +} + +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_PassedOnToProvider_LenComputed) +{ + enchant_dict_add_to_session(_dict, "hello", -1); + CHECK(addToSessionCalled); + CHECK_EQUAL(std::string("hello"), wordToAdd); +} + +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_PassedOnToProvider_LenSpecified) +{ + enchant_dict_add_to_session(_dict, "hellodisregard me", 5); + CHECK(addToSessionCalled); + CHECK_EQUAL(std::string("hello"), wordToAdd); +} + +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_WordExistsInSession_StillCallsProvider) +{ + enchant_dict_add_to_session(_dict, "session", -1); + addToSessionCalled=false; + wordToAdd = std::string(); + + enchant_dict_add_to_session(_dict, "session", -1); + CHECK(addToSessionCalled); + CHECK_EQUAL(std::string("session"), wordToAdd); +} + +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_WordExistsInPersonal_StillCallsProvider) +{ + enchant_dict_add(_dict, "personal", -1); + CHECK(!addToSessionCalled); + + enchant_dict_add_to_session(_dict, "personal", -1); + CHECK(addToSessionCalled); + CHECK_EQUAL(std::string("personal"), wordToAdd); +} + +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_WordExistsInExclude_AddedToSessionNotRemovedFromExcludeFile) +{ + enchant_dict_remove(_dict, "personal", -1); + + enchant_dict_add_to_session(_dict, "personal", -1); + CHECK(IsWordInDictionary("personal")); + CHECK(ExcludeFileHasContents()); +} + + +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_WordAddedToSession) +{ + enchant_dict_add_to_session(_dict, "hello", -1); + CHECK(IsWordInSession("hello")); +} + +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_InBrokerSession) +{ + enchant_dict_add_to_session(_pwl, "personal", -1); + CHECK(!addToSessionCalled); + CHECK(!PersonalWordListFileHasContents()); +} + +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_IsNotPermanent) +{ + enchant_dict_add_to_session(_dict, "hello", -1); + CHECK(IsWordInSession("hello")); + + ReloadTestDictionary(); + + CHECK(!IsWordInSession("hello")); +} + +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockDictionary("something bad happened"); + + enchant_dict_add_to_session(_dict, "hello", -1); + CHECK_EQUAL((void*)NULL, (void*)enchant_dict_get_error(_dict)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_NullDictionary_NotAdded) +{ + enchant_dict_add_to_session(NULL, "hello", -1); + CHECK(!addToSessionCalled); +} + +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_NullWord_NotAdded) +{ + enchant_dict_add_to_session(_dict, NULL, -1); + CHECK(!addToSessionCalled); +} + +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_EmptyWord_NotAdded) +{ + enchant_dict_add_to_session(_dict, "", -1); + CHECK(!addToSessionCalled); +} + +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_WordSize0_NotAdded) +{ + enchant_dict_add_to_session(_dict, "hello", 0); + CHECK(!addToSessionCalled); +} + +TEST_FIXTURE(EnchantDictionaryAddToSession_TestFixture, + EnchantDictionaryAddToSession_InvalidUtf8Word_NotAdded) +{ + enchant_dict_add_to_session(_dict, "\xa5\xf1\x08", -1); + CHECK(!addToSessionCalled); +} + +TEST_FIXTURE(EnchantDictionaryAddToSessionNotImplemented_TestFixture, + EnchantDictionaryAddToSessionNotImplemented_WordAddedToSession) +{ + enchant_dict_add_to_session(_dict, "hello", -1); + CHECK(IsWordInSession("hello")); +} + +TEST_FIXTURE(EnchantDictionaryAddToSessionNotImplemented_TestFixture, + EnchantDictionaryAddToSessionNotImplemented_NotPassedOnToProvider) +{ + enchant_dict_add_to_session(_dict, "hello", -1); + CHECK(!addToSessionCalled); +} diff --git a/unittests/dictionary/enchant_dict_check_tests.cpp b/unittests/dictionary/enchant_dict_check_tests.cpp new file mode 100644 index 0000000..3091d34 --- /dev/null +++ b/unittests/dictionary/enchant_dict_check_tests.cpp @@ -0,0 +1,224 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantDictionaryTestFixture.h" + +static bool dictCheckCalled; + +static int +MockDictionaryCheck (EnchantDict * dict, const char *const word, size_t len) +{ + dict; + dictCheckCalled = true; + if(strncmp("hello", word, len)==0) + { + return 0; //good word + } + return 1; // bad word +} + +static EnchantDict* MockProviderRequestCheckMockDictionary(EnchantProvider * me, const char *tag) +{ + + EnchantDict* dict = MockProviderRequestEmptyMockDictionary(me, tag); + dict->check = MockDictionaryCheck; + return dict; +} + + +static void DictionaryCheck_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = MockProviderRequestCheckMockDictionary; + me->dispose_dict = MockProviderDisposeDictionary; +} + + +struct EnchantDictionaryCheck_TestFixture : EnchantDictionaryTestFixture +{ + //Setup + EnchantDictionaryCheck_TestFixture(): + EnchantDictionaryTestFixture(DictionaryCheck_ProviderConfiguration) + { + dictCheckCalled = false; + } +}; + +struct EnchantDictionaryCheckNotImplemented_TestFixture : EnchantDictionaryTestFixture +{ + //Setup + EnchantDictionaryCheckNotImplemented_TestFixture(): + EnchantDictionaryTestFixture(EmptyDictionary_ProviderConfiguration) + { + dictCheckCalled = false; + } +}; + + +/** + * enchant_dict_check + * @dict: A non-null #EnchantDict + * @word: The non-null word you wish to correct, in UTF-8 encoding + * @len: The byte length of @word, or -1 for strlen (@word) + * + * Will return an "incorrect" value if any of those pre-conditions + * are not met. + * + * Returns: 0 if the word is correctly spelled, positive if not, negative if error + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_WordExists_LenComputed_0) +{ + CHECK_EQUAL(0, enchant_dict_check(_dict, "hello", -1)); + CHECK(dictCheckCalled); +} + +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_WordExists_LenSpecified_0) +{ + CHECK_EQUAL(0, enchant_dict_check(_dict, "hellodisregard me", 5)); + CHECK(dictCheckCalled); +} + +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_WordDoesNotExist_LenComputed_1) +{ + CHECK_EQUAL(1, enchant_dict_check(_dict, "helo", -1)); + CHECK(dictCheckCalled); +} + +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_WordDoesNotExist_LenSpecified_1) +{ + CHECK_EQUAL(1, enchant_dict_check(_dict, "helodisregard me", 4)); + CHECK(dictCheckCalled); +} + +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_WordExistsInSession_0_DoesNotCallProvider) +{ + enchant_dict_add_to_session(_dict, "session", -1); + CHECK_EQUAL(0, enchant_dict_check(_dict, "session", -1)); + CHECK(!dictCheckCalled); +} + +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_WordExistsInPersonal_0_DoesNotCallProvider) +{ + enchant_dict_add(_dict, "personal", -1); + + CHECK_EQUAL(0, enchant_dict_check(_dict, "personal", -1)); + CHECK(!dictCheckCalled); +} + +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_WordDoesExists_InBrokerPwlSession_0) +{ + enchant_dict_add_to_session(_pwl, "session", -1); + CHECK_EQUAL(0, enchant_dict_check(_pwl, "session", -1)); +} + +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_WordDoesExists_InBrokerPwl_0) +{ + enchant_dict_add(_pwl, "personal", -1); + CHECK_EQUAL(0, enchant_dict_check(_pwl, "personal", -1)); +} + +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_WordDoesNotExist_InBrokerPwl_1) +{ + CHECK_EQUAL(1, enchant_dict_check(_pwl, "session", -1)); +} + +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockDictionary("something bad happened"); + + enchant_dict_check(_dict, "hello", -1); + CHECK_EQUAL((void*)NULL, (void*)enchant_dict_get_error(_dict)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_NullDictionary_Negative1) +{ + CHECK_EQUAL(-1, enchant_dict_check(NULL, "helo", -1)); + CHECK(!dictCheckCalled); +} + +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_NullWord_Negative1) +{ + CHECK_EQUAL(-1, enchant_dict_check(_dict, NULL, -1)); + CHECK(!dictCheckCalled); +} + +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_EmptyWord_Negative1) +{ + CHECK_EQUAL(-1, enchant_dict_check(_dict, "", -1)); + CHECK(!dictCheckCalled); +} + +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_WordSize0_Negative1) +{ + CHECK_EQUAL(-1, enchant_dict_check(_dict, "helo", 0)); + CHECK(!dictCheckCalled); +} + +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_InvalidUtf8Word_Negative1) +{ + CHECK_EQUAL(-1, enchant_dict_check(_dict, "\xa5\xf1\x08", -1)); + CHECK(!dictCheckCalled); +} + +TEST_FIXTURE(EnchantDictionaryCheckNotImplemented_TestFixture, + EnchantDictionaryCheckNotImplemented_Negative1) +{ + CHECK_EQUAL(-1, enchant_dict_check(_dict, "hello", -1)); + CHECK(!dictCheckCalled); +} + +TEST_FIXTURE(EnchantDictionaryCheckNotImplemented_TestFixture, + EnchantDictionaryCheckNotImplemented_InBrokerPwl_1) +{ + CHECK_EQUAL(1, enchant_dict_check(_pwl, "hello", -1)); + CHECK(!dictCheckCalled); +} + +TEST_FIXTURE(EnchantDictionaryCheck_TestFixture, + EnchantDictionaryCheck_WordInDictionaryAndExclude_1) +{ + ExternalAddWordToExclude("hello"); + ExternalAddWordToDictionary("hello"); + + ReloadTestDictionary(); + CHECK_EQUAL(1, enchant_dict_check(_dict, "hello", -1)); +} diff --git a/unittests/dictionary/enchant_dict_describe_tests.cpp b/unittests/dictionary/enchant_dict_describe_tests.cpp new file mode 100644 index 0000000..d083f5e --- /dev/null +++ b/unittests/dictionary/enchant_dict_describe_tests.cpp @@ -0,0 +1,140 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantDictionaryTestFixture.h" + +static bool callbackCalled; + +void EnchantSingleDictionaryDescribeCallback (const char * const lang_tag, + const char * const provider_name, + const char * const provider_desc, + const char * const provider_file, + void * user_data) +{ + DictionaryDescription* description = reinterpret_cast(user_data); + *description = DictionaryDescription(lang_tag, provider_name, provider_desc, provider_file); + callbackCalled = true; +} + +static void * global_user_data; +void EnchantSingleDictionaryDescribeAssignUserDataToStaticCallback (const char * const, + const char * const, + const char * const, + const char * const, + void * user_data) +{ + global_user_data = user_data; + callbackCalled = true; +} + +struct EnchantDictionaryDescribe_TestFixtureBase : EnchantDictionaryTestFixture +{ + //Setup + EnchantDictionaryDescribe_TestFixtureBase(ConfigureHook userConfig): + EnchantDictionaryTestFixture(userConfig) + { + callbackCalled = false; + } + DictionaryDescription _description; +}; + +struct EnchantDictionaryDescribe_TestFixture : EnchantDictionaryDescribe_TestFixtureBase +{ + //Setup + EnchantDictionaryDescribe_TestFixture(): + EnchantDictionaryDescribe_TestFixtureBase(EmptyDictionary_ProviderConfiguration) + { } +}; + + +/** + * enchant_dict_describe + * @broker: A non-null #EnchantDict + * @dict: A non-null #EnchantDictDescribeFn + * @user_data: Optional user-data + * + * Describes an individual dictionary + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +TEST_FIXTURE(EnchantDictionaryDescribe_TestFixture, + EnchantDictionaryDescribe) +{ + enchant_dict_describe(_dict, EnchantSingleDictionaryDescribeCallback, &_description); + CHECK(callbackCalled); + CHECK(_description.DataIsComplete()); + CHECK_EQUAL(std::string("qaa"), _description.LanguageTag); +} + +TEST_FIXTURE(EnchantDictionaryDescribe_TestFixture, + EnchantDictionaryDescribe_Pwl) +{ + enchant_dict_describe(_pwl, EnchantSingleDictionaryDescribeCallback, &_description); + CHECK(callbackCalled); + CHECK(_description.DataIsComplete()); + CHECK_EQUAL(std::string("Personal Wordlist"), _description.LanguageTag); + CHECK_EQUAL(std::string("Personal Wordlist"), _description.Name); + CHECK_EQUAL(std::string("Personal Wordlist"), _description.Description); +} + +TEST_FIXTURE(EnchantDictionaryDescribe_TestFixture, + EnchantDictionaryDescribe_NullUserdata_PassedToCallback) +{ + enchant_dict_describe(_dict, EnchantSingleDictionaryDescribeAssignUserDataToStaticCallback, NULL); + CHECK(callbackCalled); + CHECK_EQUAL((void*)NULL, global_user_data); +} + +TEST_FIXTURE(EnchantDictionaryDescribe_TestFixture, + EnchantDictionaryDescribe_NonNullUserdata_PassedToCallback) +{ + void* userData = "some user data"; + enchant_dict_describe(_dict, EnchantSingleDictionaryDescribeAssignUserDataToStaticCallback, userData); + CHECK(callbackCalled); + CHECK_EQUAL(userData, global_user_data); +} + +TEST_FIXTURE(EnchantDictionaryDescribe_TestFixture, + EnchantDictionaryDescribe_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockDictionary("something bad happened"); + + enchant_dict_describe(_dict, EnchantSingleDictionaryDescribeAssignUserDataToStaticCallback, NULL); + CHECK_EQUAL((void*)NULL, (void*)enchant_dict_get_error(_dict)); +} +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantDictionaryDescribe_TestFixture, + EnchantDictionaryDescribe_NullDict_DoNothing) +{ + enchant_dict_describe(NULL, EnchantSingleDictionaryDescribeCallback, &_description); + CHECK(!callbackCalled); +} + +TEST_FIXTURE(EnchantDictionaryDescribe_TestFixture, + EnchantDictionaryDescribe_NullCallback_DoNothing) +{ + enchant_dict_describe(_dict, NULL, &_description); + CHECK(!callbackCalled); +} \ No newline at end of file diff --git a/unittests/dictionary/enchant_dict_free_string_list_tests.cpp b/unittests/dictionary/enchant_dict_free_string_list_tests.cpp new file mode 100644 index 0000000..a98455d --- /dev/null +++ b/unittests/dictionary/enchant_dict_free_string_list_tests.cpp @@ -0,0 +1,95 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantDictionaryTestFixture.h" + +struct EnchantDictFreeStringList_TestFixture : EnchantDictionaryTestFixture +{ + //setup + EnchantDictFreeStringList_TestFixture() + { + _string_list = enchant_dict_suggest (_dict, "foo", -1, NULL); + _pwl_string_list = enchant_dict_suggest (_pwl, "foo", -1, NULL); + } + //Teardown + ~EnchantDictFreeStringList_TestFixture() + { + FreeStringList(_string_list); + FreeStringList(_pwl_string_list); + } + char ** _string_list; + char ** _pwl_string_list; +}; + +/** + * enchant_dict_free_string_list + * @dict: A non-null #EnchantDict + * @string_list: A non-null string list returned from enchant_dict_suggest + * + * Releases the string list + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +TEST_FIXTURE(EnchantDictFreeStringList_TestFixture, + EnchantDictFreeStringList) +{ + testResults_; + enchant_dict_free_string_list(_dict, _string_list); + _string_list = NULL; +} + +TEST_FIXTURE(EnchantDictFreeStringList_TestFixture, + EnchantDictFreeStringListOnPwl) +{ + testResults_; + enchant_dict_free_string_list(_pwl, _string_list); + _string_list = NULL; +} + +TEST_FIXTURE(EnchantDictFreeStringList_TestFixture, + EnchantDictFreeStringList_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockDictionary("something bad happened"); + + enchant_dict_free_string_list(_dict, _string_list); + _string_list = NULL; + + CHECK_EQUAL((void*)NULL, (void*)enchant_dict_get_error(_dict)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantDictFreeStringList_TestFixture, + EnchantDictFreeStringList_NullDict_DoNothing) +{ + testResults_; + enchant_dict_free_string_list(NULL, _string_list); +} + +TEST_FIXTURE(EnchantDictFreeStringList_TestFixture, + EnchantDictFreeStringList_NullStringList_DoNothing) +{ + testResults_; + enchant_dict_free_string_list(_dict, NULL); +} diff --git a/unittests/dictionary/enchant_dict_get_error_tests.cpp b/unittests/dictionary/enchant_dict_get_error_tests.cpp new file mode 100644 index 0000000..a401bb1 --- /dev/null +++ b/unittests/dictionary/enchant_dict_get_error_tests.cpp @@ -0,0 +1,70 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantDictionaryTestFixture.h" + +/** + * enchant_dict_get_error + * @dict: A non-null dictionary + * + * Returns a const char string or NULL describing the last exception. + * WARNING: error is transient. It will likely be cleared as soon as + * the next dictionary operation is called + * + * Returns: an error message + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST_FIXTURE(EnchantDictionaryTestFixture, + EnchantDictionaryGetError_HasPreviousError_Error) +{ + std::string errorMessage("something bad happened"); + SetErrorOnMockDictionary(errorMessage); + + CHECK_EQUAL(errorMessage.c_str(), enchant_dict_get_error(_dict)); +} + +TEST_FIXTURE(EnchantDictionaryTestFixture, + EnchantDictionaryGetErrorOnPwl_HasPreviousError_Error) +{ + std::string errorMessage("something bad happened"); + enchant_dict_set_error(_pwl, errorMessage.c_str()); + + + CHECK_EQUAL(errorMessage.c_str(), enchant_dict_get_error(_pwl)); +} + +TEST_FIXTURE(EnchantDictionaryTestFixture, + EnchantDictionaryGetError_NoPreviousError_Null) +{ + CHECK_EQUAL((void*)NULL, (void*)enchant_dict_get_error(_dict)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST(EnchantDictionaryGetError_NullBroker_Null) +{ + CHECK_EQUAL((void*)NULL, (void*)enchant_dict_get_error(NULL)); +} \ No newline at end of file diff --git a/unittests/dictionary/enchant_dict_is_added_tests.cpp b/unittests/dictionary/enchant_dict_is_added_tests.cpp new file mode 100644 index 0000000..4005667 --- /dev/null +++ b/unittests/dictionary/enchant_dict_is_added_tests.cpp @@ -0,0 +1,132 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantDictionaryTestFixture.h" + +struct EnchantDictionaryIsAdded_TestFixture : EnchantDictionaryTestFixture +{}; + +/** + * enchant_dict_is_added + * @dict: A non-null #EnchantDict + * @word: The word you wish to see if it's in your session + * @len: the byte length of @word, or -1 for strlen (@word) + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +TEST_FIXTURE(EnchantDictionaryIsAdded_TestFixture, + EnchantDictionaryIsAdded_AddedToSession_1) +{ + enchant_dict_add_to_session(_dict, "hello", -1); + + CHECK_EQUAL(1, enchant_dict_is_added(_dict, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsAdded_TestFixture, + EnchantDictionaryIsAdded_Added_1) +{ + enchant_dict_add(_dict, "hello", -1); + + CHECK_EQUAL(1, enchant_dict_is_added(_dict, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsAdded_TestFixture, + EnchantDictionaryIsAdded_NotAdded_0) +{ + CHECK_EQUAL(0, enchant_dict_is_added(_dict, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsAdded_TestFixture, + EnchantDictionaryIsAdded_OnBrokerPwl_AddedToSession_1) +{ + enchant_dict_add_to_session(_pwl, "hello", -1); + + CHECK_EQUAL(1, enchant_dict_is_added(_pwl, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsAdded_TestFixture, + EnchantDictionaryIsAdded_OnBrokerPwl_Added_1) +{ + enchant_dict_add(_pwl, "hello", -1); + + CHECK_EQUAL(1, enchant_dict_is_added(_pwl, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsAdded_TestFixture, + EnchantDictionaryIsAdded_OnBrokerPwl_NotAdded_0) +{ + CHECK_EQUAL(0, enchant_dict_is_added(_pwl, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsAdded_TestFixture, + EnchantDictionaryIsAdded_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockDictionary("something bad happened"); + + enchant_dict_is_added(_dict, "hello", -1); + CHECK_EQUAL((void*)NULL, (void*)enchant_dict_get_error(_dict)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantDictionaryIsAdded_TestFixture, + EnchantDictionaryIsAdded_NullDictionary_0) +{ + CHECK_EQUAL(0, enchant_dict_is_added(NULL, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsAdded_TestFixture, + EnchantDictionaryIsAdded_NullWord_0) +{ + CHECK_EQUAL(0, enchant_dict_is_added(_dict, NULL, -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsAdded_TestFixture, + EnchantDictionaryIsAdded_EmptyWord_0) +{ + CHECK_EQUAL(0, enchant_dict_is_added(_dict, "", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsAdded_TestFixture, + EnchantDictionaryIsAdded_WordSize0_0) +{ + CHECK_EQUAL(0, enchant_dict_is_added(_dict, "hello", 0)); +} + +TEST_FIXTURE(EnchantDictionaryIsAdded_TestFixture, + EnchantDictionaryIsAdded_InvalidUtf8Word_0) +{ + CHECK_EQUAL(0, enchant_dict_is_added(_dict, "\xa5\xf1\x08", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsAdded_TestFixture, + EnchantDictionaryIsAdded_WordExistsInPwlAndExclude_0) +{ + ExternalAddWordToDictionary("hello"); + ExternalAddWordToExclude("hello"); + + ReloadTestDictionary(); + CHECK_EQUAL(0, enchant_dict_is_added(_dict, "hello", -1)); +} + diff --git a/unittests/dictionary/enchant_dict_is_removed_tests.cpp b/unittests/dictionary/enchant_dict_is_removed_tests.cpp new file mode 100644 index 0000000..8efdd2c --- /dev/null +++ b/unittests/dictionary/enchant_dict_is_removed_tests.cpp @@ -0,0 +1,134 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantDictionaryTestFixture.h" + +struct EnchantDictionaryIsRemoved_TestFixture : EnchantDictionaryTestFixture +{}; + +/** + * enchant_dict_is_removed + * @dict: A non-null #EnchantDict + * @word: The word you wish to query + * @len: the byte length of @word, or -1 for strlen (@word) + * + * returns: 1 if word has been removed (from session or permanently) + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +TEST_FIXTURE(EnchantDictionaryIsRemoved_TestFixture, + EnchantDictionaryIsRemoved_RemovedFromSession_1) +{ + enchant_dict_remove_from_session(_dict, "hello", -1); + + CHECK_EQUAL(1, enchant_dict_is_removed(_dict, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsRemoved_TestFixture, + EnchantDictionaryIsRemoved_Removed_1) +{ + enchant_dict_remove(_dict, "hello", -1); + + CHECK_EQUAL(1, enchant_dict_is_removed(_dict, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsRemoved_TestFixture, + EnchantDictionaryIsRemoved_NotRemoved_0) +{ + CHECK_EQUAL(0, enchant_dict_is_removed(_dict, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsRemoved_TestFixture, + EnchantDictionaryIsRemoved_OnBrokerPwl_RemovedFromSession_1) +{ + enchant_dict_remove_from_session(_pwl, "hello", -1); + + CHECK_EQUAL(1, enchant_dict_is_removed(_pwl, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsRemoved_TestFixture, + EnchantDictionaryIsRemoved_OnBrokerPwl_Removed_1) +{ + enchant_dict_remove(_pwl, "hello", -1); + + CHECK_EQUAL(1, enchant_dict_is_removed(_pwl, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsRemoved_TestFixture, + EnchantDictionaryIsRemoved_OnBrokerPwl_NotRemoved_0) +{ + CHECK_EQUAL(0, enchant_dict_is_removed(_pwl, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsRemoved_TestFixture, + EnchantDictionaryIsRemoved_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockDictionary("something bad happened"); + + enchant_dict_is_removed(_dict, "hello", -1); + CHECK_EQUAL((void*)NULL, (void*)enchant_dict_get_error(_dict)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantDictionaryIsRemoved_TestFixture, + EnchantDictionaryIsRemoved_NullDictionary_0) +{ + CHECK_EQUAL(0, enchant_dict_is_removed(NULL, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsRemoved_TestFixture, + EnchantDictionaryIsRemoved_NullWord_0) +{ + CHECK_EQUAL(0, enchant_dict_is_removed(_dict, NULL, -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsRemoved_TestFixture, + EnchantDictionaryIsRemoved_EmptyWord_0) +{ + CHECK_EQUAL(0, enchant_dict_is_removed(_dict, "", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsRemoved_TestFixture, + EnchantDictionaryIsRemoved_WordSize0_0) +{ + CHECK_EQUAL(0, enchant_dict_is_removed(_dict, "hello", 0)); +} + +TEST_FIXTURE(EnchantDictionaryIsRemoved_TestFixture, + EnchantDictionaryIsRemoved_InvalidUtf8Word_0) +{ + CHECK_EQUAL(0, enchant_dict_is_removed(_dict, "\xa5\xf1\x08", -1)); +} + +TEST_FIXTURE(EnchantDictionaryIsRemoved_TestFixture, + EnchantDictionaryIsRemoved_WordExistsInPwlAndExclude_1) +{ + ExternalAddWordToExclude("hello"); + ExternalAddWordToDictionary("hello"); + + ReloadTestDictionary(); + CHECK_EQUAL(1, enchant_dict_is_removed(_dict, "hello", -1)); +} + diff --git a/unittests/dictionary/enchant_dict_remove_from_session_tests.cpp b/unittests/dictionary/enchant_dict_remove_from_session_tests.cpp new file mode 100644 index 0000000..eeb2053 --- /dev/null +++ b/unittests/dictionary/enchant_dict_remove_from_session_tests.cpp @@ -0,0 +1,194 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#define NOMINMAX //don't want windows to collide with std::min + +#include +#include +#include "../EnchantDictionaryTestFixture.h" + +static bool dictCheckCalled; + +static int +MockDictionaryCheck (EnchantDict * dict, const char *const word, size_t len) +{ + dict; + dictCheckCalled = true; + if(strncmp("hello", word, len)==0) + { + return 0; //good word + } + return 1; // bad word +} + + +static EnchantDict* MockProviderRequestCheckMockDictionary(EnchantProvider * me, const char *tag) +{ + + EnchantDict* dict = MockProviderRequestBasicMockDictionary(me, tag); + dict->check = MockDictionaryCheck; + return dict; +} + +static void DictionaryCheck_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = MockProviderRequestCheckMockDictionary; + me->dispose_dict = MockProviderDisposeDictionary; +} + +struct EnchantDictionaryRemoveFromSession_TestFixture : EnchantDictionaryTestFixture +{ + //Setup + EnchantDictionaryRemoveFromSession_TestFixture(): + EnchantDictionaryTestFixture(DictionaryCheck_ProviderConfiguration) + { + dictCheckCalled = false; + } +}; + +/** + * enchant_dict_remove_from_session + * @dict: A non-null #EnchantDict + * @word: The non-null word you wish to exclude from this spell-checking session, in UTF-8 encoding + * @len: The byte length of @word, or -1 for strlen (@word) + * + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +TEST_FIXTURE(EnchantDictionaryRemoveFromSession_TestFixture, + EnchantDictionaryRemoveFromSession_WordNotAddedToEnchantExcludeFile) +{ + enchant_dict_remove_from_session(_dict, "hello", -1); + CHECK(!ExcludeFileHasContents()); +} + +TEST_FIXTURE(EnchantDictionaryRemoveFromSession_TestFixture, + EnchantDictionaryRemoveFromSession_WordRemovedFromSession) +{ + enchant_dict_add_to_session(_dict, "hello", -1); + CHECK(IsWordInSession("hello")); + enchant_dict_remove_from_session(_dict, "hello", -1); + CHECK(!IsWordInSession("hello")); +} + +TEST_FIXTURE(EnchantDictionaryRemoveFromSession_TestFixture, + EnchantDictionaryRemoveFromSession_CalledTwice) +{ + enchant_dict_add_to_session(_dict, "hello", -1); + enchant_dict_remove_from_session(_dict, "hello", -1); + enchant_dict_remove_from_session(_dict, "hello", -1); + CHECK(!IsWordInSession("hello")); +} + +TEST_FIXTURE(EnchantDictionaryRemoveFromSession_TestFixture, + EnchantDictionaryRemoveFromSession_WordExcludedFromDictionary) +{ + enchant_dict_remove_from_session(_dict, "hello", -1); + CHECK(!IsWordInDictionary("hello")); +} + +TEST_FIXTURE(EnchantDictionaryRemoveFromSession_TestFixture, + EnchantDictionaryRemoveFromSession_InBrokerSession_WordExcludedFromBrokerSession) +{ + enchant_dict_add_to_session(_pwl, "hello", -1); + CHECK(enchant_dict_is_in_session(_pwl, "hello", -1)); + enchant_dict_remove_from_session(_pwl, "hello", -1); + CHECK(!enchant_dict_is_in_session(_pwl, "hello", -1)); +} + +TEST_FIXTURE(EnchantDictionaryRemoveFromSession_TestFixture, + EnchantDictionaryRemoveFromSession_IsNotPermanent) +{ + enchant_dict_remove_from_session(_dict, "hello", -1); + CHECK(!IsWordInDictionary("hello")); + + ReloadTestDictionary(); + + CHECK(IsWordInDictionary("hello")); +} + +TEST_FIXTURE(EnchantDictionaryRemoveFromSession_TestFixture, + EnchantDictionaryRemoveFromSession_NotGivenAsSuggestion) +{ + enchant_dict_remove_from_session(_dict, "aelo", -1); + + std::vector suggestions = GetSuggestions("helo"); + CHECK_EQUAL(3, suggestions.size()); + CHECK_ARRAY_EQUAL(GetExpectedSuggestions("helo",1), suggestions, std::min(3u,suggestions.size()) ); +} + +TEST_FIXTURE(EnchantDictionaryRemoveFromSession_TestFixture, + EnchantDictionaryRemoveFromSession_ThenAddedToSession_GivenAsSuggestion) +{ + enchant_dict_remove_from_session(_dict, "aelo", -1); + enchant_dict_add_to_session(_dict, "aelo", -1); + + std::vector suggestions = GetSuggestions("helo"); + CHECK_EQUAL(4, suggestions.size()); + CHECK_ARRAY_EQUAL(GetExpectedSuggestions("helo"), suggestions, suggestions.size()); +} + +TEST_FIXTURE(EnchantDictionaryRemoveFromSession_TestFixture, + EnchantDictionaryRemoveFromSession_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockDictionary("something bad happened"); + + enchant_dict_remove_from_session(_dict, "hello", -1); + CHECK_EQUAL((void*)NULL, (void*)enchant_dict_get_error(_dict)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantDictionaryRemoveFromSession_TestFixture, + EnchantDictionaryRemoveFromSession_NullDictionary_NotRemoved) +{ + enchant_dict_remove_from_session(NULL, "hello", -1); + CHECK(IsWordInDictionary("hello")); +} + +TEST_FIXTURE(EnchantDictionaryRemoveFromSession_TestFixture, + EnchantDictionaryRemoveFromSession_NullWord_NotRemoved) +{ + enchant_dict_remove_from_session(_dict, NULL, -1); + CHECK(IsWordInDictionary("hello")); +} + +TEST_FIXTURE(EnchantDictionaryRemoveFromSession_TestFixture, + EnchantDictionaryRemoveFromSession_EmptyWord_NotRemoved) +{ + enchant_dict_remove_from_session(_dict, "", -1); + CHECK(IsWordInDictionary("hello")); +} + +TEST_FIXTURE(EnchantDictionaryRemoveFromSession_TestFixture, + EnchantDictionaryRemoveFromSession_WordSize0_NotRemoved) +{ + enchant_dict_remove_from_session(_dict, "hello", 0); + CHECK(IsWordInDictionary("hello")); +} + +TEST_FIXTURE(EnchantDictionaryRemoveFromSession_TestFixture, + EnchantDictionaryRemoveFromSession_InvalidUtf8Word_NotRemoved) +{ + enchant_dict_remove_from_session(_dict, "\xa5\xf1\x08", -1); + CHECK(IsWordInDictionary("hello")); +} diff --git a/unittests/dictionary/enchant_dict_remove_tests.cpp b/unittests/dictionary/enchant_dict_remove_tests.cpp new file mode 100644 index 0000000..5d2f730 --- /dev/null +++ b/unittests/dictionary/enchant_dict_remove_tests.cpp @@ -0,0 +1,336 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#define NOMINMAX //don't want windows to collide with std::min + +#include +#include +#include "../EnchantDictionaryTestFixture.h" + +static bool addToExcludeCalled; +static std::string wordToAdd; + +static void +MockDictionaryAddToExclude (EnchantDict * dict, const char *const word, size_t len) +{ + dict; + addToExcludeCalled = true; + wordToAdd = std::string(word, len); +} + +static bool dictCheckCalled; + +static int +MockDictionaryCheck (EnchantDict * dict, const char *const word, size_t len) +{ + dict; + dictCheckCalled = true; + if(strncmp("hello", word, len)==0) + { + return 0; //good word + } + return 1; // bad word +} + + +static EnchantDict* MockProviderRequestAddToExcludeMockDictionary(EnchantProvider * me, const char *tag) +{ + + EnchantDict* dict = MockProviderRequestBasicMockDictionary(me, tag); + dict->add_to_exclude = MockDictionaryAddToExclude; + dict->check = MockDictionaryCheck; + return dict; +} + +static void DictionaryAddToExclude_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = MockProviderRequestAddToExcludeMockDictionary; + me->dispose_dict = MockProviderDisposeDictionary; +} + +struct EnchantDictionaryRemove_TestFixture : EnchantDictionaryTestFixture +{ + //Setup + EnchantDictionaryRemove_TestFixture(): + EnchantDictionaryTestFixture(DictionaryAddToExclude_ProviderConfiguration) + { + addToExcludeCalled = false; + dictCheckCalled = false; + } +}; + +static EnchantDict* MockProviderRequestCheckMockDictionary(EnchantProvider * me, const char *tag) +{ + + EnchantDict* dict = MockProviderRequestEmptyMockDictionary(me, tag); + dict->check = MockDictionaryCheck; + return dict; +} + +static void DictionaryCheck_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = MockProviderRequestCheckMockDictionary; + me->dispose_dict = MockProviderDisposeDictionary; +} + +struct EnchantDictionaryRemoveNotImplemented_TestFixture : EnchantDictionaryTestFixture +{ + //Setup + EnchantDictionaryRemoveNotImplemented_TestFixture(): + EnchantDictionaryTestFixture(DictionaryCheck_ProviderConfiguration) + { + addToExcludeCalled = false; + dictCheckCalled = false; + } +}; +/** + * enchant_dict_remove + * @dict: A non-null #EnchantDict + * @word: The non-null word you wish to add to your exclude dictionary and + * remove from the personal dictionary, in UTF-8 encoding + * @len: The byte length of @word, or -1 for strlen (@word) + * + */ + + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_WordNoLongerExistsInDictionary) +{ + CHECK(IsWordInDictionary("hello")); + enchant_dict_remove(_dict, "hello", -1); + CHECK(!IsWordInDictionary("hello")); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_WordNoLongerExistsInSession) +{ + enchant_dict_add(_dict, "hello", -1); + CHECK(IsWordInSession("hello")); + enchant_dict_remove(_dict, "hello", -1); + CHECK(!IsWordInSession("hello")); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_WordAddedToEnchantExcludeFile) +{ + CHECK(!ExcludeFileHasContents()); + enchant_dict_remove(_dict, "hello", -1); + CHECK(ExcludeFileHasContents()); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_WordRemovedFromEnchantPwlFile) +{ + enchant_dict_add(_dict, "hello", -1); + CHECK(PersonalWordListFileHasContents()); + enchant_dict_remove(_dict, "hello", -1); + CHECK(!PersonalWordListFileHasContents()); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_PassedOnToProvider_LenComputed) +{ + enchant_dict_remove(_dict, "hello", -1); + CHECK(addToExcludeCalled); + CHECK_EQUAL(std::string("hello"), wordToAdd); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_PassedOnToProvider_LenSpecified) +{ + enchant_dict_remove(_dict, "hellodisregard me", 5); + CHECK(addToExcludeCalled); + CHECK_EQUAL(std::string("hello"), wordToAdd); +} + + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_WordRemovedFromSession_StillCallsProvider) +{ + enchant_dict_remove_from_session(_dict, "hello", -1); + CHECK(!addToExcludeCalled); + + enchant_dict_remove(_dict, "hello", -1); + CHECK(addToExcludeCalled); + CHECK_EQUAL(std::string("hello"), wordToAdd); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_WordExistsInExclude_StillCallsProvider) +{ + enchant_dict_remove(_dict, "hello", -1); + addToExcludeCalled=false; + wordToAdd = std::string(); + + enchant_dict_remove(_dict, "hello", -1); + CHECK(addToExcludeCalled); + CHECK_EQUAL(std::string("hello"), wordToAdd); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_ProviderNotAskedIfWordExistsInDictionary) +{ + enchant_dict_remove(_dict, "hello", -1); + CHECK(!IsWordInDictionary("hello")); + CHECK(!dictCheckCalled); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_NotGivenAsSuggestion) +{ + enchant_dict_remove(_dict, "aelo", -1); + + std::vector suggestions = GetSuggestions("helo"); + CHECK_EQUAL(3, suggestions.size()); + CHECK_ARRAY_EQUAL(GetExpectedSuggestions("helo",1), suggestions, std::min(3u,suggestions.size())); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_ThenAddedToSession_GivenAsSuggestion) +{ + enchant_dict_remove(_dict, "aelo", -1); + enchant_dict_add_to_session(_dict, "aelo", -1); + + std::vector suggestions = GetSuggestions("helo"); + CHECK_EQUAL(4, suggestions.size()); + CHECK_ARRAY_EQUAL(GetExpectedSuggestions("helo"), suggestions, suggestions.size()); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_ThenAddedToSession_WhenSessionOverStillExcluded) +{ + enchant_dict_remove(_dict, "aelo", -1); + enchant_dict_add_to_session(_dict, "aelo", -1); + + ReloadTestDictionary(); // to see what actually persisted + + std::vector suggestions = GetSuggestions("helo"); + CHECK_EQUAL(3, suggestions.size()); + CHECK_ARRAY_EQUAL(GetExpectedSuggestions("helo",1), suggestions, std::min(3u,suggestions.size())); +} + +/* since the broker pwl file is a read/write file (there is no readonly dictionary in addition) + * there is no need for complementary exclude file to add a word to. The word just needs to be + * removed from the broker pwl file + */ +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_OnBrokerPwl_RemovedFromBrokerPwlFile) +{ + CHECK(!BrokerPWLFileHasContents()); + enchant_dict_add(_pwl, "personal", -1); + CHECK(BrokerPWLFileHasContents()); + + enchant_dict_remove(_pwl, "personal", -1); + CHECK(!BrokerPWLFileHasContents()); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_OnBrokerPwl_NotAddedToOtherPwlFile) +{ + CHECK(!BrokerPWLFileHasContents()); + + enchant_dict_remove(_pwl, "personal", -1); + CHECK(!addToExcludeCalled); + CHECK(!ExcludeFileHasContents()); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_IsPermanent) +{ + enchant_dict_remove(_dict, "hello", -1); + CHECK(!IsWordInDictionary("hello")); + + ReloadTestDictionary(); + + CHECK(!IsWordInDictionary("hello")); +} + + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockDictionary("something bad happened"); + + enchant_dict_remove(_dict, "hello", -1); + CHECK_EQUAL((void*)NULL, (void*)enchant_dict_get_error(_dict)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_NullDictionary_NotRemoved) +{ + enchant_dict_remove(NULL, "hello", -1); + CHECK(!addToExcludeCalled); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_NullWord_NotRemoved) +{ + enchant_dict_remove(_dict, NULL, -1); + CHECK(!addToExcludeCalled); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_EmptyWord_NotRemoved) +{ + enchant_dict_remove(_dict, "", -1); + CHECK(!addToExcludeCalled); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_WordSize0_NotRemoved) +{ + enchant_dict_remove(_dict, "hello", 0); + CHECK(!addToExcludeCalled); +} + +TEST_FIXTURE(EnchantDictionaryRemove_TestFixture, + EnchantDictionaryRemove_InvalidUtf8Word_NotRemoved) +{ + enchant_dict_remove(_dict, "\xa5\xf1\x08", -1); + CHECK(!addToExcludeCalled); +} + +TEST_FIXTURE(EnchantDictionaryRemoveNotImplemented_TestFixture, + EnchantDictionaryRemoveNotImplemented_WordAddedToExcludeFile) +{ + CHECK(!ExcludeFileHasContents()); + enchant_dict_remove(_dict, "hello", -1); + CHECK(ExcludeFileHasContents()); +} + +TEST_FIXTURE(EnchantDictionaryRemoveNotImplemented_TestFixture, + EnchantDictionaryRemoveNotImplemented_NotPassedOnToProvider) +{ + enchant_dict_remove(_dict, "hello", -1); + CHECK(!addToExcludeCalled); +} + +TEST_FIXTURE(EnchantDictionaryRemoveNotImplemented_TestFixture, + EnchantDictionaryRemoveNotImplemented_StillExcluded) +{ + CHECK(IsWordInDictionary("hello")); + enchant_dict_remove(_dict, "hello", -1); + CHECK(!IsWordInDictionary("hello")); +} diff --git a/unittests/dictionary/enchant_dict_store_replacement_tests.cpp b/unittests/dictionary/enchant_dict_store_replacement_tests.cpp new file mode 100644 index 0000000..3a8c514 --- /dev/null +++ b/unittests/dictionary/enchant_dict_store_replacement_tests.cpp @@ -0,0 +1,228 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantDictionaryTestFixture.h" + +struct EnchantDictionaryStoreReplacement_TestFixture : EnchantDictionaryTestFixture +{ + static std::string misspelling; + static std::string correction; + + static bool storeReplacementCalled; + + //Setup + EnchantDictionaryStoreReplacement_TestFixture(): + EnchantDictionaryTestFixture(DictionaryStoreReplacement_ProviderConfiguration) + { + storeReplacementCalled = false; + misspelling.clear(); + correction.clear(); + } + + static void + MockDictionaryStoreReplacement (EnchantDict *, + const char *const mis, size_t mis_len, + const char *const cor, size_t cor_len) + { + misspelling = std::string(mis, mis_len); + correction = std::string(cor, cor_len); + storeReplacementCalled = true; + } + + static EnchantDict* + MockProviderRequestStoreReplacementMockDictionary(EnchantProvider * me, const char *tag) + { + + EnchantDict* dict = MockProviderRequestEmptyMockDictionary(me, tag); + dict->store_replacement = MockDictionaryStoreReplacement; + return dict; + } + + static void DictionaryStoreReplacement_ProviderConfiguration (EnchantProvider * me, const char *) + { + me->request_dict = MockProviderRequestStoreReplacementMockDictionary; + me->dispose_dict = MockProviderDisposeDictionary; + } +}; +bool EnchantDictionaryStoreReplacement_TestFixture::storeReplacementCalled; +std::string EnchantDictionaryStoreReplacement_TestFixture::misspelling; +std::string EnchantDictionaryStoreReplacement_TestFixture::correction; + +struct EnchantDictionaryLacksStoreReplacement_TestFixture : EnchantDictionaryTestFixture +{ + //Setup + EnchantDictionaryLacksStoreReplacement_TestFixture(): + EnchantDictionaryTestFixture(EmptyDictionary_ProviderConfiguration) + { } +}; + + + + +/** + * enchant_dict_store_replacement + * @dict: A non-null #EnchantDict + * @mis: The non-null word you wish to add a correction for, in UTF-8 encoding + * @mis_len: The byte length of @mis, or -1 for strlen (@mis) + * @cor: The non-null correction word, in UTF-8 encoding + * @cor_len: The byte length of @cor, or -1 for strlen (@cor) + * + * Notes that you replaced @mis with @cor, so it's possibly more likely + * that future occurrences of @mis will be replaced with @cor. So it might + * bump @cor up in the suggestion list. + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +TEST_FIXTURE(EnchantDictionaryStoreReplacement_TestFixture, + EnchantDictStoreReplacment_ExplicitWordLength) +{ + std::string misspelling("helo"); + std::string correction("hello"); + enchant_dict_store_replacement(_dict, + misspelling.c_str(), + misspelling.size(), + correction.c_str(), + correction.size()); + CHECK(storeReplacementCalled); + CHECK_EQUAL(EnchantDictionaryStoreReplacement_TestFixture::misspelling, misspelling); + CHECK_EQUAL(EnchantDictionaryStoreReplacement_TestFixture::correction, correction); +} + +TEST_FIXTURE(EnchantDictionaryStoreReplacement_TestFixture, + EnchantDictStoreReplacment_ComputedWordLength) +{ + std::string misspelling("helo"); + std::string correction("hello"); + enchant_dict_store_replacement(_dict, + misspelling.c_str(), + -1, + correction.c_str(), + -1); + CHECK(storeReplacementCalled); + CHECK_EQUAL(EnchantDictionaryStoreReplacement_TestFixture::misspelling, misspelling); + CHECK_EQUAL(EnchantDictionaryStoreReplacement_TestFixture::correction, correction); +} + +TEST_FIXTURE(EnchantDictionaryStoreReplacement_TestFixture, + EnchantDictStoreReplacment_OnBrokerPwl) +{ + std::string misspelling("helo"); + std::string correction("hello"); + enchant_dict_store_replacement(_pwl, + misspelling.c_str(), + -1, + correction.c_str(), + -1); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantDictionaryStoreReplacement_TestFixture, + EnchantDictStoreReplacment_NullDict_DoNothing) +{ + enchant_dict_store_replacement(NULL, "helo", -1, "hello", -1); + CHECK(!storeReplacementCalled); +} + +TEST_FIXTURE(EnchantDictionaryStoreReplacement_TestFixture, + EnchantDictStoreReplacment_NullMisspelling_DoNothing) +{ + enchant_dict_store_replacement(_dict, NULL, -1, "hello", -1); + CHECK(!storeReplacementCalled); +} + +TEST_FIXTURE(EnchantDictionaryStoreReplacement_TestFixture, + EnchantDictStoreReplacment_NullCorrection_DoNothing) +{ + enchant_dict_store_replacement(_dict, "helo", -1, NULL, -1); + CHECK(!storeReplacementCalled); +} + +TEST_FIXTURE(EnchantDictionaryStoreReplacement_TestFixture, + EnchantDictStoreReplacment_EmptyMisspelling_DoNothing) +{ + enchant_dict_store_replacement(_dict, "", -1, "hello", -1); + CHECK(!storeReplacementCalled); +} + +TEST_FIXTURE(EnchantDictionaryStoreReplacement_TestFixture, + EnchantDictStoreReplacment_EmptyCorrection_DoNothing) +{ + enchant_dict_store_replacement(_dict, "helo", -1, "", -1); + CHECK(!storeReplacementCalled); +} + +TEST_FIXTURE(EnchantDictionaryStoreReplacement_TestFixture, + EnchantDictStoreReplacment_ZeroMisspellingLength_DoNothing) +{ + enchant_dict_store_replacement(_dict, "helo", 0, "hello", -1); + CHECK(!storeReplacementCalled); +} + +TEST_FIXTURE(EnchantDictionaryStoreReplacement_TestFixture, + EnchantDictStoreReplacment_ZeroCorrectionLength_DoNothing) +{ + enchant_dict_store_replacement(_dict, "helo", -1, "hello", 0); + CHECK(!storeReplacementCalled); +} + +TEST_FIXTURE(EnchantDictionaryStoreReplacement_TestFixture, + EnchantDictStoreReplacement_InvalidUtf8Misspelling_DoNothing) +{ + enchant_dict_store_replacement(_dict, "\xa5\xf1\x08", -1, "hello", -1); + CHECK(!storeReplacementCalled); +} + +TEST_FIXTURE(EnchantDictionaryStoreReplacement_TestFixture, + EnchantDictStoreReplacement_InvalidUtf8Correction_DoNothing) +{ + enchant_dict_store_replacement(_dict, "helo", -1, "\xa5\xf1\x08", -1); + CHECK(!storeReplacementCalled); +} + +TEST_FIXTURE(EnchantDictionaryLacksStoreReplacement_TestFixture, + EnchantDictStoreReplacment_ProviderLacksStoreReplacement_DoNothing) +{ + testResults_; + enchant_dict_store_replacement(_dict, "helo", -1, "hello", -1); +} + +TEST_FIXTURE(EnchantDictionaryStoreReplacement_TestFixture, + EnchantDictStoreReplacment_ExplicitWordLengthDoesNotCoincideWithNulTerminator) +{ + std::string misspelling("helo1"); + std::string correction("hello1"); + enchant_dict_store_replacement(_dict, + misspelling.c_str(), + misspelling.size()-1, + correction.c_str(), + correction.size()-1); + + misspelling.resize(misspelling.size()-1); + correction.resize(correction.size()-1); + + CHECK(storeReplacementCalled); + CHECK_EQUAL(EnchantDictionaryStoreReplacement_TestFixture::misspelling, misspelling); + CHECK_EQUAL(EnchantDictionaryStoreReplacement_TestFixture::correction, correction); +} diff --git a/unittests/dictionary/enchant_dict_suggest_tests.cpp b/unittests/dictionary/enchant_dict_suggest_tests.cpp new file mode 100644 index 0000000..3c486c9 --- /dev/null +++ b/unittests/dictionary/enchant_dict_suggest_tests.cpp @@ -0,0 +1,463 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#define NOMINMAX //don't want windows to collide with std::min +#include +#include +#include "../EnchantDictionaryTestFixture.h" +#include +#include + +static bool dictSuggestCalled; +static bool providerFreeStringListCalled; +std::string suggestWord; + +static enum SuggestBehavior{ + returnNull, + returnZero, + returnFour, + returnFourOneInvalidUtf8, + returnFianceNfc +} suggestBehavior; + +struct EnchantDictionarySuggestTestFixtureBase : EnchantDictionaryTestFixture +{ + //Setup + EnchantDictionarySuggestTestFixtureBase(ConfigureHook userConfiguration): + EnchantDictionaryTestFixture(userConfiguration) + { + dictSuggestCalled = false; + providerFreeStringListCalled = false; + _suggestions = NULL; + suggestWord = std::string(); + suggestBehavior = returnFour; + } + //Teardown + ~EnchantDictionarySuggestTestFixtureBase() + { + FreeStringList(_suggestions); + } + + char** _suggestions; +}; + +static char ** +MyMockDictionarySuggest (EnchantDict * dict, const char *const word, size_t len, size_t * out_n_suggs) +{ + dictSuggestCalled = true; + suggestWord = std::string(word,len); + *out_n_suggs = 0; + char **sugg_arr = NULL; + + switch(suggestBehavior) + { + case returnNull: + sugg_arr = NULL; + break; + case returnZero: + sugg_arr = g_new0 (char *, *out_n_suggs + 1); + break; + case returnFianceNfc: + *out_n_suggs = 1; + sugg_arr = g_new0 (char *, *out_n_suggs + 1); + sugg_arr[0] = g_strdup ("fianc\xc3\xa9"); // c3 a9 = utf8 for u00e9 = Latin small letter e with acute + break; + case returnFour: + sugg_arr = MockDictionarySuggest(dict, word, len, out_n_suggs); + break; + case returnFourOneInvalidUtf8: + sugg_arr = MockDictionarySuggest(dict, word, len, out_n_suggs); + g_free(sugg_arr[0]); + sugg_arr[0] = g_strdup ("\xa5\xf1\x08"); + break; + } + + return sugg_arr; +} + +static void +MyMockProviderFreeStringList (EnchantProvider * provider, char **str_list) +{ + providerFreeStringListCalled = true; + return MockProviderFreeStringList(provider, str_list); +} + +static EnchantDict* MockProviderRequestSuggestMockDictionary(EnchantProvider * me, const char *tag) +{ + + EnchantDict* dict = MockProviderRequestEmptyMockDictionary(me, tag); + dict->suggest = MyMockDictionarySuggest; + return dict; +} + +static void DictionarySuggest_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = MockProviderRequestSuggestMockDictionary; + me->dispose_dict = MockProviderDisposeDictionary; + me->free_string_list = MyMockProviderFreeStringList; +} + + + + +struct EnchantDictionarySuggest_TestFixture : EnchantDictionarySuggestTestFixtureBase +{ + //Setup + EnchantDictionarySuggest_TestFixture(): + EnchantDictionarySuggestTestFixtureBase(DictionarySuggest_ProviderConfiguration) + { } +}; + + + + +static EnchantDict* MockProviderRequestNoSuggestMockDictionary(EnchantProvider * me, const char *tag) +{ + + EnchantDict* dict = MockProviderRequestEmptyMockDictionary(me, tag); + dict->suggest = NULL; + return dict; +} + +static void DictionaryNoSuggest_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = MockProviderRequestNoSuggestMockDictionary; + me->dispose_dict = MockProviderDisposeDictionary; + me->free_string_list = MyMockProviderFreeStringList; +} + +struct EnchantDictionarySuggestNotImplemented_TestFixture : EnchantDictionarySuggestTestFixtureBase +{ + //Setup + EnchantDictionarySuggestNotImplemented_TestFixture(): + EnchantDictionarySuggestTestFixtureBase(DictionaryNoSuggest_ProviderConfiguration) + { } +}; + + +static EnchantDict* MockProviderRequestNoFreeMockDictionary(EnchantProvider * me, const char *tag) +{ + + EnchantDict* dict = MockProviderRequestEmptyMockDictionary(me, tag); + dict->suggest = MyMockDictionarySuggest; + return dict; +} + +static void DictionaryNoFree_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = MockProviderRequestNoFreeMockDictionary; + me->dispose_dict = MockProviderDisposeDictionary; + me->free_string_list = NULL; +} + +struct EnchantDictionaryFreeNotImplemented_TestFixture : EnchantDictionarySuggestTestFixtureBase +{ + //Setup + EnchantDictionaryFreeNotImplemented_TestFixture(): + EnchantDictionarySuggestTestFixtureBase(DictionaryNoFree_ProviderConfiguration) + { } +}; + + + + +/** + * enchant_dict_suggest + * @dict: A non-null #EnchantDict + * @word: The non-null word you wish to find suggestions for, in UTF-8 encoding + * @len: The byte length of @word, or -1 for strlen (@word) + * @out_n_suggs: The location to store the # of suggestions returned, or %null + * + * Will return an %null value if any of those pre-conditions + * are not met. + * + * Returns: A %null terminated list of UTF-8 encoded suggestions, or %null + */ +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_LenComputed) +{ + size_t cSuggestions; + _suggestions = enchant_dict_suggest(_dict, "helo", -1, &cSuggestions); + CHECK(_suggestions); + CHECK_EQUAL(std::string("helo"), suggestWord); + CHECK_EQUAL(4, cSuggestions); + + std::vector suggestions; + if(_suggestions != NULL){ + suggestions.insert(suggestions.begin(), _suggestions, _suggestions+cSuggestions); + } + + CHECK_ARRAY_EQUAL(GetExpectedSuggestions("helo"), suggestions, std::min((size_t)4,cSuggestions)); +} + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_LenSpecified) +{ + size_t cSuggestions; + _suggestions = enchant_dict_suggest(_dict, "helodisregard me", 4, &cSuggestions); + CHECK(_suggestions); + CHECK_EQUAL(std::string("helo"), suggestWord); + CHECK_EQUAL(4, cSuggestions); + + std::vector suggestions; + if(_suggestions != NULL){ + suggestions.insert(suggestions.begin(), _suggestions, _suggestions+cSuggestions); + } + + CHECK_ARRAY_EQUAL(GetExpectedSuggestions("helo"), suggestions, std::min((size_t)4,cSuggestions)); +} + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_StringListFreed) +{ + size_t cSuggs; + _suggestions = enchant_dict_suggest(_dict, "helo", -1, &cSuggs); + CHECK(dictSuggestCalled); + CHECK(providerFreeStringListCalled); +} + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_NullOutputSuggestionCount) +{ + _suggestions = enchant_dict_suggest(_dict, "helo", -1, NULL); + CHECK(_suggestions); + CHECK(dictSuggestCalled); +} + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_InBrokerPwlSession) +{ + enchant_dict_add(_pwl, "hello", -1); + _suggestions = enchant_dict_suggest(_pwl, "helo", -1, NULL); + CHECK(_suggestions); + CHECK(!dictSuggestCalled); +} + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_SuggestionsFromPersonal_addedToEnd) +{ + size_t cSuggestions; + enchant_dict_add(_dict, "hello", -1); + _suggestions = enchant_dict_suggest(_dict, "helo", -1, &cSuggestions); + CHECK(_suggestions); + CHECK_EQUAL(5, cSuggestions); + + std::vector suggestions; + if(_suggestions != NULL){ + suggestions.insert(suggestions.begin(), _suggestions, _suggestions+cSuggestions); + } + + std::vector expected = GetExpectedSuggestions("helo"); + expected.push_back("hello"); + + CHECK_ARRAY_EQUAL(expected, suggestions, std::min((size_t)5,cSuggestions)); +} + + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_DuplicateSuggestionsFromPersonal_notIncluded) +{ + size_t cSuggestions; + + enchant_dict_add(_dict, "aelo", -1); + _suggestions = enchant_dict_suggest(_dict, "helo", -1, &cSuggestions); + CHECK(_suggestions); + CHECK_EQUAL(4, cSuggestions); + + std::vector suggestions; + if(_suggestions != NULL){ + suggestions.insert(suggestions.begin(), _suggestions, _suggestions+cSuggestions); + } + + CHECK_ARRAY_EQUAL(GetExpectedSuggestions("helo"), suggestions, std::min((size_t)4,cSuggestions)); +} + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_SuggestionExcluded_Null) +{ + suggestBehavior = returnFianceNfc; + RemoveWordFromDictionary(Convert(L"fianc\xe9")); // u00e9 = Latin small letter e with acute + + _suggestions = enchant_dict_suggest(_dict, "fiance", -1, NULL); + CHECK(!_suggestions); +} + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_HasPreviousError_ErrorCleared) +{ + SetErrorOnMockDictionary("something bad happened"); + + _suggestions = enchant_dict_suggest(_dict, "helo", -1, NULL); + CHECK_EQUAL((void*)NULL, (void*)enchant_dict_get_error(_dict)); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_NullDictionary_NullSuggestions) +{ + _suggestions = enchant_dict_suggest(NULL, "helo", -1, NULL); + + CHECK(!_suggestions); + CHECK(!dictSuggestCalled); + CHECK(!providerFreeStringListCalled); +} + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_NullWord_NullSuggestions) +{ + _suggestions = enchant_dict_suggest(_dict, NULL, -1, NULL); + + CHECK(!_suggestions); + CHECK(!dictSuggestCalled); + CHECK(!providerFreeStringListCalled); +} + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_EmptyWord_NullSuggestions) +{ + _suggestions = enchant_dict_suggest(_dict, "", -1, NULL); + + CHECK(!_suggestions); + CHECK(!dictSuggestCalled); + CHECK(!providerFreeStringListCalled); +} + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_WordSize0_NullSuggestions) +{ + _suggestions = enchant_dict_suggest(_dict, "helo", 0, NULL); + + CHECK(!_suggestions); + CHECK(!dictSuggestCalled); + CHECK(!providerFreeStringListCalled); +} + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_InvalidUtf8Correction_DoNothing) +{ + _suggestions = enchant_dict_suggest(_dict, "\xa5\xf1\x08", -1, NULL); + + CHECK(!_suggestions); + CHECK(!dictSuggestCalled); + CHECK(!providerFreeStringListCalled); +} + + +TEST_FIXTURE(EnchantDictionarySuggestNotImplemented_TestFixture, + EnchantDictionarySuggestNotImplemented_NullSuggestions) +{ + _suggestions = enchant_dict_suggest(_dict, "helo", -1, NULL); + + CHECK(!_suggestions); + CHECK(!dictSuggestCalled); + CHECK(!providerFreeStringListCalled); +} + +TEST_FIXTURE(EnchantDictionaryFreeNotImplemented_TestFixture, + EnchantDictionaryFreeNotImplemented_Suggestions_FreeNotCalled) +{ + _suggestions = enchant_dict_suggest(_dict, "helo", -1, NULL); + + CHECK(_suggestions); + CHECK(dictSuggestCalled); + CHECK(!providerFreeStringListCalled); +} + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_EmptySuggestionList_FreeCalled) +{ + suggestBehavior = returnZero; + size_t cSuggs; + _suggestions = enchant_dict_suggest(_dict, "helo", -1, &cSuggs); + CHECK(!_suggestions); + CHECK_EQUAL(0, cSuggs); + CHECK(dictSuggestCalled); + CHECK(providerFreeStringListCalled); +} + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_NullSuggestionList_FreeNotCalled) +{ + suggestBehavior = returnNull; + size_t cSuggs; + _suggestions = enchant_dict_suggest(_dict, "helo", -1, &cSuggs); + CHECK(!_suggestions); + CHECK_EQUAL(0, cSuggs); + CHECK(dictSuggestCalled); + CHECK(!providerFreeStringListCalled); +} + + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_SuggestionListWithInvalidUtf8_InvalidSuggestionIgnored_FreeCalled) +{ + suggestBehavior = returnFourOneInvalidUtf8; + size_t cSuggestions; + _suggestions = enchant_dict_suggest(_dict, "helo", -1, &cSuggestions); + CHECK(_suggestions); + CHECK(dictSuggestCalled); + CHECK(providerFreeStringListCalled); + + CHECK_EQUAL(3, cSuggestions); + + std::vector suggestions; + if(_suggestions != NULL){ + suggestions.insert(suggestions.begin(), _suggestions, _suggestions+cSuggestions); + } + + CHECK_ARRAY_EQUAL(GetExpectedSuggestions("helo",1), suggestions, std::min((size_t)3,cSuggestions)); +} + +TEST_FIXTURE(EnchantDictionarySuggest_TestFixture, + EnchantDictionarySuggest_WordNfcInDictionaryNfdInPwl_ReturnsFromDict) +{ + suggestBehavior = returnFianceNfc; + + ExternalAddWordToDictionary(Convert(L"fiance\x301")); // NFD u0301 = Combining acute accent + + ReloadTestDictionary(); + + size_t cSuggestions; + _suggestions = enchant_dict_suggest(_dict, "fiance", -1, &cSuggestions); + CHECK(_suggestions); + + CHECK_EQUAL(1, cSuggestions); + CHECK_EQUAL(Convert(L"fianc\xe9"), _suggestions[0]); +} + +TEST_FIXTURE(EnchantDictionarySuggestNotImplemented_TestFixture, + EnchantDictionarySuggest_WordInDictionaryAndExclude_NotInSuggestions) +{ + ExternalAddWordToExclude("hello"); + ExternalAddWordToDictionary("hello"); + + ReloadTestDictionary(); + + size_t cSuggestions; + _suggestions = enchant_dict_suggest(_dict, "helo", -1, &cSuggestions); + CHECK(!_suggestions); + + CHECK_EQUAL(0, cSuggestions); +} + diff --git a/unittests/enchant_providers/Dictionary/dictionary_check.cpp b/unittests/enchant_providers/Dictionary/dictionary_check.cpp new file mode 100644 index 0000000..eca898a --- /dev/null +++ b/unittests/enchant_providers/Dictionary/dictionary_check.cpp @@ -0,0 +1,206 @@ +/* Copyright (c) 2008 Eric Scott Albright + * + * 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. + */ +#include + +#include "../unittest_enchant_providers.h" +#include +#include +#include +#include + +struct DictionaryCheck_TestFixture : Provider_TestFixture +{ + typedef std::multimap AddedWordsByDict; + EnchantDict* _dict; + AddedWordsByDict _addedWordsByDict; + //Setup + DictionaryCheck_TestFixture():_dict(NULL) + { + _dict = GetFirstAvailableDictionary(); + } + + //Teardown + ~DictionaryCheck_TestFixture() + { + ReleaseDictionary(_dict); + } + + virtual void ReleaseDictionary(EnchantDict* dict){ + std::pair + addedWords = _addedWordsByDict.equal_range(dict); + + AddedWordsByDict::const_iterator it; + for(it = addedWords.first; it != addedWords.second; ++it) + { + if(dict->add_to_exclude) + { + (*dict->add_to_exclude)(dict, it->second.c_str(), it->second.length()); + } + } + _addedWordsByDict.erase(dict); + Provider_TestFixture::ReleaseDictionary(dict); + } + + bool IsWordInDictionary(const std::string& word) + { + return IsWordInDictionary(_dict, word); + } + + static bool IsWordInDictionary(EnchantDict* dict, const std::string& word) + { + assert(dict && dict->check); // tests must check this before calling + + return (*dict->check)(dict, word.c_str(), word.length()) == 0; //check returns 0 when successful and 1 when not successful + } + + bool AddWordToDictionary(const std::string& word) + { + return AddWordToDictionary(_dict, word); + } + + bool AddWordToDictionary(EnchantDict* dict, const std::string& word) + { + if(dict == NULL) + { + return false; + } + + if(IsWordInDictionary(dict, word)) + { + return true; + } + + // prefer adding it to the session so it will get automatically removed + if(dict->add_to_session) + { + (*dict->add_to_session) (dict, word.c_str(), word.length()); + if(IsWordInDictionary(word)) + { + return true; + } + } + + if(dict->add_to_personal) + { + _addedWordsByDict.insert(std::pair(dict,word)); + (*dict->add_to_personal) (dict, word.c_str(), word.length()); + if(IsWordInDictionary(word)) + { + return true; + } + } + + return false; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////////////// +// Unicode normalization +TEST_FIXTURE(DictionaryCheck_TestFixture, + IsWordInDictionary_AddedComposed_SuccessfulCheckWithComposedAndDecomposed) +{ + if(_dict && _dict->check) + { + if(AddWordToDictionary(Convert(L"fianc\x00e9" L"deleteme"))) // u00e9 = Latin small letter e with acute + { + CHECK( IsWordInDictionary(Convert(L"fianc\x00e9" L"deleteme")) ); //NFC + CHECK( IsWordInDictionary(Convert(L"fiance\x0301" L"deleteme")) ); //NFD u0301 = Combining acute accent + } + } +} + +TEST_FIXTURE(DictionaryCheck_TestFixture, + IsWordInDictionary_AddedDecomposed_SuccessfulCheckWithComposedAndDecomposed) +{ + if(_dict && _dict->check) + { + if(AddWordToDictionary(Convert(L"fiance\x0301" L"deletethis"))) // u0301 = Combining acute accent + { + CHECK( IsWordInDictionary(Convert(L"fianc\x00e9" L"deletethis")) ); //NFC + CHECK( IsWordInDictionary(Convert(L"fiance\x0301" L"deletethis")) ); //NFD + } + } +} + +TEST_FIXTURE(DictionaryCheck_TestFixture, + IsWordInDictionary_SuccessfulCheckWithComposedAndDecomposed) +{ + EnchantDict* dict = GetDictionary("fr_FR"); + if(dict && dict->check) + { + CHECK( IsWordInDictionary(dict, Convert(L"Fran\x00e7" L"ais")) ); //NFC latin small letter c with cedilla + CHECK( IsWordInDictionary(dict, Convert(L"Franc\x0327" L"ais")) ); //NFD combining cedilla + } + ReleaseDictionary(dict); +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// Capitalization +TEST_FIXTURE(DictionaryCheck_TestFixture, + IsWordInDictionary_AddedAllCaps_OnlyAllCapsSuccessful) +{ + if(_dict && _dict->check) + { + if(AddWordToDictionary("ZYX")) + { + CHECK( IsWordInDictionary("ZYX") ); + CHECK(!IsWordInDictionary("ZYx") ); + CHECK(!IsWordInDictionary("Zyx") ); + CHECK(!IsWordInDictionary("zyx") ); + CHECK(!IsWordInDictionary("zYx") ); + } + } +} + +TEST_FIXTURE(DictionaryCheck_TestFixture, + IsWordInDictionary_AddedTitle_lowerCaseAndMixedCaseNotSuccessful) +{ + if(_dict && _dict->check) + { + if(AddWordToDictionary("Zxyz")) + { + + CHECK( IsWordInDictionary("ZXYZ") ); + CHECK(!IsWordInDictionary("ZXyz") ); + CHECK( IsWordInDictionary("Zxyz") ); + CHECK(!IsWordInDictionary("zxyz") ); + CHECK(!IsWordInDictionary("zXyz") ); + } + } +} + +TEST_FIXTURE(DictionaryCheck_TestFixture, + IsWordInDictionary_Addedlower_MixedCaseNotSuccessful) +{ + if(_dict && _dict->check) + { + if(AddWordToDictionary("zyxz")) + { + CHECK( IsWordInDictionary("ZYXZ") ); + CHECK(!IsWordInDictionary("ZYxz") ); + CHECK( IsWordInDictionary("Zyxz") ); + CHECK( IsWordInDictionary("zyxz") ); + CHECK(!IsWordInDictionary("zYxz") ); + } + } +} diff --git a/unittests/enchant_providers/Dictionary/dictionary_suggest.cpp b/unittests/enchant_providers/Dictionary/dictionary_suggest.cpp new file mode 100644 index 0000000..2949cae --- /dev/null +++ b/unittests/enchant_providers/Dictionary/dictionary_suggest.cpp @@ -0,0 +1,173 @@ +/* Copyright (c) 2008 Eric Scott Albright + * + * 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. + */ +#include + +#include "../unittest_enchant_providers.h" +#include +#include +#include + +struct DictionarySuggest_TestFixture : Provider_TestFixture +{ + EnchantDict* _dict; + std::vector _addedWords; + //Setup + DictionarySuggest_TestFixture():_dict(NULL) + { + _dict = GetFirstAvailableDictionary(); + } + + //Teardown + ~DictionarySuggest_TestFixture() + { + ReleaseDictionary(_dict); + } + + std::vector GetSuggestionsFromWord(EnchantDict* dict, const std::string& word) + { + std::vector result; + if(dict && dict->suggest) + { + size_t cSuggestions; + char** suggestions = (*dict->suggest)(dict, word.c_str(), word.size(), &cSuggestions); + + if(suggestions != NULL){ + result.insert(result.begin(), suggestions, suggestions+cSuggestions); + } + + if(_provider->free_string_list){ + _provider->free_string_list(_provider, suggestions); + } + } + + return result; + } + + std::vector GetSuggestionsFromWord(const std::string& word) + { + return GetSuggestionsFromWord(_dict, word); + } + + bool IsWordAllCaps(const std::string& word) + { + const char* it, *itEnd; + bool hasCap = false; + + for(it = word.c_str(), itEnd = it+word.length(); + it < itEnd; it = g_utf8_next_char(it)) + { + GUnicodeType type = g_unichar_type(g_utf8_get_char(it)); + switch(type) + { + case G_UNICODE_UPPERCASE_LETTER: + hasCap = true; + break; + case G_UNICODE_TITLECASE_LETTER: + case G_UNICODE_LOWERCASE_LETTER: + return false; + } + } + + return hasCap; + } + + bool IsFirstLetterCapitalOrTitleCase(const std::string& word) + { + gunichar ch; + GUnicodeType type; + + ch = g_utf8_get_char(word.c_str()); + + type = g_unichar_type(ch); + if(type == G_UNICODE_UPPERCASE_LETTER || type == G_UNICODE_TITLECASE_LETTER) + return true; + + return false; + } + +}; + +///////////////////////////////////////////////////////////////////////////////////////////////// +TEST_FIXTURE(DictionarySuggest_TestFixture, + Suggest_ReturnsSuggestions) +{ + if(_dict && _dict->suggest) + { + std::vector suggestions = GetSuggestionsFromWord("fiance"); + CHECK(suggestions.size() != 0); + } +} + +TEST_FIXTURE(DictionarySuggest_TestFixture, + Suggest_HandlesNFC) +{ + EnchantDict* dict = GetDictionary("fr_FR"); + if(dict && dict->suggest) + { + std::vector suggestions = GetSuggestionsFromWord(dict, Convert(L"fran\x00e7" L"ais")); //NFC latin small letter c with cedilla + CHECK(suggestions.size() != 0); + } + ReleaseDictionary(dict); +} + +TEST_FIXTURE(DictionarySuggest_TestFixture, + Suggest_HandlesNFD) +{ + EnchantDict* dict = GetDictionary("fr_FR"); + if(dict && dict->suggest) + { + std::vector suggestions = GetSuggestionsFromWord(dict, Convert(L"franc\x0327" L"ais")); //NFD combining cedilla + CHECK(suggestions.size() != 0); + } + ReleaseDictionary(dict); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// Capitalization +TEST_FIXTURE(DictionarySuggest_TestFixture, + GetSuggestions_FromAllCaps_ResultsAllCaps) +{ + if(_dict && _dict->suggest) + { + std::vector suggestions = GetSuggestionsFromWord("AAA"); + for(std::vector::const_iterator i = suggestions.begin(); + i != suggestions.end(); + ++i) + { + CHECK(IsWordAllCaps(*i)); + } + } +} + +TEST_FIXTURE(DictionarySuggest_TestFixture, + GetSuggestions_FromTitle_ResultsTitleOrAllCaps) +{ + if(_dict && _dict->suggest) + { + std::vector suggestions = GetSuggestionsFromWord("Aaa"); + for(std::vector::const_iterator i = suggestions.begin(); + i != suggestions.end(); + ++i) + { + CHECK(IsFirstLetterCapitalOrTitleCase(*i)); + } + } +} diff --git a/unittests/enchant_providers/Provider/provider_describe_dict.cpp b/unittests/enchant_providers/Provider/provider_describe_dict.cpp new file mode 100644 index 0000000..a3b6910 --- /dev/null +++ b/unittests/enchant_providers/Provider/provider_describe_dict.cpp @@ -0,0 +1,68 @@ +/* Copyright (c) 2008 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include + +#include "../unittest_enchant_providers.h" + +struct ProviderDescribe_TestFixture : Provider_TestFixture +{ + //Setup + ProviderDescribe_TestFixture() + { + } + //Teardown + ~ProviderDescribe_TestFixture() + { + } +}; + +// describe is mandatory + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST_FIXTURE(ProviderDescribe_TestFixture, + ProviderDescribe_FunctionExists) +{ + CHECK(_provider->describe != NULL); +} + +TEST_FIXTURE(ProviderDescribe_TestFixture, + ProviderDescribe_ReturnNonNull) +{ + CHECK((*_provider->describe)(_provider) != NULL); +} + +TEST_FIXTURE(ProviderDescribe_TestFixture, + ProviderDescribe_ReturnNotEmpty) +{ + CHECK(strlen((*_provider->describe)(_provider)) != 0); +} + +TEST_FIXTURE(ProviderDescribe_TestFixture, + ProviderDescribe_ReturnsValidUtf8String) +{ + CHECK(g_utf8_validate((*_provider->describe)(_provider), -1, NULL)); +} + \ No newline at end of file diff --git a/unittests/enchant_providers/Provider/provider_dictionary_exists.cpp b/unittests/enchant_providers/Provider/provider_dictionary_exists.cpp new file mode 100644 index 0000000..819444c --- /dev/null +++ b/unittests/enchant_providers/Provider/provider_dictionary_exists.cpp @@ -0,0 +1,61 @@ +/* Copyright (c) 2008 Eric Scott Albright + * + * 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. + */ + +#include +#include + +#include "../unittest_enchant_providers.h" + +struct ProviderDictionaryExists_TestFixture : Provider_TestFixture +{ + //Setup + ProviderDictionaryExists_TestFixture() + { + } + //Teardown + ~ProviderDictionaryExists_TestFixture() + { + } +}; + +// dictionary_exists is optional + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST_FIXTURE(ProviderDictionaryExists_TestFixture, + ProviderDictionaryExists_ProviderDoesNotHave_ReturnsFalse) +{ + if (_provider->dictionary_exists) + { + CHECK_EQUAL(0, (*_provider->dictionary_exists) (_provider, "zxx")); /*zxx is no linguistic content*/ + } +} + +TEST_FIXTURE(ProviderDictionaryExists_TestFixture, + ProviderDictionaryExists_ProviderDoesNotHave_ProviderDoesNotSetError) +{ + if (_provider->dictionary_exists) + { + int exists = (*_provider->dictionary_exists) (_provider, "zxx"); /*zxx is no linguistic content*/ + CHECK_EQUAL((void*)NULL, GetErrorMessage(_provider)); + } +} diff --git a/unittests/enchant_providers/Provider/provider_identify_dict.cpp b/unittests/enchant_providers/Provider/provider_identify_dict.cpp new file mode 100644 index 0000000..9b09042 --- /dev/null +++ b/unittests/enchant_providers/Provider/provider_identify_dict.cpp @@ -0,0 +1,68 @@ +/* Copyright (c) 2008 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include + +#include "../unittest_enchant_providers.h" + +struct ProviderIdentify_TestFixture : Provider_TestFixture +{ + //Setup + ProviderIdentify_TestFixture() + { + } + //Teardown + ~ProviderIdentify_TestFixture() + { + } +}; + +// identify is mandatory + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST_FIXTURE(ProviderIdentify_TestFixture, + ProviderIdentify_FunctionExists) +{ + CHECK(_provider->identify != NULL); +} + +TEST_FIXTURE(ProviderIdentify_TestFixture, + ProviderIdentify_ReturnNonNull) +{ + CHECK((*_provider->identify)(_provider) != NULL); +} + +TEST_FIXTURE(ProviderIdentify_TestFixture, + ProviderIdentify_ReturnNotEmpty) +{ + CHECK(strlen((*_provider->identify)(_provider)) != 0); +} + +TEST_FIXTURE(ProviderIdentify_TestFixture, + ProviderIdentify_ReturnsValidUtf8String) +{ + CHECK(g_utf8_validate((*_provider->identify)(_provider), -1, NULL)); +} + \ No newline at end of file diff --git a/unittests/enchant_providers/Provider/provider_list_dicts.cpp b/unittests/enchant_providers/Provider/provider_list_dicts.cpp new file mode 100644 index 0000000..3b068dd --- /dev/null +++ b/unittests/enchant_providers/Provider/provider_list_dicts.cpp @@ -0,0 +1,97 @@ +/* Copyright (c) 2008 Eric Scott Albright + * + * 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. + */ + +#include +#include + +#include "../unittest_enchant_providers.h" + +struct ProviderListDicts_TestFixture : Provider_TestFixture +{ + char ** _dicts; + + //Setup + ProviderListDicts_TestFixture():_dicts(NULL) + { + } + //Teardown + ~ProviderListDicts_TestFixture() + { + if (_dicts && _provider->free_string_list) + (*_provider->free_string_list) (_provider, _dicts); + } +}; + +// list_dicts is optional + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST_FIXTURE(ProviderListDicts_TestFixture, + ProviderListDicts_ListReturnedHasContent) +{ + if(_provider->list_dicts) + { + size_t n_dicts; + + _dicts = (*_provider->list_dicts) (_provider, &n_dicts); + for (size_t i = 0; i < n_dicts; i++) + { + CHECK(_dicts[i] != NULL); + } + } +} + +TEST_FIXTURE(ProviderListDicts_TestFixture, + ProviderListDicts_ForEachReturned_RequestDictSucceeds) +{ + if(_provider->list_dicts && _provider->request_dict) + { + size_t n_dicts; + + _dicts = (*_provider->list_dicts) (_provider, &n_dicts); + for (size_t i = 0; i < n_dicts; i++) + { + EnchantDict* dict = (*_provider->request_dict) (_provider, _dicts[i]); + CHECK(dict != NULL); + if (dict && _provider->dispose_dict) + { + _provider->dispose_dict(_provider, dict); + } + } + } +} + +TEST_FIXTURE(ProviderListDicts_TestFixture, + ProviderListDicts_ForEachReturned_DictExistsSucceeds) +{ + if(_provider->list_dicts && _provider->dictionary_exists) + { + size_t n_dicts; + + _dicts = (*_provider->list_dicts) (_provider, &n_dicts); + for (size_t i = 0; i < n_dicts; i++) + { + CHECK_EQUAL(1, (*_provider->dictionary_exists) (_provider, _dicts[i])); + } + } +} + diff --git a/unittests/enchant_providers/Provider/provider_request_dict.cpp b/unittests/enchant_providers/Provider/provider_request_dict.cpp new file mode 100644 index 0000000..ab129b7 --- /dev/null +++ b/unittests/enchant_providers/Provider/provider_request_dict.cpp @@ -0,0 +1,67 @@ +/* Copyright (c) 2008 Eric Scott Albright + * + * 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. + */ + +#include +#include + +#include "../unittest_enchant_providers.h" + +struct ProviderRequestDictionary_TestFixture : Provider_TestFixture +{ + EnchantDict* _dict; + //Setup + ProviderRequestDictionary_TestFixture():_dict(NULL) + { + } + //Teardown + ~ProviderRequestDictionary_TestFixture() + { + if (_dict && _provider->dispose_dict) + { + _provider->dispose_dict(_provider, _dict); + } + } +}; + +// request_dict is optional + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST_FIXTURE(ProviderRequestDictionary_TestFixture, + ProviderRequestDictionary_ProviderDoesNotHave_ReturnsNull) +{ + if (_provider->request_dict) + { + _dict = (*_provider->request_dict) (_provider, "zxx"); /*zxx is no linguistic content*/ + CHECK_EQUAL((void*)NULL, _dict); + } +} + +TEST_FIXTURE(ProviderRequestDictionary_TestFixture, + ProviderRequestDictionary_ProviderDoesNotHave_ProviderDoesNotSetError) +{ + if (_provider->request_dict) + { + _dict = (*_provider->request_dict) (_provider, "zxx"); /*zxx is no linguistic content*/ + CHECK_EQUAL((void*)NULL, GetErrorMessage(_provider)); + } +} diff --git a/unittests/enchant_providers/Readme.txt b/unittests/enchant_providers/Readme.txt new file mode 100644 index 0000000..a0e4304 --- /dev/null +++ b/unittests/enchant_providers/Readme.txt @@ -0,0 +1,9 @@ +These are generic tests to test the providers. They should work on any provider. +When a provider does not support a particular function, those tests will just pass. + +Some tests rely on the provider having specific language support: +fr_FR + +The tests try to clean up after themselves but some words may be added to +the personal dictionary if the provider does not provide a way to remove +words from the personal dictionary. \ No newline at end of file diff --git a/unittests/enchant_providers/main.cpp b/unittests/enchant_providers/main.cpp new file mode 100644 index 0000000..26ff6ca --- /dev/null +++ b/unittests/enchant_providers/main.cpp @@ -0,0 +1,183 @@ +/* Copyright (c) 2008 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include "unittest_enchant_providers.h" + +int Test(char* path); +int TestProvider(char* filename); +int TestProvidersInDirectory(char * dir_name); + +typedef EnchantProvider *(*EnchantProviderInitFunc) (void); +typedef void (*EnchantPreConfigureFunc) (EnchantProvider * provider, const char * module_dir); + +// from enchant.c we need this so that providers can set errors. +struct str_enchant_broker +{ + GSList *provider_list; /* list of all of the spelling backend providers */ + GHashTable *dict_map; /* map of language tag -> dictionary */ + GHashTable *provider_ordering; /* map of language tag -> provider order */ + + gchar * error; +}; + +// comes with a list of directories or providers +int main(int argc, char* argv[]) +{ + int result = 0; + for(int i=1; i < argc; ++i) + { + int resultT = Test(argv[i]); + if(resultT != 0) + { + result = resultT; + } + } + + if(argc == 1) + { + char* current_dir = g_get_current_dir(); + TestProvidersInDirectory(current_dir); + g_free(current_dir); + } + + return result; +} + +EnchantProvider* g_provider; +EnchantProvider* GetProviderForTests() +{ + return g_provider; +} + +char* GetErrorMessage(EnchantProvider* provider) +{ + return provider->owner->error; +} + +//path is provider filename or directory containing providers +int Test(char* path) +{ + assert(path); + if (g_file_test (path, (GFileTest)(G_FILE_TEST_IS_DIR))) + { + return TestProvidersInDirectory(path); + } + else + { + return TestProvider(path); + } +} + +int TestProvidersInDirectory(char * dir_name) +{ + GDir *dir; + G_CONST_RETURN char *dir_entry; + size_t entry_len, g_module_suffix_len; + + char * filename; + int result = 0; + + dir = g_dir_open (dir_name, 0, NULL); + if (!dir) + return 0; + + g_module_suffix_len = strlen (G_MODULE_SUFFIX); + + while ((dir_entry = g_dir_read_name (dir)) != NULL) + { + entry_len = strlen (dir_entry); + if ((entry_len > g_module_suffix_len) && + !strcmp(dir_entry+(entry_len-g_module_suffix_len), G_MODULE_SUFFIX)) + { + filename = g_build_filename (dir_name, dir_entry, NULL); + int resultT = Test(filename); + if(resultT != 0) + { + result = resultT; + } + g_free (filename); + } + } + + g_dir_close (dir); + return result; +} + +int TestProvider(char* filename) +{ + assert(g_provider == NULL); + int result = 0; + + EnchantProviderInitFunc init_func; + EnchantPreConfigureFunc conf_func; + + GModule* module = g_module_open (filename, (GModuleFlags) 0); + if (module) + { + if (g_module_symbol(module, "init_enchant_provider", (gpointer *) (&init_func)) + && init_func) + { + g_provider = init_func (); + } + + /* optional entry point to allow modules to look for associated files + */ + if (g_provider && + g_module_symbol(module, "configure_enchant_provider", (gpointer *) (&conf_func)) + && conf_func) + { + char* dir_name = g_path_get_dirname(filename); + conf_func (g_provider, dir_name); + g_free(dir_name); + } + } + else + { + g_warning ("Could not load provider: %s\n", g_module_error()); + } + + if (g_provider) + { + EnchantBroker broker; // just so we have someplace to put errors + broker.error=NULL; + + g_provider->enchant_private_data = (void *) module; + g_provider->owner = &broker; + printf("\nRunning tests on %s\n", filename); + result = UnitTest::RunAllTests(); + if(g_provider->dispose) + g_provider->dispose(g_provider); + + g_provider = NULL; + } + + if(module){ + g_module_close(module); + } + return result; +} + diff --git a/unittests/enchant_providers/unittest_enchant_providers.h b/unittests/enchant_providers/unittest_enchant_providers.h new file mode 100644 index 0000000..8a871e9 --- /dev/null +++ b/unittests/enchant_providers/unittest_enchant_providers.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2008 Eric Scott Albright + * + * 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. + */ + +#include "enchant.h" +#include "enchant-provider.h" +#include + +EnchantProvider* GetProviderForTests(); +char* GetErrorMessage(EnchantProvider* provider); + +struct Provider_TestFixture +{ + EnchantProvider* _provider; + + //Setup + Provider_TestFixture() + { + _provider = GetProviderForTests(); + } + //Teardown + ~Provider_TestFixture() + { + } + + std::string Convert(const std::wstring & ws) + { + gchar* str = g_utf16_to_utf8((gunichar2*)ws.c_str(), (glong)ws.length(), NULL, NULL, NULL); + std::string s(str); + g_free(str); + return s; + } + + std::wstring Convert(const std::string & s) + { + gunichar2* str = g_utf8_to_utf16(s.c_str(), (glong)s.length(), NULL, NULL, NULL); + std::wstring ws((wchar_t*)str); + g_free(str); + return ws; + } + + EnchantDict* GetFirstAvailableDictionary() + { + EnchantDict* dict=NULL; + + // get the first dictionary listed as being available + if(_provider->list_dicts && _provider->request_dict) + { + size_t n_dicts; + + char ** dicts = (*_provider->list_dicts) (_provider, &n_dicts); + for (size_t i = 0; i < n_dicts; i++) + { + dict = (*_provider->request_dict) (_provider, dicts[i]); + break; + } + if (dicts && _provider->free_string_list) + { + (*_provider->free_string_list) (_provider, dicts); + } + } + return dict; + } + + EnchantDict* GetDictionary(const char* language) + { + if(_provider->request_dict) + { + return (*_provider->request_dict) (_provider, language); + } + return NULL; + } + + virtual void ReleaseDictionary(EnchantDict* dict) + { + if (dict && _provider->dispose_dict) + { + _provider->dispose_dict(_provider, dict); + } + } +}; diff --git a/unittests/main.cpp b/unittests/main.cpp new file mode 100644 index 0000000..44b5ac3 --- /dev/null +++ b/unittests/main.cpp @@ -0,0 +1,31 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include "EnchantBrokerTestFixture.h" +EnchantProvider * EnchantBrokerTestFixture::mock_provider=NULL; +ConfigureHook EnchantBrokerTestFixture::userMockProviderConfiguration=NULL; +ConfigureHook EnchantBrokerTestFixture::userMockProvider2Configuration=NULL; + + +int main(){ + return UnitTest::RunAllTests(); +} \ No newline at end of file diff --git a/unittests/mock_provider/mock_provider.cpp b/unittests/mock_provider/mock_provider.cpp new file mode 100644 index 0000000..f421b1c --- /dev/null +++ b/unittests/mock_provider/mock_provider.cpp @@ -0,0 +1,108 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ +#include +#include +#include "mock_provider.h" + +ENCHANT_PLUGIN_DECLARE("mock") + +static void +mock_provider_dispose(EnchantProvider *me) +{ + g_free(me); +} + +static const char * +mock_provider_identify (EnchantProvider *) +{ + return "mock"; +} + +static const char * +mock_provider_describe (EnchantProvider *) +{ + return "Mock Provider"; +} + +static ConfigureHook _hook; + + +extern "C" { + +ENCHANT_MODULE_EXPORT(void) +set_configure(ConfigureHook hook){ + _hook = hook; +} + + +ENCHANT_MODULE_EXPORT(EnchantProvider *) +init_enchant_provider(void) +{ + bool hasIdentify = true; + bool hasDescribe = true; +#if defined(_WIN32) + std::wstring null_provider(L"null_provider.dll"); + std::wstring null_identify(L"null_identify.dll"); + std::wstring null_describe(L"null_describe.dll"); + WCHAR szFilename[MAX_PATH]; + DWORD cFilename = GetModuleFileName((HMODULE)s_hModule, (LPWSTR) &szFilename, sizeof(szFilename)); + if(cFilename > null_provider.size()){ + if(std::wstring(&szFilename[cFilename-null_provider.size()]) == null_provider){ + return NULL; + } + } + if(cFilename > null_identify.size()){ + if(std::wstring(&szFilename[cFilename-null_identify.size()]) == null_identify){ + hasIdentify = false; + } + } + if(cFilename > null_describe.size()){ + if(std::wstring(&szFilename[cFilename-null_describe.size()]) == null_describe){ + hasDescribe = false; + } + } + +#endif + + EnchantProvider *provider; + + provider = g_new0(EnchantProvider, 1); + provider->dispose = mock_provider_dispose; //although this is technically optional, it will result in a memory leak + provider->request_dict = NULL; + provider->dispose_dict = NULL; + provider->identify = hasIdentify ? mock_provider_identify : NULL; // this is required or module won't load + provider->describe = hasDescribe ? mock_provider_describe : NULL; // this is required or module won't load + provider->list_dicts = NULL; + provider->dictionary_exists = NULL; + provider->free_string_list = NULL; + + return provider; +} + +ENCHANT_MODULE_EXPORT(void) +configure_enchant_provider(EnchantProvider * me, const char *dir_name) +{ + if(_hook){ + _hook(me, dir_name); + } +} + +} \ No newline at end of file diff --git a/unittests/mock_provider/mock_provider.h b/unittests/mock_provider/mock_provider.h new file mode 100644 index 0000000..3e1a997 --- /dev/null +++ b/unittests/mock_provider/mock_provider.h @@ -0,0 +1,22 @@ +#ifndef ___MOCK_PROVIDER_H +#define ___MOCK_PROVIDER_H + +#include "enchant-provider.h" + +#ifdef _MSC_VER +#pragma once +#endif + +typedef void (*ConfigureHook) (EnchantProvider * me, const char * dir_name); + +#ifdef __cplusplus +extern "C" { +#endif + +ENCHANT_MODULE_EXPORT(void) set_configure(ConfigureHook hook); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/unittests/provider/enchant_provider_broker_set_error_tests.cpp b/unittests/provider/enchant_provider_broker_set_error_tests.cpp new file mode 100644 index 0000000..84bfe0d --- /dev/null +++ b/unittests/provider/enchant_provider_broker_set_error_tests.cpp @@ -0,0 +1,101 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include + +#include "../EnchantBrokerTestFixture.h" + +struct EnchantBrokerSetErrorTests : EnchantBrokerTestFixture +{ + //Setup + EnchantBrokerSetErrorTests() + { + provider = GetMockProvider(); + } + + std::string GetErrorMessage(){ + char* error = enchant_broker_get_error(_broker); + if(error == NULL){ + error = ""; + } + return std::string(error); + } + + EnchantProvider* provider; +}; + +/** + * enchant_provider_set_error + * @provider: A non-null provider + * @err: A non-null error message + * + * Sets the current runtime error to @err. This API is private to + * the providers. + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST_FIXTURE(EnchantBrokerSetErrorTests, + SetErrorMessageOnProvider) +{ + std::string expectedErrorMessage("Error message to display"); + enchant_provider_set_error(provider, expectedErrorMessage.c_str()); + + CHECK_EQUAL(expectedErrorMessage, GetErrorMessage()); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantBrokerSetErrorTests, + SetErrorMessageOnProvider_NullProvider_NoErrorSet) +{ + enchant_provider_set_error(NULL, "Error message to display"); + + CHECK_EQUAL(std::string(), GetErrorMessage()); +} + +TEST_FIXTURE(EnchantBrokerSetErrorTests, + SetErrorMessageOnProvider_NullError_NoErrorSet) +{ + enchant_provider_set_error(provider, NULL); + + CHECK_EQUAL(std::string(), GetErrorMessage()); +} + +TEST_FIXTURE(EnchantBrokerSetErrorTests, + SetErrorMessageOnProvider_InvalidUtf8ErrorMessage_NoErrorSet) +{ + enchant_provider_set_error(provider, "\xa5\xf1\x08"); + + CHECK_EQUAL(std::string(), GetErrorMessage()); +} + +TEST_FIXTURE(EnchantBrokerSetErrorTests, + SetErrorMessageOnProvider_MessageCopied) +{ + std::string expectedErrorMessage("Error message to display"); + enchant_provider_set_error(provider, expectedErrorMessage.c_str()); + + expectedErrorMessage[0] = 'e'; + CHECK(expectedErrorMessage != GetErrorMessage()); +} diff --git a/unittests/provider/enchant_provider_dict_set_error_tests.cpp b/unittests/provider/enchant_provider_dict_set_error_tests.cpp new file mode 100644 index 0000000..49144e2 --- /dev/null +++ b/unittests/provider/enchant_provider_dict_set_error_tests.cpp @@ -0,0 +1,93 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantDictionaryTestFixture.h" + +struct EnchantDictionarySetErrorTests : EnchantDictionaryTestFixture +{ + //Setup + std::string GetErrorMessage(){ + char* error = enchant_dict_get_error(_dict); + if(error == NULL){ + error = ""; + } + return std::string(error); + } +}; + +/** + * enchant_dict_set_error + * @dict: A non-null dictionary + * @err: A non-null error message + * + * Sets the current runtime error to @err. This API is private to the + * providers. + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation + +TEST_FIXTURE(EnchantDictionarySetErrorTests, + SetErrorMessageOnDictionary) +{ + std::string expectedErrorMessage("Error message to display"); + enchant_dict_set_error(_dict, expectedErrorMessage.c_str()); + + CHECK_EQUAL(expectedErrorMessage, GetErrorMessage()); +} + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST_FIXTURE(EnchantDictionarySetErrorTests, + SetErrorMessageOnDictionary_NullDictionary_NoErrorSet) +{ + enchant_dict_set_error(NULL, "Error message to display"); + + CHECK_EQUAL(std::string(), GetErrorMessage()); +} + +TEST_FIXTURE(EnchantDictionarySetErrorTests, + SetErrorMessageOnDictionary_NullError_NoErrorSet) +{ + enchant_dict_set_error(_dict, NULL); + + CHECK_EQUAL(std::string(), GetErrorMessage()); +} + +TEST_FIXTURE(EnchantDictionarySetErrorTests, + SetErrorMessageOnDictionary_InvalidUtf8ErrorMessage_NoErrorSet) +{ + enchant_dict_set_error(_dict, "\xa5\xf1\x08"); + + CHECK_EQUAL(std::string(), GetErrorMessage()); +} + +TEST_FIXTURE(EnchantDictionarySetErrorTests, + SetErrorMessageOnDictionary_MessageCopied) +{ + std::string expectedErrorMessage("Error message to display"); + enchant_dict_set_error(_dict, expectedErrorMessage.c_str()); + + expectedErrorMessage[0] = 'e'; + CHECK(expectedErrorMessage != GetErrorMessage()); +} \ No newline at end of file diff --git a/unittests/provider/enchant_provider_get_prefix_dir_tests.cpp b/unittests/provider/enchant_provider_get_prefix_dir_tests.cpp new file mode 100644 index 0000000..429d7cc --- /dev/null +++ b/unittests/provider/enchant_provider_get_prefix_dir_tests.cpp @@ -0,0 +1,68 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include +#include "../EnchantTestFixture.h" + +struct EnchantGetPrefixDirTestFixture : EnchantTestFixture{ + //Setup + EnchantGetPrefixDirTestFixture() + { + expectedPrefixDir = GetDirectoryOfThisModule(); + } + + const char* ExpectedPrefixDir() + { + if(expectedPrefixDir.empty()){ + return NULL; + } + + return expectedPrefixDir.c_str(); + } + + std::string expectedPrefixDir; + +}; + +/** + * enchant_get_prefix_dir + * + * Returns a string giving the location of the base directory + * of the enchant installation. This corresponds roughly to + * the --prefix option given to ./configure when enchant is + * compiled, except it is determined at runtime based on the location + * of the enchant library. + * + * This API is private to the providers. + * + * returns NULL if it cannot dynamically determine the location of + * the enchant library + */ + +TEST_FIXTURE(EnchantGetPrefixDirTestFixture, + EnchantGetPrefixDir) +{ + char* prefixDir = enchant_get_prefix_dir(); + CHECK_EQUAL(ExpectedPrefixDir(), prefixDir); + g_free(prefixDir); +} \ No newline at end of file diff --git a/unittests/provider/enchant_provider_get_registry_value_tests.cpp b/unittests/provider/enchant_provider_get_registry_value_tests.cpp new file mode 100644 index 0000000..e978c42 --- /dev/null +++ b/unittests/provider/enchant_provider_get_registry_value_tests.cpp @@ -0,0 +1,180 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include "../EnchantTestFixture.h" + +struct EnchantGetRegistryValue_TestFixture : EnchantTestFixture{ + //Teardown + ~EnchantGetRegistryValue_TestFixture(){ + ClearRegistryValue(HKEY_CURRENT_USER, L"Software\\Enchant\\Test", L"Value"); + ClearRegistryValue(HKEY_LOCAL_MACHINE, L"Software\\Enchant\\Test", L"Value"); + } + + void SetUserRegistryValue(const std::string& value) + { + SetRegistryValue(HKEY_CURRENT_USER, L"Software\\Enchant\\Test", L"Value", value); + } + + void SetMachineRegistryValue(const std::string& value) + { + SetRegistryValue(HKEY_LOCAL_MACHINE, L"Software\\Enchant\\Test", L"Value", value); + } +}; + + +/** + * enchant_get_registry_value + * @prefix: Your category, such as "Ispell" or "Myspell" + * @key: The tag within your category that you're interested in + * + * Returns: the value if it exists and is not an empty string ("") or %null otherwise. Must be free'd. + * + * Choices: User Machine Result + * "hello" "world" "hello" + * "hello" NULL "hello" + * "hello" "" "hello" + * "" "world" "world" + * "" NULL "" + * "" "" "" + * NULL "world" "world" + * NULL NULL NULL + * NULL "" "" + * + * This API is private to the providers. + */ + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +#ifdef _WIN32 +TEST_FIXTURE(EnchantGetRegistryValue_TestFixture, + GetRegistryValue_UserAndMachine_ValueFromUser) +{ + SetUserRegistryValue("hello"); + SetMachineRegistryValue("world"); + + char * value = enchant_get_registry_value("Test", "Value"); + + CHECK(value); + CHECK_EQUAL("hello", value); + + g_free(value); +} + +TEST_FIXTURE(EnchantGetRegistryValue_TestFixture, + GetRegistryValue_UserOnly_ValueFromUser) +{ + SetUserRegistryValue("hello"); + char * value = enchant_get_registry_value("Test", "Value"); + + CHECK(value); + CHECK_EQUAL("hello", value); + + g_free(value); +} + +TEST_FIXTURE(EnchantGetRegistryValue_TestFixture, + GetRegistryValue_MachineOnly_ValueFromMachine) +{ + SetMachineRegistryValue("world"); + char * value = enchant_get_registry_value("Test", "Value"); + + CHECK(value); + CHECK_EQUAL("world", value); + + g_free(value); +} + +TEST_FIXTURE(EnchantGetRegistryValue_TestFixture, + GetRegistryValue_UserEmptyAndMachineSet_ValueFromMachine) +{ + SetUserRegistryValue(""); + SetMachineRegistryValue("world"); + + char * value = enchant_get_registry_value("Test", "Value"); + + CHECK(value); + CHECK_EQUAL("world", value); + + g_free(value); +} + +TEST_FIXTURE(EnchantGetRegistryValue_TestFixture, + GetRegistryValue_UserEmptyAndMachineNotSet_Null) +{ + SetUserRegistryValue(""); + + char * value = enchant_get_registry_value("Test", "Value"); + + CHECK(!value); + + g_free(value); +} +#endif + + +TEST(GetRegistryValue_None_NULL) +{ + char * value = enchant_get_registry_value("Test", "Value"); + + CHECK(value == NULL); +} + + +///////////////////////////////////////////////////////////////////////////// +// Test Error Conditions +TEST(GetRegistryValue_NullPrefix_NULL) +{ + char * value = enchant_get_registry_value(NULL, "Value"); + + CHECK(value == NULL); +} + +TEST(GetRegistryValue_NullKey_NULL) +{ + char * value = enchant_get_registry_value("Test", NULL); + + CHECK(value == NULL); +} + +#ifdef _WIN32 +TEST_FIXTURE(EnchantGetRegistryValue_TestFixture, + GetRegistryValue_NullPrefix_Null) +{ + SetUserRegistryValue("hello"); + + char * value = enchant_get_registry_value(NULL, "Value"); + + CHECK(value == NULL); +} + +TEST_FIXTURE(EnchantGetRegistryValue_TestFixture, + GetRegistryValue_NullKey_Null) +{ + SetUserRegistryValue("hello"); + + char * value = enchant_get_registry_value("Test", NULL); + + CHECK(value == NULL); +} + +#endif \ No newline at end of file diff --git a/unittests/provider/enchant_provider_get_user_config_dirs_tests.cpp b/unittests/provider/enchant_provider_get_user_config_dirs_tests.cpp new file mode 100644 index 0000000..7a4facd --- /dev/null +++ b/unittests/provider/enchant_provider_get_user_config_dirs_tests.cpp @@ -0,0 +1,122 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include "../EnchantTestFixture.h" +#include +#include + +/** + * enchant_get_user_config_dir + * + * Returns: the user's enchant directory, or %null. Returned value + * must be free'd. + * + * The enchant directory is the place where enchant finds user providers and + * dictionaries and settings related to enchant + * + * This API is private to the providers. + */ + +/* + * The user's config directory on windows can be overridden using the registry + * setting HKEY_CURRENT_USER\Software\Enchant\Config\Data_Dir + */ +#ifdef _WIN32 +TEST_FIXTURE(EnchantTestFixture, + GetUserConfigDir_FromRegistryConfigDataDir) +{ + std::string configDir("here I am"); + SetUserRegistryConfigDir(configDir); + + GSList * enchantUserConfigDirs = enchant_get_user_config_dirs(); + + CHECK(enchantUserConfigDirs); + CHECK_EQUAL(configDir, (gchar*) enchantUserConfigDirs->data); + + g_slist_free(enchantUserConfigDirs); +} + +TEST_FIXTURE(EnchantTestFixture, + GetUserConfigDir_BlankFromRegistryConfigDataDir_RegistryEntryIgnored) +{ + std::string configDir(""); + SetUserRegistryConfigDir(configDir); + + GSList * enchantUserConfigDirs = enchant_get_user_config_dirs(); + + CHECK(enchantUserConfigDirs); + + CHECK_EQUAL(GetEnchantHomeDirFromBase(g_get_user_config_dir()), (gchar*) enchantUserConfigDirs->data); + + g_slist_free(enchantUserConfigDirs); +} + +TEST_FIXTURE(EnchantTestFixture, + GetUserConfigDir_FromRegistryConfigHomeDir) +{ + std::string homeDir("here I am"); + SetRegistryHomeDir(homeDir); + + GSList * enchantUserConfigDirs = enchant_get_user_config_dirs(); + + CHECK(enchantUserConfigDirs); + GSList* iter = enchantUserConfigDirs->next; + CHECK(iter); + + CHECK_EQUAL(GetEnchantHomeDirFromBase(homeDir), (gchar*) iter->data); + + g_slist_free(enchantUserConfigDirs); +} + +TEST_FIXTURE(EnchantTestFixture, + GetUserConfigDir_BlankFromRegistryConfigHomeDir_RegistryEntryIgnored) +{ + std::string homeDir(""); + SetRegistryHomeDir(homeDir); + + GSList * enchantUserConfigDirs = enchant_get_user_config_dirs(); + + CHECK(enchantUserConfigDirs); + GSList* iter = enchantUserConfigDirs->next; + CHECK(iter); + + CHECK_EQUAL(GetEnchantHomeDirFromBase(g_get_home_dir()), (gchar*) iter->data); + + g_slist_free(enchantUserConfigDirs); +} + +#endif + +TEST_FIXTURE(EnchantTestFixture, + GetUserConfigDir) +{ + GSList * enchantUserConfigDirs = enchant_get_user_config_dirs(); + + CHECK(enchantUserConfigDirs); + GSList* iter = enchantUserConfigDirs; +#ifdef _WIN32 + CHECK_EQUAL(GetEnchantHomeDirFromBase(g_get_user_config_dir()), (gchar*) iter->data); + iter = iter->next; +#endif + + CHECK_EQUAL(GetEnchantHomeDirFromBase(g_get_home_dir()), (gchar*) iter->data); + g_slist_free(enchantUserConfigDirs); +} \ No newline at end of file diff --git a/unittests/provider/enchant_provider_get_user_language_tests.cpp b/unittests/provider/enchant_provider_get_user_language_tests.cpp new file mode 100644 index 0000000..792cf30 --- /dev/null +++ b/unittests/provider/enchant_provider_get_user_language_tests.cpp @@ -0,0 +1,88 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ + +#include +#include +#include + +///////////////////////////////////////////////////////////////////////////// +// Test Normal Operation +TEST(EnchantGetUserLanguage) +{ + char* userLanguage = enchant_get_user_language(); + CHECK(userLanguage); + g_free(userLanguage); +} + +TEST(EnchantGetUserLanguage_FromLangEnvironmentVariable) +{ + std::string origLangEnv; + bool hasLangEnv = (g_getenv("LANG") != NULL); + if(hasLangEnv) + { + origLangEnv = std::string(g_getenv("LANG")); + } + + g_setenv("LANG", "qaa", TRUE); + char* userLanguage = enchant_get_user_language(); + CHECK(userLanguage); + CHECK_EQUAL("qaa", userLanguage); + + g_free(userLanguage); + + if(hasLangEnv) + { + g_setenv("LANG", origLangEnv.c_str(), TRUE); + } + else{ + g_unsetenv("LANG"); + } +} + +#ifndef _WIN32 +TEST(EnchantGetUserLanguage_FromLocale) +{ + std::string origLocale(setlocale (LC_ALL, NULL)); + + setlocale (LC_ALL, "qaa"); + char* userLanguage = enchant_get_user_language(); + CHECK(userLanguage); + CHECK_EQUAL("qaa", userLanguage); + + g_free(userLanguage); + + setlocale (LC_ALL, origLocale.c_str()); +} + +TEST(EnchantGetUserLanguage_LocaleIsC_LocalIsEn) +{ + std::string origLocale(setlocale (LC_ALL, NULL)); + + setlocale (LC_ALL, "C"); + char* userLanguage = enchant_get_user_language(); + CHECK(userLanguage); + CHECK_EQUAL("en", userLanguage); + + g_free(userLanguage); + + setlocale (LC_ALL, origLocale.c_str()); +} +#endif \ No newline at end of file diff --git a/unittests/pwl/enchant_pwl_tests.cpp b/unittests/pwl/enchant_pwl_tests.cpp new file mode 100644 index 0000000..bf76103 --- /dev/null +++ b/unittests/pwl/enchant_pwl_tests.cpp @@ -0,0 +1,1579 @@ +/* Copyright (c) 2007 Eric Scott Albright + * + * 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. + */ +#define NOMINMAX //don't want windows to collide with std::min +#include +#include +#include + +#include "../EnchantDictionaryTestFixture.h" + +#include + +static char ** +DictionarySuggestsSat (EnchantDict * dict, const char *const word, size_t len, size_t * out_n_suggs) +{ + *out_n_suggs = 1; + char **sugg_arr = NULL; + + sugg_arr = g_new0 (char *, *out_n_suggs + 1); + sugg_arr[0] = g_strdup ("sat"); + + return sugg_arr; +} + +static EnchantDict* MockProviderRequestSuggestMockDictionary(EnchantProvider * me, const char *tag) +{ + + EnchantDict* dict = MockProviderRequestEmptyMockDictionary(me, tag); + dict->suggest = DictionarySuggestsSat; + return dict; +} + +static void DictionarySuggest_ProviderConfiguration (EnchantProvider * me, const char *) +{ + me->request_dict = MockProviderRequestSuggestMockDictionary; + me->dispose_dict = MockProviderDisposeDictionary; + me->free_string_list = MockProviderFreeStringList; +} + + +struct EnchantPwlWithDictSuggs_TestFixture : EnchantDictionaryTestFixture +{ + EnchantPwlWithDictSuggs_TestFixture(const std::string& languageTag="qaa"): + EnchantDictionaryTestFixture(DictionarySuggest_ProviderConfiguration, languageTag) + { } +}; + +struct EnchantPwl_TestFixture : EnchantDictionaryTestFixture +{ + //Setup + EnchantPwl_TestFixture(const std::string& languageTag="qaa"): + EnchantDictionaryTestFixture(EmptyDictionary_ProviderConfiguration, languageTag) + { } +}; + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestionsFromWord_MultipleSuggestions_ReturnsOnlyClosest) +{ + std::vector sNoiseWords; + sNoiseWords.push_back("spat"); + sNoiseWords.push_back("tots"); + sNoiseWords.push_back("tater"); + sNoiseWords.push_back("ton"); + sNoiseWords.push_back("gnat"); + + std::vector sWords; + sWords.push_back("cat"); + sWords.push_back("hat"); + sWords.push_back("that"); + sWords.push_back("bat"); + sWords.push_back("tot"); + + AddWordsToDictionary(sWords); + AddWordsToDictionary(sNoiseWords); + + std::vector suggestions = GetSuggestionsFromWord("tat"); + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +TEST_FIXTURE(EnchantPwlWithDictSuggs_TestFixture, + GetSuggestionsFromWord_MultipleSuggestions_ReturnsOnlyAsCloseAsDict) +{ + std::vector sNoiseWords; + sNoiseWords.push_back("spat"); + sNoiseWords.push_back("tots"); + sNoiseWords.push_back("tater"); + sNoiseWords.push_back("ton"); + sNoiseWords.push_back("gnat"); + + std::vector sWords; + sWords.push_back("cat"); + sWords.push_back("hat"); + sWords.push_back("that"); + sWords.push_back("bat"); + sWords.push_back("tot"); + + AddWordsToDictionary(sWords); + AddWordsToDictionary(sNoiseWords); + + std::vector suggestions = GetSuggestionsFromWord("tat"); + sWords.push_back("sat"); + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// External File change +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_DictionaryChangedExternally_Successful) +{ + std::vector sWords; + sWords.push_back("cat"); + sWords.push_back("hat"); + sWords.push_back("that"); + sWords.push_back("bat"); + sWords.push_back("tot"); + + ExternalAddWordsToDictionary(sWords); + + for(std::vector::const_iterator itWord = sWords.begin(); itWord != sWords.end(); ++itWord){ + CHECK( IsWordInDictionary(*itWord) ); + } + + std::vector sNewWords; + sNewWords.push_back("potatoe"); + sNewWords.push_back("grow"); + sNewWords.push_back("another"); + + ExternalAddNewLineToDictionary(); + ExternalAddWordsToDictionary(sNewWords); + + for(std::vector::const_iterator itWord = sNewWords.begin(); itWord != sNewWords.end(); ++itWord){ + CHECK( IsWordInDictionary(*itWord) ); + if(!IsWordInDictionary(*itWord)){ + testResults_.OnTestFailure(UnitTest::TestDetails(m_details, __LINE__), itWord->c_str()); + } + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + Suggest_DictionaryChangedExternally_Successful) +{ + std::vector sWords; + sWords.push_back("cat"); + sWords.push_back("hat"); + sWords.push_back("that"); + sWords.push_back("bat"); + sWords.push_back("tot"); + + ExternalAddWordsToDictionary(sWords); + + std::vector suggestions = GetSuggestionsFromWord("tat"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// DictionaryBeginsWithBOM +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_DictionaryBeginsWithBOM_Successful) +{ + char* Utf8Bom = "\xef\xbb\xbf"; + + Sleep(1000); // FAT systems have a 2 second resolution + // NTFS is appreciably faster but no specs on what it is exactly + // c runtime library's time_t has a 1 second resolution + FILE * f = g_fopen(GetPersonalDictFileName().c_str(), "at"); + if(f) + { + fputs(Utf8Bom, f); + fputs("cat", f); + fclose(f); + } + + + ReloadTestDictionary(); + + CHECK( IsWordInDictionary("cat") ); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// DictionaryHasInvalidUtf8 +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_DictionaryHasInvalidUtf8Data_OnlyReadsValidLines) +{ + std::vector sWords; + sWords.push_back("cat"); + sWords.push_back("hat"); + sWords.push_back("that"); + sWords.push_back("bat"); + sWords.push_back("tot"); + + std::vector::const_iterator bad = sWords.insert(sWords.begin()+2, "\xa5\xf1\x08"); //invalid utf8 data + ExternalAddWordsToDictionary(sWords); + + ReloadTestDictionary(); + + for(std::vector::const_iterator itWord = sWords.begin(); itWord != bad; ++itWord){ + CHECK( IsWordInDictionary(*itWord) ); + } + CHECK(!IsWordInDictionary(*bad) ); + + for(std::vector::const_iterator itWord = bad+1; itWord != sWords.end(); ++itWord){ + CHECK(IsWordInDictionary(*itWord) ); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// Last word in Dictionary terminated By EOF instead of NL +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_LastWordNotTerminatedByNL_WordsAppendedOkay) +{ + std::vector sWords; + sWords.push_back("cat"); + sWords.push_back("hat"); + sWords.push_back("that"); + sWords.push_back("bat"); + sWords.push_back("tot"); + + Sleep(1000); // FAT systems have a 2 second resolution + // NTFS is appreciably faster but no specs on what it is exactly + // c runtime library's time_t has a 1 second resolution + FILE * f = g_fopen(GetPersonalDictFileName().c_str(), "at"); + if(f) + { + fputs(sWords[0].c_str(), f); + fclose(f); + } + + for(std::vector::const_iterator itWord = sWords.begin() +1; + itWord != sWords.end(); + ++itWord) + { + AddWordToDictionary(*itWord); + } + + for(std::vector::const_iterator itWord = sWords.begin(); + itWord != sWords.end(); + ++itWord){ + CHECK( IsWordInDictionary(*itWord) ); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +// Pwl Bugs +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_DistanceUsesUnicodeChar) +{ + std::string puaWord("\xF4\x80\x80\x80ord"); // private use character + AddWordToDictionary(puaWord); //edit distance 1 using unichar; 4 using utf8 + + std::vector suggestions = GetSuggestionsFromWord("word"); + + CHECK( !suggestions.empty()); + + if(!suggestions.empty()){ + CHECK_EQUAL(puaWord, suggestions[0]); + } +} + +// Word which is prefix of another gets edit distance which is one less. +// This means it moves to the top of the list normally but once we only bring +// back the best matches, it means the rest of the mathes aren't returned +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_EditDistanceOnWordWhichIsPrefixOfAnother) +{ + std::vector sNoiseWords; + sNoiseWords.push_back("hastens"); //4 + + std::vector sWords; + sWords.push_back("cashes"); //3 + sWords.push_back("hasten"); //3 + sWords.push_back("washes"); //3 + + AddWordsToDictionary(sWords); + AddWordsToDictionary(sNoiseWords); + + std::vector suggestions = GetSuggestionsFromWord("saskep"); + CHECK(suggestions[0] != "hasten"); + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// Commented Lines ignored +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_DictionaryHasCommentedLines_DoesNotReadCommentedLines) +{ + std::vector sWords; + sWords.push_back("cat"); + sWords.push_back("hat"); + sWords.push_back("that"); + sWords.push_back("bat"); + sWords.push_back("tot"); + + std::vector::const_iterator comment = sWords.insert(sWords.begin()+2, "#sat"); //comment + ExternalAddWordsToDictionary(sWords); + ReloadTestDictionary(); + + for(std::vector::const_iterator itWord = sWords.begin(); itWord != comment; ++itWord){ + CHECK( IsWordInDictionary(*itWord) ); + } + + CHECK(!IsWordInDictionary(*comment) ); + CHECK(!IsWordInDictionary("sat") ); + + for(std::vector::const_iterator itWord = comment+1; itWord != sWords.end(); ++itWord){ + CHECK(IsWordInDictionary(*itWord) ); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// Too long lines ignored +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_DictionaryHasSuperLongLine_DoesNotReadLine) +{ + const size_t lineLen = 2048; + + std::vector sWords; + sWords.push_back("cat"); + sWords.push_back("hat"); + sWords.push_back("that"); + sWords.push_back("bat"); + sWords.push_back("tot"); + + std::vector::const_iterator superlong = sWords.insert(sWords.begin()+2, std::string(lineLen, 'c')); //super long line + ExternalAddWordsToDictionary(sWords); + ReloadTestDictionary(); + + for(std::vector::const_iterator itWord = sWords.begin(); itWord != superlong; ++itWord){ + CHECK( IsWordInDictionary(*itWord) ); + } + + CHECK(!IsWordInDictionary(*superlong) ); + for(size_t i=0; i != lineLen; ++i) + { + CHECK(!IsWordInDictionary(std::string(i, 'c')) ); + } + + for(std::vector::const_iterator itWord = superlong+1; itWord != sWords.end(); ++itWord){ + CHECK(IsWordInDictionary(*itWord) ); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// Unicode normalization +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_DictionaryHasComposed_SuccessfulCheckWithComposedAndDecomposed) +{ + ExternalAddWordToDictionary(Convert(L"fianc\xe9")); // u00e9 = Latin small letter e with acute + + ReloadTestDictionary(); + + CHECK( IsWordInDictionary(Convert(L"fianc\xe9")) ); //NFC + CHECK( IsWordInDictionary(Convert(L"fiance\x301")) ); //NFD u0301 = Combining acute accent +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_AddedComposed_SuccessfulCheckWithComposedAndDecomposed) +{ + AddWordToDictionary(Convert(L"fianc\xe9")); // u00e9 = Latin small letter e with acute + + CHECK( IsWordInDictionary(Convert(L"fianc\xe9")) ); //NFC + CHECK( IsWordInDictionary(Convert(L"fiance\x301")) ); //NFD u0301 = Combining acute accent +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_DictionaryHasDecomposed_SuccessfulCheckWithComposedAndDecomposed) +{ + ExternalAddWordToDictionary(Convert(L"fiance\x301")); // u0301 = Combining acute accent + + ReloadTestDictionary(); + + CHECK( IsWordInDictionary(Convert(L"fianc\xe9")) ); //NFC + CHECK( IsWordInDictionary(Convert(L"fiance\x301")) ); //NFD +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_AddedDecomposed_SuccessfulCheckWithComposedAndDecomposed) +{ + AddWordToDictionary(Convert(L"fiance\x301")); // u0301 = Combining acute accent + + CHECK( IsWordInDictionary(Convert(L"fianc\xe9")) ); //NFC + CHECK( IsWordInDictionary(Convert(L"fiance\x301")) ); //NFD +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + Suggest_DictionaryHasComposed_ReturnsComposed) +{ + ExternalAddWordToDictionary(Convert(L"fianc\xe9")); // u00e9 = Latin small letter e with acute + + ReloadTestDictionary(); + + std::vector suggestions = GetSuggestionsFromWord("fiance"); + + std::vector expected; + expected.push_back(Convert(L"fianc\xe9")); // u00e9 = Latin small letter e with acute + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + Suggest_AddedComposed_ReturnsComposed) +{ + AddWordToDictionary(Convert(L"fianc\xe9")); // u00e9 = Latin small letter e with acute + + std::vector suggestions = GetSuggestionsFromWord("fiance"); + + std::vector expected; + expected.push_back(Convert(L"fianc\xe9")); // u00e9 = Latin small letter e with acute + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + Suggest_DictionaryHasDecomposed_ReturnsDecomposed) +{ + ExternalAddWordToDictionary(Convert(L"fiance\x301")); // u0301 = Combining acute accent + + ReloadTestDictionary(); + + std::vector suggestions = GetSuggestionsFromWord("fiance"); + + std::vector expected; + expected.push_back(Convert(L"fiance\x301")); // u0301 = Combining acute accent + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + Suggest_AddedDecomposed_ReturnsDecomposed) +{ + AddWordToDictionary(Convert(L"fiance\x301")); // u0301 = Combining acute accent + + std::vector suggestions = GetSuggestionsFromWord("fiance"); + + std::vector expected; + expected.push_back(Convert(L"fiance\x301")); // u0301 = Combining acute accent + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// Capitalization +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_AddedAllCaps_OnlyAllCapsSuccessful) +{ + AddWordToDictionary("CIA"); + + CHECK( IsWordInDictionary("CIA") ); + CHECK(!IsWordInDictionary("CIa") ); + CHECK(!IsWordInDictionary("Cia") ); + CHECK(!IsWordInDictionary("cia") ); + CHECK(!IsWordInDictionary("cIa") ); + + CHECK( IsWordInSession("CIA") ); + CHECK(!IsWordInSession("CIa") ); + CHECK(!IsWordInSession("Cia") ); + CHECK(!IsWordInSession("cia") ); + CHECK(!IsWordInSession("cIa") ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_AddedTitle_lowerCaseAndMixedCaseNotSuccessful) +{ + AddWordToDictionary("Eric"); + + CHECK( IsWordInDictionary("ERIC") ); + CHECK(!IsWordInDictionary("ERic") ); + CHECK( IsWordInDictionary("Eric") ); + CHECK(!IsWordInDictionary("eric") ); + CHECK(!IsWordInDictionary("eRic") ); + + CHECK( IsWordInSession("ERIC") ); + CHECK(!IsWordInSession("ERic") ); + CHECK( IsWordInSession("Eric") ); + CHECK(!IsWordInSession("eric") ); + CHECK(!IsWordInSession("eRic") ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_Addedlower_MixedCaseNotSuccessful) +{ + AddWordToDictionary("rice"); + + CHECK( IsWordInDictionary("RICE") ); + CHECK(!IsWordInDictionary("RIce") ); + CHECK( IsWordInDictionary("Rice") ); + CHECK( IsWordInDictionary("rice") ); + CHECK(!IsWordInDictionary("rIce") ); + + CHECK( IsWordInSession("RICE") ); + CHECK(!IsWordInSession("RIce") ); + CHECK( IsWordInSession("Rice") ); + CHECK( IsWordInSession("rice") ); + CHECK(!IsWordInSession("rIce") ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_AddedAllCapsOfTrueTitleCase_OnlyAllCapsSuccessful) +{ + AddWordToDictionary(Convert(L"\x01f1IE")); // u01f1 is Latin captial letter Dz + + CHECK( IsWordInDictionary(Convert(L"\x01f1IE")) ); // u01f1 is Latin captial letter Dz + CHECK(!IsWordInDictionary(Convert(L"\x01f2IE")) ); // u01f2 is Latin capital letter d with small letter z + CHECK(!IsWordInDictionary(Convert(L"\x01f2ie")) ); // u01f2 is Latin capital letter d with small letter z + CHECK(!IsWordInDictionary(Convert(L"\x01f3ie")) ); // u01f3 is Latin small letter dz + CHECK(!IsWordInDictionary(Convert(L"\x01f3Ie")) ); + + CHECK( IsWordInSession(Convert(L"\x01f1IE")) ); // u01f1 is Latin captial letter Dz + CHECK(!IsWordInSession(Convert(L"\x01f2IE")) ); // u01f2 is Latin capital letter d with small letter z + CHECK(!IsWordInSession(Convert(L"\x01f2ie")) ); // u01f2 is Latin capital letter d with small letter z + CHECK(!IsWordInSession(Convert(L"\x01f3ie")) ); // u01f3 is Latin small letter dz + CHECK(!IsWordInSession(Convert(L"\x01f3Ie")) ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_AddedTrueTitleCase_lowerCaseAndMixedCaseNotSuccessful) +{ + AddWordToDictionary(Convert(L"\x01f2ie")); // u01f2 is Latin capital letter d with small letter z + + CHECK( IsWordInDictionary(Convert(L"\x01f1IE")) ); // u01f1 is Latin captial letter Dz + CHECK(!IsWordInDictionary(Convert(L"\x01f2IE")) ); // u01f2 is Latin capital letter d with small letter z + CHECK( IsWordInDictionary(Convert(L"\x01f2ie")) ); // u01f2 is Latin capital letter d with small letter z + CHECK(!IsWordInDictionary(Convert(L"\x01f3ie")) ); // u01f3 is Latin small letter dz + CHECK(!IsWordInDictionary(Convert(L"\x01f3Ie")) ); + + CHECK( IsWordInSession(Convert(L"\x01f1IE")) ); // u01f1 is Latin captial letter Dz + CHECK(!IsWordInSession(Convert(L"\x01f2IE")) ); // u01f2 is Latin capital letter d with small letter z + CHECK( IsWordInSession(Convert(L"\x01f2ie")) ); // u01f2 is Latin capital letter d with small letter z + CHECK(!IsWordInSession(Convert(L"\x01f3ie")) ); // u01f3 is Latin small letter dz + CHECK(!IsWordInSession(Convert(L"\x01f3Ie")) ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + IsWordInDictionary_AddedLowerOfTrueTitleCase_MixedCaseNotSuccessful) +{ + AddWordToDictionary(Convert(L"\x01f3ie")); // u01f2 is Latin small letter dz + + CHECK( IsWordInDictionary(Convert(L"\x01f1IE")) ); // u01f1 is Latin captial letter Dz + CHECK(!IsWordInDictionary(Convert(L"\x01f2IE")) ); // u01f2 is Latin capital letter d with small letter z + CHECK( IsWordInDictionary(Convert(L"\x01f2ie")) ); // u01f2 is Latin capital letter d with small letter z + CHECK( IsWordInDictionary(Convert(L"\x01f3ie")) ); // u01f3 is Latin small letter dz + CHECK(!IsWordInDictionary(Convert(L"\x01f3Ie")) ); + + CHECK( IsWordInSession(Convert(L"\x01f1IE")) ); // u01f1 is Latin captial letter Dz + CHECK(!IsWordInSession(Convert(L"\x01f2IE")) ); // u01f2 is Latin capital letter d with small letter z + CHECK( IsWordInSession(Convert(L"\x01f2ie")) ); // u01f2 is Latin capital letter d with small letter z + CHECK( IsWordInSession(Convert(L"\x01f3ie")) ); // u01f3 is Latin small letter dz + CHECK(!IsWordInSession(Convert(L"\x01f3Ie")) ); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +// Capitalization on Suggestions +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedAllCaps_WordAllCaps_SuggestionAllCaps) +{ + AddWordToDictionary("CIA"); + + std::vector suggestions = GetSuggestionsFromWord("CEA"); + + std::vector expected; + expected.push_back("CIA"); + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedAllCaps_WordTitleCase_SuggestionAllCaps) +{ + AddWordToDictionary("CIA"); + + std::vector suggestions = GetSuggestionsFromWord("Cea"); + + std::vector expected; + expected.push_back("CIA"); + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedAllCaps_WordLowerCase_SuggestionAllCaps) +{ + AddWordToDictionary("CIA"); + + std::vector suggestions = GetSuggestionsFromWord("cea"); + + std::vector expected; + expected.push_back("CIA"); + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedTitleCase_WordAllCaps_SuggestionAllCaps) +{ + AddWordToDictionary("Eric"); + + std::vector suggestions = GetSuggestionsFromWord("RIC"); + + std::vector expected; + expected.push_back("ERIC"); + CHECK_EQUAL(expected.size(), suggestions.size()); + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedTitleCase_WordTitleCase_SuggestionTitleCase) +{ + AddWordToDictionary("Eric"); + + std::vector suggestions = GetSuggestionsFromWord("Ric"); + + std::vector expected; + expected.push_back("Eric"); + CHECK_EQUAL(expected.size(), suggestions.size()); + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedTitleCase_WordLowerCase_SuggestionTitleCase) +{ + AddWordToDictionary("Eric"); + + std::vector suggestions = GetSuggestionsFromWord("ric"); + + std::vector expected; + expected.push_back("Eric"); + CHECK_EQUAL(expected.size(), suggestions.size()); + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedLowerCase_WordAllCaps_SuggestionAllCaps) +{ + AddWordToDictionary("rice"); + + std::vector suggestions = GetSuggestionsFromWord("RIC"); + + std::vector expected; + expected.push_back("RICE"); + CHECK_EQUAL(expected.size(), suggestions.size()); + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedLowerCase_WordTitleCase_SuggestionTitleCase) +{ + AddWordToDictionary("rice"); + + std::vector suggestions = GetSuggestionsFromWord("Ric"); + + std::vector expected; + expected.push_back("Rice"); + CHECK_EQUAL(expected.size(), suggestions.size()); + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedLowerCase_WordLowerCase_SuggestionLowerCase) +{ + AddWordToDictionary("rice"); + + std::vector suggestions = GetSuggestionsFromWord("ric"); + + std::vector expected; + expected.push_back("rice"); + CHECK_EQUAL(expected.size(), suggestions.size()); + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); +} + + + + + + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedAllCapsOfTrueTitleCase_WordAllCaps_SuggestionAllCaps) +{ + AddWordToDictionary(Convert(L"\x01f1IE")); // u01f1 is Latin captial letter Dz + + std::vector suggestions = GetSuggestionsFromWord("RIE"); + + std::vector expected; + expected.push_back(Convert(L"\x01f1IE")); // u01f1 is Latin captial letter Dz + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedAllCapsOfTrueTitleCase_WordTitleCase_SuggestionAllCaps) +{ + AddWordToDictionary(Convert(L"\x01f1IE")); // u01f1 is Latin captial letter Dz + + std::vector suggestions = GetSuggestionsFromWord("Rie"); + + std::vector expected; + expected.push_back(Convert(L"\x01f1IE")); // u01f1 is Latin captial letter Dz + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedAllCapsOfTrueTitleCase_WordLowerCase_SuggestionAllCaps) +{ + AddWordToDictionary(Convert(L"\x01f1IE")); // u01f1 is Latin captial letter Dz + + std::vector suggestions = GetSuggestionsFromWord("rie"); + + std::vector expected; + expected.push_back(Convert(L"\x01f1IE")); // u01f1 is Latin captial letter Dz + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedTitleCaseOfTrueTitleCase_WordAllCaps_SuggestionAllCaps) +{ + AddWordToDictionary(Convert(L"\x01f2ie")); // u01f2 is Latin capital letter d with small letter z + + std::vector suggestions = GetSuggestionsFromWord("RIE"); + + std::vector expected; + expected.push_back(Convert(L"\x01f1IE")); // u01f1 is Latin captial letter Dz + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedTitleCaseOfTrueTitleCase_WordTitleCase_SuggestionTitleCase) +{ + AddWordToDictionary(Convert(L"\x01f2ie")); // u01f2 is Latin capital letter d with small letter z + + std::vector suggestions = GetSuggestionsFromWord("Rie"); + + std::vector expected; + expected.push_back(Convert(L"\x01f2ie")); // u01f2 is Latin capital letter d with small letter z + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedTitleCaseOfTrueTitleCase_WordLowerCase_SuggestionTitleCase) +{ + AddWordToDictionary(Convert(L"\x01f2ie")); // u01f2 is Latin capital letter d with small letter z + + std::vector suggestions = GetSuggestionsFromWord("rie"); + + std::vector expected; + expected.push_back(Convert(L"\x01f2ie")); // u01f2 is Latin capital letter d with small letter z + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedLowerCaseOfTrueTitleCase_WordAllCaps_SuggestionAllCaps) +{ + AddWordToDictionary(Convert(L"\x01f3ie")); // u01f3 is Latin small letter dz + + std::vector suggestions = GetSuggestionsFromWord("RIE"); + + std::vector expected; + expected.push_back(Convert(L"\x01f1IE")); // u01f1 is Latin captial letter Dz + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedLowerCaseOfTrueTitleCase_WordTitleCase_SuggestionTitleCase) +{ + AddWordToDictionary(Convert(L"\x01f3ie")); // u01f3 is Latin small letter dz + + std::vector suggestions = GetSuggestionsFromWord("Rie"); + + std::vector expected; + expected.push_back(Convert(L"\x01f2ie")); // u01f2 is Latin capital letter d with small letter z + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedLowerCaseOfTrueTitleCase_WordLowerCase_SuggestionLowerCase) +{ + AddWordToDictionary(Convert(L"\x01f3ie")); // u01f3 is Latin small letter dz + + std::vector suggestions = GetSuggestionsFromWord("rie"); + + std::vector expected; + expected.push_back(Convert(L"\x01f3ie")); // u01f3 is Latin small letter dz + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedAllCapsWithPrefix_WordLowerCase_SuggestionAllCaps) +{ + AddWordToDictionary("CIAL"); + AddWordToDictionary("CIALAND"); + + std::vector suggestions = GetSuggestionsFromWord("ceal"); + + std::vector expected; + expected.push_back("CIAL"); + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + GetSuggestions_AddedTitleCaseWithPrefix_WordLowerCase_SuggestionTitlecase) +{ + AddWordToDictionary("Eric"); + AddWordToDictionary("Ericson"); + + std::vector suggestions = GetSuggestionsFromWord("eruc"); + + std::vector expected; + expected.push_back("Eric"); + CHECK_EQUAL(expected.size(), suggestions.size()); + if(expected.size() == suggestions.size()) + { + CHECK_ARRAY_EQUAL(expected, suggestions, expected.size()); + } +} + +///////////////////////////////////////////////////////////////////////////// +// Remove from PWL +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_SharedPrefix1) +{ + AddWordToDictionary("help"); + AddWordToDictionary("hello"); + + CHECK( IsWordInDictionary("help") ); + CHECK( IsWordInDictionary("hello") ); + + RemoveWordFromDictionary("help"); + + CHECK(!IsWordInDictionary("help") ); + CHECK( IsWordInDictionary("hello") ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_SharedPrefix2) +{ + AddWordToDictionary("help"); + AddWordToDictionary("hello"); + + CHECK( IsWordInDictionary("help") ); + CHECK( IsWordInDictionary("hello") ); + + RemoveWordFromDictionary("hello"); + + CHECK( IsWordInDictionary("help") ); + CHECK(!IsWordInDictionary("hello") ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_SharedPrefix3) +{ + AddWordToDictionary("help"); + AddWordToDictionary("hello"); + AddWordToDictionary("helm"); + + CHECK( IsWordInDictionary("help") ); + CHECK( IsWordInDictionary("hello") ); + CHECK( IsWordInDictionary("helm") ); + + RemoveWordFromDictionary("hello"); + + CHECK( IsWordInDictionary("help") ); + CHECK(!IsWordInDictionary("hello") ); + CHECK( IsWordInDictionary("helm") ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_SharedPrefix4) +{ + AddWordToDictionary("help"); + AddWordToDictionary("hello"); + AddWordToDictionary("helm"); + + CHECK( IsWordInDictionary("help") ); + CHECK( IsWordInDictionary("hello") ); + CHECK( IsWordInDictionary("helm") ); + + RemoveWordFromDictionary("help"); + + CHECK(!IsWordInDictionary("help") ); + CHECK( IsWordInDictionary("hello") ); + CHECK( IsWordInDictionary("helm") ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_SingleWord) +{ + AddWordToDictionary("hello"); + + CHECK( IsWordInDictionary("hello") ); + + RemoveWordFromDictionary("hello"); + + CHECK(!IsWordInDictionary("hello") ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_MultipleWords1) +{ + AddWordToDictionary("special"); + AddWordToDictionary("hello"); + + CHECK( IsWordInDictionary("special") ); + CHECK( IsWordInDictionary("hello") ); + + RemoveWordFromDictionary("hello"); + + CHECK( IsWordInDictionary("special") ); + CHECK(!IsWordInDictionary("hello") ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_MultipleWords2) +{ + AddWordToDictionary("special"); + AddWordToDictionary("hello"); + + CHECK( IsWordInDictionary("special") ); + CHECK( IsWordInDictionary("hello") ); + + RemoveWordFromDictionary("special"); + + CHECK(!IsWordInDictionary("special") ); + CHECK( IsWordInDictionary("hello") ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_ProperPrefix1) +{ + AddWordToDictionary("ant"); + AddWordToDictionary("anteater"); + + CHECK( IsWordInDictionary("ant") ); + CHECK( IsWordInDictionary("anteater") ); + + RemoveWordFromDictionary("ant"); + + CHECK(!IsWordInDictionary("ant") ); + CHECK( IsWordInDictionary("anteater") ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_ProperPrefix2) +{ + AddWordToDictionary("anteater"); + AddWordToDictionary("ant"); + + CHECK( IsWordInDictionary("ant") ); + CHECK( IsWordInDictionary("anteater") ); + + RemoveWordFromDictionary("ant"); + + CHECK(!IsWordInDictionary("ant") ); + CHECK( IsWordInDictionary("anteater") ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_ProperPrefix3) +{ + AddWordToDictionary("ant"); + AddWordToDictionary("anteater"); + + CHECK( IsWordInDictionary("ant") ); + CHECK( IsWordInDictionary("anteater") ); + + RemoveWordFromDictionary("anteater"); + + CHECK( IsWordInDictionary("ant") ); + CHECK(!IsWordInDictionary("anteater") ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_ProperPrefix4) +{ + AddWordToDictionary("anteater"); + AddWordToDictionary("ant"); + + CHECK( IsWordInDictionary("ant") ); + CHECK( IsWordInDictionary("anteater") ); + + RemoveWordFromDictionary("anteater"); + + CHECK( IsWordInDictionary("ant") ); + CHECK(!IsWordInDictionary("anteater") ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_ItemRemovedFromFile) +{ + std::vector sWords; + sWords.push_back("cat"); + sWords.push_back("hat"); + sWords.push_back("that"); + sWords.push_back("bat"); + sWords.push_back("tot"); + + std::vector::const_iterator removed = sWords.insert(sWords.begin()+2, "hello"); + AddWordsToDictionary(sWords); + + RemoveWordFromDictionary("hello"); + + ReloadTestDictionary(); // to see what actually persisted + + for(std::vector::const_iterator itWord = sWords.begin(); itWord != removed; ++itWord){ + CHECK( IsWordInDictionary(*itWord) ); + } + CHECK(!IsWordInDictionary(*removed) ); + + for(std::vector::const_iterator itWord = removed+1; itWord != sWords.end(); ++itWord){ + CHECK(IsWordInDictionary(*itWord) ); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_ItemRemovedFromBeginningOfFile) +{ + std::vector sWords; + sWords.push_back("cat"); + sWords.push_back("hat"); + sWords.push_back("that"); + sWords.push_back("bat"); + sWords.push_back("tot"); + + std::vector::const_iterator removed = sWords.insert(sWords.begin(), "hello"); + AddWordsToDictionary(sWords); + + RemoveWordFromDictionary("hello"); + + ReloadTestDictionary(); // to see what actually persisted + + CHECK(!IsWordInDictionary(*removed) ); + + for(std::vector::const_iterator itWord = removed+1; itWord != sWords.end(); ++itWord){ + CHECK(IsWordInDictionary(*itWord) ); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_ItemRemovedFromBeginningOfFileWithBOM) +{ + char* Utf8Bom = "\xef\xbb\xbf"; + + std::vector sWords; + sWords.push_back("hello"); + sWords.push_back("cat"); + sWords.push_back("hat"); + + Sleep(1000); // FAT systems have a 2 second resolution + // NTFS is appreciably faster but no specs on what it is exactly + // c runtime library's time_t has a 1 second resolution + FILE * f = g_fopen(GetPersonalDictFileName().c_str(), "at"); + if(f) + { + fputs(Utf8Bom, f); + for(std::vector::const_iterator + itWord = sWords.begin(); + itWord != sWords.end(); + ++itWord) + { + if(itWord != sWords.begin()){ + fputc('\n', f); + } + fputs(itWord->c_str(), f); + } + fclose(f); + } + + RemoveWordFromDictionary("hello"); + + ReloadTestDictionary(); // to see what actually persisted + + CHECK(!IsWordInDictionary("hello") ); + + for(std::vector::const_iterator itWord = sWords.begin()+1; itWord != sWords.end(); ++itWord){ + CHECK(IsWordInDictionary(*itWord) ); + } +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_ItemRemovedFromEndOfFile) +{ + std::vector sWords; + sWords.push_back("cat"); + sWords.push_back("hat"); + sWords.push_back("that"); + sWords.push_back("bat"); + sWords.push_back("tot"); + + std::vector::const_iterator removed = sWords.insert(sWords.end(), "hello"); + AddWordsToDictionary(sWords); + + RemoveWordFromDictionary("hello"); + + ReloadTestDictionary(); // to see what actually persisted + + for(std::vector::const_iterator itWord = sWords.begin(); itWord != removed; ++itWord){ + CHECK( IsWordInDictionary(*itWord) ); + } + CHECK(!IsWordInDictionary(*removed) ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_ItemRemovedFromEndOfFile_ExternalSetup) +{ + std::vector sWords; + sWords.push_back("cat"); + sWords.push_back("hat"); + sWords.push_back("that"); + sWords.push_back("bat"); + sWords.push_back("tot"); + + std::vector::const_iterator removed = sWords.insert(sWords.end(), "hello"); + ExternalAddWordsToDictionary(sWords); + + RemoveWordFromDictionary("hello"); + + ReloadTestDictionary(); // to see what actually persisted + + for(std::vector::const_iterator itWord = sWords.begin(); itWord != removed; ++itWord){ + CHECK( IsWordInDictionary(*itWord) ); + } + CHECK(!IsWordInDictionary(*removed) ); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlRemove_FileHasProperSubset_ItemRemovedFromFile) +{ + std::vector sWords; + sWords.push_back("cat"); + sWords.push_back("hat"); + sWords.push_back("that"); + sWords.push_back("bat"); + sWords.push_back("tot"); + sWords.push_back("anteater"); + + std::vector::const_iterator removed = sWords.insert(sWords.end(), "ant"); + AddWordsToDictionary(sWords); + RemoveWordFromDictionary("ant"); + + ReloadTestDictionary(); // to see what actually persisted + + for(std::vector::const_iterator itWord = sWords.begin(); itWord != removed; ++itWord){ + CHECK( IsWordInDictionary(*itWord) ); + } + CHECK(!IsWordInDictionary(*removed) ); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Pwl Edit distance +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_SubstituteFirstChar) +{ + std::vector sWords; + sWords.push_back("cat"); //1 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("catsup"); //4 + + std::vector suggestions = GetSuggestionsFromWord("tat"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_SubstituteFirstChar_Insert1) +{ + std::vector sWords; + sWords.push_back("cats"); //2 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("catsup"); //4 + + std::vector suggestions = GetSuggestionsFromWord("tat"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_SubstituteFirstChar_Insert2) +{ + std::vector sWords; + sWords.push_back("catch"); //3 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("catchy"); //4 + + std::vector suggestions = GetSuggestionsFromWord("tat"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_Insert1) +{ + std::vector sWords; + sWords.push_back("tad"); //1 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("taddle"); //4 + + std::vector suggestions = GetSuggestionsFromWord("ta"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_Insert2) +{ + std::vector sWords; + sWords.push_back("tote"); //2 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("totems"); //4 + + std::vector suggestions = GetSuggestionsFromWord("to"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_Insert3) +{ + std::vector sWords; + sWords.push_back("catch"); //3 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("catchy"); //4 + + std::vector suggestions = GetSuggestionsFromWord("ca"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_Delete1) +{ + std::vector sWords; + sWords.push_back("tape"); //1 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("tapestry"); //4 + + std::vector suggestions = GetSuggestionsFromWord("tapen"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_Delete2) +{ + std::vector sWords; + sWords.push_back("tot"); //2 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("totality"); //4 + + std::vector suggestions = GetSuggestionsFromWord("totil"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_Delete3) +{ + std::vector sWords; + sWords.push_back("cat"); //3 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("catcher"); //4 + + std::vector suggestions = GetSuggestionsFromWord("catsip"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_Substitute1) +{ + std::vector sWords; + sWords.push_back("small"); //1 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("smallest"); //4 + + std::vector suggestions = GetSuggestionsFromWord("skall"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_Substitute2) +{ + std::vector sWords; + sWords.push_back("catch"); //2 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("catcher"); //4 + + std::vector suggestions = GetSuggestionsFromWord("cafdh"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_Substitute3) +{ + std::vector sWords; + sWords.push_back("hasten"); //3 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("hastens"); //4 + + std::vector suggestions = GetSuggestionsFromWord("hasopo"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_Transpose1) +{ + std::vector sWords; + sWords.push_back("small"); //1 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("smallest"); //4 + + std::vector suggestions = GetSuggestionsFromWord("smlal"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_Transpose2) +{ + std::vector sWords; + sWords.push_back("catch"); //2 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("catcher"); //4 + + std::vector suggestions = GetSuggestionsFromWord("acthc"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_HasProperSubset_Transpose3) +{ + std::vector sWords; + sWords.push_back("hasten"); //3 + + AddWordsToDictionary(sWords); + + AddWordToDictionary("hastens"); //4 + + std::vector suggestions = GetSuggestionsFromWord("ahtsne"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_Transpose1Insert2) +{ + std::vector sWords; + sWords.push_back("catch"); //3 + + AddWordsToDictionary(sWords); + + std::vector suggestions = GetSuggestionsFromWord("act"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + +TEST_FIXTURE(EnchantPwl_TestFixture, + PwlSuggest_Transpose2) +{ + std::vector sWords; + sWords.push_back("catch"); //2 + + AddWordsToDictionary(sWords); + + std::vector suggestions = GetSuggestionsFromWord("acthc"); + + CHECK_EQUAL(sWords.size(), suggestions.size()); + + std::sort(sWords.begin(), sWords.end()); + std::sort(suggestions.begin(), suggestions.end()); + + CHECK_ARRAY_EQUAL(sWords, suggestions, std::min(sWords.size(), suggestions.size())); +} + -- 2.7.4