diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:36:47 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:36:47 +0000 |
commit | 0441d265f2bb9da249c7abf333f0f771fadb4ab5 (patch) | |
tree | 3f3789daa2f6db22da6e55e92bee0062a7d613fe /src/lib | |
parent | Initial commit. (diff) | |
download | dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.tar.xz dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.zip |
Adding upstream version 1:2.3.21+dfsg1.upstream/1%2.3.21+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib')
420 files changed, 122193 insertions, 0 deletions
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am new file mode 100644 index 0000000..ac5c62b --- /dev/null +++ b/src/lib/Makefile.am @@ -0,0 +1,481 @@ +AM_CPPFLAGS = \ + $(LIBUNWIND_CFLAGS) + +noinst_LTLIBRARIES = liblib.la + +BUILT_SOURCES = $(srcdir)/unicodemap.c \ + event-filter-lexer.c \ + event-filter-parser.c \ + event-filter-parser.h + +EXTRA_DIST = unicodemap.c unicodemap.pl UnicodeData.txt + +# Squelch autoconf error about using .[ly] sources but not defining $(LEX) +# and $(YACC). Using false here avoids accidental use. +LEX=/bin/false +YACC=/bin/false + +# We use custom rules here because we want to use flex and bison instead +# of lex and yacc (or bison in yacc-compatibility mode). Both flex and +# bison can handle properly naming the generated files, and it is simpler +# and cleaner to make this rule ourselves instead of working around ylwrap +# and yywrap's antiquated notion of what is hapenning. +.l.c: + $(AM_V_GEN)$(FLEX) -o $@ $< + +.y.c: + $(AM_V_GEN)$(BISON) -o $@ $< + +# Bison generates both a header and a .c file. Without the following +# dependency, anything including the header will race the bison process. +event-filter-parser.h: event-filter-parser.c + +$(srcdir)/UnicodeData.txt: + test -f $@ || wget -O $@ https://dovecot.org/res/UnicodeData.txt + +$(srcdir)/unicodemap.c: $(srcdir)/unicodemap.pl $(srcdir)/UnicodeData.txt + perl $(srcdir)/unicodemap.pl < $(srcdir)/UnicodeData.txt > $@ + +liblib_la_LIBADD = $(LIBUNWIND_LIBS) +liblib_la_SOURCES = \ + array.c \ + aqueue.c \ + askpass.c \ + backtrace-string.c \ + base32.c \ + base64.c \ + bits.c \ + bsearch-insert-pos.c \ + buffer.c \ + buffer-istream.c \ + child-wait.c \ + compat.c \ + connection.c \ + cpu-limit.c \ + crc32.c \ + data-stack.c \ + eacces-error.c \ + env-util.c \ + event-filter.c \ + event-filter-lexer.l \ + event-filter-parser.y \ + event-log.c \ + execv-const.c \ + failures.c \ + fd-util.c \ + fdatasync-path.c \ + fdpass.c \ + file-cache.c \ + file-create-locked.c \ + file-copy.c \ + file-dotlock.c \ + file-lock.c \ + file-set-size.c \ + guid.c \ + hash.c \ + hash-format.c \ + hash-method.c \ + hash2.c \ + hex-binary.c \ + hex-dec.c \ + hmac.c \ + hmac-cram-md5.c \ + home-expand.c \ + hook-build.c \ + hostpid.c \ + imem.c \ + ipwd.c \ + iostream.c \ + iostream-pump.c \ + iostream-proxy.c \ + iostream-rawlog.c \ + iostream-temp.c \ + iso8601-date.c \ + istream.c \ + istream-base64-decoder.c \ + istream-base64-encoder.c \ + istream-callback.c \ + istream-chain.c \ + istream-concat.c \ + istream-crlf.c \ + istream-data.c \ + istream-failure-at.c \ + istream-file.c \ + istream-hash.c \ + istream-jsonstr.c \ + istream-limit.c \ + istream-multiplex.c \ + istream-rawlog.c \ + istream-seekable.c \ + istream-sized.c \ + istream-tee.c \ + istream-try.c \ + istream-timeout.c \ + istream-unix.c \ + ioloop.c \ + ioloop-iolist.c \ + ioloop-notify-none.c \ + ioloop-notify-fd.c \ + ioloop-notify-inotify.c \ + ioloop-notify-kqueue.c \ + ioloop-poll.c \ + ioloop-select.c \ + ioloop-epoll.c \ + ioloop-kqueue.c \ + json-parser.c \ + json-tree.c \ + lib.c \ + lib-event.c \ + lib-signals.c \ + log-throttle.c \ + md4.c \ + md5.c \ + memarea.c \ + mempool.c \ + mempool-allocfree.c \ + mempool-alloconly.c \ + mempool-datastack.c \ + mempool-system.c \ + mempool-unsafe-datastack.c \ + mkdir-parents.c \ + mmap-anon.c \ + mmap-util.c \ + module-dir.c \ + mountpoint.c \ + net.c \ + nfs-workarounds.c \ + numpack.c \ + ostream.c \ + ostream-buffer.c \ + ostream-failure-at.c \ + ostream-file.c \ + ostream-hash.c \ + ostream-multiplex.c \ + ostream-null.c \ + ostream-rawlog.c \ + ostream-unix.c \ + ostream-wrapper.c \ + path-util.c \ + pkcs5.c \ + primes.c \ + printf-format-fix.c \ + process-stat.c \ + process-title.c \ + priorityq.c \ + randgen.c \ + rand.c \ + read-full.c \ + restrict-access.c \ + restrict-process-size.c \ + safe-memset.c \ + safe-mkdir.c \ + safe-mkstemp.c \ + sendfile-util.c \ + seq-range-array.c \ + seq-set-builder.c \ + sha1.c \ + sha2.c \ + sha3.c \ + sleep.c \ + sort.c \ + stats-dist.c \ + str.c \ + str-find.c \ + str-sanitize.c \ + str-table.c \ + strescape.c \ + strfuncs.c \ + strnum.c \ + time-util.c \ + unix-socket-create.c \ + unlink-directory.c \ + unlink-old-files.c \ + unichar.c \ + uri-util.c \ + utc-offset.c \ + utc-mktime.c \ + var-expand.c \ + var-expand-if.c \ + wildcard-match.c \ + write-full.c + +headers = \ + aqueue.h \ + array.h \ + array-decl.h \ + askpass.h \ + backtrace-string.h \ + base32.h \ + base64.h \ + bits.h \ + bsearch-insert-pos.h \ + buffer.h \ + byteorder.h \ + child-wait.h \ + compat.h \ + connection.h \ + cpu-limit.h \ + crc32.h \ + data-stack.h \ + eacces-error.h \ + env-util.h \ + event-filter.h \ + event-filter-parser.h \ + event-filter-private.h \ + event-log.h \ + execv-const.h \ + failures.h \ + failures-private.h \ + fd-util.h \ + fdatasync-path.h \ + fdpass.h \ + file-cache.h \ + file-create-locked.h \ + file-copy.h \ + file-dotlock.h \ + file-lock.h \ + file-set-size.h \ + fsync-mode.h \ + guid.h \ + hash.h \ + hash-decl.h \ + hash-format.h \ + hash-method.h \ + hash2.h \ + hex-binary.h \ + hex-dec.h \ + hmac.h \ + hmac-cram-md5.h \ + home-expand.h \ + hook-build.h \ + hostpid.h \ + imem.h \ + ipwd.h \ + iostream.h \ + iostream-private.h \ + iostream-pump.h \ + iostream-proxy.h \ + iostream-rawlog.h \ + iostream-rawlog-private.h \ + iostream-temp.h \ + iso8601-date.h \ + istream.h \ + istream-base64.h \ + istream-callback.h \ + istream-chain.h \ + istream-concat.h \ + istream-crlf.h \ + istream-failure-at.h \ + istream-file-private.h \ + istream-hash.h \ + istream-jsonstr.h \ + istream-multiplex.h \ + istream-private.h \ + istream-rawlog.h \ + istream-seekable.h \ + istream-sized.h \ + istream-tee.h \ + istream-try.h \ + istream-timeout.h \ + istream-unix.h \ + ioloop.h \ + ioloop-iolist.h \ + ioloop-private.h \ + ioloop-notify-fd.h \ + json-parser.h \ + json-tree.h \ + lib.h \ + lib-event.h \ + lib-event-private.h \ + lib-signals.h \ + llist.h \ + log-throttle.h \ + macros.h \ + md4.h \ + md5.h \ + malloc-overflow.h \ + memarea.h \ + mempool.h \ + mkdir-parents.h \ + mmap-util.h \ + module-context.h \ + module-dir.h \ + mountpoint.h \ + net.h \ + nfs-workarounds.h \ + numpack.h \ + ostream.h \ + ostream-failure-at.h \ + ostream-file-private.h \ + ostream-hash.h \ + ostream-multiplex.h \ + ostream-private.h \ + ostream-null.h \ + ostream-rawlog.h \ + ostream-unix.h \ + ostream-wrapper.h \ + path-util.h \ + pkcs5.h \ + primes.h \ + printf-format-fix.h \ + process-stat.h \ + process-title.h \ + priorityq.h \ + randgen.h \ + read-full.h \ + restrict-access.h \ + restrict-process-size.h \ + safe-memset.h \ + safe-mkdir.h \ + safe-mkstemp.h \ + sendfile-util.h \ + seq-range-array.h \ + seq-set-builder.h \ + sha-common.h \ + sha1.h \ + sha2.h \ + sha3.h \ + sleep.h \ + sort.h \ + stats-dist.h \ + str.h \ + str-find.h \ + str-sanitize.h \ + str-table.h \ + strescape.h \ + strfuncs.h \ + strnum.h \ + time-util.h \ + unix-socket-create.h \ + unlink-directory.h \ + unlink-old-files.h \ + unichar.h \ + uri-util.h \ + utc-offset.h \ + utc-mktime.h \ + var-expand.h \ + var-expand-private.h \ + wildcard-match.h \ + write-full.h + +test_programs = test-lib +noinst_PROGRAMS = $(test_programs) + +test_lib_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-test + +test_libs = \ + ../lib-test/libtest.la \ + liblib.la + +test_lib_SOURCES = \ + test-lib.c \ + test-array.c \ + test-aqueue.c \ + test-backtrace.c \ + test-base32.c \ + test-base64.c \ + test-bits.c \ + test-bsearch-insert-pos.c \ + test-buffer.c \ + test-buffer-istream.c \ + test-byteorder.c \ + test-connection.c \ + test-crc32.c \ + test-cpu-limit.c \ + test-data-stack.c \ + test-env-util.c \ + test-event-category-register.c \ + test-event-filter.c \ + test-event-filter-expr.c \ + test-event-filter-merge.c \ + test-event-filter-parser.c \ + test-event-flatten.c \ + test-event-log.c \ + test-failures.c \ + test-fd-util.c \ + test-file-cache.c \ + test-file-create-locked.c \ + test-guid.c \ + test-hash.c \ + test-hash-format.c \ + test-hash-method.c \ + test-hmac.c \ + test-hex-binary.c \ + test-imem.c \ + test-ioloop.c \ + test-iso8601-date.c \ + test-iostream-pump.c \ + test-iostream-proxy.c \ + test-iostream-temp.c \ + test-istream.c \ + test-istream-base64-decoder.c \ + test-istream-base64-encoder.c \ + test-istream-chain.c \ + test-istream-concat.c \ + test-istream-crlf.c \ + test-istream-failure-at.c \ + test-istream-jsonstr.c \ + test-istream-multiplex.c \ + test-istream-seekable.c \ + test-istream-sized.c \ + test-istream-tee.c \ + test-istream-try.c \ + test-istream-unix.c \ + test-json-parser.c \ + test-json-tree.c \ + test-lib-event.c \ + test-lib-signals.c \ + test-llist.c \ + test-log-throttle.c \ + test-macros.c \ + test-malloc-overflow.c \ + test-memarea.c \ + test-mempool.c \ + test-mempool-allocfree.c \ + test-mempool-alloconly.c \ + test-pkcs5.c \ + test-net.c \ + test-numpack.c \ + test-ostream-buffer.c \ + test-ostream-failure-at.c \ + test-ostream-file.c \ + test-ostream-multiplex.c \ + test-multiplex.c \ + test-path-util.c \ + test-primes.c \ + test-printf-format-fix.c \ + test-priorityq.c \ + test-random.c \ + test-seq-range-array.c \ + test-seq-set-builder.c \ + test-stats-dist.c \ + test-str.c \ + test-strescape.c \ + test-strfuncs.c \ + test-strnum.c \ + test-str-find.c \ + test-str-sanitize.c \ + test-str-table.c \ + test-time-util.c \ + test-unichar.c \ + test-utc-mktime.c \ + test-uri.c \ + test-var-expand.c \ + test-wildcard-match.c + +test_headers = \ + test-lib.h \ + test-lib.inc + +test_lib_LDADD = $(test_libs) -lm +test_lib_DEPENDENCIES = $(test_libs) + +check-local: + for bin in $(test_programs); do \ + if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ + done + +pkginc_libdir=$(pkgincludedir) +pkginc_lib_HEADERS = $(headers) +noinst_HEADERS = $(test_headers) diff --git a/src/lib/Makefile.in b/src/lib/Makefile.in new file mode 100644 index 0000000..1773595 --- /dev/null +++ b/src/lib/Makefile.in @@ -0,0 +1,3711 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +noinst_PROGRAMS = $(am__EXEEXT_1) +subdir = src/lib +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ac_checktype2.m4 \ + $(top_srcdir)/m4/ac_typeof.m4 $(top_srcdir)/m4/arc4random.m4 \ + $(top_srcdir)/m4/blockdev.m4 $(top_srcdir)/m4/c99_vsnprintf.m4 \ + $(top_srcdir)/m4/clock_gettime.m4 $(top_srcdir)/m4/crypt.m4 \ + $(top_srcdir)/m4/crypt_xpg6.m4 $(top_srcdir)/m4/dbqlk.m4 \ + $(top_srcdir)/m4/dirent_dtype.m4 $(top_srcdir)/m4/dovecot.m4 \ + $(top_srcdir)/m4/fd_passing.m4 $(top_srcdir)/m4/fdatasync.m4 \ + $(top_srcdir)/m4/flexible_array_member.m4 \ + $(top_srcdir)/m4/glibc.m4 $(top_srcdir)/m4/gmtime_max.m4 \ + $(top_srcdir)/m4/gmtime_tm_gmtoff.m4 \ + $(top_srcdir)/m4/ioloop.m4 $(top_srcdir)/m4/iovec.m4 \ + $(top_srcdir)/m4/ipv6.m4 $(top_srcdir)/m4/libcap.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libwrap.m4 \ + $(top_srcdir)/m4/linux_mremap.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/mmap_write.m4 \ + $(top_srcdir)/m4/mntctl.m4 $(top_srcdir)/m4/modules.m4 \ + $(top_srcdir)/m4/notify.m4 $(top_srcdir)/m4/nsl.m4 \ + $(top_srcdir)/m4/off_t_max.m4 $(top_srcdir)/m4/pkg.m4 \ + $(top_srcdir)/m4/pr_set_dumpable.m4 \ + $(top_srcdir)/m4/q_quotactl.m4 $(top_srcdir)/m4/quota.m4 \ + $(top_srcdir)/m4/random.m4 $(top_srcdir)/m4/rlimit.m4 \ + $(top_srcdir)/m4/sendfile.m4 $(top_srcdir)/m4/size_t_signed.m4 \ + $(top_srcdir)/m4/sockpeercred.m4 $(top_srcdir)/m4/sql.m4 \ + $(top_srcdir)/m4/ssl.m4 $(top_srcdir)/m4/st_tim.m4 \ + $(top_srcdir)/m4/static_array.m4 $(top_srcdir)/m4/test_with.m4 \ + $(top_srcdir)/m4/time_t.m4 $(top_srcdir)/m4/typeof.m4 \ + $(top_srcdir)/m4/typeof_dev_t.m4 \ + $(top_srcdir)/m4/uoff_t_max.m4 $(top_srcdir)/m4/vararg.m4 \ + $(top_srcdir)/m4/want_apparmor.m4 \ + $(top_srcdir)/m4/want_bsdauth.m4 \ + $(top_srcdir)/m4/want_bzlib.m4 \ + $(top_srcdir)/m4/want_cassandra.m4 \ + $(top_srcdir)/m4/want_cdb.m4 \ + $(top_srcdir)/m4/want_checkpassword.m4 \ + $(top_srcdir)/m4/want_clucene.m4 $(top_srcdir)/m4/want_db.m4 \ + $(top_srcdir)/m4/want_gssapi.m4 $(top_srcdir)/m4/want_icu.m4 \ + $(top_srcdir)/m4/want_ldap.m4 $(top_srcdir)/m4/want_lua.m4 \ + $(top_srcdir)/m4/want_lz4.m4 $(top_srcdir)/m4/want_lzma.m4 \ + $(top_srcdir)/m4/want_mysql.m4 $(top_srcdir)/m4/want_pam.m4 \ + $(top_srcdir)/m4/want_passwd.m4 $(top_srcdir)/m4/want_pgsql.m4 \ + $(top_srcdir)/m4/want_prefetch.m4 \ + $(top_srcdir)/m4/want_shadow.m4 \ + $(top_srcdir)/m4/want_sodium.m4 $(top_srcdir)/m4/want_solr.m4 \ + $(top_srcdir)/m4/want_sqlite.m4 \ + $(top_srcdir)/m4/want_stemmer.m4 \ + $(top_srcdir)/m4/want_systemd.m4 \ + $(top_srcdir)/m4/want_textcat.m4 \ + $(top_srcdir)/m4/want_unwind.m4 $(top_srcdir)/m4/want_zlib.m4 \ + $(top_srcdir)/m4/want_zstd.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ + $(pkginc_lib_HEADERS) $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__EXEEXT_1 = test-lib$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +liblib_la_DEPENDENCIES = $(am__DEPENDENCIES_1) +am_liblib_la_OBJECTS = array.lo aqueue.lo askpass.lo \ + backtrace-string.lo base32.lo base64.lo bits.lo \ + bsearch-insert-pos.lo buffer.lo buffer-istream.lo \ + child-wait.lo compat.lo connection.lo cpu-limit.lo crc32.lo \ + data-stack.lo eacces-error.lo env-util.lo event-filter.lo \ + event-filter-lexer.lo event-filter-parser.lo event-log.lo \ + execv-const.lo failures.lo fd-util.lo fdatasync-path.lo \ + fdpass.lo file-cache.lo file-create-locked.lo file-copy.lo \ + file-dotlock.lo file-lock.lo file-set-size.lo guid.lo hash.lo \ + hash-format.lo hash-method.lo hash2.lo hex-binary.lo \ + hex-dec.lo hmac.lo hmac-cram-md5.lo home-expand.lo \ + hook-build.lo hostpid.lo imem.lo ipwd.lo iostream.lo \ + iostream-pump.lo iostream-proxy.lo iostream-rawlog.lo \ + iostream-temp.lo iso8601-date.lo istream.lo \ + istream-base64-decoder.lo istream-base64-encoder.lo \ + istream-callback.lo istream-chain.lo istream-concat.lo \ + istream-crlf.lo istream-data.lo istream-failure-at.lo \ + istream-file.lo istream-hash.lo istream-jsonstr.lo \ + istream-limit.lo istream-multiplex.lo istream-rawlog.lo \ + istream-seekable.lo istream-sized.lo istream-tee.lo \ + istream-try.lo istream-timeout.lo istream-unix.lo ioloop.lo \ + ioloop-iolist.lo ioloop-notify-none.lo ioloop-notify-fd.lo \ + ioloop-notify-inotify.lo ioloop-notify-kqueue.lo \ + ioloop-poll.lo ioloop-select.lo ioloop-epoll.lo \ + ioloop-kqueue.lo json-parser.lo json-tree.lo lib.lo \ + lib-event.lo lib-signals.lo log-throttle.lo md4.lo md5.lo \ + memarea.lo mempool.lo mempool-allocfree.lo \ + mempool-alloconly.lo mempool-datastack.lo mempool-system.lo \ + mempool-unsafe-datastack.lo mkdir-parents.lo mmap-anon.lo \ + mmap-util.lo module-dir.lo mountpoint.lo net.lo \ + nfs-workarounds.lo numpack.lo ostream.lo ostream-buffer.lo \ + ostream-failure-at.lo ostream-file.lo ostream-hash.lo \ + ostream-multiplex.lo ostream-null.lo ostream-rawlog.lo \ + ostream-unix.lo ostream-wrapper.lo path-util.lo pkcs5.lo \ + primes.lo printf-format-fix.lo process-stat.lo \ + process-title.lo priorityq.lo randgen.lo rand.lo read-full.lo \ + restrict-access.lo restrict-process-size.lo safe-memset.lo \ + safe-mkdir.lo safe-mkstemp.lo sendfile-util.lo \ + seq-range-array.lo seq-set-builder.lo sha1.lo sha2.lo sha3.lo \ + sleep.lo sort.lo stats-dist.lo str.lo str-find.lo \ + str-sanitize.lo str-table.lo strescape.lo strfuncs.lo \ + strnum.lo time-util.lo unix-socket-create.lo \ + unlink-directory.lo unlink-old-files.lo unichar.lo uri-util.lo \ + utc-offset.lo utc-mktime.lo var-expand.lo var-expand-if.lo \ + wildcard-match.lo write-full.lo +liblib_la_OBJECTS = $(am_liblib_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +am_test_lib_OBJECTS = test_lib-test-lib.$(OBJEXT) \ + test_lib-test-array.$(OBJEXT) test_lib-test-aqueue.$(OBJEXT) \ + test_lib-test-backtrace.$(OBJEXT) \ + test_lib-test-base32.$(OBJEXT) test_lib-test-base64.$(OBJEXT) \ + test_lib-test-bits.$(OBJEXT) \ + test_lib-test-bsearch-insert-pos.$(OBJEXT) \ + test_lib-test-buffer.$(OBJEXT) \ + test_lib-test-buffer-istream.$(OBJEXT) \ + test_lib-test-byteorder.$(OBJEXT) \ + test_lib-test-connection.$(OBJEXT) \ + test_lib-test-crc32.$(OBJEXT) \ + test_lib-test-cpu-limit.$(OBJEXT) \ + test_lib-test-data-stack.$(OBJEXT) \ + test_lib-test-env-util.$(OBJEXT) \ + test_lib-test-event-category-register.$(OBJEXT) \ + test_lib-test-event-filter.$(OBJEXT) \ + test_lib-test-event-filter-expr.$(OBJEXT) \ + test_lib-test-event-filter-merge.$(OBJEXT) \ + test_lib-test-event-filter-parser.$(OBJEXT) \ + test_lib-test-event-flatten.$(OBJEXT) \ + test_lib-test-event-log.$(OBJEXT) \ + test_lib-test-failures.$(OBJEXT) \ + test_lib-test-fd-util.$(OBJEXT) \ + test_lib-test-file-cache.$(OBJEXT) \ + test_lib-test-file-create-locked.$(OBJEXT) \ + test_lib-test-guid.$(OBJEXT) test_lib-test-hash.$(OBJEXT) \ + test_lib-test-hash-format.$(OBJEXT) \ + test_lib-test-hash-method.$(OBJEXT) \ + test_lib-test-hmac.$(OBJEXT) \ + test_lib-test-hex-binary.$(OBJEXT) \ + test_lib-test-imem.$(OBJEXT) test_lib-test-ioloop.$(OBJEXT) \ + test_lib-test-iso8601-date.$(OBJEXT) \ + test_lib-test-iostream-pump.$(OBJEXT) \ + test_lib-test-iostream-proxy.$(OBJEXT) \ + test_lib-test-iostream-temp.$(OBJEXT) \ + test_lib-test-istream.$(OBJEXT) \ + test_lib-test-istream-base64-decoder.$(OBJEXT) \ + test_lib-test-istream-base64-encoder.$(OBJEXT) \ + test_lib-test-istream-chain.$(OBJEXT) \ + test_lib-test-istream-concat.$(OBJEXT) \ + test_lib-test-istream-crlf.$(OBJEXT) \ + test_lib-test-istream-failure-at.$(OBJEXT) \ + test_lib-test-istream-jsonstr.$(OBJEXT) \ + test_lib-test-istream-multiplex.$(OBJEXT) \ + test_lib-test-istream-seekable.$(OBJEXT) \ + test_lib-test-istream-sized.$(OBJEXT) \ + test_lib-test-istream-tee.$(OBJEXT) \ + test_lib-test-istream-try.$(OBJEXT) \ + test_lib-test-istream-unix.$(OBJEXT) \ + test_lib-test-json-parser.$(OBJEXT) \ + test_lib-test-json-tree.$(OBJEXT) \ + test_lib-test-lib-event.$(OBJEXT) \ + test_lib-test-lib-signals.$(OBJEXT) \ + test_lib-test-llist.$(OBJEXT) \ + test_lib-test-log-throttle.$(OBJEXT) \ + test_lib-test-macros.$(OBJEXT) \ + test_lib-test-malloc-overflow.$(OBJEXT) \ + test_lib-test-memarea.$(OBJEXT) \ + test_lib-test-mempool.$(OBJEXT) \ + test_lib-test-mempool-allocfree.$(OBJEXT) \ + test_lib-test-mempool-alloconly.$(OBJEXT) \ + test_lib-test-pkcs5.$(OBJEXT) test_lib-test-net.$(OBJEXT) \ + test_lib-test-numpack.$(OBJEXT) \ + test_lib-test-ostream-buffer.$(OBJEXT) \ + test_lib-test-ostream-failure-at.$(OBJEXT) \ + test_lib-test-ostream-file.$(OBJEXT) \ + test_lib-test-ostream-multiplex.$(OBJEXT) \ + test_lib-test-multiplex.$(OBJEXT) \ + test_lib-test-path-util.$(OBJEXT) \ + test_lib-test-primes.$(OBJEXT) \ + test_lib-test-printf-format-fix.$(OBJEXT) \ + test_lib-test-priorityq.$(OBJEXT) \ + test_lib-test-random.$(OBJEXT) \ + test_lib-test-seq-range-array.$(OBJEXT) \ + test_lib-test-seq-set-builder.$(OBJEXT) \ + test_lib-test-stats-dist.$(OBJEXT) test_lib-test-str.$(OBJEXT) \ + test_lib-test-strescape.$(OBJEXT) \ + test_lib-test-strfuncs.$(OBJEXT) \ + test_lib-test-strnum.$(OBJEXT) \ + test_lib-test-str-find.$(OBJEXT) \ + test_lib-test-str-sanitize.$(OBJEXT) \ + test_lib-test-str-table.$(OBJEXT) \ + test_lib-test-time-util.$(OBJEXT) \ + test_lib-test-unichar.$(OBJEXT) \ + test_lib-test-utc-mktime.$(OBJEXT) test_lib-test-uri.$(OBJEXT) \ + test_lib-test-var-expand.$(OBJEXT) \ + test_lib-test-wildcard-match.$(OBJEXT) +test_lib_OBJECTS = $(am_test_lib_OBJECTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/aqueue.Plo ./$(DEPDIR)/array.Plo \ + ./$(DEPDIR)/askpass.Plo ./$(DEPDIR)/backtrace-string.Plo \ + ./$(DEPDIR)/base32.Plo ./$(DEPDIR)/base64.Plo \ + ./$(DEPDIR)/bits.Plo ./$(DEPDIR)/bsearch-insert-pos.Plo \ + ./$(DEPDIR)/buffer-istream.Plo ./$(DEPDIR)/buffer.Plo \ + ./$(DEPDIR)/child-wait.Plo ./$(DEPDIR)/compat.Plo \ + ./$(DEPDIR)/connection.Plo ./$(DEPDIR)/cpu-limit.Plo \ + ./$(DEPDIR)/crc32.Plo ./$(DEPDIR)/data-stack.Plo \ + ./$(DEPDIR)/eacces-error.Plo ./$(DEPDIR)/env-util.Plo \ + ./$(DEPDIR)/event-filter-lexer.Plo \ + ./$(DEPDIR)/event-filter-parser.Plo \ + ./$(DEPDIR)/event-filter.Plo ./$(DEPDIR)/event-log.Plo \ + ./$(DEPDIR)/execv-const.Plo ./$(DEPDIR)/failures.Plo \ + ./$(DEPDIR)/fd-util.Plo ./$(DEPDIR)/fdatasync-path.Plo \ + ./$(DEPDIR)/fdpass.Plo ./$(DEPDIR)/file-cache.Plo \ + ./$(DEPDIR)/file-copy.Plo ./$(DEPDIR)/file-create-locked.Plo \ + ./$(DEPDIR)/file-dotlock.Plo ./$(DEPDIR)/file-lock.Plo \ + ./$(DEPDIR)/file-set-size.Plo ./$(DEPDIR)/guid.Plo \ + ./$(DEPDIR)/hash-format.Plo ./$(DEPDIR)/hash-method.Plo \ + ./$(DEPDIR)/hash.Plo ./$(DEPDIR)/hash2.Plo \ + ./$(DEPDIR)/hex-binary.Plo ./$(DEPDIR)/hex-dec.Plo \ + ./$(DEPDIR)/hmac-cram-md5.Plo ./$(DEPDIR)/hmac.Plo \ + ./$(DEPDIR)/home-expand.Plo ./$(DEPDIR)/hook-build.Plo \ + ./$(DEPDIR)/hostpid.Plo ./$(DEPDIR)/imem.Plo \ + ./$(DEPDIR)/ioloop-epoll.Plo ./$(DEPDIR)/ioloop-iolist.Plo \ + ./$(DEPDIR)/ioloop-kqueue.Plo ./$(DEPDIR)/ioloop-notify-fd.Plo \ + ./$(DEPDIR)/ioloop-notify-inotify.Plo \ + ./$(DEPDIR)/ioloop-notify-kqueue.Plo \ + ./$(DEPDIR)/ioloop-notify-none.Plo ./$(DEPDIR)/ioloop-poll.Plo \ + ./$(DEPDIR)/ioloop-select.Plo ./$(DEPDIR)/ioloop.Plo \ + ./$(DEPDIR)/iostream-proxy.Plo ./$(DEPDIR)/iostream-pump.Plo \ + ./$(DEPDIR)/iostream-rawlog.Plo ./$(DEPDIR)/iostream-temp.Plo \ + ./$(DEPDIR)/iostream.Plo ./$(DEPDIR)/ipwd.Plo \ + ./$(DEPDIR)/iso8601-date.Plo \ + ./$(DEPDIR)/istream-base64-decoder.Plo \ + ./$(DEPDIR)/istream-base64-encoder.Plo \ + ./$(DEPDIR)/istream-callback.Plo ./$(DEPDIR)/istream-chain.Plo \ + ./$(DEPDIR)/istream-concat.Plo ./$(DEPDIR)/istream-crlf.Plo \ + ./$(DEPDIR)/istream-data.Plo \ + ./$(DEPDIR)/istream-failure-at.Plo \ + ./$(DEPDIR)/istream-file.Plo ./$(DEPDIR)/istream-hash.Plo \ + ./$(DEPDIR)/istream-jsonstr.Plo ./$(DEPDIR)/istream-limit.Plo \ + ./$(DEPDIR)/istream-multiplex.Plo \ + ./$(DEPDIR)/istream-rawlog.Plo \ + ./$(DEPDIR)/istream-seekable.Plo ./$(DEPDIR)/istream-sized.Plo \ + ./$(DEPDIR)/istream-tee.Plo ./$(DEPDIR)/istream-timeout.Plo \ + ./$(DEPDIR)/istream-try.Plo ./$(DEPDIR)/istream-unix.Plo \ + ./$(DEPDIR)/istream.Plo ./$(DEPDIR)/json-parser.Plo \ + ./$(DEPDIR)/json-tree.Plo ./$(DEPDIR)/lib-event.Plo \ + ./$(DEPDIR)/lib-signals.Plo ./$(DEPDIR)/lib.Plo \ + ./$(DEPDIR)/log-throttle.Plo ./$(DEPDIR)/md4.Plo \ + ./$(DEPDIR)/md5.Plo ./$(DEPDIR)/memarea.Plo \ + ./$(DEPDIR)/mempool-allocfree.Plo \ + ./$(DEPDIR)/mempool-alloconly.Plo \ + ./$(DEPDIR)/mempool-datastack.Plo \ + ./$(DEPDIR)/mempool-system.Plo \ + ./$(DEPDIR)/mempool-unsafe-datastack.Plo \ + ./$(DEPDIR)/mempool.Plo ./$(DEPDIR)/mkdir-parents.Plo \ + ./$(DEPDIR)/mmap-anon.Plo ./$(DEPDIR)/mmap-util.Plo \ + ./$(DEPDIR)/module-dir.Plo ./$(DEPDIR)/mountpoint.Plo \ + ./$(DEPDIR)/net.Plo ./$(DEPDIR)/nfs-workarounds.Plo \ + ./$(DEPDIR)/numpack.Plo ./$(DEPDIR)/ostream-buffer.Plo \ + ./$(DEPDIR)/ostream-failure-at.Plo \ + ./$(DEPDIR)/ostream-file.Plo ./$(DEPDIR)/ostream-hash.Plo \ + ./$(DEPDIR)/ostream-multiplex.Plo ./$(DEPDIR)/ostream-null.Plo \ + ./$(DEPDIR)/ostream-rawlog.Plo ./$(DEPDIR)/ostream-unix.Plo \ + ./$(DEPDIR)/ostream-wrapper.Plo ./$(DEPDIR)/ostream.Plo \ + ./$(DEPDIR)/path-util.Plo ./$(DEPDIR)/pkcs5.Plo \ + ./$(DEPDIR)/primes.Plo ./$(DEPDIR)/printf-format-fix.Plo \ + ./$(DEPDIR)/priorityq.Plo ./$(DEPDIR)/process-stat.Plo \ + ./$(DEPDIR)/process-title.Plo ./$(DEPDIR)/rand.Plo \ + ./$(DEPDIR)/randgen.Plo ./$(DEPDIR)/read-full.Plo \ + ./$(DEPDIR)/restrict-access.Plo \ + ./$(DEPDIR)/restrict-process-size.Plo \ + ./$(DEPDIR)/safe-memset.Plo ./$(DEPDIR)/safe-mkdir.Plo \ + ./$(DEPDIR)/safe-mkstemp.Plo ./$(DEPDIR)/sendfile-util.Plo \ + ./$(DEPDIR)/seq-range-array.Plo \ + ./$(DEPDIR)/seq-set-builder.Plo ./$(DEPDIR)/sha1.Plo \ + ./$(DEPDIR)/sha2.Plo ./$(DEPDIR)/sha3.Plo \ + ./$(DEPDIR)/sleep.Plo ./$(DEPDIR)/sort.Plo \ + ./$(DEPDIR)/stats-dist.Plo ./$(DEPDIR)/str-find.Plo \ + ./$(DEPDIR)/str-sanitize.Plo ./$(DEPDIR)/str-table.Plo \ + ./$(DEPDIR)/str.Plo ./$(DEPDIR)/strescape.Plo \ + ./$(DEPDIR)/strfuncs.Plo ./$(DEPDIR)/strnum.Plo \ + ./$(DEPDIR)/test_lib-test-aqueue.Po \ + ./$(DEPDIR)/test_lib-test-array.Po \ + ./$(DEPDIR)/test_lib-test-backtrace.Po \ + ./$(DEPDIR)/test_lib-test-base32.Po \ + ./$(DEPDIR)/test_lib-test-base64.Po \ + ./$(DEPDIR)/test_lib-test-bits.Po \ + ./$(DEPDIR)/test_lib-test-bsearch-insert-pos.Po \ + ./$(DEPDIR)/test_lib-test-buffer-istream.Po \ + ./$(DEPDIR)/test_lib-test-buffer.Po \ + ./$(DEPDIR)/test_lib-test-byteorder.Po \ + ./$(DEPDIR)/test_lib-test-connection.Po \ + ./$(DEPDIR)/test_lib-test-cpu-limit.Po \ + ./$(DEPDIR)/test_lib-test-crc32.Po \ + ./$(DEPDIR)/test_lib-test-data-stack.Po \ + ./$(DEPDIR)/test_lib-test-env-util.Po \ + ./$(DEPDIR)/test_lib-test-event-category-register.Po \ + ./$(DEPDIR)/test_lib-test-event-filter-expr.Po \ + ./$(DEPDIR)/test_lib-test-event-filter-merge.Po \ + ./$(DEPDIR)/test_lib-test-event-filter-parser.Po \ + ./$(DEPDIR)/test_lib-test-event-filter.Po \ + ./$(DEPDIR)/test_lib-test-event-flatten.Po \ + ./$(DEPDIR)/test_lib-test-event-log.Po \ + ./$(DEPDIR)/test_lib-test-failures.Po \ + ./$(DEPDIR)/test_lib-test-fd-util.Po \ + ./$(DEPDIR)/test_lib-test-file-cache.Po \ + ./$(DEPDIR)/test_lib-test-file-create-locked.Po \ + ./$(DEPDIR)/test_lib-test-guid.Po \ + ./$(DEPDIR)/test_lib-test-hash-format.Po \ + ./$(DEPDIR)/test_lib-test-hash-method.Po \ + ./$(DEPDIR)/test_lib-test-hash.Po \ + ./$(DEPDIR)/test_lib-test-hex-binary.Po \ + ./$(DEPDIR)/test_lib-test-hmac.Po \ + ./$(DEPDIR)/test_lib-test-imem.Po \ + ./$(DEPDIR)/test_lib-test-ioloop.Po \ + ./$(DEPDIR)/test_lib-test-iostream-proxy.Po \ + ./$(DEPDIR)/test_lib-test-iostream-pump.Po \ + ./$(DEPDIR)/test_lib-test-iostream-temp.Po \ + ./$(DEPDIR)/test_lib-test-iso8601-date.Po \ + ./$(DEPDIR)/test_lib-test-istream-base64-decoder.Po \ + ./$(DEPDIR)/test_lib-test-istream-base64-encoder.Po \ + ./$(DEPDIR)/test_lib-test-istream-chain.Po \ + ./$(DEPDIR)/test_lib-test-istream-concat.Po \ + ./$(DEPDIR)/test_lib-test-istream-crlf.Po \ + ./$(DEPDIR)/test_lib-test-istream-failure-at.Po \ + ./$(DEPDIR)/test_lib-test-istream-jsonstr.Po \ + ./$(DEPDIR)/test_lib-test-istream-multiplex.Po \ + ./$(DEPDIR)/test_lib-test-istream-seekable.Po \ + ./$(DEPDIR)/test_lib-test-istream-sized.Po \ + ./$(DEPDIR)/test_lib-test-istream-tee.Po \ + ./$(DEPDIR)/test_lib-test-istream-try.Po \ + ./$(DEPDIR)/test_lib-test-istream-unix.Po \ + ./$(DEPDIR)/test_lib-test-istream.Po \ + ./$(DEPDIR)/test_lib-test-json-parser.Po \ + ./$(DEPDIR)/test_lib-test-json-tree.Po \ + ./$(DEPDIR)/test_lib-test-lib-event.Po \ + ./$(DEPDIR)/test_lib-test-lib-signals.Po \ + ./$(DEPDIR)/test_lib-test-lib.Po \ + ./$(DEPDIR)/test_lib-test-llist.Po \ + ./$(DEPDIR)/test_lib-test-log-throttle.Po \ + ./$(DEPDIR)/test_lib-test-macros.Po \ + ./$(DEPDIR)/test_lib-test-malloc-overflow.Po \ + ./$(DEPDIR)/test_lib-test-memarea.Po \ + ./$(DEPDIR)/test_lib-test-mempool-allocfree.Po \ + ./$(DEPDIR)/test_lib-test-mempool-alloconly.Po \ + ./$(DEPDIR)/test_lib-test-mempool.Po \ + ./$(DEPDIR)/test_lib-test-multiplex.Po \ + ./$(DEPDIR)/test_lib-test-net.Po \ + ./$(DEPDIR)/test_lib-test-numpack.Po \ + ./$(DEPDIR)/test_lib-test-ostream-buffer.Po \ + ./$(DEPDIR)/test_lib-test-ostream-failure-at.Po \ + ./$(DEPDIR)/test_lib-test-ostream-file.Po \ + ./$(DEPDIR)/test_lib-test-ostream-multiplex.Po \ + ./$(DEPDIR)/test_lib-test-path-util.Po \ + ./$(DEPDIR)/test_lib-test-pkcs5.Po \ + ./$(DEPDIR)/test_lib-test-primes.Po \ + ./$(DEPDIR)/test_lib-test-printf-format-fix.Po \ + ./$(DEPDIR)/test_lib-test-priorityq.Po \ + ./$(DEPDIR)/test_lib-test-random.Po \ + ./$(DEPDIR)/test_lib-test-seq-range-array.Po \ + ./$(DEPDIR)/test_lib-test-seq-set-builder.Po \ + ./$(DEPDIR)/test_lib-test-stats-dist.Po \ + ./$(DEPDIR)/test_lib-test-str-find.Po \ + ./$(DEPDIR)/test_lib-test-str-sanitize.Po \ + ./$(DEPDIR)/test_lib-test-str-table.Po \ + ./$(DEPDIR)/test_lib-test-str.Po \ + ./$(DEPDIR)/test_lib-test-strescape.Po \ + ./$(DEPDIR)/test_lib-test-strfuncs.Po \ + ./$(DEPDIR)/test_lib-test-strnum.Po \ + ./$(DEPDIR)/test_lib-test-time-util.Po \ + ./$(DEPDIR)/test_lib-test-unichar.Po \ + ./$(DEPDIR)/test_lib-test-uri.Po \ + ./$(DEPDIR)/test_lib-test-utc-mktime.Po \ + ./$(DEPDIR)/test_lib-test-var-expand.Po \ + ./$(DEPDIR)/test_lib-test-wildcard-match.Po \ + ./$(DEPDIR)/time-util.Plo ./$(DEPDIR)/unichar.Plo \ + ./$(DEPDIR)/unix-socket-create.Plo \ + ./$(DEPDIR)/unlink-directory.Plo \ + ./$(DEPDIR)/unlink-old-files.Plo ./$(DEPDIR)/uri-util.Plo \ + ./$(DEPDIR)/utc-mktime.Plo ./$(DEPDIR)/utc-offset.Plo \ + ./$(DEPDIR)/var-expand-if.Plo ./$(DEPDIR)/var-expand.Plo \ + ./$(DEPDIR)/wildcard-match.Plo ./$(DEPDIR)/write-full.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +@MAINTAINER_MODE_FALSE@am__skiplex = test -f $@ || +LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS) +LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS) +AM_V_LEX = $(am__v_LEX_@AM_V@) +am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@) +am__v_LEX_0 = @echo " LEX " $@; +am__v_LEX_1 = +YLWRAP = $(top_srcdir)/ylwrap +@MAINTAINER_MODE_FALSE@am__skipyacc = test -f $@ || +am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \ + -e s/c++$$/h++/ -e s/c$$/h/ +YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS) +LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS) +AM_V_YACC = $(am__v_YACC_@AM_V@) +am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@) +am__v_YACC_0 = @echo " YACC " $@; +am__v_YACC_1 = +SOURCES = $(liblib_la_SOURCES) $(test_lib_SOURCES) +DIST_SOURCES = $(liblib_la_SOURCES) $(test_lib_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkginc_libdir)" +HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ + $(top_srcdir)/ylwrap event-filter-lexer.c \ + event-filter-parser.c +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPARMOR_LIBS = @APPARMOR_LIBS@ +AR = @AR@ +AUTH_CFLAGS = @AUTH_CFLAGS@ +AUTH_LIBS = @AUTH_LIBS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BINARY_CFLAGS = @BINARY_CFLAGS@ +BINARY_LDFLAGS = @BINARY_LDFLAGS@ +BISON = @BISON@ +CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ +CASSANDRA_LIBS = @CASSANDRA_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CDB_LIBS = @CDB_LIBS@ +CFLAGS = @CFLAGS@ +CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ +CLUCENE_LIBS = @CLUCENE_LIBS@ +COMPRESS_LIBS = @COMPRESS_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPT_LIBS = @CRYPT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DICT_LIBS = @DICT_LIBS@ +DLLIB = @DLLIB@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FLEX = @FLEX@ +FUZZER_CPPFLAGS = @FUZZER_CPPFLAGS@ +FUZZER_LDFLAGS = @FUZZER_LDFLAGS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KRB5CONFIG = @KRB5CONFIG@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_LIBS = @KRB5_LIBS@ +LD = @LD@ +LDAP_LIBS = @LDAP_LIBS@ +LDFLAGS = @LDFLAGS@ +LD_NO_WHOLE_ARCHIVE = @LD_NO_WHOLE_ARCHIVE@ +LD_WHOLE_ARCHIVE = @LD_WHOLE_ARCHIVE@ +LIBCAP = @LIBCAP@ +LIBDOVECOT = @LIBDOVECOT@ +LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ +LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ +LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ +LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ +LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ +LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ +LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ +LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ +LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ +LIBDOVECOT_LUA = @LIBDOVECOT_LUA@ +LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@ +LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ +LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ +LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ +LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ +LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ +LIBICONV = @LIBICONV@ +LIBICU_CFLAGS = @LIBICU_CFLAGS@ +LIBICU_LIBS = @LIBICU_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSODIUM_CFLAGS = @LIBSODIUM_CFLAGS@ +LIBSODIUM_LIBS = @LIBSODIUM_LIBS@ +LIBTIRPC_CFLAGS = @LIBTIRPC_CFLAGS@ +LIBTIRPC_LIBS = @LIBTIRPC_LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@ +LIBUNWIND_LIBS = @LIBUNWIND_LIBS@ +LIBWRAP_LIBS = @LIBWRAP_LIBS@ +LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LUA_CFLAGS = @LUA_CFLAGS@ +LUA_LIBS = @LUA_LIBS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MODULE_LIBS = @MODULE_LIBS@ +MODULE_SUFFIX = @MODULE_SUFFIX@ +MYSQL_CFLAGS = @MYSQL_CFLAGS@ +MYSQL_CONFIG = @MYSQL_CONFIG@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PANDOC = @PANDOC@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PGSQL_CFLAGS = @PGSQL_CFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PG_CONFIG = @PG_CONFIG@ +PIE_CFLAGS = @PIE_CFLAGS@ +PIE_LDFLAGS = @PIE_LDFLAGS@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +QUOTA_LIBS = @QUOTA_LIBS@ +RANLIB = @RANLIB@ +RELRO_LDFLAGS = @RELRO_LDFLAGS@ +RPCGEN = @RPCGEN@ +RUN_TEST = @RUN_TEST@ +SED = @SED@ +SETTING_FILES = @SETTING_FILES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SQLITE_CFLAGS = @SQLITE_CFLAGS@ +SQLITE_LIBS = @SQLITE_LIBS@ +SQL_CFLAGS = @SQL_CFLAGS@ +SQL_LIBS = @SQL_LIBS@ +SSL_CFLAGS = @SSL_CFLAGS@ +SSL_LIBS = @SSL_LIBS@ +STRIP = @STRIP@ +SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ +SYSTEMD_LIBS = @SYSTEMD_LIBS@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +ZSTD_CFLAGS = @ZSTD_CFLAGS@ +ZSTD_LIBS = @ZSTD_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +dict_drivers = @dict_drivers@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +moduledir = @moduledir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rundir = @rundir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sql_drivers = @sql_drivers@ +srcdir = @srcdir@ +ssldir = @ssldir@ +statedir = @statedir@ +sysconfdir = @sysconfdir@ +systemdservicetype = @systemdservicetype@ +systemdsystemunitdir = @systemdsystemunitdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = \ + $(LIBUNWIND_CFLAGS) + +noinst_LTLIBRARIES = liblib.la +BUILT_SOURCES = $(srcdir)/unicodemap.c \ + event-filter-lexer.c \ + event-filter-parser.c \ + event-filter-parser.h + +EXTRA_DIST = unicodemap.c unicodemap.pl UnicodeData.txt + +# Squelch autoconf error about using .[ly] sources but not defining $(LEX) +# and $(YACC). Using false here avoids accidental use. +LEX = /bin/false +YACC = /bin/false +liblib_la_LIBADD = $(LIBUNWIND_LIBS) +liblib_la_SOURCES = \ + array.c \ + aqueue.c \ + askpass.c \ + backtrace-string.c \ + base32.c \ + base64.c \ + bits.c \ + bsearch-insert-pos.c \ + buffer.c \ + buffer-istream.c \ + child-wait.c \ + compat.c \ + connection.c \ + cpu-limit.c \ + crc32.c \ + data-stack.c \ + eacces-error.c \ + env-util.c \ + event-filter.c \ + event-filter-lexer.l \ + event-filter-parser.y \ + event-log.c \ + execv-const.c \ + failures.c \ + fd-util.c \ + fdatasync-path.c \ + fdpass.c \ + file-cache.c \ + file-create-locked.c \ + file-copy.c \ + file-dotlock.c \ + file-lock.c \ + file-set-size.c \ + guid.c \ + hash.c \ + hash-format.c \ + hash-method.c \ + hash2.c \ + hex-binary.c \ + hex-dec.c \ + hmac.c \ + hmac-cram-md5.c \ + home-expand.c \ + hook-build.c \ + hostpid.c \ + imem.c \ + ipwd.c \ + iostream.c \ + iostream-pump.c \ + iostream-proxy.c \ + iostream-rawlog.c \ + iostream-temp.c \ + iso8601-date.c \ + istream.c \ + istream-base64-decoder.c \ + istream-base64-encoder.c \ + istream-callback.c \ + istream-chain.c \ + istream-concat.c \ + istream-crlf.c \ + istream-data.c \ + istream-failure-at.c \ + istream-file.c \ + istream-hash.c \ + istream-jsonstr.c \ + istream-limit.c \ + istream-multiplex.c \ + istream-rawlog.c \ + istream-seekable.c \ + istream-sized.c \ + istream-tee.c \ + istream-try.c \ + istream-timeout.c \ + istream-unix.c \ + ioloop.c \ + ioloop-iolist.c \ + ioloop-notify-none.c \ + ioloop-notify-fd.c \ + ioloop-notify-inotify.c \ + ioloop-notify-kqueue.c \ + ioloop-poll.c \ + ioloop-select.c \ + ioloop-epoll.c \ + ioloop-kqueue.c \ + json-parser.c \ + json-tree.c \ + lib.c \ + lib-event.c \ + lib-signals.c \ + log-throttle.c \ + md4.c \ + md5.c \ + memarea.c \ + mempool.c \ + mempool-allocfree.c \ + mempool-alloconly.c \ + mempool-datastack.c \ + mempool-system.c \ + mempool-unsafe-datastack.c \ + mkdir-parents.c \ + mmap-anon.c \ + mmap-util.c \ + module-dir.c \ + mountpoint.c \ + net.c \ + nfs-workarounds.c \ + numpack.c \ + ostream.c \ + ostream-buffer.c \ + ostream-failure-at.c \ + ostream-file.c \ + ostream-hash.c \ + ostream-multiplex.c \ + ostream-null.c \ + ostream-rawlog.c \ + ostream-unix.c \ + ostream-wrapper.c \ + path-util.c \ + pkcs5.c \ + primes.c \ + printf-format-fix.c \ + process-stat.c \ + process-title.c \ + priorityq.c \ + randgen.c \ + rand.c \ + read-full.c \ + restrict-access.c \ + restrict-process-size.c \ + safe-memset.c \ + safe-mkdir.c \ + safe-mkstemp.c \ + sendfile-util.c \ + seq-range-array.c \ + seq-set-builder.c \ + sha1.c \ + sha2.c \ + sha3.c \ + sleep.c \ + sort.c \ + stats-dist.c \ + str.c \ + str-find.c \ + str-sanitize.c \ + str-table.c \ + strescape.c \ + strfuncs.c \ + strnum.c \ + time-util.c \ + unix-socket-create.c \ + unlink-directory.c \ + unlink-old-files.c \ + unichar.c \ + uri-util.c \ + utc-offset.c \ + utc-mktime.c \ + var-expand.c \ + var-expand-if.c \ + wildcard-match.c \ + write-full.c + +headers = \ + aqueue.h \ + array.h \ + array-decl.h \ + askpass.h \ + backtrace-string.h \ + base32.h \ + base64.h \ + bits.h \ + bsearch-insert-pos.h \ + buffer.h \ + byteorder.h \ + child-wait.h \ + compat.h \ + connection.h \ + cpu-limit.h \ + crc32.h \ + data-stack.h \ + eacces-error.h \ + env-util.h \ + event-filter.h \ + event-filter-parser.h \ + event-filter-private.h \ + event-log.h \ + execv-const.h \ + failures.h \ + failures-private.h \ + fd-util.h \ + fdatasync-path.h \ + fdpass.h \ + file-cache.h \ + file-create-locked.h \ + file-copy.h \ + file-dotlock.h \ + file-lock.h \ + file-set-size.h \ + fsync-mode.h \ + guid.h \ + hash.h \ + hash-decl.h \ + hash-format.h \ + hash-method.h \ + hash2.h \ + hex-binary.h \ + hex-dec.h \ + hmac.h \ + hmac-cram-md5.h \ + home-expand.h \ + hook-build.h \ + hostpid.h \ + imem.h \ + ipwd.h \ + iostream.h \ + iostream-private.h \ + iostream-pump.h \ + iostream-proxy.h \ + iostream-rawlog.h \ + iostream-rawlog-private.h \ + iostream-temp.h \ + iso8601-date.h \ + istream.h \ + istream-base64.h \ + istream-callback.h \ + istream-chain.h \ + istream-concat.h \ + istream-crlf.h \ + istream-failure-at.h \ + istream-file-private.h \ + istream-hash.h \ + istream-jsonstr.h \ + istream-multiplex.h \ + istream-private.h \ + istream-rawlog.h \ + istream-seekable.h \ + istream-sized.h \ + istream-tee.h \ + istream-try.h \ + istream-timeout.h \ + istream-unix.h \ + ioloop.h \ + ioloop-iolist.h \ + ioloop-private.h \ + ioloop-notify-fd.h \ + json-parser.h \ + json-tree.h \ + lib.h \ + lib-event.h \ + lib-event-private.h \ + lib-signals.h \ + llist.h \ + log-throttle.h \ + macros.h \ + md4.h \ + md5.h \ + malloc-overflow.h \ + memarea.h \ + mempool.h \ + mkdir-parents.h \ + mmap-util.h \ + module-context.h \ + module-dir.h \ + mountpoint.h \ + net.h \ + nfs-workarounds.h \ + numpack.h \ + ostream.h \ + ostream-failure-at.h \ + ostream-file-private.h \ + ostream-hash.h \ + ostream-multiplex.h \ + ostream-private.h \ + ostream-null.h \ + ostream-rawlog.h \ + ostream-unix.h \ + ostream-wrapper.h \ + path-util.h \ + pkcs5.h \ + primes.h \ + printf-format-fix.h \ + process-stat.h \ + process-title.h \ + priorityq.h \ + randgen.h \ + read-full.h \ + restrict-access.h \ + restrict-process-size.h \ + safe-memset.h \ + safe-mkdir.h \ + safe-mkstemp.h \ + sendfile-util.h \ + seq-range-array.h \ + seq-set-builder.h \ + sha-common.h \ + sha1.h \ + sha2.h \ + sha3.h \ + sleep.h \ + sort.h \ + stats-dist.h \ + str.h \ + str-find.h \ + str-sanitize.h \ + str-table.h \ + strescape.h \ + strfuncs.h \ + strnum.h \ + time-util.h \ + unix-socket-create.h \ + unlink-directory.h \ + unlink-old-files.h \ + unichar.h \ + uri-util.h \ + utc-offset.h \ + utc-mktime.h \ + var-expand.h \ + var-expand-private.h \ + wildcard-match.h \ + write-full.h + +test_programs = test-lib +test_lib_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-test + +test_libs = \ + ../lib-test/libtest.la \ + liblib.la + +test_lib_SOURCES = \ + test-lib.c \ + test-array.c \ + test-aqueue.c \ + test-backtrace.c \ + test-base32.c \ + test-base64.c \ + test-bits.c \ + test-bsearch-insert-pos.c \ + test-buffer.c \ + test-buffer-istream.c \ + test-byteorder.c \ + test-connection.c \ + test-crc32.c \ + test-cpu-limit.c \ + test-data-stack.c \ + test-env-util.c \ + test-event-category-register.c \ + test-event-filter.c \ + test-event-filter-expr.c \ + test-event-filter-merge.c \ + test-event-filter-parser.c \ + test-event-flatten.c \ + test-event-log.c \ + test-failures.c \ + test-fd-util.c \ + test-file-cache.c \ + test-file-create-locked.c \ + test-guid.c \ + test-hash.c \ + test-hash-format.c \ + test-hash-method.c \ + test-hmac.c \ + test-hex-binary.c \ + test-imem.c \ + test-ioloop.c \ + test-iso8601-date.c \ + test-iostream-pump.c \ + test-iostream-proxy.c \ + test-iostream-temp.c \ + test-istream.c \ + test-istream-base64-decoder.c \ + test-istream-base64-encoder.c \ + test-istream-chain.c \ + test-istream-concat.c \ + test-istream-crlf.c \ + test-istream-failure-at.c \ + test-istream-jsonstr.c \ + test-istream-multiplex.c \ + test-istream-seekable.c \ + test-istream-sized.c \ + test-istream-tee.c \ + test-istream-try.c \ + test-istream-unix.c \ + test-json-parser.c \ + test-json-tree.c \ + test-lib-event.c \ + test-lib-signals.c \ + test-llist.c \ + test-log-throttle.c \ + test-macros.c \ + test-malloc-overflow.c \ + test-memarea.c \ + test-mempool.c \ + test-mempool-allocfree.c \ + test-mempool-alloconly.c \ + test-pkcs5.c \ + test-net.c \ + test-numpack.c \ + test-ostream-buffer.c \ + test-ostream-failure-at.c \ + test-ostream-file.c \ + test-ostream-multiplex.c \ + test-multiplex.c \ + test-path-util.c \ + test-primes.c \ + test-printf-format-fix.c \ + test-priorityq.c \ + test-random.c \ + test-seq-range-array.c \ + test-seq-set-builder.c \ + test-stats-dist.c \ + test-str.c \ + test-strescape.c \ + test-strfuncs.c \ + test-strnum.c \ + test-str-find.c \ + test-str-sanitize.c \ + test-str-table.c \ + test-time-util.c \ + test-unichar.c \ + test-utc-mktime.c \ + test-uri.c \ + test-var-expand.c \ + test-wildcard-match.c + +test_headers = \ + test-lib.h \ + test-lib.inc + +test_lib_LDADD = $(test_libs) -lm +test_lib_DEPENDENCIES = $(test_libs) +pkginc_libdir = $(pkgincludedir) +pkginc_lib_HEADERS = $(headers) +noinst_HEADERS = $(test_headers) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .l .lo .o .obj .y +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +liblib.la: $(liblib_la_OBJECTS) $(liblib_la_DEPENDENCIES) $(EXTRA_liblib_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(liblib_la_OBJECTS) $(liblib_la_LIBADD) $(LIBS) + +test-lib$(EXEEXT): $(test_lib_OBJECTS) $(test_lib_DEPENDENCIES) $(EXTRA_test_lib_DEPENDENCIES) + @rm -f test-lib$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_lib_OBJECTS) $(test_lib_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aqueue.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/askpass.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backtrace-string.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base32.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bits.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bsearch-insert-pos.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer-istream.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/child-wait.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-limit.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc32.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data-stack.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eacces-error.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/env-util.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/event-filter-lexer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/event-filter-parser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/event-filter.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/event-log.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/execv-const.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failures.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fd-util.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdatasync-path.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdpass.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-cache.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-copy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-create-locked.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-dotlock.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-lock.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-set-size.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/guid.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash-format.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash-method.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hex-binary.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hex-dec.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hmac-cram-md5.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hmac.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/home-expand.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hook-build.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostpid.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imem.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-epoll.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-iolist.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-kqueue.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-notify-fd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-notify-inotify.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-notify-kqueue.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-notify-none.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-poll.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop-select.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ioloop.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iostream-proxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iostream-pump.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iostream-rawlog.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iostream-temp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iostream.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipwd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iso8601-date.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-base64-decoder.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-base64-encoder.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-callback.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-chain.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-concat.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-crlf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-data.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-failure-at.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-file.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-hash.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-jsonstr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-limit.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-multiplex.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-rawlog.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-seekable.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-sized.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-tee.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-timeout.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-try.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream-unix.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/istream.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json-parser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json-tree.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lib-event.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lib-signals.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lib.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log-throttle.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md4.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memarea.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mempool-allocfree.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mempool-alloconly.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mempool-datastack.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mempool-system.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mempool-unsafe-datastack.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mempool.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mkdir-parents.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmap-anon.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmap-util.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/module-dir.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mountpoint.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/net.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfs-workarounds.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/numpack.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-buffer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-failure-at.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-file.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-hash.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-multiplex.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-null.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-rawlog.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-unix.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream-wrapper.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ostream.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/path-util.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs5.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/primes.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/printf-format-fix.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/priorityq.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/process-stat.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/process-title.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rand.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/randgen.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read-full.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/restrict-access.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/restrict-process-size.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safe-memset.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safe-mkdir.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safe-mkstemp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sendfile-util.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/seq-range-array.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/seq-set-builder.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha1.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha3.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sleep.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sort.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stats-dist.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/str-find.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/str-sanitize.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/str-table.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/str.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strescape.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strfuncs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strnum.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-aqueue.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-array.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-backtrace.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-base32.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-base64.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-bits.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-bsearch-insert-pos.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-buffer-istream.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-buffer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-byteorder.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-connection.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-cpu-limit.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-crc32.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-data-stack.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-env-util.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-event-category-register.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-event-filter-expr.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-event-filter-merge.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-event-filter-parser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-event-filter.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-event-flatten.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-event-log.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-failures.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-fd-util.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-file-cache.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-file-create-locked.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-guid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-hash-format.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-hash-method.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-hash.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-hex-binary.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-hmac.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-imem.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-ioloop.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-iostream-proxy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-iostream-pump.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-iostream-temp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-iso8601-date.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-base64-decoder.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-base64-encoder.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-chain.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-concat.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-crlf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-failure-at.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-jsonstr.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-multiplex.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-seekable.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-sized.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-tee.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-try.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream-unix.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-istream.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-json-parser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-json-tree.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-lib-event.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-lib-signals.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-lib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-llist.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-log-throttle.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-macros.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-malloc-overflow.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-memarea.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-mempool-allocfree.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-mempool-alloconly.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-mempool.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-multiplex.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-net.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-numpack.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-ostream-buffer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-ostream-failure-at.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-ostream-file.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-ostream-multiplex.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-path-util.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-pkcs5.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-primes.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-printf-format-fix.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-priorityq.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-random.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-seq-range-array.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-seq-set-builder.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-stats-dist.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-str-find.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-str-sanitize.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-str-table.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-str.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-strescape.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-strfuncs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-strnum.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-time-util.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-unichar.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-uri.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-utc-mktime.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-var-expand.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lib-test-wildcard-match.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time-util.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unichar.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unix-socket-create.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unlink-directory.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unlink-old-files.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uri-util.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utc-mktime.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utc-offset.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/var-expand-if.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/var-expand.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wildcard-match.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/write-full.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +test_lib-test-lib.o: test-lib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-lib.o -MD -MP -MF $(DEPDIR)/test_lib-test-lib.Tpo -c -o test_lib-test-lib.o `test -f 'test-lib.c' || echo '$(srcdir)/'`test-lib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-lib.Tpo $(DEPDIR)/test_lib-test-lib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-lib.c' object='test_lib-test-lib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-lib.o `test -f 'test-lib.c' || echo '$(srcdir)/'`test-lib.c + +test_lib-test-lib.obj: test-lib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-lib.obj -MD -MP -MF $(DEPDIR)/test_lib-test-lib.Tpo -c -o test_lib-test-lib.obj `if test -f 'test-lib.c'; then $(CYGPATH_W) 'test-lib.c'; else $(CYGPATH_W) '$(srcdir)/test-lib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-lib.Tpo $(DEPDIR)/test_lib-test-lib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-lib.c' object='test_lib-test-lib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-lib.obj `if test -f 'test-lib.c'; then $(CYGPATH_W) 'test-lib.c'; else $(CYGPATH_W) '$(srcdir)/test-lib.c'; fi` + +test_lib-test-array.o: test-array.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-array.o -MD -MP -MF $(DEPDIR)/test_lib-test-array.Tpo -c -o test_lib-test-array.o `test -f 'test-array.c' || echo '$(srcdir)/'`test-array.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-array.Tpo $(DEPDIR)/test_lib-test-array.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-array.c' object='test_lib-test-array.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-array.o `test -f 'test-array.c' || echo '$(srcdir)/'`test-array.c + +test_lib-test-array.obj: test-array.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-array.obj -MD -MP -MF $(DEPDIR)/test_lib-test-array.Tpo -c -o test_lib-test-array.obj `if test -f 'test-array.c'; then $(CYGPATH_W) 'test-array.c'; else $(CYGPATH_W) '$(srcdir)/test-array.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-array.Tpo $(DEPDIR)/test_lib-test-array.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-array.c' object='test_lib-test-array.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-array.obj `if test -f 'test-array.c'; then $(CYGPATH_W) 'test-array.c'; else $(CYGPATH_W) '$(srcdir)/test-array.c'; fi` + +test_lib-test-aqueue.o: test-aqueue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-aqueue.o -MD -MP -MF $(DEPDIR)/test_lib-test-aqueue.Tpo -c -o test_lib-test-aqueue.o `test -f 'test-aqueue.c' || echo '$(srcdir)/'`test-aqueue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-aqueue.Tpo $(DEPDIR)/test_lib-test-aqueue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-aqueue.c' object='test_lib-test-aqueue.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-aqueue.o `test -f 'test-aqueue.c' || echo '$(srcdir)/'`test-aqueue.c + +test_lib-test-aqueue.obj: test-aqueue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-aqueue.obj -MD -MP -MF $(DEPDIR)/test_lib-test-aqueue.Tpo -c -o test_lib-test-aqueue.obj `if test -f 'test-aqueue.c'; then $(CYGPATH_W) 'test-aqueue.c'; else $(CYGPATH_W) '$(srcdir)/test-aqueue.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-aqueue.Tpo $(DEPDIR)/test_lib-test-aqueue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-aqueue.c' object='test_lib-test-aqueue.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-aqueue.obj `if test -f 'test-aqueue.c'; then $(CYGPATH_W) 'test-aqueue.c'; else $(CYGPATH_W) '$(srcdir)/test-aqueue.c'; fi` + +test_lib-test-backtrace.o: test-backtrace.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-backtrace.o -MD -MP -MF $(DEPDIR)/test_lib-test-backtrace.Tpo -c -o test_lib-test-backtrace.o `test -f 'test-backtrace.c' || echo '$(srcdir)/'`test-backtrace.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-backtrace.Tpo $(DEPDIR)/test_lib-test-backtrace.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-backtrace.c' object='test_lib-test-backtrace.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-backtrace.o `test -f 'test-backtrace.c' || echo '$(srcdir)/'`test-backtrace.c + +test_lib-test-backtrace.obj: test-backtrace.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-backtrace.obj -MD -MP -MF $(DEPDIR)/test_lib-test-backtrace.Tpo -c -o test_lib-test-backtrace.obj `if test -f 'test-backtrace.c'; then $(CYGPATH_W) 'test-backtrace.c'; else $(CYGPATH_W) '$(srcdir)/test-backtrace.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-backtrace.Tpo $(DEPDIR)/test_lib-test-backtrace.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-backtrace.c' object='test_lib-test-backtrace.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-backtrace.obj `if test -f 'test-backtrace.c'; then $(CYGPATH_W) 'test-backtrace.c'; else $(CYGPATH_W) '$(srcdir)/test-backtrace.c'; fi` + +test_lib-test-base32.o: test-base32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-base32.o -MD -MP -MF $(DEPDIR)/test_lib-test-base32.Tpo -c -o test_lib-test-base32.o `test -f 'test-base32.c' || echo '$(srcdir)/'`test-base32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-base32.Tpo $(DEPDIR)/test_lib-test-base32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-base32.c' object='test_lib-test-base32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-base32.o `test -f 'test-base32.c' || echo '$(srcdir)/'`test-base32.c + +test_lib-test-base32.obj: test-base32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-base32.obj -MD -MP -MF $(DEPDIR)/test_lib-test-base32.Tpo -c -o test_lib-test-base32.obj `if test -f 'test-base32.c'; then $(CYGPATH_W) 'test-base32.c'; else $(CYGPATH_W) '$(srcdir)/test-base32.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-base32.Tpo $(DEPDIR)/test_lib-test-base32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-base32.c' object='test_lib-test-base32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-base32.obj `if test -f 'test-base32.c'; then $(CYGPATH_W) 'test-base32.c'; else $(CYGPATH_W) '$(srcdir)/test-base32.c'; fi` + +test_lib-test-base64.o: test-base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-base64.o -MD -MP -MF $(DEPDIR)/test_lib-test-base64.Tpo -c -o test_lib-test-base64.o `test -f 'test-base64.c' || echo '$(srcdir)/'`test-base64.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-base64.Tpo $(DEPDIR)/test_lib-test-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-base64.c' object='test_lib-test-base64.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-base64.o `test -f 'test-base64.c' || echo '$(srcdir)/'`test-base64.c + +test_lib-test-base64.obj: test-base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-base64.obj -MD -MP -MF $(DEPDIR)/test_lib-test-base64.Tpo -c -o test_lib-test-base64.obj `if test -f 'test-base64.c'; then $(CYGPATH_W) 'test-base64.c'; else $(CYGPATH_W) '$(srcdir)/test-base64.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-base64.Tpo $(DEPDIR)/test_lib-test-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-base64.c' object='test_lib-test-base64.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-base64.obj `if test -f 'test-base64.c'; then $(CYGPATH_W) 'test-base64.c'; else $(CYGPATH_W) '$(srcdir)/test-base64.c'; fi` + +test_lib-test-bits.o: test-bits.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-bits.o -MD -MP -MF $(DEPDIR)/test_lib-test-bits.Tpo -c -o test_lib-test-bits.o `test -f 'test-bits.c' || echo '$(srcdir)/'`test-bits.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-bits.Tpo $(DEPDIR)/test_lib-test-bits.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-bits.c' object='test_lib-test-bits.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-bits.o `test -f 'test-bits.c' || echo '$(srcdir)/'`test-bits.c + +test_lib-test-bits.obj: test-bits.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-bits.obj -MD -MP -MF $(DEPDIR)/test_lib-test-bits.Tpo -c -o test_lib-test-bits.obj `if test -f 'test-bits.c'; then $(CYGPATH_W) 'test-bits.c'; else $(CYGPATH_W) '$(srcdir)/test-bits.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-bits.Tpo $(DEPDIR)/test_lib-test-bits.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-bits.c' object='test_lib-test-bits.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-bits.obj `if test -f 'test-bits.c'; then $(CYGPATH_W) 'test-bits.c'; else $(CYGPATH_W) '$(srcdir)/test-bits.c'; fi` + +test_lib-test-bsearch-insert-pos.o: test-bsearch-insert-pos.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-bsearch-insert-pos.o -MD -MP -MF $(DEPDIR)/test_lib-test-bsearch-insert-pos.Tpo -c -o test_lib-test-bsearch-insert-pos.o `test -f 'test-bsearch-insert-pos.c' || echo '$(srcdir)/'`test-bsearch-insert-pos.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-bsearch-insert-pos.Tpo $(DEPDIR)/test_lib-test-bsearch-insert-pos.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-bsearch-insert-pos.c' object='test_lib-test-bsearch-insert-pos.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-bsearch-insert-pos.o `test -f 'test-bsearch-insert-pos.c' || echo '$(srcdir)/'`test-bsearch-insert-pos.c + +test_lib-test-bsearch-insert-pos.obj: test-bsearch-insert-pos.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-bsearch-insert-pos.obj -MD -MP -MF $(DEPDIR)/test_lib-test-bsearch-insert-pos.Tpo -c -o test_lib-test-bsearch-insert-pos.obj `if test -f 'test-bsearch-insert-pos.c'; then $(CYGPATH_W) 'test-bsearch-insert-pos.c'; else $(CYGPATH_W) '$(srcdir)/test-bsearch-insert-pos.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-bsearch-insert-pos.Tpo $(DEPDIR)/test_lib-test-bsearch-insert-pos.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-bsearch-insert-pos.c' object='test_lib-test-bsearch-insert-pos.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-bsearch-insert-pos.obj `if test -f 'test-bsearch-insert-pos.c'; then $(CYGPATH_W) 'test-bsearch-insert-pos.c'; else $(CYGPATH_W) '$(srcdir)/test-bsearch-insert-pos.c'; fi` + +test_lib-test-buffer.o: test-buffer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-buffer.o -MD -MP -MF $(DEPDIR)/test_lib-test-buffer.Tpo -c -o test_lib-test-buffer.o `test -f 'test-buffer.c' || echo '$(srcdir)/'`test-buffer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-buffer.Tpo $(DEPDIR)/test_lib-test-buffer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-buffer.c' object='test_lib-test-buffer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-buffer.o `test -f 'test-buffer.c' || echo '$(srcdir)/'`test-buffer.c + +test_lib-test-buffer.obj: test-buffer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-buffer.obj -MD -MP -MF $(DEPDIR)/test_lib-test-buffer.Tpo -c -o test_lib-test-buffer.obj `if test -f 'test-buffer.c'; then $(CYGPATH_W) 'test-buffer.c'; else $(CYGPATH_W) '$(srcdir)/test-buffer.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-buffer.Tpo $(DEPDIR)/test_lib-test-buffer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-buffer.c' object='test_lib-test-buffer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-buffer.obj `if test -f 'test-buffer.c'; then $(CYGPATH_W) 'test-buffer.c'; else $(CYGPATH_W) '$(srcdir)/test-buffer.c'; fi` + +test_lib-test-buffer-istream.o: test-buffer-istream.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-buffer-istream.o -MD -MP -MF $(DEPDIR)/test_lib-test-buffer-istream.Tpo -c -o test_lib-test-buffer-istream.o `test -f 'test-buffer-istream.c' || echo '$(srcdir)/'`test-buffer-istream.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-buffer-istream.Tpo $(DEPDIR)/test_lib-test-buffer-istream.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-buffer-istream.c' object='test_lib-test-buffer-istream.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-buffer-istream.o `test -f 'test-buffer-istream.c' || echo '$(srcdir)/'`test-buffer-istream.c + +test_lib-test-buffer-istream.obj: test-buffer-istream.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-buffer-istream.obj -MD -MP -MF $(DEPDIR)/test_lib-test-buffer-istream.Tpo -c -o test_lib-test-buffer-istream.obj `if test -f 'test-buffer-istream.c'; then $(CYGPATH_W) 'test-buffer-istream.c'; else $(CYGPATH_W) '$(srcdir)/test-buffer-istream.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-buffer-istream.Tpo $(DEPDIR)/test_lib-test-buffer-istream.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-buffer-istream.c' object='test_lib-test-buffer-istream.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-buffer-istream.obj `if test -f 'test-buffer-istream.c'; then $(CYGPATH_W) 'test-buffer-istream.c'; else $(CYGPATH_W) '$(srcdir)/test-buffer-istream.c'; fi` + +test_lib-test-byteorder.o: test-byteorder.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-byteorder.o -MD -MP -MF $(DEPDIR)/test_lib-test-byteorder.Tpo -c -o test_lib-test-byteorder.o `test -f 'test-byteorder.c' || echo '$(srcdir)/'`test-byteorder.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-byteorder.Tpo $(DEPDIR)/test_lib-test-byteorder.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-byteorder.c' object='test_lib-test-byteorder.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-byteorder.o `test -f 'test-byteorder.c' || echo '$(srcdir)/'`test-byteorder.c + +test_lib-test-byteorder.obj: test-byteorder.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-byteorder.obj -MD -MP -MF $(DEPDIR)/test_lib-test-byteorder.Tpo -c -o test_lib-test-byteorder.obj `if test -f 'test-byteorder.c'; then $(CYGPATH_W) 'test-byteorder.c'; else $(CYGPATH_W) '$(srcdir)/test-byteorder.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-byteorder.Tpo $(DEPDIR)/test_lib-test-byteorder.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-byteorder.c' object='test_lib-test-byteorder.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-byteorder.obj `if test -f 'test-byteorder.c'; then $(CYGPATH_W) 'test-byteorder.c'; else $(CYGPATH_W) '$(srcdir)/test-byteorder.c'; fi` + +test_lib-test-connection.o: test-connection.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-connection.o -MD -MP -MF $(DEPDIR)/test_lib-test-connection.Tpo -c -o test_lib-test-connection.o `test -f 'test-connection.c' || echo '$(srcdir)/'`test-connection.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-connection.Tpo $(DEPDIR)/test_lib-test-connection.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-connection.c' object='test_lib-test-connection.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-connection.o `test -f 'test-connection.c' || echo '$(srcdir)/'`test-connection.c + +test_lib-test-connection.obj: test-connection.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-connection.obj -MD -MP -MF $(DEPDIR)/test_lib-test-connection.Tpo -c -o test_lib-test-connection.obj `if test -f 'test-connection.c'; then $(CYGPATH_W) 'test-connection.c'; else $(CYGPATH_W) '$(srcdir)/test-connection.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-connection.Tpo $(DEPDIR)/test_lib-test-connection.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-connection.c' object='test_lib-test-connection.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-connection.obj `if test -f 'test-connection.c'; then $(CYGPATH_W) 'test-connection.c'; else $(CYGPATH_W) '$(srcdir)/test-connection.c'; fi` + +test_lib-test-crc32.o: test-crc32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-crc32.o -MD -MP -MF $(DEPDIR)/test_lib-test-crc32.Tpo -c -o test_lib-test-crc32.o `test -f 'test-crc32.c' || echo '$(srcdir)/'`test-crc32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-crc32.Tpo $(DEPDIR)/test_lib-test-crc32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-crc32.c' object='test_lib-test-crc32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-crc32.o `test -f 'test-crc32.c' || echo '$(srcdir)/'`test-crc32.c + +test_lib-test-crc32.obj: test-crc32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-crc32.obj -MD -MP -MF $(DEPDIR)/test_lib-test-crc32.Tpo -c -o test_lib-test-crc32.obj `if test -f 'test-crc32.c'; then $(CYGPATH_W) 'test-crc32.c'; else $(CYGPATH_W) '$(srcdir)/test-crc32.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-crc32.Tpo $(DEPDIR)/test_lib-test-crc32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-crc32.c' object='test_lib-test-crc32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-crc32.obj `if test -f 'test-crc32.c'; then $(CYGPATH_W) 'test-crc32.c'; else $(CYGPATH_W) '$(srcdir)/test-crc32.c'; fi` + +test_lib-test-cpu-limit.o: test-cpu-limit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-cpu-limit.o -MD -MP -MF $(DEPDIR)/test_lib-test-cpu-limit.Tpo -c -o test_lib-test-cpu-limit.o `test -f 'test-cpu-limit.c' || echo '$(srcdir)/'`test-cpu-limit.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-cpu-limit.Tpo $(DEPDIR)/test_lib-test-cpu-limit.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-cpu-limit.c' object='test_lib-test-cpu-limit.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-cpu-limit.o `test -f 'test-cpu-limit.c' || echo '$(srcdir)/'`test-cpu-limit.c + +test_lib-test-cpu-limit.obj: test-cpu-limit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-cpu-limit.obj -MD -MP -MF $(DEPDIR)/test_lib-test-cpu-limit.Tpo -c -o test_lib-test-cpu-limit.obj `if test -f 'test-cpu-limit.c'; then $(CYGPATH_W) 'test-cpu-limit.c'; else $(CYGPATH_W) '$(srcdir)/test-cpu-limit.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-cpu-limit.Tpo $(DEPDIR)/test_lib-test-cpu-limit.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-cpu-limit.c' object='test_lib-test-cpu-limit.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-cpu-limit.obj `if test -f 'test-cpu-limit.c'; then $(CYGPATH_W) 'test-cpu-limit.c'; else $(CYGPATH_W) '$(srcdir)/test-cpu-limit.c'; fi` + +test_lib-test-data-stack.o: test-data-stack.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-data-stack.o -MD -MP -MF $(DEPDIR)/test_lib-test-data-stack.Tpo -c -o test_lib-test-data-stack.o `test -f 'test-data-stack.c' || echo '$(srcdir)/'`test-data-stack.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-data-stack.Tpo $(DEPDIR)/test_lib-test-data-stack.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-data-stack.c' object='test_lib-test-data-stack.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-data-stack.o `test -f 'test-data-stack.c' || echo '$(srcdir)/'`test-data-stack.c + +test_lib-test-data-stack.obj: test-data-stack.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-data-stack.obj -MD -MP -MF $(DEPDIR)/test_lib-test-data-stack.Tpo -c -o test_lib-test-data-stack.obj `if test -f 'test-data-stack.c'; then $(CYGPATH_W) 'test-data-stack.c'; else $(CYGPATH_W) '$(srcdir)/test-data-stack.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-data-stack.Tpo $(DEPDIR)/test_lib-test-data-stack.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-data-stack.c' object='test_lib-test-data-stack.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-data-stack.obj `if test -f 'test-data-stack.c'; then $(CYGPATH_W) 'test-data-stack.c'; else $(CYGPATH_W) '$(srcdir)/test-data-stack.c'; fi` + +test_lib-test-env-util.o: test-env-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-env-util.o -MD -MP -MF $(DEPDIR)/test_lib-test-env-util.Tpo -c -o test_lib-test-env-util.o `test -f 'test-env-util.c' || echo '$(srcdir)/'`test-env-util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-env-util.Tpo $(DEPDIR)/test_lib-test-env-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-env-util.c' object='test_lib-test-env-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-env-util.o `test -f 'test-env-util.c' || echo '$(srcdir)/'`test-env-util.c + +test_lib-test-env-util.obj: test-env-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-env-util.obj -MD -MP -MF $(DEPDIR)/test_lib-test-env-util.Tpo -c -o test_lib-test-env-util.obj `if test -f 'test-env-util.c'; then $(CYGPATH_W) 'test-env-util.c'; else $(CYGPATH_W) '$(srcdir)/test-env-util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-env-util.Tpo $(DEPDIR)/test_lib-test-env-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-env-util.c' object='test_lib-test-env-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-env-util.obj `if test -f 'test-env-util.c'; then $(CYGPATH_W) 'test-env-util.c'; else $(CYGPATH_W) '$(srcdir)/test-env-util.c'; fi` + +test_lib-test-event-category-register.o: test-event-category-register.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-event-category-register.o -MD -MP -MF $(DEPDIR)/test_lib-test-event-category-register.Tpo -c -o test_lib-test-event-category-register.o `test -f 'test-event-category-register.c' || echo '$(srcdir)/'`test-event-category-register.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-event-category-register.Tpo $(DEPDIR)/test_lib-test-event-category-register.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-event-category-register.c' object='test_lib-test-event-category-register.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-event-category-register.o `test -f 'test-event-category-register.c' || echo '$(srcdir)/'`test-event-category-register.c + +test_lib-test-event-category-register.obj: test-event-category-register.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-event-category-register.obj -MD -MP -MF $(DEPDIR)/test_lib-test-event-category-register.Tpo -c -o test_lib-test-event-category-register.obj `if test -f 'test-event-category-register.c'; then $(CYGPATH_W) 'test-event-category-register.c'; else $(CYGPATH_W) '$(srcdir)/test-event-category-register.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-event-category-register.Tpo $(DEPDIR)/test_lib-test-event-category-register.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-event-category-register.c' object='test_lib-test-event-category-register.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-event-category-register.obj `if test -f 'test-event-category-register.c'; then $(CYGPATH_W) 'test-event-category-register.c'; else $(CYGPATH_W) '$(srcdir)/test-event-category-register.c'; fi` + +test_lib-test-event-filter.o: test-event-filter.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-event-filter.o -MD -MP -MF $(DEPDIR)/test_lib-test-event-filter.Tpo -c -o test_lib-test-event-filter.o `test -f 'test-event-filter.c' || echo '$(srcdir)/'`test-event-filter.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-event-filter.Tpo $(DEPDIR)/test_lib-test-event-filter.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-event-filter.c' object='test_lib-test-event-filter.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-event-filter.o `test -f 'test-event-filter.c' || echo '$(srcdir)/'`test-event-filter.c + +test_lib-test-event-filter.obj: test-event-filter.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-event-filter.obj -MD -MP -MF $(DEPDIR)/test_lib-test-event-filter.Tpo -c -o test_lib-test-event-filter.obj `if test -f 'test-event-filter.c'; then $(CYGPATH_W) 'test-event-filter.c'; else $(CYGPATH_W) '$(srcdir)/test-event-filter.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-event-filter.Tpo $(DEPDIR)/test_lib-test-event-filter.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-event-filter.c' object='test_lib-test-event-filter.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-event-filter.obj `if test -f 'test-event-filter.c'; then $(CYGPATH_W) 'test-event-filter.c'; else $(CYGPATH_W) '$(srcdir)/test-event-filter.c'; fi` + +test_lib-test-event-filter-expr.o: test-event-filter-expr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-event-filter-expr.o -MD -MP -MF $(DEPDIR)/test_lib-test-event-filter-expr.Tpo -c -o test_lib-test-event-filter-expr.o `test -f 'test-event-filter-expr.c' || echo '$(srcdir)/'`test-event-filter-expr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-event-filter-expr.Tpo $(DEPDIR)/test_lib-test-event-filter-expr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-event-filter-expr.c' object='test_lib-test-event-filter-expr.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-event-filter-expr.o `test -f 'test-event-filter-expr.c' || echo '$(srcdir)/'`test-event-filter-expr.c + +test_lib-test-event-filter-expr.obj: test-event-filter-expr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-event-filter-expr.obj -MD -MP -MF $(DEPDIR)/test_lib-test-event-filter-expr.Tpo -c -o test_lib-test-event-filter-expr.obj `if test -f 'test-event-filter-expr.c'; then $(CYGPATH_W) 'test-event-filter-expr.c'; else $(CYGPATH_W) '$(srcdir)/test-event-filter-expr.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-event-filter-expr.Tpo $(DEPDIR)/test_lib-test-event-filter-expr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-event-filter-expr.c' object='test_lib-test-event-filter-expr.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-event-filter-expr.obj `if test -f 'test-event-filter-expr.c'; then $(CYGPATH_W) 'test-event-filter-expr.c'; else $(CYGPATH_W) '$(srcdir)/test-event-filter-expr.c'; fi` + +test_lib-test-event-filter-merge.o: test-event-filter-merge.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-event-filter-merge.o -MD -MP -MF $(DEPDIR)/test_lib-test-event-filter-merge.Tpo -c -o test_lib-test-event-filter-merge.o `test -f 'test-event-filter-merge.c' || echo '$(srcdir)/'`test-event-filter-merge.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-event-filter-merge.Tpo $(DEPDIR)/test_lib-test-event-filter-merge.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-event-filter-merge.c' object='test_lib-test-event-filter-merge.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-event-filter-merge.o `test -f 'test-event-filter-merge.c' || echo '$(srcdir)/'`test-event-filter-merge.c + +test_lib-test-event-filter-merge.obj: test-event-filter-merge.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-event-filter-merge.obj -MD -MP -MF $(DEPDIR)/test_lib-test-event-filter-merge.Tpo -c -o test_lib-test-event-filter-merge.obj `if test -f 'test-event-filter-merge.c'; then $(CYGPATH_W) 'test-event-filter-merge.c'; else $(CYGPATH_W) '$(srcdir)/test-event-filter-merge.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-event-filter-merge.Tpo $(DEPDIR)/test_lib-test-event-filter-merge.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-event-filter-merge.c' object='test_lib-test-event-filter-merge.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-event-filter-merge.obj `if test -f 'test-event-filter-merge.c'; then $(CYGPATH_W) 'test-event-filter-merge.c'; else $(CYGPATH_W) '$(srcdir)/test-event-filter-merge.c'; fi` + +test_lib-test-event-filter-parser.o: test-event-filter-parser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-event-filter-parser.o -MD -MP -MF $(DEPDIR)/test_lib-test-event-filter-parser.Tpo -c -o test_lib-test-event-filter-parser.o `test -f 'test-event-filter-parser.c' || echo '$(srcdir)/'`test-event-filter-parser.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-event-filter-parser.Tpo $(DEPDIR)/test_lib-test-event-filter-parser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-event-filter-parser.c' object='test_lib-test-event-filter-parser.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-event-filter-parser.o `test -f 'test-event-filter-parser.c' || echo '$(srcdir)/'`test-event-filter-parser.c + +test_lib-test-event-filter-parser.obj: test-event-filter-parser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-event-filter-parser.obj -MD -MP -MF $(DEPDIR)/test_lib-test-event-filter-parser.Tpo -c -o test_lib-test-event-filter-parser.obj `if test -f 'test-event-filter-parser.c'; then $(CYGPATH_W) 'test-event-filter-parser.c'; else $(CYGPATH_W) '$(srcdir)/test-event-filter-parser.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-event-filter-parser.Tpo $(DEPDIR)/test_lib-test-event-filter-parser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-event-filter-parser.c' object='test_lib-test-event-filter-parser.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-event-filter-parser.obj `if test -f 'test-event-filter-parser.c'; then $(CYGPATH_W) 'test-event-filter-parser.c'; else $(CYGPATH_W) '$(srcdir)/test-event-filter-parser.c'; fi` + +test_lib-test-event-flatten.o: test-event-flatten.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-event-flatten.o -MD -MP -MF $(DEPDIR)/test_lib-test-event-flatten.Tpo -c -o test_lib-test-event-flatten.o `test -f 'test-event-flatten.c' || echo '$(srcdir)/'`test-event-flatten.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-event-flatten.Tpo $(DEPDIR)/test_lib-test-event-flatten.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-event-flatten.c' object='test_lib-test-event-flatten.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-event-flatten.o `test -f 'test-event-flatten.c' || echo '$(srcdir)/'`test-event-flatten.c + +test_lib-test-event-flatten.obj: test-event-flatten.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-event-flatten.obj -MD -MP -MF $(DEPDIR)/test_lib-test-event-flatten.Tpo -c -o test_lib-test-event-flatten.obj `if test -f 'test-event-flatten.c'; then $(CYGPATH_W) 'test-event-flatten.c'; else $(CYGPATH_W) '$(srcdir)/test-event-flatten.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-event-flatten.Tpo $(DEPDIR)/test_lib-test-event-flatten.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-event-flatten.c' object='test_lib-test-event-flatten.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-event-flatten.obj `if test -f 'test-event-flatten.c'; then $(CYGPATH_W) 'test-event-flatten.c'; else $(CYGPATH_W) '$(srcdir)/test-event-flatten.c'; fi` + +test_lib-test-event-log.o: test-event-log.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-event-log.o -MD -MP -MF $(DEPDIR)/test_lib-test-event-log.Tpo -c -o test_lib-test-event-log.o `test -f 'test-event-log.c' || echo '$(srcdir)/'`test-event-log.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-event-log.Tpo $(DEPDIR)/test_lib-test-event-log.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-event-log.c' object='test_lib-test-event-log.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-event-log.o `test -f 'test-event-log.c' || echo '$(srcdir)/'`test-event-log.c + +test_lib-test-event-log.obj: test-event-log.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-event-log.obj -MD -MP -MF $(DEPDIR)/test_lib-test-event-log.Tpo -c -o test_lib-test-event-log.obj `if test -f 'test-event-log.c'; then $(CYGPATH_W) 'test-event-log.c'; else $(CYGPATH_W) '$(srcdir)/test-event-log.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-event-log.Tpo $(DEPDIR)/test_lib-test-event-log.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-event-log.c' object='test_lib-test-event-log.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-event-log.obj `if test -f 'test-event-log.c'; then $(CYGPATH_W) 'test-event-log.c'; else $(CYGPATH_W) '$(srcdir)/test-event-log.c'; fi` + +test_lib-test-failures.o: test-failures.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-failures.o -MD -MP -MF $(DEPDIR)/test_lib-test-failures.Tpo -c -o test_lib-test-failures.o `test -f 'test-failures.c' || echo '$(srcdir)/'`test-failures.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-failures.Tpo $(DEPDIR)/test_lib-test-failures.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-failures.c' object='test_lib-test-failures.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-failures.o `test -f 'test-failures.c' || echo '$(srcdir)/'`test-failures.c + +test_lib-test-failures.obj: test-failures.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-failures.obj -MD -MP -MF $(DEPDIR)/test_lib-test-failures.Tpo -c -o test_lib-test-failures.obj `if test -f 'test-failures.c'; then $(CYGPATH_W) 'test-failures.c'; else $(CYGPATH_W) '$(srcdir)/test-failures.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-failures.Tpo $(DEPDIR)/test_lib-test-failures.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-failures.c' object='test_lib-test-failures.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-failures.obj `if test -f 'test-failures.c'; then $(CYGPATH_W) 'test-failures.c'; else $(CYGPATH_W) '$(srcdir)/test-failures.c'; fi` + +test_lib-test-fd-util.o: test-fd-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-fd-util.o -MD -MP -MF $(DEPDIR)/test_lib-test-fd-util.Tpo -c -o test_lib-test-fd-util.o `test -f 'test-fd-util.c' || echo '$(srcdir)/'`test-fd-util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-fd-util.Tpo $(DEPDIR)/test_lib-test-fd-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-fd-util.c' object='test_lib-test-fd-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-fd-util.o `test -f 'test-fd-util.c' || echo '$(srcdir)/'`test-fd-util.c + +test_lib-test-fd-util.obj: test-fd-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-fd-util.obj -MD -MP -MF $(DEPDIR)/test_lib-test-fd-util.Tpo -c -o test_lib-test-fd-util.obj `if test -f 'test-fd-util.c'; then $(CYGPATH_W) 'test-fd-util.c'; else $(CYGPATH_W) '$(srcdir)/test-fd-util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-fd-util.Tpo $(DEPDIR)/test_lib-test-fd-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-fd-util.c' object='test_lib-test-fd-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-fd-util.obj `if test -f 'test-fd-util.c'; then $(CYGPATH_W) 'test-fd-util.c'; else $(CYGPATH_W) '$(srcdir)/test-fd-util.c'; fi` + +test_lib-test-file-cache.o: test-file-cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-file-cache.o -MD -MP -MF $(DEPDIR)/test_lib-test-file-cache.Tpo -c -o test_lib-test-file-cache.o `test -f 'test-file-cache.c' || echo '$(srcdir)/'`test-file-cache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-file-cache.Tpo $(DEPDIR)/test_lib-test-file-cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-file-cache.c' object='test_lib-test-file-cache.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-file-cache.o `test -f 'test-file-cache.c' || echo '$(srcdir)/'`test-file-cache.c + +test_lib-test-file-cache.obj: test-file-cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-file-cache.obj -MD -MP -MF $(DEPDIR)/test_lib-test-file-cache.Tpo -c -o test_lib-test-file-cache.obj `if test -f 'test-file-cache.c'; then $(CYGPATH_W) 'test-file-cache.c'; else $(CYGPATH_W) '$(srcdir)/test-file-cache.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-file-cache.Tpo $(DEPDIR)/test_lib-test-file-cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-file-cache.c' object='test_lib-test-file-cache.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-file-cache.obj `if test -f 'test-file-cache.c'; then $(CYGPATH_W) 'test-file-cache.c'; else $(CYGPATH_W) '$(srcdir)/test-file-cache.c'; fi` + +test_lib-test-file-create-locked.o: test-file-create-locked.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-file-create-locked.o -MD -MP -MF $(DEPDIR)/test_lib-test-file-create-locked.Tpo -c -o test_lib-test-file-create-locked.o `test -f 'test-file-create-locked.c' || echo '$(srcdir)/'`test-file-create-locked.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-file-create-locked.Tpo $(DEPDIR)/test_lib-test-file-create-locked.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-file-create-locked.c' object='test_lib-test-file-create-locked.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-file-create-locked.o `test -f 'test-file-create-locked.c' || echo '$(srcdir)/'`test-file-create-locked.c + +test_lib-test-file-create-locked.obj: test-file-create-locked.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-file-create-locked.obj -MD -MP -MF $(DEPDIR)/test_lib-test-file-create-locked.Tpo -c -o test_lib-test-file-create-locked.obj `if test -f 'test-file-create-locked.c'; then $(CYGPATH_W) 'test-file-create-locked.c'; else $(CYGPATH_W) '$(srcdir)/test-file-create-locked.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-file-create-locked.Tpo $(DEPDIR)/test_lib-test-file-create-locked.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-file-create-locked.c' object='test_lib-test-file-create-locked.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-file-create-locked.obj `if test -f 'test-file-create-locked.c'; then $(CYGPATH_W) 'test-file-create-locked.c'; else $(CYGPATH_W) '$(srcdir)/test-file-create-locked.c'; fi` + +test_lib-test-guid.o: test-guid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-guid.o -MD -MP -MF $(DEPDIR)/test_lib-test-guid.Tpo -c -o test_lib-test-guid.o `test -f 'test-guid.c' || echo '$(srcdir)/'`test-guid.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-guid.Tpo $(DEPDIR)/test_lib-test-guid.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-guid.c' object='test_lib-test-guid.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-guid.o `test -f 'test-guid.c' || echo '$(srcdir)/'`test-guid.c + +test_lib-test-guid.obj: test-guid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-guid.obj -MD -MP -MF $(DEPDIR)/test_lib-test-guid.Tpo -c -o test_lib-test-guid.obj `if test -f 'test-guid.c'; then $(CYGPATH_W) 'test-guid.c'; else $(CYGPATH_W) '$(srcdir)/test-guid.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-guid.Tpo $(DEPDIR)/test_lib-test-guid.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-guid.c' object='test_lib-test-guid.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-guid.obj `if test -f 'test-guid.c'; then $(CYGPATH_W) 'test-guid.c'; else $(CYGPATH_W) '$(srcdir)/test-guid.c'; fi` + +test_lib-test-hash.o: test-hash.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hash.o -MD -MP -MF $(DEPDIR)/test_lib-test-hash.Tpo -c -o test_lib-test-hash.o `test -f 'test-hash.c' || echo '$(srcdir)/'`test-hash.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hash.Tpo $(DEPDIR)/test_lib-test-hash.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hash.c' object='test_lib-test-hash.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hash.o `test -f 'test-hash.c' || echo '$(srcdir)/'`test-hash.c + +test_lib-test-hash.obj: test-hash.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hash.obj -MD -MP -MF $(DEPDIR)/test_lib-test-hash.Tpo -c -o test_lib-test-hash.obj `if test -f 'test-hash.c'; then $(CYGPATH_W) 'test-hash.c'; else $(CYGPATH_W) '$(srcdir)/test-hash.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hash.Tpo $(DEPDIR)/test_lib-test-hash.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hash.c' object='test_lib-test-hash.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hash.obj `if test -f 'test-hash.c'; then $(CYGPATH_W) 'test-hash.c'; else $(CYGPATH_W) '$(srcdir)/test-hash.c'; fi` + +test_lib-test-hash-format.o: test-hash-format.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hash-format.o -MD -MP -MF $(DEPDIR)/test_lib-test-hash-format.Tpo -c -o test_lib-test-hash-format.o `test -f 'test-hash-format.c' || echo '$(srcdir)/'`test-hash-format.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hash-format.Tpo $(DEPDIR)/test_lib-test-hash-format.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hash-format.c' object='test_lib-test-hash-format.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hash-format.o `test -f 'test-hash-format.c' || echo '$(srcdir)/'`test-hash-format.c + +test_lib-test-hash-format.obj: test-hash-format.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hash-format.obj -MD -MP -MF $(DEPDIR)/test_lib-test-hash-format.Tpo -c -o test_lib-test-hash-format.obj `if test -f 'test-hash-format.c'; then $(CYGPATH_W) 'test-hash-format.c'; else $(CYGPATH_W) '$(srcdir)/test-hash-format.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hash-format.Tpo $(DEPDIR)/test_lib-test-hash-format.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hash-format.c' object='test_lib-test-hash-format.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hash-format.obj `if test -f 'test-hash-format.c'; then $(CYGPATH_W) 'test-hash-format.c'; else $(CYGPATH_W) '$(srcdir)/test-hash-format.c'; fi` + +test_lib-test-hash-method.o: test-hash-method.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hash-method.o -MD -MP -MF $(DEPDIR)/test_lib-test-hash-method.Tpo -c -o test_lib-test-hash-method.o `test -f 'test-hash-method.c' || echo '$(srcdir)/'`test-hash-method.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hash-method.Tpo $(DEPDIR)/test_lib-test-hash-method.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hash-method.c' object='test_lib-test-hash-method.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hash-method.o `test -f 'test-hash-method.c' || echo '$(srcdir)/'`test-hash-method.c + +test_lib-test-hash-method.obj: test-hash-method.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hash-method.obj -MD -MP -MF $(DEPDIR)/test_lib-test-hash-method.Tpo -c -o test_lib-test-hash-method.obj `if test -f 'test-hash-method.c'; then $(CYGPATH_W) 'test-hash-method.c'; else $(CYGPATH_W) '$(srcdir)/test-hash-method.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hash-method.Tpo $(DEPDIR)/test_lib-test-hash-method.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hash-method.c' object='test_lib-test-hash-method.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hash-method.obj `if test -f 'test-hash-method.c'; then $(CYGPATH_W) 'test-hash-method.c'; else $(CYGPATH_W) '$(srcdir)/test-hash-method.c'; fi` + +test_lib-test-hmac.o: test-hmac.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hmac.o -MD -MP -MF $(DEPDIR)/test_lib-test-hmac.Tpo -c -o test_lib-test-hmac.o `test -f 'test-hmac.c' || echo '$(srcdir)/'`test-hmac.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hmac.Tpo $(DEPDIR)/test_lib-test-hmac.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hmac.c' object='test_lib-test-hmac.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hmac.o `test -f 'test-hmac.c' || echo '$(srcdir)/'`test-hmac.c + +test_lib-test-hmac.obj: test-hmac.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hmac.obj -MD -MP -MF $(DEPDIR)/test_lib-test-hmac.Tpo -c -o test_lib-test-hmac.obj `if test -f 'test-hmac.c'; then $(CYGPATH_W) 'test-hmac.c'; else $(CYGPATH_W) '$(srcdir)/test-hmac.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hmac.Tpo $(DEPDIR)/test_lib-test-hmac.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hmac.c' object='test_lib-test-hmac.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hmac.obj `if test -f 'test-hmac.c'; then $(CYGPATH_W) 'test-hmac.c'; else $(CYGPATH_W) '$(srcdir)/test-hmac.c'; fi` + +test_lib-test-hex-binary.o: test-hex-binary.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hex-binary.o -MD -MP -MF $(DEPDIR)/test_lib-test-hex-binary.Tpo -c -o test_lib-test-hex-binary.o `test -f 'test-hex-binary.c' || echo '$(srcdir)/'`test-hex-binary.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hex-binary.Tpo $(DEPDIR)/test_lib-test-hex-binary.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hex-binary.c' object='test_lib-test-hex-binary.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hex-binary.o `test -f 'test-hex-binary.c' || echo '$(srcdir)/'`test-hex-binary.c + +test_lib-test-hex-binary.obj: test-hex-binary.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-hex-binary.obj -MD -MP -MF $(DEPDIR)/test_lib-test-hex-binary.Tpo -c -o test_lib-test-hex-binary.obj `if test -f 'test-hex-binary.c'; then $(CYGPATH_W) 'test-hex-binary.c'; else $(CYGPATH_W) '$(srcdir)/test-hex-binary.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-hex-binary.Tpo $(DEPDIR)/test_lib-test-hex-binary.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-hex-binary.c' object='test_lib-test-hex-binary.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-hex-binary.obj `if test -f 'test-hex-binary.c'; then $(CYGPATH_W) 'test-hex-binary.c'; else $(CYGPATH_W) '$(srcdir)/test-hex-binary.c'; fi` + +test_lib-test-imem.o: test-imem.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-imem.o -MD -MP -MF $(DEPDIR)/test_lib-test-imem.Tpo -c -o test_lib-test-imem.o `test -f 'test-imem.c' || echo '$(srcdir)/'`test-imem.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-imem.Tpo $(DEPDIR)/test_lib-test-imem.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-imem.c' object='test_lib-test-imem.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-imem.o `test -f 'test-imem.c' || echo '$(srcdir)/'`test-imem.c + +test_lib-test-imem.obj: test-imem.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-imem.obj -MD -MP -MF $(DEPDIR)/test_lib-test-imem.Tpo -c -o test_lib-test-imem.obj `if test -f 'test-imem.c'; then $(CYGPATH_W) 'test-imem.c'; else $(CYGPATH_W) '$(srcdir)/test-imem.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-imem.Tpo $(DEPDIR)/test_lib-test-imem.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-imem.c' object='test_lib-test-imem.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-imem.obj `if test -f 'test-imem.c'; then $(CYGPATH_W) 'test-imem.c'; else $(CYGPATH_W) '$(srcdir)/test-imem.c'; fi` + +test_lib-test-ioloop.o: test-ioloop.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ioloop.o -MD -MP -MF $(DEPDIR)/test_lib-test-ioloop.Tpo -c -o test_lib-test-ioloop.o `test -f 'test-ioloop.c' || echo '$(srcdir)/'`test-ioloop.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ioloop.Tpo $(DEPDIR)/test_lib-test-ioloop.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ioloop.c' object='test_lib-test-ioloop.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ioloop.o `test -f 'test-ioloop.c' || echo '$(srcdir)/'`test-ioloop.c + +test_lib-test-ioloop.obj: test-ioloop.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ioloop.obj -MD -MP -MF $(DEPDIR)/test_lib-test-ioloop.Tpo -c -o test_lib-test-ioloop.obj `if test -f 'test-ioloop.c'; then $(CYGPATH_W) 'test-ioloop.c'; else $(CYGPATH_W) '$(srcdir)/test-ioloop.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ioloop.Tpo $(DEPDIR)/test_lib-test-ioloop.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ioloop.c' object='test_lib-test-ioloop.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ioloop.obj `if test -f 'test-ioloop.c'; then $(CYGPATH_W) 'test-ioloop.c'; else $(CYGPATH_W) '$(srcdir)/test-ioloop.c'; fi` + +test_lib-test-iso8601-date.o: test-iso8601-date.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-iso8601-date.o -MD -MP -MF $(DEPDIR)/test_lib-test-iso8601-date.Tpo -c -o test_lib-test-iso8601-date.o `test -f 'test-iso8601-date.c' || echo '$(srcdir)/'`test-iso8601-date.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-iso8601-date.Tpo $(DEPDIR)/test_lib-test-iso8601-date.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-iso8601-date.c' object='test_lib-test-iso8601-date.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-iso8601-date.o `test -f 'test-iso8601-date.c' || echo '$(srcdir)/'`test-iso8601-date.c + +test_lib-test-iso8601-date.obj: test-iso8601-date.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-iso8601-date.obj -MD -MP -MF $(DEPDIR)/test_lib-test-iso8601-date.Tpo -c -o test_lib-test-iso8601-date.obj `if test -f 'test-iso8601-date.c'; then $(CYGPATH_W) 'test-iso8601-date.c'; else $(CYGPATH_W) '$(srcdir)/test-iso8601-date.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-iso8601-date.Tpo $(DEPDIR)/test_lib-test-iso8601-date.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-iso8601-date.c' object='test_lib-test-iso8601-date.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-iso8601-date.obj `if test -f 'test-iso8601-date.c'; then $(CYGPATH_W) 'test-iso8601-date.c'; else $(CYGPATH_W) '$(srcdir)/test-iso8601-date.c'; fi` + +test_lib-test-iostream-pump.o: test-iostream-pump.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-iostream-pump.o -MD -MP -MF $(DEPDIR)/test_lib-test-iostream-pump.Tpo -c -o test_lib-test-iostream-pump.o `test -f 'test-iostream-pump.c' || echo '$(srcdir)/'`test-iostream-pump.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-iostream-pump.Tpo $(DEPDIR)/test_lib-test-iostream-pump.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-iostream-pump.c' object='test_lib-test-iostream-pump.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-iostream-pump.o `test -f 'test-iostream-pump.c' || echo '$(srcdir)/'`test-iostream-pump.c + +test_lib-test-iostream-pump.obj: test-iostream-pump.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-iostream-pump.obj -MD -MP -MF $(DEPDIR)/test_lib-test-iostream-pump.Tpo -c -o test_lib-test-iostream-pump.obj `if test -f 'test-iostream-pump.c'; then $(CYGPATH_W) 'test-iostream-pump.c'; else $(CYGPATH_W) '$(srcdir)/test-iostream-pump.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-iostream-pump.Tpo $(DEPDIR)/test_lib-test-iostream-pump.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-iostream-pump.c' object='test_lib-test-iostream-pump.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-iostream-pump.obj `if test -f 'test-iostream-pump.c'; then $(CYGPATH_W) 'test-iostream-pump.c'; else $(CYGPATH_W) '$(srcdir)/test-iostream-pump.c'; fi` + +test_lib-test-iostream-proxy.o: test-iostream-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-iostream-proxy.o -MD -MP -MF $(DEPDIR)/test_lib-test-iostream-proxy.Tpo -c -o test_lib-test-iostream-proxy.o `test -f 'test-iostream-proxy.c' || echo '$(srcdir)/'`test-iostream-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-iostream-proxy.Tpo $(DEPDIR)/test_lib-test-iostream-proxy.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-iostream-proxy.c' object='test_lib-test-iostream-proxy.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-iostream-proxy.o `test -f 'test-iostream-proxy.c' || echo '$(srcdir)/'`test-iostream-proxy.c + +test_lib-test-iostream-proxy.obj: test-iostream-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-iostream-proxy.obj -MD -MP -MF $(DEPDIR)/test_lib-test-iostream-proxy.Tpo -c -o test_lib-test-iostream-proxy.obj `if test -f 'test-iostream-proxy.c'; then $(CYGPATH_W) 'test-iostream-proxy.c'; else $(CYGPATH_W) '$(srcdir)/test-iostream-proxy.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-iostream-proxy.Tpo $(DEPDIR)/test_lib-test-iostream-proxy.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-iostream-proxy.c' object='test_lib-test-iostream-proxy.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-iostream-proxy.obj `if test -f 'test-iostream-proxy.c'; then $(CYGPATH_W) 'test-iostream-proxy.c'; else $(CYGPATH_W) '$(srcdir)/test-iostream-proxy.c'; fi` + +test_lib-test-iostream-temp.o: test-iostream-temp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-iostream-temp.o -MD -MP -MF $(DEPDIR)/test_lib-test-iostream-temp.Tpo -c -o test_lib-test-iostream-temp.o `test -f 'test-iostream-temp.c' || echo '$(srcdir)/'`test-iostream-temp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-iostream-temp.Tpo $(DEPDIR)/test_lib-test-iostream-temp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-iostream-temp.c' object='test_lib-test-iostream-temp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-iostream-temp.o `test -f 'test-iostream-temp.c' || echo '$(srcdir)/'`test-iostream-temp.c + +test_lib-test-iostream-temp.obj: test-iostream-temp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-iostream-temp.obj -MD -MP -MF $(DEPDIR)/test_lib-test-iostream-temp.Tpo -c -o test_lib-test-iostream-temp.obj `if test -f 'test-iostream-temp.c'; then $(CYGPATH_W) 'test-iostream-temp.c'; else $(CYGPATH_W) '$(srcdir)/test-iostream-temp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-iostream-temp.Tpo $(DEPDIR)/test_lib-test-iostream-temp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-iostream-temp.c' object='test_lib-test-iostream-temp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-iostream-temp.obj `if test -f 'test-iostream-temp.c'; then $(CYGPATH_W) 'test-iostream-temp.c'; else $(CYGPATH_W) '$(srcdir)/test-iostream-temp.c'; fi` + +test_lib-test-istream.o: test-istream.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream.Tpo -c -o test_lib-test-istream.o `test -f 'test-istream.c' || echo '$(srcdir)/'`test-istream.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream.Tpo $(DEPDIR)/test_lib-test-istream.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream.c' object='test_lib-test-istream.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream.o `test -f 'test-istream.c' || echo '$(srcdir)/'`test-istream.c + +test_lib-test-istream.obj: test-istream.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream.Tpo -c -o test_lib-test-istream.obj `if test -f 'test-istream.c'; then $(CYGPATH_W) 'test-istream.c'; else $(CYGPATH_W) '$(srcdir)/test-istream.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream.Tpo $(DEPDIR)/test_lib-test-istream.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream.c' object='test_lib-test-istream.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream.obj `if test -f 'test-istream.c'; then $(CYGPATH_W) 'test-istream.c'; else $(CYGPATH_W) '$(srcdir)/test-istream.c'; fi` + +test_lib-test-istream-base64-decoder.o: test-istream-base64-decoder.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-base64-decoder.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-base64-decoder.Tpo -c -o test_lib-test-istream-base64-decoder.o `test -f 'test-istream-base64-decoder.c' || echo '$(srcdir)/'`test-istream-base64-decoder.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-base64-decoder.Tpo $(DEPDIR)/test_lib-test-istream-base64-decoder.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-base64-decoder.c' object='test_lib-test-istream-base64-decoder.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-base64-decoder.o `test -f 'test-istream-base64-decoder.c' || echo '$(srcdir)/'`test-istream-base64-decoder.c + +test_lib-test-istream-base64-decoder.obj: test-istream-base64-decoder.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-base64-decoder.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-base64-decoder.Tpo -c -o test_lib-test-istream-base64-decoder.obj `if test -f 'test-istream-base64-decoder.c'; then $(CYGPATH_W) 'test-istream-base64-decoder.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-base64-decoder.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-base64-decoder.Tpo $(DEPDIR)/test_lib-test-istream-base64-decoder.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-base64-decoder.c' object='test_lib-test-istream-base64-decoder.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-base64-decoder.obj `if test -f 'test-istream-base64-decoder.c'; then $(CYGPATH_W) 'test-istream-base64-decoder.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-base64-decoder.c'; fi` + +test_lib-test-istream-base64-encoder.o: test-istream-base64-encoder.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-base64-encoder.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-base64-encoder.Tpo -c -o test_lib-test-istream-base64-encoder.o `test -f 'test-istream-base64-encoder.c' || echo '$(srcdir)/'`test-istream-base64-encoder.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-base64-encoder.Tpo $(DEPDIR)/test_lib-test-istream-base64-encoder.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-base64-encoder.c' object='test_lib-test-istream-base64-encoder.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-base64-encoder.o `test -f 'test-istream-base64-encoder.c' || echo '$(srcdir)/'`test-istream-base64-encoder.c + +test_lib-test-istream-base64-encoder.obj: test-istream-base64-encoder.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-base64-encoder.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-base64-encoder.Tpo -c -o test_lib-test-istream-base64-encoder.obj `if test -f 'test-istream-base64-encoder.c'; then $(CYGPATH_W) 'test-istream-base64-encoder.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-base64-encoder.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-base64-encoder.Tpo $(DEPDIR)/test_lib-test-istream-base64-encoder.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-base64-encoder.c' object='test_lib-test-istream-base64-encoder.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-base64-encoder.obj `if test -f 'test-istream-base64-encoder.c'; then $(CYGPATH_W) 'test-istream-base64-encoder.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-base64-encoder.c'; fi` + +test_lib-test-istream-chain.o: test-istream-chain.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-chain.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-chain.Tpo -c -o test_lib-test-istream-chain.o `test -f 'test-istream-chain.c' || echo '$(srcdir)/'`test-istream-chain.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-chain.Tpo $(DEPDIR)/test_lib-test-istream-chain.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-chain.c' object='test_lib-test-istream-chain.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-chain.o `test -f 'test-istream-chain.c' || echo '$(srcdir)/'`test-istream-chain.c + +test_lib-test-istream-chain.obj: test-istream-chain.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-chain.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-chain.Tpo -c -o test_lib-test-istream-chain.obj `if test -f 'test-istream-chain.c'; then $(CYGPATH_W) 'test-istream-chain.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-chain.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-chain.Tpo $(DEPDIR)/test_lib-test-istream-chain.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-chain.c' object='test_lib-test-istream-chain.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-chain.obj `if test -f 'test-istream-chain.c'; then $(CYGPATH_W) 'test-istream-chain.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-chain.c'; fi` + +test_lib-test-istream-concat.o: test-istream-concat.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-concat.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-concat.Tpo -c -o test_lib-test-istream-concat.o `test -f 'test-istream-concat.c' || echo '$(srcdir)/'`test-istream-concat.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-concat.Tpo $(DEPDIR)/test_lib-test-istream-concat.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-concat.c' object='test_lib-test-istream-concat.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-concat.o `test -f 'test-istream-concat.c' || echo '$(srcdir)/'`test-istream-concat.c + +test_lib-test-istream-concat.obj: test-istream-concat.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-concat.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-concat.Tpo -c -o test_lib-test-istream-concat.obj `if test -f 'test-istream-concat.c'; then $(CYGPATH_W) 'test-istream-concat.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-concat.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-concat.Tpo $(DEPDIR)/test_lib-test-istream-concat.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-concat.c' object='test_lib-test-istream-concat.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-concat.obj `if test -f 'test-istream-concat.c'; then $(CYGPATH_W) 'test-istream-concat.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-concat.c'; fi` + +test_lib-test-istream-crlf.o: test-istream-crlf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-crlf.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-crlf.Tpo -c -o test_lib-test-istream-crlf.o `test -f 'test-istream-crlf.c' || echo '$(srcdir)/'`test-istream-crlf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-crlf.Tpo $(DEPDIR)/test_lib-test-istream-crlf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-crlf.c' object='test_lib-test-istream-crlf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-crlf.o `test -f 'test-istream-crlf.c' || echo '$(srcdir)/'`test-istream-crlf.c + +test_lib-test-istream-crlf.obj: test-istream-crlf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-crlf.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-crlf.Tpo -c -o test_lib-test-istream-crlf.obj `if test -f 'test-istream-crlf.c'; then $(CYGPATH_W) 'test-istream-crlf.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-crlf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-crlf.Tpo $(DEPDIR)/test_lib-test-istream-crlf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-crlf.c' object='test_lib-test-istream-crlf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-crlf.obj `if test -f 'test-istream-crlf.c'; then $(CYGPATH_W) 'test-istream-crlf.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-crlf.c'; fi` + +test_lib-test-istream-failure-at.o: test-istream-failure-at.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-failure-at.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-failure-at.Tpo -c -o test_lib-test-istream-failure-at.o `test -f 'test-istream-failure-at.c' || echo '$(srcdir)/'`test-istream-failure-at.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-failure-at.Tpo $(DEPDIR)/test_lib-test-istream-failure-at.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-failure-at.c' object='test_lib-test-istream-failure-at.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-failure-at.o `test -f 'test-istream-failure-at.c' || echo '$(srcdir)/'`test-istream-failure-at.c + +test_lib-test-istream-failure-at.obj: test-istream-failure-at.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-failure-at.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-failure-at.Tpo -c -o test_lib-test-istream-failure-at.obj `if test -f 'test-istream-failure-at.c'; then $(CYGPATH_W) 'test-istream-failure-at.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-failure-at.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-failure-at.Tpo $(DEPDIR)/test_lib-test-istream-failure-at.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-failure-at.c' object='test_lib-test-istream-failure-at.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-failure-at.obj `if test -f 'test-istream-failure-at.c'; then $(CYGPATH_W) 'test-istream-failure-at.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-failure-at.c'; fi` + +test_lib-test-istream-jsonstr.o: test-istream-jsonstr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-jsonstr.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-jsonstr.Tpo -c -o test_lib-test-istream-jsonstr.o `test -f 'test-istream-jsonstr.c' || echo '$(srcdir)/'`test-istream-jsonstr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-jsonstr.Tpo $(DEPDIR)/test_lib-test-istream-jsonstr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-jsonstr.c' object='test_lib-test-istream-jsonstr.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-jsonstr.o `test -f 'test-istream-jsonstr.c' || echo '$(srcdir)/'`test-istream-jsonstr.c + +test_lib-test-istream-jsonstr.obj: test-istream-jsonstr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-jsonstr.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-jsonstr.Tpo -c -o test_lib-test-istream-jsonstr.obj `if test -f 'test-istream-jsonstr.c'; then $(CYGPATH_W) 'test-istream-jsonstr.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-jsonstr.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-jsonstr.Tpo $(DEPDIR)/test_lib-test-istream-jsonstr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-jsonstr.c' object='test_lib-test-istream-jsonstr.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-jsonstr.obj `if test -f 'test-istream-jsonstr.c'; then $(CYGPATH_W) 'test-istream-jsonstr.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-jsonstr.c'; fi` + +test_lib-test-istream-multiplex.o: test-istream-multiplex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-multiplex.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-multiplex.Tpo -c -o test_lib-test-istream-multiplex.o `test -f 'test-istream-multiplex.c' || echo '$(srcdir)/'`test-istream-multiplex.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-multiplex.Tpo $(DEPDIR)/test_lib-test-istream-multiplex.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-multiplex.c' object='test_lib-test-istream-multiplex.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-multiplex.o `test -f 'test-istream-multiplex.c' || echo '$(srcdir)/'`test-istream-multiplex.c + +test_lib-test-istream-multiplex.obj: test-istream-multiplex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-multiplex.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-multiplex.Tpo -c -o test_lib-test-istream-multiplex.obj `if test -f 'test-istream-multiplex.c'; then $(CYGPATH_W) 'test-istream-multiplex.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-multiplex.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-multiplex.Tpo $(DEPDIR)/test_lib-test-istream-multiplex.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-multiplex.c' object='test_lib-test-istream-multiplex.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-multiplex.obj `if test -f 'test-istream-multiplex.c'; then $(CYGPATH_W) 'test-istream-multiplex.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-multiplex.c'; fi` + +test_lib-test-istream-seekable.o: test-istream-seekable.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-seekable.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-seekable.Tpo -c -o test_lib-test-istream-seekable.o `test -f 'test-istream-seekable.c' || echo '$(srcdir)/'`test-istream-seekable.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-seekable.Tpo $(DEPDIR)/test_lib-test-istream-seekable.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-seekable.c' object='test_lib-test-istream-seekable.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-seekable.o `test -f 'test-istream-seekable.c' || echo '$(srcdir)/'`test-istream-seekable.c + +test_lib-test-istream-seekable.obj: test-istream-seekable.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-seekable.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-seekable.Tpo -c -o test_lib-test-istream-seekable.obj `if test -f 'test-istream-seekable.c'; then $(CYGPATH_W) 'test-istream-seekable.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-seekable.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-seekable.Tpo $(DEPDIR)/test_lib-test-istream-seekable.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-seekable.c' object='test_lib-test-istream-seekable.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-seekable.obj `if test -f 'test-istream-seekable.c'; then $(CYGPATH_W) 'test-istream-seekable.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-seekable.c'; fi` + +test_lib-test-istream-sized.o: test-istream-sized.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-sized.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-sized.Tpo -c -o test_lib-test-istream-sized.o `test -f 'test-istream-sized.c' || echo '$(srcdir)/'`test-istream-sized.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-sized.Tpo $(DEPDIR)/test_lib-test-istream-sized.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-sized.c' object='test_lib-test-istream-sized.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-sized.o `test -f 'test-istream-sized.c' || echo '$(srcdir)/'`test-istream-sized.c + +test_lib-test-istream-sized.obj: test-istream-sized.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-sized.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-sized.Tpo -c -o test_lib-test-istream-sized.obj `if test -f 'test-istream-sized.c'; then $(CYGPATH_W) 'test-istream-sized.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-sized.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-sized.Tpo $(DEPDIR)/test_lib-test-istream-sized.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-sized.c' object='test_lib-test-istream-sized.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-sized.obj `if test -f 'test-istream-sized.c'; then $(CYGPATH_W) 'test-istream-sized.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-sized.c'; fi` + +test_lib-test-istream-tee.o: test-istream-tee.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-tee.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-tee.Tpo -c -o test_lib-test-istream-tee.o `test -f 'test-istream-tee.c' || echo '$(srcdir)/'`test-istream-tee.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-tee.Tpo $(DEPDIR)/test_lib-test-istream-tee.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-tee.c' object='test_lib-test-istream-tee.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-tee.o `test -f 'test-istream-tee.c' || echo '$(srcdir)/'`test-istream-tee.c + +test_lib-test-istream-tee.obj: test-istream-tee.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-tee.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-tee.Tpo -c -o test_lib-test-istream-tee.obj `if test -f 'test-istream-tee.c'; then $(CYGPATH_W) 'test-istream-tee.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-tee.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-tee.Tpo $(DEPDIR)/test_lib-test-istream-tee.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-tee.c' object='test_lib-test-istream-tee.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-tee.obj `if test -f 'test-istream-tee.c'; then $(CYGPATH_W) 'test-istream-tee.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-tee.c'; fi` + +test_lib-test-istream-try.o: test-istream-try.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-try.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-try.Tpo -c -o test_lib-test-istream-try.o `test -f 'test-istream-try.c' || echo '$(srcdir)/'`test-istream-try.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-try.Tpo $(DEPDIR)/test_lib-test-istream-try.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-try.c' object='test_lib-test-istream-try.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-try.o `test -f 'test-istream-try.c' || echo '$(srcdir)/'`test-istream-try.c + +test_lib-test-istream-try.obj: test-istream-try.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-try.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-try.Tpo -c -o test_lib-test-istream-try.obj `if test -f 'test-istream-try.c'; then $(CYGPATH_W) 'test-istream-try.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-try.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-try.Tpo $(DEPDIR)/test_lib-test-istream-try.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-try.c' object='test_lib-test-istream-try.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-try.obj `if test -f 'test-istream-try.c'; then $(CYGPATH_W) 'test-istream-try.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-try.c'; fi` + +test_lib-test-istream-unix.o: test-istream-unix.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-unix.o -MD -MP -MF $(DEPDIR)/test_lib-test-istream-unix.Tpo -c -o test_lib-test-istream-unix.o `test -f 'test-istream-unix.c' || echo '$(srcdir)/'`test-istream-unix.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-unix.Tpo $(DEPDIR)/test_lib-test-istream-unix.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-unix.c' object='test_lib-test-istream-unix.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-unix.o `test -f 'test-istream-unix.c' || echo '$(srcdir)/'`test-istream-unix.c + +test_lib-test-istream-unix.obj: test-istream-unix.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-istream-unix.obj -MD -MP -MF $(DEPDIR)/test_lib-test-istream-unix.Tpo -c -o test_lib-test-istream-unix.obj `if test -f 'test-istream-unix.c'; then $(CYGPATH_W) 'test-istream-unix.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-unix.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-istream-unix.Tpo $(DEPDIR)/test_lib-test-istream-unix.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-istream-unix.c' object='test_lib-test-istream-unix.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-istream-unix.obj `if test -f 'test-istream-unix.c'; then $(CYGPATH_W) 'test-istream-unix.c'; else $(CYGPATH_W) '$(srcdir)/test-istream-unix.c'; fi` + +test_lib-test-json-parser.o: test-json-parser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-json-parser.o -MD -MP -MF $(DEPDIR)/test_lib-test-json-parser.Tpo -c -o test_lib-test-json-parser.o `test -f 'test-json-parser.c' || echo '$(srcdir)/'`test-json-parser.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-json-parser.Tpo $(DEPDIR)/test_lib-test-json-parser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-json-parser.c' object='test_lib-test-json-parser.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-json-parser.o `test -f 'test-json-parser.c' || echo '$(srcdir)/'`test-json-parser.c + +test_lib-test-json-parser.obj: test-json-parser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-json-parser.obj -MD -MP -MF $(DEPDIR)/test_lib-test-json-parser.Tpo -c -o test_lib-test-json-parser.obj `if test -f 'test-json-parser.c'; then $(CYGPATH_W) 'test-json-parser.c'; else $(CYGPATH_W) '$(srcdir)/test-json-parser.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-json-parser.Tpo $(DEPDIR)/test_lib-test-json-parser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-json-parser.c' object='test_lib-test-json-parser.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-json-parser.obj `if test -f 'test-json-parser.c'; then $(CYGPATH_W) 'test-json-parser.c'; else $(CYGPATH_W) '$(srcdir)/test-json-parser.c'; fi` + +test_lib-test-json-tree.o: test-json-tree.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-json-tree.o -MD -MP -MF $(DEPDIR)/test_lib-test-json-tree.Tpo -c -o test_lib-test-json-tree.o `test -f 'test-json-tree.c' || echo '$(srcdir)/'`test-json-tree.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-json-tree.Tpo $(DEPDIR)/test_lib-test-json-tree.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-json-tree.c' object='test_lib-test-json-tree.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-json-tree.o `test -f 'test-json-tree.c' || echo '$(srcdir)/'`test-json-tree.c + +test_lib-test-json-tree.obj: test-json-tree.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-json-tree.obj -MD -MP -MF $(DEPDIR)/test_lib-test-json-tree.Tpo -c -o test_lib-test-json-tree.obj `if test -f 'test-json-tree.c'; then $(CYGPATH_W) 'test-json-tree.c'; else $(CYGPATH_W) '$(srcdir)/test-json-tree.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-json-tree.Tpo $(DEPDIR)/test_lib-test-json-tree.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-json-tree.c' object='test_lib-test-json-tree.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-json-tree.obj `if test -f 'test-json-tree.c'; then $(CYGPATH_W) 'test-json-tree.c'; else $(CYGPATH_W) '$(srcdir)/test-json-tree.c'; fi` + +test_lib-test-lib-event.o: test-lib-event.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-lib-event.o -MD -MP -MF $(DEPDIR)/test_lib-test-lib-event.Tpo -c -o test_lib-test-lib-event.o `test -f 'test-lib-event.c' || echo '$(srcdir)/'`test-lib-event.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-lib-event.Tpo $(DEPDIR)/test_lib-test-lib-event.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-lib-event.c' object='test_lib-test-lib-event.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-lib-event.o `test -f 'test-lib-event.c' || echo '$(srcdir)/'`test-lib-event.c + +test_lib-test-lib-event.obj: test-lib-event.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-lib-event.obj -MD -MP -MF $(DEPDIR)/test_lib-test-lib-event.Tpo -c -o test_lib-test-lib-event.obj `if test -f 'test-lib-event.c'; then $(CYGPATH_W) 'test-lib-event.c'; else $(CYGPATH_W) '$(srcdir)/test-lib-event.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-lib-event.Tpo $(DEPDIR)/test_lib-test-lib-event.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-lib-event.c' object='test_lib-test-lib-event.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-lib-event.obj `if test -f 'test-lib-event.c'; then $(CYGPATH_W) 'test-lib-event.c'; else $(CYGPATH_W) '$(srcdir)/test-lib-event.c'; fi` + +test_lib-test-lib-signals.o: test-lib-signals.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-lib-signals.o -MD -MP -MF $(DEPDIR)/test_lib-test-lib-signals.Tpo -c -o test_lib-test-lib-signals.o `test -f 'test-lib-signals.c' || echo '$(srcdir)/'`test-lib-signals.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-lib-signals.Tpo $(DEPDIR)/test_lib-test-lib-signals.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-lib-signals.c' object='test_lib-test-lib-signals.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-lib-signals.o `test -f 'test-lib-signals.c' || echo '$(srcdir)/'`test-lib-signals.c + +test_lib-test-lib-signals.obj: test-lib-signals.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-lib-signals.obj -MD -MP -MF $(DEPDIR)/test_lib-test-lib-signals.Tpo -c -o test_lib-test-lib-signals.obj `if test -f 'test-lib-signals.c'; then $(CYGPATH_W) 'test-lib-signals.c'; else $(CYGPATH_W) '$(srcdir)/test-lib-signals.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-lib-signals.Tpo $(DEPDIR)/test_lib-test-lib-signals.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-lib-signals.c' object='test_lib-test-lib-signals.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-lib-signals.obj `if test -f 'test-lib-signals.c'; then $(CYGPATH_W) 'test-lib-signals.c'; else $(CYGPATH_W) '$(srcdir)/test-lib-signals.c'; fi` + +test_lib-test-llist.o: test-llist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-llist.o -MD -MP -MF $(DEPDIR)/test_lib-test-llist.Tpo -c -o test_lib-test-llist.o `test -f 'test-llist.c' || echo '$(srcdir)/'`test-llist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-llist.Tpo $(DEPDIR)/test_lib-test-llist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-llist.c' object='test_lib-test-llist.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-llist.o `test -f 'test-llist.c' || echo '$(srcdir)/'`test-llist.c + +test_lib-test-llist.obj: test-llist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-llist.obj -MD -MP -MF $(DEPDIR)/test_lib-test-llist.Tpo -c -o test_lib-test-llist.obj `if test -f 'test-llist.c'; then $(CYGPATH_W) 'test-llist.c'; else $(CYGPATH_W) '$(srcdir)/test-llist.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-llist.Tpo $(DEPDIR)/test_lib-test-llist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-llist.c' object='test_lib-test-llist.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-llist.obj `if test -f 'test-llist.c'; then $(CYGPATH_W) 'test-llist.c'; else $(CYGPATH_W) '$(srcdir)/test-llist.c'; fi` + +test_lib-test-log-throttle.o: test-log-throttle.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-log-throttle.o -MD -MP -MF $(DEPDIR)/test_lib-test-log-throttle.Tpo -c -o test_lib-test-log-throttle.o `test -f 'test-log-throttle.c' || echo '$(srcdir)/'`test-log-throttle.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-log-throttle.Tpo $(DEPDIR)/test_lib-test-log-throttle.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-log-throttle.c' object='test_lib-test-log-throttle.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-log-throttle.o `test -f 'test-log-throttle.c' || echo '$(srcdir)/'`test-log-throttle.c + +test_lib-test-log-throttle.obj: test-log-throttle.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-log-throttle.obj -MD -MP -MF $(DEPDIR)/test_lib-test-log-throttle.Tpo -c -o test_lib-test-log-throttle.obj `if test -f 'test-log-throttle.c'; then $(CYGPATH_W) 'test-log-throttle.c'; else $(CYGPATH_W) '$(srcdir)/test-log-throttle.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-log-throttle.Tpo $(DEPDIR)/test_lib-test-log-throttle.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-log-throttle.c' object='test_lib-test-log-throttle.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-log-throttle.obj `if test -f 'test-log-throttle.c'; then $(CYGPATH_W) 'test-log-throttle.c'; else $(CYGPATH_W) '$(srcdir)/test-log-throttle.c'; fi` + +test_lib-test-macros.o: test-macros.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-macros.o -MD -MP -MF $(DEPDIR)/test_lib-test-macros.Tpo -c -o test_lib-test-macros.o `test -f 'test-macros.c' || echo '$(srcdir)/'`test-macros.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-macros.Tpo $(DEPDIR)/test_lib-test-macros.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-macros.c' object='test_lib-test-macros.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-macros.o `test -f 'test-macros.c' || echo '$(srcdir)/'`test-macros.c + +test_lib-test-macros.obj: test-macros.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-macros.obj -MD -MP -MF $(DEPDIR)/test_lib-test-macros.Tpo -c -o test_lib-test-macros.obj `if test -f 'test-macros.c'; then $(CYGPATH_W) 'test-macros.c'; else $(CYGPATH_W) '$(srcdir)/test-macros.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-macros.Tpo $(DEPDIR)/test_lib-test-macros.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-macros.c' object='test_lib-test-macros.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-macros.obj `if test -f 'test-macros.c'; then $(CYGPATH_W) 'test-macros.c'; else $(CYGPATH_W) '$(srcdir)/test-macros.c'; fi` + +test_lib-test-malloc-overflow.o: test-malloc-overflow.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-malloc-overflow.o -MD -MP -MF $(DEPDIR)/test_lib-test-malloc-overflow.Tpo -c -o test_lib-test-malloc-overflow.o `test -f 'test-malloc-overflow.c' || echo '$(srcdir)/'`test-malloc-overflow.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-malloc-overflow.Tpo $(DEPDIR)/test_lib-test-malloc-overflow.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-malloc-overflow.c' object='test_lib-test-malloc-overflow.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-malloc-overflow.o `test -f 'test-malloc-overflow.c' || echo '$(srcdir)/'`test-malloc-overflow.c + +test_lib-test-malloc-overflow.obj: test-malloc-overflow.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-malloc-overflow.obj -MD -MP -MF $(DEPDIR)/test_lib-test-malloc-overflow.Tpo -c -o test_lib-test-malloc-overflow.obj `if test -f 'test-malloc-overflow.c'; then $(CYGPATH_W) 'test-malloc-overflow.c'; else $(CYGPATH_W) '$(srcdir)/test-malloc-overflow.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-malloc-overflow.Tpo $(DEPDIR)/test_lib-test-malloc-overflow.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-malloc-overflow.c' object='test_lib-test-malloc-overflow.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-malloc-overflow.obj `if test -f 'test-malloc-overflow.c'; then $(CYGPATH_W) 'test-malloc-overflow.c'; else $(CYGPATH_W) '$(srcdir)/test-malloc-overflow.c'; fi` + +test_lib-test-memarea.o: test-memarea.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-memarea.o -MD -MP -MF $(DEPDIR)/test_lib-test-memarea.Tpo -c -o test_lib-test-memarea.o `test -f 'test-memarea.c' || echo '$(srcdir)/'`test-memarea.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-memarea.Tpo $(DEPDIR)/test_lib-test-memarea.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-memarea.c' object='test_lib-test-memarea.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-memarea.o `test -f 'test-memarea.c' || echo '$(srcdir)/'`test-memarea.c + +test_lib-test-memarea.obj: test-memarea.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-memarea.obj -MD -MP -MF $(DEPDIR)/test_lib-test-memarea.Tpo -c -o test_lib-test-memarea.obj `if test -f 'test-memarea.c'; then $(CYGPATH_W) 'test-memarea.c'; else $(CYGPATH_W) '$(srcdir)/test-memarea.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-memarea.Tpo $(DEPDIR)/test_lib-test-memarea.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-memarea.c' object='test_lib-test-memarea.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-memarea.obj `if test -f 'test-memarea.c'; then $(CYGPATH_W) 'test-memarea.c'; else $(CYGPATH_W) '$(srcdir)/test-memarea.c'; fi` + +test_lib-test-mempool.o: test-mempool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-mempool.o -MD -MP -MF $(DEPDIR)/test_lib-test-mempool.Tpo -c -o test_lib-test-mempool.o `test -f 'test-mempool.c' || echo '$(srcdir)/'`test-mempool.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-mempool.Tpo $(DEPDIR)/test_lib-test-mempool.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mempool.c' object='test_lib-test-mempool.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-mempool.o `test -f 'test-mempool.c' || echo '$(srcdir)/'`test-mempool.c + +test_lib-test-mempool.obj: test-mempool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-mempool.obj -MD -MP -MF $(DEPDIR)/test_lib-test-mempool.Tpo -c -o test_lib-test-mempool.obj `if test -f 'test-mempool.c'; then $(CYGPATH_W) 'test-mempool.c'; else $(CYGPATH_W) '$(srcdir)/test-mempool.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-mempool.Tpo $(DEPDIR)/test_lib-test-mempool.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mempool.c' object='test_lib-test-mempool.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-mempool.obj `if test -f 'test-mempool.c'; then $(CYGPATH_W) 'test-mempool.c'; else $(CYGPATH_W) '$(srcdir)/test-mempool.c'; fi` + +test_lib-test-mempool-allocfree.o: test-mempool-allocfree.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-mempool-allocfree.o -MD -MP -MF $(DEPDIR)/test_lib-test-mempool-allocfree.Tpo -c -o test_lib-test-mempool-allocfree.o `test -f 'test-mempool-allocfree.c' || echo '$(srcdir)/'`test-mempool-allocfree.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-mempool-allocfree.Tpo $(DEPDIR)/test_lib-test-mempool-allocfree.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mempool-allocfree.c' object='test_lib-test-mempool-allocfree.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-mempool-allocfree.o `test -f 'test-mempool-allocfree.c' || echo '$(srcdir)/'`test-mempool-allocfree.c + +test_lib-test-mempool-allocfree.obj: test-mempool-allocfree.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-mempool-allocfree.obj -MD -MP -MF $(DEPDIR)/test_lib-test-mempool-allocfree.Tpo -c -o test_lib-test-mempool-allocfree.obj `if test -f 'test-mempool-allocfree.c'; then $(CYGPATH_W) 'test-mempool-allocfree.c'; else $(CYGPATH_W) '$(srcdir)/test-mempool-allocfree.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-mempool-allocfree.Tpo $(DEPDIR)/test_lib-test-mempool-allocfree.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mempool-allocfree.c' object='test_lib-test-mempool-allocfree.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-mempool-allocfree.obj `if test -f 'test-mempool-allocfree.c'; then $(CYGPATH_W) 'test-mempool-allocfree.c'; else $(CYGPATH_W) '$(srcdir)/test-mempool-allocfree.c'; fi` + +test_lib-test-mempool-alloconly.o: test-mempool-alloconly.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-mempool-alloconly.o -MD -MP -MF $(DEPDIR)/test_lib-test-mempool-alloconly.Tpo -c -o test_lib-test-mempool-alloconly.o `test -f 'test-mempool-alloconly.c' || echo '$(srcdir)/'`test-mempool-alloconly.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-mempool-alloconly.Tpo $(DEPDIR)/test_lib-test-mempool-alloconly.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mempool-alloconly.c' object='test_lib-test-mempool-alloconly.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-mempool-alloconly.o `test -f 'test-mempool-alloconly.c' || echo '$(srcdir)/'`test-mempool-alloconly.c + +test_lib-test-mempool-alloconly.obj: test-mempool-alloconly.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-mempool-alloconly.obj -MD -MP -MF $(DEPDIR)/test_lib-test-mempool-alloconly.Tpo -c -o test_lib-test-mempool-alloconly.obj `if test -f 'test-mempool-alloconly.c'; then $(CYGPATH_W) 'test-mempool-alloconly.c'; else $(CYGPATH_W) '$(srcdir)/test-mempool-alloconly.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-mempool-alloconly.Tpo $(DEPDIR)/test_lib-test-mempool-alloconly.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mempool-alloconly.c' object='test_lib-test-mempool-alloconly.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-mempool-alloconly.obj `if test -f 'test-mempool-alloconly.c'; then $(CYGPATH_W) 'test-mempool-alloconly.c'; else $(CYGPATH_W) '$(srcdir)/test-mempool-alloconly.c'; fi` + +test_lib-test-pkcs5.o: test-pkcs5.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-pkcs5.o -MD -MP -MF $(DEPDIR)/test_lib-test-pkcs5.Tpo -c -o test_lib-test-pkcs5.o `test -f 'test-pkcs5.c' || echo '$(srcdir)/'`test-pkcs5.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-pkcs5.Tpo $(DEPDIR)/test_lib-test-pkcs5.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-pkcs5.c' object='test_lib-test-pkcs5.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-pkcs5.o `test -f 'test-pkcs5.c' || echo '$(srcdir)/'`test-pkcs5.c + +test_lib-test-pkcs5.obj: test-pkcs5.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-pkcs5.obj -MD -MP -MF $(DEPDIR)/test_lib-test-pkcs5.Tpo -c -o test_lib-test-pkcs5.obj `if test -f 'test-pkcs5.c'; then $(CYGPATH_W) 'test-pkcs5.c'; else $(CYGPATH_W) '$(srcdir)/test-pkcs5.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-pkcs5.Tpo $(DEPDIR)/test_lib-test-pkcs5.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-pkcs5.c' object='test_lib-test-pkcs5.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-pkcs5.obj `if test -f 'test-pkcs5.c'; then $(CYGPATH_W) 'test-pkcs5.c'; else $(CYGPATH_W) '$(srcdir)/test-pkcs5.c'; fi` + +test_lib-test-net.o: test-net.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-net.o -MD -MP -MF $(DEPDIR)/test_lib-test-net.Tpo -c -o test_lib-test-net.o `test -f 'test-net.c' || echo '$(srcdir)/'`test-net.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-net.Tpo $(DEPDIR)/test_lib-test-net.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-net.c' object='test_lib-test-net.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-net.o `test -f 'test-net.c' || echo '$(srcdir)/'`test-net.c + +test_lib-test-net.obj: test-net.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-net.obj -MD -MP -MF $(DEPDIR)/test_lib-test-net.Tpo -c -o test_lib-test-net.obj `if test -f 'test-net.c'; then $(CYGPATH_W) 'test-net.c'; else $(CYGPATH_W) '$(srcdir)/test-net.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-net.Tpo $(DEPDIR)/test_lib-test-net.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-net.c' object='test_lib-test-net.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-net.obj `if test -f 'test-net.c'; then $(CYGPATH_W) 'test-net.c'; else $(CYGPATH_W) '$(srcdir)/test-net.c'; fi` + +test_lib-test-numpack.o: test-numpack.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-numpack.o -MD -MP -MF $(DEPDIR)/test_lib-test-numpack.Tpo -c -o test_lib-test-numpack.o `test -f 'test-numpack.c' || echo '$(srcdir)/'`test-numpack.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-numpack.Tpo $(DEPDIR)/test_lib-test-numpack.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-numpack.c' object='test_lib-test-numpack.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-numpack.o `test -f 'test-numpack.c' || echo '$(srcdir)/'`test-numpack.c + +test_lib-test-numpack.obj: test-numpack.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-numpack.obj -MD -MP -MF $(DEPDIR)/test_lib-test-numpack.Tpo -c -o test_lib-test-numpack.obj `if test -f 'test-numpack.c'; then $(CYGPATH_W) 'test-numpack.c'; else $(CYGPATH_W) '$(srcdir)/test-numpack.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-numpack.Tpo $(DEPDIR)/test_lib-test-numpack.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-numpack.c' object='test_lib-test-numpack.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-numpack.obj `if test -f 'test-numpack.c'; then $(CYGPATH_W) 'test-numpack.c'; else $(CYGPATH_W) '$(srcdir)/test-numpack.c'; fi` + +test_lib-test-ostream-buffer.o: test-ostream-buffer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-buffer.o -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-buffer.Tpo -c -o test_lib-test-ostream-buffer.o `test -f 'test-ostream-buffer.c' || echo '$(srcdir)/'`test-ostream-buffer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-buffer.Tpo $(DEPDIR)/test_lib-test-ostream-buffer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-buffer.c' object='test_lib-test-ostream-buffer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-buffer.o `test -f 'test-ostream-buffer.c' || echo '$(srcdir)/'`test-ostream-buffer.c + +test_lib-test-ostream-buffer.obj: test-ostream-buffer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-buffer.obj -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-buffer.Tpo -c -o test_lib-test-ostream-buffer.obj `if test -f 'test-ostream-buffer.c'; then $(CYGPATH_W) 'test-ostream-buffer.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-buffer.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-buffer.Tpo $(DEPDIR)/test_lib-test-ostream-buffer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-buffer.c' object='test_lib-test-ostream-buffer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-buffer.obj `if test -f 'test-ostream-buffer.c'; then $(CYGPATH_W) 'test-ostream-buffer.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-buffer.c'; fi` + +test_lib-test-ostream-failure-at.o: test-ostream-failure-at.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-failure-at.o -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-failure-at.Tpo -c -o test_lib-test-ostream-failure-at.o `test -f 'test-ostream-failure-at.c' || echo '$(srcdir)/'`test-ostream-failure-at.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-failure-at.Tpo $(DEPDIR)/test_lib-test-ostream-failure-at.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-failure-at.c' object='test_lib-test-ostream-failure-at.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-failure-at.o `test -f 'test-ostream-failure-at.c' || echo '$(srcdir)/'`test-ostream-failure-at.c + +test_lib-test-ostream-failure-at.obj: test-ostream-failure-at.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-failure-at.obj -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-failure-at.Tpo -c -o test_lib-test-ostream-failure-at.obj `if test -f 'test-ostream-failure-at.c'; then $(CYGPATH_W) 'test-ostream-failure-at.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-failure-at.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-failure-at.Tpo $(DEPDIR)/test_lib-test-ostream-failure-at.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-failure-at.c' object='test_lib-test-ostream-failure-at.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-failure-at.obj `if test -f 'test-ostream-failure-at.c'; then $(CYGPATH_W) 'test-ostream-failure-at.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-failure-at.c'; fi` + +test_lib-test-ostream-file.o: test-ostream-file.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-file.o -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-file.Tpo -c -o test_lib-test-ostream-file.o `test -f 'test-ostream-file.c' || echo '$(srcdir)/'`test-ostream-file.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-file.Tpo $(DEPDIR)/test_lib-test-ostream-file.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-file.c' object='test_lib-test-ostream-file.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-file.o `test -f 'test-ostream-file.c' || echo '$(srcdir)/'`test-ostream-file.c + +test_lib-test-ostream-file.obj: test-ostream-file.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-file.obj -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-file.Tpo -c -o test_lib-test-ostream-file.obj `if test -f 'test-ostream-file.c'; then $(CYGPATH_W) 'test-ostream-file.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-file.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-file.Tpo $(DEPDIR)/test_lib-test-ostream-file.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-file.c' object='test_lib-test-ostream-file.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-file.obj `if test -f 'test-ostream-file.c'; then $(CYGPATH_W) 'test-ostream-file.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-file.c'; fi` + +test_lib-test-ostream-multiplex.o: test-ostream-multiplex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-multiplex.o -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-multiplex.Tpo -c -o test_lib-test-ostream-multiplex.o `test -f 'test-ostream-multiplex.c' || echo '$(srcdir)/'`test-ostream-multiplex.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-multiplex.Tpo $(DEPDIR)/test_lib-test-ostream-multiplex.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-multiplex.c' object='test_lib-test-ostream-multiplex.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-multiplex.o `test -f 'test-ostream-multiplex.c' || echo '$(srcdir)/'`test-ostream-multiplex.c + +test_lib-test-ostream-multiplex.obj: test-ostream-multiplex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-ostream-multiplex.obj -MD -MP -MF $(DEPDIR)/test_lib-test-ostream-multiplex.Tpo -c -o test_lib-test-ostream-multiplex.obj `if test -f 'test-ostream-multiplex.c'; then $(CYGPATH_W) 'test-ostream-multiplex.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-multiplex.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-ostream-multiplex.Tpo $(DEPDIR)/test_lib-test-ostream-multiplex.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-ostream-multiplex.c' object='test_lib-test-ostream-multiplex.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-ostream-multiplex.obj `if test -f 'test-ostream-multiplex.c'; then $(CYGPATH_W) 'test-ostream-multiplex.c'; else $(CYGPATH_W) '$(srcdir)/test-ostream-multiplex.c'; fi` + +test_lib-test-multiplex.o: test-multiplex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-multiplex.o -MD -MP -MF $(DEPDIR)/test_lib-test-multiplex.Tpo -c -o test_lib-test-multiplex.o `test -f 'test-multiplex.c' || echo '$(srcdir)/'`test-multiplex.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-multiplex.Tpo $(DEPDIR)/test_lib-test-multiplex.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-multiplex.c' object='test_lib-test-multiplex.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-multiplex.o `test -f 'test-multiplex.c' || echo '$(srcdir)/'`test-multiplex.c + +test_lib-test-multiplex.obj: test-multiplex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-multiplex.obj -MD -MP -MF $(DEPDIR)/test_lib-test-multiplex.Tpo -c -o test_lib-test-multiplex.obj `if test -f 'test-multiplex.c'; then $(CYGPATH_W) 'test-multiplex.c'; else $(CYGPATH_W) '$(srcdir)/test-multiplex.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-multiplex.Tpo $(DEPDIR)/test_lib-test-multiplex.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-multiplex.c' object='test_lib-test-multiplex.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-multiplex.obj `if test -f 'test-multiplex.c'; then $(CYGPATH_W) 'test-multiplex.c'; else $(CYGPATH_W) '$(srcdir)/test-multiplex.c'; fi` + +test_lib-test-path-util.o: test-path-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-path-util.o -MD -MP -MF $(DEPDIR)/test_lib-test-path-util.Tpo -c -o test_lib-test-path-util.o `test -f 'test-path-util.c' || echo '$(srcdir)/'`test-path-util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-path-util.Tpo $(DEPDIR)/test_lib-test-path-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-path-util.c' object='test_lib-test-path-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-path-util.o `test -f 'test-path-util.c' || echo '$(srcdir)/'`test-path-util.c + +test_lib-test-path-util.obj: test-path-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-path-util.obj -MD -MP -MF $(DEPDIR)/test_lib-test-path-util.Tpo -c -o test_lib-test-path-util.obj `if test -f 'test-path-util.c'; then $(CYGPATH_W) 'test-path-util.c'; else $(CYGPATH_W) '$(srcdir)/test-path-util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-path-util.Tpo $(DEPDIR)/test_lib-test-path-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-path-util.c' object='test_lib-test-path-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-path-util.obj `if test -f 'test-path-util.c'; then $(CYGPATH_W) 'test-path-util.c'; else $(CYGPATH_W) '$(srcdir)/test-path-util.c'; fi` + +test_lib-test-primes.o: test-primes.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-primes.o -MD -MP -MF $(DEPDIR)/test_lib-test-primes.Tpo -c -o test_lib-test-primes.o `test -f 'test-primes.c' || echo '$(srcdir)/'`test-primes.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-primes.Tpo $(DEPDIR)/test_lib-test-primes.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-primes.c' object='test_lib-test-primes.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-primes.o `test -f 'test-primes.c' || echo '$(srcdir)/'`test-primes.c + +test_lib-test-primes.obj: test-primes.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-primes.obj -MD -MP -MF $(DEPDIR)/test_lib-test-primes.Tpo -c -o test_lib-test-primes.obj `if test -f 'test-primes.c'; then $(CYGPATH_W) 'test-primes.c'; else $(CYGPATH_W) '$(srcdir)/test-primes.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-primes.Tpo $(DEPDIR)/test_lib-test-primes.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-primes.c' object='test_lib-test-primes.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-primes.obj `if test -f 'test-primes.c'; then $(CYGPATH_W) 'test-primes.c'; else $(CYGPATH_W) '$(srcdir)/test-primes.c'; fi` + +test_lib-test-printf-format-fix.o: test-printf-format-fix.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-printf-format-fix.o -MD -MP -MF $(DEPDIR)/test_lib-test-printf-format-fix.Tpo -c -o test_lib-test-printf-format-fix.o `test -f 'test-printf-format-fix.c' || echo '$(srcdir)/'`test-printf-format-fix.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-printf-format-fix.Tpo $(DEPDIR)/test_lib-test-printf-format-fix.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-printf-format-fix.c' object='test_lib-test-printf-format-fix.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-printf-format-fix.o `test -f 'test-printf-format-fix.c' || echo '$(srcdir)/'`test-printf-format-fix.c + +test_lib-test-printf-format-fix.obj: test-printf-format-fix.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-printf-format-fix.obj -MD -MP -MF $(DEPDIR)/test_lib-test-printf-format-fix.Tpo -c -o test_lib-test-printf-format-fix.obj `if test -f 'test-printf-format-fix.c'; then $(CYGPATH_W) 'test-printf-format-fix.c'; else $(CYGPATH_W) '$(srcdir)/test-printf-format-fix.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-printf-format-fix.Tpo $(DEPDIR)/test_lib-test-printf-format-fix.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-printf-format-fix.c' object='test_lib-test-printf-format-fix.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-printf-format-fix.obj `if test -f 'test-printf-format-fix.c'; then $(CYGPATH_W) 'test-printf-format-fix.c'; else $(CYGPATH_W) '$(srcdir)/test-printf-format-fix.c'; fi` + +test_lib-test-priorityq.o: test-priorityq.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-priorityq.o -MD -MP -MF $(DEPDIR)/test_lib-test-priorityq.Tpo -c -o test_lib-test-priorityq.o `test -f 'test-priorityq.c' || echo '$(srcdir)/'`test-priorityq.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-priorityq.Tpo $(DEPDIR)/test_lib-test-priorityq.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-priorityq.c' object='test_lib-test-priorityq.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-priorityq.o `test -f 'test-priorityq.c' || echo '$(srcdir)/'`test-priorityq.c + +test_lib-test-priorityq.obj: test-priorityq.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-priorityq.obj -MD -MP -MF $(DEPDIR)/test_lib-test-priorityq.Tpo -c -o test_lib-test-priorityq.obj `if test -f 'test-priorityq.c'; then $(CYGPATH_W) 'test-priorityq.c'; else $(CYGPATH_W) '$(srcdir)/test-priorityq.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-priorityq.Tpo $(DEPDIR)/test_lib-test-priorityq.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-priorityq.c' object='test_lib-test-priorityq.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-priorityq.obj `if test -f 'test-priorityq.c'; then $(CYGPATH_W) 'test-priorityq.c'; else $(CYGPATH_W) '$(srcdir)/test-priorityq.c'; fi` + +test_lib-test-random.o: test-random.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-random.o -MD -MP -MF $(DEPDIR)/test_lib-test-random.Tpo -c -o test_lib-test-random.o `test -f 'test-random.c' || echo '$(srcdir)/'`test-random.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-random.Tpo $(DEPDIR)/test_lib-test-random.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-random.c' object='test_lib-test-random.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-random.o `test -f 'test-random.c' || echo '$(srcdir)/'`test-random.c + +test_lib-test-random.obj: test-random.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-random.obj -MD -MP -MF $(DEPDIR)/test_lib-test-random.Tpo -c -o test_lib-test-random.obj `if test -f 'test-random.c'; then $(CYGPATH_W) 'test-random.c'; else $(CYGPATH_W) '$(srcdir)/test-random.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-random.Tpo $(DEPDIR)/test_lib-test-random.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-random.c' object='test_lib-test-random.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-random.obj `if test -f 'test-random.c'; then $(CYGPATH_W) 'test-random.c'; else $(CYGPATH_W) '$(srcdir)/test-random.c'; fi` + +test_lib-test-seq-range-array.o: test-seq-range-array.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-seq-range-array.o -MD -MP -MF $(DEPDIR)/test_lib-test-seq-range-array.Tpo -c -o test_lib-test-seq-range-array.o `test -f 'test-seq-range-array.c' || echo '$(srcdir)/'`test-seq-range-array.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-seq-range-array.Tpo $(DEPDIR)/test_lib-test-seq-range-array.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-seq-range-array.c' object='test_lib-test-seq-range-array.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-seq-range-array.o `test -f 'test-seq-range-array.c' || echo '$(srcdir)/'`test-seq-range-array.c + +test_lib-test-seq-range-array.obj: test-seq-range-array.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-seq-range-array.obj -MD -MP -MF $(DEPDIR)/test_lib-test-seq-range-array.Tpo -c -o test_lib-test-seq-range-array.obj `if test -f 'test-seq-range-array.c'; then $(CYGPATH_W) 'test-seq-range-array.c'; else $(CYGPATH_W) '$(srcdir)/test-seq-range-array.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-seq-range-array.Tpo $(DEPDIR)/test_lib-test-seq-range-array.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-seq-range-array.c' object='test_lib-test-seq-range-array.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-seq-range-array.obj `if test -f 'test-seq-range-array.c'; then $(CYGPATH_W) 'test-seq-range-array.c'; else $(CYGPATH_W) '$(srcdir)/test-seq-range-array.c'; fi` + +test_lib-test-seq-set-builder.o: test-seq-set-builder.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-seq-set-builder.o -MD -MP -MF $(DEPDIR)/test_lib-test-seq-set-builder.Tpo -c -o test_lib-test-seq-set-builder.o `test -f 'test-seq-set-builder.c' || echo '$(srcdir)/'`test-seq-set-builder.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-seq-set-builder.Tpo $(DEPDIR)/test_lib-test-seq-set-builder.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-seq-set-builder.c' object='test_lib-test-seq-set-builder.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-seq-set-builder.o `test -f 'test-seq-set-builder.c' || echo '$(srcdir)/'`test-seq-set-builder.c + +test_lib-test-seq-set-builder.obj: test-seq-set-builder.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-seq-set-builder.obj -MD -MP -MF $(DEPDIR)/test_lib-test-seq-set-builder.Tpo -c -o test_lib-test-seq-set-builder.obj `if test -f 'test-seq-set-builder.c'; then $(CYGPATH_W) 'test-seq-set-builder.c'; else $(CYGPATH_W) '$(srcdir)/test-seq-set-builder.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-seq-set-builder.Tpo $(DEPDIR)/test_lib-test-seq-set-builder.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-seq-set-builder.c' object='test_lib-test-seq-set-builder.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-seq-set-builder.obj `if test -f 'test-seq-set-builder.c'; then $(CYGPATH_W) 'test-seq-set-builder.c'; else $(CYGPATH_W) '$(srcdir)/test-seq-set-builder.c'; fi` + +test_lib-test-stats-dist.o: test-stats-dist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-stats-dist.o -MD -MP -MF $(DEPDIR)/test_lib-test-stats-dist.Tpo -c -o test_lib-test-stats-dist.o `test -f 'test-stats-dist.c' || echo '$(srcdir)/'`test-stats-dist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-stats-dist.Tpo $(DEPDIR)/test_lib-test-stats-dist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-stats-dist.c' object='test_lib-test-stats-dist.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-stats-dist.o `test -f 'test-stats-dist.c' || echo '$(srcdir)/'`test-stats-dist.c + +test_lib-test-stats-dist.obj: test-stats-dist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-stats-dist.obj -MD -MP -MF $(DEPDIR)/test_lib-test-stats-dist.Tpo -c -o test_lib-test-stats-dist.obj `if test -f 'test-stats-dist.c'; then $(CYGPATH_W) 'test-stats-dist.c'; else $(CYGPATH_W) '$(srcdir)/test-stats-dist.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-stats-dist.Tpo $(DEPDIR)/test_lib-test-stats-dist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-stats-dist.c' object='test_lib-test-stats-dist.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-stats-dist.obj `if test -f 'test-stats-dist.c'; then $(CYGPATH_W) 'test-stats-dist.c'; else $(CYGPATH_W) '$(srcdir)/test-stats-dist.c'; fi` + +test_lib-test-str.o: test-str.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str.o -MD -MP -MF $(DEPDIR)/test_lib-test-str.Tpo -c -o test_lib-test-str.o `test -f 'test-str.c' || echo '$(srcdir)/'`test-str.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str.Tpo $(DEPDIR)/test_lib-test-str.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str.c' object='test_lib-test-str.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str.o `test -f 'test-str.c' || echo '$(srcdir)/'`test-str.c + +test_lib-test-str.obj: test-str.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str.obj -MD -MP -MF $(DEPDIR)/test_lib-test-str.Tpo -c -o test_lib-test-str.obj `if test -f 'test-str.c'; then $(CYGPATH_W) 'test-str.c'; else $(CYGPATH_W) '$(srcdir)/test-str.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str.Tpo $(DEPDIR)/test_lib-test-str.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str.c' object='test_lib-test-str.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str.obj `if test -f 'test-str.c'; then $(CYGPATH_W) 'test-str.c'; else $(CYGPATH_W) '$(srcdir)/test-str.c'; fi` + +test_lib-test-strescape.o: test-strescape.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-strescape.o -MD -MP -MF $(DEPDIR)/test_lib-test-strescape.Tpo -c -o test_lib-test-strescape.o `test -f 'test-strescape.c' || echo '$(srcdir)/'`test-strescape.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-strescape.Tpo $(DEPDIR)/test_lib-test-strescape.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-strescape.c' object='test_lib-test-strescape.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-strescape.o `test -f 'test-strescape.c' || echo '$(srcdir)/'`test-strescape.c + +test_lib-test-strescape.obj: test-strescape.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-strescape.obj -MD -MP -MF $(DEPDIR)/test_lib-test-strescape.Tpo -c -o test_lib-test-strescape.obj `if test -f 'test-strescape.c'; then $(CYGPATH_W) 'test-strescape.c'; else $(CYGPATH_W) '$(srcdir)/test-strescape.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-strescape.Tpo $(DEPDIR)/test_lib-test-strescape.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-strescape.c' object='test_lib-test-strescape.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-strescape.obj `if test -f 'test-strescape.c'; then $(CYGPATH_W) 'test-strescape.c'; else $(CYGPATH_W) '$(srcdir)/test-strescape.c'; fi` + +test_lib-test-strfuncs.o: test-strfuncs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-strfuncs.o -MD -MP -MF $(DEPDIR)/test_lib-test-strfuncs.Tpo -c -o test_lib-test-strfuncs.o `test -f 'test-strfuncs.c' || echo '$(srcdir)/'`test-strfuncs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-strfuncs.Tpo $(DEPDIR)/test_lib-test-strfuncs.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-strfuncs.c' object='test_lib-test-strfuncs.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-strfuncs.o `test -f 'test-strfuncs.c' || echo '$(srcdir)/'`test-strfuncs.c + +test_lib-test-strfuncs.obj: test-strfuncs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-strfuncs.obj -MD -MP -MF $(DEPDIR)/test_lib-test-strfuncs.Tpo -c -o test_lib-test-strfuncs.obj `if test -f 'test-strfuncs.c'; then $(CYGPATH_W) 'test-strfuncs.c'; else $(CYGPATH_W) '$(srcdir)/test-strfuncs.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-strfuncs.Tpo $(DEPDIR)/test_lib-test-strfuncs.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-strfuncs.c' object='test_lib-test-strfuncs.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-strfuncs.obj `if test -f 'test-strfuncs.c'; then $(CYGPATH_W) 'test-strfuncs.c'; else $(CYGPATH_W) '$(srcdir)/test-strfuncs.c'; fi` + +test_lib-test-strnum.o: test-strnum.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-strnum.o -MD -MP -MF $(DEPDIR)/test_lib-test-strnum.Tpo -c -o test_lib-test-strnum.o `test -f 'test-strnum.c' || echo '$(srcdir)/'`test-strnum.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-strnum.Tpo $(DEPDIR)/test_lib-test-strnum.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-strnum.c' object='test_lib-test-strnum.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-strnum.o `test -f 'test-strnum.c' || echo '$(srcdir)/'`test-strnum.c + +test_lib-test-strnum.obj: test-strnum.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-strnum.obj -MD -MP -MF $(DEPDIR)/test_lib-test-strnum.Tpo -c -o test_lib-test-strnum.obj `if test -f 'test-strnum.c'; then $(CYGPATH_W) 'test-strnum.c'; else $(CYGPATH_W) '$(srcdir)/test-strnum.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-strnum.Tpo $(DEPDIR)/test_lib-test-strnum.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-strnum.c' object='test_lib-test-strnum.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-strnum.obj `if test -f 'test-strnum.c'; then $(CYGPATH_W) 'test-strnum.c'; else $(CYGPATH_W) '$(srcdir)/test-strnum.c'; fi` + +test_lib-test-str-find.o: test-str-find.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str-find.o -MD -MP -MF $(DEPDIR)/test_lib-test-str-find.Tpo -c -o test_lib-test-str-find.o `test -f 'test-str-find.c' || echo '$(srcdir)/'`test-str-find.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str-find.Tpo $(DEPDIR)/test_lib-test-str-find.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str-find.c' object='test_lib-test-str-find.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str-find.o `test -f 'test-str-find.c' || echo '$(srcdir)/'`test-str-find.c + +test_lib-test-str-find.obj: test-str-find.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str-find.obj -MD -MP -MF $(DEPDIR)/test_lib-test-str-find.Tpo -c -o test_lib-test-str-find.obj `if test -f 'test-str-find.c'; then $(CYGPATH_W) 'test-str-find.c'; else $(CYGPATH_W) '$(srcdir)/test-str-find.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str-find.Tpo $(DEPDIR)/test_lib-test-str-find.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str-find.c' object='test_lib-test-str-find.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str-find.obj `if test -f 'test-str-find.c'; then $(CYGPATH_W) 'test-str-find.c'; else $(CYGPATH_W) '$(srcdir)/test-str-find.c'; fi` + +test_lib-test-str-sanitize.o: test-str-sanitize.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str-sanitize.o -MD -MP -MF $(DEPDIR)/test_lib-test-str-sanitize.Tpo -c -o test_lib-test-str-sanitize.o `test -f 'test-str-sanitize.c' || echo '$(srcdir)/'`test-str-sanitize.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str-sanitize.Tpo $(DEPDIR)/test_lib-test-str-sanitize.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str-sanitize.c' object='test_lib-test-str-sanitize.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str-sanitize.o `test -f 'test-str-sanitize.c' || echo '$(srcdir)/'`test-str-sanitize.c + +test_lib-test-str-sanitize.obj: test-str-sanitize.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str-sanitize.obj -MD -MP -MF $(DEPDIR)/test_lib-test-str-sanitize.Tpo -c -o test_lib-test-str-sanitize.obj `if test -f 'test-str-sanitize.c'; then $(CYGPATH_W) 'test-str-sanitize.c'; else $(CYGPATH_W) '$(srcdir)/test-str-sanitize.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str-sanitize.Tpo $(DEPDIR)/test_lib-test-str-sanitize.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str-sanitize.c' object='test_lib-test-str-sanitize.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str-sanitize.obj `if test -f 'test-str-sanitize.c'; then $(CYGPATH_W) 'test-str-sanitize.c'; else $(CYGPATH_W) '$(srcdir)/test-str-sanitize.c'; fi` + +test_lib-test-str-table.o: test-str-table.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str-table.o -MD -MP -MF $(DEPDIR)/test_lib-test-str-table.Tpo -c -o test_lib-test-str-table.o `test -f 'test-str-table.c' || echo '$(srcdir)/'`test-str-table.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str-table.Tpo $(DEPDIR)/test_lib-test-str-table.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str-table.c' object='test_lib-test-str-table.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str-table.o `test -f 'test-str-table.c' || echo '$(srcdir)/'`test-str-table.c + +test_lib-test-str-table.obj: test-str-table.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-str-table.obj -MD -MP -MF $(DEPDIR)/test_lib-test-str-table.Tpo -c -o test_lib-test-str-table.obj `if test -f 'test-str-table.c'; then $(CYGPATH_W) 'test-str-table.c'; else $(CYGPATH_W) '$(srcdir)/test-str-table.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-str-table.Tpo $(DEPDIR)/test_lib-test-str-table.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-str-table.c' object='test_lib-test-str-table.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-str-table.obj `if test -f 'test-str-table.c'; then $(CYGPATH_W) 'test-str-table.c'; else $(CYGPATH_W) '$(srcdir)/test-str-table.c'; fi` + +test_lib-test-time-util.o: test-time-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-time-util.o -MD -MP -MF $(DEPDIR)/test_lib-test-time-util.Tpo -c -o test_lib-test-time-util.o `test -f 'test-time-util.c' || echo '$(srcdir)/'`test-time-util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-time-util.Tpo $(DEPDIR)/test_lib-test-time-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-time-util.c' object='test_lib-test-time-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-time-util.o `test -f 'test-time-util.c' || echo '$(srcdir)/'`test-time-util.c + +test_lib-test-time-util.obj: test-time-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-time-util.obj -MD -MP -MF $(DEPDIR)/test_lib-test-time-util.Tpo -c -o test_lib-test-time-util.obj `if test -f 'test-time-util.c'; then $(CYGPATH_W) 'test-time-util.c'; else $(CYGPATH_W) '$(srcdir)/test-time-util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-time-util.Tpo $(DEPDIR)/test_lib-test-time-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-time-util.c' object='test_lib-test-time-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-time-util.obj `if test -f 'test-time-util.c'; then $(CYGPATH_W) 'test-time-util.c'; else $(CYGPATH_W) '$(srcdir)/test-time-util.c'; fi` + +test_lib-test-unichar.o: test-unichar.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-unichar.o -MD -MP -MF $(DEPDIR)/test_lib-test-unichar.Tpo -c -o test_lib-test-unichar.o `test -f 'test-unichar.c' || echo '$(srcdir)/'`test-unichar.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-unichar.Tpo $(DEPDIR)/test_lib-test-unichar.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-unichar.c' object='test_lib-test-unichar.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-unichar.o `test -f 'test-unichar.c' || echo '$(srcdir)/'`test-unichar.c + +test_lib-test-unichar.obj: test-unichar.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-unichar.obj -MD -MP -MF $(DEPDIR)/test_lib-test-unichar.Tpo -c -o test_lib-test-unichar.obj `if test -f 'test-unichar.c'; then $(CYGPATH_W) 'test-unichar.c'; else $(CYGPATH_W) '$(srcdir)/test-unichar.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-unichar.Tpo $(DEPDIR)/test_lib-test-unichar.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-unichar.c' object='test_lib-test-unichar.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-unichar.obj `if test -f 'test-unichar.c'; then $(CYGPATH_W) 'test-unichar.c'; else $(CYGPATH_W) '$(srcdir)/test-unichar.c'; fi` + +test_lib-test-utc-mktime.o: test-utc-mktime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-utc-mktime.o -MD -MP -MF $(DEPDIR)/test_lib-test-utc-mktime.Tpo -c -o test_lib-test-utc-mktime.o `test -f 'test-utc-mktime.c' || echo '$(srcdir)/'`test-utc-mktime.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-utc-mktime.Tpo $(DEPDIR)/test_lib-test-utc-mktime.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-utc-mktime.c' object='test_lib-test-utc-mktime.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-utc-mktime.o `test -f 'test-utc-mktime.c' || echo '$(srcdir)/'`test-utc-mktime.c + +test_lib-test-utc-mktime.obj: test-utc-mktime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-utc-mktime.obj -MD -MP -MF $(DEPDIR)/test_lib-test-utc-mktime.Tpo -c -o test_lib-test-utc-mktime.obj `if test -f 'test-utc-mktime.c'; then $(CYGPATH_W) 'test-utc-mktime.c'; else $(CYGPATH_W) '$(srcdir)/test-utc-mktime.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-utc-mktime.Tpo $(DEPDIR)/test_lib-test-utc-mktime.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-utc-mktime.c' object='test_lib-test-utc-mktime.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-utc-mktime.obj `if test -f 'test-utc-mktime.c'; then $(CYGPATH_W) 'test-utc-mktime.c'; else $(CYGPATH_W) '$(srcdir)/test-utc-mktime.c'; fi` + +test_lib-test-uri.o: test-uri.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-uri.o -MD -MP -MF $(DEPDIR)/test_lib-test-uri.Tpo -c -o test_lib-test-uri.o `test -f 'test-uri.c' || echo '$(srcdir)/'`test-uri.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-uri.Tpo $(DEPDIR)/test_lib-test-uri.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-uri.c' object='test_lib-test-uri.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-uri.o `test -f 'test-uri.c' || echo '$(srcdir)/'`test-uri.c + +test_lib-test-uri.obj: test-uri.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-uri.obj -MD -MP -MF $(DEPDIR)/test_lib-test-uri.Tpo -c -o test_lib-test-uri.obj `if test -f 'test-uri.c'; then $(CYGPATH_W) 'test-uri.c'; else $(CYGPATH_W) '$(srcdir)/test-uri.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-uri.Tpo $(DEPDIR)/test_lib-test-uri.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-uri.c' object='test_lib-test-uri.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-uri.obj `if test -f 'test-uri.c'; then $(CYGPATH_W) 'test-uri.c'; else $(CYGPATH_W) '$(srcdir)/test-uri.c'; fi` + +test_lib-test-var-expand.o: test-var-expand.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-var-expand.o -MD -MP -MF $(DEPDIR)/test_lib-test-var-expand.Tpo -c -o test_lib-test-var-expand.o `test -f 'test-var-expand.c' || echo '$(srcdir)/'`test-var-expand.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-var-expand.Tpo $(DEPDIR)/test_lib-test-var-expand.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-var-expand.c' object='test_lib-test-var-expand.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-var-expand.o `test -f 'test-var-expand.c' || echo '$(srcdir)/'`test-var-expand.c + +test_lib-test-var-expand.obj: test-var-expand.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-var-expand.obj -MD -MP -MF $(DEPDIR)/test_lib-test-var-expand.Tpo -c -o test_lib-test-var-expand.obj `if test -f 'test-var-expand.c'; then $(CYGPATH_W) 'test-var-expand.c'; else $(CYGPATH_W) '$(srcdir)/test-var-expand.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-var-expand.Tpo $(DEPDIR)/test_lib-test-var-expand.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-var-expand.c' object='test_lib-test-var-expand.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-var-expand.obj `if test -f 'test-var-expand.c'; then $(CYGPATH_W) 'test-var-expand.c'; else $(CYGPATH_W) '$(srcdir)/test-var-expand.c'; fi` + +test_lib-test-wildcard-match.o: test-wildcard-match.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-wildcard-match.o -MD -MP -MF $(DEPDIR)/test_lib-test-wildcard-match.Tpo -c -o test_lib-test-wildcard-match.o `test -f 'test-wildcard-match.c' || echo '$(srcdir)/'`test-wildcard-match.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-wildcard-match.Tpo $(DEPDIR)/test_lib-test-wildcard-match.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-wildcard-match.c' object='test_lib-test-wildcard-match.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-wildcard-match.o `test -f 'test-wildcard-match.c' || echo '$(srcdir)/'`test-wildcard-match.c + +test_lib-test-wildcard-match.obj: test-wildcard-match.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lib-test-wildcard-match.obj -MD -MP -MF $(DEPDIR)/test_lib-test-wildcard-match.Tpo -c -o test_lib-test-wildcard-match.obj `if test -f 'test-wildcard-match.c'; then $(CYGPATH_W) 'test-wildcard-match.c'; else $(CYGPATH_W) '$(srcdir)/test-wildcard-match.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_lib-test-wildcard-match.Tpo $(DEPDIR)/test_lib-test-wildcard-match.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-wildcard-match.c' object='test_lib-test-wildcard-match.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lib-test-wildcard-match.obj `if test -f 'test-wildcard-match.c'; then $(CYGPATH_W) 'test-wildcard-match.c'; else $(CYGPATH_W) '$(srcdir)/test-wildcard-match.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ + done + +uninstall-pkginc_libHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-local +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -rm -f event-filter-lexer.c + -rm -f event-filter-parser.c + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/aqueue.Plo + -rm -f ./$(DEPDIR)/array.Plo + -rm -f ./$(DEPDIR)/askpass.Plo + -rm -f ./$(DEPDIR)/backtrace-string.Plo + -rm -f ./$(DEPDIR)/base32.Plo + -rm -f ./$(DEPDIR)/base64.Plo + -rm -f ./$(DEPDIR)/bits.Plo + -rm -f ./$(DEPDIR)/bsearch-insert-pos.Plo + -rm -f ./$(DEPDIR)/buffer-istream.Plo + -rm -f ./$(DEPDIR)/buffer.Plo + -rm -f ./$(DEPDIR)/child-wait.Plo + -rm -f ./$(DEPDIR)/compat.Plo + -rm -f ./$(DEPDIR)/connection.Plo + -rm -f ./$(DEPDIR)/cpu-limit.Plo + -rm -f ./$(DEPDIR)/crc32.Plo + -rm -f ./$(DEPDIR)/data-stack.Plo + -rm -f ./$(DEPDIR)/eacces-error.Plo + -rm -f ./$(DEPDIR)/env-util.Plo + -rm -f ./$(DEPDIR)/event-filter-lexer.Plo + -rm -f ./$(DEPDIR)/event-filter-parser.Plo + -rm -f ./$(DEPDIR)/event-filter.Plo + -rm -f ./$(DEPDIR)/event-log.Plo + -rm -f ./$(DEPDIR)/execv-const.Plo + -rm -f ./$(DEPDIR)/failures.Plo + -rm -f ./$(DEPDIR)/fd-util.Plo + -rm -f ./$(DEPDIR)/fdatasync-path.Plo + -rm -f ./$(DEPDIR)/fdpass.Plo + -rm -f ./$(DEPDIR)/file-cache.Plo + -rm -f ./$(DEPDIR)/file-copy.Plo + -rm -f ./$(DEPDIR)/file-create-locked.Plo + -rm -f ./$(DEPDIR)/file-dotlock.Plo + -rm -f ./$(DEPDIR)/file-lock.Plo + -rm -f ./$(DEPDIR)/file-set-size.Plo + -rm -f ./$(DEPDIR)/guid.Plo + -rm -f ./$(DEPDIR)/hash-format.Plo + -rm -f ./$(DEPDIR)/hash-method.Plo + -rm -f ./$(DEPDIR)/hash.Plo + -rm -f ./$(DEPDIR)/hash2.Plo + -rm -f ./$(DEPDIR)/hex-binary.Plo + -rm -f ./$(DEPDIR)/hex-dec.Plo + -rm -f ./$(DEPDIR)/hmac-cram-md5.Plo + -rm -f ./$(DEPDIR)/hmac.Plo + -rm -f ./$(DEPDIR)/home-expand.Plo + -rm -f ./$(DEPDIR)/hook-build.Plo + -rm -f ./$(DEPDIR)/hostpid.Plo + -rm -f ./$(DEPDIR)/imem.Plo + -rm -f ./$(DEPDIR)/ioloop-epoll.Plo + -rm -f ./$(DEPDIR)/ioloop-iolist.Plo + -rm -f ./$(DEPDIR)/ioloop-kqueue.Plo + -rm -f ./$(DEPDIR)/ioloop-notify-fd.Plo + -rm -f ./$(DEPDIR)/ioloop-notify-inotify.Plo + -rm -f ./$(DEPDIR)/ioloop-notify-kqueue.Plo + -rm -f ./$(DEPDIR)/ioloop-notify-none.Plo + -rm -f ./$(DEPDIR)/ioloop-poll.Plo + -rm -f ./$(DEPDIR)/ioloop-select.Plo + -rm -f ./$(DEPDIR)/ioloop.Plo + -rm -f ./$(DEPDIR)/iostream-proxy.Plo + -rm -f ./$(DEPDIR)/iostream-pump.Plo + -rm -f ./$(DEPDIR)/iostream-rawlog.Plo + -rm -f ./$(DEPDIR)/iostream-temp.Plo + -rm -f ./$(DEPDIR)/iostream.Plo + -rm -f ./$(DEPDIR)/ipwd.Plo + -rm -f ./$(DEPDIR)/iso8601-date.Plo + -rm -f ./$(DEPDIR)/istream-base64-decoder.Plo + -rm -f ./$(DEPDIR)/istream-base64-encoder.Plo + -rm -f ./$(DEPDIR)/istream-callback.Plo + -rm -f ./$(DEPDIR)/istream-chain.Plo + -rm -f ./$(DEPDIR)/istream-concat.Plo + -rm -f ./$(DEPDIR)/istream-crlf.Plo + -rm -f ./$(DEPDIR)/istream-data.Plo + -rm -f ./$(DEPDIR)/istream-failure-at.Plo + -rm -f ./$(DEPDIR)/istream-file.Plo + -rm -f ./$(DEPDIR)/istream-hash.Plo + -rm -f ./$(DEPDIR)/istream-jsonstr.Plo + -rm -f ./$(DEPDIR)/istream-limit.Plo + -rm -f ./$(DEPDIR)/istream-multiplex.Plo + -rm -f ./$(DEPDIR)/istream-rawlog.Plo + -rm -f ./$(DEPDIR)/istream-seekable.Plo + -rm -f ./$(DEPDIR)/istream-sized.Plo + -rm -f ./$(DEPDIR)/istream-tee.Plo + -rm -f ./$(DEPDIR)/istream-timeout.Plo + -rm -f ./$(DEPDIR)/istream-try.Plo + -rm -f ./$(DEPDIR)/istream-unix.Plo + -rm -f ./$(DEPDIR)/istream.Plo + -rm -f ./$(DEPDIR)/json-parser.Plo + -rm -f ./$(DEPDIR)/json-tree.Plo + -rm -f ./$(DEPDIR)/lib-event.Plo + -rm -f ./$(DEPDIR)/lib-signals.Plo + -rm -f ./$(DEPDIR)/lib.Plo + -rm -f ./$(DEPDIR)/log-throttle.Plo + -rm -f ./$(DEPDIR)/md4.Plo + -rm -f ./$(DEPDIR)/md5.Plo + -rm -f ./$(DEPDIR)/memarea.Plo + -rm -f ./$(DEPDIR)/mempool-allocfree.Plo + -rm -f ./$(DEPDIR)/mempool-alloconly.Plo + -rm -f ./$(DEPDIR)/mempool-datastack.Plo + -rm -f ./$(DEPDIR)/mempool-system.Plo + -rm -f ./$(DEPDIR)/mempool-unsafe-datastack.Plo + -rm -f ./$(DEPDIR)/mempool.Plo + -rm -f ./$(DEPDIR)/mkdir-parents.Plo + -rm -f ./$(DEPDIR)/mmap-anon.Plo + -rm -f ./$(DEPDIR)/mmap-util.Plo + -rm -f ./$(DEPDIR)/module-dir.Plo + -rm -f ./$(DEPDIR)/mountpoint.Plo + -rm -f ./$(DEPDIR)/net.Plo + -rm -f ./$(DEPDIR)/nfs-workarounds.Plo + -rm -f ./$(DEPDIR)/numpack.Plo + -rm -f ./$(DEPDIR)/ostream-buffer.Plo + -rm -f ./$(DEPDIR)/ostream-failure-at.Plo + -rm -f ./$(DEPDIR)/ostream-file.Plo + -rm -f ./$(DEPDIR)/ostream-hash.Plo + -rm -f ./$(DEPDIR)/ostream-multiplex.Plo + -rm -f ./$(DEPDIR)/ostream-null.Plo + -rm -f ./$(DEPDIR)/ostream-rawlog.Plo + -rm -f ./$(DEPDIR)/ostream-unix.Plo + -rm -f ./$(DEPDIR)/ostream-wrapper.Plo + -rm -f ./$(DEPDIR)/ostream.Plo + -rm -f ./$(DEPDIR)/path-util.Plo + -rm -f ./$(DEPDIR)/pkcs5.Plo + -rm -f ./$(DEPDIR)/primes.Plo + -rm -f ./$(DEPDIR)/printf-format-fix.Plo + -rm -f ./$(DEPDIR)/priorityq.Plo + -rm -f ./$(DEPDIR)/process-stat.Plo + -rm -f ./$(DEPDIR)/process-title.Plo + -rm -f ./$(DEPDIR)/rand.Plo + -rm -f ./$(DEPDIR)/randgen.Plo + -rm -f ./$(DEPDIR)/read-full.Plo + -rm -f ./$(DEPDIR)/restrict-access.Plo + -rm -f ./$(DEPDIR)/restrict-process-size.Plo + -rm -f ./$(DEPDIR)/safe-memset.Plo + -rm -f ./$(DEPDIR)/safe-mkdir.Plo + -rm -f ./$(DEPDIR)/safe-mkstemp.Plo + -rm -f ./$(DEPDIR)/sendfile-util.Plo + -rm -f ./$(DEPDIR)/seq-range-array.Plo + -rm -f ./$(DEPDIR)/seq-set-builder.Plo + -rm -f ./$(DEPDIR)/sha1.Plo + -rm -f ./$(DEPDIR)/sha2.Plo + -rm -f ./$(DEPDIR)/sha3.Plo + -rm -f ./$(DEPDIR)/sleep.Plo + -rm -f ./$(DEPDIR)/sort.Plo + -rm -f ./$(DEPDIR)/stats-dist.Plo + -rm -f ./$(DEPDIR)/str-find.Plo + -rm -f ./$(DEPDIR)/str-sanitize.Plo + -rm -f ./$(DEPDIR)/str-table.Plo + -rm -f ./$(DEPDIR)/str.Plo + -rm -f ./$(DEPDIR)/strescape.Plo + -rm -f ./$(DEPDIR)/strfuncs.Plo + -rm -f ./$(DEPDIR)/strnum.Plo + -rm -f ./$(DEPDIR)/test_lib-test-aqueue.Po + -rm -f ./$(DEPDIR)/test_lib-test-array.Po + -rm -f ./$(DEPDIR)/test_lib-test-backtrace.Po + -rm -f ./$(DEPDIR)/test_lib-test-base32.Po + -rm -f ./$(DEPDIR)/test_lib-test-base64.Po + -rm -f ./$(DEPDIR)/test_lib-test-bits.Po + -rm -f ./$(DEPDIR)/test_lib-test-bsearch-insert-pos.Po + -rm -f ./$(DEPDIR)/test_lib-test-buffer-istream.Po + -rm -f ./$(DEPDIR)/test_lib-test-buffer.Po + -rm -f ./$(DEPDIR)/test_lib-test-byteorder.Po + -rm -f ./$(DEPDIR)/test_lib-test-connection.Po + -rm -f ./$(DEPDIR)/test_lib-test-cpu-limit.Po + -rm -f ./$(DEPDIR)/test_lib-test-crc32.Po + -rm -f ./$(DEPDIR)/test_lib-test-data-stack.Po + -rm -f ./$(DEPDIR)/test_lib-test-env-util.Po + -rm -f ./$(DEPDIR)/test_lib-test-event-category-register.Po + -rm -f ./$(DEPDIR)/test_lib-test-event-filter-expr.Po + -rm -f ./$(DEPDIR)/test_lib-test-event-filter-merge.Po + -rm -f ./$(DEPDIR)/test_lib-test-event-filter-parser.Po + -rm -f ./$(DEPDIR)/test_lib-test-event-filter.Po + -rm -f ./$(DEPDIR)/test_lib-test-event-flatten.Po + -rm -f ./$(DEPDIR)/test_lib-test-event-log.Po + -rm -f ./$(DEPDIR)/test_lib-test-failures.Po + -rm -f ./$(DEPDIR)/test_lib-test-fd-util.Po + -rm -f ./$(DEPDIR)/test_lib-test-file-cache.Po + -rm -f ./$(DEPDIR)/test_lib-test-file-create-locked.Po + -rm -f ./$(DEPDIR)/test_lib-test-guid.Po + -rm -f ./$(DEPDIR)/test_lib-test-hash-format.Po + -rm -f ./$(DEPDIR)/test_lib-test-hash-method.Po + -rm -f ./$(DEPDIR)/test_lib-test-hash.Po + -rm -f ./$(DEPDIR)/test_lib-test-hex-binary.Po + -rm -f ./$(DEPDIR)/test_lib-test-hmac.Po + -rm -f ./$(DEPDIR)/test_lib-test-imem.Po + -rm -f ./$(DEPDIR)/test_lib-test-ioloop.Po + -rm -f ./$(DEPDIR)/test_lib-test-iostream-proxy.Po + -rm -f ./$(DEPDIR)/test_lib-test-iostream-pump.Po + -rm -f ./$(DEPDIR)/test_lib-test-iostream-temp.Po + -rm -f ./$(DEPDIR)/test_lib-test-iso8601-date.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-base64-decoder.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-base64-encoder.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-chain.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-concat.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-crlf.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-failure-at.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-jsonstr.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-multiplex.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-seekable.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-sized.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-tee.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-try.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-unix.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream.Po + -rm -f ./$(DEPDIR)/test_lib-test-json-parser.Po + -rm -f ./$(DEPDIR)/test_lib-test-json-tree.Po + -rm -f ./$(DEPDIR)/test_lib-test-lib-event.Po + -rm -f ./$(DEPDIR)/test_lib-test-lib-signals.Po + -rm -f ./$(DEPDIR)/test_lib-test-lib.Po + -rm -f ./$(DEPDIR)/test_lib-test-llist.Po + -rm -f ./$(DEPDIR)/test_lib-test-log-throttle.Po + -rm -f ./$(DEPDIR)/test_lib-test-macros.Po + -rm -f ./$(DEPDIR)/test_lib-test-malloc-overflow.Po + -rm -f ./$(DEPDIR)/test_lib-test-memarea.Po + -rm -f ./$(DEPDIR)/test_lib-test-mempool-allocfree.Po + -rm -f ./$(DEPDIR)/test_lib-test-mempool-alloconly.Po + -rm -f ./$(DEPDIR)/test_lib-test-mempool.Po + -rm -f ./$(DEPDIR)/test_lib-test-multiplex.Po + -rm -f ./$(DEPDIR)/test_lib-test-net.Po + -rm -f ./$(DEPDIR)/test_lib-test-numpack.Po + -rm -f ./$(DEPDIR)/test_lib-test-ostream-buffer.Po + -rm -f ./$(DEPDIR)/test_lib-test-ostream-failure-at.Po + -rm -f ./$(DEPDIR)/test_lib-test-ostream-file.Po + -rm -f ./$(DEPDIR)/test_lib-test-ostream-multiplex.Po + -rm -f ./$(DEPDIR)/test_lib-test-path-util.Po + -rm -f ./$(DEPDIR)/test_lib-test-pkcs5.Po + -rm -f ./$(DEPDIR)/test_lib-test-primes.Po + -rm -f ./$(DEPDIR)/test_lib-test-printf-format-fix.Po + -rm -f ./$(DEPDIR)/test_lib-test-priorityq.Po + -rm -f ./$(DEPDIR)/test_lib-test-random.Po + -rm -f ./$(DEPDIR)/test_lib-test-seq-range-array.Po + -rm -f ./$(DEPDIR)/test_lib-test-seq-set-builder.Po + -rm -f ./$(DEPDIR)/test_lib-test-stats-dist.Po + -rm -f ./$(DEPDIR)/test_lib-test-str-find.Po + -rm -f ./$(DEPDIR)/test_lib-test-str-sanitize.Po + -rm -f ./$(DEPDIR)/test_lib-test-str-table.Po + -rm -f ./$(DEPDIR)/test_lib-test-str.Po + -rm -f ./$(DEPDIR)/test_lib-test-strescape.Po + -rm -f ./$(DEPDIR)/test_lib-test-strfuncs.Po + -rm -f ./$(DEPDIR)/test_lib-test-strnum.Po + -rm -f ./$(DEPDIR)/test_lib-test-time-util.Po + -rm -f ./$(DEPDIR)/test_lib-test-unichar.Po + -rm -f ./$(DEPDIR)/test_lib-test-uri.Po + -rm -f ./$(DEPDIR)/test_lib-test-utc-mktime.Po + -rm -f ./$(DEPDIR)/test_lib-test-var-expand.Po + -rm -f ./$(DEPDIR)/test_lib-test-wildcard-match.Po + -rm -f ./$(DEPDIR)/time-util.Plo + -rm -f ./$(DEPDIR)/unichar.Plo + -rm -f ./$(DEPDIR)/unix-socket-create.Plo + -rm -f ./$(DEPDIR)/unlink-directory.Plo + -rm -f ./$(DEPDIR)/unlink-old-files.Plo + -rm -f ./$(DEPDIR)/uri-util.Plo + -rm -f ./$(DEPDIR)/utc-mktime.Plo + -rm -f ./$(DEPDIR)/utc-offset.Plo + -rm -f ./$(DEPDIR)/var-expand-if.Plo + -rm -f ./$(DEPDIR)/var-expand.Plo + -rm -f ./$(DEPDIR)/wildcard-match.Plo + -rm -f ./$(DEPDIR)/write-full.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkginc_libHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/aqueue.Plo + -rm -f ./$(DEPDIR)/array.Plo + -rm -f ./$(DEPDIR)/askpass.Plo + -rm -f ./$(DEPDIR)/backtrace-string.Plo + -rm -f ./$(DEPDIR)/base32.Plo + -rm -f ./$(DEPDIR)/base64.Plo + -rm -f ./$(DEPDIR)/bits.Plo + -rm -f ./$(DEPDIR)/bsearch-insert-pos.Plo + -rm -f ./$(DEPDIR)/buffer-istream.Plo + -rm -f ./$(DEPDIR)/buffer.Plo + -rm -f ./$(DEPDIR)/child-wait.Plo + -rm -f ./$(DEPDIR)/compat.Plo + -rm -f ./$(DEPDIR)/connection.Plo + -rm -f ./$(DEPDIR)/cpu-limit.Plo + -rm -f ./$(DEPDIR)/crc32.Plo + -rm -f ./$(DEPDIR)/data-stack.Plo + -rm -f ./$(DEPDIR)/eacces-error.Plo + -rm -f ./$(DEPDIR)/env-util.Plo + -rm -f ./$(DEPDIR)/event-filter-lexer.Plo + -rm -f ./$(DEPDIR)/event-filter-parser.Plo + -rm -f ./$(DEPDIR)/event-filter.Plo + -rm -f ./$(DEPDIR)/event-log.Plo + -rm -f ./$(DEPDIR)/execv-const.Plo + -rm -f ./$(DEPDIR)/failures.Plo + -rm -f ./$(DEPDIR)/fd-util.Plo + -rm -f ./$(DEPDIR)/fdatasync-path.Plo + -rm -f ./$(DEPDIR)/fdpass.Plo + -rm -f ./$(DEPDIR)/file-cache.Plo + -rm -f ./$(DEPDIR)/file-copy.Plo + -rm -f ./$(DEPDIR)/file-create-locked.Plo + -rm -f ./$(DEPDIR)/file-dotlock.Plo + -rm -f ./$(DEPDIR)/file-lock.Plo + -rm -f ./$(DEPDIR)/file-set-size.Plo + -rm -f ./$(DEPDIR)/guid.Plo + -rm -f ./$(DEPDIR)/hash-format.Plo + -rm -f ./$(DEPDIR)/hash-method.Plo + -rm -f ./$(DEPDIR)/hash.Plo + -rm -f ./$(DEPDIR)/hash2.Plo + -rm -f ./$(DEPDIR)/hex-binary.Plo + -rm -f ./$(DEPDIR)/hex-dec.Plo + -rm -f ./$(DEPDIR)/hmac-cram-md5.Plo + -rm -f ./$(DEPDIR)/hmac.Plo + -rm -f ./$(DEPDIR)/home-expand.Plo + -rm -f ./$(DEPDIR)/hook-build.Plo + -rm -f ./$(DEPDIR)/hostpid.Plo + -rm -f ./$(DEPDIR)/imem.Plo + -rm -f ./$(DEPDIR)/ioloop-epoll.Plo + -rm -f ./$(DEPDIR)/ioloop-iolist.Plo + -rm -f ./$(DEPDIR)/ioloop-kqueue.Plo + -rm -f ./$(DEPDIR)/ioloop-notify-fd.Plo + -rm -f ./$(DEPDIR)/ioloop-notify-inotify.Plo + -rm -f ./$(DEPDIR)/ioloop-notify-kqueue.Plo + -rm -f ./$(DEPDIR)/ioloop-notify-none.Plo + -rm -f ./$(DEPDIR)/ioloop-poll.Plo + -rm -f ./$(DEPDIR)/ioloop-select.Plo + -rm -f ./$(DEPDIR)/ioloop.Plo + -rm -f ./$(DEPDIR)/iostream-proxy.Plo + -rm -f ./$(DEPDIR)/iostream-pump.Plo + -rm -f ./$(DEPDIR)/iostream-rawlog.Plo + -rm -f ./$(DEPDIR)/iostream-temp.Plo + -rm -f ./$(DEPDIR)/iostream.Plo + -rm -f ./$(DEPDIR)/ipwd.Plo + -rm -f ./$(DEPDIR)/iso8601-date.Plo + -rm -f ./$(DEPDIR)/istream-base64-decoder.Plo + -rm -f ./$(DEPDIR)/istream-base64-encoder.Plo + -rm -f ./$(DEPDIR)/istream-callback.Plo + -rm -f ./$(DEPDIR)/istream-chain.Plo + -rm -f ./$(DEPDIR)/istream-concat.Plo + -rm -f ./$(DEPDIR)/istream-crlf.Plo + -rm -f ./$(DEPDIR)/istream-data.Plo + -rm -f ./$(DEPDIR)/istream-failure-at.Plo + -rm -f ./$(DEPDIR)/istream-file.Plo + -rm -f ./$(DEPDIR)/istream-hash.Plo + -rm -f ./$(DEPDIR)/istream-jsonstr.Plo + -rm -f ./$(DEPDIR)/istream-limit.Plo + -rm -f ./$(DEPDIR)/istream-multiplex.Plo + -rm -f ./$(DEPDIR)/istream-rawlog.Plo + -rm -f ./$(DEPDIR)/istream-seekable.Plo + -rm -f ./$(DEPDIR)/istream-sized.Plo + -rm -f ./$(DEPDIR)/istream-tee.Plo + -rm -f ./$(DEPDIR)/istream-timeout.Plo + -rm -f ./$(DEPDIR)/istream-try.Plo + -rm -f ./$(DEPDIR)/istream-unix.Plo + -rm -f ./$(DEPDIR)/istream.Plo + -rm -f ./$(DEPDIR)/json-parser.Plo + -rm -f ./$(DEPDIR)/json-tree.Plo + -rm -f ./$(DEPDIR)/lib-event.Plo + -rm -f ./$(DEPDIR)/lib-signals.Plo + -rm -f ./$(DEPDIR)/lib.Plo + -rm -f ./$(DEPDIR)/log-throttle.Plo + -rm -f ./$(DEPDIR)/md4.Plo + -rm -f ./$(DEPDIR)/md5.Plo + -rm -f ./$(DEPDIR)/memarea.Plo + -rm -f ./$(DEPDIR)/mempool-allocfree.Plo + -rm -f ./$(DEPDIR)/mempool-alloconly.Plo + -rm -f ./$(DEPDIR)/mempool-datastack.Plo + -rm -f ./$(DEPDIR)/mempool-system.Plo + -rm -f ./$(DEPDIR)/mempool-unsafe-datastack.Plo + -rm -f ./$(DEPDIR)/mempool.Plo + -rm -f ./$(DEPDIR)/mkdir-parents.Plo + -rm -f ./$(DEPDIR)/mmap-anon.Plo + -rm -f ./$(DEPDIR)/mmap-util.Plo + -rm -f ./$(DEPDIR)/module-dir.Plo + -rm -f ./$(DEPDIR)/mountpoint.Plo + -rm -f ./$(DEPDIR)/net.Plo + -rm -f ./$(DEPDIR)/nfs-workarounds.Plo + -rm -f ./$(DEPDIR)/numpack.Plo + -rm -f ./$(DEPDIR)/ostream-buffer.Plo + -rm -f ./$(DEPDIR)/ostream-failure-at.Plo + -rm -f ./$(DEPDIR)/ostream-file.Plo + -rm -f ./$(DEPDIR)/ostream-hash.Plo + -rm -f ./$(DEPDIR)/ostream-multiplex.Plo + -rm -f ./$(DEPDIR)/ostream-null.Plo + -rm -f ./$(DEPDIR)/ostream-rawlog.Plo + -rm -f ./$(DEPDIR)/ostream-unix.Plo + -rm -f ./$(DEPDIR)/ostream-wrapper.Plo + -rm -f ./$(DEPDIR)/ostream.Plo + -rm -f ./$(DEPDIR)/path-util.Plo + -rm -f ./$(DEPDIR)/pkcs5.Plo + -rm -f ./$(DEPDIR)/primes.Plo + -rm -f ./$(DEPDIR)/printf-format-fix.Plo + -rm -f ./$(DEPDIR)/priorityq.Plo + -rm -f ./$(DEPDIR)/process-stat.Plo + -rm -f ./$(DEPDIR)/process-title.Plo + -rm -f ./$(DEPDIR)/rand.Plo + -rm -f ./$(DEPDIR)/randgen.Plo + -rm -f ./$(DEPDIR)/read-full.Plo + -rm -f ./$(DEPDIR)/restrict-access.Plo + -rm -f ./$(DEPDIR)/restrict-process-size.Plo + -rm -f ./$(DEPDIR)/safe-memset.Plo + -rm -f ./$(DEPDIR)/safe-mkdir.Plo + -rm -f ./$(DEPDIR)/safe-mkstemp.Plo + -rm -f ./$(DEPDIR)/sendfile-util.Plo + -rm -f ./$(DEPDIR)/seq-range-array.Plo + -rm -f ./$(DEPDIR)/seq-set-builder.Plo + -rm -f ./$(DEPDIR)/sha1.Plo + -rm -f ./$(DEPDIR)/sha2.Plo + -rm -f ./$(DEPDIR)/sha3.Plo + -rm -f ./$(DEPDIR)/sleep.Plo + -rm -f ./$(DEPDIR)/sort.Plo + -rm -f ./$(DEPDIR)/stats-dist.Plo + -rm -f ./$(DEPDIR)/str-find.Plo + -rm -f ./$(DEPDIR)/str-sanitize.Plo + -rm -f ./$(DEPDIR)/str-table.Plo + -rm -f ./$(DEPDIR)/str.Plo + -rm -f ./$(DEPDIR)/strescape.Plo + -rm -f ./$(DEPDIR)/strfuncs.Plo + -rm -f ./$(DEPDIR)/strnum.Plo + -rm -f ./$(DEPDIR)/test_lib-test-aqueue.Po + -rm -f ./$(DEPDIR)/test_lib-test-array.Po + -rm -f ./$(DEPDIR)/test_lib-test-backtrace.Po + -rm -f ./$(DEPDIR)/test_lib-test-base32.Po + -rm -f ./$(DEPDIR)/test_lib-test-base64.Po + -rm -f ./$(DEPDIR)/test_lib-test-bits.Po + -rm -f ./$(DEPDIR)/test_lib-test-bsearch-insert-pos.Po + -rm -f ./$(DEPDIR)/test_lib-test-buffer-istream.Po + -rm -f ./$(DEPDIR)/test_lib-test-buffer.Po + -rm -f ./$(DEPDIR)/test_lib-test-byteorder.Po + -rm -f ./$(DEPDIR)/test_lib-test-connection.Po + -rm -f ./$(DEPDIR)/test_lib-test-cpu-limit.Po + -rm -f ./$(DEPDIR)/test_lib-test-crc32.Po + -rm -f ./$(DEPDIR)/test_lib-test-data-stack.Po + -rm -f ./$(DEPDIR)/test_lib-test-env-util.Po + -rm -f ./$(DEPDIR)/test_lib-test-event-category-register.Po + -rm -f ./$(DEPDIR)/test_lib-test-event-filter-expr.Po + -rm -f ./$(DEPDIR)/test_lib-test-event-filter-merge.Po + -rm -f ./$(DEPDIR)/test_lib-test-event-filter-parser.Po + -rm -f ./$(DEPDIR)/test_lib-test-event-filter.Po + -rm -f ./$(DEPDIR)/test_lib-test-event-flatten.Po + -rm -f ./$(DEPDIR)/test_lib-test-event-log.Po + -rm -f ./$(DEPDIR)/test_lib-test-failures.Po + -rm -f ./$(DEPDIR)/test_lib-test-fd-util.Po + -rm -f ./$(DEPDIR)/test_lib-test-file-cache.Po + -rm -f ./$(DEPDIR)/test_lib-test-file-create-locked.Po + -rm -f ./$(DEPDIR)/test_lib-test-guid.Po + -rm -f ./$(DEPDIR)/test_lib-test-hash-format.Po + -rm -f ./$(DEPDIR)/test_lib-test-hash-method.Po + -rm -f ./$(DEPDIR)/test_lib-test-hash.Po + -rm -f ./$(DEPDIR)/test_lib-test-hex-binary.Po + -rm -f ./$(DEPDIR)/test_lib-test-hmac.Po + -rm -f ./$(DEPDIR)/test_lib-test-imem.Po + -rm -f ./$(DEPDIR)/test_lib-test-ioloop.Po + -rm -f ./$(DEPDIR)/test_lib-test-iostream-proxy.Po + -rm -f ./$(DEPDIR)/test_lib-test-iostream-pump.Po + -rm -f ./$(DEPDIR)/test_lib-test-iostream-temp.Po + -rm -f ./$(DEPDIR)/test_lib-test-iso8601-date.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-base64-decoder.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-base64-encoder.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-chain.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-concat.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-crlf.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-failure-at.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-jsonstr.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-multiplex.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-seekable.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-sized.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-tee.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-try.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream-unix.Po + -rm -f ./$(DEPDIR)/test_lib-test-istream.Po + -rm -f ./$(DEPDIR)/test_lib-test-json-parser.Po + -rm -f ./$(DEPDIR)/test_lib-test-json-tree.Po + -rm -f ./$(DEPDIR)/test_lib-test-lib-event.Po + -rm -f ./$(DEPDIR)/test_lib-test-lib-signals.Po + -rm -f ./$(DEPDIR)/test_lib-test-lib.Po + -rm -f ./$(DEPDIR)/test_lib-test-llist.Po + -rm -f ./$(DEPDIR)/test_lib-test-log-throttle.Po + -rm -f ./$(DEPDIR)/test_lib-test-macros.Po + -rm -f ./$(DEPDIR)/test_lib-test-malloc-overflow.Po + -rm -f ./$(DEPDIR)/test_lib-test-memarea.Po + -rm -f ./$(DEPDIR)/test_lib-test-mempool-allocfree.Po + -rm -f ./$(DEPDIR)/test_lib-test-mempool-alloconly.Po + -rm -f ./$(DEPDIR)/test_lib-test-mempool.Po + -rm -f ./$(DEPDIR)/test_lib-test-multiplex.Po + -rm -f ./$(DEPDIR)/test_lib-test-net.Po + -rm -f ./$(DEPDIR)/test_lib-test-numpack.Po + -rm -f ./$(DEPDIR)/test_lib-test-ostream-buffer.Po + -rm -f ./$(DEPDIR)/test_lib-test-ostream-failure-at.Po + -rm -f ./$(DEPDIR)/test_lib-test-ostream-file.Po + -rm -f ./$(DEPDIR)/test_lib-test-ostream-multiplex.Po + -rm -f ./$(DEPDIR)/test_lib-test-path-util.Po + -rm -f ./$(DEPDIR)/test_lib-test-pkcs5.Po + -rm -f ./$(DEPDIR)/test_lib-test-primes.Po + -rm -f ./$(DEPDIR)/test_lib-test-printf-format-fix.Po + -rm -f ./$(DEPDIR)/test_lib-test-priorityq.Po + -rm -f ./$(DEPDIR)/test_lib-test-random.Po + -rm -f ./$(DEPDIR)/test_lib-test-seq-range-array.Po + -rm -f ./$(DEPDIR)/test_lib-test-seq-set-builder.Po + -rm -f ./$(DEPDIR)/test_lib-test-stats-dist.Po + -rm -f ./$(DEPDIR)/test_lib-test-str-find.Po + -rm -f ./$(DEPDIR)/test_lib-test-str-sanitize.Po + -rm -f ./$(DEPDIR)/test_lib-test-str-table.Po + -rm -f ./$(DEPDIR)/test_lib-test-str.Po + -rm -f ./$(DEPDIR)/test_lib-test-strescape.Po + -rm -f ./$(DEPDIR)/test_lib-test-strfuncs.Po + -rm -f ./$(DEPDIR)/test_lib-test-strnum.Po + -rm -f ./$(DEPDIR)/test_lib-test-time-util.Po + -rm -f ./$(DEPDIR)/test_lib-test-unichar.Po + -rm -f ./$(DEPDIR)/test_lib-test-uri.Po + -rm -f ./$(DEPDIR)/test_lib-test-utc-mktime.Po + -rm -f ./$(DEPDIR)/test_lib-test-var-expand.Po + -rm -f ./$(DEPDIR)/test_lib-test-wildcard-match.Po + -rm -f ./$(DEPDIR)/time-util.Plo + -rm -f ./$(DEPDIR)/unichar.Plo + -rm -f ./$(DEPDIR)/unix-socket-create.Plo + -rm -f ./$(DEPDIR)/unlink-directory.Plo + -rm -f ./$(DEPDIR)/unlink-old-files.Plo + -rm -f ./$(DEPDIR)/uri-util.Plo + -rm -f ./$(DEPDIR)/utc-mktime.Plo + -rm -f ./$(DEPDIR)/utc-offset.Plo + -rm -f ./$(DEPDIR)/var-expand-if.Plo + -rm -f ./$(DEPDIR)/var-expand.Plo + -rm -f ./$(DEPDIR)/wildcard-match.Plo + -rm -f ./$(DEPDIR)/write-full.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkginc_libHEADERS + +.MAKE: all check check-am install install-am install-exec \ + install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am \ + check-local clean clean-generic clean-libtool \ + clean-noinstLTLIBRARIES clean-noinstPROGRAMS cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-pkginc_libHEADERS install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-pkginc_libHEADERS + +.PRECIOUS: Makefile + + +# We use custom rules here because we want to use flex and bison instead +# of lex and yacc (or bison in yacc-compatibility mode). Both flex and +# bison can handle properly naming the generated files, and it is simpler +# and cleaner to make this rule ourselves instead of working around ylwrap +# and yywrap's antiquated notion of what is hapenning. +.l.c: + $(AM_V_GEN)$(FLEX) -o $@ $< + +.y.c: + $(AM_V_GEN)$(BISON) -o $@ $< + +# Bison generates both a header and a .c file. Without the following +# dependency, anything including the header will race the bison process. +event-filter-parser.h: event-filter-parser.c + +$(srcdir)/UnicodeData.txt: + test -f $@ || wget -O $@ https://dovecot.org/res/UnicodeData.txt + +$(srcdir)/unicodemap.c: $(srcdir)/unicodemap.pl $(srcdir)/UnicodeData.txt + perl $(srcdir)/unicodemap.pl < $(srcdir)/UnicodeData.txt > $@ + +check-local: + for bin in $(test_programs); do \ + if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ + done + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/lib/UnicodeData.txt b/src/lib/UnicodeData.txt new file mode 100644 index 0000000..a756976 --- /dev/null +++ b/src/lib/UnicodeData.txt @@ -0,0 +1,30592 @@ +0000;<control>;Cc;0;BN;;;;;N;NULL;;;; +0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; +0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; +0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; +0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; +0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; +0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; +0007;<control>;Cc;0;BN;;;;;N;BELL;;;; +0008;<control>;Cc;0;BN;;;;;N;BACKSPACE;;;; +0009;<control>;Cc;0;S;;;;;N;CHARACTER TABULATION;;;; +000A;<control>;Cc;0;B;;;;;N;LINE FEED (LF);;;; +000B;<control>;Cc;0;S;;;;;N;LINE TABULATION;;;; +000C;<control>;Cc;0;WS;;;;;N;FORM FEED (FF);;;; +000D;<control>;Cc;0;B;;;;;N;CARRIAGE RETURN (CR);;;; +000E;<control>;Cc;0;BN;;;;;N;SHIFT OUT;;;; +000F;<control>;Cc;0;BN;;;;;N;SHIFT IN;;;; +0010;<control>;Cc;0;BN;;;;;N;DATA LINK ESCAPE;;;; +0011;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL ONE;;;; +0012;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL TWO;;;; +0013;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL THREE;;;; +0014;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL FOUR;;;; +0015;<control>;Cc;0;BN;;;;;N;NEGATIVE ACKNOWLEDGE;;;; +0016;<control>;Cc;0;BN;;;;;N;SYNCHRONOUS IDLE;;;; +0017;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION BLOCK;;;; +0018;<control>;Cc;0;BN;;;;;N;CANCEL;;;; +0019;<control>;Cc;0;BN;;;;;N;END OF MEDIUM;;;; +001A;<control>;Cc;0;BN;;;;;N;SUBSTITUTE;;;; +001B;<control>;Cc;0;BN;;;;;N;ESCAPE;;;; +001C;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR FOUR;;;; +001D;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR THREE;;;; +001E;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR TWO;;;; +001F;<control>;Cc;0;S;;;;;N;INFORMATION SEPARATOR ONE;;;; +0020;SPACE;Zs;0;WS;;;;;N;;;;; +0021;EXCLAMATION MARK;Po;0;ON;;;;;N;;;;; +0022;QUOTATION MARK;Po;0;ON;;;;;N;;;;; +0023;NUMBER SIGN;Po;0;ET;;;;;N;;;;; +0024;DOLLAR SIGN;Sc;0;ET;;;;;N;;;;; +0025;PERCENT SIGN;Po;0;ET;;;;;N;;;;; +0026;AMPERSAND;Po;0;ON;;;;;N;;;;; +0027;APOSTROPHE;Po;0;ON;;;;;N;APOSTROPHE-QUOTE;;;; +0028;LEFT PARENTHESIS;Ps;0;ON;;;;;Y;OPENING PARENTHESIS;;;; +0029;RIGHT PARENTHESIS;Pe;0;ON;;;;;Y;CLOSING PARENTHESIS;;;; +002A;ASTERISK;Po;0;ON;;;;;N;;;;; +002B;PLUS SIGN;Sm;0;ES;;;;;N;;;;; +002C;COMMA;Po;0;CS;;;;;N;;;;; +002D;HYPHEN-MINUS;Pd;0;ES;;;;;N;;;;; +002E;FULL STOP;Po;0;CS;;;;;N;PERIOD;;;; +002F;SOLIDUS;Po;0;CS;;;;;N;SLASH;;;; +0030;DIGIT ZERO;Nd;0;EN;;0;0;0;N;;;;; +0031;DIGIT ONE;Nd;0;EN;;1;1;1;N;;;;; +0032;DIGIT TWO;Nd;0;EN;;2;2;2;N;;;;; +0033;DIGIT THREE;Nd;0;EN;;3;3;3;N;;;;; +0034;DIGIT FOUR;Nd;0;EN;;4;4;4;N;;;;; +0035;DIGIT FIVE;Nd;0;EN;;5;5;5;N;;;;; +0036;DIGIT SIX;Nd;0;EN;;6;6;6;N;;;;; +0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;; +0038;DIGIT EIGHT;Nd;0;EN;;8;8;8;N;;;;; +0039;DIGIT NINE;Nd;0;EN;;9;9;9;N;;;;; +003A;COLON;Po;0;CS;;;;;N;;;;; +003B;SEMICOLON;Po;0;ON;;;;;N;;;;; +003C;LESS-THAN SIGN;Sm;0;ON;;;;;Y;;;;; +003D;EQUALS SIGN;Sm;0;ON;;;;;N;;;;; +003E;GREATER-THAN SIGN;Sm;0;ON;;;;;Y;;;;; +003F;QUESTION MARK;Po;0;ON;;;;;N;;;;; +0040;COMMERCIAL AT;Po;0;ON;;;;;N;;;;; +0041;LATIN CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0061; +0042;LATIN CAPITAL LETTER B;Lu;0;L;;;;;N;;;;0062; +0043;LATIN CAPITAL LETTER C;Lu;0;L;;;;;N;;;;0063; +0044;LATIN CAPITAL LETTER D;Lu;0;L;;;;;N;;;;0064; +0045;LATIN CAPITAL LETTER E;Lu;0;L;;;;;N;;;;0065; +0046;LATIN CAPITAL LETTER F;Lu;0;L;;;;;N;;;;0066; +0047;LATIN CAPITAL LETTER G;Lu;0;L;;;;;N;;;;0067; +0048;LATIN CAPITAL LETTER H;Lu;0;L;;;;;N;;;;0068; +0049;LATIN CAPITAL LETTER I;Lu;0;L;;;;;N;;;;0069; +004A;LATIN CAPITAL LETTER J;Lu;0;L;;;;;N;;;;006A; +004B;LATIN CAPITAL LETTER K;Lu;0;L;;;;;N;;;;006B; +004C;LATIN CAPITAL LETTER L;Lu;0;L;;;;;N;;;;006C; +004D;LATIN CAPITAL LETTER M;Lu;0;L;;;;;N;;;;006D; +004E;LATIN CAPITAL LETTER N;Lu;0;L;;;;;N;;;;006E; +004F;LATIN CAPITAL LETTER O;Lu;0;L;;;;;N;;;;006F; +0050;LATIN CAPITAL LETTER P;Lu;0;L;;;;;N;;;;0070; +0051;LATIN CAPITAL LETTER Q;Lu;0;L;;;;;N;;;;0071; +0052;LATIN CAPITAL LETTER R;Lu;0;L;;;;;N;;;;0072; +0053;LATIN CAPITAL LETTER S;Lu;0;L;;;;;N;;;;0073; +0054;LATIN CAPITAL LETTER T;Lu;0;L;;;;;N;;;;0074; +0055;LATIN CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0075; +0056;LATIN CAPITAL LETTER V;Lu;0;L;;;;;N;;;;0076; +0057;LATIN CAPITAL LETTER W;Lu;0;L;;;;;N;;;;0077; +0058;LATIN CAPITAL LETTER X;Lu;0;L;;;;;N;;;;0078; +0059;LATIN CAPITAL LETTER Y;Lu;0;L;;;;;N;;;;0079; +005A;LATIN CAPITAL LETTER Z;Lu;0;L;;;;;N;;;;007A; +005B;LEFT SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING SQUARE BRACKET;;;; +005C;REVERSE SOLIDUS;Po;0;ON;;;;;N;BACKSLASH;;;; +005D;RIGHT SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING SQUARE BRACKET;;;; +005E;CIRCUMFLEX ACCENT;Sk;0;ON;;;;;N;SPACING CIRCUMFLEX;;;; +005F;LOW LINE;Pc;0;ON;;;;;N;SPACING UNDERSCORE;;;; +0060;GRAVE ACCENT;Sk;0;ON;;;;;N;SPACING GRAVE;;;; +0061;LATIN SMALL LETTER A;Ll;0;L;;;;;N;;;0041;;0041 +0062;LATIN SMALL LETTER B;Ll;0;L;;;;;N;;;0042;;0042 +0063;LATIN SMALL LETTER C;Ll;0;L;;;;;N;;;0043;;0043 +0064;LATIN SMALL LETTER D;Ll;0;L;;;;;N;;;0044;;0044 +0065;LATIN SMALL LETTER E;Ll;0;L;;;;;N;;;0045;;0045 +0066;LATIN SMALL LETTER F;Ll;0;L;;;;;N;;;0046;;0046 +0067;LATIN SMALL LETTER G;Ll;0;L;;;;;N;;;0047;;0047 +0068;LATIN SMALL LETTER H;Ll;0;L;;;;;N;;;0048;;0048 +0069;LATIN SMALL LETTER I;Ll;0;L;;;;;N;;;0049;;0049 +006A;LATIN SMALL LETTER J;Ll;0;L;;;;;N;;;004A;;004A +006B;LATIN SMALL LETTER K;Ll;0;L;;;;;N;;;004B;;004B +006C;LATIN SMALL LETTER L;Ll;0;L;;;;;N;;;004C;;004C +006D;LATIN SMALL LETTER M;Ll;0;L;;;;;N;;;004D;;004D +006E;LATIN SMALL LETTER N;Ll;0;L;;;;;N;;;004E;;004E +006F;LATIN SMALL LETTER O;Ll;0;L;;;;;N;;;004F;;004F +0070;LATIN SMALL LETTER P;Ll;0;L;;;;;N;;;0050;;0050 +0071;LATIN SMALL LETTER Q;Ll;0;L;;;;;N;;;0051;;0051 +0072;LATIN SMALL LETTER R;Ll;0;L;;;;;N;;;0052;;0052 +0073;LATIN SMALL LETTER S;Ll;0;L;;;;;N;;;0053;;0053 +0074;LATIN SMALL LETTER T;Ll;0;L;;;;;N;;;0054;;0054 +0075;LATIN SMALL LETTER U;Ll;0;L;;;;;N;;;0055;;0055 +0076;LATIN SMALL LETTER V;Ll;0;L;;;;;N;;;0056;;0056 +0077;LATIN SMALL LETTER W;Ll;0;L;;;;;N;;;0057;;0057 +0078;LATIN SMALL LETTER X;Ll;0;L;;;;;N;;;0058;;0058 +0079;LATIN SMALL LETTER Y;Ll;0;L;;;;;N;;;0059;;0059 +007A;LATIN SMALL LETTER Z;Ll;0;L;;;;;N;;;005A;;005A +007B;LEFT CURLY BRACKET;Ps;0;ON;;;;;Y;OPENING CURLY BRACKET;;;; +007C;VERTICAL LINE;Sm;0;ON;;;;;N;VERTICAL BAR;;;; +007D;RIGHT CURLY BRACKET;Pe;0;ON;;;;;Y;CLOSING CURLY BRACKET;;;; +007E;TILDE;Sm;0;ON;;;;;N;;;;; +007F;<control>;Cc;0;BN;;;;;N;DELETE;;;; +0080;<control>;Cc;0;BN;;;;;N;;;;; +0081;<control>;Cc;0;BN;;;;;N;;;;; +0082;<control>;Cc;0;BN;;;;;N;BREAK PERMITTED HERE;;;; +0083;<control>;Cc;0;BN;;;;;N;NO BREAK HERE;;;; +0084;<control>;Cc;0;BN;;;;;N;;;;; +0085;<control>;Cc;0;B;;;;;N;NEXT LINE (NEL);;;; +0086;<control>;Cc;0;BN;;;;;N;START OF SELECTED AREA;;;; +0087;<control>;Cc;0;BN;;;;;N;END OF SELECTED AREA;;;; +0088;<control>;Cc;0;BN;;;;;N;CHARACTER TABULATION SET;;;; +0089;<control>;Cc;0;BN;;;;;N;CHARACTER TABULATION WITH JUSTIFICATION;;;; +008A;<control>;Cc;0;BN;;;;;N;LINE TABULATION SET;;;; +008B;<control>;Cc;0;BN;;;;;N;PARTIAL LINE FORWARD;;;; +008C;<control>;Cc;0;BN;;;;;N;PARTIAL LINE BACKWARD;;;; +008D;<control>;Cc;0;BN;;;;;N;REVERSE LINE FEED;;;; +008E;<control>;Cc;0;BN;;;;;N;SINGLE SHIFT TWO;;;; +008F;<control>;Cc;0;BN;;;;;N;SINGLE SHIFT THREE;;;; +0090;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL STRING;;;; +0091;<control>;Cc;0;BN;;;;;N;PRIVATE USE ONE;;;; +0092;<control>;Cc;0;BN;;;;;N;PRIVATE USE TWO;;;; +0093;<control>;Cc;0;BN;;;;;N;SET TRANSMIT STATE;;;; +0094;<control>;Cc;0;BN;;;;;N;CANCEL CHARACTER;;;; +0095;<control>;Cc;0;BN;;;;;N;MESSAGE WAITING;;;; +0096;<control>;Cc;0;BN;;;;;N;START OF GUARDED AREA;;;; +0097;<control>;Cc;0;BN;;;;;N;END OF GUARDED AREA;;;; +0098;<control>;Cc;0;BN;;;;;N;START OF STRING;;;; +0099;<control>;Cc;0;BN;;;;;N;;;;; +009A;<control>;Cc;0;BN;;;;;N;SINGLE CHARACTER INTRODUCER;;;; +009B;<control>;Cc;0;BN;;;;;N;CONTROL SEQUENCE INTRODUCER;;;; +009C;<control>;Cc;0;BN;;;;;N;STRING TERMINATOR;;;; +009D;<control>;Cc;0;BN;;;;;N;OPERATING SYSTEM COMMAND;;;; +009E;<control>;Cc;0;BN;;;;;N;PRIVACY MESSAGE;;;; +009F;<control>;Cc;0;BN;;;;;N;APPLICATION PROGRAM COMMAND;;;; +00A0;NO-BREAK SPACE;Zs;0;CS;<noBreak> 0020;;;;N;NON-BREAKING SPACE;;;; +00A1;INVERTED EXCLAMATION MARK;Po;0;ON;;;;;N;;;;; +00A2;CENT SIGN;Sc;0;ET;;;;;N;;;;; +00A3;POUND SIGN;Sc;0;ET;;;;;N;;;;; +00A4;CURRENCY SIGN;Sc;0;ET;;;;;N;;;;; +00A5;YEN SIGN;Sc;0;ET;;;;;N;;;;; +00A6;BROKEN BAR;So;0;ON;;;;;N;BROKEN VERTICAL BAR;;;; +00A7;SECTION SIGN;Po;0;ON;;;;;N;;;;; +00A8;DIAERESIS;Sk;0;ON;<compat> 0020 0308;;;;N;SPACING DIAERESIS;;;; +00A9;COPYRIGHT SIGN;So;0;ON;;;;;N;;;;; +00AA;FEMININE ORDINAL INDICATOR;Lo;0;L;<super> 0061;;;;N;;;;; +00AB;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING GUILLEMET;;;; +00AC;NOT SIGN;Sm;0;ON;;;;;N;;;;; +00AD;SOFT HYPHEN;Cf;0;BN;;;;;N;;;;; +00AE;REGISTERED SIGN;So;0;ON;;;;;N;REGISTERED TRADE MARK SIGN;;;; +00AF;MACRON;Sk;0;ON;<compat> 0020 0304;;;;N;SPACING MACRON;;;; +00B0;DEGREE SIGN;So;0;ET;;;;;N;;;;; +00B1;PLUS-MINUS SIGN;Sm;0;ET;;;;;N;PLUS-OR-MINUS SIGN;;;; +00B2;SUPERSCRIPT TWO;No;0;EN;<super> 0032;;2;2;N;SUPERSCRIPT DIGIT TWO;;;; +00B3;SUPERSCRIPT THREE;No;0;EN;<super> 0033;;3;3;N;SUPERSCRIPT DIGIT THREE;;;; +00B4;ACUTE ACCENT;Sk;0;ON;<compat> 0020 0301;;;;N;SPACING ACUTE;;;; +00B5;MICRO SIGN;Ll;0;L;<compat> 03BC;;;;N;;;039C;;039C +00B6;PILCROW SIGN;Po;0;ON;;;;;N;PARAGRAPH SIGN;;;; +00B7;MIDDLE DOT;Po;0;ON;;;;;N;;;;; +00B8;CEDILLA;Sk;0;ON;<compat> 0020 0327;;;;N;SPACING CEDILLA;;;; +00B9;SUPERSCRIPT ONE;No;0;EN;<super> 0031;;1;1;N;SUPERSCRIPT DIGIT ONE;;;; +00BA;MASCULINE ORDINAL INDICATOR;Lo;0;L;<super> 006F;;;;N;;;;; +00BB;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING GUILLEMET;;;; +00BC;VULGAR FRACTION ONE QUARTER;No;0;ON;<fraction> 0031 2044 0034;;;1/4;N;FRACTION ONE QUARTER;;;; +00BD;VULGAR FRACTION ONE HALF;No;0;ON;<fraction> 0031 2044 0032;;;1/2;N;FRACTION ONE HALF;;;; +00BE;VULGAR FRACTION THREE QUARTERS;No;0;ON;<fraction> 0033 2044 0034;;;3/4;N;FRACTION THREE QUARTERS;;;; +00BF;INVERTED QUESTION MARK;Po;0;ON;;;;;N;;;;; +00C0;LATIN CAPITAL LETTER A WITH GRAVE;Lu;0;L;0041 0300;;;;N;LATIN CAPITAL LETTER A GRAVE;;;00E0; +00C1;LATIN CAPITAL LETTER A WITH ACUTE;Lu;0;L;0041 0301;;;;N;LATIN CAPITAL LETTER A ACUTE;;;00E1; +00C2;LATIN CAPITAL LETTER A WITH CIRCUMFLEX;Lu;0;L;0041 0302;;;;N;LATIN CAPITAL LETTER A CIRCUMFLEX;;;00E2; +00C3;LATIN CAPITAL LETTER A WITH TILDE;Lu;0;L;0041 0303;;;;N;LATIN CAPITAL LETTER A TILDE;;;00E3; +00C4;LATIN CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0041 0308;;;;N;LATIN CAPITAL LETTER A DIAERESIS;;;00E4; +00C5;LATIN CAPITAL LETTER A WITH RING ABOVE;Lu;0;L;0041 030A;;;;N;LATIN CAPITAL LETTER A RING;;;00E5; +00C6;LATIN CAPITAL LETTER AE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER A E;;;00E6; +00C7;LATIN CAPITAL LETTER C WITH CEDILLA;Lu;0;L;0043 0327;;;;N;LATIN CAPITAL LETTER C CEDILLA;;;00E7; +00C8;LATIN CAPITAL LETTER E WITH GRAVE;Lu;0;L;0045 0300;;;;N;LATIN CAPITAL LETTER E GRAVE;;;00E8; +00C9;LATIN CAPITAL LETTER E WITH ACUTE;Lu;0;L;0045 0301;;;;N;LATIN CAPITAL LETTER E ACUTE;;;00E9; +00CA;LATIN CAPITAL LETTER E WITH CIRCUMFLEX;Lu;0;L;0045 0302;;;;N;LATIN CAPITAL LETTER E CIRCUMFLEX;;;00EA; +00CB;LATIN CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;0045 0308;;;;N;LATIN CAPITAL LETTER E DIAERESIS;;;00EB; +00CC;LATIN CAPITAL LETTER I WITH GRAVE;Lu;0;L;0049 0300;;;;N;LATIN CAPITAL LETTER I GRAVE;;;00EC; +00CD;LATIN CAPITAL LETTER I WITH ACUTE;Lu;0;L;0049 0301;;;;N;LATIN CAPITAL LETTER I ACUTE;;;00ED; +00CE;LATIN CAPITAL LETTER I WITH CIRCUMFLEX;Lu;0;L;0049 0302;;;;N;LATIN CAPITAL LETTER I CIRCUMFLEX;;;00EE; +00CF;LATIN CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0049 0308;;;;N;LATIN CAPITAL LETTER I DIAERESIS;;;00EF; +00D0;LATIN CAPITAL LETTER ETH;Lu;0;L;;;;;N;;;;00F0; +00D1;LATIN CAPITAL LETTER N WITH TILDE;Lu;0;L;004E 0303;;;;N;LATIN CAPITAL LETTER N TILDE;;;00F1; +00D2;LATIN CAPITAL LETTER O WITH GRAVE;Lu;0;L;004F 0300;;;;N;LATIN CAPITAL LETTER O GRAVE;;;00F2; +00D3;LATIN CAPITAL LETTER O WITH ACUTE;Lu;0;L;004F 0301;;;;N;LATIN CAPITAL LETTER O ACUTE;;;00F3; +00D4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX;Lu;0;L;004F 0302;;;;N;LATIN CAPITAL LETTER O CIRCUMFLEX;;;00F4; +00D5;LATIN CAPITAL LETTER O WITH TILDE;Lu;0;L;004F 0303;;;;N;LATIN CAPITAL LETTER O TILDE;;;00F5; +00D6;LATIN CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;004F 0308;;;;N;LATIN CAPITAL LETTER O DIAERESIS;;;00F6; +00D7;MULTIPLICATION SIGN;Sm;0;ON;;;;;N;;;;; +00D8;LATIN CAPITAL LETTER O WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O SLASH;;;00F8; +00D9;LATIN CAPITAL LETTER U WITH GRAVE;Lu;0;L;0055 0300;;;;N;LATIN CAPITAL LETTER U GRAVE;;;00F9; +00DA;LATIN CAPITAL LETTER U WITH ACUTE;Lu;0;L;0055 0301;;;;N;LATIN CAPITAL LETTER U ACUTE;;;00FA; +00DB;LATIN CAPITAL LETTER U WITH CIRCUMFLEX;Lu;0;L;0055 0302;;;;N;LATIN CAPITAL LETTER U CIRCUMFLEX;;;00FB; +00DC;LATIN CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0055 0308;;;;N;LATIN CAPITAL LETTER U DIAERESIS;;;00FC; +00DD;LATIN CAPITAL LETTER Y WITH ACUTE;Lu;0;L;0059 0301;;;;N;LATIN CAPITAL LETTER Y ACUTE;;;00FD; +00DE;LATIN CAPITAL LETTER THORN;Lu;0;L;;;;;N;;;;00FE; +00DF;LATIN SMALL LETTER SHARP S;Ll;0;L;;;;;N;;;;; +00E0;LATIN SMALL LETTER A WITH GRAVE;Ll;0;L;0061 0300;;;;N;LATIN SMALL LETTER A GRAVE;;00C0;;00C0 +00E1;LATIN SMALL LETTER A WITH ACUTE;Ll;0;L;0061 0301;;;;N;LATIN SMALL LETTER A ACUTE;;00C1;;00C1 +00E2;LATIN SMALL LETTER A WITH CIRCUMFLEX;Ll;0;L;0061 0302;;;;N;LATIN SMALL LETTER A CIRCUMFLEX;;00C2;;00C2 +00E3;LATIN SMALL LETTER A WITH TILDE;Ll;0;L;0061 0303;;;;N;LATIN SMALL LETTER A TILDE;;00C3;;00C3 +00E4;LATIN SMALL LETTER A WITH DIAERESIS;Ll;0;L;0061 0308;;;;N;LATIN SMALL LETTER A DIAERESIS;;00C4;;00C4 +00E5;LATIN SMALL LETTER A WITH RING ABOVE;Ll;0;L;0061 030A;;;;N;LATIN SMALL LETTER A RING;;00C5;;00C5 +00E6;LATIN SMALL LETTER AE;Ll;0;L;;;;;N;LATIN SMALL LETTER A E;;00C6;;00C6 +00E7;LATIN SMALL LETTER C WITH CEDILLA;Ll;0;L;0063 0327;;;;N;LATIN SMALL LETTER C CEDILLA;;00C7;;00C7 +00E8;LATIN SMALL LETTER E WITH GRAVE;Ll;0;L;0065 0300;;;;N;LATIN SMALL LETTER E GRAVE;;00C8;;00C8 +00E9;LATIN SMALL LETTER E WITH ACUTE;Ll;0;L;0065 0301;;;;N;LATIN SMALL LETTER E ACUTE;;00C9;;00C9 +00EA;LATIN SMALL LETTER E WITH CIRCUMFLEX;Ll;0;L;0065 0302;;;;N;LATIN SMALL LETTER E CIRCUMFLEX;;00CA;;00CA +00EB;LATIN SMALL LETTER E WITH DIAERESIS;Ll;0;L;0065 0308;;;;N;LATIN SMALL LETTER E DIAERESIS;;00CB;;00CB +00EC;LATIN SMALL LETTER I WITH GRAVE;Ll;0;L;0069 0300;;;;N;LATIN SMALL LETTER I GRAVE;;00CC;;00CC +00ED;LATIN SMALL LETTER I WITH ACUTE;Ll;0;L;0069 0301;;;;N;LATIN SMALL LETTER I ACUTE;;00CD;;00CD +00EE;LATIN SMALL LETTER I WITH CIRCUMFLEX;Ll;0;L;0069 0302;;;;N;LATIN SMALL LETTER I CIRCUMFLEX;;00CE;;00CE +00EF;LATIN SMALL LETTER I WITH DIAERESIS;Ll;0;L;0069 0308;;;;N;LATIN SMALL LETTER I DIAERESIS;;00CF;;00CF +00F0;LATIN SMALL LETTER ETH;Ll;0;L;;;;;N;;;00D0;;00D0 +00F1;LATIN SMALL LETTER N WITH TILDE;Ll;0;L;006E 0303;;;;N;LATIN SMALL LETTER N TILDE;;00D1;;00D1 +00F2;LATIN SMALL LETTER O WITH GRAVE;Ll;0;L;006F 0300;;;;N;LATIN SMALL LETTER O GRAVE;;00D2;;00D2 +00F3;LATIN SMALL LETTER O WITH ACUTE;Ll;0;L;006F 0301;;;;N;LATIN SMALL LETTER O ACUTE;;00D3;;00D3 +00F4;LATIN SMALL LETTER O WITH CIRCUMFLEX;Ll;0;L;006F 0302;;;;N;LATIN SMALL LETTER O CIRCUMFLEX;;00D4;;00D4 +00F5;LATIN SMALL LETTER O WITH TILDE;Ll;0;L;006F 0303;;;;N;LATIN SMALL LETTER O TILDE;;00D5;;00D5 +00F6;LATIN SMALL LETTER O WITH DIAERESIS;Ll;0;L;006F 0308;;;;N;LATIN SMALL LETTER O DIAERESIS;;00D6;;00D6 +00F7;DIVISION SIGN;Sm;0;ON;;;;;N;;;;; +00F8;LATIN SMALL LETTER O WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER O SLASH;;00D8;;00D8 +00F9;LATIN SMALL LETTER U WITH GRAVE;Ll;0;L;0075 0300;;;;N;LATIN SMALL LETTER U GRAVE;;00D9;;00D9 +00FA;LATIN SMALL LETTER U WITH ACUTE;Ll;0;L;0075 0301;;;;N;LATIN SMALL LETTER U ACUTE;;00DA;;00DA +00FB;LATIN SMALL LETTER U WITH CIRCUMFLEX;Ll;0;L;0075 0302;;;;N;LATIN SMALL LETTER U CIRCUMFLEX;;00DB;;00DB +00FC;LATIN SMALL LETTER U WITH DIAERESIS;Ll;0;L;0075 0308;;;;N;LATIN SMALL LETTER U DIAERESIS;;00DC;;00DC +00FD;LATIN SMALL LETTER Y WITH ACUTE;Ll;0;L;0079 0301;;;;N;LATIN SMALL LETTER Y ACUTE;;00DD;;00DD +00FE;LATIN SMALL LETTER THORN;Ll;0;L;;;;;N;;;00DE;;00DE +00FF;LATIN SMALL LETTER Y WITH DIAERESIS;Ll;0;L;0079 0308;;;;N;LATIN SMALL LETTER Y DIAERESIS;;0178;;0178 +0100;LATIN CAPITAL LETTER A WITH MACRON;Lu;0;L;0041 0304;;;;N;LATIN CAPITAL LETTER A MACRON;;;0101; +0101;LATIN SMALL LETTER A WITH MACRON;Ll;0;L;0061 0304;;;;N;LATIN SMALL LETTER A MACRON;;0100;;0100 +0102;LATIN CAPITAL LETTER A WITH BREVE;Lu;0;L;0041 0306;;;;N;LATIN CAPITAL LETTER A BREVE;;;0103; +0103;LATIN SMALL LETTER A WITH BREVE;Ll;0;L;0061 0306;;;;N;LATIN SMALL LETTER A BREVE;;0102;;0102 +0104;LATIN CAPITAL LETTER A WITH OGONEK;Lu;0;L;0041 0328;;;;N;LATIN CAPITAL LETTER A OGONEK;;;0105; +0105;LATIN SMALL LETTER A WITH OGONEK;Ll;0;L;0061 0328;;;;N;LATIN SMALL LETTER A OGONEK;;0104;;0104 +0106;LATIN CAPITAL LETTER C WITH ACUTE;Lu;0;L;0043 0301;;;;N;LATIN CAPITAL LETTER C ACUTE;;;0107; +0107;LATIN SMALL LETTER C WITH ACUTE;Ll;0;L;0063 0301;;;;N;LATIN SMALL LETTER C ACUTE;;0106;;0106 +0108;LATIN CAPITAL LETTER C WITH CIRCUMFLEX;Lu;0;L;0043 0302;;;;N;LATIN CAPITAL LETTER C CIRCUMFLEX;;;0109; +0109;LATIN SMALL LETTER C WITH CIRCUMFLEX;Ll;0;L;0063 0302;;;;N;LATIN SMALL LETTER C CIRCUMFLEX;;0108;;0108 +010A;LATIN CAPITAL LETTER C WITH DOT ABOVE;Lu;0;L;0043 0307;;;;N;LATIN CAPITAL LETTER C DOT;;;010B; +010B;LATIN SMALL LETTER C WITH DOT ABOVE;Ll;0;L;0063 0307;;;;N;LATIN SMALL LETTER C DOT;;010A;;010A +010C;LATIN CAPITAL LETTER C WITH CARON;Lu;0;L;0043 030C;;;;N;LATIN CAPITAL LETTER C HACEK;;;010D; +010D;LATIN SMALL LETTER C WITH CARON;Ll;0;L;0063 030C;;;;N;LATIN SMALL LETTER C HACEK;;010C;;010C +010E;LATIN CAPITAL LETTER D WITH CARON;Lu;0;L;0044 030C;;;;N;LATIN CAPITAL LETTER D HACEK;;;010F; +010F;LATIN SMALL LETTER D WITH CARON;Ll;0;L;0064 030C;;;;N;LATIN SMALL LETTER D HACEK;;010E;;010E +0110;LATIN CAPITAL LETTER D WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D BAR;;;0111; +0111;LATIN SMALL LETTER D WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER D BAR;;0110;;0110 +0112;LATIN CAPITAL LETTER E WITH MACRON;Lu;0;L;0045 0304;;;;N;LATIN CAPITAL LETTER E MACRON;;;0113; +0113;LATIN SMALL LETTER E WITH MACRON;Ll;0;L;0065 0304;;;;N;LATIN SMALL LETTER E MACRON;;0112;;0112 +0114;LATIN CAPITAL LETTER E WITH BREVE;Lu;0;L;0045 0306;;;;N;LATIN CAPITAL LETTER E BREVE;;;0115; +0115;LATIN SMALL LETTER E WITH BREVE;Ll;0;L;0065 0306;;;;N;LATIN SMALL LETTER E BREVE;;0114;;0114 +0116;LATIN CAPITAL LETTER E WITH DOT ABOVE;Lu;0;L;0045 0307;;;;N;LATIN CAPITAL LETTER E DOT;;;0117; +0117;LATIN SMALL LETTER E WITH DOT ABOVE;Ll;0;L;0065 0307;;;;N;LATIN SMALL LETTER E DOT;;0116;;0116 +0118;LATIN CAPITAL LETTER E WITH OGONEK;Lu;0;L;0045 0328;;;;N;LATIN CAPITAL LETTER E OGONEK;;;0119; +0119;LATIN SMALL LETTER E WITH OGONEK;Ll;0;L;0065 0328;;;;N;LATIN SMALL LETTER E OGONEK;;0118;;0118 +011A;LATIN CAPITAL LETTER E WITH CARON;Lu;0;L;0045 030C;;;;N;LATIN CAPITAL LETTER E HACEK;;;011B; +011B;LATIN SMALL LETTER E WITH CARON;Ll;0;L;0065 030C;;;;N;LATIN SMALL LETTER E HACEK;;011A;;011A +011C;LATIN CAPITAL LETTER G WITH CIRCUMFLEX;Lu;0;L;0047 0302;;;;N;LATIN CAPITAL LETTER G CIRCUMFLEX;;;011D; +011D;LATIN SMALL LETTER G WITH CIRCUMFLEX;Ll;0;L;0067 0302;;;;N;LATIN SMALL LETTER G CIRCUMFLEX;;011C;;011C +011E;LATIN CAPITAL LETTER G WITH BREVE;Lu;0;L;0047 0306;;;;N;LATIN CAPITAL LETTER G BREVE;;;011F; +011F;LATIN SMALL LETTER G WITH BREVE;Ll;0;L;0067 0306;;;;N;LATIN SMALL LETTER G BREVE;;011E;;011E +0120;LATIN CAPITAL LETTER G WITH DOT ABOVE;Lu;0;L;0047 0307;;;;N;LATIN CAPITAL LETTER G DOT;;;0121; +0121;LATIN SMALL LETTER G WITH DOT ABOVE;Ll;0;L;0067 0307;;;;N;LATIN SMALL LETTER G DOT;;0120;;0120 +0122;LATIN CAPITAL LETTER G WITH CEDILLA;Lu;0;L;0047 0327;;;;N;LATIN CAPITAL LETTER G CEDILLA;;;0123; +0123;LATIN SMALL LETTER G WITH CEDILLA;Ll;0;L;0067 0327;;;;N;LATIN SMALL LETTER G CEDILLA;;0122;;0122 +0124;LATIN CAPITAL LETTER H WITH CIRCUMFLEX;Lu;0;L;0048 0302;;;;N;LATIN CAPITAL LETTER H CIRCUMFLEX;;;0125; +0125;LATIN SMALL LETTER H WITH CIRCUMFLEX;Ll;0;L;0068 0302;;;;N;LATIN SMALL LETTER H CIRCUMFLEX;;0124;;0124 +0126;LATIN CAPITAL LETTER H WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER H BAR;;;0127; +0127;LATIN SMALL LETTER H WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER H BAR;;0126;;0126 +0128;LATIN CAPITAL LETTER I WITH TILDE;Lu;0;L;0049 0303;;;;N;LATIN CAPITAL LETTER I TILDE;;;0129; +0129;LATIN SMALL LETTER I WITH TILDE;Ll;0;L;0069 0303;;;;N;LATIN SMALL LETTER I TILDE;;0128;;0128 +012A;LATIN CAPITAL LETTER I WITH MACRON;Lu;0;L;0049 0304;;;;N;LATIN CAPITAL LETTER I MACRON;;;012B; +012B;LATIN SMALL LETTER I WITH MACRON;Ll;0;L;0069 0304;;;;N;LATIN SMALL LETTER I MACRON;;012A;;012A +012C;LATIN CAPITAL LETTER I WITH BREVE;Lu;0;L;0049 0306;;;;N;LATIN CAPITAL LETTER I BREVE;;;012D; +012D;LATIN SMALL LETTER I WITH BREVE;Ll;0;L;0069 0306;;;;N;LATIN SMALL LETTER I BREVE;;012C;;012C +012E;LATIN CAPITAL LETTER I WITH OGONEK;Lu;0;L;0049 0328;;;;N;LATIN CAPITAL LETTER I OGONEK;;;012F; +012F;LATIN SMALL LETTER I WITH OGONEK;Ll;0;L;0069 0328;;;;N;LATIN SMALL LETTER I OGONEK;;012E;;012E +0130;LATIN CAPITAL LETTER I WITH DOT ABOVE;Lu;0;L;0049 0307;;;;N;LATIN CAPITAL LETTER I DOT;;;0069; +0131;LATIN SMALL LETTER DOTLESS I;Ll;0;L;;;;;N;;;0049;;0049 +0132;LATIN CAPITAL LIGATURE IJ;Lu;0;L;<compat> 0049 004A;;;;N;LATIN CAPITAL LETTER I J;;;0133; +0133;LATIN SMALL LIGATURE IJ;Ll;0;L;<compat> 0069 006A;;;;N;LATIN SMALL LETTER I J;;0132;;0132 +0134;LATIN CAPITAL LETTER J WITH CIRCUMFLEX;Lu;0;L;004A 0302;;;;N;LATIN CAPITAL LETTER J CIRCUMFLEX;;;0135; +0135;LATIN SMALL LETTER J WITH CIRCUMFLEX;Ll;0;L;006A 0302;;;;N;LATIN SMALL LETTER J CIRCUMFLEX;;0134;;0134 +0136;LATIN CAPITAL LETTER K WITH CEDILLA;Lu;0;L;004B 0327;;;;N;LATIN CAPITAL LETTER K CEDILLA;;;0137; +0137;LATIN SMALL LETTER K WITH CEDILLA;Ll;0;L;006B 0327;;;;N;LATIN SMALL LETTER K CEDILLA;;0136;;0136 +0138;LATIN SMALL LETTER KRA;Ll;0;L;;;;;N;;;;; +0139;LATIN CAPITAL LETTER L WITH ACUTE;Lu;0;L;004C 0301;;;;N;LATIN CAPITAL LETTER L ACUTE;;;013A; +013A;LATIN SMALL LETTER L WITH ACUTE;Ll;0;L;006C 0301;;;;N;LATIN SMALL LETTER L ACUTE;;0139;;0139 +013B;LATIN CAPITAL LETTER L WITH CEDILLA;Lu;0;L;004C 0327;;;;N;LATIN CAPITAL LETTER L CEDILLA;;;013C; +013C;LATIN SMALL LETTER L WITH CEDILLA;Ll;0;L;006C 0327;;;;N;LATIN SMALL LETTER L CEDILLA;;013B;;013B +013D;LATIN CAPITAL LETTER L WITH CARON;Lu;0;L;004C 030C;;;;N;LATIN CAPITAL LETTER L HACEK;;;013E; +013E;LATIN SMALL LETTER L WITH CARON;Ll;0;L;006C 030C;;;;N;LATIN SMALL LETTER L HACEK;;013D;;013D +013F;LATIN CAPITAL LETTER L WITH MIDDLE DOT;Lu;0;L;<compat> 004C 00B7;;;;N;;;;0140; +0140;LATIN SMALL LETTER L WITH MIDDLE DOT;Ll;0;L;<compat> 006C 00B7;;;;N;;;013F;;013F +0141;LATIN CAPITAL LETTER L WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER L SLASH;;;0142; +0142;LATIN SMALL LETTER L WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER L SLASH;;0141;;0141 +0143;LATIN CAPITAL LETTER N WITH ACUTE;Lu;0;L;004E 0301;;;;N;LATIN CAPITAL LETTER N ACUTE;;;0144; +0144;LATIN SMALL LETTER N WITH ACUTE;Ll;0;L;006E 0301;;;;N;LATIN SMALL LETTER N ACUTE;;0143;;0143 +0145;LATIN CAPITAL LETTER N WITH CEDILLA;Lu;0;L;004E 0327;;;;N;LATIN CAPITAL LETTER N CEDILLA;;;0146; +0146;LATIN SMALL LETTER N WITH CEDILLA;Ll;0;L;006E 0327;;;;N;LATIN SMALL LETTER N CEDILLA;;0145;;0145 +0147;LATIN CAPITAL LETTER N WITH CARON;Lu;0;L;004E 030C;;;;N;LATIN CAPITAL LETTER N HACEK;;;0148; +0148;LATIN SMALL LETTER N WITH CARON;Ll;0;L;006E 030C;;;;N;LATIN SMALL LETTER N HACEK;;0147;;0147 +0149;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE;Ll;0;L;<compat> 02BC 006E;;;;N;LATIN SMALL LETTER APOSTROPHE N;;;; +014A;LATIN CAPITAL LETTER ENG;Lu;0;L;;;;;N;;;;014B; +014B;LATIN SMALL LETTER ENG;Ll;0;L;;;;;N;;;014A;;014A +014C;LATIN CAPITAL LETTER O WITH MACRON;Lu;0;L;004F 0304;;;;N;LATIN CAPITAL LETTER O MACRON;;;014D; +014D;LATIN SMALL LETTER O WITH MACRON;Ll;0;L;006F 0304;;;;N;LATIN SMALL LETTER O MACRON;;014C;;014C +014E;LATIN CAPITAL LETTER O WITH BREVE;Lu;0;L;004F 0306;;;;N;LATIN CAPITAL LETTER O BREVE;;;014F; +014F;LATIN SMALL LETTER O WITH BREVE;Ll;0;L;006F 0306;;;;N;LATIN SMALL LETTER O BREVE;;014E;;014E +0150;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE;Lu;0;L;004F 030B;;;;N;LATIN CAPITAL LETTER O DOUBLE ACUTE;;;0151; +0151;LATIN SMALL LETTER O WITH DOUBLE ACUTE;Ll;0;L;006F 030B;;;;N;LATIN SMALL LETTER O DOUBLE ACUTE;;0150;;0150 +0152;LATIN CAPITAL LIGATURE OE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O E;;;0153; +0153;LATIN SMALL LIGATURE OE;Ll;0;L;;;;;N;LATIN SMALL LETTER O E;;0152;;0152 +0154;LATIN CAPITAL LETTER R WITH ACUTE;Lu;0;L;0052 0301;;;;N;LATIN CAPITAL LETTER R ACUTE;;;0155; +0155;LATIN SMALL LETTER R WITH ACUTE;Ll;0;L;0072 0301;;;;N;LATIN SMALL LETTER R ACUTE;;0154;;0154 +0156;LATIN CAPITAL LETTER R WITH CEDILLA;Lu;0;L;0052 0327;;;;N;LATIN CAPITAL LETTER R CEDILLA;;;0157; +0157;LATIN SMALL LETTER R WITH CEDILLA;Ll;0;L;0072 0327;;;;N;LATIN SMALL LETTER R CEDILLA;;0156;;0156 +0158;LATIN CAPITAL LETTER R WITH CARON;Lu;0;L;0052 030C;;;;N;LATIN CAPITAL LETTER R HACEK;;;0159; +0159;LATIN SMALL LETTER R WITH CARON;Ll;0;L;0072 030C;;;;N;LATIN SMALL LETTER R HACEK;;0158;;0158 +015A;LATIN CAPITAL LETTER S WITH ACUTE;Lu;0;L;0053 0301;;;;N;LATIN CAPITAL LETTER S ACUTE;;;015B; +015B;LATIN SMALL LETTER S WITH ACUTE;Ll;0;L;0073 0301;;;;N;LATIN SMALL LETTER S ACUTE;;015A;;015A +015C;LATIN CAPITAL LETTER S WITH CIRCUMFLEX;Lu;0;L;0053 0302;;;;N;LATIN CAPITAL LETTER S CIRCUMFLEX;;;015D; +015D;LATIN SMALL LETTER S WITH CIRCUMFLEX;Ll;0;L;0073 0302;;;;N;LATIN SMALL LETTER S CIRCUMFLEX;;015C;;015C +015E;LATIN CAPITAL LETTER S WITH CEDILLA;Lu;0;L;0053 0327;;;;N;LATIN CAPITAL LETTER S CEDILLA;;;015F; +015F;LATIN SMALL LETTER S WITH CEDILLA;Ll;0;L;0073 0327;;;;N;LATIN SMALL LETTER S CEDILLA;;015E;;015E +0160;LATIN CAPITAL LETTER S WITH CARON;Lu;0;L;0053 030C;;;;N;LATIN CAPITAL LETTER S HACEK;;;0161; +0161;LATIN SMALL LETTER S WITH CARON;Ll;0;L;0073 030C;;;;N;LATIN SMALL LETTER S HACEK;;0160;;0160 +0162;LATIN CAPITAL LETTER T WITH CEDILLA;Lu;0;L;0054 0327;;;;N;LATIN CAPITAL LETTER T CEDILLA;;;0163; +0163;LATIN SMALL LETTER T WITH CEDILLA;Ll;0;L;0074 0327;;;;N;LATIN SMALL LETTER T CEDILLA;;0162;;0162 +0164;LATIN CAPITAL LETTER T WITH CARON;Lu;0;L;0054 030C;;;;N;LATIN CAPITAL LETTER T HACEK;;;0165; +0165;LATIN SMALL LETTER T WITH CARON;Ll;0;L;0074 030C;;;;N;LATIN SMALL LETTER T HACEK;;0164;;0164 +0166;LATIN CAPITAL LETTER T WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T BAR;;;0167; +0167;LATIN SMALL LETTER T WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER T BAR;;0166;;0166 +0168;LATIN CAPITAL LETTER U WITH TILDE;Lu;0;L;0055 0303;;;;N;LATIN CAPITAL LETTER U TILDE;;;0169; +0169;LATIN SMALL LETTER U WITH TILDE;Ll;0;L;0075 0303;;;;N;LATIN SMALL LETTER U TILDE;;0168;;0168 +016A;LATIN CAPITAL LETTER U WITH MACRON;Lu;0;L;0055 0304;;;;N;LATIN CAPITAL LETTER U MACRON;;;016B; +016B;LATIN SMALL LETTER U WITH MACRON;Ll;0;L;0075 0304;;;;N;LATIN SMALL LETTER U MACRON;;016A;;016A +016C;LATIN CAPITAL LETTER U WITH BREVE;Lu;0;L;0055 0306;;;;N;LATIN CAPITAL LETTER U BREVE;;;016D; +016D;LATIN SMALL LETTER U WITH BREVE;Ll;0;L;0075 0306;;;;N;LATIN SMALL LETTER U BREVE;;016C;;016C +016E;LATIN CAPITAL LETTER U WITH RING ABOVE;Lu;0;L;0055 030A;;;;N;LATIN CAPITAL LETTER U RING;;;016F; +016F;LATIN SMALL LETTER U WITH RING ABOVE;Ll;0;L;0075 030A;;;;N;LATIN SMALL LETTER U RING;;016E;;016E +0170;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0055 030B;;;;N;LATIN CAPITAL LETTER U DOUBLE ACUTE;;;0171; +0171;LATIN SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0075 030B;;;;N;LATIN SMALL LETTER U DOUBLE ACUTE;;0170;;0170 +0172;LATIN CAPITAL LETTER U WITH OGONEK;Lu;0;L;0055 0328;;;;N;LATIN CAPITAL LETTER U OGONEK;;;0173; +0173;LATIN SMALL LETTER U WITH OGONEK;Ll;0;L;0075 0328;;;;N;LATIN SMALL LETTER U OGONEK;;0172;;0172 +0174;LATIN CAPITAL LETTER W WITH CIRCUMFLEX;Lu;0;L;0057 0302;;;;N;LATIN CAPITAL LETTER W CIRCUMFLEX;;;0175; +0175;LATIN SMALL LETTER W WITH CIRCUMFLEX;Ll;0;L;0077 0302;;;;N;LATIN SMALL LETTER W CIRCUMFLEX;;0174;;0174 +0176;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX;Lu;0;L;0059 0302;;;;N;LATIN CAPITAL LETTER Y CIRCUMFLEX;;;0177; +0177;LATIN SMALL LETTER Y WITH CIRCUMFLEX;Ll;0;L;0079 0302;;;;N;LATIN SMALL LETTER Y CIRCUMFLEX;;0176;;0176 +0178;LATIN CAPITAL LETTER Y WITH DIAERESIS;Lu;0;L;0059 0308;;;;N;LATIN CAPITAL LETTER Y DIAERESIS;;;00FF; +0179;LATIN CAPITAL LETTER Z WITH ACUTE;Lu;0;L;005A 0301;;;;N;LATIN CAPITAL LETTER Z ACUTE;;;017A; +017A;LATIN SMALL LETTER Z WITH ACUTE;Ll;0;L;007A 0301;;;;N;LATIN SMALL LETTER Z ACUTE;;0179;;0179 +017B;LATIN CAPITAL LETTER Z WITH DOT ABOVE;Lu;0;L;005A 0307;;;;N;LATIN CAPITAL LETTER Z DOT;;;017C; +017C;LATIN SMALL LETTER Z WITH DOT ABOVE;Ll;0;L;007A 0307;;;;N;LATIN SMALL LETTER Z DOT;;017B;;017B +017D;LATIN CAPITAL LETTER Z WITH CARON;Lu;0;L;005A 030C;;;;N;LATIN CAPITAL LETTER Z HACEK;;;017E; +017E;LATIN SMALL LETTER Z WITH CARON;Ll;0;L;007A 030C;;;;N;LATIN SMALL LETTER Z HACEK;;017D;;017D +017F;LATIN SMALL LETTER LONG S;Ll;0;L;<compat> 0073;;;;N;;;0053;;0053 +0180;LATIN SMALL LETTER B WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER B BAR;;0243;;0243 +0181;LATIN CAPITAL LETTER B WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B HOOK;;;0253; +0182;LATIN CAPITAL LETTER B WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B TOPBAR;;;0183; +0183;LATIN SMALL LETTER B WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER B TOPBAR;;0182;;0182 +0184;LATIN CAPITAL LETTER TONE SIX;Lu;0;L;;;;;N;;;;0185; +0185;LATIN SMALL LETTER TONE SIX;Ll;0;L;;;;;N;;;0184;;0184 +0186;LATIN CAPITAL LETTER OPEN O;Lu;0;L;;;;;N;;;;0254; +0187;LATIN CAPITAL LETTER C WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER C HOOK;;;0188; +0188;LATIN SMALL LETTER C WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER C HOOK;;0187;;0187 +0189;LATIN CAPITAL LETTER AFRICAN D;Lu;0;L;;;;;N;;;;0256; +018A;LATIN CAPITAL LETTER D WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D HOOK;;;0257; +018B;LATIN CAPITAL LETTER D WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D TOPBAR;;;018C; +018C;LATIN SMALL LETTER D WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER D TOPBAR;;018B;;018B +018D;LATIN SMALL LETTER TURNED DELTA;Ll;0;L;;;;;N;;;;; +018E;LATIN CAPITAL LETTER REVERSED E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER TURNED E;;;01DD; +018F;LATIN CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;0259; +0190;LATIN CAPITAL LETTER OPEN E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER EPSILON;;;025B; +0191;LATIN CAPITAL LETTER F WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER F HOOK;;;0192; +0192;LATIN SMALL LETTER F WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT F;;0191;;0191 +0193;LATIN CAPITAL LETTER G WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G HOOK;;;0260; +0194;LATIN CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;0263; +0195;LATIN SMALL LETTER HV;Ll;0;L;;;;;N;LATIN SMALL LETTER H V;;01F6;;01F6 +0196;LATIN CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;0269; +0197;LATIN CAPITAL LETTER I WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED I;;;0268; +0198;LATIN CAPITAL LETTER K WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER K HOOK;;;0199; +0199;LATIN SMALL LETTER K WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER K HOOK;;0198;;0198 +019A;LATIN SMALL LETTER L WITH BAR;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED L;;023D;;023D +019B;LATIN SMALL LETTER LAMBDA WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED LAMBDA;;;; +019C;LATIN CAPITAL LETTER TURNED M;Lu;0;L;;;;;N;;;;026F; +019D;LATIN CAPITAL LETTER N WITH LEFT HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER N HOOK;;;0272; +019E;LATIN SMALL LETTER N WITH LONG RIGHT LEG;Ll;0;L;;;;;N;;;0220;;0220 +019F;LATIN CAPITAL LETTER O WITH MIDDLE TILDE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED O;;;0275; +01A0;LATIN CAPITAL LETTER O WITH HORN;Lu;0;L;004F 031B;;;;N;LATIN CAPITAL LETTER O HORN;;;01A1; +01A1;LATIN SMALL LETTER O WITH HORN;Ll;0;L;006F 031B;;;;N;LATIN SMALL LETTER O HORN;;01A0;;01A0 +01A2;LATIN CAPITAL LETTER OI;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O I;;;01A3; +01A3;LATIN SMALL LETTER OI;Ll;0;L;;;;;N;LATIN SMALL LETTER O I;;01A2;;01A2 +01A4;LATIN CAPITAL LETTER P WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER P HOOK;;;01A5; +01A5;LATIN SMALL LETTER P WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER P HOOK;;01A4;;01A4 +01A6;LATIN LETTER YR;Lu;0;L;;;;;N;LATIN LETTER Y R;;;0280; +01A7;LATIN CAPITAL LETTER TONE TWO;Lu;0;L;;;;;N;;;;01A8; +01A8;LATIN SMALL LETTER TONE TWO;Ll;0;L;;;;;N;;;01A7;;01A7 +01A9;LATIN CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;0283; +01AA;LATIN LETTER REVERSED ESH LOOP;Ll;0;L;;;;;N;;;;; +01AB;LATIN SMALL LETTER T WITH PALATAL HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T PALATAL HOOK;;;; +01AC;LATIN CAPITAL LETTER T WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T HOOK;;;01AD; +01AD;LATIN SMALL LETTER T WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T HOOK;;01AC;;01AC +01AE;LATIN CAPITAL LETTER T WITH RETROFLEX HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T RETROFLEX HOOK;;;0288; +01AF;LATIN CAPITAL LETTER U WITH HORN;Lu;0;L;0055 031B;;;;N;LATIN CAPITAL LETTER U HORN;;;01B0; +01B0;LATIN SMALL LETTER U WITH HORN;Ll;0;L;0075 031B;;;;N;LATIN SMALL LETTER U HORN;;01AF;;01AF +01B1;LATIN CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;028A; +01B2;LATIN CAPITAL LETTER V WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER SCRIPT V;;;028B; +01B3;LATIN CAPITAL LETTER Y WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Y HOOK;;;01B4; +01B4;LATIN SMALL LETTER Y WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Y HOOK;;01B3;;01B3 +01B5;LATIN CAPITAL LETTER Z WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Z BAR;;;01B6; +01B6;LATIN SMALL LETTER Z WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER Z BAR;;01B5;;01B5 +01B7;LATIN CAPITAL LETTER EZH;Lu;0;L;;;;;N;LATIN CAPITAL LETTER YOGH;;;0292; +01B8;LATIN CAPITAL LETTER EZH REVERSED;Lu;0;L;;;;;N;LATIN CAPITAL LETTER REVERSED YOGH;;;01B9; +01B9;LATIN SMALL LETTER EZH REVERSED;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED YOGH;;01B8;;01B8 +01BA;LATIN SMALL LETTER EZH WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH WITH TAIL;;;; +01BB;LATIN LETTER TWO WITH STROKE;Lo;0;L;;;;;N;LATIN LETTER TWO BAR;;;; +01BC;LATIN CAPITAL LETTER TONE FIVE;Lu;0;L;;;;;N;;;;01BD; +01BD;LATIN SMALL LETTER TONE FIVE;Ll;0;L;;;;;N;;;01BC;;01BC +01BE;LATIN LETTER INVERTED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER INVERTED GLOTTAL STOP BAR;;;; +01BF;LATIN LETTER WYNN;Ll;0;L;;;;;N;;;01F7;;01F7 +01C0;LATIN LETTER DENTAL CLICK;Lo;0;L;;;;;N;LATIN LETTER PIPE;;;; +01C1;LATIN LETTER LATERAL CLICK;Lo;0;L;;;;;N;LATIN LETTER DOUBLE PIPE;;;; +01C2;LATIN LETTER ALVEOLAR CLICK;Lo;0;L;;;;;N;LATIN LETTER PIPE DOUBLE BAR;;;; +01C3;LATIN LETTER RETROFLEX CLICK;Lo;0;L;;;;;N;LATIN LETTER EXCLAMATION MARK;;;; +01C4;LATIN CAPITAL LETTER DZ WITH CARON;Lu;0;L;<compat> 0044 017D;;;;N;LATIN CAPITAL LETTER D Z HACEK;;;01C6;01C5 +01C5;LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON;Lt;0;L;<compat> 0044 017E;;;;N;LATIN LETTER CAPITAL D SMALL Z HACEK;;01C4;01C6;01C5 +01C6;LATIN SMALL LETTER DZ WITH CARON;Ll;0;L;<compat> 0064 017E;;;;N;LATIN SMALL LETTER D Z HACEK;;01C4;;01C5 +01C7;LATIN CAPITAL LETTER LJ;Lu;0;L;<compat> 004C 004A;;;;N;LATIN CAPITAL LETTER L J;;;01C9;01C8 +01C8;LATIN CAPITAL LETTER L WITH SMALL LETTER J;Lt;0;L;<compat> 004C 006A;;;;N;LATIN LETTER CAPITAL L SMALL J;;01C7;01C9;01C8 +01C9;LATIN SMALL LETTER LJ;Ll;0;L;<compat> 006C 006A;;;;N;LATIN SMALL LETTER L J;;01C7;;01C8 +01CA;LATIN CAPITAL LETTER NJ;Lu;0;L;<compat> 004E 004A;;;;N;LATIN CAPITAL LETTER N J;;;01CC;01CB +01CB;LATIN CAPITAL LETTER N WITH SMALL LETTER J;Lt;0;L;<compat> 004E 006A;;;;N;LATIN LETTER CAPITAL N SMALL J;;01CA;01CC;01CB +01CC;LATIN SMALL LETTER NJ;Ll;0;L;<compat> 006E 006A;;;;N;LATIN SMALL LETTER N J;;01CA;;01CB +01CD;LATIN CAPITAL LETTER A WITH CARON;Lu;0;L;0041 030C;;;;N;LATIN CAPITAL LETTER A HACEK;;;01CE; +01CE;LATIN SMALL LETTER A WITH CARON;Ll;0;L;0061 030C;;;;N;LATIN SMALL LETTER A HACEK;;01CD;;01CD +01CF;LATIN CAPITAL LETTER I WITH CARON;Lu;0;L;0049 030C;;;;N;LATIN CAPITAL LETTER I HACEK;;;01D0; +01D0;LATIN SMALL LETTER I WITH CARON;Ll;0;L;0069 030C;;;;N;LATIN SMALL LETTER I HACEK;;01CF;;01CF +01D1;LATIN CAPITAL LETTER O WITH CARON;Lu;0;L;004F 030C;;;;N;LATIN CAPITAL LETTER O HACEK;;;01D2; +01D2;LATIN SMALL LETTER O WITH CARON;Ll;0;L;006F 030C;;;;N;LATIN SMALL LETTER O HACEK;;01D1;;01D1 +01D3;LATIN CAPITAL LETTER U WITH CARON;Lu;0;L;0055 030C;;;;N;LATIN CAPITAL LETTER U HACEK;;;01D4; +01D4;LATIN SMALL LETTER U WITH CARON;Ll;0;L;0075 030C;;;;N;LATIN SMALL LETTER U HACEK;;01D3;;01D3 +01D5;LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON;Lu;0;L;00DC 0304;;;;N;LATIN CAPITAL LETTER U DIAERESIS MACRON;;;01D6; +01D6;LATIN SMALL LETTER U WITH DIAERESIS AND MACRON;Ll;0;L;00FC 0304;;;;N;LATIN SMALL LETTER U DIAERESIS MACRON;;01D5;;01D5 +01D7;LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE;Lu;0;L;00DC 0301;;;;N;LATIN CAPITAL LETTER U DIAERESIS ACUTE;;;01D8; +01D8;LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE;Ll;0;L;00FC 0301;;;;N;LATIN SMALL LETTER U DIAERESIS ACUTE;;01D7;;01D7 +01D9;LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON;Lu;0;L;00DC 030C;;;;N;LATIN CAPITAL LETTER U DIAERESIS HACEK;;;01DA; +01DA;LATIN SMALL LETTER U WITH DIAERESIS AND CARON;Ll;0;L;00FC 030C;;;;N;LATIN SMALL LETTER U DIAERESIS HACEK;;01D9;;01D9 +01DB;LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE;Lu;0;L;00DC 0300;;;;N;LATIN CAPITAL LETTER U DIAERESIS GRAVE;;;01DC; +01DC;LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE;Ll;0;L;00FC 0300;;;;N;LATIN SMALL LETTER U DIAERESIS GRAVE;;01DB;;01DB +01DD;LATIN SMALL LETTER TURNED E;Ll;0;L;;;;;N;;;018E;;018E +01DE;LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON;Lu;0;L;00C4 0304;;;;N;LATIN CAPITAL LETTER A DIAERESIS MACRON;;;01DF; +01DF;LATIN SMALL LETTER A WITH DIAERESIS AND MACRON;Ll;0;L;00E4 0304;;;;N;LATIN SMALL LETTER A DIAERESIS MACRON;;01DE;;01DE +01E0;LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON;Lu;0;L;0226 0304;;;;N;LATIN CAPITAL LETTER A DOT MACRON;;;01E1; +01E1;LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON;Ll;0;L;0227 0304;;;;N;LATIN SMALL LETTER A DOT MACRON;;01E0;;01E0 +01E2;LATIN CAPITAL LETTER AE WITH MACRON;Lu;0;L;00C6 0304;;;;N;LATIN CAPITAL LETTER A E MACRON;;;01E3; +01E3;LATIN SMALL LETTER AE WITH MACRON;Ll;0;L;00E6 0304;;;;N;LATIN SMALL LETTER A E MACRON;;01E2;;01E2 +01E4;LATIN CAPITAL LETTER G WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G BAR;;;01E5; +01E5;LATIN SMALL LETTER G WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER G BAR;;01E4;;01E4 +01E6;LATIN CAPITAL LETTER G WITH CARON;Lu;0;L;0047 030C;;;;N;LATIN CAPITAL LETTER G HACEK;;;01E7; +01E7;LATIN SMALL LETTER G WITH CARON;Ll;0;L;0067 030C;;;;N;LATIN SMALL LETTER G HACEK;;01E6;;01E6 +01E8;LATIN CAPITAL LETTER K WITH CARON;Lu;0;L;004B 030C;;;;N;LATIN CAPITAL LETTER K HACEK;;;01E9; +01E9;LATIN SMALL LETTER K WITH CARON;Ll;0;L;006B 030C;;;;N;LATIN SMALL LETTER K HACEK;;01E8;;01E8 +01EA;LATIN CAPITAL LETTER O WITH OGONEK;Lu;0;L;004F 0328;;;;N;LATIN CAPITAL LETTER O OGONEK;;;01EB; +01EB;LATIN SMALL LETTER O WITH OGONEK;Ll;0;L;006F 0328;;;;N;LATIN SMALL LETTER O OGONEK;;01EA;;01EA +01EC;LATIN CAPITAL LETTER O WITH OGONEK AND MACRON;Lu;0;L;01EA 0304;;;;N;LATIN CAPITAL LETTER O OGONEK MACRON;;;01ED; +01ED;LATIN SMALL LETTER O WITH OGONEK AND MACRON;Ll;0;L;01EB 0304;;;;N;LATIN SMALL LETTER O OGONEK MACRON;;01EC;;01EC +01EE;LATIN CAPITAL LETTER EZH WITH CARON;Lu;0;L;01B7 030C;;;;N;LATIN CAPITAL LETTER YOGH HACEK;;;01EF; +01EF;LATIN SMALL LETTER EZH WITH CARON;Ll;0;L;0292 030C;;;;N;LATIN SMALL LETTER YOGH HACEK;;01EE;;01EE +01F0;LATIN SMALL LETTER J WITH CARON;Ll;0;L;006A 030C;;;;N;LATIN SMALL LETTER J HACEK;;;; +01F1;LATIN CAPITAL LETTER DZ;Lu;0;L;<compat> 0044 005A;;;;N;;;;01F3;01F2 +01F2;LATIN CAPITAL LETTER D WITH SMALL LETTER Z;Lt;0;L;<compat> 0044 007A;;;;N;;;01F1;01F3;01F2 +01F3;LATIN SMALL LETTER DZ;Ll;0;L;<compat> 0064 007A;;;;N;;;01F1;;01F2 +01F4;LATIN CAPITAL LETTER G WITH ACUTE;Lu;0;L;0047 0301;;;;N;;;;01F5; +01F5;LATIN SMALL LETTER G WITH ACUTE;Ll;0;L;0067 0301;;;;N;;;01F4;;01F4 +01F6;LATIN CAPITAL LETTER HWAIR;Lu;0;L;;;;;N;;;;0195; +01F7;LATIN CAPITAL LETTER WYNN;Lu;0;L;;;;;N;;;;01BF; +01F8;LATIN CAPITAL LETTER N WITH GRAVE;Lu;0;L;004E 0300;;;;N;;;;01F9; +01F9;LATIN SMALL LETTER N WITH GRAVE;Ll;0;L;006E 0300;;;;N;;;01F8;;01F8 +01FA;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE;Lu;0;L;00C5 0301;;;;N;;;;01FB; +01FB;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE;Ll;0;L;00E5 0301;;;;N;;;01FA;;01FA +01FC;LATIN CAPITAL LETTER AE WITH ACUTE;Lu;0;L;00C6 0301;;;;N;;;;01FD; +01FD;LATIN SMALL LETTER AE WITH ACUTE;Ll;0;L;00E6 0301;;;;N;;;01FC;;01FC +01FE;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE;Lu;0;L;00D8 0301;;;;N;;;;01FF; +01FF;LATIN SMALL LETTER O WITH STROKE AND ACUTE;Ll;0;L;00F8 0301;;;;N;;;01FE;;01FE +0200;LATIN CAPITAL LETTER A WITH DOUBLE GRAVE;Lu;0;L;0041 030F;;;;N;;;;0201; +0201;LATIN SMALL LETTER A WITH DOUBLE GRAVE;Ll;0;L;0061 030F;;;;N;;;0200;;0200 +0202;LATIN CAPITAL LETTER A WITH INVERTED BREVE;Lu;0;L;0041 0311;;;;N;;;;0203; +0203;LATIN SMALL LETTER A WITH INVERTED BREVE;Ll;0;L;0061 0311;;;;N;;;0202;;0202 +0204;LATIN CAPITAL LETTER E WITH DOUBLE GRAVE;Lu;0;L;0045 030F;;;;N;;;;0205; +0205;LATIN SMALL LETTER E WITH DOUBLE GRAVE;Ll;0;L;0065 030F;;;;N;;;0204;;0204 +0206;LATIN CAPITAL LETTER E WITH INVERTED BREVE;Lu;0;L;0045 0311;;;;N;;;;0207; +0207;LATIN SMALL LETTER E WITH INVERTED BREVE;Ll;0;L;0065 0311;;;;N;;;0206;;0206 +0208;LATIN CAPITAL LETTER I WITH DOUBLE GRAVE;Lu;0;L;0049 030F;;;;N;;;;0209; +0209;LATIN SMALL LETTER I WITH DOUBLE GRAVE;Ll;0;L;0069 030F;;;;N;;;0208;;0208 +020A;LATIN CAPITAL LETTER I WITH INVERTED BREVE;Lu;0;L;0049 0311;;;;N;;;;020B; +020B;LATIN SMALL LETTER I WITH INVERTED BREVE;Ll;0;L;0069 0311;;;;N;;;020A;;020A +020C;LATIN CAPITAL LETTER O WITH DOUBLE GRAVE;Lu;0;L;004F 030F;;;;N;;;;020D; +020D;LATIN SMALL LETTER O WITH DOUBLE GRAVE;Ll;0;L;006F 030F;;;;N;;;020C;;020C +020E;LATIN CAPITAL LETTER O WITH INVERTED BREVE;Lu;0;L;004F 0311;;;;N;;;;020F; +020F;LATIN SMALL LETTER O WITH INVERTED BREVE;Ll;0;L;006F 0311;;;;N;;;020E;;020E +0210;LATIN CAPITAL LETTER R WITH DOUBLE GRAVE;Lu;0;L;0052 030F;;;;N;;;;0211; +0211;LATIN SMALL LETTER R WITH DOUBLE GRAVE;Ll;0;L;0072 030F;;;;N;;;0210;;0210 +0212;LATIN CAPITAL LETTER R WITH INVERTED BREVE;Lu;0;L;0052 0311;;;;N;;;;0213; +0213;LATIN SMALL LETTER R WITH INVERTED BREVE;Ll;0;L;0072 0311;;;;N;;;0212;;0212 +0214;LATIN CAPITAL LETTER U WITH DOUBLE GRAVE;Lu;0;L;0055 030F;;;;N;;;;0215; +0215;LATIN SMALL LETTER U WITH DOUBLE GRAVE;Ll;0;L;0075 030F;;;;N;;;0214;;0214 +0216;LATIN CAPITAL LETTER U WITH INVERTED BREVE;Lu;0;L;0055 0311;;;;N;;;;0217; +0217;LATIN SMALL LETTER U WITH INVERTED BREVE;Ll;0;L;0075 0311;;;;N;;;0216;;0216 +0218;LATIN CAPITAL LETTER S WITH COMMA BELOW;Lu;0;L;0053 0326;;;;N;;;;0219; +0219;LATIN SMALL LETTER S WITH COMMA BELOW;Ll;0;L;0073 0326;;;;N;;;0218;;0218 +021A;LATIN CAPITAL LETTER T WITH COMMA BELOW;Lu;0;L;0054 0326;;;;N;;;;021B; +021B;LATIN SMALL LETTER T WITH COMMA BELOW;Ll;0;L;0074 0326;;;;N;;;021A;;021A +021C;LATIN CAPITAL LETTER YOGH;Lu;0;L;;;;;N;;;;021D; +021D;LATIN SMALL LETTER YOGH;Ll;0;L;;;;;N;;;021C;;021C +021E;LATIN CAPITAL LETTER H WITH CARON;Lu;0;L;0048 030C;;;;N;;;;021F; +021F;LATIN SMALL LETTER H WITH CARON;Ll;0;L;0068 030C;;;;N;;;021E;;021E +0220;LATIN CAPITAL LETTER N WITH LONG RIGHT LEG;Lu;0;L;;;;;N;;;;019E; +0221;LATIN SMALL LETTER D WITH CURL;Ll;0;L;;;;;N;;;;; +0222;LATIN CAPITAL LETTER OU;Lu;0;L;;;;;N;;;;0223; +0223;LATIN SMALL LETTER OU;Ll;0;L;;;;;N;;;0222;;0222 +0224;LATIN CAPITAL LETTER Z WITH HOOK;Lu;0;L;;;;;N;;;;0225; +0225;LATIN SMALL LETTER Z WITH HOOK;Ll;0;L;;;;;N;;;0224;;0224 +0226;LATIN CAPITAL LETTER A WITH DOT ABOVE;Lu;0;L;0041 0307;;;;N;;;;0227; +0227;LATIN SMALL LETTER A WITH DOT ABOVE;Ll;0;L;0061 0307;;;;N;;;0226;;0226 +0228;LATIN CAPITAL LETTER E WITH CEDILLA;Lu;0;L;0045 0327;;;;N;;;;0229; +0229;LATIN SMALL LETTER E WITH CEDILLA;Ll;0;L;0065 0327;;;;N;;;0228;;0228 +022A;LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON;Lu;0;L;00D6 0304;;;;N;;;;022B; +022B;LATIN SMALL LETTER O WITH DIAERESIS AND MACRON;Ll;0;L;00F6 0304;;;;N;;;022A;;022A +022C;LATIN CAPITAL LETTER O WITH TILDE AND MACRON;Lu;0;L;00D5 0304;;;;N;;;;022D; +022D;LATIN SMALL LETTER O WITH TILDE AND MACRON;Ll;0;L;00F5 0304;;;;N;;;022C;;022C +022E;LATIN CAPITAL LETTER O WITH DOT ABOVE;Lu;0;L;004F 0307;;;;N;;;;022F; +022F;LATIN SMALL LETTER O WITH DOT ABOVE;Ll;0;L;006F 0307;;;;N;;;022E;;022E +0230;LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON;Lu;0;L;022E 0304;;;;N;;;;0231; +0231;LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON;Ll;0;L;022F 0304;;;;N;;;0230;;0230 +0232;LATIN CAPITAL LETTER Y WITH MACRON;Lu;0;L;0059 0304;;;;N;;;;0233; +0233;LATIN SMALL LETTER Y WITH MACRON;Ll;0;L;0079 0304;;;;N;;;0232;;0232 +0234;LATIN SMALL LETTER L WITH CURL;Ll;0;L;;;;;N;;;;; +0235;LATIN SMALL LETTER N WITH CURL;Ll;0;L;;;;;N;;;;; +0236;LATIN SMALL LETTER T WITH CURL;Ll;0;L;;;;;N;;;;; +0237;LATIN SMALL LETTER DOTLESS J;Ll;0;L;;;;;N;;;;; +0238;LATIN SMALL LETTER DB DIGRAPH;Ll;0;L;;;;;N;;;;; +0239;LATIN SMALL LETTER QP DIGRAPH;Ll;0;L;;;;;N;;;;; +023A;LATIN CAPITAL LETTER A WITH STROKE;Lu;0;L;;;;;N;;;;2C65; +023B;LATIN CAPITAL LETTER C WITH STROKE;Lu;0;L;;;;;N;;;;023C; +023C;LATIN SMALL LETTER C WITH STROKE;Ll;0;L;;;;;N;;;023B;;023B +023D;LATIN CAPITAL LETTER L WITH BAR;Lu;0;L;;;;;N;;;;019A; +023E;LATIN CAPITAL LETTER T WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;2C66; +023F;LATIN SMALL LETTER S WITH SWASH TAIL;Ll;0;L;;;;;N;;;2C7E;;2C7E +0240;LATIN SMALL LETTER Z WITH SWASH TAIL;Ll;0;L;;;;;N;;;2C7F;;2C7F +0241;LATIN CAPITAL LETTER GLOTTAL STOP;Lu;0;L;;;;;N;;;;0242; +0242;LATIN SMALL LETTER GLOTTAL STOP;Ll;0;L;;;;;N;;;0241;;0241 +0243;LATIN CAPITAL LETTER B WITH STROKE;Lu;0;L;;;;;N;;;;0180; +0244;LATIN CAPITAL LETTER U BAR;Lu;0;L;;;;;N;;;;0289; +0245;LATIN CAPITAL LETTER TURNED V;Lu;0;L;;;;;N;;;;028C; +0246;LATIN CAPITAL LETTER E WITH STROKE;Lu;0;L;;;;;N;;;;0247; +0247;LATIN SMALL LETTER E WITH STROKE;Ll;0;L;;;;;N;;;0246;;0246 +0248;LATIN CAPITAL LETTER J WITH STROKE;Lu;0;L;;;;;N;;;;0249; +0249;LATIN SMALL LETTER J WITH STROKE;Ll;0;L;;;;;N;;;0248;;0248 +024A;LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL;Lu;0;L;;;;;N;;;;024B; +024B;LATIN SMALL LETTER Q WITH HOOK TAIL;Ll;0;L;;;;;N;;;024A;;024A +024C;LATIN CAPITAL LETTER R WITH STROKE;Lu;0;L;;;;;N;;;;024D; +024D;LATIN SMALL LETTER R WITH STROKE;Ll;0;L;;;;;N;;;024C;;024C +024E;LATIN CAPITAL LETTER Y WITH STROKE;Lu;0;L;;;;;N;;;;024F; +024F;LATIN SMALL LETTER Y WITH STROKE;Ll;0;L;;;;;N;;;024E;;024E +0250;LATIN SMALL LETTER TURNED A;Ll;0;L;;;;;N;;;2C6F;;2C6F +0251;LATIN SMALL LETTER ALPHA;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT A;;2C6D;;2C6D +0252;LATIN SMALL LETTER TURNED ALPHA;Ll;0;L;;;;;N;LATIN SMALL LETTER TURNED SCRIPT A;;2C70;;2C70 +0253;LATIN SMALL LETTER B WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER B HOOK;;0181;;0181 +0254;LATIN SMALL LETTER OPEN O;Ll;0;L;;;;;N;;;0186;;0186 +0255;LATIN SMALL LETTER C WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER C CURL;;;; +0256;LATIN SMALL LETTER D WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER D RETROFLEX HOOK;;0189;;0189 +0257;LATIN SMALL LETTER D WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER D HOOK;;018A;;018A +0258;LATIN SMALL LETTER REVERSED E;Ll;0;L;;;;;N;;;;; +0259;LATIN SMALL LETTER SCHWA;Ll;0;L;;;;;N;;;018F;;018F +025A;LATIN SMALL LETTER SCHWA WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCHWA HOOK;;;; +025B;LATIN SMALL LETTER OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER EPSILON;;0190;;0190 +025C;LATIN SMALL LETTER REVERSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED EPSILON;;A7AB;;A7AB +025D;LATIN SMALL LETTER REVERSED OPEN E WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED EPSILON HOOK;;;; +025E;LATIN SMALL LETTER CLOSED REVERSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER CLOSED REVERSED EPSILON;;;; +025F;LATIN SMALL LETTER DOTLESS J WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR;;;; +0260;LATIN SMALL LETTER G WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER G HOOK;;0193;;0193 +0261;LATIN SMALL LETTER SCRIPT G;Ll;0;L;;;;;N;;;A7AC;;A7AC +0262;LATIN LETTER SMALL CAPITAL G;Ll;0;L;;;;;N;;;;; +0263;LATIN SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0194;;0194 +0264;LATIN SMALL LETTER RAMS HORN;Ll;0;L;;;;;N;LATIN SMALL LETTER BABY GAMMA;;;; +0265;LATIN SMALL LETTER TURNED H;Ll;0;L;;;;;N;;;A78D;;A78D +0266;LATIN SMALL LETTER H WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER H HOOK;;A7AA;;A7AA +0267;LATIN SMALL LETTER HENG WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER HENG HOOK;;;; +0268;LATIN SMALL LETTER I WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED I;;0197;;0197 +0269;LATIN SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0196;;0196 +026A;LATIN LETTER SMALL CAPITAL I;Ll;0;L;;;;;N;;;A7AE;;A7AE +026B;LATIN SMALL LETTER L WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;2C62;;2C62 +026C;LATIN SMALL LETTER L WITH BELT;Ll;0;L;;;;;N;LATIN SMALL LETTER L BELT;;A7AD;;A7AD +026D;LATIN SMALL LETTER L WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER L RETROFLEX HOOK;;;; +026E;LATIN SMALL LETTER LEZH;Ll;0;L;;;;;N;LATIN SMALL LETTER L YOGH;;;; +026F;LATIN SMALL LETTER TURNED M;Ll;0;L;;;;;N;;;019C;;019C +0270;LATIN SMALL LETTER TURNED M WITH LONG LEG;Ll;0;L;;;;;N;;;;; +0271;LATIN SMALL LETTER M WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER M HOOK;;2C6E;;2C6E +0272;LATIN SMALL LETTER N WITH LEFT HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER N HOOK;;019D;;019D +0273;LATIN SMALL LETTER N WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER N RETROFLEX HOOK;;;; +0274;LATIN LETTER SMALL CAPITAL N;Ll;0;L;;;;;N;;;;; +0275;LATIN SMALL LETTER BARRED O;Ll;0;L;;;;;N;;;019F;;019F +0276;LATIN LETTER SMALL CAPITAL OE;Ll;0;L;;;;;N;LATIN LETTER SMALL CAPITAL O E;;;; +0277;LATIN SMALL LETTER CLOSED OMEGA;Ll;0;L;;;;;N;;;;; +0278;LATIN SMALL LETTER PHI;Ll;0;L;;;;;N;;;;; +0279;LATIN SMALL LETTER TURNED R;Ll;0;L;;;;;N;;;;; +027A;LATIN SMALL LETTER TURNED R WITH LONG LEG;Ll;0;L;;;;;N;;;;; +027B;LATIN SMALL LETTER TURNED R WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER TURNED R HOOK;;;; +027C;LATIN SMALL LETTER R WITH LONG LEG;Ll;0;L;;;;;N;;;;; +027D;LATIN SMALL LETTER R WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER R HOOK;;2C64;;2C64 +027E;LATIN SMALL LETTER R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER FISHHOOK R;;;; +027F;LATIN SMALL LETTER REVERSED R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED FISHHOOK R;;;; +0280;LATIN LETTER SMALL CAPITAL R;Ll;0;L;;;;;N;;;01A6;;01A6 +0281;LATIN LETTER SMALL CAPITAL INVERTED R;Ll;0;L;;;;;N;;;;; +0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;;; +0283;LATIN SMALL LETTER ESH;Ll;0;L;;;;;N;;;01A9;;01A9 +0284;LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR HOOK;;;; +0285;LATIN SMALL LETTER SQUAT REVERSED ESH;Ll;0;L;;;;;N;;;;; +0286;LATIN SMALL LETTER ESH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER ESH CURL;;;; +0287;LATIN SMALL LETTER TURNED T;Ll;0;L;;;;;N;;;A7B1;;A7B1 +0288;LATIN SMALL LETTER T WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T RETROFLEX HOOK;;01AE;;01AE +0289;LATIN SMALL LETTER U BAR;Ll;0;L;;;;;N;;;0244;;0244 +028A;LATIN SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;01B1;;01B1 +028B;LATIN SMALL LETTER V WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT V;;01B2;;01B2 +028C;LATIN SMALL LETTER TURNED V;Ll;0;L;;;;;N;;;0245;;0245 +028D;LATIN SMALL LETTER TURNED W;Ll;0;L;;;;;N;;;;; +028E;LATIN SMALL LETTER TURNED Y;Ll;0;L;;;;;N;;;;; +028F;LATIN LETTER SMALL CAPITAL Y;Ll;0;L;;;;;N;;;;; +0290;LATIN SMALL LETTER Z WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Z RETROFLEX HOOK;;;; +0291;LATIN SMALL LETTER Z WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER Z CURL;;;; +0292;LATIN SMALL LETTER EZH;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH;;01B7;;01B7 +0293;LATIN SMALL LETTER EZH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH CURL;;;; +0294;LATIN LETTER GLOTTAL STOP;Lo;0;L;;;;;N;;;;; +0295;LATIN LETTER PHARYNGEAL VOICED FRICATIVE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP;;;; +0296;LATIN LETTER INVERTED GLOTTAL STOP;Ll;0;L;;;;;N;;;;; +0297;LATIN LETTER STRETCHED C;Ll;0;L;;;;;N;;;;; +0298;LATIN LETTER BILABIAL CLICK;Ll;0;L;;;;;N;LATIN LETTER BULLSEYE;;;; +0299;LATIN LETTER SMALL CAPITAL B;Ll;0;L;;;;;N;;;;; +029A;LATIN SMALL LETTER CLOSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER CLOSED EPSILON;;;; +029B;LATIN LETTER SMALL CAPITAL G WITH HOOK;Ll;0;L;;;;;N;LATIN LETTER SMALL CAPITAL G HOOK;;;; +029C;LATIN LETTER SMALL CAPITAL H;Ll;0;L;;;;;N;;;;; +029D;LATIN SMALL LETTER J WITH CROSSED-TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER CROSSED-TAIL J;;A7B2;;A7B2 +029E;LATIN SMALL LETTER TURNED K;Ll;0;L;;;;;N;;;A7B0;;A7B0 +029F;LATIN LETTER SMALL CAPITAL L;Ll;0;L;;;;;N;;;;; +02A0;LATIN SMALL LETTER Q WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Q HOOK;;;; +02A1;LATIN LETTER GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER GLOTTAL STOP BAR;;;; +02A2;LATIN LETTER REVERSED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP BAR;;;; +02A3;LATIN SMALL LETTER DZ DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER D Z;;;; +02A4;LATIN SMALL LETTER DEZH DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER D YOGH;;;; +02A5;LATIN SMALL LETTER DZ DIGRAPH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER D Z CURL;;;; +02A6;LATIN SMALL LETTER TS DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER T S;;;; +02A7;LATIN SMALL LETTER TESH DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER T ESH;;;; +02A8;LATIN SMALL LETTER TC DIGRAPH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER T C CURL;;;; +02A9;LATIN SMALL LETTER FENG DIGRAPH;Ll;0;L;;;;;N;;;;; +02AA;LATIN SMALL LETTER LS DIGRAPH;Ll;0;L;;;;;N;;;;; +02AB;LATIN SMALL LETTER LZ DIGRAPH;Ll;0;L;;;;;N;;;;; +02AC;LATIN LETTER BILABIAL PERCUSSIVE;Ll;0;L;;;;;N;;;;; +02AD;LATIN LETTER BIDENTAL PERCUSSIVE;Ll;0;L;;;;;N;;;;; +02AE;LATIN SMALL LETTER TURNED H WITH FISHHOOK;Ll;0;L;;;;;N;;;;; +02AF;LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL;Ll;0;L;;;;;N;;;;; +02B0;MODIFIER LETTER SMALL H;Lm;0;L;<super> 0068;;;;N;;;;; +02B1;MODIFIER LETTER SMALL H WITH HOOK;Lm;0;L;<super> 0266;;;;N;MODIFIER LETTER SMALL H HOOK;;;; +02B2;MODIFIER LETTER SMALL J;Lm;0;L;<super> 006A;;;;N;;;;; +02B3;MODIFIER LETTER SMALL R;Lm;0;L;<super> 0072;;;;N;;;;; +02B4;MODIFIER LETTER SMALL TURNED R;Lm;0;L;<super> 0279;;;;N;;;;; +02B5;MODIFIER LETTER SMALL TURNED R WITH HOOK;Lm;0;L;<super> 027B;;;;N;MODIFIER LETTER SMALL TURNED R HOOK;;;; +02B6;MODIFIER LETTER SMALL CAPITAL INVERTED R;Lm;0;L;<super> 0281;;;;N;;;;; +02B7;MODIFIER LETTER SMALL W;Lm;0;L;<super> 0077;;;;N;;;;; +02B8;MODIFIER LETTER SMALL Y;Lm;0;L;<super> 0079;;;;N;;;;; +02B9;MODIFIER LETTER PRIME;Lm;0;ON;;;;;N;;;;; +02BA;MODIFIER LETTER DOUBLE PRIME;Lm;0;ON;;;;;N;;;;; +02BB;MODIFIER LETTER TURNED COMMA;Lm;0;L;;;;;N;;;;; +02BC;MODIFIER LETTER APOSTROPHE;Lm;0;L;;;;;N;;;;; +02BD;MODIFIER LETTER REVERSED COMMA;Lm;0;L;;;;;N;;;;; +02BE;MODIFIER LETTER RIGHT HALF RING;Lm;0;L;;;;;N;;;;; +02BF;MODIFIER LETTER LEFT HALF RING;Lm;0;L;;;;;N;;;;; +02C0;MODIFIER LETTER GLOTTAL STOP;Lm;0;L;;;;;N;;;;; +02C1;MODIFIER LETTER REVERSED GLOTTAL STOP;Lm;0;L;;;;;N;;;;; +02C2;MODIFIER LETTER LEFT ARROWHEAD;Sk;0;ON;;;;;N;;;;; +02C3;MODIFIER LETTER RIGHT ARROWHEAD;Sk;0;ON;;;;;N;;;;; +02C4;MODIFIER LETTER UP ARROWHEAD;Sk;0;ON;;;;;N;;;;; +02C5;MODIFIER LETTER DOWN ARROWHEAD;Sk;0;ON;;;;;N;;;;; +02C6;MODIFIER LETTER CIRCUMFLEX ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER CIRCUMFLEX;;;; +02C7;CARON;Lm;0;ON;;;;;N;MODIFIER LETTER HACEK;;;; +02C8;MODIFIER LETTER VERTICAL LINE;Lm;0;ON;;;;;N;;;;; +02C9;MODIFIER LETTER MACRON;Lm;0;ON;;;;;N;;;;; +02CA;MODIFIER LETTER ACUTE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER ACUTE;;;; +02CB;MODIFIER LETTER GRAVE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER GRAVE;;;; +02CC;MODIFIER LETTER LOW VERTICAL LINE;Lm;0;ON;;;;;N;;;;; +02CD;MODIFIER LETTER LOW MACRON;Lm;0;ON;;;;;N;;;;; +02CE;MODIFIER LETTER LOW GRAVE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER LOW GRAVE;;;; +02CF;MODIFIER LETTER LOW ACUTE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER LOW ACUTE;;;; +02D0;MODIFIER LETTER TRIANGULAR COLON;Lm;0;L;;;;;N;;;;; +02D1;MODIFIER LETTER HALF TRIANGULAR COLON;Lm;0;L;;;;;N;;;;; +02D2;MODIFIER LETTER CENTRED RIGHT HALF RING;Sk;0;ON;;;;;N;MODIFIER LETTER CENTERED RIGHT HALF RING;;;; +02D3;MODIFIER LETTER CENTRED LEFT HALF RING;Sk;0;ON;;;;;N;MODIFIER LETTER CENTERED LEFT HALF RING;;;; +02D4;MODIFIER LETTER UP TACK;Sk;0;ON;;;;;N;;;;; +02D5;MODIFIER LETTER DOWN TACK;Sk;0;ON;;;;;N;;;;; +02D6;MODIFIER LETTER PLUS SIGN;Sk;0;ON;;;;;N;;;;; +02D7;MODIFIER LETTER MINUS SIGN;Sk;0;ON;;;;;N;;;;; +02D8;BREVE;Sk;0;ON;<compat> 0020 0306;;;;N;SPACING BREVE;;;; +02D9;DOT ABOVE;Sk;0;ON;<compat> 0020 0307;;;;N;SPACING DOT ABOVE;;;; +02DA;RING ABOVE;Sk;0;ON;<compat> 0020 030A;;;;N;SPACING RING ABOVE;;;; +02DB;OGONEK;Sk;0;ON;<compat> 0020 0328;;;;N;SPACING OGONEK;;;; +02DC;SMALL TILDE;Sk;0;ON;<compat> 0020 0303;;;;N;SPACING TILDE;;;; +02DD;DOUBLE ACUTE ACCENT;Sk;0;ON;<compat> 0020 030B;;;;N;SPACING DOUBLE ACUTE;;;; +02DE;MODIFIER LETTER RHOTIC HOOK;Sk;0;ON;;;;;N;;;;; +02DF;MODIFIER LETTER CROSS ACCENT;Sk;0;ON;;;;;N;;;;; +02E0;MODIFIER LETTER SMALL GAMMA;Lm;0;L;<super> 0263;;;;N;;;;; +02E1;MODIFIER LETTER SMALL L;Lm;0;L;<super> 006C;;;;N;;;;; +02E2;MODIFIER LETTER SMALL S;Lm;0;L;<super> 0073;;;;N;;;;; +02E3;MODIFIER LETTER SMALL X;Lm;0;L;<super> 0078;;;;N;;;;; +02E4;MODIFIER LETTER SMALL REVERSED GLOTTAL STOP;Lm;0;L;<super> 0295;;;;N;;;;; +02E5;MODIFIER LETTER EXTRA-HIGH TONE BAR;Sk;0;ON;;;;;N;;;;; +02E6;MODIFIER LETTER HIGH TONE BAR;Sk;0;ON;;;;;N;;;;; +02E7;MODIFIER LETTER MID TONE BAR;Sk;0;ON;;;;;N;;;;; +02E8;MODIFIER LETTER LOW TONE BAR;Sk;0;ON;;;;;N;;;;; +02E9;MODIFIER LETTER EXTRA-LOW TONE BAR;Sk;0;ON;;;;;N;;;;; +02EA;MODIFIER LETTER YIN DEPARTING TONE MARK;Sk;0;ON;;;;;N;;;;; +02EB;MODIFIER LETTER YANG DEPARTING TONE MARK;Sk;0;ON;;;;;N;;;;; +02EC;MODIFIER LETTER VOICING;Lm;0;ON;;;;;N;;;;; +02ED;MODIFIER LETTER UNASPIRATED;Sk;0;ON;;;;;N;;;;; +02EE;MODIFIER LETTER DOUBLE APOSTROPHE;Lm;0;L;;;;;N;;;;; +02EF;MODIFIER LETTER LOW DOWN ARROWHEAD;Sk;0;ON;;;;;N;;;;; +02F0;MODIFIER LETTER LOW UP ARROWHEAD;Sk;0;ON;;;;;N;;;;; +02F1;MODIFIER LETTER LOW LEFT ARROWHEAD;Sk;0;ON;;;;;N;;;;; +02F2;MODIFIER LETTER LOW RIGHT ARROWHEAD;Sk;0;ON;;;;;N;;;;; +02F3;MODIFIER LETTER LOW RING;Sk;0;ON;;;;;N;;;;; +02F4;MODIFIER LETTER MIDDLE GRAVE ACCENT;Sk;0;ON;;;;;N;;;;; +02F5;MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT;Sk;0;ON;;;;;N;;;;; +02F6;MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT;Sk;0;ON;;;;;N;;;;; +02F7;MODIFIER LETTER LOW TILDE;Sk;0;ON;;;;;N;;;;; +02F8;MODIFIER LETTER RAISED COLON;Sk;0;ON;;;;;N;;;;; +02F9;MODIFIER LETTER BEGIN HIGH TONE;Sk;0;ON;;;;;N;;;;; +02FA;MODIFIER LETTER END HIGH TONE;Sk;0;ON;;;;;N;;;;; +02FB;MODIFIER LETTER BEGIN LOW TONE;Sk;0;ON;;;;;N;;;;; +02FC;MODIFIER LETTER END LOW TONE;Sk;0;ON;;;;;N;;;;; +02FD;MODIFIER LETTER SHELF;Sk;0;ON;;;;;N;;;;; +02FE;MODIFIER LETTER OPEN SHELF;Sk;0;ON;;;;;N;;;;; +02FF;MODIFIER LETTER LOW LEFT ARROW;Sk;0;ON;;;;;N;;;;; +0300;COMBINING GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING GRAVE;;;; +0301;COMBINING ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING ACUTE;;;; +0302;COMBINING CIRCUMFLEX ACCENT;Mn;230;NSM;;;;;N;NON-SPACING CIRCUMFLEX;;;; +0303;COMBINING TILDE;Mn;230;NSM;;;;;N;NON-SPACING TILDE;;;; +0304;COMBINING MACRON;Mn;230;NSM;;;;;N;NON-SPACING MACRON;;;; +0305;COMBINING OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING OVERSCORE;;;; +0306;COMBINING BREVE;Mn;230;NSM;;;;;N;NON-SPACING BREVE;;;; +0307;COMBINING DOT ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOT ABOVE;;;; +0308;COMBINING DIAERESIS;Mn;230;NSM;;;;;N;NON-SPACING DIAERESIS;;;; +0309;COMBINING HOOK ABOVE;Mn;230;NSM;;;;;N;NON-SPACING HOOK ABOVE;;;; +030A;COMBINING RING ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RING ABOVE;;;; +030B;COMBINING DOUBLE ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE ACUTE;;;; +030C;COMBINING CARON;Mn;230;NSM;;;;;N;NON-SPACING HACEK;;;; +030D;COMBINING VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL LINE ABOVE;;;; +030E;COMBINING DOUBLE VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE VERTICAL LINE ABOVE;;;; +030F;COMBINING DOUBLE GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE GRAVE;;;; +0310;COMBINING CANDRABINDU;Mn;230;NSM;;;;;N;NON-SPACING CANDRABINDU;;;; +0311;COMBINING INVERTED BREVE;Mn;230;NSM;;;;;N;NON-SPACING INVERTED BREVE;;;; +0312;COMBINING TURNED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING TURNED COMMA ABOVE;;;; +0313;COMBINING COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING COMMA ABOVE;;;; +0314;COMBINING REVERSED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING REVERSED COMMA ABOVE;;;; +0315;COMBINING COMMA ABOVE RIGHT;Mn;232;NSM;;;;;N;NON-SPACING COMMA ABOVE RIGHT;;;; +0316;COMBINING GRAVE ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING GRAVE BELOW;;;; +0317;COMBINING ACUTE ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING ACUTE BELOW;;;; +0318;COMBINING LEFT TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING LEFT TACK BELOW;;;; +0319;COMBINING RIGHT TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING RIGHT TACK BELOW;;;; +031A;COMBINING LEFT ANGLE ABOVE;Mn;232;NSM;;;;;N;NON-SPACING LEFT ANGLE ABOVE;;;; +031B;COMBINING HORN;Mn;216;NSM;;;;;N;NON-SPACING HORN;;;; +031C;COMBINING LEFT HALF RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING LEFT HALF RING BELOW;;;; +031D;COMBINING UP TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING UP TACK BELOW;;;; +031E;COMBINING DOWN TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOWN TACK BELOW;;;; +031F;COMBINING PLUS SIGN BELOW;Mn;220;NSM;;;;;N;NON-SPACING PLUS SIGN BELOW;;;; +0320;COMBINING MINUS SIGN BELOW;Mn;220;NSM;;;;;N;NON-SPACING MINUS SIGN BELOW;;;; +0321;COMBINING PALATALIZED HOOK BELOW;Mn;202;NSM;;;;;N;NON-SPACING PALATALIZED HOOK BELOW;;;; +0322;COMBINING RETROFLEX HOOK BELOW;Mn;202;NSM;;;;;N;NON-SPACING RETROFLEX HOOK BELOW;;;; +0323;COMBINING DOT BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOT BELOW;;;; +0324;COMBINING DIAERESIS BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOUBLE DOT BELOW;;;; +0325;COMBINING RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RING BELOW;;;; +0326;COMBINING COMMA BELOW;Mn;220;NSM;;;;;N;NON-SPACING COMMA BELOW;;;; +0327;COMBINING CEDILLA;Mn;202;NSM;;;;;N;NON-SPACING CEDILLA;;;; +0328;COMBINING OGONEK;Mn;202;NSM;;;;;N;NON-SPACING OGONEK;;;; +0329;COMBINING VERTICAL LINE BELOW;Mn;220;NSM;;;;;N;NON-SPACING VERTICAL LINE BELOW;;;; +032A;COMBINING BRIDGE BELOW;Mn;220;NSM;;;;;N;NON-SPACING BRIDGE BELOW;;;; +032B;COMBINING INVERTED DOUBLE ARCH BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED DOUBLE ARCH BELOW;;;; +032C;COMBINING CARON BELOW;Mn;220;NSM;;;;;N;NON-SPACING HACEK BELOW;;;; +032D;COMBINING CIRCUMFLEX ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING CIRCUMFLEX BELOW;;;; +032E;COMBINING BREVE BELOW;Mn;220;NSM;;;;;N;NON-SPACING BREVE BELOW;;;; +032F;COMBINING INVERTED BREVE BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED BREVE BELOW;;;; +0330;COMBINING TILDE BELOW;Mn;220;NSM;;;;;N;NON-SPACING TILDE BELOW;;;; +0331;COMBINING MACRON BELOW;Mn;220;NSM;;;;;N;NON-SPACING MACRON BELOW;;;; +0332;COMBINING LOW LINE;Mn;220;NSM;;;;;N;NON-SPACING UNDERSCORE;;;; +0333;COMBINING DOUBLE LOW LINE;Mn;220;NSM;;;;;N;NON-SPACING DOUBLE UNDERSCORE;;;; +0334;COMBINING TILDE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING TILDE OVERLAY;;;; +0335;COMBINING SHORT STROKE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT BAR OVERLAY;;;; +0336;COMBINING LONG STROKE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG BAR OVERLAY;;;; +0337;COMBINING SHORT SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT SLASH OVERLAY;;;; +0338;COMBINING LONG SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG SLASH OVERLAY;;;; +0339;COMBINING RIGHT HALF RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RIGHT HALF RING BELOW;;;; +033A;COMBINING INVERTED BRIDGE BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED BRIDGE BELOW;;;; +033B;COMBINING SQUARE BELOW;Mn;220;NSM;;;;;N;NON-SPACING SQUARE BELOW;;;; +033C;COMBINING SEAGULL BELOW;Mn;220;NSM;;;;;N;NON-SPACING SEAGULL BELOW;;;; +033D;COMBINING X ABOVE;Mn;230;NSM;;;;;N;NON-SPACING X ABOVE;;;; +033E;COMBINING VERTICAL TILDE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL TILDE;;;; +033F;COMBINING DOUBLE OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE OVERSCORE;;;; +0340;COMBINING GRAVE TONE MARK;Mn;230;NSM;0300;;;;N;NON-SPACING GRAVE TONE MARK;;;; +0341;COMBINING ACUTE TONE MARK;Mn;230;NSM;0301;;;;N;NON-SPACING ACUTE TONE MARK;;;; +0342;COMBINING GREEK PERISPOMENI;Mn;230;NSM;;;;;N;;;;; +0343;COMBINING GREEK KORONIS;Mn;230;NSM;0313;;;;N;;;;; +0344;COMBINING GREEK DIALYTIKA TONOS;Mn;230;NSM;0308 0301;;;;N;GREEK NON-SPACING DIAERESIS TONOS;;;; +0345;COMBINING GREEK YPOGEGRAMMENI;Mn;240;NSM;;;;;N;GREEK NON-SPACING IOTA BELOW;;0399;;0399 +0346;COMBINING BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;; +0347;COMBINING EQUALS SIGN BELOW;Mn;220;NSM;;;;;N;;;;; +0348;COMBINING DOUBLE VERTICAL LINE BELOW;Mn;220;NSM;;;;;N;;;;; +0349;COMBINING LEFT ANGLE BELOW;Mn;220;NSM;;;;;N;;;;; +034A;COMBINING NOT TILDE ABOVE;Mn;230;NSM;;;;;N;;;;; +034B;COMBINING HOMOTHETIC ABOVE;Mn;230;NSM;;;;;N;;;;; +034C;COMBINING ALMOST EQUAL TO ABOVE;Mn;230;NSM;;;;;N;;;;; +034D;COMBINING LEFT RIGHT ARROW BELOW;Mn;220;NSM;;;;;N;;;;; +034E;COMBINING UPWARDS ARROW BELOW;Mn;220;NSM;;;;;N;;;;; +034F;COMBINING GRAPHEME JOINER;Mn;0;NSM;;;;;N;;;;; +0350;COMBINING RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;; +0351;COMBINING LEFT HALF RING ABOVE;Mn;230;NSM;;;;;N;;;;; +0352;COMBINING FERMATA;Mn;230;NSM;;;;;N;;;;; +0353;COMBINING X BELOW;Mn;220;NSM;;;;;N;;;;; +0354;COMBINING LEFT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;; +0355;COMBINING RIGHT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;; +0356;COMBINING RIGHT ARROWHEAD AND UP ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;; +0357;COMBINING RIGHT HALF RING ABOVE;Mn;230;NSM;;;;;N;;;;; +0358;COMBINING DOT ABOVE RIGHT;Mn;232;NSM;;;;;N;;;;; +0359;COMBINING ASTERISK BELOW;Mn;220;NSM;;;;;N;;;;; +035A;COMBINING DOUBLE RING BELOW;Mn;220;NSM;;;;;N;;;;; +035B;COMBINING ZIGZAG ABOVE;Mn;230;NSM;;;;;N;;;;; +035C;COMBINING DOUBLE BREVE BELOW;Mn;233;NSM;;;;;N;;;;; +035D;COMBINING DOUBLE BREVE;Mn;234;NSM;;;;;N;;;;; +035E;COMBINING DOUBLE MACRON;Mn;234;NSM;;;;;N;;;;; +035F;COMBINING DOUBLE MACRON BELOW;Mn;233;NSM;;;;;N;;;;; +0360;COMBINING DOUBLE TILDE;Mn;234;NSM;;;;;N;;;;; +0361;COMBINING DOUBLE INVERTED BREVE;Mn;234;NSM;;;;;N;;;;; +0362;COMBINING DOUBLE RIGHTWARDS ARROW BELOW;Mn;233;NSM;;;;;N;;;;; +0363;COMBINING LATIN SMALL LETTER A;Mn;230;NSM;;;;;N;;;;; +0364;COMBINING LATIN SMALL LETTER E;Mn;230;NSM;;;;;N;;;;; +0365;COMBINING LATIN SMALL LETTER I;Mn;230;NSM;;;;;N;;;;; +0366;COMBINING LATIN SMALL LETTER O;Mn;230;NSM;;;;;N;;;;; +0367;COMBINING LATIN SMALL LETTER U;Mn;230;NSM;;;;;N;;;;; +0368;COMBINING LATIN SMALL LETTER C;Mn;230;NSM;;;;;N;;;;; +0369;COMBINING LATIN SMALL LETTER D;Mn;230;NSM;;;;;N;;;;; +036A;COMBINING LATIN SMALL LETTER H;Mn;230;NSM;;;;;N;;;;; +036B;COMBINING LATIN SMALL LETTER M;Mn;230;NSM;;;;;N;;;;; +036C;COMBINING LATIN SMALL LETTER R;Mn;230;NSM;;;;;N;;;;; +036D;COMBINING LATIN SMALL LETTER T;Mn;230;NSM;;;;;N;;;;; +036E;COMBINING LATIN SMALL LETTER V;Mn;230;NSM;;;;;N;;;;; +036F;COMBINING LATIN SMALL LETTER X;Mn;230;NSM;;;;;N;;;;; +0370;GREEK CAPITAL LETTER HETA;Lu;0;L;;;;;N;;;;0371; +0371;GREEK SMALL LETTER HETA;Ll;0;L;;;;;N;;;0370;;0370 +0372;GREEK CAPITAL LETTER ARCHAIC SAMPI;Lu;0;L;;;;;N;;;;0373; +0373;GREEK SMALL LETTER ARCHAIC SAMPI;Ll;0;L;;;;;N;;;0372;;0372 +0374;GREEK NUMERAL SIGN;Lm;0;ON;02B9;;;;N;GREEK UPPER NUMERAL SIGN;;;; +0375;GREEK LOWER NUMERAL SIGN;Sk;0;ON;;;;;N;;;;; +0376;GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA;Lu;0;L;;;;;N;;;;0377; +0377;GREEK SMALL LETTER PAMPHYLIAN DIGAMMA;Ll;0;L;;;;;N;;;0376;;0376 +037A;GREEK YPOGEGRAMMENI;Lm;0;L;<compat> 0020 0345;;;;N;GREEK SPACING IOTA BELOW;;;; +037B;GREEK SMALL REVERSED LUNATE SIGMA SYMBOL;Ll;0;L;;;;;N;;;03FD;;03FD +037C;GREEK SMALL DOTTED LUNATE SIGMA SYMBOL;Ll;0;L;;;;;N;;;03FE;;03FE +037D;GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL;Ll;0;L;;;;;N;;;03FF;;03FF +037E;GREEK QUESTION MARK;Po;0;ON;003B;;;;N;;;;; +037F;GREEK CAPITAL LETTER YOT;Lu;0;L;;;;;N;;;;03F3; +0384;GREEK TONOS;Sk;0;ON;<compat> 0020 0301;;;;N;GREEK SPACING TONOS;;;; +0385;GREEK DIALYTIKA TONOS;Sk;0;ON;00A8 0301;;;;N;GREEK SPACING DIAERESIS TONOS;;;; +0386;GREEK CAPITAL LETTER ALPHA WITH TONOS;Lu;0;L;0391 0301;;;;N;GREEK CAPITAL LETTER ALPHA TONOS;;;03AC; +0387;GREEK ANO TELEIA;Po;0;ON;00B7;;;;N;;;;; +0388;GREEK CAPITAL LETTER EPSILON WITH TONOS;Lu;0;L;0395 0301;;;;N;GREEK CAPITAL LETTER EPSILON TONOS;;;03AD; +0389;GREEK CAPITAL LETTER ETA WITH TONOS;Lu;0;L;0397 0301;;;;N;GREEK CAPITAL LETTER ETA TONOS;;;03AE; +038A;GREEK CAPITAL LETTER IOTA WITH TONOS;Lu;0;L;0399 0301;;;;N;GREEK CAPITAL LETTER IOTA TONOS;;;03AF; +038C;GREEK CAPITAL LETTER OMICRON WITH TONOS;Lu;0;L;039F 0301;;;;N;GREEK CAPITAL LETTER OMICRON TONOS;;;03CC; +038E;GREEK CAPITAL LETTER UPSILON WITH TONOS;Lu;0;L;03A5 0301;;;;N;GREEK CAPITAL LETTER UPSILON TONOS;;;03CD; +038F;GREEK CAPITAL LETTER OMEGA WITH TONOS;Lu;0;L;03A9 0301;;;;N;GREEK CAPITAL LETTER OMEGA TONOS;;;03CE; +0390;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS;Ll;0;L;03CA 0301;;;;N;GREEK SMALL LETTER IOTA DIAERESIS TONOS;;;; +0391;GREEK CAPITAL LETTER ALPHA;Lu;0;L;;;;;N;;;;03B1; +0392;GREEK CAPITAL LETTER BETA;Lu;0;L;;;;;N;;;;03B2; +0393;GREEK CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;03B3; +0394;GREEK CAPITAL LETTER DELTA;Lu;0;L;;;;;N;;;;03B4; +0395;GREEK CAPITAL LETTER EPSILON;Lu;0;L;;;;;N;;;;03B5; +0396;GREEK CAPITAL LETTER ZETA;Lu;0;L;;;;;N;;;;03B6; +0397;GREEK CAPITAL LETTER ETA;Lu;0;L;;;;;N;;;;03B7; +0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8; +0399;GREEK CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;03B9; +039A;GREEK CAPITAL LETTER KAPPA;Lu;0;L;;;;;N;;;;03BA; +039B;GREEK CAPITAL LETTER LAMDA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER LAMBDA;;;03BB; +039C;GREEK CAPITAL LETTER MU;Lu;0;L;;;;;N;;;;03BC; +039D;GREEK CAPITAL LETTER NU;Lu;0;L;;;;;N;;;;03BD; +039E;GREEK CAPITAL LETTER XI;Lu;0;L;;;;;N;;;;03BE; +039F;GREEK CAPITAL LETTER OMICRON;Lu;0;L;;;;;N;;;;03BF; +03A0;GREEK CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;03C0; +03A1;GREEK CAPITAL LETTER RHO;Lu;0;L;;;;;N;;;;03C1; +03A3;GREEK CAPITAL LETTER SIGMA;Lu;0;L;;;;;N;;;;03C3; +03A4;GREEK CAPITAL LETTER TAU;Lu;0;L;;;;;N;;;;03C4; +03A5;GREEK CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;03C5; +03A6;GREEK CAPITAL LETTER PHI;Lu;0;L;;;;;N;;;;03C6; +03A7;GREEK CAPITAL LETTER CHI;Lu;0;L;;;;;N;;;;03C7; +03A8;GREEK CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;03C8; +03A9;GREEK CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;03C9; +03AA;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA;Lu;0;L;0399 0308;;;;N;GREEK CAPITAL LETTER IOTA DIAERESIS;;;03CA; +03AB;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA;Lu;0;L;03A5 0308;;;;N;GREEK CAPITAL LETTER UPSILON DIAERESIS;;;03CB; +03AC;GREEK SMALL LETTER ALPHA WITH TONOS;Ll;0;L;03B1 0301;;;;N;GREEK SMALL LETTER ALPHA TONOS;;0386;;0386 +03AD;GREEK SMALL LETTER EPSILON WITH TONOS;Ll;0;L;03B5 0301;;;;N;GREEK SMALL LETTER EPSILON TONOS;;0388;;0388 +03AE;GREEK SMALL LETTER ETA WITH TONOS;Ll;0;L;03B7 0301;;;;N;GREEK SMALL LETTER ETA TONOS;;0389;;0389 +03AF;GREEK SMALL LETTER IOTA WITH TONOS;Ll;0;L;03B9 0301;;;;N;GREEK SMALL LETTER IOTA TONOS;;038A;;038A +03B0;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS;Ll;0;L;03CB 0301;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS TONOS;;;; +03B1;GREEK SMALL LETTER ALPHA;Ll;0;L;;;;;N;;;0391;;0391 +03B2;GREEK SMALL LETTER BETA;Ll;0;L;;;;;N;;;0392;;0392 +03B3;GREEK SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0393;;0393 +03B4;GREEK SMALL LETTER DELTA;Ll;0;L;;;;;N;;;0394;;0394 +03B5;GREEK SMALL LETTER EPSILON;Ll;0;L;;;;;N;;;0395;;0395 +03B6;GREEK SMALL LETTER ZETA;Ll;0;L;;;;;N;;;0396;;0396 +03B7;GREEK SMALL LETTER ETA;Ll;0;L;;;;;N;;;0397;;0397 +03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398 +03B9;GREEK SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0399;;0399 +03BA;GREEK SMALL LETTER KAPPA;Ll;0;L;;;;;N;;;039A;;039A +03BB;GREEK SMALL LETTER LAMDA;Ll;0;L;;;;;N;GREEK SMALL LETTER LAMBDA;;039B;;039B +03BC;GREEK SMALL LETTER MU;Ll;0;L;;;;;N;;;039C;;039C +03BD;GREEK SMALL LETTER NU;Ll;0;L;;;;;N;;;039D;;039D +03BE;GREEK SMALL LETTER XI;Ll;0;L;;;;;N;;;039E;;039E +03BF;GREEK SMALL LETTER OMICRON;Ll;0;L;;;;;N;;;039F;;039F +03C0;GREEK SMALL LETTER PI;Ll;0;L;;;;;N;;;03A0;;03A0 +03C1;GREEK SMALL LETTER RHO;Ll;0;L;;;;;N;;;03A1;;03A1 +03C2;GREEK SMALL LETTER FINAL SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3 +03C3;GREEK SMALL LETTER SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3 +03C4;GREEK SMALL LETTER TAU;Ll;0;L;;;;;N;;;03A4;;03A4 +03C5;GREEK SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;03A5;;03A5 +03C6;GREEK SMALL LETTER PHI;Ll;0;L;;;;;N;;;03A6;;03A6 +03C7;GREEK SMALL LETTER CHI;Ll;0;L;;;;;N;;;03A7;;03A7 +03C8;GREEK SMALL LETTER PSI;Ll;0;L;;;;;N;;;03A8;;03A8 +03C9;GREEK SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;03A9;;03A9 +03CA;GREEK SMALL LETTER IOTA WITH DIALYTIKA;Ll;0;L;03B9 0308;;;;N;GREEK SMALL LETTER IOTA DIAERESIS;;03AA;;03AA +03CB;GREEK SMALL LETTER UPSILON WITH DIALYTIKA;Ll;0;L;03C5 0308;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS;;03AB;;03AB +03CC;GREEK SMALL LETTER OMICRON WITH TONOS;Ll;0;L;03BF 0301;;;;N;GREEK SMALL LETTER OMICRON TONOS;;038C;;038C +03CD;GREEK SMALL LETTER UPSILON WITH TONOS;Ll;0;L;03C5 0301;;;;N;GREEK SMALL LETTER UPSILON TONOS;;038E;;038E +03CE;GREEK SMALL LETTER OMEGA WITH TONOS;Ll;0;L;03C9 0301;;;;N;GREEK SMALL LETTER OMEGA TONOS;;038F;;038F +03CF;GREEK CAPITAL KAI SYMBOL;Lu;0;L;;;;;N;;;;03D7; +03D0;GREEK BETA SYMBOL;Ll;0;L;<compat> 03B2;;;;N;GREEK SMALL LETTER CURLED BETA;;0392;;0392 +03D1;GREEK THETA SYMBOL;Ll;0;L;<compat> 03B8;;;;N;GREEK SMALL LETTER SCRIPT THETA;;0398;;0398 +03D2;GREEK UPSILON WITH HOOK SYMBOL;Lu;0;L;<compat> 03A5;;;;N;GREEK CAPITAL LETTER UPSILON HOOK;;;; +03D3;GREEK UPSILON WITH ACUTE AND HOOK SYMBOL;Lu;0;L;03D2 0301;;;;N;GREEK CAPITAL LETTER UPSILON HOOK TONOS;;;; +03D4;GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL;Lu;0;L;03D2 0308;;;;N;GREEK CAPITAL LETTER UPSILON HOOK DIAERESIS;;;; +03D5;GREEK PHI SYMBOL;Ll;0;L;<compat> 03C6;;;;N;GREEK SMALL LETTER SCRIPT PHI;;03A6;;03A6 +03D6;GREEK PI SYMBOL;Ll;0;L;<compat> 03C0;;;;N;GREEK SMALL LETTER OMEGA PI;;03A0;;03A0 +03D7;GREEK KAI SYMBOL;Ll;0;L;;;;;N;;;03CF;;03CF +03D8;GREEK LETTER ARCHAIC KOPPA;Lu;0;L;;;;;N;;;;03D9; +03D9;GREEK SMALL LETTER ARCHAIC KOPPA;Ll;0;L;;;;;N;;;03D8;;03D8 +03DA;GREEK LETTER STIGMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER STIGMA;;;03DB; +03DB;GREEK SMALL LETTER STIGMA;Ll;0;L;;;;;N;;;03DA;;03DA +03DC;GREEK LETTER DIGAMMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DIGAMMA;;;03DD; +03DD;GREEK SMALL LETTER DIGAMMA;Ll;0;L;;;;;N;;;03DC;;03DC +03DE;GREEK LETTER KOPPA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KOPPA;;;03DF; +03DF;GREEK SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;03DE;;03DE +03E0;GREEK LETTER SAMPI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SAMPI;;;03E1; +03E1;GREEK SMALL LETTER SAMPI;Ll;0;L;;;;;N;;;03E0;;03E0 +03E2;COPTIC CAPITAL LETTER SHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHEI;;;03E3; +03E3;COPTIC SMALL LETTER SHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER SHEI;;03E2;;03E2 +03E4;COPTIC CAPITAL LETTER FEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER FEI;;;03E5; +03E5;COPTIC SMALL LETTER FEI;Ll;0;L;;;;;N;GREEK SMALL LETTER FEI;;03E4;;03E4 +03E6;COPTIC CAPITAL LETTER KHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KHEI;;;03E7; +03E7;COPTIC SMALL LETTER KHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER KHEI;;03E6;;03E6 +03E8;COPTIC CAPITAL LETTER HORI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER HORI;;;03E9; +03E9;COPTIC SMALL LETTER HORI;Ll;0;L;;;;;N;GREEK SMALL LETTER HORI;;03E8;;03E8 +03EA;COPTIC CAPITAL LETTER GANGIA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER GANGIA;;;03EB; +03EB;COPTIC SMALL LETTER GANGIA;Ll;0;L;;;;;N;GREEK SMALL LETTER GANGIA;;03EA;;03EA +03EC;COPTIC CAPITAL LETTER SHIMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHIMA;;;03ED; +03ED;COPTIC SMALL LETTER SHIMA;Ll;0;L;;;;;N;GREEK SMALL LETTER SHIMA;;03EC;;03EC +03EE;COPTIC CAPITAL LETTER DEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DEI;;;03EF; +03EF;COPTIC SMALL LETTER DEI;Ll;0;L;;;;;N;GREEK SMALL LETTER DEI;;03EE;;03EE +03F0;GREEK KAPPA SYMBOL;Ll;0;L;<compat> 03BA;;;;N;GREEK SMALL LETTER SCRIPT KAPPA;;039A;;039A +03F1;GREEK RHO SYMBOL;Ll;0;L;<compat> 03C1;;;;N;GREEK SMALL LETTER TAILED RHO;;03A1;;03A1 +03F2;GREEK LUNATE SIGMA SYMBOL;Ll;0;L;<compat> 03C2;;;;N;GREEK SMALL LETTER LUNATE SIGMA;;03F9;;03F9 +03F3;GREEK LETTER YOT;Ll;0;L;;;;;N;;;037F;;037F +03F4;GREEK CAPITAL THETA SYMBOL;Lu;0;L;<compat> 0398;;;;N;;;;03B8; +03F5;GREEK LUNATE EPSILON SYMBOL;Ll;0;L;<compat> 03B5;;;;N;;;0395;;0395 +03F6;GREEK REVERSED LUNATE EPSILON SYMBOL;Sm;0;ON;;;;;N;;;;; +03F7;GREEK CAPITAL LETTER SHO;Lu;0;L;;;;;N;;;;03F8; +03F8;GREEK SMALL LETTER SHO;Ll;0;L;;;;;N;;;03F7;;03F7 +03F9;GREEK CAPITAL LUNATE SIGMA SYMBOL;Lu;0;L;<compat> 03A3;;;;N;;;;03F2; +03FA;GREEK CAPITAL LETTER SAN;Lu;0;L;;;;;N;;;;03FB; +03FB;GREEK SMALL LETTER SAN;Ll;0;L;;;;;N;;;03FA;;03FA +03FC;GREEK RHO WITH STROKE SYMBOL;Ll;0;L;;;;;N;;;;; +03FD;GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL;Lu;0;L;;;;;N;;;;037B; +03FE;GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL;Lu;0;L;;;;;N;;;;037C; +03FF;GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL;Lu;0;L;;;;;N;;;;037D; +0400;CYRILLIC CAPITAL LETTER IE WITH GRAVE;Lu;0;L;0415 0300;;;;N;;;;0450; +0401;CYRILLIC CAPITAL LETTER IO;Lu;0;L;0415 0308;;;;N;;;;0451; +0402;CYRILLIC CAPITAL LETTER DJE;Lu;0;L;;;;;N;;;;0452; +0403;CYRILLIC CAPITAL LETTER GJE;Lu;0;L;0413 0301;;;;N;;;;0453; +0404;CYRILLIC CAPITAL LETTER UKRAINIAN IE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER E;;;0454; +0405;CYRILLIC CAPITAL LETTER DZE;Lu;0;L;;;;;N;;;;0455; +0406;CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER I;;;0456; +0407;CYRILLIC CAPITAL LETTER YI;Lu;0;L;0406 0308;;;;N;;;;0457; +0408;CYRILLIC CAPITAL LETTER JE;Lu;0;L;;;;;N;;;;0458; +0409;CYRILLIC CAPITAL LETTER LJE;Lu;0;L;;;;;N;;;;0459; +040A;CYRILLIC CAPITAL LETTER NJE;Lu;0;L;;;;;N;;;;045A; +040B;CYRILLIC CAPITAL LETTER TSHE;Lu;0;L;;;;;N;;;;045B; +040C;CYRILLIC CAPITAL LETTER KJE;Lu;0;L;041A 0301;;;;N;;;;045C; +040D;CYRILLIC CAPITAL LETTER I WITH GRAVE;Lu;0;L;0418 0300;;;;N;;;;045D; +040E;CYRILLIC CAPITAL LETTER SHORT U;Lu;0;L;0423 0306;;;;N;;;;045E; +040F;CYRILLIC CAPITAL LETTER DZHE;Lu;0;L;;;;;N;;;;045F; +0410;CYRILLIC CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0430; +0411;CYRILLIC CAPITAL LETTER BE;Lu;0;L;;;;;N;;;;0431; +0412;CYRILLIC CAPITAL LETTER VE;Lu;0;L;;;;;N;;;;0432; +0413;CYRILLIC CAPITAL LETTER GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE;;;0433; +0414;CYRILLIC CAPITAL LETTER DE;Lu;0;L;;;;;N;;;;0434; +0415;CYRILLIC CAPITAL LETTER IE;Lu;0;L;;;;;N;;;;0435; +0416;CYRILLIC CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;0436; +0417;CYRILLIC CAPITAL LETTER ZE;Lu;0;L;;;;;N;;;;0437; +0418;CYRILLIC CAPITAL LETTER I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER II;;;0438; +0419;CYRILLIC CAPITAL LETTER SHORT I;Lu;0;L;0418 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT II;;;0439; +041A;CYRILLIC CAPITAL LETTER KA;Lu;0;L;;;;;N;;;;043A; +041B;CYRILLIC CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;043B; +041C;CYRILLIC CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;043C; +041D;CYRILLIC CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;043D; +041E;CYRILLIC CAPITAL LETTER O;Lu;0;L;;;;;N;;;;043E; +041F;CYRILLIC CAPITAL LETTER PE;Lu;0;L;;;;;N;;;;043F; +0420;CYRILLIC CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;0440; +0421;CYRILLIC CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;0441; +0422;CYRILLIC CAPITAL LETTER TE;Lu;0;L;;;;;N;;;;0442; +0423;CYRILLIC CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0443; +0424;CYRILLIC CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;0444; +0425;CYRILLIC CAPITAL LETTER HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA;;;0445; +0426;CYRILLIC CAPITAL LETTER TSE;Lu;0;L;;;;;N;;;;0446; +0427;CYRILLIC CAPITAL LETTER CHE;Lu;0;L;;;;;N;;;;0447; +0428;CYRILLIC CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0448; +0429;CYRILLIC CAPITAL LETTER SHCHA;Lu;0;L;;;;;N;;;;0449; +042A;CYRILLIC CAPITAL LETTER HARD SIGN;Lu;0;L;;;;;N;;;;044A; +042B;CYRILLIC CAPITAL LETTER YERU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER YERI;;;044B; +042C;CYRILLIC CAPITAL LETTER SOFT SIGN;Lu;0;L;;;;;N;;;;044C; +042D;CYRILLIC CAPITAL LETTER E;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED E;;;044D; +042E;CYRILLIC CAPITAL LETTER YU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IU;;;044E; +042F;CYRILLIC CAPITAL LETTER YA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IA;;;044F; +0430;CYRILLIC SMALL LETTER A;Ll;0;L;;;;;N;;;0410;;0410 +0431;CYRILLIC SMALL LETTER BE;Ll;0;L;;;;;N;;;0411;;0411 +0432;CYRILLIC SMALL LETTER VE;Ll;0;L;;;;;N;;;0412;;0412 +0433;CYRILLIC SMALL LETTER GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE;;0413;;0413 +0434;CYRILLIC SMALL LETTER DE;Ll;0;L;;;;;N;;;0414;;0414 +0435;CYRILLIC SMALL LETTER IE;Ll;0;L;;;;;N;;;0415;;0415 +0436;CYRILLIC SMALL LETTER ZHE;Ll;0;L;;;;;N;;;0416;;0416 +0437;CYRILLIC SMALL LETTER ZE;Ll;0;L;;;;;N;;;0417;;0417 +0438;CYRILLIC SMALL LETTER I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER II;;0418;;0418 +0439;CYRILLIC SMALL LETTER SHORT I;Ll;0;L;0438 0306;;;;N;CYRILLIC SMALL LETTER SHORT II;;0419;;0419 +043A;CYRILLIC SMALL LETTER KA;Ll;0;L;;;;;N;;;041A;;041A +043B;CYRILLIC SMALL LETTER EL;Ll;0;L;;;;;N;;;041B;;041B +043C;CYRILLIC SMALL LETTER EM;Ll;0;L;;;;;N;;;041C;;041C +043D;CYRILLIC SMALL LETTER EN;Ll;0;L;;;;;N;;;041D;;041D +043E;CYRILLIC SMALL LETTER O;Ll;0;L;;;;;N;;;041E;;041E +043F;CYRILLIC SMALL LETTER PE;Ll;0;L;;;;;N;;;041F;;041F +0440;CYRILLIC SMALL LETTER ER;Ll;0;L;;;;;N;;;0420;;0420 +0441;CYRILLIC SMALL LETTER ES;Ll;0;L;;;;;N;;;0421;;0421 +0442;CYRILLIC SMALL LETTER TE;Ll;0;L;;;;;N;;;0422;;0422 +0443;CYRILLIC SMALL LETTER U;Ll;0;L;;;;;N;;;0423;;0423 +0444;CYRILLIC SMALL LETTER EF;Ll;0;L;;;;;N;;;0424;;0424 +0445;CYRILLIC SMALL LETTER HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA;;0425;;0425 +0446;CYRILLIC SMALL LETTER TSE;Ll;0;L;;;;;N;;;0426;;0426 +0447;CYRILLIC SMALL LETTER CHE;Ll;0;L;;;;;N;;;0427;;0427 +0448;CYRILLIC SMALL LETTER SHA;Ll;0;L;;;;;N;;;0428;;0428 +0449;CYRILLIC SMALL LETTER SHCHA;Ll;0;L;;;;;N;;;0429;;0429 +044A;CYRILLIC SMALL LETTER HARD SIGN;Ll;0;L;;;;;N;;;042A;;042A +044B;CYRILLIC SMALL LETTER YERU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER YERI;;042B;;042B +044C;CYRILLIC SMALL LETTER SOFT SIGN;Ll;0;L;;;;;N;;;042C;;042C +044D;CYRILLIC SMALL LETTER E;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED E;;042D;;042D +044E;CYRILLIC SMALL LETTER YU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IU;;042E;;042E +044F;CYRILLIC SMALL LETTER YA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IA;;042F;;042F +0450;CYRILLIC SMALL LETTER IE WITH GRAVE;Ll;0;L;0435 0300;;;;N;;;0400;;0400 +0451;CYRILLIC SMALL LETTER IO;Ll;0;L;0435 0308;;;;N;;;0401;;0401 +0452;CYRILLIC SMALL LETTER DJE;Ll;0;L;;;;;N;;;0402;;0402 +0453;CYRILLIC SMALL LETTER GJE;Ll;0;L;0433 0301;;;;N;;;0403;;0403 +0454;CYRILLIC SMALL LETTER UKRAINIAN IE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER E;;0404;;0404 +0455;CYRILLIC SMALL LETTER DZE;Ll;0;L;;;;;N;;;0405;;0405 +0456;CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER I;;0406;;0406 +0457;CYRILLIC SMALL LETTER YI;Ll;0;L;0456 0308;;;;N;;;0407;;0407 +0458;CYRILLIC SMALL LETTER JE;Ll;0;L;;;;;N;;;0408;;0408 +0459;CYRILLIC SMALL LETTER LJE;Ll;0;L;;;;;N;;;0409;;0409 +045A;CYRILLIC SMALL LETTER NJE;Ll;0;L;;;;;N;;;040A;;040A +045B;CYRILLIC SMALL LETTER TSHE;Ll;0;L;;;;;N;;;040B;;040B +045C;CYRILLIC SMALL LETTER KJE;Ll;0;L;043A 0301;;;;N;;;040C;;040C +045D;CYRILLIC SMALL LETTER I WITH GRAVE;Ll;0;L;0438 0300;;;;N;;;040D;;040D +045E;CYRILLIC SMALL LETTER SHORT U;Ll;0;L;0443 0306;;;;N;;;040E;;040E +045F;CYRILLIC SMALL LETTER DZHE;Ll;0;L;;;;;N;;;040F;;040F +0460;CYRILLIC CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;0461; +0461;CYRILLIC SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;0460;;0460 +0462;CYRILLIC CAPITAL LETTER YAT;Lu;0;L;;;;;N;;;;0463; +0463;CYRILLIC SMALL LETTER YAT;Ll;0;L;;;;;N;;;0462;;0462 +0464;CYRILLIC CAPITAL LETTER IOTIFIED E;Lu;0;L;;;;;N;;;;0465; +0465;CYRILLIC SMALL LETTER IOTIFIED E;Ll;0;L;;;;;N;;;0464;;0464 +0466;CYRILLIC CAPITAL LETTER LITTLE YUS;Lu;0;L;;;;;N;;;;0467; +0467;CYRILLIC SMALL LETTER LITTLE YUS;Ll;0;L;;;;;N;;;0466;;0466 +0468;CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS;Lu;0;L;;;;;N;;;;0469; +0469;CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS;Ll;0;L;;;;;N;;;0468;;0468 +046A;CYRILLIC CAPITAL LETTER BIG YUS;Lu;0;L;;;;;N;;;;046B; +046B;CYRILLIC SMALL LETTER BIG YUS;Ll;0;L;;;;;N;;;046A;;046A +046C;CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS;Lu;0;L;;;;;N;;;;046D; +046D;CYRILLIC SMALL LETTER IOTIFIED BIG YUS;Ll;0;L;;;;;N;;;046C;;046C +046E;CYRILLIC CAPITAL LETTER KSI;Lu;0;L;;;;;N;;;;046F; +046F;CYRILLIC SMALL LETTER KSI;Ll;0;L;;;;;N;;;046E;;046E +0470;CYRILLIC CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;0471; +0471;CYRILLIC SMALL LETTER PSI;Ll;0;L;;;;;N;;;0470;;0470 +0472;CYRILLIC CAPITAL LETTER FITA;Lu;0;L;;;;;N;;;;0473; +0473;CYRILLIC SMALL LETTER FITA;Ll;0;L;;;;;N;;;0472;;0472 +0474;CYRILLIC CAPITAL LETTER IZHITSA;Lu;0;L;;;;;N;;;;0475; +0475;CYRILLIC SMALL LETTER IZHITSA;Ll;0;L;;;;;N;;;0474;;0474 +0476;CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT;Lu;0;L;0474 030F;;;;N;CYRILLIC CAPITAL LETTER IZHITSA DOUBLE GRAVE;;;0477; +0477;CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT;Ll;0;L;0475 030F;;;;N;CYRILLIC SMALL LETTER IZHITSA DOUBLE GRAVE;;0476;;0476 +0478;CYRILLIC CAPITAL LETTER UK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER UK DIGRAPH;;;0479; +0479;CYRILLIC SMALL LETTER UK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER UK DIGRAPH;;0478;;0478 +047A;CYRILLIC CAPITAL LETTER ROUND OMEGA;Lu;0;L;;;;;N;;;;047B; +047B;CYRILLIC SMALL LETTER ROUND OMEGA;Ll;0;L;;;;;N;;;047A;;047A +047C;CYRILLIC CAPITAL LETTER OMEGA WITH TITLO;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER OMEGA TITLO;;;047D; +047D;CYRILLIC SMALL LETTER OMEGA WITH TITLO;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER OMEGA TITLO;;047C;;047C +047E;CYRILLIC CAPITAL LETTER OT;Lu;0;L;;;;;N;;;;047F; +047F;CYRILLIC SMALL LETTER OT;Ll;0;L;;;;;N;;;047E;;047E +0480;CYRILLIC CAPITAL LETTER KOPPA;Lu;0;L;;;;;N;;;;0481; +0481;CYRILLIC SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;0480;;0480 +0482;CYRILLIC THOUSANDS SIGN;So;0;L;;;;;N;;;;; +0483;COMBINING CYRILLIC TITLO;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING TITLO;;;; +0484;COMBINING CYRILLIC PALATALIZATION;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PALATALIZATION;;;; +0485;COMBINING CYRILLIC DASIA PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING DASIA PNEUMATA;;;; +0486;COMBINING CYRILLIC PSILI PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PSILI PNEUMATA;;;; +0487;COMBINING CYRILLIC POKRYTIE;Mn;230;NSM;;;;;N;;;;; +0488;COMBINING CYRILLIC HUNDRED THOUSANDS SIGN;Me;0;NSM;;;;;N;;;;; +0489;COMBINING CYRILLIC MILLIONS SIGN;Me;0;NSM;;;;;N;;;;; +048A;CYRILLIC CAPITAL LETTER SHORT I WITH TAIL;Lu;0;L;;;;;N;;;;048B; +048B;CYRILLIC SMALL LETTER SHORT I WITH TAIL;Ll;0;L;;;;;N;;;048A;;048A +048C;CYRILLIC CAPITAL LETTER SEMISOFT SIGN;Lu;0;L;;;;;N;;;;048D; +048D;CYRILLIC SMALL LETTER SEMISOFT SIGN;Ll;0;L;;;;;N;;;048C;;048C +048E;CYRILLIC CAPITAL LETTER ER WITH TICK;Lu;0;L;;;;;N;;;;048F; +048F;CYRILLIC SMALL LETTER ER WITH TICK;Ll;0;L;;;;;N;;;048E;;048E +0490;CYRILLIC CAPITAL LETTER GHE WITH UPTURN;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE WITH UPTURN;;;0491; +0491;CYRILLIC SMALL LETTER GHE WITH UPTURN;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE WITH UPTURN;;0490;;0490 +0492;CYRILLIC CAPITAL LETTER GHE WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE BAR;;;0493; +0493;CYRILLIC SMALL LETTER GHE WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE BAR;;0492;;0492 +0494;CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE HOOK;;;0495; +0495;CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE HOOK;;0494;;0494 +0496;CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ZHE WITH RIGHT DESCENDER;;;0497; +0497;CYRILLIC SMALL LETTER ZHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ZHE WITH RIGHT DESCENDER;;0496;;0496 +0498;CYRILLIC CAPITAL LETTER ZE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ZE CEDILLA;;;0499; +0499;CYRILLIC SMALL LETTER ZE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ZE CEDILLA;;0498;;0498 +049A;CYRILLIC CAPITAL LETTER KA WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA WITH RIGHT DESCENDER;;;049B; +049B;CYRILLIC SMALL LETTER KA WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA WITH RIGHT DESCENDER;;049A;;049A +049C;CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA VERTICAL BAR;;;049D; +049D;CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA VERTICAL BAR;;049C;;049C +049E;CYRILLIC CAPITAL LETTER KA WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA BAR;;;049F; +049F;CYRILLIC SMALL LETTER KA WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA BAR;;049E;;049E +04A0;CYRILLIC CAPITAL LETTER BASHKIR KA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED GE KA;;;04A1; +04A1;CYRILLIC SMALL LETTER BASHKIR KA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED GE KA;;04A0;;04A0 +04A2;CYRILLIC CAPITAL LETTER EN WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN WITH RIGHT DESCENDER;;;04A3; +04A3;CYRILLIC SMALL LETTER EN WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN WITH RIGHT DESCENDER;;04A2;;04A2 +04A4;CYRILLIC CAPITAL LIGATURE EN GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN GE;;;04A5; +04A5;CYRILLIC SMALL LIGATURE EN GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN GE;;04A4;;04A4 +04A6;CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER PE HOOK;;;04A7; +04A7;CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER PE HOOK;;04A6;;04A6 +04A8;CYRILLIC CAPITAL LETTER ABKHASIAN HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER O HOOK;;;04A9; +04A9;CYRILLIC SMALL LETTER ABKHASIAN HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER O HOOK;;04A8;;04A8 +04AA;CYRILLIC CAPITAL LETTER ES WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ES CEDILLA;;;04AB; +04AB;CYRILLIC SMALL LETTER ES WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ES CEDILLA;;04AA;;04AA +04AC;CYRILLIC CAPITAL LETTER TE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER TE WITH RIGHT DESCENDER;;;04AD; +04AD;CYRILLIC SMALL LETTER TE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER TE WITH RIGHT DESCENDER;;04AC;;04AC +04AE;CYRILLIC CAPITAL LETTER STRAIGHT U;Lu;0;L;;;;;N;;;;04AF; +04AF;CYRILLIC SMALL LETTER STRAIGHT U;Ll;0;L;;;;;N;;;04AE;;04AE +04B0;CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER STRAIGHT U BAR;;;04B1; +04B1;CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER STRAIGHT U BAR;;04B0;;04B0 +04B2;CYRILLIC CAPITAL LETTER HA WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA WITH RIGHT DESCENDER;;;04B3; +04B3;CYRILLIC SMALL LETTER HA WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA WITH RIGHT DESCENDER;;04B2;;04B2 +04B4;CYRILLIC CAPITAL LIGATURE TE TSE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER TE TSE;;;04B5; +04B5;CYRILLIC SMALL LIGATURE TE TSE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER TE TSE;;04B4;;04B4 +04B6;CYRILLIC CAPITAL LETTER CHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE WITH RIGHT DESCENDER;;;04B7; +04B7;CYRILLIC SMALL LETTER CHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE WITH RIGHT DESCENDER;;04B6;;04B6 +04B8;CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE VERTICAL BAR;;;04B9; +04B9;CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE VERTICAL BAR;;04B8;;04B8 +04BA;CYRILLIC CAPITAL LETTER SHHA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER H;;;04BB; +04BB;CYRILLIC SMALL LETTER SHHA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER H;;04BA;;04BA +04BC;CYRILLIC CAPITAL LETTER ABKHASIAN CHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IE HOOK;;;04BD; +04BD;CYRILLIC SMALL LETTER ABKHASIAN CHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IE HOOK;;04BC;;04BC +04BE;CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IE HOOK OGONEK;;;04BF; +04BF;CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IE HOOK OGONEK;;04BE;;04BE +04C0;CYRILLIC LETTER PALOCHKA;Lu;0;L;;;;;N;CYRILLIC LETTER I;;;04CF; +04C1;CYRILLIC CAPITAL LETTER ZHE WITH BREVE;Lu;0;L;0416 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT ZHE;;;04C2; +04C2;CYRILLIC SMALL LETTER ZHE WITH BREVE;Ll;0;L;0436 0306;;;;N;CYRILLIC SMALL LETTER SHORT ZHE;;04C1;;04C1 +04C3;CYRILLIC CAPITAL LETTER KA WITH HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA HOOK;;;04C4; +04C4;CYRILLIC SMALL LETTER KA WITH HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA HOOK;;04C3;;04C3 +04C5;CYRILLIC CAPITAL LETTER EL WITH TAIL;Lu;0;L;;;;;N;;;;04C6; +04C6;CYRILLIC SMALL LETTER EL WITH TAIL;Ll;0;L;;;;;N;;;04C5;;04C5 +04C7;CYRILLIC CAPITAL LETTER EN WITH HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN HOOK;;;04C8; +04C8;CYRILLIC SMALL LETTER EN WITH HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN HOOK;;04C7;;04C7 +04C9;CYRILLIC CAPITAL LETTER EN WITH TAIL;Lu;0;L;;;;;N;;;;04CA; +04CA;CYRILLIC SMALL LETTER EN WITH TAIL;Ll;0;L;;;;;N;;;04C9;;04C9 +04CB;CYRILLIC CAPITAL LETTER KHAKASSIAN CHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE WITH LEFT DESCENDER;;;04CC; +04CC;CYRILLIC SMALL LETTER KHAKASSIAN CHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE WITH LEFT DESCENDER;;04CB;;04CB +04CD;CYRILLIC CAPITAL LETTER EM WITH TAIL;Lu;0;L;;;;;N;;;;04CE; +04CE;CYRILLIC SMALL LETTER EM WITH TAIL;Ll;0;L;;;;;N;;;04CD;;04CD +04CF;CYRILLIC SMALL LETTER PALOCHKA;Ll;0;L;;;;;N;;;04C0;;04C0 +04D0;CYRILLIC CAPITAL LETTER A WITH BREVE;Lu;0;L;0410 0306;;;;N;;;;04D1; +04D1;CYRILLIC SMALL LETTER A WITH BREVE;Ll;0;L;0430 0306;;;;N;;;04D0;;04D0 +04D2;CYRILLIC CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0410 0308;;;;N;;;;04D3; +04D3;CYRILLIC SMALL LETTER A WITH DIAERESIS;Ll;0;L;0430 0308;;;;N;;;04D2;;04D2 +04D4;CYRILLIC CAPITAL LIGATURE A IE;Lu;0;L;;;;;N;;;;04D5; +04D5;CYRILLIC SMALL LIGATURE A IE;Ll;0;L;;;;;N;;;04D4;;04D4 +04D6;CYRILLIC CAPITAL LETTER IE WITH BREVE;Lu;0;L;0415 0306;;;;N;;;;04D7; +04D7;CYRILLIC SMALL LETTER IE WITH BREVE;Ll;0;L;0435 0306;;;;N;;;04D6;;04D6 +04D8;CYRILLIC CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;04D9; +04D9;CYRILLIC SMALL LETTER SCHWA;Ll;0;L;;;;;N;;;04D8;;04D8 +04DA;CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS;Lu;0;L;04D8 0308;;;;N;;;;04DB; +04DB;CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS;Ll;0;L;04D9 0308;;;;N;;;04DA;;04DA +04DC;CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS;Lu;0;L;0416 0308;;;;N;;;;04DD; +04DD;CYRILLIC SMALL LETTER ZHE WITH DIAERESIS;Ll;0;L;0436 0308;;;;N;;;04DC;;04DC +04DE;CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS;Lu;0;L;0417 0308;;;;N;;;;04DF; +04DF;CYRILLIC SMALL LETTER ZE WITH DIAERESIS;Ll;0;L;0437 0308;;;;N;;;04DE;;04DE +04E0;CYRILLIC CAPITAL LETTER ABKHASIAN DZE;Lu;0;L;;;;;N;;;;04E1; +04E1;CYRILLIC SMALL LETTER ABKHASIAN DZE;Ll;0;L;;;;;N;;;04E0;;04E0 +04E2;CYRILLIC CAPITAL LETTER I WITH MACRON;Lu;0;L;0418 0304;;;;N;;;;04E3; +04E3;CYRILLIC SMALL LETTER I WITH MACRON;Ll;0;L;0438 0304;;;;N;;;04E2;;04E2 +04E4;CYRILLIC CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0418 0308;;;;N;;;;04E5; +04E5;CYRILLIC SMALL LETTER I WITH DIAERESIS;Ll;0;L;0438 0308;;;;N;;;04E4;;04E4 +04E6;CYRILLIC CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;041E 0308;;;;N;;;;04E7; +04E7;CYRILLIC SMALL LETTER O WITH DIAERESIS;Ll;0;L;043E 0308;;;;N;;;04E6;;04E6 +04E8;CYRILLIC CAPITAL LETTER BARRED O;Lu;0;L;;;;;N;;;;04E9; +04E9;CYRILLIC SMALL LETTER BARRED O;Ll;0;L;;;;;N;;;04E8;;04E8 +04EA;CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS;Lu;0;L;04E8 0308;;;;N;;;;04EB; +04EB;CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS;Ll;0;L;04E9 0308;;;;N;;;04EA;;04EA +04EC;CYRILLIC CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;042D 0308;;;;N;;;;04ED; +04ED;CYRILLIC SMALL LETTER E WITH DIAERESIS;Ll;0;L;044D 0308;;;;N;;;04EC;;04EC +04EE;CYRILLIC CAPITAL LETTER U WITH MACRON;Lu;0;L;0423 0304;;;;N;;;;04EF; +04EF;CYRILLIC SMALL LETTER U WITH MACRON;Ll;0;L;0443 0304;;;;N;;;04EE;;04EE +04F0;CYRILLIC CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0423 0308;;;;N;;;;04F1; +04F1;CYRILLIC SMALL LETTER U WITH DIAERESIS;Ll;0;L;0443 0308;;;;N;;;04F0;;04F0 +04F2;CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0423 030B;;;;N;;;;04F3; +04F3;CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0443 030B;;;;N;;;04F2;;04F2 +04F4;CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS;Lu;0;L;0427 0308;;;;N;;;;04F5; +04F5;CYRILLIC SMALL LETTER CHE WITH DIAERESIS;Ll;0;L;0447 0308;;;;N;;;04F4;;04F4 +04F6;CYRILLIC CAPITAL LETTER GHE WITH DESCENDER;Lu;0;L;;;;;N;;;;04F7; +04F7;CYRILLIC SMALL LETTER GHE WITH DESCENDER;Ll;0;L;;;;;N;;;04F6;;04F6 +04F8;CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS;Lu;0;L;042B 0308;;;;N;;;;04F9; +04F9;CYRILLIC SMALL LETTER YERU WITH DIAERESIS;Ll;0;L;044B 0308;;;;N;;;04F8;;04F8 +04FA;CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK;Lu;0;L;;;;;N;;;;04FB; +04FB;CYRILLIC SMALL LETTER GHE WITH STROKE AND HOOK;Ll;0;L;;;;;N;;;04FA;;04FA +04FC;CYRILLIC CAPITAL LETTER HA WITH HOOK;Lu;0;L;;;;;N;;;;04FD; +04FD;CYRILLIC SMALL LETTER HA WITH HOOK;Ll;0;L;;;;;N;;;04FC;;04FC +04FE;CYRILLIC CAPITAL LETTER HA WITH STROKE;Lu;0;L;;;;;N;;;;04FF; +04FF;CYRILLIC SMALL LETTER HA WITH STROKE;Ll;0;L;;;;;N;;;04FE;;04FE +0500;CYRILLIC CAPITAL LETTER KOMI DE;Lu;0;L;;;;;N;;;;0501; +0501;CYRILLIC SMALL LETTER KOMI DE;Ll;0;L;;;;;N;;;0500;;0500 +0502;CYRILLIC CAPITAL LETTER KOMI DJE;Lu;0;L;;;;;N;;;;0503; +0503;CYRILLIC SMALL LETTER KOMI DJE;Ll;0;L;;;;;N;;;0502;;0502 +0504;CYRILLIC CAPITAL LETTER KOMI ZJE;Lu;0;L;;;;;N;;;;0505; +0505;CYRILLIC SMALL LETTER KOMI ZJE;Ll;0;L;;;;;N;;;0504;;0504 +0506;CYRILLIC CAPITAL LETTER KOMI DZJE;Lu;0;L;;;;;N;;;;0507; +0507;CYRILLIC SMALL LETTER KOMI DZJE;Ll;0;L;;;;;N;;;0506;;0506 +0508;CYRILLIC CAPITAL LETTER KOMI LJE;Lu;0;L;;;;;N;;;;0509; +0509;CYRILLIC SMALL LETTER KOMI LJE;Ll;0;L;;;;;N;;;0508;;0508 +050A;CYRILLIC CAPITAL LETTER KOMI NJE;Lu;0;L;;;;;N;;;;050B; +050B;CYRILLIC SMALL LETTER KOMI NJE;Ll;0;L;;;;;N;;;050A;;050A +050C;CYRILLIC CAPITAL LETTER KOMI SJE;Lu;0;L;;;;;N;;;;050D; +050D;CYRILLIC SMALL LETTER KOMI SJE;Ll;0;L;;;;;N;;;050C;;050C +050E;CYRILLIC CAPITAL LETTER KOMI TJE;Lu;0;L;;;;;N;;;;050F; +050F;CYRILLIC SMALL LETTER KOMI TJE;Ll;0;L;;;;;N;;;050E;;050E +0510;CYRILLIC CAPITAL LETTER REVERSED ZE;Lu;0;L;;;;;N;;;;0511; +0511;CYRILLIC SMALL LETTER REVERSED ZE;Ll;0;L;;;;;N;;;0510;;0510 +0512;CYRILLIC CAPITAL LETTER EL WITH HOOK;Lu;0;L;;;;;N;;;;0513; +0513;CYRILLIC SMALL LETTER EL WITH HOOK;Ll;0;L;;;;;N;;;0512;;0512 +0514;CYRILLIC CAPITAL LETTER LHA;Lu;0;L;;;;;N;;;;0515; +0515;CYRILLIC SMALL LETTER LHA;Ll;0;L;;;;;N;;;0514;;0514 +0516;CYRILLIC CAPITAL LETTER RHA;Lu;0;L;;;;;N;;;;0517; +0517;CYRILLIC SMALL LETTER RHA;Ll;0;L;;;;;N;;;0516;;0516 +0518;CYRILLIC CAPITAL LETTER YAE;Lu;0;L;;;;;N;;;;0519; +0519;CYRILLIC SMALL LETTER YAE;Ll;0;L;;;;;N;;;0518;;0518 +051A;CYRILLIC CAPITAL LETTER QA;Lu;0;L;;;;;N;;;;051B; +051B;CYRILLIC SMALL LETTER QA;Ll;0;L;;;;;N;;;051A;;051A +051C;CYRILLIC CAPITAL LETTER WE;Lu;0;L;;;;;N;;;;051D; +051D;CYRILLIC SMALL LETTER WE;Ll;0;L;;;;;N;;;051C;;051C +051E;CYRILLIC CAPITAL LETTER ALEUT KA;Lu;0;L;;;;;N;;;;051F; +051F;CYRILLIC SMALL LETTER ALEUT KA;Ll;0;L;;;;;N;;;051E;;051E +0520;CYRILLIC CAPITAL LETTER EL WITH MIDDLE HOOK;Lu;0;L;;;;;N;;;;0521; +0521;CYRILLIC SMALL LETTER EL WITH MIDDLE HOOK;Ll;0;L;;;;;N;;;0520;;0520 +0522;CYRILLIC CAPITAL LETTER EN WITH MIDDLE HOOK;Lu;0;L;;;;;N;;;;0523; +0523;CYRILLIC SMALL LETTER EN WITH MIDDLE HOOK;Ll;0;L;;;;;N;;;0522;;0522 +0524;CYRILLIC CAPITAL LETTER PE WITH DESCENDER;Lu;0;L;;;;;N;;;;0525; +0525;CYRILLIC SMALL LETTER PE WITH DESCENDER;Ll;0;L;;;;;N;;;0524;;0524 +0526;CYRILLIC CAPITAL LETTER SHHA WITH DESCENDER;Lu;0;L;;;;;N;;;;0527; +0527;CYRILLIC SMALL LETTER SHHA WITH DESCENDER;Ll;0;L;;;;;N;;;0526;;0526 +0528;CYRILLIC CAPITAL LETTER EN WITH LEFT HOOK;Lu;0;L;;;;;N;;;;0529; +0529;CYRILLIC SMALL LETTER EN WITH LEFT HOOK;Ll;0;L;;;;;N;;;0528;;0528 +052A;CYRILLIC CAPITAL LETTER DZZHE;Lu;0;L;;;;;N;;;;052B; +052B;CYRILLIC SMALL LETTER DZZHE;Ll;0;L;;;;;N;;;052A;;052A +052C;CYRILLIC CAPITAL LETTER DCHE;Lu;0;L;;;;;N;;;;052D; +052D;CYRILLIC SMALL LETTER DCHE;Ll;0;L;;;;;N;;;052C;;052C +052E;CYRILLIC CAPITAL LETTER EL WITH DESCENDER;Lu;0;L;;;;;N;;;;052F; +052F;CYRILLIC SMALL LETTER EL WITH DESCENDER;Ll;0;L;;;;;N;;;052E;;052E +0531;ARMENIAN CAPITAL LETTER AYB;Lu;0;L;;;;;N;;;;0561; +0532;ARMENIAN CAPITAL LETTER BEN;Lu;0;L;;;;;N;;;;0562; +0533;ARMENIAN CAPITAL LETTER GIM;Lu;0;L;;;;;N;;;;0563; +0534;ARMENIAN CAPITAL LETTER DA;Lu;0;L;;;;;N;;;;0564; +0535;ARMENIAN CAPITAL LETTER ECH;Lu;0;L;;;;;N;;;;0565; +0536;ARMENIAN CAPITAL LETTER ZA;Lu;0;L;;;;;N;;;;0566; +0537;ARMENIAN CAPITAL LETTER EH;Lu;0;L;;;;;N;;;;0567; +0538;ARMENIAN CAPITAL LETTER ET;Lu;0;L;;;;;N;;;;0568; +0539;ARMENIAN CAPITAL LETTER TO;Lu;0;L;;;;;N;;;;0569; +053A;ARMENIAN CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;056A; +053B;ARMENIAN CAPITAL LETTER INI;Lu;0;L;;;;;N;;;;056B; +053C;ARMENIAN CAPITAL LETTER LIWN;Lu;0;L;;;;;N;;;;056C; +053D;ARMENIAN CAPITAL LETTER XEH;Lu;0;L;;;;;N;;;;056D; +053E;ARMENIAN CAPITAL LETTER CA;Lu;0;L;;;;;N;;;;056E; +053F;ARMENIAN CAPITAL LETTER KEN;Lu;0;L;;;;;N;;;;056F; +0540;ARMENIAN CAPITAL LETTER HO;Lu;0;L;;;;;N;;;;0570; +0541;ARMENIAN CAPITAL LETTER JA;Lu;0;L;;;;;N;;;;0571; +0542;ARMENIAN CAPITAL LETTER GHAD;Lu;0;L;;;;;N;ARMENIAN CAPITAL LETTER LAD;;;0572; +0543;ARMENIAN CAPITAL LETTER CHEH;Lu;0;L;;;;;N;;;;0573; +0544;ARMENIAN CAPITAL LETTER MEN;Lu;0;L;;;;;N;;;;0574; +0545;ARMENIAN CAPITAL LETTER YI;Lu;0;L;;;;;N;;;;0575; +0546;ARMENIAN CAPITAL LETTER NOW;Lu;0;L;;;;;N;;;;0576; +0547;ARMENIAN CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0577; +0548;ARMENIAN CAPITAL LETTER VO;Lu;0;L;;;;;N;;;;0578; +0549;ARMENIAN CAPITAL LETTER CHA;Lu;0;L;;;;;N;;;;0579; +054A;ARMENIAN CAPITAL LETTER PEH;Lu;0;L;;;;;N;;;;057A; +054B;ARMENIAN CAPITAL LETTER JHEH;Lu;0;L;;;;;N;;;;057B; +054C;ARMENIAN CAPITAL LETTER RA;Lu;0;L;;;;;N;;;;057C; +054D;ARMENIAN CAPITAL LETTER SEH;Lu;0;L;;;;;N;;;;057D; +054E;ARMENIAN CAPITAL LETTER VEW;Lu;0;L;;;;;N;;;;057E; +054F;ARMENIAN CAPITAL LETTER TIWN;Lu;0;L;;;;;N;;;;057F; +0550;ARMENIAN CAPITAL LETTER REH;Lu;0;L;;;;;N;;;;0580; +0551;ARMENIAN CAPITAL LETTER CO;Lu;0;L;;;;;N;;;;0581; +0552;ARMENIAN CAPITAL LETTER YIWN;Lu;0;L;;;;;N;;;;0582; +0553;ARMENIAN CAPITAL LETTER PIWR;Lu;0;L;;;;;N;;;;0583; +0554;ARMENIAN CAPITAL LETTER KEH;Lu;0;L;;;;;N;;;;0584; +0555;ARMENIAN CAPITAL LETTER OH;Lu;0;L;;;;;N;;;;0585; +0556;ARMENIAN CAPITAL LETTER FEH;Lu;0;L;;;;;N;;;;0586; +0559;ARMENIAN MODIFIER LETTER LEFT HALF RING;Lm;0;L;;;;;N;;;;; +055A;ARMENIAN APOSTROPHE;Po;0;L;;;;;N;ARMENIAN MODIFIER LETTER RIGHT HALF RING;;;; +055B;ARMENIAN EMPHASIS MARK;Po;0;L;;;;;N;;;;; +055C;ARMENIAN EXCLAMATION MARK;Po;0;L;;;;;N;;;;; +055D;ARMENIAN COMMA;Po;0;L;;;;;N;;;;; +055E;ARMENIAN QUESTION MARK;Po;0;L;;;;;N;;;;; +055F;ARMENIAN ABBREVIATION MARK;Po;0;L;;;;;N;;;;; +0561;ARMENIAN SMALL LETTER AYB;Ll;0;L;;;;;N;;;0531;;0531 +0562;ARMENIAN SMALL LETTER BEN;Ll;0;L;;;;;N;;;0532;;0532 +0563;ARMENIAN SMALL LETTER GIM;Ll;0;L;;;;;N;;;0533;;0533 +0564;ARMENIAN SMALL LETTER DA;Ll;0;L;;;;;N;;;0534;;0534 +0565;ARMENIAN SMALL LETTER ECH;Ll;0;L;;;;;N;;;0535;;0535 +0566;ARMENIAN SMALL LETTER ZA;Ll;0;L;;;;;N;;;0536;;0536 +0567;ARMENIAN SMALL LETTER EH;Ll;0;L;;;;;N;;;0537;;0537 +0568;ARMENIAN SMALL LETTER ET;Ll;0;L;;;;;N;;;0538;;0538 +0569;ARMENIAN SMALL LETTER TO;Ll;0;L;;;;;N;;;0539;;0539 +056A;ARMENIAN SMALL LETTER ZHE;Ll;0;L;;;;;N;;;053A;;053A +056B;ARMENIAN SMALL LETTER INI;Ll;0;L;;;;;N;;;053B;;053B +056C;ARMENIAN SMALL LETTER LIWN;Ll;0;L;;;;;N;;;053C;;053C +056D;ARMENIAN SMALL LETTER XEH;Ll;0;L;;;;;N;;;053D;;053D +056E;ARMENIAN SMALL LETTER CA;Ll;0;L;;;;;N;;;053E;;053E +056F;ARMENIAN SMALL LETTER KEN;Ll;0;L;;;;;N;;;053F;;053F +0570;ARMENIAN SMALL LETTER HO;Ll;0;L;;;;;N;;;0540;;0540 +0571;ARMENIAN SMALL LETTER JA;Ll;0;L;;;;;N;;;0541;;0541 +0572;ARMENIAN SMALL LETTER GHAD;Ll;0;L;;;;;N;ARMENIAN SMALL LETTER LAD;;0542;;0542 +0573;ARMENIAN SMALL LETTER CHEH;Ll;0;L;;;;;N;;;0543;;0543 +0574;ARMENIAN SMALL LETTER MEN;Ll;0;L;;;;;N;;;0544;;0544 +0575;ARMENIAN SMALL LETTER YI;Ll;0;L;;;;;N;;;0545;;0545 +0576;ARMENIAN SMALL LETTER NOW;Ll;0;L;;;;;N;;;0546;;0546 +0577;ARMENIAN SMALL LETTER SHA;Ll;0;L;;;;;N;;;0547;;0547 +0578;ARMENIAN SMALL LETTER VO;Ll;0;L;;;;;N;;;0548;;0548 +0579;ARMENIAN SMALL LETTER CHA;Ll;0;L;;;;;N;;;0549;;0549 +057A;ARMENIAN SMALL LETTER PEH;Ll;0;L;;;;;N;;;054A;;054A +057B;ARMENIAN SMALL LETTER JHEH;Ll;0;L;;;;;N;;;054B;;054B +057C;ARMENIAN SMALL LETTER RA;Ll;0;L;;;;;N;;;054C;;054C +057D;ARMENIAN SMALL LETTER SEH;Ll;0;L;;;;;N;;;054D;;054D +057E;ARMENIAN SMALL LETTER VEW;Ll;0;L;;;;;N;;;054E;;054E +057F;ARMENIAN SMALL LETTER TIWN;Ll;0;L;;;;;N;;;054F;;054F +0580;ARMENIAN SMALL LETTER REH;Ll;0;L;;;;;N;;;0550;;0550 +0581;ARMENIAN SMALL LETTER CO;Ll;0;L;;;;;N;;;0551;;0551 +0582;ARMENIAN SMALL LETTER YIWN;Ll;0;L;;;;;N;;;0552;;0552 +0583;ARMENIAN SMALL LETTER PIWR;Ll;0;L;;;;;N;;;0553;;0553 +0584;ARMENIAN SMALL LETTER KEH;Ll;0;L;;;;;N;;;0554;;0554 +0585;ARMENIAN SMALL LETTER OH;Ll;0;L;;;;;N;;;0555;;0555 +0586;ARMENIAN SMALL LETTER FEH;Ll;0;L;;;;;N;;;0556;;0556 +0587;ARMENIAN SMALL LIGATURE ECH YIWN;Ll;0;L;<compat> 0565 0582;;;;N;;;;; +0589;ARMENIAN FULL STOP;Po;0;L;;;;;N;ARMENIAN PERIOD;;;; +058A;ARMENIAN HYPHEN;Pd;0;ON;;;;;N;;;;; +058D;RIGHT-FACING ARMENIAN ETERNITY SIGN;So;0;ON;;;;;N;;;;; +058E;LEFT-FACING ARMENIAN ETERNITY SIGN;So;0;ON;;;;;N;;;;; +058F;ARMENIAN DRAM SIGN;Sc;0;ET;;;;;N;;;;; +0591;HEBREW ACCENT ETNAHTA;Mn;220;NSM;;;;;N;;;;; +0592;HEBREW ACCENT SEGOL;Mn;230;NSM;;;;;N;;;;; +0593;HEBREW ACCENT SHALSHELET;Mn;230;NSM;;;;;N;;;;; +0594;HEBREW ACCENT ZAQEF QATAN;Mn;230;NSM;;;;;N;;;;; +0595;HEBREW ACCENT ZAQEF GADOL;Mn;230;NSM;;;;;N;;;;; +0596;HEBREW ACCENT TIPEHA;Mn;220;NSM;;;;;N;;;;; +0597;HEBREW ACCENT REVIA;Mn;230;NSM;;;;;N;;;;; +0598;HEBREW ACCENT ZARQA;Mn;230;NSM;;;;;N;;;;; +0599;HEBREW ACCENT PASHTA;Mn;230;NSM;;;;;N;;;;; +059A;HEBREW ACCENT YETIV;Mn;222;NSM;;;;;N;;;;; +059B;HEBREW ACCENT TEVIR;Mn;220;NSM;;;;;N;;;;; +059C;HEBREW ACCENT GERESH;Mn;230;NSM;;;;;N;;;;; +059D;HEBREW ACCENT GERESH MUQDAM;Mn;230;NSM;;;;;N;;;;; +059E;HEBREW ACCENT GERSHAYIM;Mn;230;NSM;;;;;N;;;;; +059F;HEBREW ACCENT QARNEY PARA;Mn;230;NSM;;;;;N;;;;; +05A0;HEBREW ACCENT TELISHA GEDOLA;Mn;230;NSM;;;;;N;;;;; +05A1;HEBREW ACCENT PAZER;Mn;230;NSM;;;;;N;;;;; +05A2;HEBREW ACCENT ATNAH HAFUKH;Mn;220;NSM;;;;;N;;;;; +05A3;HEBREW ACCENT MUNAH;Mn;220;NSM;;;;;N;;;;; +05A4;HEBREW ACCENT MAHAPAKH;Mn;220;NSM;;;;;N;;;;; +05A5;HEBREW ACCENT MERKHA;Mn;220;NSM;;;;;N;;;;; +05A6;HEBREW ACCENT MERKHA KEFULA;Mn;220;NSM;;;;;N;;;;; +05A7;HEBREW ACCENT DARGA;Mn;220;NSM;;;;;N;;;;; +05A8;HEBREW ACCENT QADMA;Mn;230;NSM;;;;;N;;;;; +05A9;HEBREW ACCENT TELISHA QETANA;Mn;230;NSM;;;;;N;;;;; +05AA;HEBREW ACCENT YERAH BEN YOMO;Mn;220;NSM;;;;;N;;;;; +05AB;HEBREW ACCENT OLE;Mn;230;NSM;;;;;N;;;;; +05AC;HEBREW ACCENT ILUY;Mn;230;NSM;;;;;N;;;;; +05AD;HEBREW ACCENT DEHI;Mn;222;NSM;;;;;N;;;;; +05AE;HEBREW ACCENT ZINOR;Mn;228;NSM;;;;;N;;;;; +05AF;HEBREW MARK MASORA CIRCLE;Mn;230;NSM;;;;;N;;;;; +05B0;HEBREW POINT SHEVA;Mn;10;NSM;;;;;N;;;;; +05B1;HEBREW POINT HATAF SEGOL;Mn;11;NSM;;;;;N;;;;; +05B2;HEBREW POINT HATAF PATAH;Mn;12;NSM;;;;;N;;;;; +05B3;HEBREW POINT HATAF QAMATS;Mn;13;NSM;;;;;N;;;;; +05B4;HEBREW POINT HIRIQ;Mn;14;NSM;;;;;N;;;;; +05B5;HEBREW POINT TSERE;Mn;15;NSM;;;;;N;;;;; +05B6;HEBREW POINT SEGOL;Mn;16;NSM;;;;;N;;;;; +05B7;HEBREW POINT PATAH;Mn;17;NSM;;;;;N;;;;; +05B8;HEBREW POINT QAMATS;Mn;18;NSM;;;;;N;;;;; +05B9;HEBREW POINT HOLAM;Mn;19;NSM;;;;;N;;;;; +05BA;HEBREW POINT HOLAM HASER FOR VAV;Mn;19;NSM;;;;;N;;;;; +05BB;HEBREW POINT QUBUTS;Mn;20;NSM;;;;;N;;;;; +05BC;HEBREW POINT DAGESH OR MAPIQ;Mn;21;NSM;;;;;N;HEBREW POINT DAGESH;;;; +05BD;HEBREW POINT METEG;Mn;22;NSM;;;;;N;;;;; +05BE;HEBREW PUNCTUATION MAQAF;Pd;0;R;;;;;N;;;;; +05BF;HEBREW POINT RAFE;Mn;23;NSM;;;;;N;;;;; +05C0;HEBREW PUNCTUATION PASEQ;Po;0;R;;;;;N;HEBREW POINT PASEQ;;;; +05C1;HEBREW POINT SHIN DOT;Mn;24;NSM;;;;;N;;;;; +05C2;HEBREW POINT SIN DOT;Mn;25;NSM;;;;;N;;;;; +05C3;HEBREW PUNCTUATION SOF PASUQ;Po;0;R;;;;;N;;;;; +05C4;HEBREW MARK UPPER DOT;Mn;230;NSM;;;;;N;;;;; +05C5;HEBREW MARK LOWER DOT;Mn;220;NSM;;;;;N;;;;; +05C6;HEBREW PUNCTUATION NUN HAFUKHA;Po;0;R;;;;;N;;;;; +05C7;HEBREW POINT QAMATS QATAN;Mn;18;NSM;;;;;N;;;;; +05D0;HEBREW LETTER ALEF;Lo;0;R;;;;;N;;;;; +05D1;HEBREW LETTER BET;Lo;0;R;;;;;N;;;;; +05D2;HEBREW LETTER GIMEL;Lo;0;R;;;;;N;;;;; +05D3;HEBREW LETTER DALET;Lo;0;R;;;;;N;;;;; +05D4;HEBREW LETTER HE;Lo;0;R;;;;;N;;;;; +05D5;HEBREW LETTER VAV;Lo;0;R;;;;;N;;;;; +05D6;HEBREW LETTER ZAYIN;Lo;0;R;;;;;N;;;;; +05D7;HEBREW LETTER HET;Lo;0;R;;;;;N;;;;; +05D8;HEBREW LETTER TET;Lo;0;R;;;;;N;;;;; +05D9;HEBREW LETTER YOD;Lo;0;R;;;;;N;;;;; +05DA;HEBREW LETTER FINAL KAF;Lo;0;R;;;;;N;;;;; +05DB;HEBREW LETTER KAF;Lo;0;R;;;;;N;;;;; +05DC;HEBREW LETTER LAMED;Lo;0;R;;;;;N;;;;; +05DD;HEBREW LETTER FINAL MEM;Lo;0;R;;;;;N;;;;; +05DE;HEBREW LETTER MEM;Lo;0;R;;;;;N;;;;; +05DF;HEBREW LETTER FINAL NUN;Lo;0;R;;;;;N;;;;; +05E0;HEBREW LETTER NUN;Lo;0;R;;;;;N;;;;; +05E1;HEBREW LETTER SAMEKH;Lo;0;R;;;;;N;;;;; +05E2;HEBREW LETTER AYIN;Lo;0;R;;;;;N;;;;; +05E3;HEBREW LETTER FINAL PE;Lo;0;R;;;;;N;;;;; +05E4;HEBREW LETTER PE;Lo;0;R;;;;;N;;;;; +05E5;HEBREW LETTER FINAL TSADI;Lo;0;R;;;;;N;;;;; +05E6;HEBREW LETTER TSADI;Lo;0;R;;;;;N;;;;; +05E7;HEBREW LETTER QOF;Lo;0;R;;;;;N;;;;; +05E8;HEBREW LETTER RESH;Lo;0;R;;;;;N;;;;; +05E9;HEBREW LETTER SHIN;Lo;0;R;;;;;N;;;;; +05EA;HEBREW LETTER TAV;Lo;0;R;;;;;N;;;;; +05F0;HEBREW LIGATURE YIDDISH DOUBLE VAV;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE VAV;;;; +05F1;HEBREW LIGATURE YIDDISH VAV YOD;Lo;0;R;;;;;N;HEBREW LETTER VAV YOD;;;; +05F2;HEBREW LIGATURE YIDDISH DOUBLE YOD;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE YOD;;;; +05F3;HEBREW PUNCTUATION GERESH;Po;0;R;;;;;N;;;;; +05F4;HEBREW PUNCTUATION GERSHAYIM;Po;0;R;;;;;N;;;;; +0600;ARABIC NUMBER SIGN;Cf;0;AN;;;;;N;;;;; +0601;ARABIC SIGN SANAH;Cf;0;AN;;;;;N;;;;; +0602;ARABIC FOOTNOTE MARKER;Cf;0;AN;;;;;N;;;;; +0603;ARABIC SIGN SAFHA;Cf;0;AN;;;;;N;;;;; +0604;ARABIC SIGN SAMVAT;Cf;0;AN;;;;;N;;;;; +0605;ARABIC NUMBER MARK ABOVE;Cf;0;AN;;;;;N;;;;; +0606;ARABIC-INDIC CUBE ROOT;Sm;0;ON;;;;;N;;;;; +0607;ARABIC-INDIC FOURTH ROOT;Sm;0;ON;;;;;N;;;;; +0608;ARABIC RAY;Sm;0;AL;;;;;N;;;;; +0609;ARABIC-INDIC PER MILLE SIGN;Po;0;ET;;;;;N;;;;; +060A;ARABIC-INDIC PER TEN THOUSAND SIGN;Po;0;ET;;;;;N;;;;; +060B;AFGHANI SIGN;Sc;0;AL;;;;;N;;;;; +060C;ARABIC COMMA;Po;0;CS;;;;;N;;;;; +060D;ARABIC DATE SEPARATOR;Po;0;AL;;;;;N;;;;; +060E;ARABIC POETIC VERSE SIGN;So;0;ON;;;;;N;;;;; +060F;ARABIC SIGN MISRA;So;0;ON;;;;;N;;;;; +0610;ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM;Mn;230;NSM;;;;;N;;;;; +0611;ARABIC SIGN ALAYHE ASSALLAM;Mn;230;NSM;;;;;N;;;;; +0612;ARABIC SIGN RAHMATULLAH ALAYHE;Mn;230;NSM;;;;;N;;;;; +0613;ARABIC SIGN RADI ALLAHOU ANHU;Mn;230;NSM;;;;;N;;;;; +0614;ARABIC SIGN TAKHALLUS;Mn;230;NSM;;;;;N;;;;; +0615;ARABIC SMALL HIGH TAH;Mn;230;NSM;;;;;N;;;;; +0616;ARABIC SMALL HIGH LIGATURE ALEF WITH LAM WITH YEH;Mn;230;NSM;;;;;N;;;;; +0617;ARABIC SMALL HIGH ZAIN;Mn;230;NSM;;;;;N;;;;; +0618;ARABIC SMALL FATHA;Mn;30;NSM;;;;;N;;;;; +0619;ARABIC SMALL DAMMA;Mn;31;NSM;;;;;N;;;;; +061A;ARABIC SMALL KASRA;Mn;32;NSM;;;;;N;;;;; +061B;ARABIC SEMICOLON;Po;0;AL;;;;;N;;;;; +061C;ARABIC LETTER MARK;Cf;0;AL;;;;;N;;;;; +061E;ARABIC TRIPLE DOT PUNCTUATION MARK;Po;0;AL;;;;;N;;;;; +061F;ARABIC QUESTION MARK;Po;0;AL;;;;;N;;;;; +0620;ARABIC LETTER KASHMIRI YEH;Lo;0;AL;;;;;N;;;;; +0621;ARABIC LETTER HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH;;;; +0622;ARABIC LETTER ALEF WITH MADDA ABOVE;Lo;0;AL;0627 0653;;;;N;ARABIC LETTER MADDAH ON ALEF;;;; +0623;ARABIC LETTER ALEF WITH HAMZA ABOVE;Lo;0;AL;0627 0654;;;;N;ARABIC LETTER HAMZAH ON ALEF;;;; +0624;ARABIC LETTER WAW WITH HAMZA ABOVE;Lo;0;AL;0648 0654;;;;N;ARABIC LETTER HAMZAH ON WAW;;;; +0625;ARABIC LETTER ALEF WITH HAMZA BELOW;Lo;0;AL;0627 0655;;;;N;ARABIC LETTER HAMZAH UNDER ALEF;;;; +0626;ARABIC LETTER YEH WITH HAMZA ABOVE;Lo;0;AL;064A 0654;;;;N;ARABIC LETTER HAMZAH ON YA;;;; +0627;ARABIC LETTER ALEF;Lo;0;AL;;;;;N;;;;; +0628;ARABIC LETTER BEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA;;;; +0629;ARABIC LETTER TEH MARBUTA;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH;;;; +062A;ARABIC LETTER TEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA;;;; +062B;ARABIC LETTER THEH;Lo;0;AL;;;;;N;ARABIC LETTER THAA;;;; +062C;ARABIC LETTER JEEM;Lo;0;AL;;;;;N;;;;; +062D;ARABIC LETTER HAH;Lo;0;AL;;;;;N;ARABIC LETTER HAA;;;; +062E;ARABIC LETTER KHAH;Lo;0;AL;;;;;N;ARABIC LETTER KHAA;;;; +062F;ARABIC LETTER DAL;Lo;0;AL;;;;;N;;;;; +0630;ARABIC LETTER THAL;Lo;0;AL;;;;;N;;;;; +0631;ARABIC LETTER REH;Lo;0;AL;;;;;N;ARABIC LETTER RA;;;; +0632;ARABIC LETTER ZAIN;Lo;0;AL;;;;;N;;;;; +0633;ARABIC LETTER SEEN;Lo;0;AL;;;;;N;;;;; +0634;ARABIC LETTER SHEEN;Lo;0;AL;;;;;N;;;;; +0635;ARABIC LETTER SAD;Lo;0;AL;;;;;N;;;;; +0636;ARABIC LETTER DAD;Lo;0;AL;;;;;N;;;;; +0637;ARABIC LETTER TAH;Lo;0;AL;;;;;N;;;;; +0638;ARABIC LETTER ZAH;Lo;0;AL;;;;;N;ARABIC LETTER DHAH;;;; +0639;ARABIC LETTER AIN;Lo;0;AL;;;;;N;;;;; +063A;ARABIC LETTER GHAIN;Lo;0;AL;;;;;N;;;;; +063B;ARABIC LETTER KEHEH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +063C;ARABIC LETTER KEHEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; +063D;ARABIC LETTER FARSI YEH WITH INVERTED V;Lo;0;AL;;;;;N;;;;; +063E;ARABIC LETTER FARSI YEH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +063F;ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +0640;ARABIC TATWEEL;Lm;0;AL;;;;;N;;;;; +0641;ARABIC LETTER FEH;Lo;0;AL;;;;;N;ARABIC LETTER FA;;;; +0642;ARABIC LETTER QAF;Lo;0;AL;;;;;N;;;;; +0643;ARABIC LETTER KAF;Lo;0;AL;;;;;N;ARABIC LETTER CAF;;;; +0644;ARABIC LETTER LAM;Lo;0;AL;;;;;N;;;;; +0645;ARABIC LETTER MEEM;Lo;0;AL;;;;;N;;;;; +0646;ARABIC LETTER NOON;Lo;0;AL;;;;;N;;;;; +0647;ARABIC LETTER HEH;Lo;0;AL;;;;;N;ARABIC LETTER HA;;;; +0648;ARABIC LETTER WAW;Lo;0;AL;;;;;N;;;;; +0649;ARABIC LETTER ALEF MAKSURA;Lo;0;AL;;;;;N;ARABIC LETTER ALEF MAQSURAH;;;; +064A;ARABIC LETTER YEH;Lo;0;AL;;;;;N;ARABIC LETTER YA;;;; +064B;ARABIC FATHATAN;Mn;27;NSM;;;;;N;;;;; +064C;ARABIC DAMMATAN;Mn;28;NSM;;;;;N;;;;; +064D;ARABIC KASRATAN;Mn;29;NSM;;;;;N;;;;; +064E;ARABIC FATHA;Mn;30;NSM;;;;;N;ARABIC FATHAH;;;; +064F;ARABIC DAMMA;Mn;31;NSM;;;;;N;ARABIC DAMMAH;;;; +0650;ARABIC KASRA;Mn;32;NSM;;;;;N;ARABIC KASRAH;;;; +0651;ARABIC SHADDA;Mn;33;NSM;;;;;N;ARABIC SHADDAH;;;; +0652;ARABIC SUKUN;Mn;34;NSM;;;;;N;;;;; +0653;ARABIC MADDAH ABOVE;Mn;230;NSM;;;;;N;;;;; +0654;ARABIC HAMZA ABOVE;Mn;230;NSM;;;;;N;;;;; +0655;ARABIC HAMZA BELOW;Mn;220;NSM;;;;;N;;;;; +0656;ARABIC SUBSCRIPT ALEF;Mn;220;NSM;;;;;N;;;;; +0657;ARABIC INVERTED DAMMA;Mn;230;NSM;;;;;N;;;;; +0658;ARABIC MARK NOON GHUNNA;Mn;230;NSM;;;;;N;;;;; +0659;ARABIC ZWARAKAY;Mn;230;NSM;;;;;N;;;;; +065A;ARABIC VOWEL SIGN SMALL V ABOVE;Mn;230;NSM;;;;;N;;;;; +065B;ARABIC VOWEL SIGN INVERTED SMALL V ABOVE;Mn;230;NSM;;;;;N;;;;; +065C;ARABIC VOWEL SIGN DOT BELOW;Mn;220;NSM;;;;;N;;;;; +065D;ARABIC REVERSED DAMMA;Mn;230;NSM;;;;;N;;;;; +065E;ARABIC FATHA WITH TWO DOTS;Mn;230;NSM;;;;;N;;;;; +065F;ARABIC WAVY HAMZA BELOW;Mn;220;NSM;;;;;N;;;;; +0660;ARABIC-INDIC DIGIT ZERO;Nd;0;AN;;0;0;0;N;;;;; +0661;ARABIC-INDIC DIGIT ONE;Nd;0;AN;;1;1;1;N;;;;; +0662;ARABIC-INDIC DIGIT TWO;Nd;0;AN;;2;2;2;N;;;;; +0663;ARABIC-INDIC DIGIT THREE;Nd;0;AN;;3;3;3;N;;;;; +0664;ARABIC-INDIC DIGIT FOUR;Nd;0;AN;;4;4;4;N;;;;; +0665;ARABIC-INDIC DIGIT FIVE;Nd;0;AN;;5;5;5;N;;;;; +0666;ARABIC-INDIC DIGIT SIX;Nd;0;AN;;6;6;6;N;;;;; +0667;ARABIC-INDIC DIGIT SEVEN;Nd;0;AN;;7;7;7;N;;;;; +0668;ARABIC-INDIC DIGIT EIGHT;Nd;0;AN;;8;8;8;N;;;;; +0669;ARABIC-INDIC DIGIT NINE;Nd;0;AN;;9;9;9;N;;;;; +066A;ARABIC PERCENT SIGN;Po;0;ET;;;;;N;;;;; +066B;ARABIC DECIMAL SEPARATOR;Po;0;AN;;;;;N;;;;; +066C;ARABIC THOUSANDS SEPARATOR;Po;0;AN;;;;;N;;;;; +066D;ARABIC FIVE POINTED STAR;Po;0;AL;;;;;N;;;;; +066E;ARABIC LETTER DOTLESS BEH;Lo;0;AL;;;;;N;;;;; +066F;ARABIC LETTER DOTLESS QAF;Lo;0;AL;;;;;N;;;;; +0670;ARABIC LETTER SUPERSCRIPT ALEF;Mn;35;NSM;;;;;N;ARABIC ALEF ABOVE;;;; +0671;ARABIC LETTER ALEF WASLA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAT WASL ON ALEF;;;; +0672;ARABIC LETTER ALEF WITH WAVY HAMZA ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER WAVY HAMZAH ON ALEF;;;; +0673;ARABIC LETTER ALEF WITH WAVY HAMZA BELOW;Lo;0;AL;;;;;N;ARABIC LETTER WAVY HAMZAH UNDER ALEF;;;; +0674;ARABIC LETTER HIGH HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HIGH HAMZAH;;;; +0675;ARABIC LETTER HIGH HAMZA ALEF;Lo;0;AL;<compat> 0627 0674;;;;N;ARABIC LETTER HIGH HAMZAH ALEF;;;; +0676;ARABIC LETTER HIGH HAMZA WAW;Lo;0;AL;<compat> 0648 0674;;;;N;ARABIC LETTER HIGH HAMZAH WAW;;;; +0677;ARABIC LETTER U WITH HAMZA ABOVE;Lo;0;AL;<compat> 06C7 0674;;;;N;ARABIC LETTER HIGH HAMZAH WAW WITH DAMMAH;;;; +0678;ARABIC LETTER HIGH HAMZA YEH;Lo;0;AL;<compat> 064A 0674;;;;N;ARABIC LETTER HIGH HAMZAH YA;;;; +0679;ARABIC LETTER TTEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH SMALL TAH;;;; +067A;ARABIC LETTER TTEHEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH TWO DOTS VERTICAL ABOVE;;;; +067B;ARABIC LETTER BEEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA WITH TWO DOTS VERTICAL BELOW;;;; +067C;ARABIC LETTER TEH WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH RING;;;; +067D;ARABIC LETTER TEH WITH THREE DOTS ABOVE DOWNWARDS;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS ABOVE DOWNWARD;;;; +067E;ARABIC LETTER PEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS BELOW;;;; +067F;ARABIC LETTER TEHEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH FOUR DOTS ABOVE;;;; +0680;ARABIC LETTER BEHEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA WITH FOUR DOTS BELOW;;;; +0681;ARABIC LETTER HAH WITH HAMZA ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH ON HAA;;;; +0682;ARABIC LETTER HAH WITH TWO DOTS VERTICAL ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH TWO DOTS VERTICAL ABOVE;;;; +0683;ARABIC LETTER NYEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE TWO DOTS;;;; +0684;ARABIC LETTER DYEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE TWO DOTS VERTICAL;;;; +0685;ARABIC LETTER HAH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH THREE DOTS ABOVE;;;; +0686;ARABIC LETTER TCHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE THREE DOTS DOWNWARD;;;; +0687;ARABIC LETTER TCHEHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE FOUR DOTS;;;; +0688;ARABIC LETTER DDAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH SMALL TAH;;;; +0689;ARABIC LETTER DAL WITH RING;Lo;0;AL;;;;;N;;;;; +068A;ARABIC LETTER DAL WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; +068B;ARABIC LETTER DAL WITH DOT BELOW AND SMALL TAH;Lo;0;AL;;;;;N;;;;; +068C;ARABIC LETTER DAHAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH TWO DOTS ABOVE;;;; +068D;ARABIC LETTER DDAHAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH TWO DOTS BELOW;;;; +068E;ARABIC LETTER DUL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH THREE DOTS ABOVE;;;; +068F;ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARDS;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARD;;;; +0690;ARABIC LETTER DAL WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +0691;ARABIC LETTER RREH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL TAH;;;; +0692;ARABIC LETTER REH WITH SMALL V;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL V;;;; +0693;ARABIC LETTER REH WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH RING;;;; +0694;ARABIC LETTER REH WITH DOT BELOW;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH DOT BELOW;;;; +0695;ARABIC LETTER REH WITH SMALL V BELOW;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL V BELOW;;;; +0696;ARABIC LETTER REH WITH DOT BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH DOT BELOW AND DOT ABOVE;;;; +0697;ARABIC LETTER REH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH TWO DOTS ABOVE;;;; +0698;ARABIC LETTER JEH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH THREE DOTS ABOVE;;;; +0699;ARABIC LETTER REH WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH FOUR DOTS ABOVE;;;; +069A;ARABIC LETTER SEEN WITH DOT BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;; +069B;ARABIC LETTER SEEN WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; +069C;ARABIC LETTER SEEN WITH THREE DOTS BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +069D;ARABIC LETTER SAD WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;; +069E;ARABIC LETTER SAD WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +069F;ARABIC LETTER TAH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +06A0;ARABIC LETTER AIN WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +06A1;ARABIC LETTER DOTLESS FEH;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS FA;;;; +06A2;ARABIC LETTER FEH WITH DOT MOVED BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH DOT MOVED BELOW;;;; +06A3;ARABIC LETTER FEH WITH DOT BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH DOT BELOW;;;; +06A4;ARABIC LETTER VEH;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH THREE DOTS ABOVE;;;; +06A5;ARABIC LETTER FEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH THREE DOTS BELOW;;;; +06A6;ARABIC LETTER PEHEH;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH FOUR DOTS ABOVE;;;; +06A7;ARABIC LETTER QAF WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; +06A8;ARABIC LETTER QAF WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +06A9;ARABIC LETTER KEHEH;Lo;0;AL;;;;;N;ARABIC LETTER OPEN CAF;;;; +06AA;ARABIC LETTER SWASH KAF;Lo;0;AL;;;;;N;ARABIC LETTER SWASH CAF;;;; +06AB;ARABIC LETTER KAF WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH RING;;;; +06AC;ARABIC LETTER KAF WITH DOT ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH DOT ABOVE;;;; +06AD;ARABIC LETTER NG;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH THREE DOTS ABOVE;;;; +06AE;ARABIC LETTER KAF WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH THREE DOTS BELOW;;;; +06AF;ARABIC LETTER GAF;Lo;0;AL;;;;;N;;;;; +06B0;ARABIC LETTER GAF WITH RING;Lo;0;AL;;;;;N;;;;; +06B1;ARABIC LETTER NGOEH;Lo;0;AL;;;;;N;ARABIC LETTER GAF WITH TWO DOTS ABOVE;;;; +06B2;ARABIC LETTER GAF WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;; +06B3;ARABIC LETTER GUEH;Lo;0;AL;;;;;N;ARABIC LETTER GAF WITH TWO DOTS VERTICAL BELOW;;;; +06B4;ARABIC LETTER GAF WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +06B5;ARABIC LETTER LAM WITH SMALL V;Lo;0;AL;;;;;N;;;;; +06B6;ARABIC LETTER LAM WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; +06B7;ARABIC LETTER LAM WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +06B8;ARABIC LETTER LAM WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; +06B9;ARABIC LETTER NOON WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; +06BA;ARABIC LETTER NOON GHUNNA;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON;;;; +06BB;ARABIC LETTER RNOON;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON WITH SMALL TAH;;;; +06BC;ARABIC LETTER NOON WITH RING;Lo;0;AL;;;;;N;;;;; +06BD;ARABIC LETTER NOON WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +06BE;ARABIC LETTER HEH DOACHASHMEE;Lo;0;AL;;;;;N;ARABIC LETTER KNOTTED HA;;;; +06BF;ARABIC LETTER TCHEH WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; +06C0;ARABIC LETTER HEH WITH YEH ABOVE;Lo;0;AL;06D5 0654;;;;N;ARABIC LETTER HAMZAH ON HA;;;; +06C1;ARABIC LETTER HEH GOAL;Lo;0;AL;;;;;N;ARABIC LETTER HA GOAL;;;; +06C2;ARABIC LETTER HEH GOAL WITH HAMZA ABOVE;Lo;0;AL;06C1 0654;;;;N;ARABIC LETTER HAMZAH ON HA GOAL;;;; +06C3;ARABIC LETTER TEH MARBUTA GOAL;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH GOAL;;;; +06C4;ARABIC LETTER WAW WITH RING;Lo;0;AL;;;;;N;;;;; +06C5;ARABIC LETTER KIRGHIZ OE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH BAR;;;; +06C6;ARABIC LETTER OE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH SMALL V;;;; +06C7;ARABIC LETTER U;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH DAMMAH;;;; +06C8;ARABIC LETTER YU;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH ALEF ABOVE;;;; +06C9;ARABIC LETTER KIRGHIZ YU;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH INVERTED SMALL V;;;; +06CA;ARABIC LETTER WAW WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +06CB;ARABIC LETTER VE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH THREE DOTS ABOVE;;;; +06CC;ARABIC LETTER FARSI YEH;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS YA;;;; +06CD;ARABIC LETTER YEH WITH TAIL;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH TAIL;;;; +06CE;ARABIC LETTER YEH WITH SMALL V;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH SMALL V;;;; +06CF;ARABIC LETTER WAW WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; +06D0;ARABIC LETTER E;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH TWO DOTS VERTICAL BELOW;;;; +06D1;ARABIC LETTER YEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH THREE DOTS BELOW;;;; +06D2;ARABIC LETTER YEH BARREE;Lo;0;AL;;;;;N;ARABIC LETTER YA BARREE;;;; +06D3;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE;Lo;0;AL;06D2 0654;;;;N;ARABIC LETTER HAMZAH ON YA BARREE;;;; +06D4;ARABIC FULL STOP;Po;0;AL;;;;;N;ARABIC PERIOD;;;; +06D5;ARABIC LETTER AE;Lo;0;AL;;;;;N;;;;; +06D6;ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;; +06D7;ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;; +06D8;ARABIC SMALL HIGH MEEM INITIAL FORM;Mn;230;NSM;;;;;N;;;;; +06D9;ARABIC SMALL HIGH LAM ALEF;Mn;230;NSM;;;;;N;;;;; +06DA;ARABIC SMALL HIGH JEEM;Mn;230;NSM;;;;;N;;;;; +06DB;ARABIC SMALL HIGH THREE DOTS;Mn;230;NSM;;;;;N;;;;; +06DC;ARABIC SMALL HIGH SEEN;Mn;230;NSM;;;;;N;;;;; +06DD;ARABIC END OF AYAH;Cf;0;AN;;;;;N;;;;; +06DE;ARABIC START OF RUB EL HIZB;So;0;ON;;;;;N;;;;; +06DF;ARABIC SMALL HIGH ROUNDED ZERO;Mn;230;NSM;;;;;N;;;;; +06E0;ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO;Mn;230;NSM;;;;;N;;;;; +06E1;ARABIC SMALL HIGH DOTLESS HEAD OF KHAH;Mn;230;NSM;;;;;N;;;;; +06E2;ARABIC SMALL HIGH MEEM ISOLATED FORM;Mn;230;NSM;;;;;N;;;;; +06E3;ARABIC SMALL LOW SEEN;Mn;220;NSM;;;;;N;;;;; +06E4;ARABIC SMALL HIGH MADDA;Mn;230;NSM;;;;;N;;;;; +06E5;ARABIC SMALL WAW;Lm;0;AL;;;;;N;;;;; +06E6;ARABIC SMALL YEH;Lm;0;AL;;;;;N;;;;; +06E7;ARABIC SMALL HIGH YEH;Mn;230;NSM;;;;;N;;;;; +06E8;ARABIC SMALL HIGH NOON;Mn;230;NSM;;;;;N;;;;; +06E9;ARABIC PLACE OF SAJDAH;So;0;ON;;;;;N;;;;; +06EA;ARABIC EMPTY CENTRE LOW STOP;Mn;220;NSM;;;;;N;;;;; +06EB;ARABIC EMPTY CENTRE HIGH STOP;Mn;230;NSM;;;;;N;;;;; +06EC;ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE;Mn;230;NSM;;;;;N;;;;; +06ED;ARABIC SMALL LOW MEEM;Mn;220;NSM;;;;;N;;;;; +06EE;ARABIC LETTER DAL WITH INVERTED V;Lo;0;AL;;;;;N;;;;; +06EF;ARABIC LETTER REH WITH INVERTED V;Lo;0;AL;;;;;N;;;;; +06F0;EXTENDED ARABIC-INDIC DIGIT ZERO;Nd;0;EN;;0;0;0;N;EASTERN ARABIC-INDIC DIGIT ZERO;;;; +06F1;EXTENDED ARABIC-INDIC DIGIT ONE;Nd;0;EN;;1;1;1;N;EASTERN ARABIC-INDIC DIGIT ONE;;;; +06F2;EXTENDED ARABIC-INDIC DIGIT TWO;Nd;0;EN;;2;2;2;N;EASTERN ARABIC-INDIC DIGIT TWO;;;; +06F3;EXTENDED ARABIC-INDIC DIGIT THREE;Nd;0;EN;;3;3;3;N;EASTERN ARABIC-INDIC DIGIT THREE;;;; +06F4;EXTENDED ARABIC-INDIC DIGIT FOUR;Nd;0;EN;;4;4;4;N;EASTERN ARABIC-INDIC DIGIT FOUR;;;; +06F5;EXTENDED ARABIC-INDIC DIGIT FIVE;Nd;0;EN;;5;5;5;N;EASTERN ARABIC-INDIC DIGIT FIVE;;;; +06F6;EXTENDED ARABIC-INDIC DIGIT SIX;Nd;0;EN;;6;6;6;N;EASTERN ARABIC-INDIC DIGIT SIX;;;; +06F7;EXTENDED ARABIC-INDIC DIGIT SEVEN;Nd;0;EN;;7;7;7;N;EASTERN ARABIC-INDIC DIGIT SEVEN;;;; +06F8;EXTENDED ARABIC-INDIC DIGIT EIGHT;Nd;0;EN;;8;8;8;N;EASTERN ARABIC-INDIC DIGIT EIGHT;;;; +06F9;EXTENDED ARABIC-INDIC DIGIT NINE;Nd;0;EN;;9;9;9;N;EASTERN ARABIC-INDIC DIGIT NINE;;;; +06FA;ARABIC LETTER SHEEN WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; +06FB;ARABIC LETTER DAD WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; +06FC;ARABIC LETTER GHAIN WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; +06FD;ARABIC SIGN SINDHI AMPERSAND;So;0;AL;;;;;N;;;;; +06FE;ARABIC SIGN SINDHI POSTPOSITION MEN;So;0;AL;;;;;N;;;;; +06FF;ARABIC LETTER HEH WITH INVERTED V;Lo;0;AL;;;;;N;;;;; +0700;SYRIAC END OF PARAGRAPH;Po;0;AL;;;;;N;;;;; +0701;SYRIAC SUPRALINEAR FULL STOP;Po;0;AL;;;;;N;;;;; +0702;SYRIAC SUBLINEAR FULL STOP;Po;0;AL;;;;;N;;;;; +0703;SYRIAC SUPRALINEAR COLON;Po;0;AL;;;;;N;;;;; +0704;SYRIAC SUBLINEAR COLON;Po;0;AL;;;;;N;;;;; +0705;SYRIAC HORIZONTAL COLON;Po;0;AL;;;;;N;;;;; +0706;SYRIAC COLON SKEWED LEFT;Po;0;AL;;;;;N;;;;; +0707;SYRIAC COLON SKEWED RIGHT;Po;0;AL;;;;;N;;;;; +0708;SYRIAC SUPRALINEAR COLON SKEWED LEFT;Po;0;AL;;;;;N;;;;; +0709;SYRIAC SUBLINEAR COLON SKEWED RIGHT;Po;0;AL;;;;;N;;;;; +070A;SYRIAC CONTRACTION;Po;0;AL;;;;;N;;;;; +070B;SYRIAC HARKLEAN OBELUS;Po;0;AL;;;;;N;;;;; +070C;SYRIAC HARKLEAN METOBELUS;Po;0;AL;;;;;N;;;;; +070D;SYRIAC HARKLEAN ASTERISCUS;Po;0;AL;;;;;N;;;;; +070F;SYRIAC ABBREVIATION MARK;Cf;0;AL;;;;;N;;;;; +0710;SYRIAC LETTER ALAPH;Lo;0;AL;;;;;N;;;;; +0711;SYRIAC LETTER SUPERSCRIPT ALAPH;Mn;36;NSM;;;;;N;;;;; +0712;SYRIAC LETTER BETH;Lo;0;AL;;;;;N;;;;; +0713;SYRIAC LETTER GAMAL;Lo;0;AL;;;;;N;;;;; +0714;SYRIAC LETTER GAMAL GARSHUNI;Lo;0;AL;;;;;N;;;;; +0715;SYRIAC LETTER DALATH;Lo;0;AL;;;;;N;;;;; +0716;SYRIAC LETTER DOTLESS DALATH RISH;Lo;0;AL;;;;;N;;;;; +0717;SYRIAC LETTER HE;Lo;0;AL;;;;;N;;;;; +0718;SYRIAC LETTER WAW;Lo;0;AL;;;;;N;;;;; +0719;SYRIAC LETTER ZAIN;Lo;0;AL;;;;;N;;;;; +071A;SYRIAC LETTER HETH;Lo;0;AL;;;;;N;;;;; +071B;SYRIAC LETTER TETH;Lo;0;AL;;;;;N;;;;; +071C;SYRIAC LETTER TETH GARSHUNI;Lo;0;AL;;;;;N;;;;; +071D;SYRIAC LETTER YUDH;Lo;0;AL;;;;;N;;;;; +071E;SYRIAC LETTER YUDH HE;Lo;0;AL;;;;;N;;;;; +071F;SYRIAC LETTER KAPH;Lo;0;AL;;;;;N;;;;; +0720;SYRIAC LETTER LAMADH;Lo;0;AL;;;;;N;;;;; +0721;SYRIAC LETTER MIM;Lo;0;AL;;;;;N;;;;; +0722;SYRIAC LETTER NUN;Lo;0;AL;;;;;N;;;;; +0723;SYRIAC LETTER SEMKATH;Lo;0;AL;;;;;N;;;;; +0724;SYRIAC LETTER FINAL SEMKATH;Lo;0;AL;;;;;N;;;;; +0725;SYRIAC LETTER E;Lo;0;AL;;;;;N;;;;; +0726;SYRIAC LETTER PE;Lo;0;AL;;;;;N;;;;; +0727;SYRIAC LETTER REVERSED PE;Lo;0;AL;;;;;N;;;;; +0728;SYRIAC LETTER SADHE;Lo;0;AL;;;;;N;;;;; +0729;SYRIAC LETTER QAPH;Lo;0;AL;;;;;N;;;;; +072A;SYRIAC LETTER RISH;Lo;0;AL;;;;;N;;;;; +072B;SYRIAC LETTER SHIN;Lo;0;AL;;;;;N;;;;; +072C;SYRIAC LETTER TAW;Lo;0;AL;;;;;N;;;;; +072D;SYRIAC LETTER PERSIAN BHETH;Lo;0;AL;;;;;N;;;;; +072E;SYRIAC LETTER PERSIAN GHAMAL;Lo;0;AL;;;;;N;;;;; +072F;SYRIAC LETTER PERSIAN DHALATH;Lo;0;AL;;;;;N;;;;; +0730;SYRIAC PTHAHA ABOVE;Mn;230;NSM;;;;;N;;;;; +0731;SYRIAC PTHAHA BELOW;Mn;220;NSM;;;;;N;;;;; +0732;SYRIAC PTHAHA DOTTED;Mn;230;NSM;;;;;N;;;;; +0733;SYRIAC ZQAPHA ABOVE;Mn;230;NSM;;;;;N;;;;; +0734;SYRIAC ZQAPHA BELOW;Mn;220;NSM;;;;;N;;;;; +0735;SYRIAC ZQAPHA DOTTED;Mn;230;NSM;;;;;N;;;;; +0736;SYRIAC RBASA ABOVE;Mn;230;NSM;;;;;N;;;;; +0737;SYRIAC RBASA BELOW;Mn;220;NSM;;;;;N;;;;; +0738;SYRIAC DOTTED ZLAMA HORIZONTAL;Mn;220;NSM;;;;;N;;;;; +0739;SYRIAC DOTTED ZLAMA ANGULAR;Mn;220;NSM;;;;;N;;;;; +073A;SYRIAC HBASA ABOVE;Mn;230;NSM;;;;;N;;;;; +073B;SYRIAC HBASA BELOW;Mn;220;NSM;;;;;N;;;;; +073C;SYRIAC HBASA-ESASA DOTTED;Mn;220;NSM;;;;;N;;;;; +073D;SYRIAC ESASA ABOVE;Mn;230;NSM;;;;;N;;;;; +073E;SYRIAC ESASA BELOW;Mn;220;NSM;;;;;N;;;;; +073F;SYRIAC RWAHA;Mn;230;NSM;;;;;N;;;;; +0740;SYRIAC FEMININE DOT;Mn;230;NSM;;;;;N;;;;; +0741;SYRIAC QUSHSHAYA;Mn;230;NSM;;;;;N;;;;; +0742;SYRIAC RUKKAKHA;Mn;220;NSM;;;;;N;;;;; +0743;SYRIAC TWO VERTICAL DOTS ABOVE;Mn;230;NSM;;;;;N;;;;; +0744;SYRIAC TWO VERTICAL DOTS BELOW;Mn;220;NSM;;;;;N;;;;; +0745;SYRIAC THREE DOTS ABOVE;Mn;230;NSM;;;;;N;;;;; +0746;SYRIAC THREE DOTS BELOW;Mn;220;NSM;;;;;N;;;;; +0747;SYRIAC OBLIQUE LINE ABOVE;Mn;230;NSM;;;;;N;;;;; +0748;SYRIAC OBLIQUE LINE BELOW;Mn;220;NSM;;;;;N;;;;; +0749;SYRIAC MUSIC;Mn;230;NSM;;;;;N;;;;; +074A;SYRIAC BARREKH;Mn;230;NSM;;;;;N;;;;; +074D;SYRIAC LETTER SOGDIAN ZHAIN;Lo;0;AL;;;;;N;;;;; +074E;SYRIAC LETTER SOGDIAN KHAPH;Lo;0;AL;;;;;N;;;;; +074F;SYRIAC LETTER SOGDIAN FE;Lo;0;AL;;;;;N;;;;; +0750;ARABIC LETTER BEH WITH THREE DOTS HORIZONTALLY BELOW;Lo;0;AL;;;;;N;;;;; +0751;ARABIC LETTER BEH WITH DOT BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +0752;ARABIC LETTER BEH WITH THREE DOTS POINTING UPWARDS BELOW;Lo;0;AL;;;;;N;;;;; +0753;ARABIC LETTER BEH WITH THREE DOTS POINTING UPWARDS BELOW AND TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +0754;ARABIC LETTER BEH WITH TWO DOTS BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;; +0755;ARABIC LETTER BEH WITH INVERTED SMALL V BELOW;Lo;0;AL;;;;;N;;;;; +0756;ARABIC LETTER BEH WITH SMALL V;Lo;0;AL;;;;;N;;;;; +0757;ARABIC LETTER HAH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +0758;ARABIC LETTER HAH WITH THREE DOTS POINTING UPWARDS BELOW;Lo;0;AL;;;;;N;;;;; +0759;ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW AND SMALL TAH;Lo;0;AL;;;;;N;;;;; +075A;ARABIC LETTER DAL WITH INVERTED SMALL V BELOW;Lo;0;AL;;;;;N;;;;; +075B;ARABIC LETTER REH WITH STROKE;Lo;0;AL;;;;;N;;;;; +075C;ARABIC LETTER SEEN WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +075D;ARABIC LETTER AIN WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +075E;ARABIC LETTER AIN WITH THREE DOTS POINTING DOWNWARDS ABOVE;Lo;0;AL;;;;;N;;;;; +075F;ARABIC LETTER AIN WITH TWO DOTS VERTICALLY ABOVE;Lo;0;AL;;;;;N;;;;; +0760;ARABIC LETTER FEH WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;; +0761;ARABIC LETTER FEH WITH THREE DOTS POINTING UPWARDS BELOW;Lo;0;AL;;;;;N;;;;; +0762;ARABIC LETTER KEHEH WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; +0763;ARABIC LETTER KEHEH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +0764;ARABIC LETTER KEHEH WITH THREE DOTS POINTING UPWARDS BELOW;Lo;0;AL;;;;;N;;;;; +0765;ARABIC LETTER MEEM WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; +0766;ARABIC LETTER MEEM WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; +0767;ARABIC LETTER NOON WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;; +0768;ARABIC LETTER NOON WITH SMALL TAH;Lo;0;AL;;;;;N;;;;; +0769;ARABIC LETTER NOON WITH SMALL V;Lo;0;AL;;;;;N;;;;; +076A;ARABIC LETTER LAM WITH BAR;Lo;0;AL;;;;;N;;;;; +076B;ARABIC LETTER REH WITH TWO DOTS VERTICALLY ABOVE;Lo;0;AL;;;;;N;;;;; +076C;ARABIC LETTER REH WITH HAMZA ABOVE;Lo;0;AL;;;;;N;;;;; +076D;ARABIC LETTER SEEN WITH TWO DOTS VERTICALLY ABOVE;Lo;0;AL;;;;;N;;;;; +076E;ARABIC LETTER HAH WITH SMALL ARABIC LETTER TAH BELOW;Lo;0;AL;;;;;N;;;;; +076F;ARABIC LETTER HAH WITH SMALL ARABIC LETTER TAH AND TWO DOTS;Lo;0;AL;;;;;N;;;;; +0770;ARABIC LETTER SEEN WITH SMALL ARABIC LETTER TAH AND TWO DOTS;Lo;0;AL;;;;;N;;;;; +0771;ARABIC LETTER REH WITH SMALL ARABIC LETTER TAH AND TWO DOTS;Lo;0;AL;;;;;N;;;;; +0772;ARABIC LETTER HAH WITH SMALL ARABIC LETTER TAH ABOVE;Lo;0;AL;;;;;N;;;;; +0773;ARABIC LETTER ALEF WITH EXTENDED ARABIC-INDIC DIGIT TWO ABOVE;Lo;0;AL;;;;;N;;;;; +0774;ARABIC LETTER ALEF WITH EXTENDED ARABIC-INDIC DIGIT THREE ABOVE;Lo;0;AL;;;;;N;;;;; +0775;ARABIC LETTER FARSI YEH WITH EXTENDED ARABIC-INDIC DIGIT TWO ABOVE;Lo;0;AL;;;;;N;;;;; +0776;ARABIC LETTER FARSI YEH WITH EXTENDED ARABIC-INDIC DIGIT THREE ABOVE;Lo;0;AL;;;;;N;;;;; +0777;ARABIC LETTER FARSI YEH WITH EXTENDED ARABIC-INDIC DIGIT FOUR BELOW;Lo;0;AL;;;;;N;;;;; +0778;ARABIC LETTER WAW WITH EXTENDED ARABIC-INDIC DIGIT TWO ABOVE;Lo;0;AL;;;;;N;;;;; +0779;ARABIC LETTER WAW WITH EXTENDED ARABIC-INDIC DIGIT THREE ABOVE;Lo;0;AL;;;;;N;;;;; +077A;ARABIC LETTER YEH BARREE WITH EXTENDED ARABIC-INDIC DIGIT TWO ABOVE;Lo;0;AL;;;;;N;;;;; +077B;ARABIC LETTER YEH BARREE WITH EXTENDED ARABIC-INDIC DIGIT THREE ABOVE;Lo;0;AL;;;;;N;;;;; +077C;ARABIC LETTER HAH WITH EXTENDED ARABIC-INDIC DIGIT FOUR BELOW;Lo;0;AL;;;;;N;;;;; +077D;ARABIC LETTER SEEN WITH EXTENDED ARABIC-INDIC DIGIT FOUR ABOVE;Lo;0;AL;;;;;N;;;;; +077E;ARABIC LETTER SEEN WITH INVERTED V;Lo;0;AL;;;;;N;;;;; +077F;ARABIC LETTER KAF WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +0780;THAANA LETTER HAA;Lo;0;AL;;;;;N;;;;; +0781;THAANA LETTER SHAVIYANI;Lo;0;AL;;;;;N;;;;; +0782;THAANA LETTER NOONU;Lo;0;AL;;;;;N;;;;; +0783;THAANA LETTER RAA;Lo;0;AL;;;;;N;;;;; +0784;THAANA LETTER BAA;Lo;0;AL;;;;;N;;;;; +0785;THAANA LETTER LHAVIYANI;Lo;0;AL;;;;;N;;;;; +0786;THAANA LETTER KAAFU;Lo;0;AL;;;;;N;;;;; +0787;THAANA LETTER ALIFU;Lo;0;AL;;;;;N;;;;; +0788;THAANA LETTER VAAVU;Lo;0;AL;;;;;N;;;;; +0789;THAANA LETTER MEEMU;Lo;0;AL;;;;;N;;;;; +078A;THAANA LETTER FAAFU;Lo;0;AL;;;;;N;;;;; +078B;THAANA LETTER DHAALU;Lo;0;AL;;;;;N;;;;; +078C;THAANA LETTER THAA;Lo;0;AL;;;;;N;;;;; +078D;THAANA LETTER LAAMU;Lo;0;AL;;;;;N;;;;; +078E;THAANA LETTER GAAFU;Lo;0;AL;;;;;N;;;;; +078F;THAANA LETTER GNAVIYANI;Lo;0;AL;;;;;N;;;;; +0790;THAANA LETTER SEENU;Lo;0;AL;;;;;N;;;;; +0791;THAANA LETTER DAVIYANI;Lo;0;AL;;;;;N;;;;; +0792;THAANA LETTER ZAVIYANI;Lo;0;AL;;;;;N;;;;; +0793;THAANA LETTER TAVIYANI;Lo;0;AL;;;;;N;;;;; +0794;THAANA LETTER YAA;Lo;0;AL;;;;;N;;;;; +0795;THAANA LETTER PAVIYANI;Lo;0;AL;;;;;N;;;;; +0796;THAANA LETTER JAVIYANI;Lo;0;AL;;;;;N;;;;; +0797;THAANA LETTER CHAVIYANI;Lo;0;AL;;;;;N;;;;; +0798;THAANA LETTER TTAA;Lo;0;AL;;;;;N;;;;; +0799;THAANA LETTER HHAA;Lo;0;AL;;;;;N;;;;; +079A;THAANA LETTER KHAA;Lo;0;AL;;;;;N;;;;; +079B;THAANA LETTER THAALU;Lo;0;AL;;;;;N;;;;; +079C;THAANA LETTER ZAA;Lo;0;AL;;;;;N;;;;; +079D;THAANA LETTER SHEENU;Lo;0;AL;;;;;N;;;;; +079E;THAANA LETTER SAADHU;Lo;0;AL;;;;;N;;;;; +079F;THAANA LETTER DAADHU;Lo;0;AL;;;;;N;;;;; +07A0;THAANA LETTER TO;Lo;0;AL;;;;;N;;;;; +07A1;THAANA LETTER ZO;Lo;0;AL;;;;;N;;;;; +07A2;THAANA LETTER AINU;Lo;0;AL;;;;;N;;;;; +07A3;THAANA LETTER GHAINU;Lo;0;AL;;;;;N;;;;; +07A4;THAANA LETTER QAAFU;Lo;0;AL;;;;;N;;;;; +07A5;THAANA LETTER WAAVU;Lo;0;AL;;;;;N;;;;; +07A6;THAANA ABAFILI;Mn;0;NSM;;;;;N;;;;; +07A7;THAANA AABAAFILI;Mn;0;NSM;;;;;N;;;;; +07A8;THAANA IBIFILI;Mn;0;NSM;;;;;N;;;;; +07A9;THAANA EEBEEFILI;Mn;0;NSM;;;;;N;;;;; +07AA;THAANA UBUFILI;Mn;0;NSM;;;;;N;;;;; +07AB;THAANA OOBOOFILI;Mn;0;NSM;;;;;N;;;;; +07AC;THAANA EBEFILI;Mn;0;NSM;;;;;N;;;;; +07AD;THAANA EYBEYFILI;Mn;0;NSM;;;;;N;;;;; +07AE;THAANA OBOFILI;Mn;0;NSM;;;;;N;;;;; +07AF;THAANA OABOAFILI;Mn;0;NSM;;;;;N;;;;; +07B0;THAANA SUKUN;Mn;0;NSM;;;;;N;;;;; +07B1;THAANA LETTER NAA;Lo;0;AL;;;;;N;;;;; +07C0;NKO DIGIT ZERO;Nd;0;R;;0;0;0;N;;;;; +07C1;NKO DIGIT ONE;Nd;0;R;;1;1;1;N;;;;; +07C2;NKO DIGIT TWO;Nd;0;R;;2;2;2;N;;;;; +07C3;NKO DIGIT THREE;Nd;0;R;;3;3;3;N;;;;; +07C4;NKO DIGIT FOUR;Nd;0;R;;4;4;4;N;;;;; +07C5;NKO DIGIT FIVE;Nd;0;R;;5;5;5;N;;;;; +07C6;NKO DIGIT SIX;Nd;0;R;;6;6;6;N;;;;; +07C7;NKO DIGIT SEVEN;Nd;0;R;;7;7;7;N;;;;; +07C8;NKO DIGIT EIGHT;Nd;0;R;;8;8;8;N;;;;; +07C9;NKO DIGIT NINE;Nd;0;R;;9;9;9;N;;;;; +07CA;NKO LETTER A;Lo;0;R;;;;;N;;;;; +07CB;NKO LETTER EE;Lo;0;R;;;;;N;;;;; +07CC;NKO LETTER I;Lo;0;R;;;;;N;;;;; +07CD;NKO LETTER E;Lo;0;R;;;;;N;;;;; +07CE;NKO LETTER U;Lo;0;R;;;;;N;;;;; +07CF;NKO LETTER OO;Lo;0;R;;;;;N;;;;; +07D0;NKO LETTER O;Lo;0;R;;;;;N;;;;; +07D1;NKO LETTER DAGBASINNA;Lo;0;R;;;;;N;;;;; +07D2;NKO LETTER N;Lo;0;R;;;;;N;;;;; +07D3;NKO LETTER BA;Lo;0;R;;;;;N;;;;; +07D4;NKO LETTER PA;Lo;0;R;;;;;N;;;;; +07D5;NKO LETTER TA;Lo;0;R;;;;;N;;;;; +07D6;NKO LETTER JA;Lo;0;R;;;;;N;;;;; +07D7;NKO LETTER CHA;Lo;0;R;;;;;N;;;;; +07D8;NKO LETTER DA;Lo;0;R;;;;;N;;;;; +07D9;NKO LETTER RA;Lo;0;R;;;;;N;;;;; +07DA;NKO LETTER RRA;Lo;0;R;;;;;N;;;;; +07DB;NKO LETTER SA;Lo;0;R;;;;;N;;;;; +07DC;NKO LETTER GBA;Lo;0;R;;;;;N;;;;; +07DD;NKO LETTER FA;Lo;0;R;;;;;N;;;;; +07DE;NKO LETTER KA;Lo;0;R;;;;;N;;;;; +07DF;NKO LETTER LA;Lo;0;R;;;;;N;;;;; +07E0;NKO LETTER NA WOLOSO;Lo;0;R;;;;;N;;;;; +07E1;NKO LETTER MA;Lo;0;R;;;;;N;;;;; +07E2;NKO LETTER NYA;Lo;0;R;;;;;N;;;;; +07E3;NKO LETTER NA;Lo;0;R;;;;;N;;;;; +07E4;NKO LETTER HA;Lo;0;R;;;;;N;;;;; +07E5;NKO LETTER WA;Lo;0;R;;;;;N;;;;; +07E6;NKO LETTER YA;Lo;0;R;;;;;N;;;;; +07E7;NKO LETTER NYA WOLOSO;Lo;0;R;;;;;N;;;;; +07E8;NKO LETTER JONA JA;Lo;0;R;;;;;N;;;;; +07E9;NKO LETTER JONA CHA;Lo;0;R;;;;;N;;;;; +07EA;NKO LETTER JONA RA;Lo;0;R;;;;;N;;;;; +07EB;NKO COMBINING SHORT HIGH TONE;Mn;230;NSM;;;;;N;;;;; +07EC;NKO COMBINING SHORT LOW TONE;Mn;230;NSM;;;;;N;;;;; +07ED;NKO COMBINING SHORT RISING TONE;Mn;230;NSM;;;;;N;;;;; +07EE;NKO COMBINING LONG DESCENDING TONE;Mn;230;NSM;;;;;N;;;;; +07EF;NKO COMBINING LONG HIGH TONE;Mn;230;NSM;;;;;N;;;;; +07F0;NKO COMBINING LONG LOW TONE;Mn;230;NSM;;;;;N;;;;; +07F1;NKO COMBINING LONG RISING TONE;Mn;230;NSM;;;;;N;;;;; +07F2;NKO COMBINING NASALIZATION MARK;Mn;220;NSM;;;;;N;;;;; +07F3;NKO COMBINING DOUBLE DOT ABOVE;Mn;230;NSM;;;;;N;;;;; +07F4;NKO HIGH TONE APOSTROPHE;Lm;0;R;;;;;N;;;;; +07F5;NKO LOW TONE APOSTROPHE;Lm;0;R;;;;;N;;;;; +07F6;NKO SYMBOL OO DENNEN;So;0;ON;;;;;N;;;;; +07F7;NKO SYMBOL GBAKURUNEN;Po;0;ON;;;;;N;;;;; +07F8;NKO COMMA;Po;0;ON;;;;;N;;;;; +07F9;NKO EXCLAMATION MARK;Po;0;ON;;;;;N;;;;; +07FA;NKO LAJANYALAN;Lm;0;R;;;;;N;;;;; +0800;SAMARITAN LETTER ALAF;Lo;0;R;;;;;N;;;;; +0801;SAMARITAN LETTER BIT;Lo;0;R;;;;;N;;;;; +0802;SAMARITAN LETTER GAMAN;Lo;0;R;;;;;N;;;;; +0803;SAMARITAN LETTER DALAT;Lo;0;R;;;;;N;;;;; +0804;SAMARITAN LETTER IY;Lo;0;R;;;;;N;;;;; +0805;SAMARITAN LETTER BAA;Lo;0;R;;;;;N;;;;; +0806;SAMARITAN LETTER ZEN;Lo;0;R;;;;;N;;;;; +0807;SAMARITAN LETTER IT;Lo;0;R;;;;;N;;;;; +0808;SAMARITAN LETTER TIT;Lo;0;R;;;;;N;;;;; +0809;SAMARITAN LETTER YUT;Lo;0;R;;;;;N;;;;; +080A;SAMARITAN LETTER KAAF;Lo;0;R;;;;;N;;;;; +080B;SAMARITAN LETTER LABAT;Lo;0;R;;;;;N;;;;; +080C;SAMARITAN LETTER MIM;Lo;0;R;;;;;N;;;;; +080D;SAMARITAN LETTER NUN;Lo;0;R;;;;;N;;;;; +080E;SAMARITAN LETTER SINGAAT;Lo;0;R;;;;;N;;;;; +080F;SAMARITAN LETTER IN;Lo;0;R;;;;;N;;;;; +0810;SAMARITAN LETTER FI;Lo;0;R;;;;;N;;;;; +0811;SAMARITAN LETTER TSAADIY;Lo;0;R;;;;;N;;;;; +0812;SAMARITAN LETTER QUF;Lo;0;R;;;;;N;;;;; +0813;SAMARITAN LETTER RISH;Lo;0;R;;;;;N;;;;; +0814;SAMARITAN LETTER SHAN;Lo;0;R;;;;;N;;;;; +0815;SAMARITAN LETTER TAAF;Lo;0;R;;;;;N;;;;; +0816;SAMARITAN MARK IN;Mn;230;NSM;;;;;N;;;;; +0817;SAMARITAN MARK IN-ALAF;Mn;230;NSM;;;;;N;;;;; +0818;SAMARITAN MARK OCCLUSION;Mn;230;NSM;;;;;N;;;;; +0819;SAMARITAN MARK DAGESH;Mn;230;NSM;;;;;N;;;;; +081A;SAMARITAN MODIFIER LETTER EPENTHETIC YUT;Lm;0;R;;;;;N;;;;; +081B;SAMARITAN MARK EPENTHETIC YUT;Mn;230;NSM;;;;;N;;;;; +081C;SAMARITAN VOWEL SIGN LONG E;Mn;230;NSM;;;;;N;;;;; +081D;SAMARITAN VOWEL SIGN E;Mn;230;NSM;;;;;N;;;;; +081E;SAMARITAN VOWEL SIGN OVERLONG AA;Mn;230;NSM;;;;;N;;;;; +081F;SAMARITAN VOWEL SIGN LONG AA;Mn;230;NSM;;;;;N;;;;; +0820;SAMARITAN VOWEL SIGN AA;Mn;230;NSM;;;;;N;;;;; +0821;SAMARITAN VOWEL SIGN OVERLONG A;Mn;230;NSM;;;;;N;;;;; +0822;SAMARITAN VOWEL SIGN LONG A;Mn;230;NSM;;;;;N;;;;; +0823;SAMARITAN VOWEL SIGN A;Mn;230;NSM;;;;;N;;;;; +0824;SAMARITAN MODIFIER LETTER SHORT A;Lm;0;R;;;;;N;;;;; +0825;SAMARITAN VOWEL SIGN SHORT A;Mn;230;NSM;;;;;N;;;;; +0826;SAMARITAN VOWEL SIGN LONG U;Mn;230;NSM;;;;;N;;;;; +0827;SAMARITAN VOWEL SIGN U;Mn;230;NSM;;;;;N;;;;; +0828;SAMARITAN MODIFIER LETTER I;Lm;0;R;;;;;N;;;;; +0829;SAMARITAN VOWEL SIGN LONG I;Mn;230;NSM;;;;;N;;;;; +082A;SAMARITAN VOWEL SIGN I;Mn;230;NSM;;;;;N;;;;; +082B;SAMARITAN VOWEL SIGN O;Mn;230;NSM;;;;;N;;;;; +082C;SAMARITAN VOWEL SIGN SUKUN;Mn;230;NSM;;;;;N;;;;; +082D;SAMARITAN MARK NEQUDAA;Mn;230;NSM;;;;;N;;;;; +0830;SAMARITAN PUNCTUATION NEQUDAA;Po;0;R;;;;;N;;;;; +0831;SAMARITAN PUNCTUATION AFSAAQ;Po;0;R;;;;;N;;;;; +0832;SAMARITAN PUNCTUATION ANGED;Po;0;R;;;;;N;;;;; +0833;SAMARITAN PUNCTUATION BAU;Po;0;R;;;;;N;;;;; +0834;SAMARITAN PUNCTUATION ATMAAU;Po;0;R;;;;;N;;;;; +0835;SAMARITAN PUNCTUATION SHIYYAALAA;Po;0;R;;;;;N;;;;; +0836;SAMARITAN ABBREVIATION MARK;Po;0;R;;;;;N;;;;; +0837;SAMARITAN PUNCTUATION MELODIC QITSA;Po;0;R;;;;;N;;;;; +0838;SAMARITAN PUNCTUATION ZIQAA;Po;0;R;;;;;N;;;;; +0839;SAMARITAN PUNCTUATION QITSA;Po;0;R;;;;;N;;;;; +083A;SAMARITAN PUNCTUATION ZAEF;Po;0;R;;;;;N;;;;; +083B;SAMARITAN PUNCTUATION TURU;Po;0;R;;;;;N;;;;; +083C;SAMARITAN PUNCTUATION ARKAANU;Po;0;R;;;;;N;;;;; +083D;SAMARITAN PUNCTUATION SOF MASHFAAT;Po;0;R;;;;;N;;;;; +083E;SAMARITAN PUNCTUATION ANNAAU;Po;0;R;;;;;N;;;;; +0840;MANDAIC LETTER HALQA;Lo;0;R;;;;;N;;;;; +0841;MANDAIC LETTER AB;Lo;0;R;;;;;N;;;;; +0842;MANDAIC LETTER AG;Lo;0;R;;;;;N;;;;; +0843;MANDAIC LETTER AD;Lo;0;R;;;;;N;;;;; +0844;MANDAIC LETTER AH;Lo;0;R;;;;;N;;;;; +0845;MANDAIC LETTER USHENNA;Lo;0;R;;;;;N;;;;; +0846;MANDAIC LETTER AZ;Lo;0;R;;;;;N;;;;; +0847;MANDAIC LETTER IT;Lo;0;R;;;;;N;;;;; +0848;MANDAIC LETTER ATT;Lo;0;R;;;;;N;;;;; +0849;MANDAIC LETTER AKSA;Lo;0;R;;;;;N;;;;; +084A;MANDAIC LETTER AK;Lo;0;R;;;;;N;;;;; +084B;MANDAIC LETTER AL;Lo;0;R;;;;;N;;;;; +084C;MANDAIC LETTER AM;Lo;0;R;;;;;N;;;;; +084D;MANDAIC LETTER AN;Lo;0;R;;;;;N;;;;; +084E;MANDAIC LETTER AS;Lo;0;R;;;;;N;;;;; +084F;MANDAIC LETTER IN;Lo;0;R;;;;;N;;;;; +0850;MANDAIC LETTER AP;Lo;0;R;;;;;N;;;;; +0851;MANDAIC LETTER ASZ;Lo;0;R;;;;;N;;;;; +0852;MANDAIC LETTER AQ;Lo;0;R;;;;;N;;;;; +0853;MANDAIC LETTER AR;Lo;0;R;;;;;N;;;;; +0854;MANDAIC LETTER ASH;Lo;0;R;;;;;N;;;;; +0855;MANDAIC LETTER AT;Lo;0;R;;;;;N;;;;; +0856;MANDAIC LETTER DUSHENNA;Lo;0;R;;;;;N;;;;; +0857;MANDAIC LETTER KAD;Lo;0;R;;;;;N;;;;; +0858;MANDAIC LETTER AIN;Lo;0;R;;;;;N;;;;; +0859;MANDAIC AFFRICATION MARK;Mn;220;NSM;;;;;N;;;;; +085A;MANDAIC VOCALIZATION MARK;Mn;220;NSM;;;;;N;;;;; +085B;MANDAIC GEMINATION MARK;Mn;220;NSM;;;;;N;;;;; +085E;MANDAIC PUNCTUATION;Po;0;R;;;;;N;;;;; +08A0;ARABIC LETTER BEH WITH SMALL V BELOW;Lo;0;AL;;;;;N;;;;; +08A1;ARABIC LETTER BEH WITH HAMZA ABOVE;Lo;0;AL;;;;;N;;;;; +08A2;ARABIC LETTER JEEM WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +08A3;ARABIC LETTER TAH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +08A4;ARABIC LETTER FEH WITH DOT BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +08A5;ARABIC LETTER QAF WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; +08A6;ARABIC LETTER LAM WITH DOUBLE BAR;Lo;0;AL;;;;;N;;;;; +08A7;ARABIC LETTER MEEM WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +08A8;ARABIC LETTER YEH WITH TWO DOTS BELOW AND HAMZA ABOVE;Lo;0;AL;;;;;N;;;;; +08A9;ARABIC LETTER YEH WITH TWO DOTS BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;; +08AA;ARABIC LETTER REH WITH LOOP;Lo;0;AL;;;;;N;;;;; +08AB;ARABIC LETTER WAW WITH DOT WITHIN;Lo;0;AL;;;;;N;;;;; +08AC;ARABIC LETTER ROHINGYA YEH;Lo;0;AL;;;;;N;;;;; +08AD;ARABIC LETTER LOW ALEF;Lo;0;AL;;;;;N;;;;; +08AE;ARABIC LETTER DAL WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; +08AF;ARABIC LETTER SAD WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; +08B0;ARABIC LETTER GAF WITH INVERTED STROKE;Lo;0;AL;;;;;N;;;;; +08B1;ARABIC LETTER STRAIGHT WAW;Lo;0;AL;;;;;N;;;;; +08B2;ARABIC LETTER ZAIN WITH INVERTED V ABOVE;Lo;0;AL;;;;;N;;;;; +08B3;ARABIC LETTER AIN WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; +08B4;ARABIC LETTER KAF WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; +08B6;ARABIC LETTER BEH WITH SMALL MEEM ABOVE;Lo;0;AL;;;;;N;;;;; +08B7;ARABIC LETTER PEH WITH SMALL MEEM ABOVE;Lo;0;AL;;;;;N;;;;; +08B8;ARABIC LETTER TEH WITH SMALL TEH ABOVE;Lo;0;AL;;;;;N;;;;; +08B9;ARABIC LETTER REH WITH SMALL NOON ABOVE;Lo;0;AL;;;;;N;;;;; +08BA;ARABIC LETTER YEH WITH TWO DOTS BELOW AND SMALL NOON ABOVE;Lo;0;AL;;;;;N;;;;; +08BB;ARABIC LETTER AFRICAN FEH;Lo;0;AL;;;;;N;;;;; +08BC;ARABIC LETTER AFRICAN QAF;Lo;0;AL;;;;;N;;;;; +08BD;ARABIC LETTER AFRICAN NOON;Lo;0;AL;;;;;N;;;;; +08D4;ARABIC SMALL HIGH WORD AR-RUB;Mn;230;NSM;;;;;N;;;;; +08D5;ARABIC SMALL HIGH SAD;Mn;230;NSM;;;;;N;;;;; +08D6;ARABIC SMALL HIGH AIN;Mn;230;NSM;;;;;N;;;;; +08D7;ARABIC SMALL HIGH QAF;Mn;230;NSM;;;;;N;;;;; +08D8;ARABIC SMALL HIGH NOON WITH KASRA;Mn;230;NSM;;;;;N;;;;; +08D9;ARABIC SMALL LOW NOON WITH KASRA;Mn;230;NSM;;;;;N;;;;; +08DA;ARABIC SMALL HIGH WORD ATH-THALATHA;Mn;230;NSM;;;;;N;;;;; +08DB;ARABIC SMALL HIGH WORD AS-SAJDA;Mn;230;NSM;;;;;N;;;;; +08DC;ARABIC SMALL HIGH WORD AN-NISF;Mn;230;NSM;;;;;N;;;;; +08DD;ARABIC SMALL HIGH WORD SAKTA;Mn;230;NSM;;;;;N;;;;; +08DE;ARABIC SMALL HIGH WORD QIF;Mn;230;NSM;;;;;N;;;;; +08DF;ARABIC SMALL HIGH WORD WAQFA;Mn;230;NSM;;;;;N;;;;; +08E0;ARABIC SMALL HIGH FOOTNOTE MARKER;Mn;230;NSM;;;;;N;;;;; +08E1;ARABIC SMALL HIGH SIGN SAFHA;Mn;230;NSM;;;;;N;;;;; +08E2;ARABIC DISPUTED END OF AYAH;Cf;0;AN;;;;;N;;;;; +08E3;ARABIC TURNED DAMMA BELOW;Mn;220;NSM;;;;;N;;;;; +08E4;ARABIC CURLY FATHA;Mn;230;NSM;;;;;N;;;;; +08E5;ARABIC CURLY DAMMA;Mn;230;NSM;;;;;N;;;;; +08E6;ARABIC CURLY KASRA;Mn;220;NSM;;;;;N;;;;; +08E7;ARABIC CURLY FATHATAN;Mn;230;NSM;;;;;N;;;;; +08E8;ARABIC CURLY DAMMATAN;Mn;230;NSM;;;;;N;;;;; +08E9;ARABIC CURLY KASRATAN;Mn;220;NSM;;;;;N;;;;; +08EA;ARABIC TONE ONE DOT ABOVE;Mn;230;NSM;;;;;N;;;;; +08EB;ARABIC TONE TWO DOTS ABOVE;Mn;230;NSM;;;;;N;;;;; +08EC;ARABIC TONE LOOP ABOVE;Mn;230;NSM;;;;;N;;;;; +08ED;ARABIC TONE ONE DOT BELOW;Mn;220;NSM;;;;;N;;;;; +08EE;ARABIC TONE TWO DOTS BELOW;Mn;220;NSM;;;;;N;;;;; +08EF;ARABIC TONE LOOP BELOW;Mn;220;NSM;;;;;N;;;;; +08F0;ARABIC OPEN FATHATAN;Mn;27;NSM;;;;;N;;;;; +08F1;ARABIC OPEN DAMMATAN;Mn;28;NSM;;;;;N;;;;; +08F2;ARABIC OPEN KASRATAN;Mn;29;NSM;;;;;N;;;;; +08F3;ARABIC SMALL HIGH WAW;Mn;230;NSM;;;;;N;;;;; +08F4;ARABIC FATHA WITH RING;Mn;230;NSM;;;;;N;;;;; +08F5;ARABIC FATHA WITH DOT ABOVE;Mn;230;NSM;;;;;N;;;;; +08F6;ARABIC KASRA WITH DOT BELOW;Mn;220;NSM;;;;;N;;;;; +08F7;ARABIC LEFT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;; +08F8;ARABIC RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;; +08F9;ARABIC LEFT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;; +08FA;ARABIC RIGHT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;; +08FB;ARABIC DOUBLE RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;; +08FC;ARABIC DOUBLE RIGHT ARROWHEAD ABOVE WITH DOT;Mn;230;NSM;;;;;N;;;;; +08FD;ARABIC RIGHT ARROWHEAD ABOVE WITH DOT;Mn;230;NSM;;;;;N;;;;; +08FE;ARABIC DAMMA WITH DOT;Mn;230;NSM;;;;;N;;;;; +08FF;ARABIC MARK SIDEWAYS NOON GHUNNA;Mn;230;NSM;;;;;N;;;;; +0900;DEVANAGARI SIGN INVERTED CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +0901;DEVANAGARI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +0902;DEVANAGARI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +0903;DEVANAGARI SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0904;DEVANAGARI LETTER SHORT A;Lo;0;L;;;;;N;;;;; +0905;DEVANAGARI LETTER A;Lo;0;L;;;;;N;;;;; +0906;DEVANAGARI LETTER AA;Lo;0;L;;;;;N;;;;; +0907;DEVANAGARI LETTER I;Lo;0;L;;;;;N;;;;; +0908;DEVANAGARI LETTER II;Lo;0;L;;;;;N;;;;; +0909;DEVANAGARI LETTER U;Lo;0;L;;;;;N;;;;; +090A;DEVANAGARI LETTER UU;Lo;0;L;;;;;N;;;;; +090B;DEVANAGARI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +090C;DEVANAGARI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +090D;DEVANAGARI LETTER CANDRA E;Lo;0;L;;;;;N;;;;; +090E;DEVANAGARI LETTER SHORT E;Lo;0;L;;;;;N;;;;; +090F;DEVANAGARI LETTER E;Lo;0;L;;;;;N;;;;; +0910;DEVANAGARI LETTER AI;Lo;0;L;;;;;N;;;;; +0911;DEVANAGARI LETTER CANDRA O;Lo;0;L;;;;;N;;;;; +0912;DEVANAGARI LETTER SHORT O;Lo;0;L;;;;;N;;;;; +0913;DEVANAGARI LETTER O;Lo;0;L;;;;;N;;;;; +0914;DEVANAGARI LETTER AU;Lo;0;L;;;;;N;;;;; +0915;DEVANAGARI LETTER KA;Lo;0;L;;;;;N;;;;; +0916;DEVANAGARI LETTER KHA;Lo;0;L;;;;;N;;;;; +0917;DEVANAGARI LETTER GA;Lo;0;L;;;;;N;;;;; +0918;DEVANAGARI LETTER GHA;Lo;0;L;;;;;N;;;;; +0919;DEVANAGARI LETTER NGA;Lo;0;L;;;;;N;;;;; +091A;DEVANAGARI LETTER CA;Lo;0;L;;;;;N;;;;; +091B;DEVANAGARI LETTER CHA;Lo;0;L;;;;;N;;;;; +091C;DEVANAGARI LETTER JA;Lo;0;L;;;;;N;;;;; +091D;DEVANAGARI LETTER JHA;Lo;0;L;;;;;N;;;;; +091E;DEVANAGARI LETTER NYA;Lo;0;L;;;;;N;;;;; +091F;DEVANAGARI LETTER TTA;Lo;0;L;;;;;N;;;;; +0920;DEVANAGARI LETTER TTHA;Lo;0;L;;;;;N;;;;; +0921;DEVANAGARI LETTER DDA;Lo;0;L;;;;;N;;;;; +0922;DEVANAGARI LETTER DDHA;Lo;0;L;;;;;N;;;;; +0923;DEVANAGARI LETTER NNA;Lo;0;L;;;;;N;;;;; +0924;DEVANAGARI LETTER TA;Lo;0;L;;;;;N;;;;; +0925;DEVANAGARI LETTER THA;Lo;0;L;;;;;N;;;;; +0926;DEVANAGARI LETTER DA;Lo;0;L;;;;;N;;;;; +0927;DEVANAGARI LETTER DHA;Lo;0;L;;;;;N;;;;; +0928;DEVANAGARI LETTER NA;Lo;0;L;;;;;N;;;;; +0929;DEVANAGARI LETTER NNNA;Lo;0;L;0928 093C;;;;N;;;;; +092A;DEVANAGARI LETTER PA;Lo;0;L;;;;;N;;;;; +092B;DEVANAGARI LETTER PHA;Lo;0;L;;;;;N;;;;; +092C;DEVANAGARI LETTER BA;Lo;0;L;;;;;N;;;;; +092D;DEVANAGARI LETTER BHA;Lo;0;L;;;;;N;;;;; +092E;DEVANAGARI LETTER MA;Lo;0;L;;;;;N;;;;; +092F;DEVANAGARI LETTER YA;Lo;0;L;;;;;N;;;;; +0930;DEVANAGARI LETTER RA;Lo;0;L;;;;;N;;;;; +0931;DEVANAGARI LETTER RRA;Lo;0;L;0930 093C;;;;N;;;;; +0932;DEVANAGARI LETTER LA;Lo;0;L;;;;;N;;;;; +0933;DEVANAGARI LETTER LLA;Lo;0;L;;;;;N;;;;; +0934;DEVANAGARI LETTER LLLA;Lo;0;L;0933 093C;;;;N;;;;; +0935;DEVANAGARI LETTER VA;Lo;0;L;;;;;N;;;;; +0936;DEVANAGARI LETTER SHA;Lo;0;L;;;;;N;;;;; +0937;DEVANAGARI LETTER SSA;Lo;0;L;;;;;N;;;;; +0938;DEVANAGARI LETTER SA;Lo;0;L;;;;;N;;;;; +0939;DEVANAGARI LETTER HA;Lo;0;L;;;;;N;;;;; +093A;DEVANAGARI VOWEL SIGN OE;Mn;0;NSM;;;;;N;;;;; +093B;DEVANAGARI VOWEL SIGN OOE;Mc;0;L;;;;;N;;;;; +093C;DEVANAGARI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +093D;DEVANAGARI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +093E;DEVANAGARI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +093F;DEVANAGARI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +0940;DEVANAGARI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +0941;DEVANAGARI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +0942;DEVANAGARI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +0943;DEVANAGARI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +0944;DEVANAGARI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +0945;DEVANAGARI VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;; +0946;DEVANAGARI VOWEL SIGN SHORT E;Mn;0;NSM;;;;;N;;;;; +0947;DEVANAGARI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +0948;DEVANAGARI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +0949;DEVANAGARI VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;; +094A;DEVANAGARI VOWEL SIGN SHORT O;Mc;0;L;;;;;N;;;;; +094B;DEVANAGARI VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +094C;DEVANAGARI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +094D;DEVANAGARI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +094E;DEVANAGARI VOWEL SIGN PRISHTHAMATRA E;Mc;0;L;;;;;N;;;;; +094F;DEVANAGARI VOWEL SIGN AW;Mc;0;L;;;;;N;;;;; +0950;DEVANAGARI OM;Lo;0;L;;;;;N;;;;; +0951;DEVANAGARI STRESS SIGN UDATTA;Mn;230;NSM;;;;;N;;;;; +0952;DEVANAGARI STRESS SIGN ANUDATTA;Mn;220;NSM;;;;;N;;;;; +0953;DEVANAGARI GRAVE ACCENT;Mn;230;NSM;;;;;N;;;;; +0954;DEVANAGARI ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;; +0955;DEVANAGARI VOWEL SIGN CANDRA LONG E;Mn;0;NSM;;;;;N;;;;; +0956;DEVANAGARI VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;; +0957;DEVANAGARI VOWEL SIGN UUE;Mn;0;NSM;;;;;N;;;;; +0958;DEVANAGARI LETTER QA;Lo;0;L;0915 093C;;;;N;;;;; +0959;DEVANAGARI LETTER KHHA;Lo;0;L;0916 093C;;;;N;;;;; +095A;DEVANAGARI LETTER GHHA;Lo;0;L;0917 093C;;;;N;;;;; +095B;DEVANAGARI LETTER ZA;Lo;0;L;091C 093C;;;;N;;;;; +095C;DEVANAGARI LETTER DDDHA;Lo;0;L;0921 093C;;;;N;;;;; +095D;DEVANAGARI LETTER RHA;Lo;0;L;0922 093C;;;;N;;;;; +095E;DEVANAGARI LETTER FA;Lo;0;L;092B 093C;;;;N;;;;; +095F;DEVANAGARI LETTER YYA;Lo;0;L;092F 093C;;;;N;;;;; +0960;DEVANAGARI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +0961;DEVANAGARI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +0962;DEVANAGARI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +0963;DEVANAGARI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +0964;DEVANAGARI DANDA;Po;0;L;;;;;N;;;;; +0965;DEVANAGARI DOUBLE DANDA;Po;0;L;;;;;N;;;;; +0966;DEVANAGARI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0967;DEVANAGARI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0968;DEVANAGARI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0969;DEVANAGARI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +096A;DEVANAGARI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +096B;DEVANAGARI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +096C;DEVANAGARI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +096D;DEVANAGARI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +096E;DEVANAGARI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +096F;DEVANAGARI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0970;DEVANAGARI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; +0971;DEVANAGARI SIGN HIGH SPACING DOT;Lm;0;L;;;;;N;;;;; +0972;DEVANAGARI LETTER CANDRA A;Lo;0;L;;;;;N;;;;; +0973;DEVANAGARI LETTER OE;Lo;0;L;;;;;N;;;;; +0974;DEVANAGARI LETTER OOE;Lo;0;L;;;;;N;;;;; +0975;DEVANAGARI LETTER AW;Lo;0;L;;;;;N;;;;; +0976;DEVANAGARI LETTER UE;Lo;0;L;;;;;N;;;;; +0977;DEVANAGARI LETTER UUE;Lo;0;L;;;;;N;;;;; +0978;DEVANAGARI LETTER MARWARI DDA;Lo;0;L;;;;;N;;;;; +0979;DEVANAGARI LETTER ZHA;Lo;0;L;;;;;N;;;;; +097A;DEVANAGARI LETTER HEAVY YA;Lo;0;L;;;;;N;;;;; +097B;DEVANAGARI LETTER GGA;Lo;0;L;;;;;N;;;;; +097C;DEVANAGARI LETTER JJA;Lo;0;L;;;;;N;;;;; +097D;DEVANAGARI LETTER GLOTTAL STOP;Lo;0;L;;;;;N;;;;; +097E;DEVANAGARI LETTER DDDA;Lo;0;L;;;;;N;;;;; +097F;DEVANAGARI LETTER BBA;Lo;0;L;;;;;N;;;;; +0980;BENGALI ANJI;Lo;0;L;;;;;N;;;;; +0981;BENGALI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +0982;BENGALI SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; +0983;BENGALI SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0985;BENGALI LETTER A;Lo;0;L;;;;;N;;;;; +0986;BENGALI LETTER AA;Lo;0;L;;;;;N;;;;; +0987;BENGALI LETTER I;Lo;0;L;;;;;N;;;;; +0988;BENGALI LETTER II;Lo;0;L;;;;;N;;;;; +0989;BENGALI LETTER U;Lo;0;L;;;;;N;;;;; +098A;BENGALI LETTER UU;Lo;0;L;;;;;N;;;;; +098B;BENGALI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +098C;BENGALI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +098F;BENGALI LETTER E;Lo;0;L;;;;;N;;;;; +0990;BENGALI LETTER AI;Lo;0;L;;;;;N;;;;; +0993;BENGALI LETTER O;Lo;0;L;;;;;N;;;;; +0994;BENGALI LETTER AU;Lo;0;L;;;;;N;;;;; +0995;BENGALI LETTER KA;Lo;0;L;;;;;N;;;;; +0996;BENGALI LETTER KHA;Lo;0;L;;;;;N;;;;; +0997;BENGALI LETTER GA;Lo;0;L;;;;;N;;;;; +0998;BENGALI LETTER GHA;Lo;0;L;;;;;N;;;;; +0999;BENGALI LETTER NGA;Lo;0;L;;;;;N;;;;; +099A;BENGALI LETTER CA;Lo;0;L;;;;;N;;;;; +099B;BENGALI LETTER CHA;Lo;0;L;;;;;N;;;;; +099C;BENGALI LETTER JA;Lo;0;L;;;;;N;;;;; +099D;BENGALI LETTER JHA;Lo;0;L;;;;;N;;;;; +099E;BENGALI LETTER NYA;Lo;0;L;;;;;N;;;;; +099F;BENGALI LETTER TTA;Lo;0;L;;;;;N;;;;; +09A0;BENGALI LETTER TTHA;Lo;0;L;;;;;N;;;;; +09A1;BENGALI LETTER DDA;Lo;0;L;;;;;N;;;;; +09A2;BENGALI LETTER DDHA;Lo;0;L;;;;;N;;;;; +09A3;BENGALI LETTER NNA;Lo;0;L;;;;;N;;;;; +09A4;BENGALI LETTER TA;Lo;0;L;;;;;N;;;;; +09A5;BENGALI LETTER THA;Lo;0;L;;;;;N;;;;; +09A6;BENGALI LETTER DA;Lo;0;L;;;;;N;;;;; +09A7;BENGALI LETTER DHA;Lo;0;L;;;;;N;;;;; +09A8;BENGALI LETTER NA;Lo;0;L;;;;;N;;;;; +09AA;BENGALI LETTER PA;Lo;0;L;;;;;N;;;;; +09AB;BENGALI LETTER PHA;Lo;0;L;;;;;N;;;;; +09AC;BENGALI LETTER BA;Lo;0;L;;;;;N;;;;; +09AD;BENGALI LETTER BHA;Lo;0;L;;;;;N;;;;; +09AE;BENGALI LETTER MA;Lo;0;L;;;;;N;;;;; +09AF;BENGALI LETTER YA;Lo;0;L;;;;;N;;;;; +09B0;BENGALI LETTER RA;Lo;0;L;;;;;N;;;;; +09B2;BENGALI LETTER LA;Lo;0;L;;;;;N;;;;; +09B6;BENGALI LETTER SHA;Lo;0;L;;;;;N;;;;; +09B7;BENGALI LETTER SSA;Lo;0;L;;;;;N;;;;; +09B8;BENGALI LETTER SA;Lo;0;L;;;;;N;;;;; +09B9;BENGALI LETTER HA;Lo;0;L;;;;;N;;;;; +09BC;BENGALI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +09BD;BENGALI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +09BE;BENGALI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +09BF;BENGALI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +09C0;BENGALI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +09C1;BENGALI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +09C2;BENGALI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +09C3;BENGALI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +09C4;BENGALI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +09C7;BENGALI VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +09C8;BENGALI VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +09CB;BENGALI VOWEL SIGN O;Mc;0;L;09C7 09BE;;;;N;;;;; +09CC;BENGALI VOWEL SIGN AU;Mc;0;L;09C7 09D7;;;;N;;;;; +09CD;BENGALI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +09CE;BENGALI LETTER KHANDA TA;Lo;0;L;;;;;N;;;;; +09D7;BENGALI AU LENGTH MARK;Mc;0;L;;;;;N;;;;; +09DC;BENGALI LETTER RRA;Lo;0;L;09A1 09BC;;;;N;;;;; +09DD;BENGALI LETTER RHA;Lo;0;L;09A2 09BC;;;;N;;;;; +09DF;BENGALI LETTER YYA;Lo;0;L;09AF 09BC;;;;N;;;;; +09E0;BENGALI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +09E1;BENGALI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +09E2;BENGALI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +09E3;BENGALI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +09E6;BENGALI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +09E7;BENGALI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +09E8;BENGALI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +09E9;BENGALI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +09EA;BENGALI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +09EB;BENGALI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +09EC;BENGALI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +09ED;BENGALI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +09EE;BENGALI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +09EF;BENGALI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +09F0;BENGALI LETTER RA WITH MIDDLE DIAGONAL;Lo;0;L;;;;;N;;;;; +09F1;BENGALI LETTER RA WITH LOWER DIAGONAL;Lo;0;L;;;;;N;BENGALI LETTER VA WITH LOWER DIAGONAL;;;; +09F2;BENGALI RUPEE MARK;Sc;0;ET;;;;;N;;;;; +09F3;BENGALI RUPEE SIGN;Sc;0;ET;;;;;N;;;;; +09F4;BENGALI CURRENCY NUMERATOR ONE;No;0;L;;;;1/16;N;;;;; +09F5;BENGALI CURRENCY NUMERATOR TWO;No;0;L;;;;1/8;N;;;;; +09F6;BENGALI CURRENCY NUMERATOR THREE;No;0;L;;;;3/16;N;;;;; +09F7;BENGALI CURRENCY NUMERATOR FOUR;No;0;L;;;;1/4;N;;;;; +09F8;BENGALI CURRENCY NUMERATOR ONE LESS THAN THE DENOMINATOR;No;0;L;;;;3/4;N;;;;; +09F9;BENGALI CURRENCY DENOMINATOR SIXTEEN;No;0;L;;;;16;N;;;;; +09FA;BENGALI ISSHAR;So;0;L;;;;;N;;;;; +09FB;BENGALI GANDA MARK;Sc;0;ET;;;;;N;;;;; +0A01;GURMUKHI SIGN ADAK BINDI;Mn;0;NSM;;;;;N;;;;; +0A02;GURMUKHI SIGN BINDI;Mn;0;NSM;;;;;N;;;;; +0A03;GURMUKHI SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0A05;GURMUKHI LETTER A;Lo;0;L;;;;;N;;;;; +0A06;GURMUKHI LETTER AA;Lo;0;L;;;;;N;;;;; +0A07;GURMUKHI LETTER I;Lo;0;L;;;;;N;;;;; +0A08;GURMUKHI LETTER II;Lo;0;L;;;;;N;;;;; +0A09;GURMUKHI LETTER U;Lo;0;L;;;;;N;;;;; +0A0A;GURMUKHI LETTER UU;Lo;0;L;;;;;N;;;;; +0A0F;GURMUKHI LETTER EE;Lo;0;L;;;;;N;;;;; +0A10;GURMUKHI LETTER AI;Lo;0;L;;;;;N;;;;; +0A13;GURMUKHI LETTER OO;Lo;0;L;;;;;N;;;;; +0A14;GURMUKHI LETTER AU;Lo;0;L;;;;;N;;;;; +0A15;GURMUKHI LETTER KA;Lo;0;L;;;;;N;;;;; +0A16;GURMUKHI LETTER KHA;Lo;0;L;;;;;N;;;;; +0A17;GURMUKHI LETTER GA;Lo;0;L;;;;;N;;;;; +0A18;GURMUKHI LETTER GHA;Lo;0;L;;;;;N;;;;; +0A19;GURMUKHI LETTER NGA;Lo;0;L;;;;;N;;;;; +0A1A;GURMUKHI LETTER CA;Lo;0;L;;;;;N;;;;; +0A1B;GURMUKHI LETTER CHA;Lo;0;L;;;;;N;;;;; +0A1C;GURMUKHI LETTER JA;Lo;0;L;;;;;N;;;;; +0A1D;GURMUKHI LETTER JHA;Lo;0;L;;;;;N;;;;; +0A1E;GURMUKHI LETTER NYA;Lo;0;L;;;;;N;;;;; +0A1F;GURMUKHI LETTER TTA;Lo;0;L;;;;;N;;;;; +0A20;GURMUKHI LETTER TTHA;Lo;0;L;;;;;N;;;;; +0A21;GURMUKHI LETTER DDA;Lo;0;L;;;;;N;;;;; +0A22;GURMUKHI LETTER DDHA;Lo;0;L;;;;;N;;;;; +0A23;GURMUKHI LETTER NNA;Lo;0;L;;;;;N;;;;; +0A24;GURMUKHI LETTER TA;Lo;0;L;;;;;N;;;;; +0A25;GURMUKHI LETTER THA;Lo;0;L;;;;;N;;;;; +0A26;GURMUKHI LETTER DA;Lo;0;L;;;;;N;;;;; +0A27;GURMUKHI LETTER DHA;Lo;0;L;;;;;N;;;;; +0A28;GURMUKHI LETTER NA;Lo;0;L;;;;;N;;;;; +0A2A;GURMUKHI LETTER PA;Lo;0;L;;;;;N;;;;; +0A2B;GURMUKHI LETTER PHA;Lo;0;L;;;;;N;;;;; +0A2C;GURMUKHI LETTER BA;Lo;0;L;;;;;N;;;;; +0A2D;GURMUKHI LETTER BHA;Lo;0;L;;;;;N;;;;; +0A2E;GURMUKHI LETTER MA;Lo;0;L;;;;;N;;;;; +0A2F;GURMUKHI LETTER YA;Lo;0;L;;;;;N;;;;; +0A30;GURMUKHI LETTER RA;Lo;0;L;;;;;N;;;;; +0A32;GURMUKHI LETTER LA;Lo;0;L;;;;;N;;;;; +0A33;GURMUKHI LETTER LLA;Lo;0;L;0A32 0A3C;;;;N;;;;; +0A35;GURMUKHI LETTER VA;Lo;0;L;;;;;N;;;;; +0A36;GURMUKHI LETTER SHA;Lo;0;L;0A38 0A3C;;;;N;;;;; +0A38;GURMUKHI LETTER SA;Lo;0;L;;;;;N;;;;; +0A39;GURMUKHI LETTER HA;Lo;0;L;;;;;N;;;;; +0A3C;GURMUKHI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +0A3E;GURMUKHI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +0A3F;GURMUKHI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +0A40;GURMUKHI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +0A41;GURMUKHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +0A42;GURMUKHI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +0A47;GURMUKHI VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;; +0A48;GURMUKHI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +0A4B;GURMUKHI VOWEL SIGN OO;Mn;0;NSM;;;;;N;;;;; +0A4C;GURMUKHI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; +0A4D;GURMUKHI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0A51;GURMUKHI SIGN UDAAT;Mn;0;NSM;;;;;N;;;;; +0A59;GURMUKHI LETTER KHHA;Lo;0;L;0A16 0A3C;;;;N;;;;; +0A5A;GURMUKHI LETTER GHHA;Lo;0;L;0A17 0A3C;;;;N;;;;; +0A5B;GURMUKHI LETTER ZA;Lo;0;L;0A1C 0A3C;;;;N;;;;; +0A5C;GURMUKHI LETTER RRA;Lo;0;L;;;;;N;;;;; +0A5E;GURMUKHI LETTER FA;Lo;0;L;0A2B 0A3C;;;;N;;;;; +0A66;GURMUKHI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0A67;GURMUKHI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0A68;GURMUKHI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0A69;GURMUKHI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0A6A;GURMUKHI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0A6B;GURMUKHI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0A6C;GURMUKHI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0A6D;GURMUKHI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0A6E;GURMUKHI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0A6F;GURMUKHI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0A70;GURMUKHI TIPPI;Mn;0;NSM;;;;;N;;;;; +0A71;GURMUKHI ADDAK;Mn;0;NSM;;;;;N;;;;; +0A72;GURMUKHI IRI;Lo;0;L;;;;;N;;;;; +0A73;GURMUKHI URA;Lo;0;L;;;;;N;;;;; +0A74;GURMUKHI EK ONKAR;Lo;0;L;;;;;N;;;;; +0A75;GURMUKHI SIGN YAKASH;Mn;0;NSM;;;;;N;;;;; +0A81;GUJARATI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +0A82;GUJARATI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +0A83;GUJARATI SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0A85;GUJARATI LETTER A;Lo;0;L;;;;;N;;;;; +0A86;GUJARATI LETTER AA;Lo;0;L;;;;;N;;;;; +0A87;GUJARATI LETTER I;Lo;0;L;;;;;N;;;;; +0A88;GUJARATI LETTER II;Lo;0;L;;;;;N;;;;; +0A89;GUJARATI LETTER U;Lo;0;L;;;;;N;;;;; +0A8A;GUJARATI LETTER UU;Lo;0;L;;;;;N;;;;; +0A8B;GUJARATI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +0A8C;GUJARATI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +0A8D;GUJARATI VOWEL CANDRA E;Lo;0;L;;;;;N;;;;; +0A8F;GUJARATI LETTER E;Lo;0;L;;;;;N;;;;; +0A90;GUJARATI LETTER AI;Lo;0;L;;;;;N;;;;; +0A91;GUJARATI VOWEL CANDRA O;Lo;0;L;;;;;N;;;;; +0A93;GUJARATI LETTER O;Lo;0;L;;;;;N;;;;; +0A94;GUJARATI LETTER AU;Lo;0;L;;;;;N;;;;; +0A95;GUJARATI LETTER KA;Lo;0;L;;;;;N;;;;; +0A96;GUJARATI LETTER KHA;Lo;0;L;;;;;N;;;;; +0A97;GUJARATI LETTER GA;Lo;0;L;;;;;N;;;;; +0A98;GUJARATI LETTER GHA;Lo;0;L;;;;;N;;;;; +0A99;GUJARATI LETTER NGA;Lo;0;L;;;;;N;;;;; +0A9A;GUJARATI LETTER CA;Lo;0;L;;;;;N;;;;; +0A9B;GUJARATI LETTER CHA;Lo;0;L;;;;;N;;;;; +0A9C;GUJARATI LETTER JA;Lo;0;L;;;;;N;;;;; +0A9D;GUJARATI LETTER JHA;Lo;0;L;;;;;N;;;;; +0A9E;GUJARATI LETTER NYA;Lo;0;L;;;;;N;;;;; +0A9F;GUJARATI LETTER TTA;Lo;0;L;;;;;N;;;;; +0AA0;GUJARATI LETTER TTHA;Lo;0;L;;;;;N;;;;; +0AA1;GUJARATI LETTER DDA;Lo;0;L;;;;;N;;;;; +0AA2;GUJARATI LETTER DDHA;Lo;0;L;;;;;N;;;;; +0AA3;GUJARATI LETTER NNA;Lo;0;L;;;;;N;;;;; +0AA4;GUJARATI LETTER TA;Lo;0;L;;;;;N;;;;; +0AA5;GUJARATI LETTER THA;Lo;0;L;;;;;N;;;;; +0AA6;GUJARATI LETTER DA;Lo;0;L;;;;;N;;;;; +0AA7;GUJARATI LETTER DHA;Lo;0;L;;;;;N;;;;; +0AA8;GUJARATI LETTER NA;Lo;0;L;;;;;N;;;;; +0AAA;GUJARATI LETTER PA;Lo;0;L;;;;;N;;;;; +0AAB;GUJARATI LETTER PHA;Lo;0;L;;;;;N;;;;; +0AAC;GUJARATI LETTER BA;Lo;0;L;;;;;N;;;;; +0AAD;GUJARATI LETTER BHA;Lo;0;L;;;;;N;;;;; +0AAE;GUJARATI LETTER MA;Lo;0;L;;;;;N;;;;; +0AAF;GUJARATI LETTER YA;Lo;0;L;;;;;N;;;;; +0AB0;GUJARATI LETTER RA;Lo;0;L;;;;;N;;;;; +0AB2;GUJARATI LETTER LA;Lo;0;L;;;;;N;;;;; +0AB3;GUJARATI LETTER LLA;Lo;0;L;;;;;N;;;;; +0AB5;GUJARATI LETTER VA;Lo;0;L;;;;;N;;;;; +0AB6;GUJARATI LETTER SHA;Lo;0;L;;;;;N;;;;; +0AB7;GUJARATI LETTER SSA;Lo;0;L;;;;;N;;;;; +0AB8;GUJARATI LETTER SA;Lo;0;L;;;;;N;;;;; +0AB9;GUJARATI LETTER HA;Lo;0;L;;;;;N;;;;; +0ABC;GUJARATI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +0ABD;GUJARATI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +0ABE;GUJARATI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +0ABF;GUJARATI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +0AC0;GUJARATI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +0AC1;GUJARATI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +0AC2;GUJARATI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +0AC3;GUJARATI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +0AC4;GUJARATI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +0AC5;GUJARATI VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;; +0AC7;GUJARATI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +0AC8;GUJARATI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +0AC9;GUJARATI VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;; +0ACB;GUJARATI VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +0ACC;GUJARATI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +0ACD;GUJARATI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0AD0;GUJARATI OM;Lo;0;L;;;;;N;;;;; +0AE0;GUJARATI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +0AE1;GUJARATI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +0AE2;GUJARATI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +0AE3;GUJARATI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +0AE6;GUJARATI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0AE7;GUJARATI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0AE8;GUJARATI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0AE9;GUJARATI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0AEA;GUJARATI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0AEB;GUJARATI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0AEC;GUJARATI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0AED;GUJARATI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0AEE;GUJARATI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0AEF;GUJARATI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0AF0;GUJARATI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; +0AF1;GUJARATI RUPEE SIGN;Sc;0;ET;;;;;N;;;;; +0AF9;GUJARATI LETTER ZHA;Lo;0;L;;;;;N;;;;; +0B01;ORIYA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +0B02;ORIYA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; +0B03;ORIYA SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0B05;ORIYA LETTER A;Lo;0;L;;;;;N;;;;; +0B06;ORIYA LETTER AA;Lo;0;L;;;;;N;;;;; +0B07;ORIYA LETTER I;Lo;0;L;;;;;N;;;;; +0B08;ORIYA LETTER II;Lo;0;L;;;;;N;;;;; +0B09;ORIYA LETTER U;Lo;0;L;;;;;N;;;;; +0B0A;ORIYA LETTER UU;Lo;0;L;;;;;N;;;;; +0B0B;ORIYA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +0B0C;ORIYA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +0B0F;ORIYA LETTER E;Lo;0;L;;;;;N;;;;; +0B10;ORIYA LETTER AI;Lo;0;L;;;;;N;;;;; +0B13;ORIYA LETTER O;Lo;0;L;;;;;N;;;;; +0B14;ORIYA LETTER AU;Lo;0;L;;;;;N;;;;; +0B15;ORIYA LETTER KA;Lo;0;L;;;;;N;;;;; +0B16;ORIYA LETTER KHA;Lo;0;L;;;;;N;;;;; +0B17;ORIYA LETTER GA;Lo;0;L;;;;;N;;;;; +0B18;ORIYA LETTER GHA;Lo;0;L;;;;;N;;;;; +0B19;ORIYA LETTER NGA;Lo;0;L;;;;;N;;;;; +0B1A;ORIYA LETTER CA;Lo;0;L;;;;;N;;;;; +0B1B;ORIYA LETTER CHA;Lo;0;L;;;;;N;;;;; +0B1C;ORIYA LETTER JA;Lo;0;L;;;;;N;;;;; +0B1D;ORIYA LETTER JHA;Lo;0;L;;;;;N;;;;; +0B1E;ORIYA LETTER NYA;Lo;0;L;;;;;N;;;;; +0B1F;ORIYA LETTER TTA;Lo;0;L;;;;;N;;;;; +0B20;ORIYA LETTER TTHA;Lo;0;L;;;;;N;;;;; +0B21;ORIYA LETTER DDA;Lo;0;L;;;;;N;;;;; +0B22;ORIYA LETTER DDHA;Lo;0;L;;;;;N;;;;; +0B23;ORIYA LETTER NNA;Lo;0;L;;;;;N;;;;; +0B24;ORIYA LETTER TA;Lo;0;L;;;;;N;;;;; +0B25;ORIYA LETTER THA;Lo;0;L;;;;;N;;;;; +0B26;ORIYA LETTER DA;Lo;0;L;;;;;N;;;;; +0B27;ORIYA LETTER DHA;Lo;0;L;;;;;N;;;;; +0B28;ORIYA LETTER NA;Lo;0;L;;;;;N;;;;; +0B2A;ORIYA LETTER PA;Lo;0;L;;;;;N;;;;; +0B2B;ORIYA LETTER PHA;Lo;0;L;;;;;N;;;;; +0B2C;ORIYA LETTER BA;Lo;0;L;;;;;N;;;;; +0B2D;ORIYA LETTER BHA;Lo;0;L;;;;;N;;;;; +0B2E;ORIYA LETTER MA;Lo;0;L;;;;;N;;;;; +0B2F;ORIYA LETTER YA;Lo;0;L;;;;;N;;;;; +0B30;ORIYA LETTER RA;Lo;0;L;;;;;N;;;;; +0B32;ORIYA LETTER LA;Lo;0;L;;;;;N;;;;; +0B33;ORIYA LETTER LLA;Lo;0;L;;;;;N;;;;; +0B35;ORIYA LETTER VA;Lo;0;L;;;;;N;;;;; +0B36;ORIYA LETTER SHA;Lo;0;L;;;;;N;;;;; +0B37;ORIYA LETTER SSA;Lo;0;L;;;;;N;;;;; +0B38;ORIYA LETTER SA;Lo;0;L;;;;;N;;;;; +0B39;ORIYA LETTER HA;Lo;0;L;;;;;N;;;;; +0B3C;ORIYA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +0B3D;ORIYA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +0B3E;ORIYA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +0B3F;ORIYA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +0B40;ORIYA VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +0B41;ORIYA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +0B42;ORIYA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +0B43;ORIYA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +0B44;ORIYA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +0B47;ORIYA VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +0B48;ORIYA VOWEL SIGN AI;Mc;0;L;0B47 0B56;;;;N;;;;; +0B4B;ORIYA VOWEL SIGN O;Mc;0;L;0B47 0B3E;;;;N;;;;; +0B4C;ORIYA VOWEL SIGN AU;Mc;0;L;0B47 0B57;;;;N;;;;; +0B4D;ORIYA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0B56;ORIYA AI LENGTH MARK;Mn;0;NSM;;;;;N;;;;; +0B57;ORIYA AU LENGTH MARK;Mc;0;L;;;;;N;;;;; +0B5C;ORIYA LETTER RRA;Lo;0;L;0B21 0B3C;;;;N;;;;; +0B5D;ORIYA LETTER RHA;Lo;0;L;0B22 0B3C;;;;N;;;;; +0B5F;ORIYA LETTER YYA;Lo;0;L;;;;;N;;;;; +0B60;ORIYA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +0B61;ORIYA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +0B62;ORIYA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +0B63;ORIYA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +0B66;ORIYA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0B67;ORIYA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0B68;ORIYA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0B69;ORIYA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0B6A;ORIYA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0B6B;ORIYA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0B6C;ORIYA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0B6D;ORIYA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0B6E;ORIYA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0B6F;ORIYA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0B70;ORIYA ISSHAR;So;0;L;;;;;N;;;;; +0B71;ORIYA LETTER WA;Lo;0;L;;;;;N;;;;; +0B72;ORIYA FRACTION ONE QUARTER;No;0;L;;;;1/4;N;;;;; +0B73;ORIYA FRACTION ONE HALF;No;0;L;;;;1/2;N;;;;; +0B74;ORIYA FRACTION THREE QUARTERS;No;0;L;;;;3/4;N;;;;; +0B75;ORIYA FRACTION ONE SIXTEENTH;No;0;L;;;;1/16;N;;;;; +0B76;ORIYA FRACTION ONE EIGHTH;No;0;L;;;;1/8;N;;;;; +0B77;ORIYA FRACTION THREE SIXTEENTHS;No;0;L;;;;3/16;N;;;;; +0B82;TAMIL SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +0B83;TAMIL SIGN VISARGA;Lo;0;L;;;;;N;;;;; +0B85;TAMIL LETTER A;Lo;0;L;;;;;N;;;;; +0B86;TAMIL LETTER AA;Lo;0;L;;;;;N;;;;; +0B87;TAMIL LETTER I;Lo;0;L;;;;;N;;;;; +0B88;TAMIL LETTER II;Lo;0;L;;;;;N;;;;; +0B89;TAMIL LETTER U;Lo;0;L;;;;;N;;;;; +0B8A;TAMIL LETTER UU;Lo;0;L;;;;;N;;;;; +0B8E;TAMIL LETTER E;Lo;0;L;;;;;N;;;;; +0B8F;TAMIL LETTER EE;Lo;0;L;;;;;N;;;;; +0B90;TAMIL LETTER AI;Lo;0;L;;;;;N;;;;; +0B92;TAMIL LETTER O;Lo;0;L;;;;;N;;;;; +0B93;TAMIL LETTER OO;Lo;0;L;;;;;N;;;;; +0B94;TAMIL LETTER AU;Lo;0;L;0B92 0BD7;;;;N;;;;; +0B95;TAMIL LETTER KA;Lo;0;L;;;;;N;;;;; +0B99;TAMIL LETTER NGA;Lo;0;L;;;;;N;;;;; +0B9A;TAMIL LETTER CA;Lo;0;L;;;;;N;;;;; +0B9C;TAMIL LETTER JA;Lo;0;L;;;;;N;;;;; +0B9E;TAMIL LETTER NYA;Lo;0;L;;;;;N;;;;; +0B9F;TAMIL LETTER TTA;Lo;0;L;;;;;N;;;;; +0BA3;TAMIL LETTER NNA;Lo;0;L;;;;;N;;;;; +0BA4;TAMIL LETTER TA;Lo;0;L;;;;;N;;;;; +0BA8;TAMIL LETTER NA;Lo;0;L;;;;;N;;;;; +0BA9;TAMIL LETTER NNNA;Lo;0;L;;;;;N;;;;; +0BAA;TAMIL LETTER PA;Lo;0;L;;;;;N;;;;; +0BAE;TAMIL LETTER MA;Lo;0;L;;;;;N;;;;; +0BAF;TAMIL LETTER YA;Lo;0;L;;;;;N;;;;; +0BB0;TAMIL LETTER RA;Lo;0;L;;;;;N;;;;; +0BB1;TAMIL LETTER RRA;Lo;0;L;;;;;N;;;;; +0BB2;TAMIL LETTER LA;Lo;0;L;;;;;N;;;;; +0BB3;TAMIL LETTER LLA;Lo;0;L;;;;;N;;;;; +0BB4;TAMIL LETTER LLLA;Lo;0;L;;;;;N;;;;; +0BB5;TAMIL LETTER VA;Lo;0;L;;;;;N;;;;; +0BB6;TAMIL LETTER SHA;Lo;0;L;;;;;N;;;;; +0BB7;TAMIL LETTER SSA;Lo;0;L;;;;;N;;;;; +0BB8;TAMIL LETTER SA;Lo;0;L;;;;;N;;;;; +0BB9;TAMIL LETTER HA;Lo;0;L;;;;;N;;;;; +0BBE;TAMIL VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +0BBF;TAMIL VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +0BC0;TAMIL VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +0BC1;TAMIL VOWEL SIGN U;Mc;0;L;;;;;N;;;;; +0BC2;TAMIL VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; +0BC6;TAMIL VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +0BC7;TAMIL VOWEL SIGN EE;Mc;0;L;;;;;N;;;;; +0BC8;TAMIL VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +0BCA;TAMIL VOWEL SIGN O;Mc;0;L;0BC6 0BBE;;;;N;;;;; +0BCB;TAMIL VOWEL SIGN OO;Mc;0;L;0BC7 0BBE;;;;N;;;;; +0BCC;TAMIL VOWEL SIGN AU;Mc;0;L;0BC6 0BD7;;;;N;;;;; +0BCD;TAMIL SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0BD0;TAMIL OM;Lo;0;L;;;;;N;;;;; +0BD7;TAMIL AU LENGTH MARK;Mc;0;L;;;;;N;;;;; +0BE6;TAMIL DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0BE7;TAMIL DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0BE8;TAMIL DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0BE9;TAMIL DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0BEA;TAMIL DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0BEB;TAMIL DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0BEC;TAMIL DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0BED;TAMIL DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0BEE;TAMIL DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0BEF;TAMIL DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0BF0;TAMIL NUMBER TEN;No;0;L;;;;10;N;;;;; +0BF1;TAMIL NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;; +0BF2;TAMIL NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;; +0BF3;TAMIL DAY SIGN;So;0;ON;;;;;N;;;;; +0BF4;TAMIL MONTH SIGN;So;0;ON;;;;;N;;;;; +0BF5;TAMIL YEAR SIGN;So;0;ON;;;;;N;;;;; +0BF6;TAMIL DEBIT SIGN;So;0;ON;;;;;N;;;;; +0BF7;TAMIL CREDIT SIGN;So;0;ON;;;;;N;;;;; +0BF8;TAMIL AS ABOVE SIGN;So;0;ON;;;;;N;;;;; +0BF9;TAMIL RUPEE SIGN;Sc;0;ET;;;;;N;;;;; +0BFA;TAMIL NUMBER SIGN;So;0;ON;;;;;N;;;;; +0C00;TELUGU SIGN COMBINING CANDRABINDU ABOVE;Mn;0;NSM;;;;;N;;;;; +0C01;TELUGU SIGN CANDRABINDU;Mc;0;L;;;;;N;;;;; +0C02;TELUGU SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; +0C03;TELUGU SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0C05;TELUGU LETTER A;Lo;0;L;;;;;N;;;;; +0C06;TELUGU LETTER AA;Lo;0;L;;;;;N;;;;; +0C07;TELUGU LETTER I;Lo;0;L;;;;;N;;;;; +0C08;TELUGU LETTER II;Lo;0;L;;;;;N;;;;; +0C09;TELUGU LETTER U;Lo;0;L;;;;;N;;;;; +0C0A;TELUGU LETTER UU;Lo;0;L;;;;;N;;;;; +0C0B;TELUGU LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +0C0C;TELUGU LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +0C0E;TELUGU LETTER E;Lo;0;L;;;;;N;;;;; +0C0F;TELUGU LETTER EE;Lo;0;L;;;;;N;;;;; +0C10;TELUGU LETTER AI;Lo;0;L;;;;;N;;;;; +0C12;TELUGU LETTER O;Lo;0;L;;;;;N;;;;; +0C13;TELUGU LETTER OO;Lo;0;L;;;;;N;;;;; +0C14;TELUGU LETTER AU;Lo;0;L;;;;;N;;;;; +0C15;TELUGU LETTER KA;Lo;0;L;;;;;N;;;;; +0C16;TELUGU LETTER KHA;Lo;0;L;;;;;N;;;;; +0C17;TELUGU LETTER GA;Lo;0;L;;;;;N;;;;; +0C18;TELUGU LETTER GHA;Lo;0;L;;;;;N;;;;; +0C19;TELUGU LETTER NGA;Lo;0;L;;;;;N;;;;; +0C1A;TELUGU LETTER CA;Lo;0;L;;;;;N;;;;; +0C1B;TELUGU LETTER CHA;Lo;0;L;;;;;N;;;;; +0C1C;TELUGU LETTER JA;Lo;0;L;;;;;N;;;;; +0C1D;TELUGU LETTER JHA;Lo;0;L;;;;;N;;;;; +0C1E;TELUGU LETTER NYA;Lo;0;L;;;;;N;;;;; +0C1F;TELUGU LETTER TTA;Lo;0;L;;;;;N;;;;; +0C20;TELUGU LETTER TTHA;Lo;0;L;;;;;N;;;;; +0C21;TELUGU LETTER DDA;Lo;0;L;;;;;N;;;;; +0C22;TELUGU LETTER DDHA;Lo;0;L;;;;;N;;;;; +0C23;TELUGU LETTER NNA;Lo;0;L;;;;;N;;;;; +0C24;TELUGU LETTER TA;Lo;0;L;;;;;N;;;;; +0C25;TELUGU LETTER THA;Lo;0;L;;;;;N;;;;; +0C26;TELUGU LETTER DA;Lo;0;L;;;;;N;;;;; +0C27;TELUGU LETTER DHA;Lo;0;L;;;;;N;;;;; +0C28;TELUGU LETTER NA;Lo;0;L;;;;;N;;;;; +0C2A;TELUGU LETTER PA;Lo;0;L;;;;;N;;;;; +0C2B;TELUGU LETTER PHA;Lo;0;L;;;;;N;;;;; +0C2C;TELUGU LETTER BA;Lo;0;L;;;;;N;;;;; +0C2D;TELUGU LETTER BHA;Lo;0;L;;;;;N;;;;; +0C2E;TELUGU LETTER MA;Lo;0;L;;;;;N;;;;; +0C2F;TELUGU LETTER YA;Lo;0;L;;;;;N;;;;; +0C30;TELUGU LETTER RA;Lo;0;L;;;;;N;;;;; +0C31;TELUGU LETTER RRA;Lo;0;L;;;;;N;;;;; +0C32;TELUGU LETTER LA;Lo;0;L;;;;;N;;;;; +0C33;TELUGU LETTER LLA;Lo;0;L;;;;;N;;;;; +0C34;TELUGU LETTER LLLA;Lo;0;L;;;;;N;;;;; +0C35;TELUGU LETTER VA;Lo;0;L;;;;;N;;;;; +0C36;TELUGU LETTER SHA;Lo;0;L;;;;;N;;;;; +0C37;TELUGU LETTER SSA;Lo;0;L;;;;;N;;;;; +0C38;TELUGU LETTER SA;Lo;0;L;;;;;N;;;;; +0C39;TELUGU LETTER HA;Lo;0;L;;;;;N;;;;; +0C3D;TELUGU SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +0C3E;TELUGU VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;; +0C3F;TELUGU VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +0C40;TELUGU VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +0C41;TELUGU VOWEL SIGN U;Mc;0;L;;;;;N;;;;; +0C42;TELUGU VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; +0C43;TELUGU VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;; +0C44;TELUGU VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;; +0C46;TELUGU VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +0C47;TELUGU VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;; +0C48;TELUGU VOWEL SIGN AI;Mn;0;NSM;0C46 0C56;;;;N;;;;; +0C4A;TELUGU VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; +0C4B;TELUGU VOWEL SIGN OO;Mn;0;NSM;;;;;N;;;;; +0C4C;TELUGU VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; +0C4D;TELUGU SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0C55;TELUGU LENGTH MARK;Mn;84;NSM;;;;;N;;;;; +0C56;TELUGU AI LENGTH MARK;Mn;91;NSM;;;;;N;;;;; +0C58;TELUGU LETTER TSA;Lo;0;L;;;;;N;;;;; +0C59;TELUGU LETTER DZA;Lo;0;L;;;;;N;;;;; +0C5A;TELUGU LETTER RRRA;Lo;0;L;;;;;N;;;;; +0C60;TELUGU LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +0C61;TELUGU LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +0C62;TELUGU VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +0C63;TELUGU VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +0C66;TELUGU DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0C67;TELUGU DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0C68;TELUGU DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0C69;TELUGU DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0C6A;TELUGU DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0C6B;TELUGU DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0C6C;TELUGU DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0C6D;TELUGU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0C6E;TELUGU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0C6F;TELUGU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0C78;TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR;No;0;ON;;;;0;N;;;;; +0C79;TELUGU FRACTION DIGIT ONE FOR ODD POWERS OF FOUR;No;0;ON;;;;1;N;;;;; +0C7A;TELUGU FRACTION DIGIT TWO FOR ODD POWERS OF FOUR;No;0;ON;;;;2;N;;;;; +0C7B;TELUGU FRACTION DIGIT THREE FOR ODD POWERS OF FOUR;No;0;ON;;;;3;N;;;;; +0C7C;TELUGU FRACTION DIGIT ONE FOR EVEN POWERS OF FOUR;No;0;ON;;;;1;N;;;;; +0C7D;TELUGU FRACTION DIGIT TWO FOR EVEN POWERS OF FOUR;No;0;ON;;;;2;N;;;;; +0C7E;TELUGU FRACTION DIGIT THREE FOR EVEN POWERS OF FOUR;No;0;ON;;;;3;N;;;;; +0C7F;TELUGU SIGN TUUMU;So;0;L;;;;;N;;;;; +0C80;KANNADA SIGN SPACING CANDRABINDU;Lo;0;L;;;;;N;;;;; +0C81;KANNADA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +0C82;KANNADA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; +0C83;KANNADA SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0C85;KANNADA LETTER A;Lo;0;L;;;;;N;;;;; +0C86;KANNADA LETTER AA;Lo;0;L;;;;;N;;;;; +0C87;KANNADA LETTER I;Lo;0;L;;;;;N;;;;; +0C88;KANNADA LETTER II;Lo;0;L;;;;;N;;;;; +0C89;KANNADA LETTER U;Lo;0;L;;;;;N;;;;; +0C8A;KANNADA LETTER UU;Lo;0;L;;;;;N;;;;; +0C8B;KANNADA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +0C8C;KANNADA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +0C8E;KANNADA LETTER E;Lo;0;L;;;;;N;;;;; +0C8F;KANNADA LETTER EE;Lo;0;L;;;;;N;;;;; +0C90;KANNADA LETTER AI;Lo;0;L;;;;;N;;;;; +0C92;KANNADA LETTER O;Lo;0;L;;;;;N;;;;; +0C93;KANNADA LETTER OO;Lo;0;L;;;;;N;;;;; +0C94;KANNADA LETTER AU;Lo;0;L;;;;;N;;;;; +0C95;KANNADA LETTER KA;Lo;0;L;;;;;N;;;;; +0C96;KANNADA LETTER KHA;Lo;0;L;;;;;N;;;;; +0C97;KANNADA LETTER GA;Lo;0;L;;;;;N;;;;; +0C98;KANNADA LETTER GHA;Lo;0;L;;;;;N;;;;; +0C99;KANNADA LETTER NGA;Lo;0;L;;;;;N;;;;; +0C9A;KANNADA LETTER CA;Lo;0;L;;;;;N;;;;; +0C9B;KANNADA LETTER CHA;Lo;0;L;;;;;N;;;;; +0C9C;KANNADA LETTER JA;Lo;0;L;;;;;N;;;;; +0C9D;KANNADA LETTER JHA;Lo;0;L;;;;;N;;;;; +0C9E;KANNADA LETTER NYA;Lo;0;L;;;;;N;;;;; +0C9F;KANNADA LETTER TTA;Lo;0;L;;;;;N;;;;; +0CA0;KANNADA LETTER TTHA;Lo;0;L;;;;;N;;;;; +0CA1;KANNADA LETTER DDA;Lo;0;L;;;;;N;;;;; +0CA2;KANNADA LETTER DDHA;Lo;0;L;;;;;N;;;;; +0CA3;KANNADA LETTER NNA;Lo;0;L;;;;;N;;;;; +0CA4;KANNADA LETTER TA;Lo;0;L;;;;;N;;;;; +0CA5;KANNADA LETTER THA;Lo;0;L;;;;;N;;;;; +0CA6;KANNADA LETTER DA;Lo;0;L;;;;;N;;;;; +0CA7;KANNADA LETTER DHA;Lo;0;L;;;;;N;;;;; +0CA8;KANNADA LETTER NA;Lo;0;L;;;;;N;;;;; +0CAA;KANNADA LETTER PA;Lo;0;L;;;;;N;;;;; +0CAB;KANNADA LETTER PHA;Lo;0;L;;;;;N;;;;; +0CAC;KANNADA LETTER BA;Lo;0;L;;;;;N;;;;; +0CAD;KANNADA LETTER BHA;Lo;0;L;;;;;N;;;;; +0CAE;KANNADA LETTER MA;Lo;0;L;;;;;N;;;;; +0CAF;KANNADA LETTER YA;Lo;0;L;;;;;N;;;;; +0CB0;KANNADA LETTER RA;Lo;0;L;;;;;N;;;;; +0CB1;KANNADA LETTER RRA;Lo;0;L;;;;;N;;;;; +0CB2;KANNADA LETTER LA;Lo;0;L;;;;;N;;;;; +0CB3;KANNADA LETTER LLA;Lo;0;L;;;;;N;;;;; +0CB5;KANNADA LETTER VA;Lo;0;L;;;;;N;;;;; +0CB6;KANNADA LETTER SHA;Lo;0;L;;;;;N;;;;; +0CB7;KANNADA LETTER SSA;Lo;0;L;;;;;N;;;;; +0CB8;KANNADA LETTER SA;Lo;0;L;;;;;N;;;;; +0CB9;KANNADA LETTER HA;Lo;0;L;;;;;N;;;;; +0CBC;KANNADA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +0CBD;KANNADA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +0CBE;KANNADA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +0CBF;KANNADA VOWEL SIGN I;Mn;0;L;;;;;N;;;;; +0CC0;KANNADA VOWEL SIGN II;Mc;0;L;0CBF 0CD5;;;;N;;;;; +0CC1;KANNADA VOWEL SIGN U;Mc;0;L;;;;;N;;;;; +0CC2;KANNADA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; +0CC3;KANNADA VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;; +0CC4;KANNADA VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;; +0CC6;KANNADA VOWEL SIGN E;Mn;0;L;;;;;N;;;;; +0CC7;KANNADA VOWEL SIGN EE;Mc;0;L;0CC6 0CD5;;;;N;;;;; +0CC8;KANNADA VOWEL SIGN AI;Mc;0;L;0CC6 0CD6;;;;N;;;;; +0CCA;KANNADA VOWEL SIGN O;Mc;0;L;0CC6 0CC2;;;;N;;;;; +0CCB;KANNADA VOWEL SIGN OO;Mc;0;L;0CCA 0CD5;;;;N;;;;; +0CCC;KANNADA VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; +0CCD;KANNADA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0CD5;KANNADA LENGTH MARK;Mc;0;L;;;;;N;;;;; +0CD6;KANNADA AI LENGTH MARK;Mc;0;L;;;;;N;;;;; +0CDE;KANNADA LETTER FA;Lo;0;L;;;;;N;;;;; +0CE0;KANNADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +0CE1;KANNADA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +0CE2;KANNADA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +0CE3;KANNADA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +0CE6;KANNADA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0CE7;KANNADA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0CE8;KANNADA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0CE9;KANNADA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0CEA;KANNADA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0CEB;KANNADA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0CEC;KANNADA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0CED;KANNADA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0CEE;KANNADA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0CEF;KANNADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0CF1;KANNADA SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;; +0CF2;KANNADA SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;; +0D01;MALAYALAM SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +0D02;MALAYALAM SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; +0D03;MALAYALAM SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0D05;MALAYALAM LETTER A;Lo;0;L;;;;;N;;;;; +0D06;MALAYALAM LETTER AA;Lo;0;L;;;;;N;;;;; +0D07;MALAYALAM LETTER I;Lo;0;L;;;;;N;;;;; +0D08;MALAYALAM LETTER II;Lo;0;L;;;;;N;;;;; +0D09;MALAYALAM LETTER U;Lo;0;L;;;;;N;;;;; +0D0A;MALAYALAM LETTER UU;Lo;0;L;;;;;N;;;;; +0D0B;MALAYALAM LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +0D0C;MALAYALAM LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +0D0E;MALAYALAM LETTER E;Lo;0;L;;;;;N;;;;; +0D0F;MALAYALAM LETTER EE;Lo;0;L;;;;;N;;;;; +0D10;MALAYALAM LETTER AI;Lo;0;L;;;;;N;;;;; +0D12;MALAYALAM LETTER O;Lo;0;L;;;;;N;;;;; +0D13;MALAYALAM LETTER OO;Lo;0;L;;;;;N;;;;; +0D14;MALAYALAM LETTER AU;Lo;0;L;;;;;N;;;;; +0D15;MALAYALAM LETTER KA;Lo;0;L;;;;;N;;;;; +0D16;MALAYALAM LETTER KHA;Lo;0;L;;;;;N;;;;; +0D17;MALAYALAM LETTER GA;Lo;0;L;;;;;N;;;;; +0D18;MALAYALAM LETTER GHA;Lo;0;L;;;;;N;;;;; +0D19;MALAYALAM LETTER NGA;Lo;0;L;;;;;N;;;;; +0D1A;MALAYALAM LETTER CA;Lo;0;L;;;;;N;;;;; +0D1B;MALAYALAM LETTER CHA;Lo;0;L;;;;;N;;;;; +0D1C;MALAYALAM LETTER JA;Lo;0;L;;;;;N;;;;; +0D1D;MALAYALAM LETTER JHA;Lo;0;L;;;;;N;;;;; +0D1E;MALAYALAM LETTER NYA;Lo;0;L;;;;;N;;;;; +0D1F;MALAYALAM LETTER TTA;Lo;0;L;;;;;N;;;;; +0D20;MALAYALAM LETTER TTHA;Lo;0;L;;;;;N;;;;; +0D21;MALAYALAM LETTER DDA;Lo;0;L;;;;;N;;;;; +0D22;MALAYALAM LETTER DDHA;Lo;0;L;;;;;N;;;;; +0D23;MALAYALAM LETTER NNA;Lo;0;L;;;;;N;;;;; +0D24;MALAYALAM LETTER TA;Lo;0;L;;;;;N;;;;; +0D25;MALAYALAM LETTER THA;Lo;0;L;;;;;N;;;;; +0D26;MALAYALAM LETTER DA;Lo;0;L;;;;;N;;;;; +0D27;MALAYALAM LETTER DHA;Lo;0;L;;;;;N;;;;; +0D28;MALAYALAM LETTER NA;Lo;0;L;;;;;N;;;;; +0D29;MALAYALAM LETTER NNNA;Lo;0;L;;;;;N;;;;; +0D2A;MALAYALAM LETTER PA;Lo;0;L;;;;;N;;;;; +0D2B;MALAYALAM LETTER PHA;Lo;0;L;;;;;N;;;;; +0D2C;MALAYALAM LETTER BA;Lo;0;L;;;;;N;;;;; +0D2D;MALAYALAM LETTER BHA;Lo;0;L;;;;;N;;;;; +0D2E;MALAYALAM LETTER MA;Lo;0;L;;;;;N;;;;; +0D2F;MALAYALAM LETTER YA;Lo;0;L;;;;;N;;;;; +0D30;MALAYALAM LETTER RA;Lo;0;L;;;;;N;;;;; +0D31;MALAYALAM LETTER RRA;Lo;0;L;;;;;N;;;;; +0D32;MALAYALAM LETTER LA;Lo;0;L;;;;;N;;;;; +0D33;MALAYALAM LETTER LLA;Lo;0;L;;;;;N;;;;; +0D34;MALAYALAM LETTER LLLA;Lo;0;L;;;;;N;;;;; +0D35;MALAYALAM LETTER VA;Lo;0;L;;;;;N;;;;; +0D36;MALAYALAM LETTER SHA;Lo;0;L;;;;;N;;;;; +0D37;MALAYALAM LETTER SSA;Lo;0;L;;;;;N;;;;; +0D38;MALAYALAM LETTER SA;Lo;0;L;;;;;N;;;;; +0D39;MALAYALAM LETTER HA;Lo;0;L;;;;;N;;;;; +0D3A;MALAYALAM LETTER TTTA;Lo;0;L;;;;;N;;;;; +0D3D;MALAYALAM SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +0D3E;MALAYALAM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +0D3F;MALAYALAM VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +0D40;MALAYALAM VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +0D41;MALAYALAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +0D42;MALAYALAM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +0D43;MALAYALAM VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +0D44;MALAYALAM VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +0D46;MALAYALAM VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +0D47;MALAYALAM VOWEL SIGN EE;Mc;0;L;;;;;N;;;;; +0D48;MALAYALAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +0D4A;MALAYALAM VOWEL SIGN O;Mc;0;L;0D46 0D3E;;;;N;;;;; +0D4B;MALAYALAM VOWEL SIGN OO;Mc;0;L;0D47 0D3E;;;;N;;;;; +0D4C;MALAYALAM VOWEL SIGN AU;Mc;0;L;0D46 0D57;;;;N;;;;; +0D4D;MALAYALAM SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0D4E;MALAYALAM LETTER DOT REPH;Lo;0;L;;;;;N;;;;; +0D4F;MALAYALAM SIGN PARA;So;0;L;;;;;N;;;;; +0D54;MALAYALAM LETTER CHILLU M;Lo;0;L;;;;;N;;;;; +0D55;MALAYALAM LETTER CHILLU Y;Lo;0;L;;;;;N;;;;; +0D56;MALAYALAM LETTER CHILLU LLL;Lo;0;L;;;;;N;;;;; +0D57;MALAYALAM AU LENGTH MARK;Mc;0;L;;;;;N;;;;; +0D58;MALAYALAM FRACTION ONE ONE-HUNDRED-AND-SIXTIETH;No;0;L;;;;1/160;N;;;;; +0D59;MALAYALAM FRACTION ONE FORTIETH;No;0;L;;;;1/40;N;;;;; +0D5A;MALAYALAM FRACTION THREE EIGHTIETHS;No;0;L;;;;3/80;N;;;;; +0D5B;MALAYALAM FRACTION ONE TWENTIETH;No;0;L;;;;1/20;N;;;;; +0D5C;MALAYALAM FRACTION ONE TENTH;No;0;L;;;;1/10;N;;;;; +0D5D;MALAYALAM FRACTION THREE TWENTIETHS;No;0;L;;;;3/20;N;;;;; +0D5E;MALAYALAM FRACTION ONE FIFTH;No;0;L;;;;1/5;N;;;;; +0D5F;MALAYALAM LETTER ARCHAIC II;Lo;0;L;;;;;N;;;;; +0D60;MALAYALAM LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +0D61;MALAYALAM LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +0D62;MALAYALAM VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +0D63;MALAYALAM VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +0D66;MALAYALAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0D67;MALAYALAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0D68;MALAYALAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0D69;MALAYALAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0D6A;MALAYALAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0D6B;MALAYALAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0D6C;MALAYALAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0D6D;MALAYALAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0D6E;MALAYALAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0D6F;MALAYALAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0D70;MALAYALAM NUMBER TEN;No;0;L;;;;10;N;;;;; +0D71;MALAYALAM NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;; +0D72;MALAYALAM NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;; +0D73;MALAYALAM FRACTION ONE QUARTER;No;0;L;;;;1/4;N;;;;; +0D74;MALAYALAM FRACTION ONE HALF;No;0;L;;;;1/2;N;;;;; +0D75;MALAYALAM FRACTION THREE QUARTERS;No;0;L;;;;3/4;N;;;;; +0D76;MALAYALAM FRACTION ONE SIXTEENTH;No;0;L;;;;1/16;N;;;;; +0D77;MALAYALAM FRACTION ONE EIGHTH;No;0;L;;;;1/8;N;;;;; +0D78;MALAYALAM FRACTION THREE SIXTEENTHS;No;0;L;;;;3/16;N;;;;; +0D79;MALAYALAM DATE MARK;So;0;L;;;;;N;;;;; +0D7A;MALAYALAM LETTER CHILLU NN;Lo;0;L;;;;;N;;;;; +0D7B;MALAYALAM LETTER CHILLU N;Lo;0;L;;;;;N;;;;; +0D7C;MALAYALAM LETTER CHILLU RR;Lo;0;L;;;;;N;;;;; +0D7D;MALAYALAM LETTER CHILLU L;Lo;0;L;;;;;N;;;;; +0D7E;MALAYALAM LETTER CHILLU LL;Lo;0;L;;;;;N;;;;; +0D7F;MALAYALAM LETTER CHILLU K;Lo;0;L;;;;;N;;;;; +0D82;SINHALA SIGN ANUSVARAYA;Mc;0;L;;;;;N;;;;; +0D83;SINHALA SIGN VISARGAYA;Mc;0;L;;;;;N;;;;; +0D85;SINHALA LETTER AYANNA;Lo;0;L;;;;;N;;;;; +0D86;SINHALA LETTER AAYANNA;Lo;0;L;;;;;N;;;;; +0D87;SINHALA LETTER AEYANNA;Lo;0;L;;;;;N;;;;; +0D88;SINHALA LETTER AEEYANNA;Lo;0;L;;;;;N;;;;; +0D89;SINHALA LETTER IYANNA;Lo;0;L;;;;;N;;;;; +0D8A;SINHALA LETTER IIYANNA;Lo;0;L;;;;;N;;;;; +0D8B;SINHALA LETTER UYANNA;Lo;0;L;;;;;N;;;;; +0D8C;SINHALA LETTER UUYANNA;Lo;0;L;;;;;N;;;;; +0D8D;SINHALA LETTER IRUYANNA;Lo;0;L;;;;;N;;;;; +0D8E;SINHALA LETTER IRUUYANNA;Lo;0;L;;;;;N;;;;; +0D8F;SINHALA LETTER ILUYANNA;Lo;0;L;;;;;N;;;;; +0D90;SINHALA LETTER ILUUYANNA;Lo;0;L;;;;;N;;;;; +0D91;SINHALA LETTER EYANNA;Lo;0;L;;;;;N;;;;; +0D92;SINHALA LETTER EEYANNA;Lo;0;L;;;;;N;;;;; +0D93;SINHALA LETTER AIYANNA;Lo;0;L;;;;;N;;;;; +0D94;SINHALA LETTER OYANNA;Lo;0;L;;;;;N;;;;; +0D95;SINHALA LETTER OOYANNA;Lo;0;L;;;;;N;;;;; +0D96;SINHALA LETTER AUYANNA;Lo;0;L;;;;;N;;;;; +0D9A;SINHALA LETTER ALPAPRAANA KAYANNA;Lo;0;L;;;;;N;;;;; +0D9B;SINHALA LETTER MAHAAPRAANA KAYANNA;Lo;0;L;;;;;N;;;;; +0D9C;SINHALA LETTER ALPAPRAANA GAYANNA;Lo;0;L;;;;;N;;;;; +0D9D;SINHALA LETTER MAHAAPRAANA GAYANNA;Lo;0;L;;;;;N;;;;; +0D9E;SINHALA LETTER KANTAJA NAASIKYAYA;Lo;0;L;;;;;N;;;;; +0D9F;SINHALA LETTER SANYAKA GAYANNA;Lo;0;L;;;;;N;;;;; +0DA0;SINHALA LETTER ALPAPRAANA CAYANNA;Lo;0;L;;;;;N;;;;; +0DA1;SINHALA LETTER MAHAAPRAANA CAYANNA;Lo;0;L;;;;;N;;;;; +0DA2;SINHALA LETTER ALPAPRAANA JAYANNA;Lo;0;L;;;;;N;;;;; +0DA3;SINHALA LETTER MAHAAPRAANA JAYANNA;Lo;0;L;;;;;N;;;;; +0DA4;SINHALA LETTER TAALUJA NAASIKYAYA;Lo;0;L;;;;;N;;;;; +0DA5;SINHALA LETTER TAALUJA SANYOOGA NAAKSIKYAYA;Lo;0;L;;;;;N;;;;; +0DA6;SINHALA LETTER SANYAKA JAYANNA;Lo;0;L;;;;;N;;;;; +0DA7;SINHALA LETTER ALPAPRAANA TTAYANNA;Lo;0;L;;;;;N;;;;; +0DA8;SINHALA LETTER MAHAAPRAANA TTAYANNA;Lo;0;L;;;;;N;;;;; +0DA9;SINHALA LETTER ALPAPRAANA DDAYANNA;Lo;0;L;;;;;N;;;;; +0DAA;SINHALA LETTER MAHAAPRAANA DDAYANNA;Lo;0;L;;;;;N;;;;; +0DAB;SINHALA LETTER MUURDHAJA NAYANNA;Lo;0;L;;;;;N;;;;; +0DAC;SINHALA LETTER SANYAKA DDAYANNA;Lo;0;L;;;;;N;;;;; +0DAD;SINHALA LETTER ALPAPRAANA TAYANNA;Lo;0;L;;;;;N;;;;; +0DAE;SINHALA LETTER MAHAAPRAANA TAYANNA;Lo;0;L;;;;;N;;;;; +0DAF;SINHALA LETTER ALPAPRAANA DAYANNA;Lo;0;L;;;;;N;;;;; +0DB0;SINHALA LETTER MAHAAPRAANA DAYANNA;Lo;0;L;;;;;N;;;;; +0DB1;SINHALA LETTER DANTAJA NAYANNA;Lo;0;L;;;;;N;;;;; +0DB3;SINHALA LETTER SANYAKA DAYANNA;Lo;0;L;;;;;N;;;;; +0DB4;SINHALA LETTER ALPAPRAANA PAYANNA;Lo;0;L;;;;;N;;;;; +0DB5;SINHALA LETTER MAHAAPRAANA PAYANNA;Lo;0;L;;;;;N;;;;; +0DB6;SINHALA LETTER ALPAPRAANA BAYANNA;Lo;0;L;;;;;N;;;;; +0DB7;SINHALA LETTER MAHAAPRAANA BAYANNA;Lo;0;L;;;;;N;;;;; +0DB8;SINHALA LETTER MAYANNA;Lo;0;L;;;;;N;;;;; +0DB9;SINHALA LETTER AMBA BAYANNA;Lo;0;L;;;;;N;;;;; +0DBA;SINHALA LETTER YAYANNA;Lo;0;L;;;;;N;;;;; +0DBB;SINHALA LETTER RAYANNA;Lo;0;L;;;;;N;;;;; +0DBD;SINHALA LETTER DANTAJA LAYANNA;Lo;0;L;;;;;N;;;;; +0DC0;SINHALA LETTER VAYANNA;Lo;0;L;;;;;N;;;;; +0DC1;SINHALA LETTER TAALUJA SAYANNA;Lo;0;L;;;;;N;;;;; +0DC2;SINHALA LETTER MUURDHAJA SAYANNA;Lo;0;L;;;;;N;;;;; +0DC3;SINHALA LETTER DANTAJA SAYANNA;Lo;0;L;;;;;N;;;;; +0DC4;SINHALA LETTER HAYANNA;Lo;0;L;;;;;N;;;;; +0DC5;SINHALA LETTER MUURDHAJA LAYANNA;Lo;0;L;;;;;N;;;;; +0DC6;SINHALA LETTER FAYANNA;Lo;0;L;;;;;N;;;;; +0DCA;SINHALA SIGN AL-LAKUNA;Mn;9;NSM;;;;;N;;;;; +0DCF;SINHALA VOWEL SIGN AELA-PILLA;Mc;0;L;;;;;N;;;;; +0DD0;SINHALA VOWEL SIGN KETTI AEDA-PILLA;Mc;0;L;;;;;N;;;;; +0DD1;SINHALA VOWEL SIGN DIGA AEDA-PILLA;Mc;0;L;;;;;N;;;;; +0DD2;SINHALA VOWEL SIGN KETTI IS-PILLA;Mn;0;NSM;;;;;N;;;;; +0DD3;SINHALA VOWEL SIGN DIGA IS-PILLA;Mn;0;NSM;;;;;N;;;;; +0DD4;SINHALA VOWEL SIGN KETTI PAA-PILLA;Mn;0;NSM;;;;;N;;;;; +0DD6;SINHALA VOWEL SIGN DIGA PAA-PILLA;Mn;0;NSM;;;;;N;;;;; +0DD8;SINHALA VOWEL SIGN GAETTA-PILLA;Mc;0;L;;;;;N;;;;; +0DD9;SINHALA VOWEL SIGN KOMBUVA;Mc;0;L;;;;;N;;;;; +0DDA;SINHALA VOWEL SIGN DIGA KOMBUVA;Mc;0;L;0DD9 0DCA;;;;N;;;;; +0DDB;SINHALA VOWEL SIGN KOMBU DEKA;Mc;0;L;;;;;N;;;;; +0DDC;SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA;Mc;0;L;0DD9 0DCF;;;;N;;;;; +0DDD;SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA;Mc;0;L;0DDC 0DCA;;;;N;;;;; +0DDE;SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA;Mc;0;L;0DD9 0DDF;;;;N;;;;; +0DDF;SINHALA VOWEL SIGN GAYANUKITTA;Mc;0;L;;;;;N;;;;; +0DE6;SINHALA LITH DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0DE7;SINHALA LITH DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0DE8;SINHALA LITH DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0DE9;SINHALA LITH DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0DEA;SINHALA LITH DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0DEB;SINHALA LITH DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0DEC;SINHALA LITH DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0DED;SINHALA LITH DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0DEE;SINHALA LITH DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0DEF;SINHALA LITH DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0DF2;SINHALA VOWEL SIGN DIGA GAETTA-PILLA;Mc;0;L;;;;;N;;;;; +0DF3;SINHALA VOWEL SIGN DIGA GAYANUKITTA;Mc;0;L;;;;;N;;;;; +0DF4;SINHALA PUNCTUATION KUNDDALIYA;Po;0;L;;;;;N;;;;; +0E01;THAI CHARACTER KO KAI;Lo;0;L;;;;;N;THAI LETTER KO KAI;;;; +0E02;THAI CHARACTER KHO KHAI;Lo;0;L;;;;;N;THAI LETTER KHO KHAI;;;; +0E03;THAI CHARACTER KHO KHUAT;Lo;0;L;;;;;N;THAI LETTER KHO KHUAT;;;; +0E04;THAI CHARACTER KHO KHWAI;Lo;0;L;;;;;N;THAI LETTER KHO KHWAI;;;; +0E05;THAI CHARACTER KHO KHON;Lo;0;L;;;;;N;THAI LETTER KHO KHON;;;; +0E06;THAI CHARACTER KHO RAKHANG;Lo;0;L;;;;;N;THAI LETTER KHO RAKHANG;;;; +0E07;THAI CHARACTER NGO NGU;Lo;0;L;;;;;N;THAI LETTER NGO NGU;;;; +0E08;THAI CHARACTER CHO CHAN;Lo;0;L;;;;;N;THAI LETTER CHO CHAN;;;; +0E09;THAI CHARACTER CHO CHING;Lo;0;L;;;;;N;THAI LETTER CHO CHING;;;; +0E0A;THAI CHARACTER CHO CHANG;Lo;0;L;;;;;N;THAI LETTER CHO CHANG;;;; +0E0B;THAI CHARACTER SO SO;Lo;0;L;;;;;N;THAI LETTER SO SO;;;; +0E0C;THAI CHARACTER CHO CHOE;Lo;0;L;;;;;N;THAI LETTER CHO CHOE;;;; +0E0D;THAI CHARACTER YO YING;Lo;0;L;;;;;N;THAI LETTER YO YING;;;; +0E0E;THAI CHARACTER DO CHADA;Lo;0;L;;;;;N;THAI LETTER DO CHADA;;;; +0E0F;THAI CHARACTER TO PATAK;Lo;0;L;;;;;N;THAI LETTER TO PATAK;;;; +0E10;THAI CHARACTER THO THAN;Lo;0;L;;;;;N;THAI LETTER THO THAN;;;; +0E11;THAI CHARACTER THO NANGMONTHO;Lo;0;L;;;;;N;THAI LETTER THO NANGMONTHO;;;; +0E12;THAI CHARACTER THO PHUTHAO;Lo;0;L;;;;;N;THAI LETTER THO PHUTHAO;;;; +0E13;THAI CHARACTER NO NEN;Lo;0;L;;;;;N;THAI LETTER NO NEN;;;; +0E14;THAI CHARACTER DO DEK;Lo;0;L;;;;;N;THAI LETTER DO DEK;;;; +0E15;THAI CHARACTER TO TAO;Lo;0;L;;;;;N;THAI LETTER TO TAO;;;; +0E16;THAI CHARACTER THO THUNG;Lo;0;L;;;;;N;THAI LETTER THO THUNG;;;; +0E17;THAI CHARACTER THO THAHAN;Lo;0;L;;;;;N;THAI LETTER THO THAHAN;;;; +0E18;THAI CHARACTER THO THONG;Lo;0;L;;;;;N;THAI LETTER THO THONG;;;; +0E19;THAI CHARACTER NO NU;Lo;0;L;;;;;N;THAI LETTER NO NU;;;; +0E1A;THAI CHARACTER BO BAIMAI;Lo;0;L;;;;;N;THAI LETTER BO BAIMAI;;;; +0E1B;THAI CHARACTER PO PLA;Lo;0;L;;;;;N;THAI LETTER PO PLA;;;; +0E1C;THAI CHARACTER PHO PHUNG;Lo;0;L;;;;;N;THAI LETTER PHO PHUNG;;;; +0E1D;THAI CHARACTER FO FA;Lo;0;L;;;;;N;THAI LETTER FO FA;;;; +0E1E;THAI CHARACTER PHO PHAN;Lo;0;L;;;;;N;THAI LETTER PHO PHAN;;;; +0E1F;THAI CHARACTER FO FAN;Lo;0;L;;;;;N;THAI LETTER FO FAN;;;; +0E20;THAI CHARACTER PHO SAMPHAO;Lo;0;L;;;;;N;THAI LETTER PHO SAMPHAO;;;; +0E21;THAI CHARACTER MO MA;Lo;0;L;;;;;N;THAI LETTER MO MA;;;; +0E22;THAI CHARACTER YO YAK;Lo;0;L;;;;;N;THAI LETTER YO YAK;;;; +0E23;THAI CHARACTER RO RUA;Lo;0;L;;;;;N;THAI LETTER RO RUA;;;; +0E24;THAI CHARACTER RU;Lo;0;L;;;;;N;THAI LETTER RU;;;; +0E25;THAI CHARACTER LO LING;Lo;0;L;;;;;N;THAI LETTER LO LING;;;; +0E26;THAI CHARACTER LU;Lo;0;L;;;;;N;THAI LETTER LU;;;; +0E27;THAI CHARACTER WO WAEN;Lo;0;L;;;;;N;THAI LETTER WO WAEN;;;; +0E28;THAI CHARACTER SO SALA;Lo;0;L;;;;;N;THAI LETTER SO SALA;;;; +0E29;THAI CHARACTER SO RUSI;Lo;0;L;;;;;N;THAI LETTER SO RUSI;;;; +0E2A;THAI CHARACTER SO SUA;Lo;0;L;;;;;N;THAI LETTER SO SUA;;;; +0E2B;THAI CHARACTER HO HIP;Lo;0;L;;;;;N;THAI LETTER HO HIP;;;; +0E2C;THAI CHARACTER LO CHULA;Lo;0;L;;;;;N;THAI LETTER LO CHULA;;;; +0E2D;THAI CHARACTER O ANG;Lo;0;L;;;;;N;THAI LETTER O ANG;;;; +0E2E;THAI CHARACTER HO NOKHUK;Lo;0;L;;;;;N;THAI LETTER HO NOK HUK;;;; +0E2F;THAI CHARACTER PAIYANNOI;Lo;0;L;;;;;N;THAI PAI YAN NOI;;;; +0E30;THAI CHARACTER SARA A;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA A;;;; +0E31;THAI CHARACTER MAI HAN-AKAT;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI HAN-AKAT;;;; +0E32;THAI CHARACTER SARA AA;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AA;;;; +0E33;THAI CHARACTER SARA AM;Lo;0;L;<compat> 0E4D 0E32;;;;N;THAI VOWEL SIGN SARA AM;;;; +0E34;THAI CHARACTER SARA I;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA I;;;; +0E35;THAI CHARACTER SARA II;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA II;;;; +0E36;THAI CHARACTER SARA UE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UE;;;; +0E37;THAI CHARACTER SARA UEE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UEE;;;; +0E38;THAI CHARACTER SARA U;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA U;;;; +0E39;THAI CHARACTER SARA UU;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA UU;;;; +0E3A;THAI CHARACTER PHINTHU;Mn;9;NSM;;;;;N;THAI VOWEL SIGN PHINTHU;;;; +0E3F;THAI CURRENCY SYMBOL BAHT;Sc;0;ET;;;;;N;THAI BAHT SIGN;;;; +0E40;THAI CHARACTER SARA E;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA E;;;; +0E41;THAI CHARACTER SARA AE;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AE;;;; +0E42;THAI CHARACTER SARA O;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA O;;;; +0E43;THAI CHARACTER SARA AI MAIMUAN;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MUAN;;;; +0E44;THAI CHARACTER SARA AI MAIMALAI;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MALAI;;;; +0E45;THAI CHARACTER LAKKHANGYAO;Lo;0;L;;;;;N;THAI LAK KHANG YAO;;;; +0E46;THAI CHARACTER MAIYAMOK;Lm;0;L;;;;;N;THAI MAI YAMOK;;;; +0E47;THAI CHARACTER MAITAIKHU;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI TAI KHU;;;; +0E48;THAI CHARACTER MAI EK;Mn;107;NSM;;;;;N;THAI TONE MAI EK;;;; +0E49;THAI CHARACTER MAI THO;Mn;107;NSM;;;;;N;THAI TONE MAI THO;;;; +0E4A;THAI CHARACTER MAI TRI;Mn;107;NSM;;;;;N;THAI TONE MAI TRI;;;; +0E4B;THAI CHARACTER MAI CHATTAWA;Mn;107;NSM;;;;;N;THAI TONE MAI CHATTAWA;;;; +0E4C;THAI CHARACTER THANTHAKHAT;Mn;0;NSM;;;;;N;THAI THANTHAKHAT;;;; +0E4D;THAI CHARACTER NIKHAHIT;Mn;0;NSM;;;;;N;THAI NIKKHAHIT;;;; +0E4E;THAI CHARACTER YAMAKKAN;Mn;0;NSM;;;;;N;THAI YAMAKKAN;;;; +0E4F;THAI CHARACTER FONGMAN;Po;0;L;;;;;N;THAI FONGMAN;;;; +0E50;THAI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0E51;THAI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0E52;THAI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0E53;THAI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0E54;THAI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0E55;THAI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0E56;THAI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0E57;THAI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0E58;THAI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0E59;THAI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0E5A;THAI CHARACTER ANGKHANKHU;Po;0;L;;;;;N;THAI ANGKHANKHU;;;; +0E5B;THAI CHARACTER KHOMUT;Po;0;L;;;;;N;THAI KHOMUT;;;; +0E81;LAO LETTER KO;Lo;0;L;;;;;N;;;;; +0E82;LAO LETTER KHO SUNG;Lo;0;L;;;;;N;;;;; +0E84;LAO LETTER KHO TAM;Lo;0;L;;;;;N;;;;; +0E87;LAO LETTER NGO;Lo;0;L;;;;;N;;;;; +0E88;LAO LETTER CO;Lo;0;L;;;;;N;;;;; +0E8A;LAO LETTER SO TAM;Lo;0;L;;;;;N;;;;; +0E8D;LAO LETTER NYO;Lo;0;L;;;;;N;;;;; +0E94;LAO LETTER DO;Lo;0;L;;;;;N;;;;; +0E95;LAO LETTER TO;Lo;0;L;;;;;N;;;;; +0E96;LAO LETTER THO SUNG;Lo;0;L;;;;;N;;;;; +0E97;LAO LETTER THO TAM;Lo;0;L;;;;;N;;;;; +0E99;LAO LETTER NO;Lo;0;L;;;;;N;;;;; +0E9A;LAO LETTER BO;Lo;0;L;;;;;N;;;;; +0E9B;LAO LETTER PO;Lo;0;L;;;;;N;;;;; +0E9C;LAO LETTER PHO SUNG;Lo;0;L;;;;;N;;;;; +0E9D;LAO LETTER FO TAM;Lo;0;L;;;;;N;;;;; +0E9E;LAO LETTER PHO TAM;Lo;0;L;;;;;N;;;;; +0E9F;LAO LETTER FO SUNG;Lo;0;L;;;;;N;;;;; +0EA1;LAO LETTER MO;Lo;0;L;;;;;N;;;;; +0EA2;LAO LETTER YO;Lo;0;L;;;;;N;;;;; +0EA3;LAO LETTER LO LING;Lo;0;L;;;;;N;;;;; +0EA5;LAO LETTER LO LOOT;Lo;0;L;;;;;N;;;;; +0EA7;LAO LETTER WO;Lo;0;L;;;;;N;;;;; +0EAA;LAO LETTER SO SUNG;Lo;0;L;;;;;N;;;;; +0EAB;LAO LETTER HO SUNG;Lo;0;L;;;;;N;;;;; +0EAD;LAO LETTER O;Lo;0;L;;;;;N;;;;; +0EAE;LAO LETTER HO TAM;Lo;0;L;;;;;N;;;;; +0EAF;LAO ELLIPSIS;Lo;0;L;;;;;N;;;;; +0EB0;LAO VOWEL SIGN A;Lo;0;L;;;;;N;;;;; +0EB1;LAO VOWEL SIGN MAI KAN;Mn;0;NSM;;;;;N;;;;; +0EB2;LAO VOWEL SIGN AA;Lo;0;L;;;;;N;;;;; +0EB3;LAO VOWEL SIGN AM;Lo;0;L;<compat> 0ECD 0EB2;;;;N;;;;; +0EB4;LAO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +0EB5;LAO VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +0EB6;LAO VOWEL SIGN Y;Mn;0;NSM;;;;;N;;;;; +0EB7;LAO VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;; +0EB8;LAO VOWEL SIGN U;Mn;118;NSM;;;;;N;;;;; +0EB9;LAO VOWEL SIGN UU;Mn;118;NSM;;;;;N;;;;; +0EBB;LAO VOWEL SIGN MAI KON;Mn;0;NSM;;;;;N;;;;; +0EBC;LAO SEMIVOWEL SIGN LO;Mn;0;NSM;;;;;N;;;;; +0EBD;LAO SEMIVOWEL SIGN NYO;Lo;0;L;;;;;N;;;;; +0EC0;LAO VOWEL SIGN E;Lo;0;L;;;;;N;;;;; +0EC1;LAO VOWEL SIGN EI;Lo;0;L;;;;;N;;;;; +0EC2;LAO VOWEL SIGN O;Lo;0;L;;;;;N;;;;; +0EC3;LAO VOWEL SIGN AY;Lo;0;L;;;;;N;;;;; +0EC4;LAO VOWEL SIGN AI;Lo;0;L;;;;;N;;;;; +0EC6;LAO KO LA;Lm;0;L;;;;;N;;;;; +0EC8;LAO TONE MAI EK;Mn;122;NSM;;;;;N;;;;; +0EC9;LAO TONE MAI THO;Mn;122;NSM;;;;;N;;;;; +0ECA;LAO TONE MAI TI;Mn;122;NSM;;;;;N;;;;; +0ECB;LAO TONE MAI CATAWA;Mn;122;NSM;;;;;N;;;;; +0ECC;LAO CANCELLATION MARK;Mn;0;NSM;;;;;N;;;;; +0ECD;LAO NIGGAHITA;Mn;0;NSM;;;;;N;;;;; +0ED0;LAO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0ED1;LAO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0ED2;LAO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0ED3;LAO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0ED4;LAO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0ED5;LAO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0ED6;LAO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0ED7;LAO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0ED8;LAO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0ED9;LAO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0EDC;LAO HO NO;Lo;0;L;<compat> 0EAB 0E99;;;;N;;;;; +0EDD;LAO HO MO;Lo;0;L;<compat> 0EAB 0EA1;;;;N;;;;; +0EDE;LAO LETTER KHMU GO;Lo;0;L;;;;;N;;;;; +0EDF;LAO LETTER KHMU NYO;Lo;0;L;;;;;N;;;;; +0F00;TIBETAN SYLLABLE OM;Lo;0;L;;;;;N;;;;; +0F01;TIBETAN MARK GTER YIG MGO TRUNCATED A;So;0;L;;;;;N;;;;; +0F02;TIBETAN MARK GTER YIG MGO -UM RNAM BCAD MA;So;0;L;;;;;N;;;;; +0F03;TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA;So;0;L;;;;;N;;;;; +0F04;TIBETAN MARK INITIAL YIG MGO MDUN MA;Po;0;L;;;;;N;TIBETAN SINGLE ORNAMENT;;;; +0F05;TIBETAN MARK CLOSING YIG MGO SGAB MA;Po;0;L;;;;;N;;;;; +0F06;TIBETAN MARK CARET YIG MGO PHUR SHAD MA;Po;0;L;;;;;N;;;;; +0F07;TIBETAN MARK YIG MGO TSHEG SHAD MA;Po;0;L;;;;;N;;;;; +0F08;TIBETAN MARK SBRUL SHAD;Po;0;L;;;;;N;TIBETAN RGYANSHAD;;;; +0F09;TIBETAN MARK BSKUR YIG MGO;Po;0;L;;;;;N;;;;; +0F0A;TIBETAN MARK BKA- SHOG YIG MGO;Po;0;L;;;;;N;;;;; +0F0B;TIBETAN MARK INTERSYLLABIC TSHEG;Po;0;L;;;;;N;TIBETAN TSEG;;;; +0F0C;TIBETAN MARK DELIMITER TSHEG BSTAR;Po;0;L;<noBreak> 0F0B;;;;N;;;;; +0F0D;TIBETAN MARK SHAD;Po;0;L;;;;;N;TIBETAN SHAD;;;; +0F0E;TIBETAN MARK NYIS SHAD;Po;0;L;;;;;N;TIBETAN DOUBLE SHAD;;;; +0F0F;TIBETAN MARK TSHEG SHAD;Po;0;L;;;;;N;;;;; +0F10;TIBETAN MARK NYIS TSHEG SHAD;Po;0;L;;;;;N;;;;; +0F11;TIBETAN MARK RIN CHEN SPUNGS SHAD;Po;0;L;;;;;N;TIBETAN RINCHANPHUNGSHAD;;;; +0F12;TIBETAN MARK RGYA GRAM SHAD;Po;0;L;;;;;N;;;;; +0F13;TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN;So;0;L;;;;;N;;;;; +0F14;TIBETAN MARK GTER TSHEG;Po;0;L;;;;;N;TIBETAN COMMA;;;; +0F15;TIBETAN LOGOTYPE SIGN CHAD RTAGS;So;0;L;;;;;N;;;;; +0F16;TIBETAN LOGOTYPE SIGN LHAG RTAGS;So;0;L;;;;;N;;;;; +0F17;TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS;So;0;L;;;;;N;;;;; +0F18;TIBETAN ASTROLOGICAL SIGN -KHYUD PA;Mn;220;NSM;;;;;N;;;;; +0F19;TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS;Mn;220;NSM;;;;;N;;;;; +0F1A;TIBETAN SIGN RDEL DKAR GCIG;So;0;L;;;;;N;;;;; +0F1B;TIBETAN SIGN RDEL DKAR GNYIS;So;0;L;;;;;N;;;;; +0F1C;TIBETAN SIGN RDEL DKAR GSUM;So;0;L;;;;;N;;;;; +0F1D;TIBETAN SIGN RDEL NAG GCIG;So;0;L;;;;;N;;;;; +0F1E;TIBETAN SIGN RDEL NAG GNYIS;So;0;L;;;;;N;;;;; +0F1F;TIBETAN SIGN RDEL DKAR RDEL NAG;So;0;L;;;;;N;;;;; +0F20;TIBETAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0F21;TIBETAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0F22;TIBETAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0F23;TIBETAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0F24;TIBETAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0F25;TIBETAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0F26;TIBETAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0F27;TIBETAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0F28;TIBETAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0F29;TIBETAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0F2A;TIBETAN DIGIT HALF ONE;No;0;L;;;;1/2;N;;;;; +0F2B;TIBETAN DIGIT HALF TWO;No;0;L;;;;3/2;N;;;;; +0F2C;TIBETAN DIGIT HALF THREE;No;0;L;;;;5/2;N;;;;; +0F2D;TIBETAN DIGIT HALF FOUR;No;0;L;;;;7/2;N;;;;; +0F2E;TIBETAN DIGIT HALF FIVE;No;0;L;;;;9/2;N;;;;; +0F2F;TIBETAN DIGIT HALF SIX;No;0;L;;;;11/2;N;;;;; +0F30;TIBETAN DIGIT HALF SEVEN;No;0;L;;;;13/2;N;;;;; +0F31;TIBETAN DIGIT HALF EIGHT;No;0;L;;;;15/2;N;;;;; +0F32;TIBETAN DIGIT HALF NINE;No;0;L;;;;17/2;N;;;;; +0F33;TIBETAN DIGIT HALF ZERO;No;0;L;;;;-1/2;N;;;;; +0F34;TIBETAN MARK BSDUS RTAGS;So;0;L;;;;;N;;;;; +0F35;TIBETAN MARK NGAS BZUNG NYI ZLA;Mn;220;NSM;;;;;N;TIBETAN HONORIFIC UNDER RING;;;; +0F36;TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN;So;0;L;;;;;N;;;;; +0F37;TIBETAN MARK NGAS BZUNG SGOR RTAGS;Mn;220;NSM;;;;;N;TIBETAN UNDER RING;;;; +0F38;TIBETAN MARK CHE MGO;So;0;L;;;;;N;;;;; +0F39;TIBETAN MARK TSA -PHRU;Mn;216;NSM;;;;;N;TIBETAN LENITION MARK;;;; +0F3A;TIBETAN MARK GUG RTAGS GYON;Ps;0;ON;;;;;Y;;;;; +0F3B;TIBETAN MARK GUG RTAGS GYAS;Pe;0;ON;;;;;Y;;;;; +0F3C;TIBETAN MARK ANG KHANG GYON;Ps;0;ON;;;;;Y;TIBETAN LEFT BRACE;;;; +0F3D;TIBETAN MARK ANG KHANG GYAS;Pe;0;ON;;;;;Y;TIBETAN RIGHT BRACE;;;; +0F3E;TIBETAN SIGN YAR TSHES;Mc;0;L;;;;;N;;;;; +0F3F;TIBETAN SIGN MAR TSHES;Mc;0;L;;;;;N;;;;; +0F40;TIBETAN LETTER KA;Lo;0;L;;;;;N;;;;; +0F41;TIBETAN LETTER KHA;Lo;0;L;;;;;N;;;;; +0F42;TIBETAN LETTER GA;Lo;0;L;;;;;N;;;;; +0F43;TIBETAN LETTER GHA;Lo;0;L;0F42 0FB7;;;;N;;;;; +0F44;TIBETAN LETTER NGA;Lo;0;L;;;;;N;;;;; +0F45;TIBETAN LETTER CA;Lo;0;L;;;;;N;;;;; +0F46;TIBETAN LETTER CHA;Lo;0;L;;;;;N;;;;; +0F47;TIBETAN LETTER JA;Lo;0;L;;;;;N;;;;; +0F49;TIBETAN LETTER NYA;Lo;0;L;;;;;N;;;;; +0F4A;TIBETAN LETTER TTA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED TA;;;; +0F4B;TIBETAN LETTER TTHA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED THA;;;; +0F4C;TIBETAN LETTER DDA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED DA;;;; +0F4D;TIBETAN LETTER DDHA;Lo;0;L;0F4C 0FB7;;;;N;;;;; +0F4E;TIBETAN LETTER NNA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED NA;;;; +0F4F;TIBETAN LETTER TA;Lo;0;L;;;;;N;;;;; +0F50;TIBETAN LETTER THA;Lo;0;L;;;;;N;;;;; +0F51;TIBETAN LETTER DA;Lo;0;L;;;;;N;;;;; +0F52;TIBETAN LETTER DHA;Lo;0;L;0F51 0FB7;;;;N;;;;; +0F53;TIBETAN LETTER NA;Lo;0;L;;;;;N;;;;; +0F54;TIBETAN LETTER PA;Lo;0;L;;;;;N;;;;; +0F55;TIBETAN LETTER PHA;Lo;0;L;;;;;N;;;;; +0F56;TIBETAN LETTER BA;Lo;0;L;;;;;N;;;;; +0F57;TIBETAN LETTER BHA;Lo;0;L;0F56 0FB7;;;;N;;;;; +0F58;TIBETAN LETTER MA;Lo;0;L;;;;;N;;;;; +0F59;TIBETAN LETTER TSA;Lo;0;L;;;;;N;;;;; +0F5A;TIBETAN LETTER TSHA;Lo;0;L;;;;;N;;;;; +0F5B;TIBETAN LETTER DZA;Lo;0;L;;;;;N;;;;; +0F5C;TIBETAN LETTER DZHA;Lo;0;L;0F5B 0FB7;;;;N;;;;; +0F5D;TIBETAN LETTER WA;Lo;0;L;;;;;N;;;;; +0F5E;TIBETAN LETTER ZHA;Lo;0;L;;;;;N;;;;; +0F5F;TIBETAN LETTER ZA;Lo;0;L;;;;;N;;;;; +0F60;TIBETAN LETTER -A;Lo;0;L;;;;;N;TIBETAN LETTER AA;;;; +0F61;TIBETAN LETTER YA;Lo;0;L;;;;;N;;;;; +0F62;TIBETAN LETTER RA;Lo;0;L;;;;;N;;;;; +0F63;TIBETAN LETTER LA;Lo;0;L;;;;;N;;;;; +0F64;TIBETAN LETTER SHA;Lo;0;L;;;;;N;;;;; +0F65;TIBETAN LETTER SSA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED SHA;;;; +0F66;TIBETAN LETTER SA;Lo;0;L;;;;;N;;;;; +0F67;TIBETAN LETTER HA;Lo;0;L;;;;;N;;;;; +0F68;TIBETAN LETTER A;Lo;0;L;;;;;N;;;;; +0F69;TIBETAN LETTER KSSA;Lo;0;L;0F40 0FB5;;;;N;;;;; +0F6A;TIBETAN LETTER FIXED-FORM RA;Lo;0;L;;;;;N;;;;; +0F6B;TIBETAN LETTER KKA;Lo;0;L;;;;;N;;;;; +0F6C;TIBETAN LETTER RRA;Lo;0;L;;;;;N;;;;; +0F71;TIBETAN VOWEL SIGN AA;Mn;129;NSM;;;;;N;;;;; +0F72;TIBETAN VOWEL SIGN I;Mn;130;NSM;;;;;N;;;;; +0F73;TIBETAN VOWEL SIGN II;Mn;0;NSM;0F71 0F72;;;;N;;;;; +0F74;TIBETAN VOWEL SIGN U;Mn;132;NSM;;;;;N;;;;; +0F75;TIBETAN VOWEL SIGN UU;Mn;0;NSM;0F71 0F74;;;;N;;;;; +0F76;TIBETAN VOWEL SIGN VOCALIC R;Mn;0;NSM;0FB2 0F80;;;;N;;;;; +0F77;TIBETAN VOWEL SIGN VOCALIC RR;Mn;0;NSM;<compat> 0FB2 0F81;;;;N;;;;; +0F78;TIBETAN VOWEL SIGN VOCALIC L;Mn;0;NSM;0FB3 0F80;;;;N;;;;; +0F79;TIBETAN VOWEL SIGN VOCALIC LL;Mn;0;NSM;<compat> 0FB3 0F81;;;;N;;;;; +0F7A;TIBETAN VOWEL SIGN E;Mn;130;NSM;;;;;N;;;;; +0F7B;TIBETAN VOWEL SIGN EE;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN AI;;;; +0F7C;TIBETAN VOWEL SIGN O;Mn;130;NSM;;;;;N;;;;; +0F7D;TIBETAN VOWEL SIGN OO;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN AU;;;; +0F7E;TIBETAN SIGN RJES SU NGA RO;Mn;0;NSM;;;;;N;TIBETAN ANUSVARA;;;; +0F7F;TIBETAN SIGN RNAM BCAD;Mc;0;L;;;;;N;TIBETAN VISARGA;;;; +0F80;TIBETAN VOWEL SIGN REVERSED I;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN SHORT I;;;; +0F81;TIBETAN VOWEL SIGN REVERSED II;Mn;0;NSM;0F71 0F80;;;;N;;;;; +0F82;TIBETAN SIGN NYI ZLA NAA DA;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU WITH ORNAMENT;;;; +0F83;TIBETAN SIGN SNA LDAN;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU;;;; +0F84;TIBETAN MARK HALANTA;Mn;9;NSM;;;;;N;TIBETAN VIRAMA;;;; +0F85;TIBETAN MARK PALUTA;Po;0;L;;;;;N;TIBETAN CHUCHENYIGE;;;; +0F86;TIBETAN SIGN LCI RTAGS;Mn;230;NSM;;;;;N;;;;; +0F87;TIBETAN SIGN YANG RTAGS;Mn;230;NSM;;;;;N;;;;; +0F88;TIBETAN SIGN LCE TSA CAN;Lo;0;L;;;;;N;;;;; +0F89;TIBETAN SIGN MCHU CAN;Lo;0;L;;;;;N;;;;; +0F8A;TIBETAN SIGN GRU CAN RGYINGS;Lo;0;L;;;;;N;;;;; +0F8B;TIBETAN SIGN GRU MED RGYINGS;Lo;0;L;;;;;N;;;;; +0F8C;TIBETAN SIGN INVERTED MCHU CAN;Lo;0;L;;;;;N;;;;; +0F8D;TIBETAN SUBJOINED SIGN LCE TSA CAN;Mn;0;NSM;;;;;N;;;;; +0F8E;TIBETAN SUBJOINED SIGN MCHU CAN;Mn;0;NSM;;;;;N;;;;; +0F8F;TIBETAN SUBJOINED SIGN INVERTED MCHU CAN;Mn;0;NSM;;;;;N;;;;; +0F90;TIBETAN SUBJOINED LETTER KA;Mn;0;NSM;;;;;N;;;;; +0F91;TIBETAN SUBJOINED LETTER KHA;Mn;0;NSM;;;;;N;;;;; +0F92;TIBETAN SUBJOINED LETTER GA;Mn;0;NSM;;;;;N;;;;; +0F93;TIBETAN SUBJOINED LETTER GHA;Mn;0;NSM;0F92 0FB7;;;;N;;;;; +0F94;TIBETAN SUBJOINED LETTER NGA;Mn;0;NSM;;;;;N;;;;; +0F95;TIBETAN SUBJOINED LETTER CA;Mn;0;NSM;;;;;N;;;;; +0F96;TIBETAN SUBJOINED LETTER CHA;Mn;0;NSM;;;;;N;;;;; +0F97;TIBETAN SUBJOINED LETTER JA;Mn;0;NSM;;;;;N;;;;; +0F99;TIBETAN SUBJOINED LETTER NYA;Mn;0;NSM;;;;;N;;;;; +0F9A;TIBETAN SUBJOINED LETTER TTA;Mn;0;NSM;;;;;N;;;;; +0F9B;TIBETAN SUBJOINED LETTER TTHA;Mn;0;NSM;;;;;N;;;;; +0F9C;TIBETAN SUBJOINED LETTER DDA;Mn;0;NSM;;;;;N;;;;; +0F9D;TIBETAN SUBJOINED LETTER DDHA;Mn;0;NSM;0F9C 0FB7;;;;N;;;;; +0F9E;TIBETAN SUBJOINED LETTER NNA;Mn;0;NSM;;;;;N;;;;; +0F9F;TIBETAN SUBJOINED LETTER TA;Mn;0;NSM;;;;;N;;;;; +0FA0;TIBETAN SUBJOINED LETTER THA;Mn;0;NSM;;;;;N;;;;; +0FA1;TIBETAN SUBJOINED LETTER DA;Mn;0;NSM;;;;;N;;;;; +0FA2;TIBETAN SUBJOINED LETTER DHA;Mn;0;NSM;0FA1 0FB7;;;;N;;;;; +0FA3;TIBETAN SUBJOINED LETTER NA;Mn;0;NSM;;;;;N;;;;; +0FA4;TIBETAN SUBJOINED LETTER PA;Mn;0;NSM;;;;;N;;;;; +0FA5;TIBETAN SUBJOINED LETTER PHA;Mn;0;NSM;;;;;N;;;;; +0FA6;TIBETAN SUBJOINED LETTER BA;Mn;0;NSM;;;;;N;;;;; +0FA7;TIBETAN SUBJOINED LETTER BHA;Mn;0;NSM;0FA6 0FB7;;;;N;;;;; +0FA8;TIBETAN SUBJOINED LETTER MA;Mn;0;NSM;;;;;N;;;;; +0FA9;TIBETAN SUBJOINED LETTER TSA;Mn;0;NSM;;;;;N;;;;; +0FAA;TIBETAN SUBJOINED LETTER TSHA;Mn;0;NSM;;;;;N;;;;; +0FAB;TIBETAN SUBJOINED LETTER DZA;Mn;0;NSM;;;;;N;;;;; +0FAC;TIBETAN SUBJOINED LETTER DZHA;Mn;0;NSM;0FAB 0FB7;;;;N;;;;; +0FAD;TIBETAN SUBJOINED LETTER WA;Mn;0;NSM;;;;;N;;;;; +0FAE;TIBETAN SUBJOINED LETTER ZHA;Mn;0;NSM;;;;;N;;;;; +0FAF;TIBETAN SUBJOINED LETTER ZA;Mn;0;NSM;;;;;N;;;;; +0FB0;TIBETAN SUBJOINED LETTER -A;Mn;0;NSM;;;;;N;;;;; +0FB1;TIBETAN SUBJOINED LETTER YA;Mn;0;NSM;;;;;N;;;;; +0FB2;TIBETAN SUBJOINED LETTER RA;Mn;0;NSM;;;;;N;;;;; +0FB3;TIBETAN SUBJOINED LETTER LA;Mn;0;NSM;;;;;N;;;;; +0FB4;TIBETAN SUBJOINED LETTER SHA;Mn;0;NSM;;;;;N;;;;; +0FB5;TIBETAN SUBJOINED LETTER SSA;Mn;0;NSM;;;;;N;;;;; +0FB6;TIBETAN SUBJOINED LETTER SA;Mn;0;NSM;;;;;N;;;;; +0FB7;TIBETAN SUBJOINED LETTER HA;Mn;0;NSM;;;;;N;;;;; +0FB8;TIBETAN SUBJOINED LETTER A;Mn;0;NSM;;;;;N;;;;; +0FB9;TIBETAN SUBJOINED LETTER KSSA;Mn;0;NSM;0F90 0FB5;;;;N;;;;; +0FBA;TIBETAN SUBJOINED LETTER FIXED-FORM WA;Mn;0;NSM;;;;;N;;;;; +0FBB;TIBETAN SUBJOINED LETTER FIXED-FORM YA;Mn;0;NSM;;;;;N;;;;; +0FBC;TIBETAN SUBJOINED LETTER FIXED-FORM RA;Mn;0;NSM;;;;;N;;;;; +0FBE;TIBETAN KU RU KHA;So;0;L;;;;;N;;;;; +0FBF;TIBETAN KU RU KHA BZHI MIG CAN;So;0;L;;;;;N;;;;; +0FC0;TIBETAN CANTILLATION SIGN HEAVY BEAT;So;0;L;;;;;N;;;;; +0FC1;TIBETAN CANTILLATION SIGN LIGHT BEAT;So;0;L;;;;;N;;;;; +0FC2;TIBETAN CANTILLATION SIGN CANG TE-U;So;0;L;;;;;N;;;;; +0FC3;TIBETAN CANTILLATION SIGN SBUB -CHAL;So;0;L;;;;;N;;;;; +0FC4;TIBETAN SYMBOL DRIL BU;So;0;L;;;;;N;;;;; +0FC5;TIBETAN SYMBOL RDO RJE;So;0;L;;;;;N;;;;; +0FC6;TIBETAN SYMBOL PADMA GDAN;Mn;220;NSM;;;;;N;;;;; +0FC7;TIBETAN SYMBOL RDO RJE RGYA GRAM;So;0;L;;;;;N;;;;; +0FC8;TIBETAN SYMBOL PHUR PA;So;0;L;;;;;N;;;;; +0FC9;TIBETAN SYMBOL NOR BU;So;0;L;;;;;N;;;;; +0FCA;TIBETAN SYMBOL NOR BU NYIS -KHYIL;So;0;L;;;;;N;;;;; +0FCB;TIBETAN SYMBOL NOR BU GSUM -KHYIL;So;0;L;;;;;N;;;;; +0FCC;TIBETAN SYMBOL NOR BU BZHI -KHYIL;So;0;L;;;;;N;;;;; +0FCE;TIBETAN SIGN RDEL NAG RDEL DKAR;So;0;L;;;;;N;;;;; +0FCF;TIBETAN SIGN RDEL NAG GSUM;So;0;L;;;;;N;;;;; +0FD0;TIBETAN MARK BSKA- SHOG GI MGO RGYAN;Po;0;L;;;;;N;;;;; +0FD1;TIBETAN MARK MNYAM YIG GI MGO RGYAN;Po;0;L;;;;;N;;;;; +0FD2;TIBETAN MARK NYIS TSHEG;Po;0;L;;;;;N;;;;; +0FD3;TIBETAN MARK INITIAL BRDA RNYING YIG MGO MDUN MA;Po;0;L;;;;;N;;;;; +0FD4;TIBETAN MARK CLOSING BRDA RNYING YIG MGO SGAB MA;Po;0;L;;;;;N;;;;; +0FD5;RIGHT-FACING SVASTI SIGN;So;0;L;;;;;N;;;;; +0FD6;LEFT-FACING SVASTI SIGN;So;0;L;;;;;N;;;;; +0FD7;RIGHT-FACING SVASTI SIGN WITH DOTS;So;0;L;;;;;N;;;;; +0FD8;LEFT-FACING SVASTI SIGN WITH DOTS;So;0;L;;;;;N;;;;; +0FD9;TIBETAN MARK LEADING MCHAN RTAGS;Po;0;L;;;;;N;;;;; +0FDA;TIBETAN MARK TRAILING MCHAN RTAGS;Po;0;L;;;;;N;;;;; +1000;MYANMAR LETTER KA;Lo;0;L;;;;;N;;;;; +1001;MYANMAR LETTER KHA;Lo;0;L;;;;;N;;;;; +1002;MYANMAR LETTER GA;Lo;0;L;;;;;N;;;;; +1003;MYANMAR LETTER GHA;Lo;0;L;;;;;N;;;;; +1004;MYANMAR LETTER NGA;Lo;0;L;;;;;N;;;;; +1005;MYANMAR LETTER CA;Lo;0;L;;;;;N;;;;; +1006;MYANMAR LETTER CHA;Lo;0;L;;;;;N;;;;; +1007;MYANMAR LETTER JA;Lo;0;L;;;;;N;;;;; +1008;MYANMAR LETTER JHA;Lo;0;L;;;;;N;;;;; +1009;MYANMAR LETTER NYA;Lo;0;L;;;;;N;;;;; +100A;MYANMAR LETTER NNYA;Lo;0;L;;;;;N;;;;; +100B;MYANMAR LETTER TTA;Lo;0;L;;;;;N;;;;; +100C;MYANMAR LETTER TTHA;Lo;0;L;;;;;N;;;;; +100D;MYANMAR LETTER DDA;Lo;0;L;;;;;N;;;;; +100E;MYANMAR LETTER DDHA;Lo;0;L;;;;;N;;;;; +100F;MYANMAR LETTER NNA;Lo;0;L;;;;;N;;;;; +1010;MYANMAR LETTER TA;Lo;0;L;;;;;N;;;;; +1011;MYANMAR LETTER THA;Lo;0;L;;;;;N;;;;; +1012;MYANMAR LETTER DA;Lo;0;L;;;;;N;;;;; +1013;MYANMAR LETTER DHA;Lo;0;L;;;;;N;;;;; +1014;MYANMAR LETTER NA;Lo;0;L;;;;;N;;;;; +1015;MYANMAR LETTER PA;Lo;0;L;;;;;N;;;;; +1016;MYANMAR LETTER PHA;Lo;0;L;;;;;N;;;;; +1017;MYANMAR LETTER BA;Lo;0;L;;;;;N;;;;; +1018;MYANMAR LETTER BHA;Lo;0;L;;;;;N;;;;; +1019;MYANMAR LETTER MA;Lo;0;L;;;;;N;;;;; +101A;MYANMAR LETTER YA;Lo;0;L;;;;;N;;;;; +101B;MYANMAR LETTER RA;Lo;0;L;;;;;N;;;;; +101C;MYANMAR LETTER LA;Lo;0;L;;;;;N;;;;; +101D;MYANMAR LETTER WA;Lo;0;L;;;;;N;;;;; +101E;MYANMAR LETTER SA;Lo;0;L;;;;;N;;;;; +101F;MYANMAR LETTER HA;Lo;0;L;;;;;N;;;;; +1020;MYANMAR LETTER LLA;Lo;0;L;;;;;N;;;;; +1021;MYANMAR LETTER A;Lo;0;L;;;;;N;;;;; +1022;MYANMAR LETTER SHAN A;Lo;0;L;;;;;N;;;;; +1023;MYANMAR LETTER I;Lo;0;L;;;;;N;;;;; +1024;MYANMAR LETTER II;Lo;0;L;;;;;N;;;;; +1025;MYANMAR LETTER U;Lo;0;L;;;;;N;;;;; +1026;MYANMAR LETTER UU;Lo;0;L;1025 102E;;;;N;;;;; +1027;MYANMAR LETTER E;Lo;0;L;;;;;N;;;;; +1028;MYANMAR LETTER MON E;Lo;0;L;;;;;N;;;;; +1029;MYANMAR LETTER O;Lo;0;L;;;;;N;;;;; +102A;MYANMAR LETTER AU;Lo;0;L;;;;;N;;;;; +102B;MYANMAR VOWEL SIGN TALL AA;Mc;0;L;;;;;N;;;;; +102C;MYANMAR VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +102D;MYANMAR VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +102E;MYANMAR VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +102F;MYANMAR VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1030;MYANMAR VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +1031;MYANMAR VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +1032;MYANMAR VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +1033;MYANMAR VOWEL SIGN MON II;Mn;0;NSM;;;;;N;;;;; +1034;MYANMAR VOWEL SIGN MON O;Mn;0;NSM;;;;;N;;;;; +1035;MYANMAR VOWEL SIGN E ABOVE;Mn;0;NSM;;;;;N;;;;; +1036;MYANMAR SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +1037;MYANMAR SIGN DOT BELOW;Mn;7;NSM;;;;;N;;;;; +1038;MYANMAR SIGN VISARGA;Mc;0;L;;;;;N;;;;; +1039;MYANMAR SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +103A;MYANMAR SIGN ASAT;Mn;9;NSM;;;;;N;;;;; +103B;MYANMAR CONSONANT SIGN MEDIAL YA;Mc;0;L;;;;;N;;;;; +103C;MYANMAR CONSONANT SIGN MEDIAL RA;Mc;0;L;;;;;N;;;;; +103D;MYANMAR CONSONANT SIGN MEDIAL WA;Mn;0;NSM;;;;;N;;;;; +103E;MYANMAR CONSONANT SIGN MEDIAL HA;Mn;0;NSM;;;;;N;;;;; +103F;MYANMAR LETTER GREAT SA;Lo;0;L;;;;;N;;;;; +1040;MYANMAR DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +1041;MYANMAR DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +1042;MYANMAR DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +1043;MYANMAR DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +1044;MYANMAR DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +1045;MYANMAR DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +1046;MYANMAR DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +1047;MYANMAR DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1048;MYANMAR DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1049;MYANMAR DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +104A;MYANMAR SIGN LITTLE SECTION;Po;0;L;;;;;N;;;;; +104B;MYANMAR SIGN SECTION;Po;0;L;;;;;N;;;;; +104C;MYANMAR SYMBOL LOCATIVE;Po;0;L;;;;;N;;;;; +104D;MYANMAR SYMBOL COMPLETED;Po;0;L;;;;;N;;;;; +104E;MYANMAR SYMBOL AFOREMENTIONED;Po;0;L;;;;;N;;;;; +104F;MYANMAR SYMBOL GENITIVE;Po;0;L;;;;;N;;;;; +1050;MYANMAR LETTER SHA;Lo;0;L;;;;;N;;;;; +1051;MYANMAR LETTER SSA;Lo;0;L;;;;;N;;;;; +1052;MYANMAR LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +1053;MYANMAR LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +1054;MYANMAR LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +1055;MYANMAR LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +1056;MYANMAR VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;; +1057;MYANMAR VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;; +1058;MYANMAR VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +1059;MYANMAR VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +105A;MYANMAR LETTER MON NGA;Lo;0;L;;;;;N;;;;; +105B;MYANMAR LETTER MON JHA;Lo;0;L;;;;;N;;;;; +105C;MYANMAR LETTER MON BBA;Lo;0;L;;;;;N;;;;; +105D;MYANMAR LETTER MON BBE;Lo;0;L;;;;;N;;;;; +105E;MYANMAR CONSONANT SIGN MON MEDIAL NA;Mn;0;NSM;;;;;N;;;;; +105F;MYANMAR CONSONANT SIGN MON MEDIAL MA;Mn;0;NSM;;;;;N;;;;; +1060;MYANMAR CONSONANT SIGN MON MEDIAL LA;Mn;0;NSM;;;;;N;;;;; +1061;MYANMAR LETTER SGAW KAREN SHA;Lo;0;L;;;;;N;;;;; +1062;MYANMAR VOWEL SIGN SGAW KAREN EU;Mc;0;L;;;;;N;;;;; +1063;MYANMAR TONE MARK SGAW KAREN HATHI;Mc;0;L;;;;;N;;;;; +1064;MYANMAR TONE MARK SGAW KAREN KE PHO;Mc;0;L;;;;;N;;;;; +1065;MYANMAR LETTER WESTERN PWO KAREN THA;Lo;0;L;;;;;N;;;;; +1066;MYANMAR LETTER WESTERN PWO KAREN PWA;Lo;0;L;;;;;N;;;;; +1067;MYANMAR VOWEL SIGN WESTERN PWO KAREN EU;Mc;0;L;;;;;N;;;;; +1068;MYANMAR VOWEL SIGN WESTERN PWO KAREN UE;Mc;0;L;;;;;N;;;;; +1069;MYANMAR SIGN WESTERN PWO KAREN TONE-1;Mc;0;L;;;;;N;;;;; +106A;MYANMAR SIGN WESTERN PWO KAREN TONE-2;Mc;0;L;;;;;N;;;;; +106B;MYANMAR SIGN WESTERN PWO KAREN TONE-3;Mc;0;L;;;;;N;;;;; +106C;MYANMAR SIGN WESTERN PWO KAREN TONE-4;Mc;0;L;;;;;N;;;;; +106D;MYANMAR SIGN WESTERN PWO KAREN TONE-5;Mc;0;L;;;;;N;;;;; +106E;MYANMAR LETTER EASTERN PWO KAREN NNA;Lo;0;L;;;;;N;;;;; +106F;MYANMAR LETTER EASTERN PWO KAREN YWA;Lo;0;L;;;;;N;;;;; +1070;MYANMAR LETTER EASTERN PWO KAREN GHWA;Lo;0;L;;;;;N;;;;; +1071;MYANMAR VOWEL SIGN GEBA KAREN I;Mn;0;NSM;;;;;N;;;;; +1072;MYANMAR VOWEL SIGN KAYAH OE;Mn;0;NSM;;;;;N;;;;; +1073;MYANMAR VOWEL SIGN KAYAH U;Mn;0;NSM;;;;;N;;;;; +1074;MYANMAR VOWEL SIGN KAYAH EE;Mn;0;NSM;;;;;N;;;;; +1075;MYANMAR LETTER SHAN KA;Lo;0;L;;;;;N;;;;; +1076;MYANMAR LETTER SHAN KHA;Lo;0;L;;;;;N;;;;; +1077;MYANMAR LETTER SHAN GA;Lo;0;L;;;;;N;;;;; +1078;MYANMAR LETTER SHAN CA;Lo;0;L;;;;;N;;;;; +1079;MYANMAR LETTER SHAN ZA;Lo;0;L;;;;;N;;;;; +107A;MYANMAR LETTER SHAN NYA;Lo;0;L;;;;;N;;;;; +107B;MYANMAR LETTER SHAN DA;Lo;0;L;;;;;N;;;;; +107C;MYANMAR LETTER SHAN NA;Lo;0;L;;;;;N;;;;; +107D;MYANMAR LETTER SHAN PHA;Lo;0;L;;;;;N;;;;; +107E;MYANMAR LETTER SHAN FA;Lo;0;L;;;;;N;;;;; +107F;MYANMAR LETTER SHAN BA;Lo;0;L;;;;;N;;;;; +1080;MYANMAR LETTER SHAN THA;Lo;0;L;;;;;N;;;;; +1081;MYANMAR LETTER SHAN HA;Lo;0;L;;;;;N;;;;; +1082;MYANMAR CONSONANT SIGN SHAN MEDIAL WA;Mn;0;NSM;;;;;N;;;;; +1083;MYANMAR VOWEL SIGN SHAN AA;Mc;0;L;;;;;N;;;;; +1084;MYANMAR VOWEL SIGN SHAN E;Mc;0;L;;;;;N;;;;; +1085;MYANMAR VOWEL SIGN SHAN E ABOVE;Mn;0;NSM;;;;;N;;;;; +1086;MYANMAR VOWEL SIGN SHAN FINAL Y;Mn;0;NSM;;;;;N;;;;; +1087;MYANMAR SIGN SHAN TONE-2;Mc;0;L;;;;;N;;;;; +1088;MYANMAR SIGN SHAN TONE-3;Mc;0;L;;;;;N;;;;; +1089;MYANMAR SIGN SHAN TONE-5;Mc;0;L;;;;;N;;;;; +108A;MYANMAR SIGN SHAN TONE-6;Mc;0;L;;;;;N;;;;; +108B;MYANMAR SIGN SHAN COUNCIL TONE-2;Mc;0;L;;;;;N;;;;; +108C;MYANMAR SIGN SHAN COUNCIL TONE-3;Mc;0;L;;;;;N;;;;; +108D;MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE;Mn;220;NSM;;;;;N;;;;; +108E;MYANMAR LETTER RUMAI PALAUNG FA;Lo;0;L;;;;;N;;;;; +108F;MYANMAR SIGN RUMAI PALAUNG TONE-5;Mc;0;L;;;;;N;;;;; +1090;MYANMAR SHAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +1091;MYANMAR SHAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +1092;MYANMAR SHAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +1093;MYANMAR SHAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +1094;MYANMAR SHAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +1095;MYANMAR SHAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +1096;MYANMAR SHAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +1097;MYANMAR SHAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1098;MYANMAR SHAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1099;MYANMAR SHAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +109A;MYANMAR SIGN KHAMTI TONE-1;Mc;0;L;;;;;N;;;;; +109B;MYANMAR SIGN KHAMTI TONE-3;Mc;0;L;;;;;N;;;;; +109C;MYANMAR VOWEL SIGN AITON A;Mc;0;L;;;;;N;;;;; +109D;MYANMAR VOWEL SIGN AITON AI;Mn;0;NSM;;;;;N;;;;; +109E;MYANMAR SYMBOL SHAN ONE;So;0;L;;;;;N;;;;; +109F;MYANMAR SYMBOL SHAN EXCLAMATION;So;0;L;;;;;N;;;;; +10A0;GEORGIAN CAPITAL LETTER AN;Lu;0;L;;;;;N;;;;2D00; +10A1;GEORGIAN CAPITAL LETTER BAN;Lu;0;L;;;;;N;;;;2D01; +10A2;GEORGIAN CAPITAL LETTER GAN;Lu;0;L;;;;;N;;;;2D02; +10A3;GEORGIAN CAPITAL LETTER DON;Lu;0;L;;;;;N;;;;2D03; +10A4;GEORGIAN CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;2D04; +10A5;GEORGIAN CAPITAL LETTER VIN;Lu;0;L;;;;;N;;;;2D05; +10A6;GEORGIAN CAPITAL LETTER ZEN;Lu;0;L;;;;;N;;;;2D06; +10A7;GEORGIAN CAPITAL LETTER TAN;Lu;0;L;;;;;N;;;;2D07; +10A8;GEORGIAN CAPITAL LETTER IN;Lu;0;L;;;;;N;;;;2D08; +10A9;GEORGIAN CAPITAL LETTER KAN;Lu;0;L;;;;;N;;;;2D09; +10AA;GEORGIAN CAPITAL LETTER LAS;Lu;0;L;;;;;N;;;;2D0A; +10AB;GEORGIAN CAPITAL LETTER MAN;Lu;0;L;;;;;N;;;;2D0B; +10AC;GEORGIAN CAPITAL LETTER NAR;Lu;0;L;;;;;N;;;;2D0C; +10AD;GEORGIAN CAPITAL LETTER ON;Lu;0;L;;;;;N;;;;2D0D; +10AE;GEORGIAN CAPITAL LETTER PAR;Lu;0;L;;;;;N;;;;2D0E; +10AF;GEORGIAN CAPITAL LETTER ZHAR;Lu;0;L;;;;;N;;;;2D0F; +10B0;GEORGIAN CAPITAL LETTER RAE;Lu;0;L;;;;;N;;;;2D10; +10B1;GEORGIAN CAPITAL LETTER SAN;Lu;0;L;;;;;N;;;;2D11; +10B2;GEORGIAN CAPITAL LETTER TAR;Lu;0;L;;;;;N;;;;2D12; +10B3;GEORGIAN CAPITAL LETTER UN;Lu;0;L;;;;;N;;;;2D13; +10B4;GEORGIAN CAPITAL LETTER PHAR;Lu;0;L;;;;;N;;;;2D14; +10B5;GEORGIAN CAPITAL LETTER KHAR;Lu;0;L;;;;;N;;;;2D15; +10B6;GEORGIAN CAPITAL LETTER GHAN;Lu;0;L;;;;;N;;;;2D16; +10B7;GEORGIAN CAPITAL LETTER QAR;Lu;0;L;;;;;N;;;;2D17; +10B8;GEORGIAN CAPITAL LETTER SHIN;Lu;0;L;;;;;N;;;;2D18; +10B9;GEORGIAN CAPITAL LETTER CHIN;Lu;0;L;;;;;N;;;;2D19; +10BA;GEORGIAN CAPITAL LETTER CAN;Lu;0;L;;;;;N;;;;2D1A; +10BB;GEORGIAN CAPITAL LETTER JIL;Lu;0;L;;;;;N;;;;2D1B; +10BC;GEORGIAN CAPITAL LETTER CIL;Lu;0;L;;;;;N;;;;2D1C; +10BD;GEORGIAN CAPITAL LETTER CHAR;Lu;0;L;;;;;N;;;;2D1D; +10BE;GEORGIAN CAPITAL LETTER XAN;Lu;0;L;;;;;N;;;;2D1E; +10BF;GEORGIAN CAPITAL LETTER JHAN;Lu;0;L;;;;;N;;;;2D1F; +10C0;GEORGIAN CAPITAL LETTER HAE;Lu;0;L;;;;;N;;;;2D20; +10C1;GEORGIAN CAPITAL LETTER HE;Lu;0;L;;;;;N;;;;2D21; +10C2;GEORGIAN CAPITAL LETTER HIE;Lu;0;L;;;;;N;;;;2D22; +10C3;GEORGIAN CAPITAL LETTER WE;Lu;0;L;;;;;N;;;;2D23; +10C4;GEORGIAN CAPITAL LETTER HAR;Lu;0;L;;;;;N;;;;2D24; +10C5;GEORGIAN CAPITAL LETTER HOE;Lu;0;L;;;;;N;;;;2D25; +10C7;GEORGIAN CAPITAL LETTER YN;Lu;0;L;;;;;N;;;;2D27; +10CD;GEORGIAN CAPITAL LETTER AEN;Lu;0;L;;;;;N;;;;2D2D; +10D0;GEORGIAN LETTER AN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER AN;;;; +10D1;GEORGIAN LETTER BAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER BAN;;;; +10D2;GEORGIAN LETTER GAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER GAN;;;; +10D3;GEORGIAN LETTER DON;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER DON;;;; +10D4;GEORGIAN LETTER EN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER EN;;;; +10D5;GEORGIAN LETTER VIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER VIN;;;; +10D6;GEORGIAN LETTER ZEN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ZEN;;;; +10D7;GEORGIAN LETTER TAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER TAN;;;; +10D8;GEORGIAN LETTER IN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER IN;;;; +10D9;GEORGIAN LETTER KAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER KAN;;;; +10DA;GEORGIAN LETTER LAS;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER LAS;;;; +10DB;GEORGIAN LETTER MAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER MAN;;;; +10DC;GEORGIAN LETTER NAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER NAR;;;; +10DD;GEORGIAN LETTER ON;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ON;;;; +10DE;GEORGIAN LETTER PAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER PAR;;;; +10DF;GEORGIAN LETTER ZHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ZHAR;;;; +10E0;GEORGIAN LETTER RAE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER RAE;;;; +10E1;GEORGIAN LETTER SAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER SAN;;;; +10E2;GEORGIAN LETTER TAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER TAR;;;; +10E3;GEORGIAN LETTER UN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER UN;;;; +10E4;GEORGIAN LETTER PHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER PHAR;;;; +10E5;GEORGIAN LETTER KHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER KHAR;;;; +10E6;GEORGIAN LETTER GHAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER GHAN;;;; +10E7;GEORGIAN LETTER QAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER QAR;;;; +10E8;GEORGIAN LETTER SHIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER SHIN;;;; +10E9;GEORGIAN LETTER CHIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CHIN;;;; +10EA;GEORGIAN LETTER CAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CAN;;;; +10EB;GEORGIAN LETTER JIL;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER JIL;;;; +10EC;GEORGIAN LETTER CIL;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CIL;;;; +10ED;GEORGIAN LETTER CHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CHAR;;;; +10EE;GEORGIAN LETTER XAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER XAN;;;; +10EF;GEORGIAN LETTER JHAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER JHAN;;;; +10F0;GEORGIAN LETTER HAE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HAE;;;; +10F1;GEORGIAN LETTER HE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HE;;;; +10F2;GEORGIAN LETTER HIE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HIE;;;; +10F3;GEORGIAN LETTER WE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER WE;;;; +10F4;GEORGIAN LETTER HAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HAR;;;; +10F5;GEORGIAN LETTER HOE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HOE;;;; +10F6;GEORGIAN LETTER FI;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER FI;;;; +10F7;GEORGIAN LETTER YN;Lo;0;L;;;;;N;;;;; +10F8;GEORGIAN LETTER ELIFI;Lo;0;L;;;;;N;;;;; +10F9;GEORGIAN LETTER TURNED GAN;Lo;0;L;;;;;N;;;;; +10FA;GEORGIAN LETTER AIN;Lo;0;L;;;;;N;;;;; +10FB;GEORGIAN PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;; +10FC;MODIFIER LETTER GEORGIAN NAR;Lm;0;L;<super> 10DC;;;;N;;;;; +10FD;GEORGIAN LETTER AEN;Lo;0;L;;;;;N;;;;; +10FE;GEORGIAN LETTER HARD SIGN;Lo;0;L;;;;;N;;;;; +10FF;GEORGIAN LETTER LABIAL SIGN;Lo;0;L;;;;;N;;;;; +1100;HANGUL CHOSEONG KIYEOK;Lo;0;L;;;;;N;;;;; +1101;HANGUL CHOSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;;;; +1102;HANGUL CHOSEONG NIEUN;Lo;0;L;;;;;N;;;;; +1103;HANGUL CHOSEONG TIKEUT;Lo;0;L;;;;;N;;;;; +1104;HANGUL CHOSEONG SSANGTIKEUT;Lo;0;L;;;;;N;;;;; +1105;HANGUL CHOSEONG RIEUL;Lo;0;L;;;;;N;;;;; +1106;HANGUL CHOSEONG MIEUM;Lo;0;L;;;;;N;;;;; +1107;HANGUL CHOSEONG PIEUP;Lo;0;L;;;;;N;;;;; +1108;HANGUL CHOSEONG SSANGPIEUP;Lo;0;L;;;;;N;;;;; +1109;HANGUL CHOSEONG SIOS;Lo;0;L;;;;;N;;;;; +110A;HANGUL CHOSEONG SSANGSIOS;Lo;0;L;;;;;N;;;;; +110B;HANGUL CHOSEONG IEUNG;Lo;0;L;;;;;N;;;;; +110C;HANGUL CHOSEONG CIEUC;Lo;0;L;;;;;N;;;;; +110D;HANGUL CHOSEONG SSANGCIEUC;Lo;0;L;;;;;N;;;;; +110E;HANGUL CHOSEONG CHIEUCH;Lo;0;L;;;;;N;;;;; +110F;HANGUL CHOSEONG KHIEUKH;Lo;0;L;;;;;N;;;;; +1110;HANGUL CHOSEONG THIEUTH;Lo;0;L;;;;;N;;;;; +1111;HANGUL CHOSEONG PHIEUPH;Lo;0;L;;;;;N;;;;; +1112;HANGUL CHOSEONG HIEUH;Lo;0;L;;;;;N;;;;; +1113;HANGUL CHOSEONG NIEUN-KIYEOK;Lo;0;L;;;;;N;;;;; +1114;HANGUL CHOSEONG SSANGNIEUN;Lo;0;L;;;;;N;;;;; +1115;HANGUL CHOSEONG NIEUN-TIKEUT;Lo;0;L;;;;;N;;;;; +1116;HANGUL CHOSEONG NIEUN-PIEUP;Lo;0;L;;;;;N;;;;; +1117;HANGUL CHOSEONG TIKEUT-KIYEOK;Lo;0;L;;;;;N;;;;; +1118;HANGUL CHOSEONG RIEUL-NIEUN;Lo;0;L;;;;;N;;;;; +1119;HANGUL CHOSEONG SSANGRIEUL;Lo;0;L;;;;;N;;;;; +111A;HANGUL CHOSEONG RIEUL-HIEUH;Lo;0;L;;;;;N;;;;; +111B;HANGUL CHOSEONG KAPYEOUNRIEUL;Lo;0;L;;;;;N;;;;; +111C;HANGUL CHOSEONG MIEUM-PIEUP;Lo;0;L;;;;;N;;;;; +111D;HANGUL CHOSEONG KAPYEOUNMIEUM;Lo;0;L;;;;;N;;;;; +111E;HANGUL CHOSEONG PIEUP-KIYEOK;Lo;0;L;;;;;N;;;;; +111F;HANGUL CHOSEONG PIEUP-NIEUN;Lo;0;L;;;;;N;;;;; +1120;HANGUL CHOSEONG PIEUP-TIKEUT;Lo;0;L;;;;;N;;;;; +1121;HANGUL CHOSEONG PIEUP-SIOS;Lo;0;L;;;;;N;;;;; +1122;HANGUL CHOSEONG PIEUP-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;; +1123;HANGUL CHOSEONG PIEUP-SIOS-TIKEUT;Lo;0;L;;;;;N;;;;; +1124;HANGUL CHOSEONG PIEUP-SIOS-PIEUP;Lo;0;L;;;;;N;;;;; +1125;HANGUL CHOSEONG PIEUP-SSANGSIOS;Lo;0;L;;;;;N;;;;; +1126;HANGUL CHOSEONG PIEUP-SIOS-CIEUC;Lo;0;L;;;;;N;;;;; +1127;HANGUL CHOSEONG PIEUP-CIEUC;Lo;0;L;;;;;N;;;;; +1128;HANGUL CHOSEONG PIEUP-CHIEUCH;Lo;0;L;;;;;N;;;;; +1129;HANGUL CHOSEONG PIEUP-THIEUTH;Lo;0;L;;;;;N;;;;; +112A;HANGUL CHOSEONG PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;; +112B;HANGUL CHOSEONG KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; +112C;HANGUL CHOSEONG KAPYEOUNSSANGPIEUP;Lo;0;L;;;;;N;;;;; +112D;HANGUL CHOSEONG SIOS-KIYEOK;Lo;0;L;;;;;N;;;;; +112E;HANGUL CHOSEONG SIOS-NIEUN;Lo;0;L;;;;;N;;;;; +112F;HANGUL CHOSEONG SIOS-TIKEUT;Lo;0;L;;;;;N;;;;; +1130;HANGUL CHOSEONG SIOS-RIEUL;Lo;0;L;;;;;N;;;;; +1131;HANGUL CHOSEONG SIOS-MIEUM;Lo;0;L;;;;;N;;;;; +1132;HANGUL CHOSEONG SIOS-PIEUP;Lo;0;L;;;;;N;;;;; +1133;HANGUL CHOSEONG SIOS-PIEUP-KIYEOK;Lo;0;L;;;;;N;;;;; +1134;HANGUL CHOSEONG SIOS-SSANGSIOS;Lo;0;L;;;;;N;;;;; +1135;HANGUL CHOSEONG SIOS-IEUNG;Lo;0;L;;;;;N;;;;; +1136;HANGUL CHOSEONG SIOS-CIEUC;Lo;0;L;;;;;N;;;;; +1137;HANGUL CHOSEONG SIOS-CHIEUCH;Lo;0;L;;;;;N;;;;; +1138;HANGUL CHOSEONG SIOS-KHIEUKH;Lo;0;L;;;;;N;;;;; +1139;HANGUL CHOSEONG SIOS-THIEUTH;Lo;0;L;;;;;N;;;;; +113A;HANGUL CHOSEONG SIOS-PHIEUPH;Lo;0;L;;;;;N;;;;; +113B;HANGUL CHOSEONG SIOS-HIEUH;Lo;0;L;;;;;N;;;;; +113C;HANGUL CHOSEONG CHITUEUMSIOS;Lo;0;L;;;;;N;;;;; +113D;HANGUL CHOSEONG CHITUEUMSSANGSIOS;Lo;0;L;;;;;N;;;;; +113E;HANGUL CHOSEONG CEONGCHIEUMSIOS;Lo;0;L;;;;;N;;;;; +113F;HANGUL CHOSEONG CEONGCHIEUMSSANGSIOS;Lo;0;L;;;;;N;;;;; +1140;HANGUL CHOSEONG PANSIOS;Lo;0;L;;;;;N;;;;; +1141;HANGUL CHOSEONG IEUNG-KIYEOK;Lo;0;L;;;;;N;;;;; +1142;HANGUL CHOSEONG IEUNG-TIKEUT;Lo;0;L;;;;;N;;;;; +1143;HANGUL CHOSEONG IEUNG-MIEUM;Lo;0;L;;;;;N;;;;; +1144;HANGUL CHOSEONG IEUNG-PIEUP;Lo;0;L;;;;;N;;;;; +1145;HANGUL CHOSEONG IEUNG-SIOS;Lo;0;L;;;;;N;;;;; +1146;HANGUL CHOSEONG IEUNG-PANSIOS;Lo;0;L;;;;;N;;;;; +1147;HANGUL CHOSEONG SSANGIEUNG;Lo;0;L;;;;;N;;;;; +1148;HANGUL CHOSEONG IEUNG-CIEUC;Lo;0;L;;;;;N;;;;; +1149;HANGUL CHOSEONG IEUNG-CHIEUCH;Lo;0;L;;;;;N;;;;; +114A;HANGUL CHOSEONG IEUNG-THIEUTH;Lo;0;L;;;;;N;;;;; +114B;HANGUL CHOSEONG IEUNG-PHIEUPH;Lo;0;L;;;;;N;;;;; +114C;HANGUL CHOSEONG YESIEUNG;Lo;0;L;;;;;N;;;;; +114D;HANGUL CHOSEONG CIEUC-IEUNG;Lo;0;L;;;;;N;;;;; +114E;HANGUL CHOSEONG CHITUEUMCIEUC;Lo;0;L;;;;;N;;;;; +114F;HANGUL CHOSEONG CHITUEUMSSANGCIEUC;Lo;0;L;;;;;N;;;;; +1150;HANGUL CHOSEONG CEONGCHIEUMCIEUC;Lo;0;L;;;;;N;;;;; +1151;HANGUL CHOSEONG CEONGCHIEUMSSANGCIEUC;Lo;0;L;;;;;N;;;;; +1152;HANGUL CHOSEONG CHIEUCH-KHIEUKH;Lo;0;L;;;;;N;;;;; +1153;HANGUL CHOSEONG CHIEUCH-HIEUH;Lo;0;L;;;;;N;;;;; +1154;HANGUL CHOSEONG CHITUEUMCHIEUCH;Lo;0;L;;;;;N;;;;; +1155;HANGUL CHOSEONG CEONGCHIEUMCHIEUCH;Lo;0;L;;;;;N;;;;; +1156;HANGUL CHOSEONG PHIEUPH-PIEUP;Lo;0;L;;;;;N;;;;; +1157;HANGUL CHOSEONG KAPYEOUNPHIEUPH;Lo;0;L;;;;;N;;;;; +1158;HANGUL CHOSEONG SSANGHIEUH;Lo;0;L;;;;;N;;;;; +1159;HANGUL CHOSEONG YEORINHIEUH;Lo;0;L;;;;;N;;;;; +115A;HANGUL CHOSEONG KIYEOK-TIKEUT;Lo;0;L;;;;;N;;;;; +115B;HANGUL CHOSEONG NIEUN-SIOS;Lo;0;L;;;;;N;;;;; +115C;HANGUL CHOSEONG NIEUN-CIEUC;Lo;0;L;;;;;N;;;;; +115D;HANGUL CHOSEONG NIEUN-HIEUH;Lo;0;L;;;;;N;;;;; +115E;HANGUL CHOSEONG TIKEUT-RIEUL;Lo;0;L;;;;;N;;;;; +115F;HANGUL CHOSEONG FILLER;Lo;0;L;;;;;N;;;;; +1160;HANGUL JUNGSEONG FILLER;Lo;0;L;;;;;N;;;;; +1161;HANGUL JUNGSEONG A;Lo;0;L;;;;;N;;;;; +1162;HANGUL JUNGSEONG AE;Lo;0;L;;;;;N;;;;; +1163;HANGUL JUNGSEONG YA;Lo;0;L;;;;;N;;;;; +1164;HANGUL JUNGSEONG YAE;Lo;0;L;;;;;N;;;;; +1165;HANGUL JUNGSEONG EO;Lo;0;L;;;;;N;;;;; +1166;HANGUL JUNGSEONG E;Lo;0;L;;;;;N;;;;; +1167;HANGUL JUNGSEONG YEO;Lo;0;L;;;;;N;;;;; +1168;HANGUL JUNGSEONG YE;Lo;0;L;;;;;N;;;;; +1169;HANGUL JUNGSEONG O;Lo;0;L;;;;;N;;;;; +116A;HANGUL JUNGSEONG WA;Lo;0;L;;;;;N;;;;; +116B;HANGUL JUNGSEONG WAE;Lo;0;L;;;;;N;;;;; +116C;HANGUL JUNGSEONG OE;Lo;0;L;;;;;N;;;;; +116D;HANGUL JUNGSEONG YO;Lo;0;L;;;;;N;;;;; +116E;HANGUL JUNGSEONG U;Lo;0;L;;;;;N;;;;; +116F;HANGUL JUNGSEONG WEO;Lo;0;L;;;;;N;;;;; +1170;HANGUL JUNGSEONG WE;Lo;0;L;;;;;N;;;;; +1171;HANGUL JUNGSEONG WI;Lo;0;L;;;;;N;;;;; +1172;HANGUL JUNGSEONG YU;Lo;0;L;;;;;N;;;;; +1173;HANGUL JUNGSEONG EU;Lo;0;L;;;;;N;;;;; +1174;HANGUL JUNGSEONG YI;Lo;0;L;;;;;N;;;;; +1175;HANGUL JUNGSEONG I;Lo;0;L;;;;;N;;;;; +1176;HANGUL JUNGSEONG A-O;Lo;0;L;;;;;N;;;;; +1177;HANGUL JUNGSEONG A-U;Lo;0;L;;;;;N;;;;; +1178;HANGUL JUNGSEONG YA-O;Lo;0;L;;;;;N;;;;; +1179;HANGUL JUNGSEONG YA-YO;Lo;0;L;;;;;N;;;;; +117A;HANGUL JUNGSEONG EO-O;Lo;0;L;;;;;N;;;;; +117B;HANGUL JUNGSEONG EO-U;Lo;0;L;;;;;N;;;;; +117C;HANGUL JUNGSEONG EO-EU;Lo;0;L;;;;;N;;;;; +117D;HANGUL JUNGSEONG YEO-O;Lo;0;L;;;;;N;;;;; +117E;HANGUL JUNGSEONG YEO-U;Lo;0;L;;;;;N;;;;; +117F;HANGUL JUNGSEONG O-EO;Lo;0;L;;;;;N;;;;; +1180;HANGUL JUNGSEONG O-E;Lo;0;L;;;;;N;;;;; +1181;HANGUL JUNGSEONG O-YE;Lo;0;L;;;;;N;;;;; +1182;HANGUL JUNGSEONG O-O;Lo;0;L;;;;;N;;;;; +1183;HANGUL JUNGSEONG O-U;Lo;0;L;;;;;N;;;;; +1184;HANGUL JUNGSEONG YO-YA;Lo;0;L;;;;;N;;;;; +1185;HANGUL JUNGSEONG YO-YAE;Lo;0;L;;;;;N;;;;; +1186;HANGUL JUNGSEONG YO-YEO;Lo;0;L;;;;;N;;;;; +1187;HANGUL JUNGSEONG YO-O;Lo;0;L;;;;;N;;;;; +1188;HANGUL JUNGSEONG YO-I;Lo;0;L;;;;;N;;;;; +1189;HANGUL JUNGSEONG U-A;Lo;0;L;;;;;N;;;;; +118A;HANGUL JUNGSEONG U-AE;Lo;0;L;;;;;N;;;;; +118B;HANGUL JUNGSEONG U-EO-EU;Lo;0;L;;;;;N;;;;; +118C;HANGUL JUNGSEONG U-YE;Lo;0;L;;;;;N;;;;; +118D;HANGUL JUNGSEONG U-U;Lo;0;L;;;;;N;;;;; +118E;HANGUL JUNGSEONG YU-A;Lo;0;L;;;;;N;;;;; +118F;HANGUL JUNGSEONG YU-EO;Lo;0;L;;;;;N;;;;; +1190;HANGUL JUNGSEONG YU-E;Lo;0;L;;;;;N;;;;; +1191;HANGUL JUNGSEONG YU-YEO;Lo;0;L;;;;;N;;;;; +1192;HANGUL JUNGSEONG YU-YE;Lo;0;L;;;;;N;;;;; +1193;HANGUL JUNGSEONG YU-U;Lo;0;L;;;;;N;;;;; +1194;HANGUL JUNGSEONG YU-I;Lo;0;L;;;;;N;;;;; +1195;HANGUL JUNGSEONG EU-U;Lo;0;L;;;;;N;;;;; +1196;HANGUL JUNGSEONG EU-EU;Lo;0;L;;;;;N;;;;; +1197;HANGUL JUNGSEONG YI-U;Lo;0;L;;;;;N;;;;; +1198;HANGUL JUNGSEONG I-A;Lo;0;L;;;;;N;;;;; +1199;HANGUL JUNGSEONG I-YA;Lo;0;L;;;;;N;;;;; +119A;HANGUL JUNGSEONG I-O;Lo;0;L;;;;;N;;;;; +119B;HANGUL JUNGSEONG I-U;Lo;0;L;;;;;N;;;;; +119C;HANGUL JUNGSEONG I-EU;Lo;0;L;;;;;N;;;;; +119D;HANGUL JUNGSEONG I-ARAEA;Lo;0;L;;;;;N;;;;; +119E;HANGUL JUNGSEONG ARAEA;Lo;0;L;;;;;N;;;;; +119F;HANGUL JUNGSEONG ARAEA-EO;Lo;0;L;;;;;N;;;;; +11A0;HANGUL JUNGSEONG ARAEA-U;Lo;0;L;;;;;N;;;;; +11A1;HANGUL JUNGSEONG ARAEA-I;Lo;0;L;;;;;N;;;;; +11A2;HANGUL JUNGSEONG SSANGARAEA;Lo;0;L;;;;;N;;;;; +11A3;HANGUL JUNGSEONG A-EU;Lo;0;L;;;;;N;;;;; +11A4;HANGUL JUNGSEONG YA-U;Lo;0;L;;;;;N;;;;; +11A5;HANGUL JUNGSEONG YEO-YA;Lo;0;L;;;;;N;;;;; +11A6;HANGUL JUNGSEONG O-YA;Lo;0;L;;;;;N;;;;; +11A7;HANGUL JUNGSEONG O-YAE;Lo;0;L;;;;;N;;;;; +11A8;HANGUL JONGSEONG KIYEOK;Lo;0;L;;;;;N;;;;; +11A9;HANGUL JONGSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;;;; +11AA;HANGUL JONGSEONG KIYEOK-SIOS;Lo;0;L;;;;;N;;;;; +11AB;HANGUL JONGSEONG NIEUN;Lo;0;L;;;;;N;;;;; +11AC;HANGUL JONGSEONG NIEUN-CIEUC;Lo;0;L;;;;;N;;;;; +11AD;HANGUL JONGSEONG NIEUN-HIEUH;Lo;0;L;;;;;N;;;;; +11AE;HANGUL JONGSEONG TIKEUT;Lo;0;L;;;;;N;;;;; +11AF;HANGUL JONGSEONG RIEUL;Lo;0;L;;;;;N;;;;; +11B0;HANGUL JONGSEONG RIEUL-KIYEOK;Lo;0;L;;;;;N;;;;; +11B1;HANGUL JONGSEONG RIEUL-MIEUM;Lo;0;L;;;;;N;;;;; +11B2;HANGUL JONGSEONG RIEUL-PIEUP;Lo;0;L;;;;;N;;;;; +11B3;HANGUL JONGSEONG RIEUL-SIOS;Lo;0;L;;;;;N;;;;; +11B4;HANGUL JONGSEONG RIEUL-THIEUTH;Lo;0;L;;;;;N;;;;; +11B5;HANGUL JONGSEONG RIEUL-PHIEUPH;Lo;0;L;;;;;N;;;;; +11B6;HANGUL JONGSEONG RIEUL-HIEUH;Lo;0;L;;;;;N;;;;; +11B7;HANGUL JONGSEONG MIEUM;Lo;0;L;;;;;N;;;;; +11B8;HANGUL JONGSEONG PIEUP;Lo;0;L;;;;;N;;;;; +11B9;HANGUL JONGSEONG PIEUP-SIOS;Lo;0;L;;;;;N;;;;; +11BA;HANGUL JONGSEONG SIOS;Lo;0;L;;;;;N;;;;; +11BB;HANGUL JONGSEONG SSANGSIOS;Lo;0;L;;;;;N;;;;; +11BC;HANGUL JONGSEONG IEUNG;Lo;0;L;;;;;N;;;;; +11BD;HANGUL JONGSEONG CIEUC;Lo;0;L;;;;;N;;;;; +11BE;HANGUL JONGSEONG CHIEUCH;Lo;0;L;;;;;N;;;;; +11BF;HANGUL JONGSEONG KHIEUKH;Lo;0;L;;;;;N;;;;; +11C0;HANGUL JONGSEONG THIEUTH;Lo;0;L;;;;;N;;;;; +11C1;HANGUL JONGSEONG PHIEUPH;Lo;0;L;;;;;N;;;;; +11C2;HANGUL JONGSEONG HIEUH;Lo;0;L;;;;;N;;;;; +11C3;HANGUL JONGSEONG KIYEOK-RIEUL;Lo;0;L;;;;;N;;;;; +11C4;HANGUL JONGSEONG KIYEOK-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;; +11C5;HANGUL JONGSEONG NIEUN-KIYEOK;Lo;0;L;;;;;N;;;;; +11C6;HANGUL JONGSEONG NIEUN-TIKEUT;Lo;0;L;;;;;N;;;;; +11C7;HANGUL JONGSEONG NIEUN-SIOS;Lo;0;L;;;;;N;;;;; +11C8;HANGUL JONGSEONG NIEUN-PANSIOS;Lo;0;L;;;;;N;;;;; +11C9;HANGUL JONGSEONG NIEUN-THIEUTH;Lo;0;L;;;;;N;;;;; +11CA;HANGUL JONGSEONG TIKEUT-KIYEOK;Lo;0;L;;;;;N;;;;; +11CB;HANGUL JONGSEONG TIKEUT-RIEUL;Lo;0;L;;;;;N;;;;; +11CC;HANGUL JONGSEONG RIEUL-KIYEOK-SIOS;Lo;0;L;;;;;N;;;;; +11CD;HANGUL JONGSEONG RIEUL-NIEUN;Lo;0;L;;;;;N;;;;; +11CE;HANGUL JONGSEONG RIEUL-TIKEUT;Lo;0;L;;;;;N;;;;; +11CF;HANGUL JONGSEONG RIEUL-TIKEUT-HIEUH;Lo;0;L;;;;;N;;;;; +11D0;HANGUL JONGSEONG SSANGRIEUL;Lo;0;L;;;;;N;;;;; +11D1;HANGUL JONGSEONG RIEUL-MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;; +11D2;HANGUL JONGSEONG RIEUL-MIEUM-SIOS;Lo;0;L;;;;;N;;;;; +11D3;HANGUL JONGSEONG RIEUL-PIEUP-SIOS;Lo;0;L;;;;;N;;;;; +11D4;HANGUL JONGSEONG RIEUL-PIEUP-HIEUH;Lo;0;L;;;;;N;;;;; +11D5;HANGUL JONGSEONG RIEUL-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; +11D6;HANGUL JONGSEONG RIEUL-SSANGSIOS;Lo;0;L;;;;;N;;;;; +11D7;HANGUL JONGSEONG RIEUL-PANSIOS;Lo;0;L;;;;;N;;;;; +11D8;HANGUL JONGSEONG RIEUL-KHIEUKH;Lo;0;L;;;;;N;;;;; +11D9;HANGUL JONGSEONG RIEUL-YEORINHIEUH;Lo;0;L;;;;;N;;;;; +11DA;HANGUL JONGSEONG MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;; +11DB;HANGUL JONGSEONG MIEUM-RIEUL;Lo;0;L;;;;;N;;;;; +11DC;HANGUL JONGSEONG MIEUM-PIEUP;Lo;0;L;;;;;N;;;;; +11DD;HANGUL JONGSEONG MIEUM-SIOS;Lo;0;L;;;;;N;;;;; +11DE;HANGUL JONGSEONG MIEUM-SSANGSIOS;Lo;0;L;;;;;N;;;;; +11DF;HANGUL JONGSEONG MIEUM-PANSIOS;Lo;0;L;;;;;N;;;;; +11E0;HANGUL JONGSEONG MIEUM-CHIEUCH;Lo;0;L;;;;;N;;;;; +11E1;HANGUL JONGSEONG MIEUM-HIEUH;Lo;0;L;;;;;N;;;;; +11E2;HANGUL JONGSEONG KAPYEOUNMIEUM;Lo;0;L;;;;;N;;;;; +11E3;HANGUL JONGSEONG PIEUP-RIEUL;Lo;0;L;;;;;N;;;;; +11E4;HANGUL JONGSEONG PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;; +11E5;HANGUL JONGSEONG PIEUP-HIEUH;Lo;0;L;;;;;N;;;;; +11E6;HANGUL JONGSEONG KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; +11E7;HANGUL JONGSEONG SIOS-KIYEOK;Lo;0;L;;;;;N;;;;; +11E8;HANGUL JONGSEONG SIOS-TIKEUT;Lo;0;L;;;;;N;;;;; +11E9;HANGUL JONGSEONG SIOS-RIEUL;Lo;0;L;;;;;N;;;;; +11EA;HANGUL JONGSEONG SIOS-PIEUP;Lo;0;L;;;;;N;;;;; +11EB;HANGUL JONGSEONG PANSIOS;Lo;0;L;;;;;N;;;;; +11EC;HANGUL JONGSEONG IEUNG-KIYEOK;Lo;0;L;;;;;N;;;;; +11ED;HANGUL JONGSEONG IEUNG-SSANGKIYEOK;Lo;0;L;;;;;N;;;;; +11EE;HANGUL JONGSEONG SSANGIEUNG;Lo;0;L;;;;;N;;;;; +11EF;HANGUL JONGSEONG IEUNG-KHIEUKH;Lo;0;L;;;;;N;;;;; +11F0;HANGUL JONGSEONG YESIEUNG;Lo;0;L;;;;;N;;;;; +11F1;HANGUL JONGSEONG YESIEUNG-SIOS;Lo;0;L;;;;;N;;;;; +11F2;HANGUL JONGSEONG YESIEUNG-PANSIOS;Lo;0;L;;;;;N;;;;; +11F3;HANGUL JONGSEONG PHIEUPH-PIEUP;Lo;0;L;;;;;N;;;;; +11F4;HANGUL JONGSEONG KAPYEOUNPHIEUPH;Lo;0;L;;;;;N;;;;; +11F5;HANGUL JONGSEONG HIEUH-NIEUN;Lo;0;L;;;;;N;;;;; +11F6;HANGUL JONGSEONG HIEUH-RIEUL;Lo;0;L;;;;;N;;;;; +11F7;HANGUL JONGSEONG HIEUH-MIEUM;Lo;0;L;;;;;N;;;;; +11F8;HANGUL JONGSEONG HIEUH-PIEUP;Lo;0;L;;;;;N;;;;; +11F9;HANGUL JONGSEONG YEORINHIEUH;Lo;0;L;;;;;N;;;;; +11FA;HANGUL JONGSEONG KIYEOK-NIEUN;Lo;0;L;;;;;N;;;;; +11FB;HANGUL JONGSEONG KIYEOK-PIEUP;Lo;0;L;;;;;N;;;;; +11FC;HANGUL JONGSEONG KIYEOK-CHIEUCH;Lo;0;L;;;;;N;;;;; +11FD;HANGUL JONGSEONG KIYEOK-KHIEUKH;Lo;0;L;;;;;N;;;;; +11FE;HANGUL JONGSEONG KIYEOK-HIEUH;Lo;0;L;;;;;N;;;;; +11FF;HANGUL JONGSEONG SSANGNIEUN;Lo;0;L;;;;;N;;;;; +1200;ETHIOPIC SYLLABLE HA;Lo;0;L;;;;;N;;;;; +1201;ETHIOPIC SYLLABLE HU;Lo;0;L;;;;;N;;;;; +1202;ETHIOPIC SYLLABLE HI;Lo;0;L;;;;;N;;;;; +1203;ETHIOPIC SYLLABLE HAA;Lo;0;L;;;;;N;;;;; +1204;ETHIOPIC SYLLABLE HEE;Lo;0;L;;;;;N;;;;; +1205;ETHIOPIC SYLLABLE HE;Lo;0;L;;;;;N;;;;; +1206;ETHIOPIC SYLLABLE HO;Lo;0;L;;;;;N;;;;; +1207;ETHIOPIC SYLLABLE HOA;Lo;0;L;;;;;N;;;;; +1208;ETHIOPIC SYLLABLE LA;Lo;0;L;;;;;N;;;;; +1209;ETHIOPIC SYLLABLE LU;Lo;0;L;;;;;N;;;;; +120A;ETHIOPIC SYLLABLE LI;Lo;0;L;;;;;N;;;;; +120B;ETHIOPIC SYLLABLE LAA;Lo;0;L;;;;;N;;;;; +120C;ETHIOPIC SYLLABLE LEE;Lo;0;L;;;;;N;;;;; +120D;ETHIOPIC SYLLABLE LE;Lo;0;L;;;;;N;;;;; +120E;ETHIOPIC SYLLABLE LO;Lo;0;L;;;;;N;;;;; +120F;ETHIOPIC SYLLABLE LWA;Lo;0;L;;;;;N;;;;; +1210;ETHIOPIC SYLLABLE HHA;Lo;0;L;;;;;N;;;;; +1211;ETHIOPIC SYLLABLE HHU;Lo;0;L;;;;;N;;;;; +1212;ETHIOPIC SYLLABLE HHI;Lo;0;L;;;;;N;;;;; +1213;ETHIOPIC SYLLABLE HHAA;Lo;0;L;;;;;N;;;;; +1214;ETHIOPIC SYLLABLE HHEE;Lo;0;L;;;;;N;;;;; +1215;ETHIOPIC SYLLABLE HHE;Lo;0;L;;;;;N;;;;; +1216;ETHIOPIC SYLLABLE HHO;Lo;0;L;;;;;N;;;;; +1217;ETHIOPIC SYLLABLE HHWA;Lo;0;L;;;;;N;;;;; +1218;ETHIOPIC SYLLABLE MA;Lo;0;L;;;;;N;;;;; +1219;ETHIOPIC SYLLABLE MU;Lo;0;L;;;;;N;;;;; +121A;ETHIOPIC SYLLABLE MI;Lo;0;L;;;;;N;;;;; +121B;ETHIOPIC SYLLABLE MAA;Lo;0;L;;;;;N;;;;; +121C;ETHIOPIC SYLLABLE MEE;Lo;0;L;;;;;N;;;;; +121D;ETHIOPIC SYLLABLE ME;Lo;0;L;;;;;N;;;;; +121E;ETHIOPIC SYLLABLE MO;Lo;0;L;;;;;N;;;;; +121F;ETHIOPIC SYLLABLE MWA;Lo;0;L;;;;;N;;;;; +1220;ETHIOPIC SYLLABLE SZA;Lo;0;L;;;;;N;;;;; +1221;ETHIOPIC SYLLABLE SZU;Lo;0;L;;;;;N;;;;; +1222;ETHIOPIC SYLLABLE SZI;Lo;0;L;;;;;N;;;;; +1223;ETHIOPIC SYLLABLE SZAA;Lo;0;L;;;;;N;;;;; +1224;ETHIOPIC SYLLABLE SZEE;Lo;0;L;;;;;N;;;;; +1225;ETHIOPIC SYLLABLE SZE;Lo;0;L;;;;;N;;;;; +1226;ETHIOPIC SYLLABLE SZO;Lo;0;L;;;;;N;;;;; +1227;ETHIOPIC SYLLABLE SZWA;Lo;0;L;;;;;N;;;;; +1228;ETHIOPIC SYLLABLE RA;Lo;0;L;;;;;N;;;;; +1229;ETHIOPIC SYLLABLE RU;Lo;0;L;;;;;N;;;;; +122A;ETHIOPIC SYLLABLE RI;Lo;0;L;;;;;N;;;;; +122B;ETHIOPIC SYLLABLE RAA;Lo;0;L;;;;;N;;;;; +122C;ETHIOPIC SYLLABLE REE;Lo;0;L;;;;;N;;;;; +122D;ETHIOPIC SYLLABLE RE;Lo;0;L;;;;;N;;;;; +122E;ETHIOPIC SYLLABLE RO;Lo;0;L;;;;;N;;;;; +122F;ETHIOPIC SYLLABLE RWA;Lo;0;L;;;;;N;;;;; +1230;ETHIOPIC SYLLABLE SA;Lo;0;L;;;;;N;;;;; +1231;ETHIOPIC SYLLABLE SU;Lo;0;L;;;;;N;;;;; +1232;ETHIOPIC SYLLABLE SI;Lo;0;L;;;;;N;;;;; +1233;ETHIOPIC SYLLABLE SAA;Lo;0;L;;;;;N;;;;; +1234;ETHIOPIC SYLLABLE SEE;Lo;0;L;;;;;N;;;;; +1235;ETHIOPIC SYLLABLE SE;Lo;0;L;;;;;N;;;;; +1236;ETHIOPIC SYLLABLE SO;Lo;0;L;;;;;N;;;;; +1237;ETHIOPIC SYLLABLE SWA;Lo;0;L;;;;;N;;;;; +1238;ETHIOPIC SYLLABLE SHA;Lo;0;L;;;;;N;;;;; +1239;ETHIOPIC SYLLABLE SHU;Lo;0;L;;;;;N;;;;; +123A;ETHIOPIC SYLLABLE SHI;Lo;0;L;;;;;N;;;;; +123B;ETHIOPIC SYLLABLE SHAA;Lo;0;L;;;;;N;;;;; +123C;ETHIOPIC SYLLABLE SHEE;Lo;0;L;;;;;N;;;;; +123D;ETHIOPIC SYLLABLE SHE;Lo;0;L;;;;;N;;;;; +123E;ETHIOPIC SYLLABLE SHO;Lo;0;L;;;;;N;;;;; +123F;ETHIOPIC SYLLABLE SHWA;Lo;0;L;;;;;N;;;;; +1240;ETHIOPIC SYLLABLE QA;Lo;0;L;;;;;N;;;;; +1241;ETHIOPIC SYLLABLE QU;Lo;0;L;;;;;N;;;;; +1242;ETHIOPIC SYLLABLE QI;Lo;0;L;;;;;N;;;;; +1243;ETHIOPIC SYLLABLE QAA;Lo;0;L;;;;;N;;;;; +1244;ETHIOPIC SYLLABLE QEE;Lo;0;L;;;;;N;;;;; +1245;ETHIOPIC SYLLABLE QE;Lo;0;L;;;;;N;;;;; +1246;ETHIOPIC SYLLABLE QO;Lo;0;L;;;;;N;;;;; +1247;ETHIOPIC SYLLABLE QOA;Lo;0;L;;;;;N;;;;; +1248;ETHIOPIC SYLLABLE QWA;Lo;0;L;;;;;N;;;;; +124A;ETHIOPIC SYLLABLE QWI;Lo;0;L;;;;;N;;;;; +124B;ETHIOPIC SYLLABLE QWAA;Lo;0;L;;;;;N;;;;; +124C;ETHIOPIC SYLLABLE QWEE;Lo;0;L;;;;;N;;;;; +124D;ETHIOPIC SYLLABLE QWE;Lo;0;L;;;;;N;;;;; +1250;ETHIOPIC SYLLABLE QHA;Lo;0;L;;;;;N;;;;; +1251;ETHIOPIC SYLLABLE QHU;Lo;0;L;;;;;N;;;;; +1252;ETHIOPIC SYLLABLE QHI;Lo;0;L;;;;;N;;;;; +1253;ETHIOPIC SYLLABLE QHAA;Lo;0;L;;;;;N;;;;; +1254;ETHIOPIC SYLLABLE QHEE;Lo;0;L;;;;;N;;;;; +1255;ETHIOPIC SYLLABLE QHE;Lo;0;L;;;;;N;;;;; +1256;ETHIOPIC SYLLABLE QHO;Lo;0;L;;;;;N;;;;; +1258;ETHIOPIC SYLLABLE QHWA;Lo;0;L;;;;;N;;;;; +125A;ETHIOPIC SYLLABLE QHWI;Lo;0;L;;;;;N;;;;; +125B;ETHIOPIC SYLLABLE QHWAA;Lo;0;L;;;;;N;;;;; +125C;ETHIOPIC SYLLABLE QHWEE;Lo;0;L;;;;;N;;;;; +125D;ETHIOPIC SYLLABLE QHWE;Lo;0;L;;;;;N;;;;; +1260;ETHIOPIC SYLLABLE BA;Lo;0;L;;;;;N;;;;; +1261;ETHIOPIC SYLLABLE BU;Lo;0;L;;;;;N;;;;; +1262;ETHIOPIC SYLLABLE BI;Lo;0;L;;;;;N;;;;; +1263;ETHIOPIC SYLLABLE BAA;Lo;0;L;;;;;N;;;;; +1264;ETHIOPIC SYLLABLE BEE;Lo;0;L;;;;;N;;;;; +1265;ETHIOPIC SYLLABLE BE;Lo;0;L;;;;;N;;;;; +1266;ETHIOPIC SYLLABLE BO;Lo;0;L;;;;;N;;;;; +1267;ETHIOPIC SYLLABLE BWA;Lo;0;L;;;;;N;;;;; +1268;ETHIOPIC SYLLABLE VA;Lo;0;L;;;;;N;;;;; +1269;ETHIOPIC SYLLABLE VU;Lo;0;L;;;;;N;;;;; +126A;ETHIOPIC SYLLABLE VI;Lo;0;L;;;;;N;;;;; +126B;ETHIOPIC SYLLABLE VAA;Lo;0;L;;;;;N;;;;; +126C;ETHIOPIC SYLLABLE VEE;Lo;0;L;;;;;N;;;;; +126D;ETHIOPIC SYLLABLE VE;Lo;0;L;;;;;N;;;;; +126E;ETHIOPIC SYLLABLE VO;Lo;0;L;;;;;N;;;;; +126F;ETHIOPIC SYLLABLE VWA;Lo;0;L;;;;;N;;;;; +1270;ETHIOPIC SYLLABLE TA;Lo;0;L;;;;;N;;;;; +1271;ETHIOPIC SYLLABLE TU;Lo;0;L;;;;;N;;;;; +1272;ETHIOPIC SYLLABLE TI;Lo;0;L;;;;;N;;;;; +1273;ETHIOPIC SYLLABLE TAA;Lo;0;L;;;;;N;;;;; +1274;ETHIOPIC SYLLABLE TEE;Lo;0;L;;;;;N;;;;; +1275;ETHIOPIC SYLLABLE TE;Lo;0;L;;;;;N;;;;; +1276;ETHIOPIC SYLLABLE TO;Lo;0;L;;;;;N;;;;; +1277;ETHIOPIC SYLLABLE TWA;Lo;0;L;;;;;N;;;;; +1278;ETHIOPIC SYLLABLE CA;Lo;0;L;;;;;N;;;;; +1279;ETHIOPIC SYLLABLE CU;Lo;0;L;;;;;N;;;;; +127A;ETHIOPIC SYLLABLE CI;Lo;0;L;;;;;N;;;;; +127B;ETHIOPIC SYLLABLE CAA;Lo;0;L;;;;;N;;;;; +127C;ETHIOPIC SYLLABLE CEE;Lo;0;L;;;;;N;;;;; +127D;ETHIOPIC SYLLABLE CE;Lo;0;L;;;;;N;;;;; +127E;ETHIOPIC SYLLABLE CO;Lo;0;L;;;;;N;;;;; +127F;ETHIOPIC SYLLABLE CWA;Lo;0;L;;;;;N;;;;; +1280;ETHIOPIC SYLLABLE XA;Lo;0;L;;;;;N;;;;; +1281;ETHIOPIC SYLLABLE XU;Lo;0;L;;;;;N;;;;; +1282;ETHIOPIC SYLLABLE XI;Lo;0;L;;;;;N;;;;; +1283;ETHIOPIC SYLLABLE XAA;Lo;0;L;;;;;N;;;;; +1284;ETHIOPIC SYLLABLE XEE;Lo;0;L;;;;;N;;;;; +1285;ETHIOPIC SYLLABLE XE;Lo;0;L;;;;;N;;;;; +1286;ETHIOPIC SYLLABLE XO;Lo;0;L;;;;;N;;;;; +1287;ETHIOPIC SYLLABLE XOA;Lo;0;L;;;;;N;;;;; +1288;ETHIOPIC SYLLABLE XWA;Lo;0;L;;;;;N;;;;; +128A;ETHIOPIC SYLLABLE XWI;Lo;0;L;;;;;N;;;;; +128B;ETHIOPIC SYLLABLE XWAA;Lo;0;L;;;;;N;;;;; +128C;ETHIOPIC SYLLABLE XWEE;Lo;0;L;;;;;N;;;;; +128D;ETHIOPIC SYLLABLE XWE;Lo;0;L;;;;;N;;;;; +1290;ETHIOPIC SYLLABLE NA;Lo;0;L;;;;;N;;;;; +1291;ETHIOPIC SYLLABLE NU;Lo;0;L;;;;;N;;;;; +1292;ETHIOPIC SYLLABLE NI;Lo;0;L;;;;;N;;;;; +1293;ETHIOPIC SYLLABLE NAA;Lo;0;L;;;;;N;;;;; +1294;ETHIOPIC SYLLABLE NEE;Lo;0;L;;;;;N;;;;; +1295;ETHIOPIC SYLLABLE NE;Lo;0;L;;;;;N;;;;; +1296;ETHIOPIC SYLLABLE NO;Lo;0;L;;;;;N;;;;; +1297;ETHIOPIC SYLLABLE NWA;Lo;0;L;;;;;N;;;;; +1298;ETHIOPIC SYLLABLE NYA;Lo;0;L;;;;;N;;;;; +1299;ETHIOPIC SYLLABLE NYU;Lo;0;L;;;;;N;;;;; +129A;ETHIOPIC SYLLABLE NYI;Lo;0;L;;;;;N;;;;; +129B;ETHIOPIC SYLLABLE NYAA;Lo;0;L;;;;;N;;;;; +129C;ETHIOPIC SYLLABLE NYEE;Lo;0;L;;;;;N;;;;; +129D;ETHIOPIC SYLLABLE NYE;Lo;0;L;;;;;N;;;;; +129E;ETHIOPIC SYLLABLE NYO;Lo;0;L;;;;;N;;;;; +129F;ETHIOPIC SYLLABLE NYWA;Lo;0;L;;;;;N;;;;; +12A0;ETHIOPIC SYLLABLE GLOTTAL A;Lo;0;L;;;;;N;;;;; +12A1;ETHIOPIC SYLLABLE GLOTTAL U;Lo;0;L;;;;;N;;;;; +12A2;ETHIOPIC SYLLABLE GLOTTAL I;Lo;0;L;;;;;N;;;;; +12A3;ETHIOPIC SYLLABLE GLOTTAL AA;Lo;0;L;;;;;N;;;;; +12A4;ETHIOPIC SYLLABLE GLOTTAL EE;Lo;0;L;;;;;N;;;;; +12A5;ETHIOPIC SYLLABLE GLOTTAL E;Lo;0;L;;;;;N;;;;; +12A6;ETHIOPIC SYLLABLE GLOTTAL O;Lo;0;L;;;;;N;;;;; +12A7;ETHIOPIC SYLLABLE GLOTTAL WA;Lo;0;L;;;;;N;;;;; +12A8;ETHIOPIC SYLLABLE KA;Lo;0;L;;;;;N;;;;; +12A9;ETHIOPIC SYLLABLE KU;Lo;0;L;;;;;N;;;;; +12AA;ETHIOPIC SYLLABLE KI;Lo;0;L;;;;;N;;;;; +12AB;ETHIOPIC SYLLABLE KAA;Lo;0;L;;;;;N;;;;; +12AC;ETHIOPIC SYLLABLE KEE;Lo;0;L;;;;;N;;;;; +12AD;ETHIOPIC SYLLABLE KE;Lo;0;L;;;;;N;;;;; +12AE;ETHIOPIC SYLLABLE KO;Lo;0;L;;;;;N;;;;; +12AF;ETHIOPIC SYLLABLE KOA;Lo;0;L;;;;;N;;;;; +12B0;ETHIOPIC SYLLABLE KWA;Lo;0;L;;;;;N;;;;; +12B2;ETHIOPIC SYLLABLE KWI;Lo;0;L;;;;;N;;;;; +12B3;ETHIOPIC SYLLABLE KWAA;Lo;0;L;;;;;N;;;;; +12B4;ETHIOPIC SYLLABLE KWEE;Lo;0;L;;;;;N;;;;; +12B5;ETHIOPIC SYLLABLE KWE;Lo;0;L;;;;;N;;;;; +12B8;ETHIOPIC SYLLABLE KXA;Lo;0;L;;;;;N;;;;; +12B9;ETHIOPIC SYLLABLE KXU;Lo;0;L;;;;;N;;;;; +12BA;ETHIOPIC SYLLABLE KXI;Lo;0;L;;;;;N;;;;; +12BB;ETHIOPIC SYLLABLE KXAA;Lo;0;L;;;;;N;;;;; +12BC;ETHIOPIC SYLLABLE KXEE;Lo;0;L;;;;;N;;;;; +12BD;ETHIOPIC SYLLABLE KXE;Lo;0;L;;;;;N;;;;; +12BE;ETHIOPIC SYLLABLE KXO;Lo;0;L;;;;;N;;;;; +12C0;ETHIOPIC SYLLABLE KXWA;Lo;0;L;;;;;N;;;;; +12C2;ETHIOPIC SYLLABLE KXWI;Lo;0;L;;;;;N;;;;; +12C3;ETHIOPIC SYLLABLE KXWAA;Lo;0;L;;;;;N;;;;; +12C4;ETHIOPIC SYLLABLE KXWEE;Lo;0;L;;;;;N;;;;; +12C5;ETHIOPIC SYLLABLE KXWE;Lo;0;L;;;;;N;;;;; +12C8;ETHIOPIC SYLLABLE WA;Lo;0;L;;;;;N;;;;; +12C9;ETHIOPIC SYLLABLE WU;Lo;0;L;;;;;N;;;;; +12CA;ETHIOPIC SYLLABLE WI;Lo;0;L;;;;;N;;;;; +12CB;ETHIOPIC SYLLABLE WAA;Lo;0;L;;;;;N;;;;; +12CC;ETHIOPIC SYLLABLE WEE;Lo;0;L;;;;;N;;;;; +12CD;ETHIOPIC SYLLABLE WE;Lo;0;L;;;;;N;;;;; +12CE;ETHIOPIC SYLLABLE WO;Lo;0;L;;;;;N;;;;; +12CF;ETHIOPIC SYLLABLE WOA;Lo;0;L;;;;;N;;;;; +12D0;ETHIOPIC SYLLABLE PHARYNGEAL A;Lo;0;L;;;;;N;;;;; +12D1;ETHIOPIC SYLLABLE PHARYNGEAL U;Lo;0;L;;;;;N;;;;; +12D2;ETHIOPIC SYLLABLE PHARYNGEAL I;Lo;0;L;;;;;N;;;;; +12D3;ETHIOPIC SYLLABLE PHARYNGEAL AA;Lo;0;L;;;;;N;;;;; +12D4;ETHIOPIC SYLLABLE PHARYNGEAL EE;Lo;0;L;;;;;N;;;;; +12D5;ETHIOPIC SYLLABLE PHARYNGEAL E;Lo;0;L;;;;;N;;;;; +12D6;ETHIOPIC SYLLABLE PHARYNGEAL O;Lo;0;L;;;;;N;;;;; +12D8;ETHIOPIC SYLLABLE ZA;Lo;0;L;;;;;N;;;;; +12D9;ETHIOPIC SYLLABLE ZU;Lo;0;L;;;;;N;;;;; +12DA;ETHIOPIC SYLLABLE ZI;Lo;0;L;;;;;N;;;;; +12DB;ETHIOPIC SYLLABLE ZAA;Lo;0;L;;;;;N;;;;; +12DC;ETHIOPIC SYLLABLE ZEE;Lo;0;L;;;;;N;;;;; +12DD;ETHIOPIC SYLLABLE ZE;Lo;0;L;;;;;N;;;;; +12DE;ETHIOPIC SYLLABLE ZO;Lo;0;L;;;;;N;;;;; +12DF;ETHIOPIC SYLLABLE ZWA;Lo;0;L;;;;;N;;;;; +12E0;ETHIOPIC SYLLABLE ZHA;Lo;0;L;;;;;N;;;;; +12E1;ETHIOPIC SYLLABLE ZHU;Lo;0;L;;;;;N;;;;; +12E2;ETHIOPIC SYLLABLE ZHI;Lo;0;L;;;;;N;;;;; +12E3;ETHIOPIC SYLLABLE ZHAA;Lo;0;L;;;;;N;;;;; +12E4;ETHIOPIC SYLLABLE ZHEE;Lo;0;L;;;;;N;;;;; +12E5;ETHIOPIC SYLLABLE ZHE;Lo;0;L;;;;;N;;;;; +12E6;ETHIOPIC SYLLABLE ZHO;Lo;0;L;;;;;N;;;;; +12E7;ETHIOPIC SYLLABLE ZHWA;Lo;0;L;;;;;N;;;;; +12E8;ETHIOPIC SYLLABLE YA;Lo;0;L;;;;;N;;;;; +12E9;ETHIOPIC SYLLABLE YU;Lo;0;L;;;;;N;;;;; +12EA;ETHIOPIC SYLLABLE YI;Lo;0;L;;;;;N;;;;; +12EB;ETHIOPIC SYLLABLE YAA;Lo;0;L;;;;;N;;;;; +12EC;ETHIOPIC SYLLABLE YEE;Lo;0;L;;;;;N;;;;; +12ED;ETHIOPIC SYLLABLE YE;Lo;0;L;;;;;N;;;;; +12EE;ETHIOPIC SYLLABLE YO;Lo;0;L;;;;;N;;;;; +12EF;ETHIOPIC SYLLABLE YOA;Lo;0;L;;;;;N;;;;; +12F0;ETHIOPIC SYLLABLE DA;Lo;0;L;;;;;N;;;;; +12F1;ETHIOPIC SYLLABLE DU;Lo;0;L;;;;;N;;;;; +12F2;ETHIOPIC SYLLABLE DI;Lo;0;L;;;;;N;;;;; +12F3;ETHIOPIC SYLLABLE DAA;Lo;0;L;;;;;N;;;;; +12F4;ETHIOPIC SYLLABLE DEE;Lo;0;L;;;;;N;;;;; +12F5;ETHIOPIC SYLLABLE DE;Lo;0;L;;;;;N;;;;; +12F6;ETHIOPIC SYLLABLE DO;Lo;0;L;;;;;N;;;;; +12F7;ETHIOPIC SYLLABLE DWA;Lo;0;L;;;;;N;;;;; +12F8;ETHIOPIC SYLLABLE DDA;Lo;0;L;;;;;N;;;;; +12F9;ETHIOPIC SYLLABLE DDU;Lo;0;L;;;;;N;;;;; +12FA;ETHIOPIC SYLLABLE DDI;Lo;0;L;;;;;N;;;;; +12FB;ETHIOPIC SYLLABLE DDAA;Lo;0;L;;;;;N;;;;; +12FC;ETHIOPIC SYLLABLE DDEE;Lo;0;L;;;;;N;;;;; +12FD;ETHIOPIC SYLLABLE DDE;Lo;0;L;;;;;N;;;;; +12FE;ETHIOPIC SYLLABLE DDO;Lo;0;L;;;;;N;;;;; +12FF;ETHIOPIC SYLLABLE DDWA;Lo;0;L;;;;;N;;;;; +1300;ETHIOPIC SYLLABLE JA;Lo;0;L;;;;;N;;;;; +1301;ETHIOPIC SYLLABLE JU;Lo;0;L;;;;;N;;;;; +1302;ETHIOPIC SYLLABLE JI;Lo;0;L;;;;;N;;;;; +1303;ETHIOPIC SYLLABLE JAA;Lo;0;L;;;;;N;;;;; +1304;ETHIOPIC SYLLABLE JEE;Lo;0;L;;;;;N;;;;; +1305;ETHIOPIC SYLLABLE JE;Lo;0;L;;;;;N;;;;; +1306;ETHIOPIC SYLLABLE JO;Lo;0;L;;;;;N;;;;; +1307;ETHIOPIC SYLLABLE JWA;Lo;0;L;;;;;N;;;;; +1308;ETHIOPIC SYLLABLE GA;Lo;0;L;;;;;N;;;;; +1309;ETHIOPIC SYLLABLE GU;Lo;0;L;;;;;N;;;;; +130A;ETHIOPIC SYLLABLE GI;Lo;0;L;;;;;N;;;;; +130B;ETHIOPIC SYLLABLE GAA;Lo;0;L;;;;;N;;;;; +130C;ETHIOPIC SYLLABLE GEE;Lo;0;L;;;;;N;;;;; +130D;ETHIOPIC SYLLABLE GE;Lo;0;L;;;;;N;;;;; +130E;ETHIOPIC SYLLABLE GO;Lo;0;L;;;;;N;;;;; +130F;ETHIOPIC SYLLABLE GOA;Lo;0;L;;;;;N;;;;; +1310;ETHIOPIC SYLLABLE GWA;Lo;0;L;;;;;N;;;;; +1312;ETHIOPIC SYLLABLE GWI;Lo;0;L;;;;;N;;;;; +1313;ETHIOPIC SYLLABLE GWAA;Lo;0;L;;;;;N;;;;; +1314;ETHIOPIC SYLLABLE GWEE;Lo;0;L;;;;;N;;;;; +1315;ETHIOPIC SYLLABLE GWE;Lo;0;L;;;;;N;;;;; +1318;ETHIOPIC SYLLABLE GGA;Lo;0;L;;;;;N;;;;; +1319;ETHIOPIC SYLLABLE GGU;Lo;0;L;;;;;N;;;;; +131A;ETHIOPIC SYLLABLE GGI;Lo;0;L;;;;;N;;;;; +131B;ETHIOPIC SYLLABLE GGAA;Lo;0;L;;;;;N;;;;; +131C;ETHIOPIC SYLLABLE GGEE;Lo;0;L;;;;;N;;;;; +131D;ETHIOPIC SYLLABLE GGE;Lo;0;L;;;;;N;;;;; +131E;ETHIOPIC SYLLABLE GGO;Lo;0;L;;;;;N;;;;; +131F;ETHIOPIC SYLLABLE GGWAA;Lo;0;L;;;;;N;;;;; +1320;ETHIOPIC SYLLABLE THA;Lo;0;L;;;;;N;;;;; +1321;ETHIOPIC SYLLABLE THU;Lo;0;L;;;;;N;;;;; +1322;ETHIOPIC SYLLABLE THI;Lo;0;L;;;;;N;;;;; +1323;ETHIOPIC SYLLABLE THAA;Lo;0;L;;;;;N;;;;; +1324;ETHIOPIC SYLLABLE THEE;Lo;0;L;;;;;N;;;;; +1325;ETHIOPIC SYLLABLE THE;Lo;0;L;;;;;N;;;;; +1326;ETHIOPIC SYLLABLE THO;Lo;0;L;;;;;N;;;;; +1327;ETHIOPIC SYLLABLE THWA;Lo;0;L;;;;;N;;;;; +1328;ETHIOPIC SYLLABLE CHA;Lo;0;L;;;;;N;;;;; +1329;ETHIOPIC SYLLABLE CHU;Lo;0;L;;;;;N;;;;; +132A;ETHIOPIC SYLLABLE CHI;Lo;0;L;;;;;N;;;;; +132B;ETHIOPIC SYLLABLE CHAA;Lo;0;L;;;;;N;;;;; +132C;ETHIOPIC SYLLABLE CHEE;Lo;0;L;;;;;N;;;;; +132D;ETHIOPIC SYLLABLE CHE;Lo;0;L;;;;;N;;;;; +132E;ETHIOPIC SYLLABLE CHO;Lo;0;L;;;;;N;;;;; +132F;ETHIOPIC SYLLABLE CHWA;Lo;0;L;;;;;N;;;;; +1330;ETHIOPIC SYLLABLE PHA;Lo;0;L;;;;;N;;;;; +1331;ETHIOPIC SYLLABLE PHU;Lo;0;L;;;;;N;;;;; +1332;ETHIOPIC SYLLABLE PHI;Lo;0;L;;;;;N;;;;; +1333;ETHIOPIC SYLLABLE PHAA;Lo;0;L;;;;;N;;;;; +1334;ETHIOPIC SYLLABLE PHEE;Lo;0;L;;;;;N;;;;; +1335;ETHIOPIC SYLLABLE PHE;Lo;0;L;;;;;N;;;;; +1336;ETHIOPIC SYLLABLE PHO;Lo;0;L;;;;;N;;;;; +1337;ETHIOPIC SYLLABLE PHWA;Lo;0;L;;;;;N;;;;; +1338;ETHIOPIC SYLLABLE TSA;Lo;0;L;;;;;N;;;;; +1339;ETHIOPIC SYLLABLE TSU;Lo;0;L;;;;;N;;;;; +133A;ETHIOPIC SYLLABLE TSI;Lo;0;L;;;;;N;;;;; +133B;ETHIOPIC SYLLABLE TSAA;Lo;0;L;;;;;N;;;;; +133C;ETHIOPIC SYLLABLE TSEE;Lo;0;L;;;;;N;;;;; +133D;ETHIOPIC SYLLABLE TSE;Lo;0;L;;;;;N;;;;; +133E;ETHIOPIC SYLLABLE TSO;Lo;0;L;;;;;N;;;;; +133F;ETHIOPIC SYLLABLE TSWA;Lo;0;L;;;;;N;;;;; +1340;ETHIOPIC SYLLABLE TZA;Lo;0;L;;;;;N;;;;; +1341;ETHIOPIC SYLLABLE TZU;Lo;0;L;;;;;N;;;;; +1342;ETHIOPIC SYLLABLE TZI;Lo;0;L;;;;;N;;;;; +1343;ETHIOPIC SYLLABLE TZAA;Lo;0;L;;;;;N;;;;; +1344;ETHIOPIC SYLLABLE TZEE;Lo;0;L;;;;;N;;;;; +1345;ETHIOPIC SYLLABLE TZE;Lo;0;L;;;;;N;;;;; +1346;ETHIOPIC SYLLABLE TZO;Lo;0;L;;;;;N;;;;; +1347;ETHIOPIC SYLLABLE TZOA;Lo;0;L;;;;;N;;;;; +1348;ETHIOPIC SYLLABLE FA;Lo;0;L;;;;;N;;;;; +1349;ETHIOPIC SYLLABLE FU;Lo;0;L;;;;;N;;;;; +134A;ETHIOPIC SYLLABLE FI;Lo;0;L;;;;;N;;;;; +134B;ETHIOPIC SYLLABLE FAA;Lo;0;L;;;;;N;;;;; +134C;ETHIOPIC SYLLABLE FEE;Lo;0;L;;;;;N;;;;; +134D;ETHIOPIC SYLLABLE FE;Lo;0;L;;;;;N;;;;; +134E;ETHIOPIC SYLLABLE FO;Lo;0;L;;;;;N;;;;; +134F;ETHIOPIC SYLLABLE FWA;Lo;0;L;;;;;N;;;;; +1350;ETHIOPIC SYLLABLE PA;Lo;0;L;;;;;N;;;;; +1351;ETHIOPIC SYLLABLE PU;Lo;0;L;;;;;N;;;;; +1352;ETHIOPIC SYLLABLE PI;Lo;0;L;;;;;N;;;;; +1353;ETHIOPIC SYLLABLE PAA;Lo;0;L;;;;;N;;;;; +1354;ETHIOPIC SYLLABLE PEE;Lo;0;L;;;;;N;;;;; +1355;ETHIOPIC SYLLABLE PE;Lo;0;L;;;;;N;;;;; +1356;ETHIOPIC SYLLABLE PO;Lo;0;L;;;;;N;;;;; +1357;ETHIOPIC SYLLABLE PWA;Lo;0;L;;;;;N;;;;; +1358;ETHIOPIC SYLLABLE RYA;Lo;0;L;;;;;N;;;;; +1359;ETHIOPIC SYLLABLE MYA;Lo;0;L;;;;;N;;;;; +135A;ETHIOPIC SYLLABLE FYA;Lo;0;L;;;;;N;;;;; +135D;ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK;Mn;230;NSM;;;;;N;;;;; +135E;ETHIOPIC COMBINING VOWEL LENGTH MARK;Mn;230;NSM;;;;;N;;;;; +135F;ETHIOPIC COMBINING GEMINATION MARK;Mn;230;NSM;;;;;N;;;;; +1360;ETHIOPIC SECTION MARK;Po;0;L;;;;;N;;;;; +1361;ETHIOPIC WORDSPACE;Po;0;L;;;;;N;;;;; +1362;ETHIOPIC FULL STOP;Po;0;L;;;;;N;;;;; +1363;ETHIOPIC COMMA;Po;0;L;;;;;N;;;;; +1364;ETHIOPIC SEMICOLON;Po;0;L;;;;;N;;;;; +1365;ETHIOPIC COLON;Po;0;L;;;;;N;;;;; +1366;ETHIOPIC PREFACE COLON;Po;0;L;;;;;N;;;;; +1367;ETHIOPIC QUESTION MARK;Po;0;L;;;;;N;;;;; +1368;ETHIOPIC PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;; +1369;ETHIOPIC DIGIT ONE;No;0;L;;;1;1;N;;;;; +136A;ETHIOPIC DIGIT TWO;No;0;L;;;2;2;N;;;;; +136B;ETHIOPIC DIGIT THREE;No;0;L;;;3;3;N;;;;; +136C;ETHIOPIC DIGIT FOUR;No;0;L;;;4;4;N;;;;; +136D;ETHIOPIC DIGIT FIVE;No;0;L;;;5;5;N;;;;; +136E;ETHIOPIC DIGIT SIX;No;0;L;;;6;6;N;;;;; +136F;ETHIOPIC DIGIT SEVEN;No;0;L;;;7;7;N;;;;; +1370;ETHIOPIC DIGIT EIGHT;No;0;L;;;8;8;N;;;;; +1371;ETHIOPIC DIGIT NINE;No;0;L;;;9;9;N;;;;; +1372;ETHIOPIC NUMBER TEN;No;0;L;;;;10;N;;;;; +1373;ETHIOPIC NUMBER TWENTY;No;0;L;;;;20;N;;;;; +1374;ETHIOPIC NUMBER THIRTY;No;0;L;;;;30;N;;;;; +1375;ETHIOPIC NUMBER FORTY;No;0;L;;;;40;N;;;;; +1376;ETHIOPIC NUMBER FIFTY;No;0;L;;;;50;N;;;;; +1377;ETHIOPIC NUMBER SIXTY;No;0;L;;;;60;N;;;;; +1378;ETHIOPIC NUMBER SEVENTY;No;0;L;;;;70;N;;;;; +1379;ETHIOPIC NUMBER EIGHTY;No;0;L;;;;80;N;;;;; +137A;ETHIOPIC NUMBER NINETY;No;0;L;;;;90;N;;;;; +137B;ETHIOPIC NUMBER HUNDRED;No;0;L;;;;100;N;;;;; +137C;ETHIOPIC NUMBER TEN THOUSAND;No;0;L;;;;10000;N;;;;; +1380;ETHIOPIC SYLLABLE SEBATBEIT MWA;Lo;0;L;;;;;N;;;;; +1381;ETHIOPIC SYLLABLE MWI;Lo;0;L;;;;;N;;;;; +1382;ETHIOPIC SYLLABLE MWEE;Lo;0;L;;;;;N;;;;; +1383;ETHIOPIC SYLLABLE MWE;Lo;0;L;;;;;N;;;;; +1384;ETHIOPIC SYLLABLE SEBATBEIT BWA;Lo;0;L;;;;;N;;;;; +1385;ETHIOPIC SYLLABLE BWI;Lo;0;L;;;;;N;;;;; +1386;ETHIOPIC SYLLABLE BWEE;Lo;0;L;;;;;N;;;;; +1387;ETHIOPIC SYLLABLE BWE;Lo;0;L;;;;;N;;;;; +1388;ETHIOPIC SYLLABLE SEBATBEIT FWA;Lo;0;L;;;;;N;;;;; +1389;ETHIOPIC SYLLABLE FWI;Lo;0;L;;;;;N;;;;; +138A;ETHIOPIC SYLLABLE FWEE;Lo;0;L;;;;;N;;;;; +138B;ETHIOPIC SYLLABLE FWE;Lo;0;L;;;;;N;;;;; +138C;ETHIOPIC SYLLABLE SEBATBEIT PWA;Lo;0;L;;;;;N;;;;; +138D;ETHIOPIC SYLLABLE PWI;Lo;0;L;;;;;N;;;;; +138E;ETHIOPIC SYLLABLE PWEE;Lo;0;L;;;;;N;;;;; +138F;ETHIOPIC SYLLABLE PWE;Lo;0;L;;;;;N;;;;; +1390;ETHIOPIC TONAL MARK YIZET;So;0;ON;;;;;N;;;;; +1391;ETHIOPIC TONAL MARK DERET;So;0;ON;;;;;N;;;;; +1392;ETHIOPIC TONAL MARK RIKRIK;So;0;ON;;;;;N;;;;; +1393;ETHIOPIC TONAL MARK SHORT RIKRIK;So;0;ON;;;;;N;;;;; +1394;ETHIOPIC TONAL MARK DIFAT;So;0;ON;;;;;N;;;;; +1395;ETHIOPIC TONAL MARK KENAT;So;0;ON;;;;;N;;;;; +1396;ETHIOPIC TONAL MARK CHIRET;So;0;ON;;;;;N;;;;; +1397;ETHIOPIC TONAL MARK HIDET;So;0;ON;;;;;N;;;;; +1398;ETHIOPIC TONAL MARK DERET-HIDET;So;0;ON;;;;;N;;;;; +1399;ETHIOPIC TONAL MARK KURT;So;0;ON;;;;;N;;;;; +13A0;CHEROKEE LETTER A;Lu;0;L;;;;;N;;;;AB70; +13A1;CHEROKEE LETTER E;Lu;0;L;;;;;N;;;;AB71; +13A2;CHEROKEE LETTER I;Lu;0;L;;;;;N;;;;AB72; +13A3;CHEROKEE LETTER O;Lu;0;L;;;;;N;;;;AB73; +13A4;CHEROKEE LETTER U;Lu;0;L;;;;;N;;;;AB74; +13A5;CHEROKEE LETTER V;Lu;0;L;;;;;N;;;;AB75; +13A6;CHEROKEE LETTER GA;Lu;0;L;;;;;N;;;;AB76; +13A7;CHEROKEE LETTER KA;Lu;0;L;;;;;N;;;;AB77; +13A8;CHEROKEE LETTER GE;Lu;0;L;;;;;N;;;;AB78; +13A9;CHEROKEE LETTER GI;Lu;0;L;;;;;N;;;;AB79; +13AA;CHEROKEE LETTER GO;Lu;0;L;;;;;N;;;;AB7A; +13AB;CHEROKEE LETTER GU;Lu;0;L;;;;;N;;;;AB7B; +13AC;CHEROKEE LETTER GV;Lu;0;L;;;;;N;;;;AB7C; +13AD;CHEROKEE LETTER HA;Lu;0;L;;;;;N;;;;AB7D; +13AE;CHEROKEE LETTER HE;Lu;0;L;;;;;N;;;;AB7E; +13AF;CHEROKEE LETTER HI;Lu;0;L;;;;;N;;;;AB7F; +13B0;CHEROKEE LETTER HO;Lu;0;L;;;;;N;;;;AB80; +13B1;CHEROKEE LETTER HU;Lu;0;L;;;;;N;;;;AB81; +13B2;CHEROKEE LETTER HV;Lu;0;L;;;;;N;;;;AB82; +13B3;CHEROKEE LETTER LA;Lu;0;L;;;;;N;;;;AB83; +13B4;CHEROKEE LETTER LE;Lu;0;L;;;;;N;;;;AB84; +13B5;CHEROKEE LETTER LI;Lu;0;L;;;;;N;;;;AB85; +13B6;CHEROKEE LETTER LO;Lu;0;L;;;;;N;;;;AB86; +13B7;CHEROKEE LETTER LU;Lu;0;L;;;;;N;;;;AB87; +13B8;CHEROKEE LETTER LV;Lu;0;L;;;;;N;;;;AB88; +13B9;CHEROKEE LETTER MA;Lu;0;L;;;;;N;;;;AB89; +13BA;CHEROKEE LETTER ME;Lu;0;L;;;;;N;;;;AB8A; +13BB;CHEROKEE LETTER MI;Lu;0;L;;;;;N;;;;AB8B; +13BC;CHEROKEE LETTER MO;Lu;0;L;;;;;N;;;;AB8C; +13BD;CHEROKEE LETTER MU;Lu;0;L;;;;;N;;;;AB8D; +13BE;CHEROKEE LETTER NA;Lu;0;L;;;;;N;;;;AB8E; +13BF;CHEROKEE LETTER HNA;Lu;0;L;;;;;N;;;;AB8F; +13C0;CHEROKEE LETTER NAH;Lu;0;L;;;;;N;;;;AB90; +13C1;CHEROKEE LETTER NE;Lu;0;L;;;;;N;;;;AB91; +13C2;CHEROKEE LETTER NI;Lu;0;L;;;;;N;;;;AB92; +13C3;CHEROKEE LETTER NO;Lu;0;L;;;;;N;;;;AB93; +13C4;CHEROKEE LETTER NU;Lu;0;L;;;;;N;;;;AB94; +13C5;CHEROKEE LETTER NV;Lu;0;L;;;;;N;;;;AB95; +13C6;CHEROKEE LETTER QUA;Lu;0;L;;;;;N;;;;AB96; +13C7;CHEROKEE LETTER QUE;Lu;0;L;;;;;N;;;;AB97; +13C8;CHEROKEE LETTER QUI;Lu;0;L;;;;;N;;;;AB98; +13C9;CHEROKEE LETTER QUO;Lu;0;L;;;;;N;;;;AB99; +13CA;CHEROKEE LETTER QUU;Lu;0;L;;;;;N;;;;AB9A; +13CB;CHEROKEE LETTER QUV;Lu;0;L;;;;;N;;;;AB9B; +13CC;CHEROKEE LETTER SA;Lu;0;L;;;;;N;;;;AB9C; +13CD;CHEROKEE LETTER S;Lu;0;L;;;;;N;;;;AB9D; +13CE;CHEROKEE LETTER SE;Lu;0;L;;;;;N;;;;AB9E; +13CF;CHEROKEE LETTER SI;Lu;0;L;;;;;N;;;;AB9F; +13D0;CHEROKEE LETTER SO;Lu;0;L;;;;;N;;;;ABA0; +13D1;CHEROKEE LETTER SU;Lu;0;L;;;;;N;;;;ABA1; +13D2;CHEROKEE LETTER SV;Lu;0;L;;;;;N;;;;ABA2; +13D3;CHEROKEE LETTER DA;Lu;0;L;;;;;N;;;;ABA3; +13D4;CHEROKEE LETTER TA;Lu;0;L;;;;;N;;;;ABA4; +13D5;CHEROKEE LETTER DE;Lu;0;L;;;;;N;;;;ABA5; +13D6;CHEROKEE LETTER TE;Lu;0;L;;;;;N;;;;ABA6; +13D7;CHEROKEE LETTER DI;Lu;0;L;;;;;N;;;;ABA7; +13D8;CHEROKEE LETTER TI;Lu;0;L;;;;;N;;;;ABA8; +13D9;CHEROKEE LETTER DO;Lu;0;L;;;;;N;;;;ABA9; +13DA;CHEROKEE LETTER DU;Lu;0;L;;;;;N;;;;ABAA; +13DB;CHEROKEE LETTER DV;Lu;0;L;;;;;N;;;;ABAB; +13DC;CHEROKEE LETTER DLA;Lu;0;L;;;;;N;;;;ABAC; +13DD;CHEROKEE LETTER TLA;Lu;0;L;;;;;N;;;;ABAD; +13DE;CHEROKEE LETTER TLE;Lu;0;L;;;;;N;;;;ABAE; +13DF;CHEROKEE LETTER TLI;Lu;0;L;;;;;N;;;;ABAF; +13E0;CHEROKEE LETTER TLO;Lu;0;L;;;;;N;;;;ABB0; +13E1;CHEROKEE LETTER TLU;Lu;0;L;;;;;N;;;;ABB1; +13E2;CHEROKEE LETTER TLV;Lu;0;L;;;;;N;;;;ABB2; +13E3;CHEROKEE LETTER TSA;Lu;0;L;;;;;N;;;;ABB3; +13E4;CHEROKEE LETTER TSE;Lu;0;L;;;;;N;;;;ABB4; +13E5;CHEROKEE LETTER TSI;Lu;0;L;;;;;N;;;;ABB5; +13E6;CHEROKEE LETTER TSO;Lu;0;L;;;;;N;;;;ABB6; +13E7;CHEROKEE LETTER TSU;Lu;0;L;;;;;N;;;;ABB7; +13E8;CHEROKEE LETTER TSV;Lu;0;L;;;;;N;;;;ABB8; +13E9;CHEROKEE LETTER WA;Lu;0;L;;;;;N;;;;ABB9; +13EA;CHEROKEE LETTER WE;Lu;0;L;;;;;N;;;;ABBA; +13EB;CHEROKEE LETTER WI;Lu;0;L;;;;;N;;;;ABBB; +13EC;CHEROKEE LETTER WO;Lu;0;L;;;;;N;;;;ABBC; +13ED;CHEROKEE LETTER WU;Lu;0;L;;;;;N;;;;ABBD; +13EE;CHEROKEE LETTER WV;Lu;0;L;;;;;N;;;;ABBE; +13EF;CHEROKEE LETTER YA;Lu;0;L;;;;;N;;;;ABBF; +13F0;CHEROKEE LETTER YE;Lu;0;L;;;;;N;;;;13F8; +13F1;CHEROKEE LETTER YI;Lu;0;L;;;;;N;;;;13F9; +13F2;CHEROKEE LETTER YO;Lu;0;L;;;;;N;;;;13FA; +13F3;CHEROKEE LETTER YU;Lu;0;L;;;;;N;;;;13FB; +13F4;CHEROKEE LETTER YV;Lu;0;L;;;;;N;;;;13FC; +13F5;CHEROKEE LETTER MV;Lu;0;L;;;;;N;;;;13FD; +13F8;CHEROKEE SMALL LETTER YE;Ll;0;L;;;;;N;;;13F0;;13F0 +13F9;CHEROKEE SMALL LETTER YI;Ll;0;L;;;;;N;;;13F1;;13F1 +13FA;CHEROKEE SMALL LETTER YO;Ll;0;L;;;;;N;;;13F2;;13F2 +13FB;CHEROKEE SMALL LETTER YU;Ll;0;L;;;;;N;;;13F3;;13F3 +13FC;CHEROKEE SMALL LETTER YV;Ll;0;L;;;;;N;;;13F4;;13F4 +13FD;CHEROKEE SMALL LETTER MV;Ll;0;L;;;;;N;;;13F5;;13F5 +1400;CANADIAN SYLLABICS HYPHEN;Pd;0;ON;;;;;N;;;;; +1401;CANADIAN SYLLABICS E;Lo;0;L;;;;;N;;;;; +1402;CANADIAN SYLLABICS AAI;Lo;0;L;;;;;N;;;;; +1403;CANADIAN SYLLABICS I;Lo;0;L;;;;;N;;;;; +1404;CANADIAN SYLLABICS II;Lo;0;L;;;;;N;;;;; +1405;CANADIAN SYLLABICS O;Lo;0;L;;;;;N;;;;; +1406;CANADIAN SYLLABICS OO;Lo;0;L;;;;;N;;;;; +1407;CANADIAN SYLLABICS Y-CREE OO;Lo;0;L;;;;;N;;;;; +1408;CANADIAN SYLLABICS CARRIER EE;Lo;0;L;;;;;N;;;;; +1409;CANADIAN SYLLABICS CARRIER I;Lo;0;L;;;;;N;;;;; +140A;CANADIAN SYLLABICS A;Lo;0;L;;;;;N;;;;; +140B;CANADIAN SYLLABICS AA;Lo;0;L;;;;;N;;;;; +140C;CANADIAN SYLLABICS WE;Lo;0;L;;;;;N;;;;; +140D;CANADIAN SYLLABICS WEST-CREE WE;Lo;0;L;;;;;N;;;;; +140E;CANADIAN SYLLABICS WI;Lo;0;L;;;;;N;;;;; +140F;CANADIAN SYLLABICS WEST-CREE WI;Lo;0;L;;;;;N;;;;; +1410;CANADIAN SYLLABICS WII;Lo;0;L;;;;;N;;;;; +1411;CANADIAN SYLLABICS WEST-CREE WII;Lo;0;L;;;;;N;;;;; +1412;CANADIAN SYLLABICS WO;Lo;0;L;;;;;N;;;;; +1413;CANADIAN SYLLABICS WEST-CREE WO;Lo;0;L;;;;;N;;;;; +1414;CANADIAN SYLLABICS WOO;Lo;0;L;;;;;N;;;;; +1415;CANADIAN SYLLABICS WEST-CREE WOO;Lo;0;L;;;;;N;;;;; +1416;CANADIAN SYLLABICS NASKAPI WOO;Lo;0;L;;;;;N;;;;; +1417;CANADIAN SYLLABICS WA;Lo;0;L;;;;;N;;;;; +1418;CANADIAN SYLLABICS WEST-CREE WA;Lo;0;L;;;;;N;;;;; +1419;CANADIAN SYLLABICS WAA;Lo;0;L;;;;;N;;;;; +141A;CANADIAN SYLLABICS WEST-CREE WAA;Lo;0;L;;;;;N;;;;; +141B;CANADIAN SYLLABICS NASKAPI WAA;Lo;0;L;;;;;N;;;;; +141C;CANADIAN SYLLABICS AI;Lo;0;L;;;;;N;;;;; +141D;CANADIAN SYLLABICS Y-CREE W;Lo;0;L;;;;;N;;;;; +141E;CANADIAN SYLLABICS GLOTTAL STOP;Lo;0;L;;;;;N;;;;; +141F;CANADIAN SYLLABICS FINAL ACUTE;Lo;0;L;;;;;N;;;;; +1420;CANADIAN SYLLABICS FINAL GRAVE;Lo;0;L;;;;;N;;;;; +1421;CANADIAN SYLLABICS FINAL BOTTOM HALF RING;Lo;0;L;;;;;N;;;;; +1422;CANADIAN SYLLABICS FINAL TOP HALF RING;Lo;0;L;;;;;N;;;;; +1423;CANADIAN SYLLABICS FINAL RIGHT HALF RING;Lo;0;L;;;;;N;;;;; +1424;CANADIAN SYLLABICS FINAL RING;Lo;0;L;;;;;N;;;;; +1425;CANADIAN SYLLABICS FINAL DOUBLE ACUTE;Lo;0;L;;;;;N;;;;; +1426;CANADIAN SYLLABICS FINAL DOUBLE SHORT VERTICAL STROKES;Lo;0;L;;;;;N;;;;; +1427;CANADIAN SYLLABICS FINAL MIDDLE DOT;Lo;0;L;;;;;N;;;;; +1428;CANADIAN SYLLABICS FINAL SHORT HORIZONTAL STROKE;Lo;0;L;;;;;N;;;;; +1429;CANADIAN SYLLABICS FINAL PLUS;Lo;0;L;;;;;N;;;;; +142A;CANADIAN SYLLABICS FINAL DOWN TACK;Lo;0;L;;;;;N;;;;; +142B;CANADIAN SYLLABICS EN;Lo;0;L;;;;;N;;;;; +142C;CANADIAN SYLLABICS IN;Lo;0;L;;;;;N;;;;; +142D;CANADIAN SYLLABICS ON;Lo;0;L;;;;;N;;;;; +142E;CANADIAN SYLLABICS AN;Lo;0;L;;;;;N;;;;; +142F;CANADIAN SYLLABICS PE;Lo;0;L;;;;;N;;;;; +1430;CANADIAN SYLLABICS PAAI;Lo;0;L;;;;;N;;;;; +1431;CANADIAN SYLLABICS PI;Lo;0;L;;;;;N;;;;; +1432;CANADIAN SYLLABICS PII;Lo;0;L;;;;;N;;;;; +1433;CANADIAN SYLLABICS PO;Lo;0;L;;;;;N;;;;; +1434;CANADIAN SYLLABICS POO;Lo;0;L;;;;;N;;;;; +1435;CANADIAN SYLLABICS Y-CREE POO;Lo;0;L;;;;;N;;;;; +1436;CANADIAN SYLLABICS CARRIER HEE;Lo;0;L;;;;;N;;;;; +1437;CANADIAN SYLLABICS CARRIER HI;Lo;0;L;;;;;N;;;;; +1438;CANADIAN SYLLABICS PA;Lo;0;L;;;;;N;;;;; +1439;CANADIAN SYLLABICS PAA;Lo;0;L;;;;;N;;;;; +143A;CANADIAN SYLLABICS PWE;Lo;0;L;;;;;N;;;;; +143B;CANADIAN SYLLABICS WEST-CREE PWE;Lo;0;L;;;;;N;;;;; +143C;CANADIAN SYLLABICS PWI;Lo;0;L;;;;;N;;;;; +143D;CANADIAN SYLLABICS WEST-CREE PWI;Lo;0;L;;;;;N;;;;; +143E;CANADIAN SYLLABICS PWII;Lo;0;L;;;;;N;;;;; +143F;CANADIAN SYLLABICS WEST-CREE PWII;Lo;0;L;;;;;N;;;;; +1440;CANADIAN SYLLABICS PWO;Lo;0;L;;;;;N;;;;; +1441;CANADIAN SYLLABICS WEST-CREE PWO;Lo;0;L;;;;;N;;;;; +1442;CANADIAN SYLLABICS PWOO;Lo;0;L;;;;;N;;;;; +1443;CANADIAN SYLLABICS WEST-CREE PWOO;Lo;0;L;;;;;N;;;;; +1444;CANADIAN SYLLABICS PWA;Lo;0;L;;;;;N;;;;; +1445;CANADIAN SYLLABICS WEST-CREE PWA;Lo;0;L;;;;;N;;;;; +1446;CANADIAN SYLLABICS PWAA;Lo;0;L;;;;;N;;;;; +1447;CANADIAN SYLLABICS WEST-CREE PWAA;Lo;0;L;;;;;N;;;;; +1448;CANADIAN SYLLABICS Y-CREE PWAA;Lo;0;L;;;;;N;;;;; +1449;CANADIAN SYLLABICS P;Lo;0;L;;;;;N;;;;; +144A;CANADIAN SYLLABICS WEST-CREE P;Lo;0;L;;;;;N;;;;; +144B;CANADIAN SYLLABICS CARRIER H;Lo;0;L;;;;;N;;;;; +144C;CANADIAN SYLLABICS TE;Lo;0;L;;;;;N;;;;; +144D;CANADIAN SYLLABICS TAAI;Lo;0;L;;;;;N;;;;; +144E;CANADIAN SYLLABICS TI;Lo;0;L;;;;;N;;;;; +144F;CANADIAN SYLLABICS TII;Lo;0;L;;;;;N;;;;; +1450;CANADIAN SYLLABICS TO;Lo;0;L;;;;;N;;;;; +1451;CANADIAN SYLLABICS TOO;Lo;0;L;;;;;N;;;;; +1452;CANADIAN SYLLABICS Y-CREE TOO;Lo;0;L;;;;;N;;;;; +1453;CANADIAN SYLLABICS CARRIER DEE;Lo;0;L;;;;;N;;;;; +1454;CANADIAN SYLLABICS CARRIER DI;Lo;0;L;;;;;N;;;;; +1455;CANADIAN SYLLABICS TA;Lo;0;L;;;;;N;;;;; +1456;CANADIAN SYLLABICS TAA;Lo;0;L;;;;;N;;;;; +1457;CANADIAN SYLLABICS TWE;Lo;0;L;;;;;N;;;;; +1458;CANADIAN SYLLABICS WEST-CREE TWE;Lo;0;L;;;;;N;;;;; +1459;CANADIAN SYLLABICS TWI;Lo;0;L;;;;;N;;;;; +145A;CANADIAN SYLLABICS WEST-CREE TWI;Lo;0;L;;;;;N;;;;; +145B;CANADIAN SYLLABICS TWII;Lo;0;L;;;;;N;;;;; +145C;CANADIAN SYLLABICS WEST-CREE TWII;Lo;0;L;;;;;N;;;;; +145D;CANADIAN SYLLABICS TWO;Lo;0;L;;;;;N;;;;; +145E;CANADIAN SYLLABICS WEST-CREE TWO;Lo;0;L;;;;;N;;;;; +145F;CANADIAN SYLLABICS TWOO;Lo;0;L;;;;;N;;;;; +1460;CANADIAN SYLLABICS WEST-CREE TWOO;Lo;0;L;;;;;N;;;;; +1461;CANADIAN SYLLABICS TWA;Lo;0;L;;;;;N;;;;; +1462;CANADIAN SYLLABICS WEST-CREE TWA;Lo;0;L;;;;;N;;;;; +1463;CANADIAN SYLLABICS TWAA;Lo;0;L;;;;;N;;;;; +1464;CANADIAN SYLLABICS WEST-CREE TWAA;Lo;0;L;;;;;N;;;;; +1465;CANADIAN SYLLABICS NASKAPI TWAA;Lo;0;L;;;;;N;;;;; +1466;CANADIAN SYLLABICS T;Lo;0;L;;;;;N;;;;; +1467;CANADIAN SYLLABICS TTE;Lo;0;L;;;;;N;;;;; +1468;CANADIAN SYLLABICS TTI;Lo;0;L;;;;;N;;;;; +1469;CANADIAN SYLLABICS TTO;Lo;0;L;;;;;N;;;;; +146A;CANADIAN SYLLABICS TTA;Lo;0;L;;;;;N;;;;; +146B;CANADIAN SYLLABICS KE;Lo;0;L;;;;;N;;;;; +146C;CANADIAN SYLLABICS KAAI;Lo;0;L;;;;;N;;;;; +146D;CANADIAN SYLLABICS KI;Lo;0;L;;;;;N;;;;; +146E;CANADIAN SYLLABICS KII;Lo;0;L;;;;;N;;;;; +146F;CANADIAN SYLLABICS KO;Lo;0;L;;;;;N;;;;; +1470;CANADIAN SYLLABICS KOO;Lo;0;L;;;;;N;;;;; +1471;CANADIAN SYLLABICS Y-CREE KOO;Lo;0;L;;;;;N;;;;; +1472;CANADIAN SYLLABICS KA;Lo;0;L;;;;;N;;;;; +1473;CANADIAN SYLLABICS KAA;Lo;0;L;;;;;N;;;;; +1474;CANADIAN SYLLABICS KWE;Lo;0;L;;;;;N;;;;; +1475;CANADIAN SYLLABICS WEST-CREE KWE;Lo;0;L;;;;;N;;;;; +1476;CANADIAN SYLLABICS KWI;Lo;0;L;;;;;N;;;;; +1477;CANADIAN SYLLABICS WEST-CREE KWI;Lo;0;L;;;;;N;;;;; +1478;CANADIAN SYLLABICS KWII;Lo;0;L;;;;;N;;;;; +1479;CANADIAN SYLLABICS WEST-CREE KWII;Lo;0;L;;;;;N;;;;; +147A;CANADIAN SYLLABICS KWO;Lo;0;L;;;;;N;;;;; +147B;CANADIAN SYLLABICS WEST-CREE KWO;Lo;0;L;;;;;N;;;;; +147C;CANADIAN SYLLABICS KWOO;Lo;0;L;;;;;N;;;;; +147D;CANADIAN SYLLABICS WEST-CREE KWOO;Lo;0;L;;;;;N;;;;; +147E;CANADIAN SYLLABICS KWA;Lo;0;L;;;;;N;;;;; +147F;CANADIAN SYLLABICS WEST-CREE KWA;Lo;0;L;;;;;N;;;;; +1480;CANADIAN SYLLABICS KWAA;Lo;0;L;;;;;N;;;;; +1481;CANADIAN SYLLABICS WEST-CREE KWAA;Lo;0;L;;;;;N;;;;; +1482;CANADIAN SYLLABICS NASKAPI KWAA;Lo;0;L;;;;;N;;;;; +1483;CANADIAN SYLLABICS K;Lo;0;L;;;;;N;;;;; +1484;CANADIAN SYLLABICS KW;Lo;0;L;;;;;N;;;;; +1485;CANADIAN SYLLABICS SOUTH-SLAVEY KEH;Lo;0;L;;;;;N;;;;; +1486;CANADIAN SYLLABICS SOUTH-SLAVEY KIH;Lo;0;L;;;;;N;;;;; +1487;CANADIAN SYLLABICS SOUTH-SLAVEY KOH;Lo;0;L;;;;;N;;;;; +1488;CANADIAN SYLLABICS SOUTH-SLAVEY KAH;Lo;0;L;;;;;N;;;;; +1489;CANADIAN SYLLABICS CE;Lo;0;L;;;;;N;;;;; +148A;CANADIAN SYLLABICS CAAI;Lo;0;L;;;;;N;;;;; +148B;CANADIAN SYLLABICS CI;Lo;0;L;;;;;N;;;;; +148C;CANADIAN SYLLABICS CII;Lo;0;L;;;;;N;;;;; +148D;CANADIAN SYLLABICS CO;Lo;0;L;;;;;N;;;;; +148E;CANADIAN SYLLABICS COO;Lo;0;L;;;;;N;;;;; +148F;CANADIAN SYLLABICS Y-CREE COO;Lo;0;L;;;;;N;;;;; +1490;CANADIAN SYLLABICS CA;Lo;0;L;;;;;N;;;;; +1491;CANADIAN SYLLABICS CAA;Lo;0;L;;;;;N;;;;; +1492;CANADIAN SYLLABICS CWE;Lo;0;L;;;;;N;;;;; +1493;CANADIAN SYLLABICS WEST-CREE CWE;Lo;0;L;;;;;N;;;;; +1494;CANADIAN SYLLABICS CWI;Lo;0;L;;;;;N;;;;; +1495;CANADIAN SYLLABICS WEST-CREE CWI;Lo;0;L;;;;;N;;;;; +1496;CANADIAN SYLLABICS CWII;Lo;0;L;;;;;N;;;;; +1497;CANADIAN SYLLABICS WEST-CREE CWII;Lo;0;L;;;;;N;;;;; +1498;CANADIAN SYLLABICS CWO;Lo;0;L;;;;;N;;;;; +1499;CANADIAN SYLLABICS WEST-CREE CWO;Lo;0;L;;;;;N;;;;; +149A;CANADIAN SYLLABICS CWOO;Lo;0;L;;;;;N;;;;; +149B;CANADIAN SYLLABICS WEST-CREE CWOO;Lo;0;L;;;;;N;;;;; +149C;CANADIAN SYLLABICS CWA;Lo;0;L;;;;;N;;;;; +149D;CANADIAN SYLLABICS WEST-CREE CWA;Lo;0;L;;;;;N;;;;; +149E;CANADIAN SYLLABICS CWAA;Lo;0;L;;;;;N;;;;; +149F;CANADIAN SYLLABICS WEST-CREE CWAA;Lo;0;L;;;;;N;;;;; +14A0;CANADIAN SYLLABICS NASKAPI CWAA;Lo;0;L;;;;;N;;;;; +14A1;CANADIAN SYLLABICS C;Lo;0;L;;;;;N;;;;; +14A2;CANADIAN SYLLABICS SAYISI TH;Lo;0;L;;;;;N;;;;; +14A3;CANADIAN SYLLABICS ME;Lo;0;L;;;;;N;;;;; +14A4;CANADIAN SYLLABICS MAAI;Lo;0;L;;;;;N;;;;; +14A5;CANADIAN SYLLABICS MI;Lo;0;L;;;;;N;;;;; +14A6;CANADIAN SYLLABICS MII;Lo;0;L;;;;;N;;;;; +14A7;CANADIAN SYLLABICS MO;Lo;0;L;;;;;N;;;;; +14A8;CANADIAN SYLLABICS MOO;Lo;0;L;;;;;N;;;;; +14A9;CANADIAN SYLLABICS Y-CREE MOO;Lo;0;L;;;;;N;;;;; +14AA;CANADIAN SYLLABICS MA;Lo;0;L;;;;;N;;;;; +14AB;CANADIAN SYLLABICS MAA;Lo;0;L;;;;;N;;;;; +14AC;CANADIAN SYLLABICS MWE;Lo;0;L;;;;;N;;;;; +14AD;CANADIAN SYLLABICS WEST-CREE MWE;Lo;0;L;;;;;N;;;;; +14AE;CANADIAN SYLLABICS MWI;Lo;0;L;;;;;N;;;;; +14AF;CANADIAN SYLLABICS WEST-CREE MWI;Lo;0;L;;;;;N;;;;; +14B0;CANADIAN SYLLABICS MWII;Lo;0;L;;;;;N;;;;; +14B1;CANADIAN SYLLABICS WEST-CREE MWII;Lo;0;L;;;;;N;;;;; +14B2;CANADIAN SYLLABICS MWO;Lo;0;L;;;;;N;;;;; +14B3;CANADIAN SYLLABICS WEST-CREE MWO;Lo;0;L;;;;;N;;;;; +14B4;CANADIAN SYLLABICS MWOO;Lo;0;L;;;;;N;;;;; +14B5;CANADIAN SYLLABICS WEST-CREE MWOO;Lo;0;L;;;;;N;;;;; +14B6;CANADIAN SYLLABICS MWA;Lo;0;L;;;;;N;;;;; +14B7;CANADIAN SYLLABICS WEST-CREE MWA;Lo;0;L;;;;;N;;;;; +14B8;CANADIAN SYLLABICS MWAA;Lo;0;L;;;;;N;;;;; +14B9;CANADIAN SYLLABICS WEST-CREE MWAA;Lo;0;L;;;;;N;;;;; +14BA;CANADIAN SYLLABICS NASKAPI MWAA;Lo;0;L;;;;;N;;;;; +14BB;CANADIAN SYLLABICS M;Lo;0;L;;;;;N;;;;; +14BC;CANADIAN SYLLABICS WEST-CREE M;Lo;0;L;;;;;N;;;;; +14BD;CANADIAN SYLLABICS MH;Lo;0;L;;;;;N;;;;; +14BE;CANADIAN SYLLABICS ATHAPASCAN M;Lo;0;L;;;;;N;;;;; +14BF;CANADIAN SYLLABICS SAYISI M;Lo;0;L;;;;;N;;;;; +14C0;CANADIAN SYLLABICS NE;Lo;0;L;;;;;N;;;;; +14C1;CANADIAN SYLLABICS NAAI;Lo;0;L;;;;;N;;;;; +14C2;CANADIAN SYLLABICS NI;Lo;0;L;;;;;N;;;;; +14C3;CANADIAN SYLLABICS NII;Lo;0;L;;;;;N;;;;; +14C4;CANADIAN SYLLABICS NO;Lo;0;L;;;;;N;;;;; +14C5;CANADIAN SYLLABICS NOO;Lo;0;L;;;;;N;;;;; +14C6;CANADIAN SYLLABICS Y-CREE NOO;Lo;0;L;;;;;N;;;;; +14C7;CANADIAN SYLLABICS NA;Lo;0;L;;;;;N;;;;; +14C8;CANADIAN SYLLABICS NAA;Lo;0;L;;;;;N;;;;; +14C9;CANADIAN SYLLABICS NWE;Lo;0;L;;;;;N;;;;; +14CA;CANADIAN SYLLABICS WEST-CREE NWE;Lo;0;L;;;;;N;;;;; +14CB;CANADIAN SYLLABICS NWA;Lo;0;L;;;;;N;;;;; +14CC;CANADIAN SYLLABICS WEST-CREE NWA;Lo;0;L;;;;;N;;;;; +14CD;CANADIAN SYLLABICS NWAA;Lo;0;L;;;;;N;;;;; +14CE;CANADIAN SYLLABICS WEST-CREE NWAA;Lo;0;L;;;;;N;;;;; +14CF;CANADIAN SYLLABICS NASKAPI NWAA;Lo;0;L;;;;;N;;;;; +14D0;CANADIAN SYLLABICS N;Lo;0;L;;;;;N;;;;; +14D1;CANADIAN SYLLABICS CARRIER NG;Lo;0;L;;;;;N;;;;; +14D2;CANADIAN SYLLABICS NH;Lo;0;L;;;;;N;;;;; +14D3;CANADIAN SYLLABICS LE;Lo;0;L;;;;;N;;;;; +14D4;CANADIAN SYLLABICS LAAI;Lo;0;L;;;;;N;;;;; +14D5;CANADIAN SYLLABICS LI;Lo;0;L;;;;;N;;;;; +14D6;CANADIAN SYLLABICS LII;Lo;0;L;;;;;N;;;;; +14D7;CANADIAN SYLLABICS LO;Lo;0;L;;;;;N;;;;; +14D8;CANADIAN SYLLABICS LOO;Lo;0;L;;;;;N;;;;; +14D9;CANADIAN SYLLABICS Y-CREE LOO;Lo;0;L;;;;;N;;;;; +14DA;CANADIAN SYLLABICS LA;Lo;0;L;;;;;N;;;;; +14DB;CANADIAN SYLLABICS LAA;Lo;0;L;;;;;N;;;;; +14DC;CANADIAN SYLLABICS LWE;Lo;0;L;;;;;N;;;;; +14DD;CANADIAN SYLLABICS WEST-CREE LWE;Lo;0;L;;;;;N;;;;; +14DE;CANADIAN SYLLABICS LWI;Lo;0;L;;;;;N;;;;; +14DF;CANADIAN SYLLABICS WEST-CREE LWI;Lo;0;L;;;;;N;;;;; +14E0;CANADIAN SYLLABICS LWII;Lo;0;L;;;;;N;;;;; +14E1;CANADIAN SYLLABICS WEST-CREE LWII;Lo;0;L;;;;;N;;;;; +14E2;CANADIAN SYLLABICS LWO;Lo;0;L;;;;;N;;;;; +14E3;CANADIAN SYLLABICS WEST-CREE LWO;Lo;0;L;;;;;N;;;;; +14E4;CANADIAN SYLLABICS LWOO;Lo;0;L;;;;;N;;;;; +14E5;CANADIAN SYLLABICS WEST-CREE LWOO;Lo;0;L;;;;;N;;;;; +14E6;CANADIAN SYLLABICS LWA;Lo;0;L;;;;;N;;;;; +14E7;CANADIAN SYLLABICS WEST-CREE LWA;Lo;0;L;;;;;N;;;;; +14E8;CANADIAN SYLLABICS LWAA;Lo;0;L;;;;;N;;;;; +14E9;CANADIAN SYLLABICS WEST-CREE LWAA;Lo;0;L;;;;;N;;;;; +14EA;CANADIAN SYLLABICS L;Lo;0;L;;;;;N;;;;; +14EB;CANADIAN SYLLABICS WEST-CREE L;Lo;0;L;;;;;N;;;;; +14EC;CANADIAN SYLLABICS MEDIAL L;Lo;0;L;;;;;N;;;;; +14ED;CANADIAN SYLLABICS SE;Lo;0;L;;;;;N;;;;; +14EE;CANADIAN SYLLABICS SAAI;Lo;0;L;;;;;N;;;;; +14EF;CANADIAN SYLLABICS SI;Lo;0;L;;;;;N;;;;; +14F0;CANADIAN SYLLABICS SII;Lo;0;L;;;;;N;;;;; +14F1;CANADIAN SYLLABICS SO;Lo;0;L;;;;;N;;;;; +14F2;CANADIAN SYLLABICS SOO;Lo;0;L;;;;;N;;;;; +14F3;CANADIAN SYLLABICS Y-CREE SOO;Lo;0;L;;;;;N;;;;; +14F4;CANADIAN SYLLABICS SA;Lo;0;L;;;;;N;;;;; +14F5;CANADIAN SYLLABICS SAA;Lo;0;L;;;;;N;;;;; +14F6;CANADIAN SYLLABICS SWE;Lo;0;L;;;;;N;;;;; +14F7;CANADIAN SYLLABICS WEST-CREE SWE;Lo;0;L;;;;;N;;;;; +14F8;CANADIAN SYLLABICS SWI;Lo;0;L;;;;;N;;;;; +14F9;CANADIAN SYLLABICS WEST-CREE SWI;Lo;0;L;;;;;N;;;;; +14FA;CANADIAN SYLLABICS SWII;Lo;0;L;;;;;N;;;;; +14FB;CANADIAN SYLLABICS WEST-CREE SWII;Lo;0;L;;;;;N;;;;; +14FC;CANADIAN SYLLABICS SWO;Lo;0;L;;;;;N;;;;; +14FD;CANADIAN SYLLABICS WEST-CREE SWO;Lo;0;L;;;;;N;;;;; +14FE;CANADIAN SYLLABICS SWOO;Lo;0;L;;;;;N;;;;; +14FF;CANADIAN SYLLABICS WEST-CREE SWOO;Lo;0;L;;;;;N;;;;; +1500;CANADIAN SYLLABICS SWA;Lo;0;L;;;;;N;;;;; +1501;CANADIAN SYLLABICS WEST-CREE SWA;Lo;0;L;;;;;N;;;;; +1502;CANADIAN SYLLABICS SWAA;Lo;0;L;;;;;N;;;;; +1503;CANADIAN SYLLABICS WEST-CREE SWAA;Lo;0;L;;;;;N;;;;; +1504;CANADIAN SYLLABICS NASKAPI SWAA;Lo;0;L;;;;;N;;;;; +1505;CANADIAN SYLLABICS S;Lo;0;L;;;;;N;;;;; +1506;CANADIAN SYLLABICS ATHAPASCAN S;Lo;0;L;;;;;N;;;;; +1507;CANADIAN SYLLABICS SW;Lo;0;L;;;;;N;;;;; +1508;CANADIAN SYLLABICS BLACKFOOT S;Lo;0;L;;;;;N;;;;; +1509;CANADIAN SYLLABICS MOOSE-CREE SK;Lo;0;L;;;;;N;;;;; +150A;CANADIAN SYLLABICS NASKAPI SKW;Lo;0;L;;;;;N;;;;; +150B;CANADIAN SYLLABICS NASKAPI S-W;Lo;0;L;;;;;N;;;;; +150C;CANADIAN SYLLABICS NASKAPI SPWA;Lo;0;L;;;;;N;;;;; +150D;CANADIAN SYLLABICS NASKAPI STWA;Lo;0;L;;;;;N;;;;; +150E;CANADIAN SYLLABICS NASKAPI SKWA;Lo;0;L;;;;;N;;;;; +150F;CANADIAN SYLLABICS NASKAPI SCWA;Lo;0;L;;;;;N;;;;; +1510;CANADIAN SYLLABICS SHE;Lo;0;L;;;;;N;;;;; +1511;CANADIAN SYLLABICS SHI;Lo;0;L;;;;;N;;;;; +1512;CANADIAN SYLLABICS SHII;Lo;0;L;;;;;N;;;;; +1513;CANADIAN SYLLABICS SHO;Lo;0;L;;;;;N;;;;; +1514;CANADIAN SYLLABICS SHOO;Lo;0;L;;;;;N;;;;; +1515;CANADIAN SYLLABICS SHA;Lo;0;L;;;;;N;;;;; +1516;CANADIAN SYLLABICS SHAA;Lo;0;L;;;;;N;;;;; +1517;CANADIAN SYLLABICS SHWE;Lo;0;L;;;;;N;;;;; +1518;CANADIAN SYLLABICS WEST-CREE SHWE;Lo;0;L;;;;;N;;;;; +1519;CANADIAN SYLLABICS SHWI;Lo;0;L;;;;;N;;;;; +151A;CANADIAN SYLLABICS WEST-CREE SHWI;Lo;0;L;;;;;N;;;;; +151B;CANADIAN SYLLABICS SHWII;Lo;0;L;;;;;N;;;;; +151C;CANADIAN SYLLABICS WEST-CREE SHWII;Lo;0;L;;;;;N;;;;; +151D;CANADIAN SYLLABICS SHWO;Lo;0;L;;;;;N;;;;; +151E;CANADIAN SYLLABICS WEST-CREE SHWO;Lo;0;L;;;;;N;;;;; +151F;CANADIAN SYLLABICS SHWOO;Lo;0;L;;;;;N;;;;; +1520;CANADIAN SYLLABICS WEST-CREE SHWOO;Lo;0;L;;;;;N;;;;; +1521;CANADIAN SYLLABICS SHWA;Lo;0;L;;;;;N;;;;; +1522;CANADIAN SYLLABICS WEST-CREE SHWA;Lo;0;L;;;;;N;;;;; +1523;CANADIAN SYLLABICS SHWAA;Lo;0;L;;;;;N;;;;; +1524;CANADIAN SYLLABICS WEST-CREE SHWAA;Lo;0;L;;;;;N;;;;; +1525;CANADIAN SYLLABICS SH;Lo;0;L;;;;;N;;;;; +1526;CANADIAN SYLLABICS YE;Lo;0;L;;;;;N;;;;; +1527;CANADIAN SYLLABICS YAAI;Lo;0;L;;;;;N;;;;; +1528;CANADIAN SYLLABICS YI;Lo;0;L;;;;;N;;;;; +1529;CANADIAN SYLLABICS YII;Lo;0;L;;;;;N;;;;; +152A;CANADIAN SYLLABICS YO;Lo;0;L;;;;;N;;;;; +152B;CANADIAN SYLLABICS YOO;Lo;0;L;;;;;N;;;;; +152C;CANADIAN SYLLABICS Y-CREE YOO;Lo;0;L;;;;;N;;;;; +152D;CANADIAN SYLLABICS YA;Lo;0;L;;;;;N;;;;; +152E;CANADIAN SYLLABICS YAA;Lo;0;L;;;;;N;;;;; +152F;CANADIAN SYLLABICS YWE;Lo;0;L;;;;;N;;;;; +1530;CANADIAN SYLLABICS WEST-CREE YWE;Lo;0;L;;;;;N;;;;; +1531;CANADIAN SYLLABICS YWI;Lo;0;L;;;;;N;;;;; +1532;CANADIAN SYLLABICS WEST-CREE YWI;Lo;0;L;;;;;N;;;;; +1533;CANADIAN SYLLABICS YWII;Lo;0;L;;;;;N;;;;; +1534;CANADIAN SYLLABICS WEST-CREE YWII;Lo;0;L;;;;;N;;;;; +1535;CANADIAN SYLLABICS YWO;Lo;0;L;;;;;N;;;;; +1536;CANADIAN SYLLABICS WEST-CREE YWO;Lo;0;L;;;;;N;;;;; +1537;CANADIAN SYLLABICS YWOO;Lo;0;L;;;;;N;;;;; +1538;CANADIAN SYLLABICS WEST-CREE YWOO;Lo;0;L;;;;;N;;;;; +1539;CANADIAN SYLLABICS YWA;Lo;0;L;;;;;N;;;;; +153A;CANADIAN SYLLABICS WEST-CREE YWA;Lo;0;L;;;;;N;;;;; +153B;CANADIAN SYLLABICS YWAA;Lo;0;L;;;;;N;;;;; +153C;CANADIAN SYLLABICS WEST-CREE YWAA;Lo;0;L;;;;;N;;;;; +153D;CANADIAN SYLLABICS NASKAPI YWAA;Lo;0;L;;;;;N;;;;; +153E;CANADIAN SYLLABICS Y;Lo;0;L;;;;;N;;;;; +153F;CANADIAN SYLLABICS BIBLE-CREE Y;Lo;0;L;;;;;N;;;;; +1540;CANADIAN SYLLABICS WEST-CREE Y;Lo;0;L;;;;;N;;;;; +1541;CANADIAN SYLLABICS SAYISI YI;Lo;0;L;;;;;N;;;;; +1542;CANADIAN SYLLABICS RE;Lo;0;L;;;;;N;;;;; +1543;CANADIAN SYLLABICS R-CREE RE;Lo;0;L;;;;;N;;;;; +1544;CANADIAN SYLLABICS WEST-CREE LE;Lo;0;L;;;;;N;;;;; +1545;CANADIAN SYLLABICS RAAI;Lo;0;L;;;;;N;;;;; +1546;CANADIAN SYLLABICS RI;Lo;0;L;;;;;N;;;;; +1547;CANADIAN SYLLABICS RII;Lo;0;L;;;;;N;;;;; +1548;CANADIAN SYLLABICS RO;Lo;0;L;;;;;N;;;;; +1549;CANADIAN SYLLABICS ROO;Lo;0;L;;;;;N;;;;; +154A;CANADIAN SYLLABICS WEST-CREE LO;Lo;0;L;;;;;N;;;;; +154B;CANADIAN SYLLABICS RA;Lo;0;L;;;;;N;;;;; +154C;CANADIAN SYLLABICS RAA;Lo;0;L;;;;;N;;;;; +154D;CANADIAN SYLLABICS WEST-CREE LA;Lo;0;L;;;;;N;;;;; +154E;CANADIAN SYLLABICS RWAA;Lo;0;L;;;;;N;;;;; +154F;CANADIAN SYLLABICS WEST-CREE RWAA;Lo;0;L;;;;;N;;;;; +1550;CANADIAN SYLLABICS R;Lo;0;L;;;;;N;;;;; +1551;CANADIAN SYLLABICS WEST-CREE R;Lo;0;L;;;;;N;;;;; +1552;CANADIAN SYLLABICS MEDIAL R;Lo;0;L;;;;;N;;;;; +1553;CANADIAN SYLLABICS FE;Lo;0;L;;;;;N;;;;; +1554;CANADIAN SYLLABICS FAAI;Lo;0;L;;;;;N;;;;; +1555;CANADIAN SYLLABICS FI;Lo;0;L;;;;;N;;;;; +1556;CANADIAN SYLLABICS FII;Lo;0;L;;;;;N;;;;; +1557;CANADIAN SYLLABICS FO;Lo;0;L;;;;;N;;;;; +1558;CANADIAN SYLLABICS FOO;Lo;0;L;;;;;N;;;;; +1559;CANADIAN SYLLABICS FA;Lo;0;L;;;;;N;;;;; +155A;CANADIAN SYLLABICS FAA;Lo;0;L;;;;;N;;;;; +155B;CANADIAN SYLLABICS FWAA;Lo;0;L;;;;;N;;;;; +155C;CANADIAN SYLLABICS WEST-CREE FWAA;Lo;0;L;;;;;N;;;;; +155D;CANADIAN SYLLABICS F;Lo;0;L;;;;;N;;;;; +155E;CANADIAN SYLLABICS THE;Lo;0;L;;;;;N;;;;; +155F;CANADIAN SYLLABICS N-CREE THE;Lo;0;L;;;;;N;;;;; +1560;CANADIAN SYLLABICS THI;Lo;0;L;;;;;N;;;;; +1561;CANADIAN SYLLABICS N-CREE THI;Lo;0;L;;;;;N;;;;; +1562;CANADIAN SYLLABICS THII;Lo;0;L;;;;;N;;;;; +1563;CANADIAN SYLLABICS N-CREE THII;Lo;0;L;;;;;N;;;;; +1564;CANADIAN SYLLABICS THO;Lo;0;L;;;;;N;;;;; +1565;CANADIAN SYLLABICS THOO;Lo;0;L;;;;;N;;;;; +1566;CANADIAN SYLLABICS THA;Lo;0;L;;;;;N;;;;; +1567;CANADIAN SYLLABICS THAA;Lo;0;L;;;;;N;;;;; +1568;CANADIAN SYLLABICS THWAA;Lo;0;L;;;;;N;;;;; +1569;CANADIAN SYLLABICS WEST-CREE THWAA;Lo;0;L;;;;;N;;;;; +156A;CANADIAN SYLLABICS TH;Lo;0;L;;;;;N;;;;; +156B;CANADIAN SYLLABICS TTHE;Lo;0;L;;;;;N;;;;; +156C;CANADIAN SYLLABICS TTHI;Lo;0;L;;;;;N;;;;; +156D;CANADIAN SYLLABICS TTHO;Lo;0;L;;;;;N;;;;; +156E;CANADIAN SYLLABICS TTHA;Lo;0;L;;;;;N;;;;; +156F;CANADIAN SYLLABICS TTH;Lo;0;L;;;;;N;;;;; +1570;CANADIAN SYLLABICS TYE;Lo;0;L;;;;;N;;;;; +1571;CANADIAN SYLLABICS TYI;Lo;0;L;;;;;N;;;;; +1572;CANADIAN SYLLABICS TYO;Lo;0;L;;;;;N;;;;; +1573;CANADIAN SYLLABICS TYA;Lo;0;L;;;;;N;;;;; +1574;CANADIAN SYLLABICS NUNAVIK HE;Lo;0;L;;;;;N;;;;; +1575;CANADIAN SYLLABICS NUNAVIK HI;Lo;0;L;;;;;N;;;;; +1576;CANADIAN SYLLABICS NUNAVIK HII;Lo;0;L;;;;;N;;;;; +1577;CANADIAN SYLLABICS NUNAVIK HO;Lo;0;L;;;;;N;;;;; +1578;CANADIAN SYLLABICS NUNAVIK HOO;Lo;0;L;;;;;N;;;;; +1579;CANADIAN SYLLABICS NUNAVIK HA;Lo;0;L;;;;;N;;;;; +157A;CANADIAN SYLLABICS NUNAVIK HAA;Lo;0;L;;;;;N;;;;; +157B;CANADIAN SYLLABICS NUNAVIK H;Lo;0;L;;;;;N;;;;; +157C;CANADIAN SYLLABICS NUNAVUT H;Lo;0;L;;;;;N;;;;; +157D;CANADIAN SYLLABICS HK;Lo;0;L;;;;;N;;;;; +157E;CANADIAN SYLLABICS QAAI;Lo;0;L;;;;;N;;;;; +157F;CANADIAN SYLLABICS QI;Lo;0;L;;;;;N;;;;; +1580;CANADIAN SYLLABICS QII;Lo;0;L;;;;;N;;;;; +1581;CANADIAN SYLLABICS QO;Lo;0;L;;;;;N;;;;; +1582;CANADIAN SYLLABICS QOO;Lo;0;L;;;;;N;;;;; +1583;CANADIAN SYLLABICS QA;Lo;0;L;;;;;N;;;;; +1584;CANADIAN SYLLABICS QAA;Lo;0;L;;;;;N;;;;; +1585;CANADIAN SYLLABICS Q;Lo;0;L;;;;;N;;;;; +1586;CANADIAN SYLLABICS TLHE;Lo;0;L;;;;;N;;;;; +1587;CANADIAN SYLLABICS TLHI;Lo;0;L;;;;;N;;;;; +1588;CANADIAN SYLLABICS TLHO;Lo;0;L;;;;;N;;;;; +1589;CANADIAN SYLLABICS TLHA;Lo;0;L;;;;;N;;;;; +158A;CANADIAN SYLLABICS WEST-CREE RE;Lo;0;L;;;;;N;;;;; +158B;CANADIAN SYLLABICS WEST-CREE RI;Lo;0;L;;;;;N;;;;; +158C;CANADIAN SYLLABICS WEST-CREE RO;Lo;0;L;;;;;N;;;;; +158D;CANADIAN SYLLABICS WEST-CREE RA;Lo;0;L;;;;;N;;;;; +158E;CANADIAN SYLLABICS NGAAI;Lo;0;L;;;;;N;;;;; +158F;CANADIAN SYLLABICS NGI;Lo;0;L;;;;;N;;;;; +1590;CANADIAN SYLLABICS NGII;Lo;0;L;;;;;N;;;;; +1591;CANADIAN SYLLABICS NGO;Lo;0;L;;;;;N;;;;; +1592;CANADIAN SYLLABICS NGOO;Lo;0;L;;;;;N;;;;; +1593;CANADIAN SYLLABICS NGA;Lo;0;L;;;;;N;;;;; +1594;CANADIAN SYLLABICS NGAA;Lo;0;L;;;;;N;;;;; +1595;CANADIAN SYLLABICS NG;Lo;0;L;;;;;N;;;;; +1596;CANADIAN SYLLABICS NNG;Lo;0;L;;;;;N;;;;; +1597;CANADIAN SYLLABICS SAYISI SHE;Lo;0;L;;;;;N;;;;; +1598;CANADIAN SYLLABICS SAYISI SHI;Lo;0;L;;;;;N;;;;; +1599;CANADIAN SYLLABICS SAYISI SHO;Lo;0;L;;;;;N;;;;; +159A;CANADIAN SYLLABICS SAYISI SHA;Lo;0;L;;;;;N;;;;; +159B;CANADIAN SYLLABICS WOODS-CREE THE;Lo;0;L;;;;;N;;;;; +159C;CANADIAN SYLLABICS WOODS-CREE THI;Lo;0;L;;;;;N;;;;; +159D;CANADIAN SYLLABICS WOODS-CREE THO;Lo;0;L;;;;;N;;;;; +159E;CANADIAN SYLLABICS WOODS-CREE THA;Lo;0;L;;;;;N;;;;; +159F;CANADIAN SYLLABICS WOODS-CREE TH;Lo;0;L;;;;;N;;;;; +15A0;CANADIAN SYLLABICS LHI;Lo;0;L;;;;;N;;;;; +15A1;CANADIAN SYLLABICS LHII;Lo;0;L;;;;;N;;;;; +15A2;CANADIAN SYLLABICS LHO;Lo;0;L;;;;;N;;;;; +15A3;CANADIAN SYLLABICS LHOO;Lo;0;L;;;;;N;;;;; +15A4;CANADIAN SYLLABICS LHA;Lo;0;L;;;;;N;;;;; +15A5;CANADIAN SYLLABICS LHAA;Lo;0;L;;;;;N;;;;; +15A6;CANADIAN SYLLABICS LH;Lo;0;L;;;;;N;;;;; +15A7;CANADIAN SYLLABICS TH-CREE THE;Lo;0;L;;;;;N;;;;; +15A8;CANADIAN SYLLABICS TH-CREE THI;Lo;0;L;;;;;N;;;;; +15A9;CANADIAN SYLLABICS TH-CREE THII;Lo;0;L;;;;;N;;;;; +15AA;CANADIAN SYLLABICS TH-CREE THO;Lo;0;L;;;;;N;;;;; +15AB;CANADIAN SYLLABICS TH-CREE THOO;Lo;0;L;;;;;N;;;;; +15AC;CANADIAN SYLLABICS TH-CREE THA;Lo;0;L;;;;;N;;;;; +15AD;CANADIAN SYLLABICS TH-CREE THAA;Lo;0;L;;;;;N;;;;; +15AE;CANADIAN SYLLABICS TH-CREE TH;Lo;0;L;;;;;N;;;;; +15AF;CANADIAN SYLLABICS AIVILIK B;Lo;0;L;;;;;N;;;;; +15B0;CANADIAN SYLLABICS BLACKFOOT E;Lo;0;L;;;;;N;;;;; +15B1;CANADIAN SYLLABICS BLACKFOOT I;Lo;0;L;;;;;N;;;;; +15B2;CANADIAN SYLLABICS BLACKFOOT O;Lo;0;L;;;;;N;;;;; +15B3;CANADIAN SYLLABICS BLACKFOOT A;Lo;0;L;;;;;N;;;;; +15B4;CANADIAN SYLLABICS BLACKFOOT WE;Lo;0;L;;;;;N;;;;; +15B5;CANADIAN SYLLABICS BLACKFOOT WI;Lo;0;L;;;;;N;;;;; +15B6;CANADIAN SYLLABICS BLACKFOOT WO;Lo;0;L;;;;;N;;;;; +15B7;CANADIAN SYLLABICS BLACKFOOT WA;Lo;0;L;;;;;N;;;;; +15B8;CANADIAN SYLLABICS BLACKFOOT NE;Lo;0;L;;;;;N;;;;; +15B9;CANADIAN SYLLABICS BLACKFOOT NI;Lo;0;L;;;;;N;;;;; +15BA;CANADIAN SYLLABICS BLACKFOOT NO;Lo;0;L;;;;;N;;;;; +15BB;CANADIAN SYLLABICS BLACKFOOT NA;Lo;0;L;;;;;N;;;;; +15BC;CANADIAN SYLLABICS BLACKFOOT KE;Lo;0;L;;;;;N;;;;; +15BD;CANADIAN SYLLABICS BLACKFOOT KI;Lo;0;L;;;;;N;;;;; +15BE;CANADIAN SYLLABICS BLACKFOOT KO;Lo;0;L;;;;;N;;;;; +15BF;CANADIAN SYLLABICS BLACKFOOT KA;Lo;0;L;;;;;N;;;;; +15C0;CANADIAN SYLLABICS SAYISI HE;Lo;0;L;;;;;N;;;;; +15C1;CANADIAN SYLLABICS SAYISI HI;Lo;0;L;;;;;N;;;;; +15C2;CANADIAN SYLLABICS SAYISI HO;Lo;0;L;;;;;N;;;;; +15C3;CANADIAN SYLLABICS SAYISI HA;Lo;0;L;;;;;N;;;;; +15C4;CANADIAN SYLLABICS CARRIER GHU;Lo;0;L;;;;;N;;;;; +15C5;CANADIAN SYLLABICS CARRIER GHO;Lo;0;L;;;;;N;;;;; +15C6;CANADIAN SYLLABICS CARRIER GHE;Lo;0;L;;;;;N;;;;; +15C7;CANADIAN SYLLABICS CARRIER GHEE;Lo;0;L;;;;;N;;;;; +15C8;CANADIAN SYLLABICS CARRIER GHI;Lo;0;L;;;;;N;;;;; +15C9;CANADIAN SYLLABICS CARRIER GHA;Lo;0;L;;;;;N;;;;; +15CA;CANADIAN SYLLABICS CARRIER RU;Lo;0;L;;;;;N;;;;; +15CB;CANADIAN SYLLABICS CARRIER RO;Lo;0;L;;;;;N;;;;; +15CC;CANADIAN SYLLABICS CARRIER RE;Lo;0;L;;;;;N;;;;; +15CD;CANADIAN SYLLABICS CARRIER REE;Lo;0;L;;;;;N;;;;; +15CE;CANADIAN SYLLABICS CARRIER RI;Lo;0;L;;;;;N;;;;; +15CF;CANADIAN SYLLABICS CARRIER RA;Lo;0;L;;;;;N;;;;; +15D0;CANADIAN SYLLABICS CARRIER WU;Lo;0;L;;;;;N;;;;; +15D1;CANADIAN SYLLABICS CARRIER WO;Lo;0;L;;;;;N;;;;; +15D2;CANADIAN SYLLABICS CARRIER WE;Lo;0;L;;;;;N;;;;; +15D3;CANADIAN SYLLABICS CARRIER WEE;Lo;0;L;;;;;N;;;;; +15D4;CANADIAN SYLLABICS CARRIER WI;Lo;0;L;;;;;N;;;;; +15D5;CANADIAN SYLLABICS CARRIER WA;Lo;0;L;;;;;N;;;;; +15D6;CANADIAN SYLLABICS CARRIER HWU;Lo;0;L;;;;;N;;;;; +15D7;CANADIAN SYLLABICS CARRIER HWO;Lo;0;L;;;;;N;;;;; +15D8;CANADIAN SYLLABICS CARRIER HWE;Lo;0;L;;;;;N;;;;; +15D9;CANADIAN SYLLABICS CARRIER HWEE;Lo;0;L;;;;;N;;;;; +15DA;CANADIAN SYLLABICS CARRIER HWI;Lo;0;L;;;;;N;;;;; +15DB;CANADIAN SYLLABICS CARRIER HWA;Lo;0;L;;;;;N;;;;; +15DC;CANADIAN SYLLABICS CARRIER THU;Lo;0;L;;;;;N;;;;; +15DD;CANADIAN SYLLABICS CARRIER THO;Lo;0;L;;;;;N;;;;; +15DE;CANADIAN SYLLABICS CARRIER THE;Lo;0;L;;;;;N;;;;; +15DF;CANADIAN SYLLABICS CARRIER THEE;Lo;0;L;;;;;N;;;;; +15E0;CANADIAN SYLLABICS CARRIER THI;Lo;0;L;;;;;N;;;;; +15E1;CANADIAN SYLLABICS CARRIER THA;Lo;0;L;;;;;N;;;;; +15E2;CANADIAN SYLLABICS CARRIER TTU;Lo;0;L;;;;;N;;;;; +15E3;CANADIAN SYLLABICS CARRIER TTO;Lo;0;L;;;;;N;;;;; +15E4;CANADIAN SYLLABICS CARRIER TTE;Lo;0;L;;;;;N;;;;; +15E5;CANADIAN SYLLABICS CARRIER TTEE;Lo;0;L;;;;;N;;;;; +15E6;CANADIAN SYLLABICS CARRIER TTI;Lo;0;L;;;;;N;;;;; +15E7;CANADIAN SYLLABICS CARRIER TTA;Lo;0;L;;;;;N;;;;; +15E8;CANADIAN SYLLABICS CARRIER PU;Lo;0;L;;;;;N;;;;; +15E9;CANADIAN SYLLABICS CARRIER PO;Lo;0;L;;;;;N;;;;; +15EA;CANADIAN SYLLABICS CARRIER PE;Lo;0;L;;;;;N;;;;; +15EB;CANADIAN SYLLABICS CARRIER PEE;Lo;0;L;;;;;N;;;;; +15EC;CANADIAN SYLLABICS CARRIER PI;Lo;0;L;;;;;N;;;;; +15ED;CANADIAN SYLLABICS CARRIER PA;Lo;0;L;;;;;N;;;;; +15EE;CANADIAN SYLLABICS CARRIER P;Lo;0;L;;;;;N;;;;; +15EF;CANADIAN SYLLABICS CARRIER GU;Lo;0;L;;;;;N;;;;; +15F0;CANADIAN SYLLABICS CARRIER GO;Lo;0;L;;;;;N;;;;; +15F1;CANADIAN SYLLABICS CARRIER GE;Lo;0;L;;;;;N;;;;; +15F2;CANADIAN SYLLABICS CARRIER GEE;Lo;0;L;;;;;N;;;;; +15F3;CANADIAN SYLLABICS CARRIER GI;Lo;0;L;;;;;N;;;;; +15F4;CANADIAN SYLLABICS CARRIER GA;Lo;0;L;;;;;N;;;;; +15F5;CANADIAN SYLLABICS CARRIER KHU;Lo;0;L;;;;;N;;;;; +15F6;CANADIAN SYLLABICS CARRIER KHO;Lo;0;L;;;;;N;;;;; +15F7;CANADIAN SYLLABICS CARRIER KHE;Lo;0;L;;;;;N;;;;; +15F8;CANADIAN SYLLABICS CARRIER KHEE;Lo;0;L;;;;;N;;;;; +15F9;CANADIAN SYLLABICS CARRIER KHI;Lo;0;L;;;;;N;;;;; +15FA;CANADIAN SYLLABICS CARRIER KHA;Lo;0;L;;;;;N;;;;; +15FB;CANADIAN SYLLABICS CARRIER KKU;Lo;0;L;;;;;N;;;;; +15FC;CANADIAN SYLLABICS CARRIER KKO;Lo;0;L;;;;;N;;;;; +15FD;CANADIAN SYLLABICS CARRIER KKE;Lo;0;L;;;;;N;;;;; +15FE;CANADIAN SYLLABICS CARRIER KKEE;Lo;0;L;;;;;N;;;;; +15FF;CANADIAN SYLLABICS CARRIER KKI;Lo;0;L;;;;;N;;;;; +1600;CANADIAN SYLLABICS CARRIER KKA;Lo;0;L;;;;;N;;;;; +1601;CANADIAN SYLLABICS CARRIER KK;Lo;0;L;;;;;N;;;;; +1602;CANADIAN SYLLABICS CARRIER NU;Lo;0;L;;;;;N;;;;; +1603;CANADIAN SYLLABICS CARRIER NO;Lo;0;L;;;;;N;;;;; +1604;CANADIAN SYLLABICS CARRIER NE;Lo;0;L;;;;;N;;;;; +1605;CANADIAN SYLLABICS CARRIER NEE;Lo;0;L;;;;;N;;;;; +1606;CANADIAN SYLLABICS CARRIER NI;Lo;0;L;;;;;N;;;;; +1607;CANADIAN SYLLABICS CARRIER NA;Lo;0;L;;;;;N;;;;; +1608;CANADIAN SYLLABICS CARRIER MU;Lo;0;L;;;;;N;;;;; +1609;CANADIAN SYLLABICS CARRIER MO;Lo;0;L;;;;;N;;;;; +160A;CANADIAN SYLLABICS CARRIER ME;Lo;0;L;;;;;N;;;;; +160B;CANADIAN SYLLABICS CARRIER MEE;Lo;0;L;;;;;N;;;;; +160C;CANADIAN SYLLABICS CARRIER MI;Lo;0;L;;;;;N;;;;; +160D;CANADIAN SYLLABICS CARRIER MA;Lo;0;L;;;;;N;;;;; +160E;CANADIAN SYLLABICS CARRIER YU;Lo;0;L;;;;;N;;;;; +160F;CANADIAN SYLLABICS CARRIER YO;Lo;0;L;;;;;N;;;;; +1610;CANADIAN SYLLABICS CARRIER YE;Lo;0;L;;;;;N;;;;; +1611;CANADIAN SYLLABICS CARRIER YEE;Lo;0;L;;;;;N;;;;; +1612;CANADIAN SYLLABICS CARRIER YI;Lo;0;L;;;;;N;;;;; +1613;CANADIAN SYLLABICS CARRIER YA;Lo;0;L;;;;;N;;;;; +1614;CANADIAN SYLLABICS CARRIER JU;Lo;0;L;;;;;N;;;;; +1615;CANADIAN SYLLABICS SAYISI JU;Lo;0;L;;;;;N;;;;; +1616;CANADIAN SYLLABICS CARRIER JO;Lo;0;L;;;;;N;;;;; +1617;CANADIAN SYLLABICS CARRIER JE;Lo;0;L;;;;;N;;;;; +1618;CANADIAN SYLLABICS CARRIER JEE;Lo;0;L;;;;;N;;;;; +1619;CANADIAN SYLLABICS CARRIER JI;Lo;0;L;;;;;N;;;;; +161A;CANADIAN SYLLABICS SAYISI JI;Lo;0;L;;;;;N;;;;; +161B;CANADIAN SYLLABICS CARRIER JA;Lo;0;L;;;;;N;;;;; +161C;CANADIAN SYLLABICS CARRIER JJU;Lo;0;L;;;;;N;;;;; +161D;CANADIAN SYLLABICS CARRIER JJO;Lo;0;L;;;;;N;;;;; +161E;CANADIAN SYLLABICS CARRIER JJE;Lo;0;L;;;;;N;;;;; +161F;CANADIAN SYLLABICS CARRIER JJEE;Lo;0;L;;;;;N;;;;; +1620;CANADIAN SYLLABICS CARRIER JJI;Lo;0;L;;;;;N;;;;; +1621;CANADIAN SYLLABICS CARRIER JJA;Lo;0;L;;;;;N;;;;; +1622;CANADIAN SYLLABICS CARRIER LU;Lo;0;L;;;;;N;;;;; +1623;CANADIAN SYLLABICS CARRIER LO;Lo;0;L;;;;;N;;;;; +1624;CANADIAN SYLLABICS CARRIER LE;Lo;0;L;;;;;N;;;;; +1625;CANADIAN SYLLABICS CARRIER LEE;Lo;0;L;;;;;N;;;;; +1626;CANADIAN SYLLABICS CARRIER LI;Lo;0;L;;;;;N;;;;; +1627;CANADIAN SYLLABICS CARRIER LA;Lo;0;L;;;;;N;;;;; +1628;CANADIAN SYLLABICS CARRIER DLU;Lo;0;L;;;;;N;;;;; +1629;CANADIAN SYLLABICS CARRIER DLO;Lo;0;L;;;;;N;;;;; +162A;CANADIAN SYLLABICS CARRIER DLE;Lo;0;L;;;;;N;;;;; +162B;CANADIAN SYLLABICS CARRIER DLEE;Lo;0;L;;;;;N;;;;; +162C;CANADIAN SYLLABICS CARRIER DLI;Lo;0;L;;;;;N;;;;; +162D;CANADIAN SYLLABICS CARRIER DLA;Lo;0;L;;;;;N;;;;; +162E;CANADIAN SYLLABICS CARRIER LHU;Lo;0;L;;;;;N;;;;; +162F;CANADIAN SYLLABICS CARRIER LHO;Lo;0;L;;;;;N;;;;; +1630;CANADIAN SYLLABICS CARRIER LHE;Lo;0;L;;;;;N;;;;; +1631;CANADIAN SYLLABICS CARRIER LHEE;Lo;0;L;;;;;N;;;;; +1632;CANADIAN SYLLABICS CARRIER LHI;Lo;0;L;;;;;N;;;;; +1633;CANADIAN SYLLABICS CARRIER LHA;Lo;0;L;;;;;N;;;;; +1634;CANADIAN SYLLABICS CARRIER TLHU;Lo;0;L;;;;;N;;;;; +1635;CANADIAN SYLLABICS CARRIER TLHO;Lo;0;L;;;;;N;;;;; +1636;CANADIAN SYLLABICS CARRIER TLHE;Lo;0;L;;;;;N;;;;; +1637;CANADIAN SYLLABICS CARRIER TLHEE;Lo;0;L;;;;;N;;;;; +1638;CANADIAN SYLLABICS CARRIER TLHI;Lo;0;L;;;;;N;;;;; +1639;CANADIAN SYLLABICS CARRIER TLHA;Lo;0;L;;;;;N;;;;; +163A;CANADIAN SYLLABICS CARRIER TLU;Lo;0;L;;;;;N;;;;; +163B;CANADIAN SYLLABICS CARRIER TLO;Lo;0;L;;;;;N;;;;; +163C;CANADIAN SYLLABICS CARRIER TLE;Lo;0;L;;;;;N;;;;; +163D;CANADIAN SYLLABICS CARRIER TLEE;Lo;0;L;;;;;N;;;;; +163E;CANADIAN SYLLABICS CARRIER TLI;Lo;0;L;;;;;N;;;;; +163F;CANADIAN SYLLABICS CARRIER TLA;Lo;0;L;;;;;N;;;;; +1640;CANADIAN SYLLABICS CARRIER ZU;Lo;0;L;;;;;N;;;;; +1641;CANADIAN SYLLABICS CARRIER ZO;Lo;0;L;;;;;N;;;;; +1642;CANADIAN SYLLABICS CARRIER ZE;Lo;0;L;;;;;N;;;;; +1643;CANADIAN SYLLABICS CARRIER ZEE;Lo;0;L;;;;;N;;;;; +1644;CANADIAN SYLLABICS CARRIER ZI;Lo;0;L;;;;;N;;;;; +1645;CANADIAN SYLLABICS CARRIER ZA;Lo;0;L;;;;;N;;;;; +1646;CANADIAN SYLLABICS CARRIER Z;Lo;0;L;;;;;N;;;;; +1647;CANADIAN SYLLABICS CARRIER INITIAL Z;Lo;0;L;;;;;N;;;;; +1648;CANADIAN SYLLABICS CARRIER DZU;Lo;0;L;;;;;N;;;;; +1649;CANADIAN SYLLABICS CARRIER DZO;Lo;0;L;;;;;N;;;;; +164A;CANADIAN SYLLABICS CARRIER DZE;Lo;0;L;;;;;N;;;;; +164B;CANADIAN SYLLABICS CARRIER DZEE;Lo;0;L;;;;;N;;;;; +164C;CANADIAN SYLLABICS CARRIER DZI;Lo;0;L;;;;;N;;;;; +164D;CANADIAN SYLLABICS CARRIER DZA;Lo;0;L;;;;;N;;;;; +164E;CANADIAN SYLLABICS CARRIER SU;Lo;0;L;;;;;N;;;;; +164F;CANADIAN SYLLABICS CARRIER SO;Lo;0;L;;;;;N;;;;; +1650;CANADIAN SYLLABICS CARRIER SE;Lo;0;L;;;;;N;;;;; +1651;CANADIAN SYLLABICS CARRIER SEE;Lo;0;L;;;;;N;;;;; +1652;CANADIAN SYLLABICS CARRIER SI;Lo;0;L;;;;;N;;;;; +1653;CANADIAN SYLLABICS CARRIER SA;Lo;0;L;;;;;N;;;;; +1654;CANADIAN SYLLABICS CARRIER SHU;Lo;0;L;;;;;N;;;;; +1655;CANADIAN SYLLABICS CARRIER SHO;Lo;0;L;;;;;N;;;;; +1656;CANADIAN SYLLABICS CARRIER SHE;Lo;0;L;;;;;N;;;;; +1657;CANADIAN SYLLABICS CARRIER SHEE;Lo;0;L;;;;;N;;;;; +1658;CANADIAN SYLLABICS CARRIER SHI;Lo;0;L;;;;;N;;;;; +1659;CANADIAN SYLLABICS CARRIER SHA;Lo;0;L;;;;;N;;;;; +165A;CANADIAN SYLLABICS CARRIER SH;Lo;0;L;;;;;N;;;;; +165B;CANADIAN SYLLABICS CARRIER TSU;Lo;0;L;;;;;N;;;;; +165C;CANADIAN SYLLABICS CARRIER TSO;Lo;0;L;;;;;N;;;;; +165D;CANADIAN SYLLABICS CARRIER TSE;Lo;0;L;;;;;N;;;;; +165E;CANADIAN SYLLABICS CARRIER TSEE;Lo;0;L;;;;;N;;;;; +165F;CANADIAN SYLLABICS CARRIER TSI;Lo;0;L;;;;;N;;;;; +1660;CANADIAN SYLLABICS CARRIER TSA;Lo;0;L;;;;;N;;;;; +1661;CANADIAN SYLLABICS CARRIER CHU;Lo;0;L;;;;;N;;;;; +1662;CANADIAN SYLLABICS CARRIER CHO;Lo;0;L;;;;;N;;;;; +1663;CANADIAN SYLLABICS CARRIER CHE;Lo;0;L;;;;;N;;;;; +1664;CANADIAN SYLLABICS CARRIER CHEE;Lo;0;L;;;;;N;;;;; +1665;CANADIAN SYLLABICS CARRIER CHI;Lo;0;L;;;;;N;;;;; +1666;CANADIAN SYLLABICS CARRIER CHA;Lo;0;L;;;;;N;;;;; +1667;CANADIAN SYLLABICS CARRIER TTSU;Lo;0;L;;;;;N;;;;; +1668;CANADIAN SYLLABICS CARRIER TTSO;Lo;0;L;;;;;N;;;;; +1669;CANADIAN SYLLABICS CARRIER TTSE;Lo;0;L;;;;;N;;;;; +166A;CANADIAN SYLLABICS CARRIER TTSEE;Lo;0;L;;;;;N;;;;; +166B;CANADIAN SYLLABICS CARRIER TTSI;Lo;0;L;;;;;N;;;;; +166C;CANADIAN SYLLABICS CARRIER TTSA;Lo;0;L;;;;;N;;;;; +166D;CANADIAN SYLLABICS CHI SIGN;Po;0;L;;;;;N;;;;; +166E;CANADIAN SYLLABICS FULL STOP;Po;0;L;;;;;N;;;;; +166F;CANADIAN SYLLABICS QAI;Lo;0;L;;;;;N;;;;; +1670;CANADIAN SYLLABICS NGAI;Lo;0;L;;;;;N;;;;; +1671;CANADIAN SYLLABICS NNGI;Lo;0;L;;;;;N;;;;; +1672;CANADIAN SYLLABICS NNGII;Lo;0;L;;;;;N;;;;; +1673;CANADIAN SYLLABICS NNGO;Lo;0;L;;;;;N;;;;; +1674;CANADIAN SYLLABICS NNGOO;Lo;0;L;;;;;N;;;;; +1675;CANADIAN SYLLABICS NNGA;Lo;0;L;;;;;N;;;;; +1676;CANADIAN SYLLABICS NNGAA;Lo;0;L;;;;;N;;;;; +1677;CANADIAN SYLLABICS WOODS-CREE THWEE;Lo;0;L;;;;;N;;;;; +1678;CANADIAN SYLLABICS WOODS-CREE THWI;Lo;0;L;;;;;N;;;;; +1679;CANADIAN SYLLABICS WOODS-CREE THWII;Lo;0;L;;;;;N;;;;; +167A;CANADIAN SYLLABICS WOODS-CREE THWO;Lo;0;L;;;;;N;;;;; +167B;CANADIAN SYLLABICS WOODS-CREE THWOO;Lo;0;L;;;;;N;;;;; +167C;CANADIAN SYLLABICS WOODS-CREE THWA;Lo;0;L;;;;;N;;;;; +167D;CANADIAN SYLLABICS WOODS-CREE THWAA;Lo;0;L;;;;;N;;;;; +167E;CANADIAN SYLLABICS WOODS-CREE FINAL TH;Lo;0;L;;;;;N;;;;; +167F;CANADIAN SYLLABICS BLACKFOOT W;Lo;0;L;;;;;N;;;;; +1680;OGHAM SPACE MARK;Zs;0;WS;;;;;N;;;;; +1681;OGHAM LETTER BEITH;Lo;0;L;;;;;N;;;;; +1682;OGHAM LETTER LUIS;Lo;0;L;;;;;N;;;;; +1683;OGHAM LETTER FEARN;Lo;0;L;;;;;N;;;;; +1684;OGHAM LETTER SAIL;Lo;0;L;;;;;N;;;;; +1685;OGHAM LETTER NION;Lo;0;L;;;;;N;;;;; +1686;OGHAM LETTER UATH;Lo;0;L;;;;;N;;;;; +1687;OGHAM LETTER DAIR;Lo;0;L;;;;;N;;;;; +1688;OGHAM LETTER TINNE;Lo;0;L;;;;;N;;;;; +1689;OGHAM LETTER COLL;Lo;0;L;;;;;N;;;;; +168A;OGHAM LETTER CEIRT;Lo;0;L;;;;;N;;;;; +168B;OGHAM LETTER MUIN;Lo;0;L;;;;;N;;;;; +168C;OGHAM LETTER GORT;Lo;0;L;;;;;N;;;;; +168D;OGHAM LETTER NGEADAL;Lo;0;L;;;;;N;;;;; +168E;OGHAM LETTER STRAIF;Lo;0;L;;;;;N;;;;; +168F;OGHAM LETTER RUIS;Lo;0;L;;;;;N;;;;; +1690;OGHAM LETTER AILM;Lo;0;L;;;;;N;;;;; +1691;OGHAM LETTER ONN;Lo;0;L;;;;;N;;;;; +1692;OGHAM LETTER UR;Lo;0;L;;;;;N;;;;; +1693;OGHAM LETTER EADHADH;Lo;0;L;;;;;N;;;;; +1694;OGHAM LETTER IODHADH;Lo;0;L;;;;;N;;;;; +1695;OGHAM LETTER EABHADH;Lo;0;L;;;;;N;;;;; +1696;OGHAM LETTER OR;Lo;0;L;;;;;N;;;;; +1697;OGHAM LETTER UILLEANN;Lo;0;L;;;;;N;;;;; +1698;OGHAM LETTER IFIN;Lo;0;L;;;;;N;;;;; +1699;OGHAM LETTER EAMHANCHOLL;Lo;0;L;;;;;N;;;;; +169A;OGHAM LETTER PEITH;Lo;0;L;;;;;N;;;;; +169B;OGHAM FEATHER MARK;Ps;0;ON;;;;;Y;;;;; +169C;OGHAM REVERSED FEATHER MARK;Pe;0;ON;;;;;Y;;;;; +16A0;RUNIC LETTER FEHU FEOH FE F;Lo;0;L;;;;;N;;;;; +16A1;RUNIC LETTER V;Lo;0;L;;;;;N;;;;; +16A2;RUNIC LETTER URUZ UR U;Lo;0;L;;;;;N;;;;; +16A3;RUNIC LETTER YR;Lo;0;L;;;;;N;;;;; +16A4;RUNIC LETTER Y;Lo;0;L;;;;;N;;;;; +16A5;RUNIC LETTER W;Lo;0;L;;;;;N;;;;; +16A6;RUNIC LETTER THURISAZ THURS THORN;Lo;0;L;;;;;N;;;;; +16A7;RUNIC LETTER ETH;Lo;0;L;;;;;N;;;;; +16A8;RUNIC LETTER ANSUZ A;Lo;0;L;;;;;N;;;;; +16A9;RUNIC LETTER OS O;Lo;0;L;;;;;N;;;;; +16AA;RUNIC LETTER AC A;Lo;0;L;;;;;N;;;;; +16AB;RUNIC LETTER AESC;Lo;0;L;;;;;N;;;;; +16AC;RUNIC LETTER LONG-BRANCH-OSS O;Lo;0;L;;;;;N;;;;; +16AD;RUNIC LETTER SHORT-TWIG-OSS O;Lo;0;L;;;;;N;;;;; +16AE;RUNIC LETTER O;Lo;0;L;;;;;N;;;;; +16AF;RUNIC LETTER OE;Lo;0;L;;;;;N;;;;; +16B0;RUNIC LETTER ON;Lo;0;L;;;;;N;;;;; +16B1;RUNIC LETTER RAIDO RAD REID R;Lo;0;L;;;;;N;;;;; +16B2;RUNIC LETTER KAUNA;Lo;0;L;;;;;N;;;;; +16B3;RUNIC LETTER CEN;Lo;0;L;;;;;N;;;;; +16B4;RUNIC LETTER KAUN K;Lo;0;L;;;;;N;;;;; +16B5;RUNIC LETTER G;Lo;0;L;;;;;N;;;;; +16B6;RUNIC LETTER ENG;Lo;0;L;;;;;N;;;;; +16B7;RUNIC LETTER GEBO GYFU G;Lo;0;L;;;;;N;;;;; +16B8;RUNIC LETTER GAR;Lo;0;L;;;;;N;;;;; +16B9;RUNIC LETTER WUNJO WYNN W;Lo;0;L;;;;;N;;;;; +16BA;RUNIC LETTER HAGLAZ H;Lo;0;L;;;;;N;;;;; +16BB;RUNIC LETTER HAEGL H;Lo;0;L;;;;;N;;;;; +16BC;RUNIC LETTER LONG-BRANCH-HAGALL H;Lo;0;L;;;;;N;;;;; +16BD;RUNIC LETTER SHORT-TWIG-HAGALL H;Lo;0;L;;;;;N;;;;; +16BE;RUNIC LETTER NAUDIZ NYD NAUD N;Lo;0;L;;;;;N;;;;; +16BF;RUNIC LETTER SHORT-TWIG-NAUD N;Lo;0;L;;;;;N;;;;; +16C0;RUNIC LETTER DOTTED-N;Lo;0;L;;;;;N;;;;; +16C1;RUNIC LETTER ISAZ IS ISS I;Lo;0;L;;;;;N;;;;; +16C2;RUNIC LETTER E;Lo;0;L;;;;;N;;;;; +16C3;RUNIC LETTER JERAN J;Lo;0;L;;;;;N;;;;; +16C4;RUNIC LETTER GER;Lo;0;L;;;;;N;;;;; +16C5;RUNIC LETTER LONG-BRANCH-AR AE;Lo;0;L;;;;;N;;;;; +16C6;RUNIC LETTER SHORT-TWIG-AR A;Lo;0;L;;;;;N;;;;; +16C7;RUNIC LETTER IWAZ EOH;Lo;0;L;;;;;N;;;;; +16C8;RUNIC LETTER PERTHO PEORTH P;Lo;0;L;;;;;N;;;;; +16C9;RUNIC LETTER ALGIZ EOLHX;Lo;0;L;;;;;N;;;;; +16CA;RUNIC LETTER SOWILO S;Lo;0;L;;;;;N;;;;; +16CB;RUNIC LETTER SIGEL LONG-BRANCH-SOL S;Lo;0;L;;;;;N;;;;; +16CC;RUNIC LETTER SHORT-TWIG-SOL S;Lo;0;L;;;;;N;;;;; +16CD;RUNIC LETTER C;Lo;0;L;;;;;N;;;;; +16CE;RUNIC LETTER Z;Lo;0;L;;;;;N;;;;; +16CF;RUNIC LETTER TIWAZ TIR TYR T;Lo;0;L;;;;;N;;;;; +16D0;RUNIC LETTER SHORT-TWIG-TYR T;Lo;0;L;;;;;N;;;;; +16D1;RUNIC LETTER D;Lo;0;L;;;;;N;;;;; +16D2;RUNIC LETTER BERKANAN BEORC BJARKAN B;Lo;0;L;;;;;N;;;;; +16D3;RUNIC LETTER SHORT-TWIG-BJARKAN B;Lo;0;L;;;;;N;;;;; +16D4;RUNIC LETTER DOTTED-P;Lo;0;L;;;;;N;;;;; +16D5;RUNIC LETTER OPEN-P;Lo;0;L;;;;;N;;;;; +16D6;RUNIC LETTER EHWAZ EH E;Lo;0;L;;;;;N;;;;; +16D7;RUNIC LETTER MANNAZ MAN M;Lo;0;L;;;;;N;;;;; +16D8;RUNIC LETTER LONG-BRANCH-MADR M;Lo;0;L;;;;;N;;;;; +16D9;RUNIC LETTER SHORT-TWIG-MADR M;Lo;0;L;;;;;N;;;;; +16DA;RUNIC LETTER LAUKAZ LAGU LOGR L;Lo;0;L;;;;;N;;;;; +16DB;RUNIC LETTER DOTTED-L;Lo;0;L;;;;;N;;;;; +16DC;RUNIC LETTER INGWAZ;Lo;0;L;;;;;N;;;;; +16DD;RUNIC LETTER ING;Lo;0;L;;;;;N;;;;; +16DE;RUNIC LETTER DAGAZ DAEG D;Lo;0;L;;;;;N;;;;; +16DF;RUNIC LETTER OTHALAN ETHEL O;Lo;0;L;;;;;N;;;;; +16E0;RUNIC LETTER EAR;Lo;0;L;;;;;N;;;;; +16E1;RUNIC LETTER IOR;Lo;0;L;;;;;N;;;;; +16E2;RUNIC LETTER CWEORTH;Lo;0;L;;;;;N;;;;; +16E3;RUNIC LETTER CALC;Lo;0;L;;;;;N;;;;; +16E4;RUNIC LETTER CEALC;Lo;0;L;;;;;N;;;;; +16E5;RUNIC LETTER STAN;Lo;0;L;;;;;N;;;;; +16E6;RUNIC LETTER LONG-BRANCH-YR;Lo;0;L;;;;;N;;;;; +16E7;RUNIC LETTER SHORT-TWIG-YR;Lo;0;L;;;;;N;;;;; +16E8;RUNIC LETTER ICELANDIC-YR;Lo;0;L;;;;;N;;;;; +16E9;RUNIC LETTER Q;Lo;0;L;;;;;N;;;;; +16EA;RUNIC LETTER X;Lo;0;L;;;;;N;;;;; +16EB;RUNIC SINGLE PUNCTUATION;Po;0;L;;;;;N;;;;; +16EC;RUNIC MULTIPLE PUNCTUATION;Po;0;L;;;;;N;;;;; +16ED;RUNIC CROSS PUNCTUATION;Po;0;L;;;;;N;;;;; +16EE;RUNIC ARLAUG SYMBOL;Nl;0;L;;;;17;N;;;;; +16EF;RUNIC TVIMADUR SYMBOL;Nl;0;L;;;;18;N;;;;; +16F0;RUNIC BELGTHOR SYMBOL;Nl;0;L;;;;19;N;;;;; +16F1;RUNIC LETTER K;Lo;0;L;;;;;N;;;;; +16F2;RUNIC LETTER SH;Lo;0;L;;;;;N;;;;; +16F3;RUNIC LETTER OO;Lo;0;L;;;;;N;;;;; +16F4;RUNIC LETTER FRANKS CASKET OS;Lo;0;L;;;;;N;;;;; +16F5;RUNIC LETTER FRANKS CASKET IS;Lo;0;L;;;;;N;;;;; +16F6;RUNIC LETTER FRANKS CASKET EH;Lo;0;L;;;;;N;;;;; +16F7;RUNIC LETTER FRANKS CASKET AC;Lo;0;L;;;;;N;;;;; +16F8;RUNIC LETTER FRANKS CASKET AESC;Lo;0;L;;;;;N;;;;; +1700;TAGALOG LETTER A;Lo;0;L;;;;;N;;;;; +1701;TAGALOG LETTER I;Lo;0;L;;;;;N;;;;; +1702;TAGALOG LETTER U;Lo;0;L;;;;;N;;;;; +1703;TAGALOG LETTER KA;Lo;0;L;;;;;N;;;;; +1704;TAGALOG LETTER GA;Lo;0;L;;;;;N;;;;; +1705;TAGALOG LETTER NGA;Lo;0;L;;;;;N;;;;; +1706;TAGALOG LETTER TA;Lo;0;L;;;;;N;;;;; +1707;TAGALOG LETTER DA;Lo;0;L;;;;;N;;;;; +1708;TAGALOG LETTER NA;Lo;0;L;;;;;N;;;;; +1709;TAGALOG LETTER PA;Lo;0;L;;;;;N;;;;; +170A;TAGALOG LETTER BA;Lo;0;L;;;;;N;;;;; +170B;TAGALOG LETTER MA;Lo;0;L;;;;;N;;;;; +170C;TAGALOG LETTER YA;Lo;0;L;;;;;N;;;;; +170E;TAGALOG LETTER LA;Lo;0;L;;;;;N;;;;; +170F;TAGALOG LETTER WA;Lo;0;L;;;;;N;;;;; +1710;TAGALOG LETTER SA;Lo;0;L;;;;;N;;;;; +1711;TAGALOG LETTER HA;Lo;0;L;;;;;N;;;;; +1712;TAGALOG VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +1713;TAGALOG VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1714;TAGALOG SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +1720;HANUNOO LETTER A;Lo;0;L;;;;;N;;;;; +1721;HANUNOO LETTER I;Lo;0;L;;;;;N;;;;; +1722;HANUNOO LETTER U;Lo;0;L;;;;;N;;;;; +1723;HANUNOO LETTER KA;Lo;0;L;;;;;N;;;;; +1724;HANUNOO LETTER GA;Lo;0;L;;;;;N;;;;; +1725;HANUNOO LETTER NGA;Lo;0;L;;;;;N;;;;; +1726;HANUNOO LETTER TA;Lo;0;L;;;;;N;;;;; +1727;HANUNOO LETTER DA;Lo;0;L;;;;;N;;;;; +1728;HANUNOO LETTER NA;Lo;0;L;;;;;N;;;;; +1729;HANUNOO LETTER PA;Lo;0;L;;;;;N;;;;; +172A;HANUNOO LETTER BA;Lo;0;L;;;;;N;;;;; +172B;HANUNOO LETTER MA;Lo;0;L;;;;;N;;;;; +172C;HANUNOO LETTER YA;Lo;0;L;;;;;N;;;;; +172D;HANUNOO LETTER RA;Lo;0;L;;;;;N;;;;; +172E;HANUNOO LETTER LA;Lo;0;L;;;;;N;;;;; +172F;HANUNOO LETTER WA;Lo;0;L;;;;;N;;;;; +1730;HANUNOO LETTER SA;Lo;0;L;;;;;N;;;;; +1731;HANUNOO LETTER HA;Lo;0;L;;;;;N;;;;; +1732;HANUNOO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +1733;HANUNOO VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1734;HANUNOO SIGN PAMUDPOD;Mn;9;NSM;;;;;N;;;;; +1735;PHILIPPINE SINGLE PUNCTUATION;Po;0;L;;;;;N;;;;; +1736;PHILIPPINE DOUBLE PUNCTUATION;Po;0;L;;;;;N;;;;; +1740;BUHID LETTER A;Lo;0;L;;;;;N;;;;; +1741;BUHID LETTER I;Lo;0;L;;;;;N;;;;; +1742;BUHID LETTER U;Lo;0;L;;;;;N;;;;; +1743;BUHID LETTER KA;Lo;0;L;;;;;N;;;;; +1744;BUHID LETTER GA;Lo;0;L;;;;;N;;;;; +1745;BUHID LETTER NGA;Lo;0;L;;;;;N;;;;; +1746;BUHID LETTER TA;Lo;0;L;;;;;N;;;;; +1747;BUHID LETTER DA;Lo;0;L;;;;;N;;;;; +1748;BUHID LETTER NA;Lo;0;L;;;;;N;;;;; +1749;BUHID LETTER PA;Lo;0;L;;;;;N;;;;; +174A;BUHID LETTER BA;Lo;0;L;;;;;N;;;;; +174B;BUHID LETTER MA;Lo;0;L;;;;;N;;;;; +174C;BUHID LETTER YA;Lo;0;L;;;;;N;;;;; +174D;BUHID LETTER RA;Lo;0;L;;;;;N;;;;; +174E;BUHID LETTER LA;Lo;0;L;;;;;N;;;;; +174F;BUHID LETTER WA;Lo;0;L;;;;;N;;;;; +1750;BUHID LETTER SA;Lo;0;L;;;;;N;;;;; +1751;BUHID LETTER HA;Lo;0;L;;;;;N;;;;; +1752;BUHID VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +1753;BUHID VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1760;TAGBANWA LETTER A;Lo;0;L;;;;;N;;;;; +1761;TAGBANWA LETTER I;Lo;0;L;;;;;N;;;;; +1762;TAGBANWA LETTER U;Lo;0;L;;;;;N;;;;; +1763;TAGBANWA LETTER KA;Lo;0;L;;;;;N;;;;; +1764;TAGBANWA LETTER GA;Lo;0;L;;;;;N;;;;; +1765;TAGBANWA LETTER NGA;Lo;0;L;;;;;N;;;;; +1766;TAGBANWA LETTER TA;Lo;0;L;;;;;N;;;;; +1767;TAGBANWA LETTER DA;Lo;0;L;;;;;N;;;;; +1768;TAGBANWA LETTER NA;Lo;0;L;;;;;N;;;;; +1769;TAGBANWA LETTER PA;Lo;0;L;;;;;N;;;;; +176A;TAGBANWA LETTER BA;Lo;0;L;;;;;N;;;;; +176B;TAGBANWA LETTER MA;Lo;0;L;;;;;N;;;;; +176C;TAGBANWA LETTER YA;Lo;0;L;;;;;N;;;;; +176E;TAGBANWA LETTER LA;Lo;0;L;;;;;N;;;;; +176F;TAGBANWA LETTER WA;Lo;0;L;;;;;N;;;;; +1770;TAGBANWA LETTER SA;Lo;0;L;;;;;N;;;;; +1772;TAGBANWA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +1773;TAGBANWA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1780;KHMER LETTER KA;Lo;0;L;;;;;N;;;;; +1781;KHMER LETTER KHA;Lo;0;L;;;;;N;;;;; +1782;KHMER LETTER KO;Lo;0;L;;;;;N;;;;; +1783;KHMER LETTER KHO;Lo;0;L;;;;;N;;;;; +1784;KHMER LETTER NGO;Lo;0;L;;;;;N;;;;; +1785;KHMER LETTER CA;Lo;0;L;;;;;N;;;;; +1786;KHMER LETTER CHA;Lo;0;L;;;;;N;;;;; +1787;KHMER LETTER CO;Lo;0;L;;;;;N;;;;; +1788;KHMER LETTER CHO;Lo;0;L;;;;;N;;;;; +1789;KHMER LETTER NYO;Lo;0;L;;;;;N;;;;; +178A;KHMER LETTER DA;Lo;0;L;;;;;N;;;;; +178B;KHMER LETTER TTHA;Lo;0;L;;;;;N;;;;; +178C;KHMER LETTER DO;Lo;0;L;;;;;N;;;;; +178D;KHMER LETTER TTHO;Lo;0;L;;;;;N;;;;; +178E;KHMER LETTER NNO;Lo;0;L;;;;;N;;;;; +178F;KHMER LETTER TA;Lo;0;L;;;;;N;;;;; +1790;KHMER LETTER THA;Lo;0;L;;;;;N;;;;; +1791;KHMER LETTER TO;Lo;0;L;;;;;N;;;;; +1792;KHMER LETTER THO;Lo;0;L;;;;;N;;;;; +1793;KHMER LETTER NO;Lo;0;L;;;;;N;;;;; +1794;KHMER LETTER BA;Lo;0;L;;;;;N;;;;; +1795;KHMER LETTER PHA;Lo;0;L;;;;;N;;;;; +1796;KHMER LETTER PO;Lo;0;L;;;;;N;;;;; +1797;KHMER LETTER PHO;Lo;0;L;;;;;N;;;;; +1798;KHMER LETTER MO;Lo;0;L;;;;;N;;;;; +1799;KHMER LETTER YO;Lo;0;L;;;;;N;;;;; +179A;KHMER LETTER RO;Lo;0;L;;;;;N;;;;; +179B;KHMER LETTER LO;Lo;0;L;;;;;N;;;;; +179C;KHMER LETTER VO;Lo;0;L;;;;;N;;;;; +179D;KHMER LETTER SHA;Lo;0;L;;;;;N;;;;; +179E;KHMER LETTER SSO;Lo;0;L;;;;;N;;;;; +179F;KHMER LETTER SA;Lo;0;L;;;;;N;;;;; +17A0;KHMER LETTER HA;Lo;0;L;;;;;N;;;;; +17A1;KHMER LETTER LA;Lo;0;L;;;;;N;;;;; +17A2;KHMER LETTER QA;Lo;0;L;;;;;N;;;;; +17A3;KHMER INDEPENDENT VOWEL QAQ;Lo;0;L;;;;;N;;;;; +17A4;KHMER INDEPENDENT VOWEL QAA;Lo;0;L;;;;;N;;;;; +17A5;KHMER INDEPENDENT VOWEL QI;Lo;0;L;;;;;N;;;;; +17A6;KHMER INDEPENDENT VOWEL QII;Lo;0;L;;;;;N;;;;; +17A7;KHMER INDEPENDENT VOWEL QU;Lo;0;L;;;;;N;;;;; +17A8;KHMER INDEPENDENT VOWEL QUK;Lo;0;L;;;;;N;;;;; +17A9;KHMER INDEPENDENT VOWEL QUU;Lo;0;L;;;;;N;;;;; +17AA;KHMER INDEPENDENT VOWEL QUUV;Lo;0;L;;;;;N;;;;; +17AB;KHMER INDEPENDENT VOWEL RY;Lo;0;L;;;;;N;;;;; +17AC;KHMER INDEPENDENT VOWEL RYY;Lo;0;L;;;;;N;;;;; +17AD;KHMER INDEPENDENT VOWEL LY;Lo;0;L;;;;;N;;;;; +17AE;KHMER INDEPENDENT VOWEL LYY;Lo;0;L;;;;;N;;;;; +17AF;KHMER INDEPENDENT VOWEL QE;Lo;0;L;;;;;N;;;;; +17B0;KHMER INDEPENDENT VOWEL QAI;Lo;0;L;;;;;N;;;;; +17B1;KHMER INDEPENDENT VOWEL QOO TYPE ONE;Lo;0;L;;;;;N;;;;; +17B2;KHMER INDEPENDENT VOWEL QOO TYPE TWO;Lo;0;L;;;;;N;;;;; +17B3;KHMER INDEPENDENT VOWEL QAU;Lo;0;L;;;;;N;;;;; +17B4;KHMER VOWEL INHERENT AQ;Mn;0;NSM;;;;;N;;;;; +17B5;KHMER VOWEL INHERENT AA;Mn;0;NSM;;;;;N;;;;; +17B6;KHMER VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +17B7;KHMER VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +17B8;KHMER VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +17B9;KHMER VOWEL SIGN Y;Mn;0;NSM;;;;;N;;;;; +17BA;KHMER VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;; +17BB;KHMER VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +17BC;KHMER VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +17BD;KHMER VOWEL SIGN UA;Mn;0;NSM;;;;;N;;;;; +17BE;KHMER VOWEL SIGN OE;Mc;0;L;;;;;N;;;;; +17BF;KHMER VOWEL SIGN YA;Mc;0;L;;;;;N;;;;; +17C0;KHMER VOWEL SIGN IE;Mc;0;L;;;;;N;;;;; +17C1;KHMER VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +17C2;KHMER VOWEL SIGN AE;Mc;0;L;;;;;N;;;;; +17C3;KHMER VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +17C4;KHMER VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; +17C5;KHMER VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +17C6;KHMER SIGN NIKAHIT;Mn;0;NSM;;;;;N;;;;; +17C7;KHMER SIGN REAHMUK;Mc;0;L;;;;;N;;;;; +17C8;KHMER SIGN YUUKALEAPINTU;Mc;0;L;;;;;N;;;;; +17C9;KHMER SIGN MUUSIKATOAN;Mn;0;NSM;;;;;N;;;;; +17CA;KHMER SIGN TRIISAP;Mn;0;NSM;;;;;N;;;;; +17CB;KHMER SIGN BANTOC;Mn;0;NSM;;;;;N;;;;; +17CC;KHMER SIGN ROBAT;Mn;0;NSM;;;;;N;;;;; +17CD;KHMER SIGN TOANDAKHIAT;Mn;0;NSM;;;;;N;;;;; +17CE;KHMER SIGN KAKABAT;Mn;0;NSM;;;;;N;;;;; +17CF;KHMER SIGN AHSDA;Mn;0;NSM;;;;;N;;;;; +17D0;KHMER SIGN SAMYOK SANNYA;Mn;0;NSM;;;;;N;;;;; +17D1;KHMER SIGN VIRIAM;Mn;0;NSM;;;;;N;;;;; +17D2;KHMER SIGN COENG;Mn;9;NSM;;;;;N;;;;; +17D3;KHMER SIGN BATHAMASAT;Mn;0;NSM;;;;;N;;;;; +17D4;KHMER SIGN KHAN;Po;0;L;;;;;N;;;;; +17D5;KHMER SIGN BARIYOOSAN;Po;0;L;;;;;N;;;;; +17D6;KHMER SIGN CAMNUC PII KUUH;Po;0;L;;;;;N;;;;; +17D7;KHMER SIGN LEK TOO;Lm;0;L;;;;;N;;;;; +17D8;KHMER SIGN BEYYAL;Po;0;L;;;;;N;;;;; +17D9;KHMER SIGN PHNAEK MUAN;Po;0;L;;;;;N;;;;; +17DA;KHMER SIGN KOOMUUT;Po;0;L;;;;;N;;;;; +17DB;KHMER CURRENCY SYMBOL RIEL;Sc;0;ET;;;;;N;;;;; +17DC;KHMER SIGN AVAKRAHASANYA;Lo;0;L;;;;;N;;;;; +17DD;KHMER SIGN ATTHACAN;Mn;230;NSM;;;;;N;;;;; +17E0;KHMER DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +17E1;KHMER DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +17E2;KHMER DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +17E3;KHMER DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +17E4;KHMER DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +17E5;KHMER DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +17E6;KHMER DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +17E7;KHMER DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +17E8;KHMER DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +17E9;KHMER DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +17F0;KHMER SYMBOL LEK ATTAK SON;No;0;ON;;;;0;N;;;;; +17F1;KHMER SYMBOL LEK ATTAK MUOY;No;0;ON;;;;1;N;;;;; +17F2;KHMER SYMBOL LEK ATTAK PII;No;0;ON;;;;2;N;;;;; +17F3;KHMER SYMBOL LEK ATTAK BEI;No;0;ON;;;;3;N;;;;; +17F4;KHMER SYMBOL LEK ATTAK BUON;No;0;ON;;;;4;N;;;;; +17F5;KHMER SYMBOL LEK ATTAK PRAM;No;0;ON;;;;5;N;;;;; +17F6;KHMER SYMBOL LEK ATTAK PRAM-MUOY;No;0;ON;;;;6;N;;;;; +17F7;KHMER SYMBOL LEK ATTAK PRAM-PII;No;0;ON;;;;7;N;;;;; +17F8;KHMER SYMBOL LEK ATTAK PRAM-BEI;No;0;ON;;;;8;N;;;;; +17F9;KHMER SYMBOL LEK ATTAK PRAM-BUON;No;0;ON;;;;9;N;;;;; +1800;MONGOLIAN BIRGA;Po;0;ON;;;;;N;;;;; +1801;MONGOLIAN ELLIPSIS;Po;0;ON;;;;;N;;;;; +1802;MONGOLIAN COMMA;Po;0;ON;;;;;N;;;;; +1803;MONGOLIAN FULL STOP;Po;0;ON;;;;;N;;;;; +1804;MONGOLIAN COLON;Po;0;ON;;;;;N;;;;; +1805;MONGOLIAN FOUR DOTS;Po;0;ON;;;;;N;;;;; +1806;MONGOLIAN TODO SOFT HYPHEN;Pd;0;ON;;;;;N;;;;; +1807;MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER;Po;0;ON;;;;;N;;;;; +1808;MONGOLIAN MANCHU COMMA;Po;0;ON;;;;;N;;;;; +1809;MONGOLIAN MANCHU FULL STOP;Po;0;ON;;;;;N;;;;; +180A;MONGOLIAN NIRUGU;Po;0;ON;;;;;N;;;;; +180B;MONGOLIAN FREE VARIATION SELECTOR ONE;Mn;0;NSM;;;;;N;;;;; +180C;MONGOLIAN FREE VARIATION SELECTOR TWO;Mn;0;NSM;;;;;N;;;;; +180D;MONGOLIAN FREE VARIATION SELECTOR THREE;Mn;0;NSM;;;;;N;;;;; +180E;MONGOLIAN VOWEL SEPARATOR;Cf;0;BN;;;;;N;;;;; +1810;MONGOLIAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +1811;MONGOLIAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +1812;MONGOLIAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +1813;MONGOLIAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +1814;MONGOLIAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +1815;MONGOLIAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +1816;MONGOLIAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +1817;MONGOLIAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1818;MONGOLIAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1819;MONGOLIAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1820;MONGOLIAN LETTER A;Lo;0;L;;;;;N;;;;; +1821;MONGOLIAN LETTER E;Lo;0;L;;;;;N;;;;; +1822;MONGOLIAN LETTER I;Lo;0;L;;;;;N;;;;; +1823;MONGOLIAN LETTER O;Lo;0;L;;;;;N;;;;; +1824;MONGOLIAN LETTER U;Lo;0;L;;;;;N;;;;; +1825;MONGOLIAN LETTER OE;Lo;0;L;;;;;N;;;;; +1826;MONGOLIAN LETTER UE;Lo;0;L;;;;;N;;;;; +1827;MONGOLIAN LETTER EE;Lo;0;L;;;;;N;;;;; +1828;MONGOLIAN LETTER NA;Lo;0;L;;;;;N;;;;; +1829;MONGOLIAN LETTER ANG;Lo;0;L;;;;;N;;;;; +182A;MONGOLIAN LETTER BA;Lo;0;L;;;;;N;;;;; +182B;MONGOLIAN LETTER PA;Lo;0;L;;;;;N;;;;; +182C;MONGOLIAN LETTER QA;Lo;0;L;;;;;N;;;;; +182D;MONGOLIAN LETTER GA;Lo;0;L;;;;;N;;;;; +182E;MONGOLIAN LETTER MA;Lo;0;L;;;;;N;;;;; +182F;MONGOLIAN LETTER LA;Lo;0;L;;;;;N;;;;; +1830;MONGOLIAN LETTER SA;Lo;0;L;;;;;N;;;;; +1831;MONGOLIAN LETTER SHA;Lo;0;L;;;;;N;;;;; +1832;MONGOLIAN LETTER TA;Lo;0;L;;;;;N;;;;; +1833;MONGOLIAN LETTER DA;Lo;0;L;;;;;N;;;;; +1834;MONGOLIAN LETTER CHA;Lo;0;L;;;;;N;;;;; +1835;MONGOLIAN LETTER JA;Lo;0;L;;;;;N;;;;; +1836;MONGOLIAN LETTER YA;Lo;0;L;;;;;N;;;;; +1837;MONGOLIAN LETTER RA;Lo;0;L;;;;;N;;;;; +1838;MONGOLIAN LETTER WA;Lo;0;L;;;;;N;;;;; +1839;MONGOLIAN LETTER FA;Lo;0;L;;;;;N;;;;; +183A;MONGOLIAN LETTER KA;Lo;0;L;;;;;N;;;;; +183B;MONGOLIAN LETTER KHA;Lo;0;L;;;;;N;;;;; +183C;MONGOLIAN LETTER TSA;Lo;0;L;;;;;N;;;;; +183D;MONGOLIAN LETTER ZA;Lo;0;L;;;;;N;;;;; +183E;MONGOLIAN LETTER HAA;Lo;0;L;;;;;N;;;;; +183F;MONGOLIAN LETTER ZRA;Lo;0;L;;;;;N;;;;; +1840;MONGOLIAN LETTER LHA;Lo;0;L;;;;;N;;;;; +1841;MONGOLIAN LETTER ZHI;Lo;0;L;;;;;N;;;;; +1842;MONGOLIAN LETTER CHI;Lo;0;L;;;;;N;;;;; +1843;MONGOLIAN LETTER TODO LONG VOWEL SIGN;Lm;0;L;;;;;N;;;;; +1844;MONGOLIAN LETTER TODO E;Lo;0;L;;;;;N;;;;; +1845;MONGOLIAN LETTER TODO I;Lo;0;L;;;;;N;;;;; +1846;MONGOLIAN LETTER TODO O;Lo;0;L;;;;;N;;;;; +1847;MONGOLIAN LETTER TODO U;Lo;0;L;;;;;N;;;;; +1848;MONGOLIAN LETTER TODO OE;Lo;0;L;;;;;N;;;;; +1849;MONGOLIAN LETTER TODO UE;Lo;0;L;;;;;N;;;;; +184A;MONGOLIAN LETTER TODO ANG;Lo;0;L;;;;;N;;;;; +184B;MONGOLIAN LETTER TODO BA;Lo;0;L;;;;;N;;;;; +184C;MONGOLIAN LETTER TODO PA;Lo;0;L;;;;;N;;;;; +184D;MONGOLIAN LETTER TODO QA;Lo;0;L;;;;;N;;;;; +184E;MONGOLIAN LETTER TODO GA;Lo;0;L;;;;;N;;;;; +184F;MONGOLIAN LETTER TODO MA;Lo;0;L;;;;;N;;;;; +1850;MONGOLIAN LETTER TODO TA;Lo;0;L;;;;;N;;;;; +1851;MONGOLIAN LETTER TODO DA;Lo;0;L;;;;;N;;;;; +1852;MONGOLIAN LETTER TODO CHA;Lo;0;L;;;;;N;;;;; +1853;MONGOLIAN LETTER TODO JA;Lo;0;L;;;;;N;;;;; +1854;MONGOLIAN LETTER TODO TSA;Lo;0;L;;;;;N;;;;; +1855;MONGOLIAN LETTER TODO YA;Lo;0;L;;;;;N;;;;; +1856;MONGOLIAN LETTER TODO WA;Lo;0;L;;;;;N;;;;; +1857;MONGOLIAN LETTER TODO KA;Lo;0;L;;;;;N;;;;; +1858;MONGOLIAN LETTER TODO GAA;Lo;0;L;;;;;N;;;;; +1859;MONGOLIAN LETTER TODO HAA;Lo;0;L;;;;;N;;;;; +185A;MONGOLIAN LETTER TODO JIA;Lo;0;L;;;;;N;;;;; +185B;MONGOLIAN LETTER TODO NIA;Lo;0;L;;;;;N;;;;; +185C;MONGOLIAN LETTER TODO DZA;Lo;0;L;;;;;N;;;;; +185D;MONGOLIAN LETTER SIBE E;Lo;0;L;;;;;N;;;;; +185E;MONGOLIAN LETTER SIBE I;Lo;0;L;;;;;N;;;;; +185F;MONGOLIAN LETTER SIBE IY;Lo;0;L;;;;;N;;;;; +1860;MONGOLIAN LETTER SIBE UE;Lo;0;L;;;;;N;;;;; +1861;MONGOLIAN LETTER SIBE U;Lo;0;L;;;;;N;;;;; +1862;MONGOLIAN LETTER SIBE ANG;Lo;0;L;;;;;N;;;;; +1863;MONGOLIAN LETTER SIBE KA;Lo;0;L;;;;;N;;;;; +1864;MONGOLIAN LETTER SIBE GA;Lo;0;L;;;;;N;;;;; +1865;MONGOLIAN LETTER SIBE HA;Lo;0;L;;;;;N;;;;; +1866;MONGOLIAN LETTER SIBE PA;Lo;0;L;;;;;N;;;;; +1867;MONGOLIAN LETTER SIBE SHA;Lo;0;L;;;;;N;;;;; +1868;MONGOLIAN LETTER SIBE TA;Lo;0;L;;;;;N;;;;; +1869;MONGOLIAN LETTER SIBE DA;Lo;0;L;;;;;N;;;;; +186A;MONGOLIAN LETTER SIBE JA;Lo;0;L;;;;;N;;;;; +186B;MONGOLIAN LETTER SIBE FA;Lo;0;L;;;;;N;;;;; +186C;MONGOLIAN LETTER SIBE GAA;Lo;0;L;;;;;N;;;;; +186D;MONGOLIAN LETTER SIBE HAA;Lo;0;L;;;;;N;;;;; +186E;MONGOLIAN LETTER SIBE TSA;Lo;0;L;;;;;N;;;;; +186F;MONGOLIAN LETTER SIBE ZA;Lo;0;L;;;;;N;;;;; +1870;MONGOLIAN LETTER SIBE RAA;Lo;0;L;;;;;N;;;;; +1871;MONGOLIAN LETTER SIBE CHA;Lo;0;L;;;;;N;;;;; +1872;MONGOLIAN LETTER SIBE ZHA;Lo;0;L;;;;;N;;;;; +1873;MONGOLIAN LETTER MANCHU I;Lo;0;L;;;;;N;;;;; +1874;MONGOLIAN LETTER MANCHU KA;Lo;0;L;;;;;N;;;;; +1875;MONGOLIAN LETTER MANCHU RA;Lo;0;L;;;;;N;;;;; +1876;MONGOLIAN LETTER MANCHU FA;Lo;0;L;;;;;N;;;;; +1877;MONGOLIAN LETTER MANCHU ZHA;Lo;0;L;;;;;N;;;;; +1880;MONGOLIAN LETTER ALI GALI ANUSVARA ONE;Lo;0;L;;;;;N;;;;; +1881;MONGOLIAN LETTER ALI GALI VISARGA ONE;Lo;0;L;;;;;N;;;;; +1882;MONGOLIAN LETTER ALI GALI DAMARU;Lo;0;L;;;;;N;;;;; +1883;MONGOLIAN LETTER ALI GALI UBADAMA;Lo;0;L;;;;;N;;;;; +1884;MONGOLIAN LETTER ALI GALI INVERTED UBADAMA;Lo;0;L;;;;;N;;;;; +1885;MONGOLIAN LETTER ALI GALI BALUDA;Mn;0;NSM;;;;;N;;;;; +1886;MONGOLIAN LETTER ALI GALI THREE BALUDA;Mn;0;NSM;;;;;N;;;;; +1887;MONGOLIAN LETTER ALI GALI A;Lo;0;L;;;;;N;;;;; +1888;MONGOLIAN LETTER ALI GALI I;Lo;0;L;;;;;N;;;;; +1889;MONGOLIAN LETTER ALI GALI KA;Lo;0;L;;;;;N;;;;; +188A;MONGOLIAN LETTER ALI GALI NGA;Lo;0;L;;;;;N;;;;; +188B;MONGOLIAN LETTER ALI GALI CA;Lo;0;L;;;;;N;;;;; +188C;MONGOLIAN LETTER ALI GALI TTA;Lo;0;L;;;;;N;;;;; +188D;MONGOLIAN LETTER ALI GALI TTHA;Lo;0;L;;;;;N;;;;; +188E;MONGOLIAN LETTER ALI GALI DDA;Lo;0;L;;;;;N;;;;; +188F;MONGOLIAN LETTER ALI GALI NNA;Lo;0;L;;;;;N;;;;; +1890;MONGOLIAN LETTER ALI GALI TA;Lo;0;L;;;;;N;;;;; +1891;MONGOLIAN LETTER ALI GALI DA;Lo;0;L;;;;;N;;;;; +1892;MONGOLIAN LETTER ALI GALI PA;Lo;0;L;;;;;N;;;;; +1893;MONGOLIAN LETTER ALI GALI PHA;Lo;0;L;;;;;N;;;;; +1894;MONGOLIAN LETTER ALI GALI SSA;Lo;0;L;;;;;N;;;;; +1895;MONGOLIAN LETTER ALI GALI ZHA;Lo;0;L;;;;;N;;;;; +1896;MONGOLIAN LETTER ALI GALI ZA;Lo;0;L;;;;;N;;;;; +1897;MONGOLIAN LETTER ALI GALI AH;Lo;0;L;;;;;N;;;;; +1898;MONGOLIAN LETTER TODO ALI GALI TA;Lo;0;L;;;;;N;;;;; +1899;MONGOLIAN LETTER TODO ALI GALI ZHA;Lo;0;L;;;;;N;;;;; +189A;MONGOLIAN LETTER MANCHU ALI GALI GHA;Lo;0;L;;;;;N;;;;; +189B;MONGOLIAN LETTER MANCHU ALI GALI NGA;Lo;0;L;;;;;N;;;;; +189C;MONGOLIAN LETTER MANCHU ALI GALI CA;Lo;0;L;;;;;N;;;;; +189D;MONGOLIAN LETTER MANCHU ALI GALI JHA;Lo;0;L;;;;;N;;;;; +189E;MONGOLIAN LETTER MANCHU ALI GALI TTA;Lo;0;L;;;;;N;;;;; +189F;MONGOLIAN LETTER MANCHU ALI GALI DDHA;Lo;0;L;;;;;N;;;;; +18A0;MONGOLIAN LETTER MANCHU ALI GALI TA;Lo;0;L;;;;;N;;;;; +18A1;MONGOLIAN LETTER MANCHU ALI GALI DHA;Lo;0;L;;;;;N;;;;; +18A2;MONGOLIAN LETTER MANCHU ALI GALI SSA;Lo;0;L;;;;;N;;;;; +18A3;MONGOLIAN LETTER MANCHU ALI GALI CYA;Lo;0;L;;;;;N;;;;; +18A4;MONGOLIAN LETTER MANCHU ALI GALI ZHA;Lo;0;L;;;;;N;;;;; +18A5;MONGOLIAN LETTER MANCHU ALI GALI ZA;Lo;0;L;;;;;N;;;;; +18A6;MONGOLIAN LETTER ALI GALI HALF U;Lo;0;L;;;;;N;;;;; +18A7;MONGOLIAN LETTER ALI GALI HALF YA;Lo;0;L;;;;;N;;;;; +18A8;MONGOLIAN LETTER MANCHU ALI GALI BHA;Lo;0;L;;;;;N;;;;; +18A9;MONGOLIAN LETTER ALI GALI DAGALGA;Mn;228;NSM;;;;;N;;;;; +18AA;MONGOLIAN LETTER MANCHU ALI GALI LHA;Lo;0;L;;;;;N;;;;; +18B0;CANADIAN SYLLABICS OY;Lo;0;L;;;;;N;;;;; +18B1;CANADIAN SYLLABICS AY;Lo;0;L;;;;;N;;;;; +18B2;CANADIAN SYLLABICS AAY;Lo;0;L;;;;;N;;;;; +18B3;CANADIAN SYLLABICS WAY;Lo;0;L;;;;;N;;;;; +18B4;CANADIAN SYLLABICS POY;Lo;0;L;;;;;N;;;;; +18B5;CANADIAN SYLLABICS PAY;Lo;0;L;;;;;N;;;;; +18B6;CANADIAN SYLLABICS PWOY;Lo;0;L;;;;;N;;;;; +18B7;CANADIAN SYLLABICS TAY;Lo;0;L;;;;;N;;;;; +18B8;CANADIAN SYLLABICS KAY;Lo;0;L;;;;;N;;;;; +18B9;CANADIAN SYLLABICS KWAY;Lo;0;L;;;;;N;;;;; +18BA;CANADIAN SYLLABICS MAY;Lo;0;L;;;;;N;;;;; +18BB;CANADIAN SYLLABICS NOY;Lo;0;L;;;;;N;;;;; +18BC;CANADIAN SYLLABICS NAY;Lo;0;L;;;;;N;;;;; +18BD;CANADIAN SYLLABICS LAY;Lo;0;L;;;;;N;;;;; +18BE;CANADIAN SYLLABICS SOY;Lo;0;L;;;;;N;;;;; +18BF;CANADIAN SYLLABICS SAY;Lo;0;L;;;;;N;;;;; +18C0;CANADIAN SYLLABICS SHOY;Lo;0;L;;;;;N;;;;; +18C1;CANADIAN SYLLABICS SHAY;Lo;0;L;;;;;N;;;;; +18C2;CANADIAN SYLLABICS SHWOY;Lo;0;L;;;;;N;;;;; +18C3;CANADIAN SYLLABICS YOY;Lo;0;L;;;;;N;;;;; +18C4;CANADIAN SYLLABICS YAY;Lo;0;L;;;;;N;;;;; +18C5;CANADIAN SYLLABICS RAY;Lo;0;L;;;;;N;;;;; +18C6;CANADIAN SYLLABICS NWI;Lo;0;L;;;;;N;;;;; +18C7;CANADIAN SYLLABICS OJIBWAY NWI;Lo;0;L;;;;;N;;;;; +18C8;CANADIAN SYLLABICS NWII;Lo;0;L;;;;;N;;;;; +18C9;CANADIAN SYLLABICS OJIBWAY NWII;Lo;0;L;;;;;N;;;;; +18CA;CANADIAN SYLLABICS NWO;Lo;0;L;;;;;N;;;;; +18CB;CANADIAN SYLLABICS OJIBWAY NWO;Lo;0;L;;;;;N;;;;; +18CC;CANADIAN SYLLABICS NWOO;Lo;0;L;;;;;N;;;;; +18CD;CANADIAN SYLLABICS OJIBWAY NWOO;Lo;0;L;;;;;N;;;;; +18CE;CANADIAN SYLLABICS RWEE;Lo;0;L;;;;;N;;;;; +18CF;CANADIAN SYLLABICS RWI;Lo;0;L;;;;;N;;;;; +18D0;CANADIAN SYLLABICS RWII;Lo;0;L;;;;;N;;;;; +18D1;CANADIAN SYLLABICS RWO;Lo;0;L;;;;;N;;;;; +18D2;CANADIAN SYLLABICS RWOO;Lo;0;L;;;;;N;;;;; +18D3;CANADIAN SYLLABICS RWA;Lo;0;L;;;;;N;;;;; +18D4;CANADIAN SYLLABICS OJIBWAY P;Lo;0;L;;;;;N;;;;; +18D5;CANADIAN SYLLABICS OJIBWAY T;Lo;0;L;;;;;N;;;;; +18D6;CANADIAN SYLLABICS OJIBWAY K;Lo;0;L;;;;;N;;;;; +18D7;CANADIAN SYLLABICS OJIBWAY C;Lo;0;L;;;;;N;;;;; +18D8;CANADIAN SYLLABICS OJIBWAY M;Lo;0;L;;;;;N;;;;; +18D9;CANADIAN SYLLABICS OJIBWAY N;Lo;0;L;;;;;N;;;;; +18DA;CANADIAN SYLLABICS OJIBWAY S;Lo;0;L;;;;;N;;;;; +18DB;CANADIAN SYLLABICS OJIBWAY SH;Lo;0;L;;;;;N;;;;; +18DC;CANADIAN SYLLABICS EASTERN W;Lo;0;L;;;;;N;;;;; +18DD;CANADIAN SYLLABICS WESTERN W;Lo;0;L;;;;;N;;;;; +18DE;CANADIAN SYLLABICS FINAL SMALL RING;Lo;0;L;;;;;N;;;;; +18DF;CANADIAN SYLLABICS FINAL RAISED DOT;Lo;0;L;;;;;N;;;;; +18E0;CANADIAN SYLLABICS R-CREE RWE;Lo;0;L;;;;;N;;;;; +18E1;CANADIAN SYLLABICS WEST-CREE LOO;Lo;0;L;;;;;N;;;;; +18E2;CANADIAN SYLLABICS WEST-CREE LAA;Lo;0;L;;;;;N;;;;; +18E3;CANADIAN SYLLABICS THWE;Lo;0;L;;;;;N;;;;; +18E4;CANADIAN SYLLABICS THWA;Lo;0;L;;;;;N;;;;; +18E5;CANADIAN SYLLABICS TTHWE;Lo;0;L;;;;;N;;;;; +18E6;CANADIAN SYLLABICS TTHOO;Lo;0;L;;;;;N;;;;; +18E7;CANADIAN SYLLABICS TTHAA;Lo;0;L;;;;;N;;;;; +18E8;CANADIAN SYLLABICS TLHWE;Lo;0;L;;;;;N;;;;; +18E9;CANADIAN SYLLABICS TLHOO;Lo;0;L;;;;;N;;;;; +18EA;CANADIAN SYLLABICS SAYISI SHWE;Lo;0;L;;;;;N;;;;; +18EB;CANADIAN SYLLABICS SAYISI SHOO;Lo;0;L;;;;;N;;;;; +18EC;CANADIAN SYLLABICS SAYISI HOO;Lo;0;L;;;;;N;;;;; +18ED;CANADIAN SYLLABICS CARRIER GWU;Lo;0;L;;;;;N;;;;; +18EE;CANADIAN SYLLABICS CARRIER DENE GEE;Lo;0;L;;;;;N;;;;; +18EF;CANADIAN SYLLABICS CARRIER GAA;Lo;0;L;;;;;N;;;;; +18F0;CANADIAN SYLLABICS CARRIER GWA;Lo;0;L;;;;;N;;;;; +18F1;CANADIAN SYLLABICS SAYISI JUU;Lo;0;L;;;;;N;;;;; +18F2;CANADIAN SYLLABICS CARRIER JWA;Lo;0;L;;;;;N;;;;; +18F3;CANADIAN SYLLABICS BEAVER DENE L;Lo;0;L;;;;;N;;;;; +18F4;CANADIAN SYLLABICS BEAVER DENE R;Lo;0;L;;;;;N;;;;; +18F5;CANADIAN SYLLABICS CARRIER DENTAL S;Lo;0;L;;;;;N;;;;; +1900;LIMBU VOWEL-CARRIER LETTER;Lo;0;L;;;;;N;;;;; +1901;LIMBU LETTER KA;Lo;0;L;;;;;N;;;;; +1902;LIMBU LETTER KHA;Lo;0;L;;;;;N;;;;; +1903;LIMBU LETTER GA;Lo;0;L;;;;;N;;;;; +1904;LIMBU LETTER GHA;Lo;0;L;;;;;N;;;;; +1905;LIMBU LETTER NGA;Lo;0;L;;;;;N;;;;; +1906;LIMBU LETTER CA;Lo;0;L;;;;;N;;;;; +1907;LIMBU LETTER CHA;Lo;0;L;;;;;N;;;;; +1908;LIMBU LETTER JA;Lo;0;L;;;;;N;;;;; +1909;LIMBU LETTER JHA;Lo;0;L;;;;;N;;;;; +190A;LIMBU LETTER YAN;Lo;0;L;;;;;N;;;;; +190B;LIMBU LETTER TA;Lo;0;L;;;;;N;;;;; +190C;LIMBU LETTER THA;Lo;0;L;;;;;N;;;;; +190D;LIMBU LETTER DA;Lo;0;L;;;;;N;;;;; +190E;LIMBU LETTER DHA;Lo;0;L;;;;;N;;;;; +190F;LIMBU LETTER NA;Lo;0;L;;;;;N;;;;; +1910;LIMBU LETTER PA;Lo;0;L;;;;;N;;;;; +1911;LIMBU LETTER PHA;Lo;0;L;;;;;N;;;;; +1912;LIMBU LETTER BA;Lo;0;L;;;;;N;;;;; +1913;LIMBU LETTER BHA;Lo;0;L;;;;;N;;;;; +1914;LIMBU LETTER MA;Lo;0;L;;;;;N;;;;; +1915;LIMBU LETTER YA;Lo;0;L;;;;;N;;;;; +1916;LIMBU LETTER RA;Lo;0;L;;;;;N;;;;; +1917;LIMBU LETTER LA;Lo;0;L;;;;;N;;;;; +1918;LIMBU LETTER WA;Lo;0;L;;;;;N;;;;; +1919;LIMBU LETTER SHA;Lo;0;L;;;;;N;;;;; +191A;LIMBU LETTER SSA;Lo;0;L;;;;;N;;;;; +191B;LIMBU LETTER SA;Lo;0;L;;;;;N;;;;; +191C;LIMBU LETTER HA;Lo;0;L;;;;;N;;;;; +191D;LIMBU LETTER GYAN;Lo;0;L;;;;;N;;;;; +191E;LIMBU LETTER TRA;Lo;0;L;;;;;N;;;;; +1920;LIMBU VOWEL SIGN A;Mn;0;NSM;;;;;N;;;;; +1921;LIMBU VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +1922;LIMBU VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1923;LIMBU VOWEL SIGN EE;Mc;0;L;;;;;N;;;;; +1924;LIMBU VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +1925;LIMBU VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; +1926;LIMBU VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +1927;LIMBU VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +1928;LIMBU VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; +1929;LIMBU SUBJOINED LETTER YA;Mc;0;L;;;;;N;;;;; +192A;LIMBU SUBJOINED LETTER RA;Mc;0;L;;;;;N;;;;; +192B;LIMBU SUBJOINED LETTER WA;Mc;0;L;;;;;N;;;;; +1930;LIMBU SMALL LETTER KA;Mc;0;L;;;;;N;;;;; +1931;LIMBU SMALL LETTER NGA;Mc;0;L;;;;;N;;;;; +1932;LIMBU SMALL LETTER ANUSVARA;Mn;0;NSM;;;;;N;;;;; +1933;LIMBU SMALL LETTER TA;Mc;0;L;;;;;N;;;;; +1934;LIMBU SMALL LETTER NA;Mc;0;L;;;;;N;;;;; +1935;LIMBU SMALL LETTER PA;Mc;0;L;;;;;N;;;;; +1936;LIMBU SMALL LETTER MA;Mc;0;L;;;;;N;;;;; +1937;LIMBU SMALL LETTER RA;Mc;0;L;;;;;N;;;;; +1938;LIMBU SMALL LETTER LA;Mc;0;L;;;;;N;;;;; +1939;LIMBU SIGN MUKPHRENG;Mn;222;NSM;;;;;N;;;;; +193A;LIMBU SIGN KEMPHRENG;Mn;230;NSM;;;;;N;;;;; +193B;LIMBU SIGN SA-I;Mn;220;NSM;;;;;N;;;;; +1940;LIMBU SIGN LOO;So;0;ON;;;;;N;;;;; +1944;LIMBU EXCLAMATION MARK;Po;0;ON;;;;;N;;;;; +1945;LIMBU QUESTION MARK;Po;0;ON;;;;;N;;;;; +1946;LIMBU DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +1947;LIMBU DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +1948;LIMBU DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +1949;LIMBU DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +194A;LIMBU DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +194B;LIMBU DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +194C;LIMBU DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +194D;LIMBU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +194E;LIMBU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +194F;LIMBU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1950;TAI LE LETTER KA;Lo;0;L;;;;;N;;;;; +1951;TAI LE LETTER XA;Lo;0;L;;;;;N;;;;; +1952;TAI LE LETTER NGA;Lo;0;L;;;;;N;;;;; +1953;TAI LE LETTER TSA;Lo;0;L;;;;;N;;;;; +1954;TAI LE LETTER SA;Lo;0;L;;;;;N;;;;; +1955;TAI LE LETTER YA;Lo;0;L;;;;;N;;;;; +1956;TAI LE LETTER TA;Lo;0;L;;;;;N;;;;; +1957;TAI LE LETTER THA;Lo;0;L;;;;;N;;;;; +1958;TAI LE LETTER LA;Lo;0;L;;;;;N;;;;; +1959;TAI LE LETTER PA;Lo;0;L;;;;;N;;;;; +195A;TAI LE LETTER PHA;Lo;0;L;;;;;N;;;;; +195B;TAI LE LETTER MA;Lo;0;L;;;;;N;;;;; +195C;TAI LE LETTER FA;Lo;0;L;;;;;N;;;;; +195D;TAI LE LETTER VA;Lo;0;L;;;;;N;;;;; +195E;TAI LE LETTER HA;Lo;0;L;;;;;N;;;;; +195F;TAI LE LETTER QA;Lo;0;L;;;;;N;;;;; +1960;TAI LE LETTER KHA;Lo;0;L;;;;;N;;;;; +1961;TAI LE LETTER TSHA;Lo;0;L;;;;;N;;;;; +1962;TAI LE LETTER NA;Lo;0;L;;;;;N;;;;; +1963;TAI LE LETTER A;Lo;0;L;;;;;N;;;;; +1964;TAI LE LETTER I;Lo;0;L;;;;;N;;;;; +1965;TAI LE LETTER EE;Lo;0;L;;;;;N;;;;; +1966;TAI LE LETTER EH;Lo;0;L;;;;;N;;;;; +1967;TAI LE LETTER U;Lo;0;L;;;;;N;;;;; +1968;TAI LE LETTER OO;Lo;0;L;;;;;N;;;;; +1969;TAI LE LETTER O;Lo;0;L;;;;;N;;;;; +196A;TAI LE LETTER UE;Lo;0;L;;;;;N;;;;; +196B;TAI LE LETTER E;Lo;0;L;;;;;N;;;;; +196C;TAI LE LETTER AUE;Lo;0;L;;;;;N;;;;; +196D;TAI LE LETTER AI;Lo;0;L;;;;;N;;;;; +1970;TAI LE LETTER TONE-2;Lo;0;L;;;;;N;;;;; +1971;TAI LE LETTER TONE-3;Lo;0;L;;;;;N;;;;; +1972;TAI LE LETTER TONE-4;Lo;0;L;;;;;N;;;;; +1973;TAI LE LETTER TONE-5;Lo;0;L;;;;;N;;;;; +1974;TAI LE LETTER TONE-6;Lo;0;L;;;;;N;;;;; +1980;NEW TAI LUE LETTER HIGH QA;Lo;0;L;;;;;N;;;;; +1981;NEW TAI LUE LETTER LOW QA;Lo;0;L;;;;;N;;;;; +1982;NEW TAI LUE LETTER HIGH KA;Lo;0;L;;;;;N;;;;; +1983;NEW TAI LUE LETTER HIGH XA;Lo;0;L;;;;;N;;;;; +1984;NEW TAI LUE LETTER HIGH NGA;Lo;0;L;;;;;N;;;;; +1985;NEW TAI LUE LETTER LOW KA;Lo;0;L;;;;;N;;;;; +1986;NEW TAI LUE LETTER LOW XA;Lo;0;L;;;;;N;;;;; +1987;NEW TAI LUE LETTER LOW NGA;Lo;0;L;;;;;N;;;;; +1988;NEW TAI LUE LETTER HIGH TSA;Lo;0;L;;;;;N;;;;; +1989;NEW TAI LUE LETTER HIGH SA;Lo;0;L;;;;;N;;;;; +198A;NEW TAI LUE LETTER HIGH YA;Lo;0;L;;;;;N;;;;; +198B;NEW TAI LUE LETTER LOW TSA;Lo;0;L;;;;;N;;;;; +198C;NEW TAI LUE LETTER LOW SA;Lo;0;L;;;;;N;;;;; +198D;NEW TAI LUE LETTER LOW YA;Lo;0;L;;;;;N;;;;; +198E;NEW TAI LUE LETTER HIGH TA;Lo;0;L;;;;;N;;;;; +198F;NEW TAI LUE LETTER HIGH THA;Lo;0;L;;;;;N;;;;; +1990;NEW TAI LUE LETTER HIGH NA;Lo;0;L;;;;;N;;;;; +1991;NEW TAI LUE LETTER LOW TA;Lo;0;L;;;;;N;;;;; +1992;NEW TAI LUE LETTER LOW THA;Lo;0;L;;;;;N;;;;; +1993;NEW TAI LUE LETTER LOW NA;Lo;0;L;;;;;N;;;;; +1994;NEW TAI LUE LETTER HIGH PA;Lo;0;L;;;;;N;;;;; +1995;NEW TAI LUE LETTER HIGH PHA;Lo;0;L;;;;;N;;;;; +1996;NEW TAI LUE LETTER HIGH MA;Lo;0;L;;;;;N;;;;; +1997;NEW TAI LUE LETTER LOW PA;Lo;0;L;;;;;N;;;;; +1998;NEW TAI LUE LETTER LOW PHA;Lo;0;L;;;;;N;;;;; +1999;NEW TAI LUE LETTER LOW MA;Lo;0;L;;;;;N;;;;; +199A;NEW TAI LUE LETTER HIGH FA;Lo;0;L;;;;;N;;;;; +199B;NEW TAI LUE LETTER HIGH VA;Lo;0;L;;;;;N;;;;; +199C;NEW TAI LUE LETTER HIGH LA;Lo;0;L;;;;;N;;;;; +199D;NEW TAI LUE LETTER LOW FA;Lo;0;L;;;;;N;;;;; +199E;NEW TAI LUE LETTER LOW VA;Lo;0;L;;;;;N;;;;; +199F;NEW TAI LUE LETTER LOW LA;Lo;0;L;;;;;N;;;;; +19A0;NEW TAI LUE LETTER HIGH HA;Lo;0;L;;;;;N;;;;; +19A1;NEW TAI LUE LETTER HIGH DA;Lo;0;L;;;;;N;;;;; +19A2;NEW TAI LUE LETTER HIGH BA;Lo;0;L;;;;;N;;;;; +19A3;NEW TAI LUE LETTER LOW HA;Lo;0;L;;;;;N;;;;; +19A4;NEW TAI LUE LETTER LOW DA;Lo;0;L;;;;;N;;;;; +19A5;NEW TAI LUE LETTER LOW BA;Lo;0;L;;;;;N;;;;; +19A6;NEW TAI LUE LETTER HIGH KVA;Lo;0;L;;;;;N;;;;; +19A7;NEW TAI LUE LETTER HIGH XVA;Lo;0;L;;;;;N;;;;; +19A8;NEW TAI LUE LETTER LOW KVA;Lo;0;L;;;;;N;;;;; +19A9;NEW TAI LUE LETTER LOW XVA;Lo;0;L;;;;;N;;;;; +19AA;NEW TAI LUE LETTER HIGH SUA;Lo;0;L;;;;;N;;;;; +19AB;NEW TAI LUE LETTER LOW SUA;Lo;0;L;;;;;N;;;;; +19B0;NEW TAI LUE VOWEL SIGN VOWEL SHORTENER;Lo;0;L;;;;;N;;;;; +19B1;NEW TAI LUE VOWEL SIGN AA;Lo;0;L;;;;;N;;;;; +19B2;NEW TAI LUE VOWEL SIGN II;Lo;0;L;;;;;N;;;;; +19B3;NEW TAI LUE VOWEL SIGN U;Lo;0;L;;;;;N;;;;; +19B4;NEW TAI LUE VOWEL SIGN UU;Lo;0;L;;;;;N;;;;; +19B5;NEW TAI LUE VOWEL SIGN E;Lo;0;L;;;;;N;;;;; +19B6;NEW TAI LUE VOWEL SIGN AE;Lo;0;L;;;;;N;;;;; +19B7;NEW TAI LUE VOWEL SIGN O;Lo;0;L;;;;;N;;;;; +19B8;NEW TAI LUE VOWEL SIGN OA;Lo;0;L;;;;;N;;;;; +19B9;NEW TAI LUE VOWEL SIGN UE;Lo;0;L;;;;;N;;;;; +19BA;NEW TAI LUE VOWEL SIGN AY;Lo;0;L;;;;;N;;;;; +19BB;NEW TAI LUE VOWEL SIGN AAY;Lo;0;L;;;;;N;;;;; +19BC;NEW TAI LUE VOWEL SIGN UY;Lo;0;L;;;;;N;;;;; +19BD;NEW TAI LUE VOWEL SIGN OY;Lo;0;L;;;;;N;;;;; +19BE;NEW TAI LUE VOWEL SIGN OAY;Lo;0;L;;;;;N;;;;; +19BF;NEW TAI LUE VOWEL SIGN UEY;Lo;0;L;;;;;N;;;;; +19C0;NEW TAI LUE VOWEL SIGN IY;Lo;0;L;;;;;N;;;;; +19C1;NEW TAI LUE LETTER FINAL V;Lo;0;L;;;;;N;;;;; +19C2;NEW TAI LUE LETTER FINAL NG;Lo;0;L;;;;;N;;;;; +19C3;NEW TAI LUE LETTER FINAL N;Lo;0;L;;;;;N;;;;; +19C4;NEW TAI LUE LETTER FINAL M;Lo;0;L;;;;;N;;;;; +19C5;NEW TAI LUE LETTER FINAL K;Lo;0;L;;;;;N;;;;; +19C6;NEW TAI LUE LETTER FINAL D;Lo;0;L;;;;;N;;;;; +19C7;NEW TAI LUE LETTER FINAL B;Lo;0;L;;;;;N;;;;; +19C8;NEW TAI LUE TONE MARK-1;Lo;0;L;;;;;N;;;;; +19C9;NEW TAI LUE TONE MARK-2;Lo;0;L;;;;;N;;;;; +19D0;NEW TAI LUE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +19D1;NEW TAI LUE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +19D2;NEW TAI LUE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +19D3;NEW TAI LUE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +19D4;NEW TAI LUE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +19D5;NEW TAI LUE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +19D6;NEW TAI LUE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +19D7;NEW TAI LUE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +19D8;NEW TAI LUE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +19D9;NEW TAI LUE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +19DA;NEW TAI LUE THAM DIGIT ONE;No;0;L;;;1;1;N;;;;; +19DE;NEW TAI LUE SIGN LAE;So;0;ON;;;;;N;;;;; +19DF;NEW TAI LUE SIGN LAEV;So;0;ON;;;;;N;;;;; +19E0;KHMER SYMBOL PATHAMASAT;So;0;ON;;;;;N;;;;; +19E1;KHMER SYMBOL MUOY KOET;So;0;ON;;;;;N;;;;; +19E2;KHMER SYMBOL PII KOET;So;0;ON;;;;;N;;;;; +19E3;KHMER SYMBOL BEI KOET;So;0;ON;;;;;N;;;;; +19E4;KHMER SYMBOL BUON KOET;So;0;ON;;;;;N;;;;; +19E5;KHMER SYMBOL PRAM KOET;So;0;ON;;;;;N;;;;; +19E6;KHMER SYMBOL PRAM-MUOY KOET;So;0;ON;;;;;N;;;;; +19E7;KHMER SYMBOL PRAM-PII KOET;So;0;ON;;;;;N;;;;; +19E8;KHMER SYMBOL PRAM-BEI KOET;So;0;ON;;;;;N;;;;; +19E9;KHMER SYMBOL PRAM-BUON KOET;So;0;ON;;;;;N;;;;; +19EA;KHMER SYMBOL DAP KOET;So;0;ON;;;;;N;;;;; +19EB;KHMER SYMBOL DAP-MUOY KOET;So;0;ON;;;;;N;;;;; +19EC;KHMER SYMBOL DAP-PII KOET;So;0;ON;;;;;N;;;;; +19ED;KHMER SYMBOL DAP-BEI KOET;So;0;ON;;;;;N;;;;; +19EE;KHMER SYMBOL DAP-BUON KOET;So;0;ON;;;;;N;;;;; +19EF;KHMER SYMBOL DAP-PRAM KOET;So;0;ON;;;;;N;;;;; +19F0;KHMER SYMBOL TUTEYASAT;So;0;ON;;;;;N;;;;; +19F1;KHMER SYMBOL MUOY ROC;So;0;ON;;;;;N;;;;; +19F2;KHMER SYMBOL PII ROC;So;0;ON;;;;;N;;;;; +19F3;KHMER SYMBOL BEI ROC;So;0;ON;;;;;N;;;;; +19F4;KHMER SYMBOL BUON ROC;So;0;ON;;;;;N;;;;; +19F5;KHMER SYMBOL PRAM ROC;So;0;ON;;;;;N;;;;; +19F6;KHMER SYMBOL PRAM-MUOY ROC;So;0;ON;;;;;N;;;;; +19F7;KHMER SYMBOL PRAM-PII ROC;So;0;ON;;;;;N;;;;; +19F8;KHMER SYMBOL PRAM-BEI ROC;So;0;ON;;;;;N;;;;; +19F9;KHMER SYMBOL PRAM-BUON ROC;So;0;ON;;;;;N;;;;; +19FA;KHMER SYMBOL DAP ROC;So;0;ON;;;;;N;;;;; +19FB;KHMER SYMBOL DAP-MUOY ROC;So;0;ON;;;;;N;;;;; +19FC;KHMER SYMBOL DAP-PII ROC;So;0;ON;;;;;N;;;;; +19FD;KHMER SYMBOL DAP-BEI ROC;So;0;ON;;;;;N;;;;; +19FE;KHMER SYMBOL DAP-BUON ROC;So;0;ON;;;;;N;;;;; +19FF;KHMER SYMBOL DAP-PRAM ROC;So;0;ON;;;;;N;;;;; +1A00;BUGINESE LETTER KA;Lo;0;L;;;;;N;;;;; +1A01;BUGINESE LETTER GA;Lo;0;L;;;;;N;;;;; +1A02;BUGINESE LETTER NGA;Lo;0;L;;;;;N;;;;; +1A03;BUGINESE LETTER NGKA;Lo;0;L;;;;;N;;;;; +1A04;BUGINESE LETTER PA;Lo;0;L;;;;;N;;;;; +1A05;BUGINESE LETTER BA;Lo;0;L;;;;;N;;;;; +1A06;BUGINESE LETTER MA;Lo;0;L;;;;;N;;;;; +1A07;BUGINESE LETTER MPA;Lo;0;L;;;;;N;;;;; +1A08;BUGINESE LETTER TA;Lo;0;L;;;;;N;;;;; +1A09;BUGINESE LETTER DA;Lo;0;L;;;;;N;;;;; +1A0A;BUGINESE LETTER NA;Lo;0;L;;;;;N;;;;; +1A0B;BUGINESE LETTER NRA;Lo;0;L;;;;;N;;;;; +1A0C;BUGINESE LETTER CA;Lo;0;L;;;;;N;;;;; +1A0D;BUGINESE LETTER JA;Lo;0;L;;;;;N;;;;; +1A0E;BUGINESE LETTER NYA;Lo;0;L;;;;;N;;;;; +1A0F;BUGINESE LETTER NYCA;Lo;0;L;;;;;N;;;;; +1A10;BUGINESE LETTER YA;Lo;0;L;;;;;N;;;;; +1A11;BUGINESE LETTER RA;Lo;0;L;;;;;N;;;;; +1A12;BUGINESE LETTER LA;Lo;0;L;;;;;N;;;;; +1A13;BUGINESE LETTER VA;Lo;0;L;;;;;N;;;;; +1A14;BUGINESE LETTER SA;Lo;0;L;;;;;N;;;;; +1A15;BUGINESE LETTER A;Lo;0;L;;;;;N;;;;; +1A16;BUGINESE LETTER HA;Lo;0;L;;;;;N;;;;; +1A17;BUGINESE VOWEL SIGN I;Mn;230;NSM;;;;;N;;;;; +1A18;BUGINESE VOWEL SIGN U;Mn;220;NSM;;;;;N;;;;; +1A19;BUGINESE VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +1A1A;BUGINESE VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +1A1B;BUGINESE VOWEL SIGN AE;Mn;0;NSM;;;;;N;;;;; +1A1E;BUGINESE PALLAWA;Po;0;L;;;;;N;;;;; +1A1F;BUGINESE END OF SECTION;Po;0;L;;;;;N;;;;; +1A20;TAI THAM LETTER HIGH KA;Lo;0;L;;;;;N;;;;; +1A21;TAI THAM LETTER HIGH KHA;Lo;0;L;;;;;N;;;;; +1A22;TAI THAM LETTER HIGH KXA;Lo;0;L;;;;;N;;;;; +1A23;TAI THAM LETTER LOW KA;Lo;0;L;;;;;N;;;;; +1A24;TAI THAM LETTER LOW KXA;Lo;0;L;;;;;N;;;;; +1A25;TAI THAM LETTER LOW KHA;Lo;0;L;;;;;N;;;;; +1A26;TAI THAM LETTER NGA;Lo;0;L;;;;;N;;;;; +1A27;TAI THAM LETTER HIGH CA;Lo;0;L;;;;;N;;;;; +1A28;TAI THAM LETTER HIGH CHA;Lo;0;L;;;;;N;;;;; +1A29;TAI THAM LETTER LOW CA;Lo;0;L;;;;;N;;;;; +1A2A;TAI THAM LETTER LOW SA;Lo;0;L;;;;;N;;;;; +1A2B;TAI THAM LETTER LOW CHA;Lo;0;L;;;;;N;;;;; +1A2C;TAI THAM LETTER NYA;Lo;0;L;;;;;N;;;;; +1A2D;TAI THAM LETTER RATA;Lo;0;L;;;;;N;;;;; +1A2E;TAI THAM LETTER HIGH RATHA;Lo;0;L;;;;;N;;;;; +1A2F;TAI THAM LETTER DA;Lo;0;L;;;;;N;;;;; +1A30;TAI THAM LETTER LOW RATHA;Lo;0;L;;;;;N;;;;; +1A31;TAI THAM LETTER RANA;Lo;0;L;;;;;N;;;;; +1A32;TAI THAM LETTER HIGH TA;Lo;0;L;;;;;N;;;;; +1A33;TAI THAM LETTER HIGH THA;Lo;0;L;;;;;N;;;;; +1A34;TAI THAM LETTER LOW TA;Lo;0;L;;;;;N;;;;; +1A35;TAI THAM LETTER LOW THA;Lo;0;L;;;;;N;;;;; +1A36;TAI THAM LETTER NA;Lo;0;L;;;;;N;;;;; +1A37;TAI THAM LETTER BA;Lo;0;L;;;;;N;;;;; +1A38;TAI THAM LETTER HIGH PA;Lo;0;L;;;;;N;;;;; +1A39;TAI THAM LETTER HIGH PHA;Lo;0;L;;;;;N;;;;; +1A3A;TAI THAM LETTER HIGH FA;Lo;0;L;;;;;N;;;;; +1A3B;TAI THAM LETTER LOW PA;Lo;0;L;;;;;N;;;;; +1A3C;TAI THAM LETTER LOW FA;Lo;0;L;;;;;N;;;;; +1A3D;TAI THAM LETTER LOW PHA;Lo;0;L;;;;;N;;;;; +1A3E;TAI THAM LETTER MA;Lo;0;L;;;;;N;;;;; +1A3F;TAI THAM LETTER LOW YA;Lo;0;L;;;;;N;;;;; +1A40;TAI THAM LETTER HIGH YA;Lo;0;L;;;;;N;;;;; +1A41;TAI THAM LETTER RA;Lo;0;L;;;;;N;;;;; +1A42;TAI THAM LETTER RUE;Lo;0;L;;;;;N;;;;; +1A43;TAI THAM LETTER LA;Lo;0;L;;;;;N;;;;; +1A44;TAI THAM LETTER LUE;Lo;0;L;;;;;N;;;;; +1A45;TAI THAM LETTER WA;Lo;0;L;;;;;N;;;;; +1A46;TAI THAM LETTER HIGH SHA;Lo;0;L;;;;;N;;;;; +1A47;TAI THAM LETTER HIGH SSA;Lo;0;L;;;;;N;;;;; +1A48;TAI THAM LETTER HIGH SA;Lo;0;L;;;;;N;;;;; +1A49;TAI THAM LETTER HIGH HA;Lo;0;L;;;;;N;;;;; +1A4A;TAI THAM LETTER LLA;Lo;0;L;;;;;N;;;;; +1A4B;TAI THAM LETTER A;Lo;0;L;;;;;N;;;;; +1A4C;TAI THAM LETTER LOW HA;Lo;0;L;;;;;N;;;;; +1A4D;TAI THAM LETTER I;Lo;0;L;;;;;N;;;;; +1A4E;TAI THAM LETTER II;Lo;0;L;;;;;N;;;;; +1A4F;TAI THAM LETTER U;Lo;0;L;;;;;N;;;;; +1A50;TAI THAM LETTER UU;Lo;0;L;;;;;N;;;;; +1A51;TAI THAM LETTER EE;Lo;0;L;;;;;N;;;;; +1A52;TAI THAM LETTER OO;Lo;0;L;;;;;N;;;;; +1A53;TAI THAM LETTER LAE;Lo;0;L;;;;;N;;;;; +1A54;TAI THAM LETTER GREAT SA;Lo;0;L;;;;;N;;;;; +1A55;TAI THAM CONSONANT SIGN MEDIAL RA;Mc;0;L;;;;;N;;;;; +1A56;TAI THAM CONSONANT SIGN MEDIAL LA;Mn;0;NSM;;;;;N;;;;; +1A57;TAI THAM CONSONANT SIGN LA TANG LAI;Mc;0;L;;;;;N;;;;; +1A58;TAI THAM SIGN MAI KANG LAI;Mn;0;NSM;;;;;N;;;;; +1A59;TAI THAM CONSONANT SIGN FINAL NGA;Mn;0;NSM;;;;;N;;;;; +1A5A;TAI THAM CONSONANT SIGN LOW PA;Mn;0;NSM;;;;;N;;;;; +1A5B;TAI THAM CONSONANT SIGN HIGH RATHA OR LOW PA;Mn;0;NSM;;;;;N;;;;; +1A5C;TAI THAM CONSONANT SIGN MA;Mn;0;NSM;;;;;N;;;;; +1A5D;TAI THAM CONSONANT SIGN BA;Mn;0;NSM;;;;;N;;;;; +1A5E;TAI THAM CONSONANT SIGN SA;Mn;0;NSM;;;;;N;;;;; +1A60;TAI THAM SIGN SAKOT;Mn;9;NSM;;;;;N;;;;; +1A61;TAI THAM VOWEL SIGN A;Mc;0;L;;;;;N;;;;; +1A62;TAI THAM VOWEL SIGN MAI SAT;Mn;0;NSM;;;;;N;;;;; +1A63;TAI THAM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +1A64;TAI THAM VOWEL SIGN TALL AA;Mc;0;L;;;;;N;;;;; +1A65;TAI THAM VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +1A66;TAI THAM VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +1A67;TAI THAM VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;; +1A68;TAI THAM VOWEL SIGN UUE;Mn;0;NSM;;;;;N;;;;; +1A69;TAI THAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1A6A;TAI THAM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +1A6B;TAI THAM VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; +1A6C;TAI THAM VOWEL SIGN OA BELOW;Mn;0;NSM;;;;;N;;;;; +1A6D;TAI THAM VOWEL SIGN OY;Mc;0;L;;;;;N;;;;; +1A6E;TAI THAM VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +1A6F;TAI THAM VOWEL SIGN AE;Mc;0;L;;;;;N;;;;; +1A70;TAI THAM VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; +1A71;TAI THAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +1A72;TAI THAM VOWEL SIGN THAM AI;Mc;0;L;;;;;N;;;;; +1A73;TAI THAM VOWEL SIGN OA ABOVE;Mn;0;NSM;;;;;N;;;;; +1A74;TAI THAM SIGN MAI KANG;Mn;0;NSM;;;;;N;;;;; +1A75;TAI THAM SIGN TONE-1;Mn;230;NSM;;;;;N;;;;; +1A76;TAI THAM SIGN TONE-2;Mn;230;NSM;;;;;N;;;;; +1A77;TAI THAM SIGN KHUEN TONE-3;Mn;230;NSM;;;;;N;;;;; +1A78;TAI THAM SIGN KHUEN TONE-4;Mn;230;NSM;;;;;N;;;;; +1A79;TAI THAM SIGN KHUEN TONE-5;Mn;230;NSM;;;;;N;;;;; +1A7A;TAI THAM SIGN RA HAAM;Mn;230;NSM;;;;;N;;;;; +1A7B;TAI THAM SIGN MAI SAM;Mn;230;NSM;;;;;N;;;;; +1A7C;TAI THAM SIGN KHUEN-LUE KARAN;Mn;230;NSM;;;;;N;;;;; +1A7F;TAI THAM COMBINING CRYPTOGRAMMIC DOT;Mn;220;NSM;;;;;N;;;;; +1A80;TAI THAM HORA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +1A81;TAI THAM HORA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +1A82;TAI THAM HORA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +1A83;TAI THAM HORA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +1A84;TAI THAM HORA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +1A85;TAI THAM HORA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +1A86;TAI THAM HORA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +1A87;TAI THAM HORA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1A88;TAI THAM HORA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1A89;TAI THAM HORA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1A90;TAI THAM THAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +1A91;TAI THAM THAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +1A92;TAI THAM THAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +1A93;TAI THAM THAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +1A94;TAI THAM THAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +1A95;TAI THAM THAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +1A96;TAI THAM THAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +1A97;TAI THAM THAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1A98;TAI THAM THAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1A99;TAI THAM THAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1AA0;TAI THAM SIGN WIANG;Po;0;L;;;;;N;;;;; +1AA1;TAI THAM SIGN WIANGWAAK;Po;0;L;;;;;N;;;;; +1AA2;TAI THAM SIGN SAWAN;Po;0;L;;;;;N;;;;; +1AA3;TAI THAM SIGN KEOW;Po;0;L;;;;;N;;;;; +1AA4;TAI THAM SIGN HOY;Po;0;L;;;;;N;;;;; +1AA5;TAI THAM SIGN DOKMAI;Po;0;L;;;;;N;;;;; +1AA6;TAI THAM SIGN REVERSED ROTATED RANA;Po;0;L;;;;;N;;;;; +1AA7;TAI THAM SIGN MAI YAMOK;Lm;0;L;;;;;N;;;;; +1AA8;TAI THAM SIGN KAAN;Po;0;L;;;;;N;;;;; +1AA9;TAI THAM SIGN KAANKUU;Po;0;L;;;;;N;;;;; +1AAA;TAI THAM SIGN SATKAAN;Po;0;L;;;;;N;;;;; +1AAB;TAI THAM SIGN SATKAANKUU;Po;0;L;;;;;N;;;;; +1AAC;TAI THAM SIGN HANG;Po;0;L;;;;;N;;;;; +1AAD;TAI THAM SIGN CAANG;Po;0;L;;;;;N;;;;; +1AB0;COMBINING DOUBLED CIRCUMFLEX ACCENT;Mn;230;NSM;;;;;N;;;;; +1AB1;COMBINING DIAERESIS-RING;Mn;230;NSM;;;;;N;;;;; +1AB2;COMBINING INFINITY;Mn;230;NSM;;;;;N;;;;; +1AB3;COMBINING DOWNWARDS ARROW;Mn;230;NSM;;;;;N;;;;; +1AB4;COMBINING TRIPLE DOT;Mn;230;NSM;;;;;N;;;;; +1AB5;COMBINING X-X BELOW;Mn;220;NSM;;;;;N;;;;; +1AB6;COMBINING WIGGLY LINE BELOW;Mn;220;NSM;;;;;N;;;;; +1AB7;COMBINING OPEN MARK BELOW;Mn;220;NSM;;;;;N;;;;; +1AB8;COMBINING DOUBLE OPEN MARK BELOW;Mn;220;NSM;;;;;N;;;;; +1AB9;COMBINING LIGHT CENTRALIZATION STROKE BELOW;Mn;220;NSM;;;;;N;;;;; +1ABA;COMBINING STRONG CENTRALIZATION STROKE BELOW;Mn;220;NSM;;;;;N;;;;; +1ABB;COMBINING PARENTHESES ABOVE;Mn;230;NSM;;;;;N;;;;; +1ABC;COMBINING DOUBLE PARENTHESES ABOVE;Mn;230;NSM;;;;;N;;;;; +1ABD;COMBINING PARENTHESES BELOW;Mn;220;NSM;;;;;N;;;;; +1ABE;COMBINING PARENTHESES OVERLAY;Me;0;NSM;;;;;N;;;;; +1B00;BALINESE SIGN ULU RICEM;Mn;0;NSM;;;;;N;;;;; +1B01;BALINESE SIGN ULU CANDRA;Mn;0;NSM;;;;;N;;;;; +1B02;BALINESE SIGN CECEK;Mn;0;NSM;;;;;N;;;;; +1B03;BALINESE SIGN SURANG;Mn;0;NSM;;;;;N;;;;; +1B04;BALINESE SIGN BISAH;Mc;0;L;;;;;N;;;;; +1B05;BALINESE LETTER AKARA;Lo;0;L;;;;;N;;;;; +1B06;BALINESE LETTER AKARA TEDUNG;Lo;0;L;1B05 1B35;;;;N;;;;; +1B07;BALINESE LETTER IKARA;Lo;0;L;;;;;N;;;;; +1B08;BALINESE LETTER IKARA TEDUNG;Lo;0;L;1B07 1B35;;;;N;;;;; +1B09;BALINESE LETTER UKARA;Lo;0;L;;;;;N;;;;; +1B0A;BALINESE LETTER UKARA TEDUNG;Lo;0;L;1B09 1B35;;;;N;;;;; +1B0B;BALINESE LETTER RA REPA;Lo;0;L;;;;;N;;;;; +1B0C;BALINESE LETTER RA REPA TEDUNG;Lo;0;L;1B0B 1B35;;;;N;;;;; +1B0D;BALINESE LETTER LA LENGA;Lo;0;L;;;;;N;;;;; +1B0E;BALINESE LETTER LA LENGA TEDUNG;Lo;0;L;1B0D 1B35;;;;N;;;;; +1B0F;BALINESE LETTER EKARA;Lo;0;L;;;;;N;;;;; +1B10;BALINESE LETTER AIKARA;Lo;0;L;;;;;N;;;;; +1B11;BALINESE LETTER OKARA;Lo;0;L;;;;;N;;;;; +1B12;BALINESE LETTER OKARA TEDUNG;Lo;0;L;1B11 1B35;;;;N;;;;; +1B13;BALINESE LETTER KA;Lo;0;L;;;;;N;;;;; +1B14;BALINESE LETTER KA MAHAPRANA;Lo;0;L;;;;;N;;;;; +1B15;BALINESE LETTER GA;Lo;0;L;;;;;N;;;;; +1B16;BALINESE LETTER GA GORA;Lo;0;L;;;;;N;;;;; +1B17;BALINESE LETTER NGA;Lo;0;L;;;;;N;;;;; +1B18;BALINESE LETTER CA;Lo;0;L;;;;;N;;;;; +1B19;BALINESE LETTER CA LACA;Lo;0;L;;;;;N;;;;; +1B1A;BALINESE LETTER JA;Lo;0;L;;;;;N;;;;; +1B1B;BALINESE LETTER JA JERA;Lo;0;L;;;;;N;;;;; +1B1C;BALINESE LETTER NYA;Lo;0;L;;;;;N;;;;; +1B1D;BALINESE LETTER TA LATIK;Lo;0;L;;;;;N;;;;; +1B1E;BALINESE LETTER TA MURDA MAHAPRANA;Lo;0;L;;;;;N;;;;; +1B1F;BALINESE LETTER DA MURDA ALPAPRANA;Lo;0;L;;;;;N;;;;; +1B20;BALINESE LETTER DA MURDA MAHAPRANA;Lo;0;L;;;;;N;;;;; +1B21;BALINESE LETTER NA RAMBAT;Lo;0;L;;;;;N;;;;; +1B22;BALINESE LETTER TA;Lo;0;L;;;;;N;;;;; +1B23;BALINESE LETTER TA TAWA;Lo;0;L;;;;;N;;;;; +1B24;BALINESE LETTER DA;Lo;0;L;;;;;N;;;;; +1B25;BALINESE LETTER DA MADU;Lo;0;L;;;;;N;;;;; +1B26;BALINESE LETTER NA;Lo;0;L;;;;;N;;;;; +1B27;BALINESE LETTER PA;Lo;0;L;;;;;N;;;;; +1B28;BALINESE LETTER PA KAPAL;Lo;0;L;;;;;N;;;;; +1B29;BALINESE LETTER BA;Lo;0;L;;;;;N;;;;; +1B2A;BALINESE LETTER BA KEMBANG;Lo;0;L;;;;;N;;;;; +1B2B;BALINESE LETTER MA;Lo;0;L;;;;;N;;;;; +1B2C;BALINESE LETTER YA;Lo;0;L;;;;;N;;;;; +1B2D;BALINESE LETTER RA;Lo;0;L;;;;;N;;;;; +1B2E;BALINESE LETTER LA;Lo;0;L;;;;;N;;;;; +1B2F;BALINESE LETTER WA;Lo;0;L;;;;;N;;;;; +1B30;BALINESE LETTER SA SAGA;Lo;0;L;;;;;N;;;;; +1B31;BALINESE LETTER SA SAPA;Lo;0;L;;;;;N;;;;; +1B32;BALINESE LETTER SA;Lo;0;L;;;;;N;;;;; +1B33;BALINESE LETTER HA;Lo;0;L;;;;;N;;;;; +1B34;BALINESE SIGN REREKAN;Mn;7;NSM;;;;;N;;;;; +1B35;BALINESE VOWEL SIGN TEDUNG;Mc;0;L;;;;;N;;;;; +1B36;BALINESE VOWEL SIGN ULU;Mn;0;NSM;;;;;N;;;;; +1B37;BALINESE VOWEL SIGN ULU SARI;Mn;0;NSM;;;;;N;;;;; +1B38;BALINESE VOWEL SIGN SUKU;Mn;0;NSM;;;;;N;;;;; +1B39;BALINESE VOWEL SIGN SUKU ILUT;Mn;0;NSM;;;;;N;;;;; +1B3A;BALINESE VOWEL SIGN RA REPA;Mn;0;NSM;;;;;N;;;;; +1B3B;BALINESE VOWEL SIGN RA REPA TEDUNG;Mc;0;L;1B3A 1B35;;;;N;;;;; +1B3C;BALINESE VOWEL SIGN LA LENGA;Mn;0;NSM;;;;;N;;;;; +1B3D;BALINESE VOWEL SIGN LA LENGA TEDUNG;Mc;0;L;1B3C 1B35;;;;N;;;;; +1B3E;BALINESE VOWEL SIGN TALING;Mc;0;L;;;;;N;;;;; +1B3F;BALINESE VOWEL SIGN TALING REPA;Mc;0;L;;;;;N;;;;; +1B40;BALINESE VOWEL SIGN TALING TEDUNG;Mc;0;L;1B3E 1B35;;;;N;;;;; +1B41;BALINESE VOWEL SIGN TALING REPA TEDUNG;Mc;0;L;1B3F 1B35;;;;N;;;;; +1B42;BALINESE VOWEL SIGN PEPET;Mn;0;NSM;;;;;N;;;;; +1B43;BALINESE VOWEL SIGN PEPET TEDUNG;Mc;0;L;1B42 1B35;;;;N;;;;; +1B44;BALINESE ADEG ADEG;Mc;9;L;;;;;N;;;;; +1B45;BALINESE LETTER KAF SASAK;Lo;0;L;;;;;N;;;;; +1B46;BALINESE LETTER KHOT SASAK;Lo;0;L;;;;;N;;;;; +1B47;BALINESE LETTER TZIR SASAK;Lo;0;L;;;;;N;;;;; +1B48;BALINESE LETTER EF SASAK;Lo;0;L;;;;;N;;;;; +1B49;BALINESE LETTER VE SASAK;Lo;0;L;;;;;N;;;;; +1B4A;BALINESE LETTER ZAL SASAK;Lo;0;L;;;;;N;;;;; +1B4B;BALINESE LETTER ASYURA SASAK;Lo;0;L;;;;;N;;;;; +1B50;BALINESE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +1B51;BALINESE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +1B52;BALINESE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +1B53;BALINESE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +1B54;BALINESE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +1B55;BALINESE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +1B56;BALINESE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +1B57;BALINESE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1B58;BALINESE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1B59;BALINESE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1B5A;BALINESE PANTI;Po;0;L;;;;;N;;;;; +1B5B;BALINESE PAMADA;Po;0;L;;;;;N;;;;; +1B5C;BALINESE WINDU;Po;0;L;;;;;N;;;;; +1B5D;BALINESE CARIK PAMUNGKAH;Po;0;L;;;;;N;;;;; +1B5E;BALINESE CARIK SIKI;Po;0;L;;;;;N;;;;; +1B5F;BALINESE CARIK PAREREN;Po;0;L;;;;;N;;;;; +1B60;BALINESE PAMENENG;Po;0;L;;;;;N;;;;; +1B61;BALINESE MUSICAL SYMBOL DONG;So;0;L;;;;;N;;;;; +1B62;BALINESE MUSICAL SYMBOL DENG;So;0;L;;;;;N;;;;; +1B63;BALINESE MUSICAL SYMBOL DUNG;So;0;L;;;;;N;;;;; +1B64;BALINESE MUSICAL SYMBOL DANG;So;0;L;;;;;N;;;;; +1B65;BALINESE MUSICAL SYMBOL DANG SURANG;So;0;L;;;;;N;;;;; +1B66;BALINESE MUSICAL SYMBOL DING;So;0;L;;;;;N;;;;; +1B67;BALINESE MUSICAL SYMBOL DAENG;So;0;L;;;;;N;;;;; +1B68;BALINESE MUSICAL SYMBOL DEUNG;So;0;L;;;;;N;;;;; +1B69;BALINESE MUSICAL SYMBOL DAING;So;0;L;;;;;N;;;;; +1B6A;BALINESE MUSICAL SYMBOL DANG GEDE;So;0;L;;;;;N;;;;; +1B6B;BALINESE MUSICAL SYMBOL COMBINING TEGEH;Mn;230;NSM;;;;;N;;;;; +1B6C;BALINESE MUSICAL SYMBOL COMBINING ENDEP;Mn;220;NSM;;;;;N;;;;; +1B6D;BALINESE MUSICAL SYMBOL COMBINING KEMPUL;Mn;230;NSM;;;;;N;;;;; +1B6E;BALINESE MUSICAL SYMBOL COMBINING KEMPLI;Mn;230;NSM;;;;;N;;;;; +1B6F;BALINESE MUSICAL SYMBOL COMBINING JEGOGAN;Mn;230;NSM;;;;;N;;;;; +1B70;BALINESE MUSICAL SYMBOL COMBINING KEMPUL WITH JEGOGAN;Mn;230;NSM;;;;;N;;;;; +1B71;BALINESE MUSICAL SYMBOL COMBINING KEMPLI WITH JEGOGAN;Mn;230;NSM;;;;;N;;;;; +1B72;BALINESE MUSICAL SYMBOL COMBINING BENDE;Mn;230;NSM;;;;;N;;;;; +1B73;BALINESE MUSICAL SYMBOL COMBINING GONG;Mn;230;NSM;;;;;N;;;;; +1B74;BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DUG;So;0;L;;;;;N;;;;; +1B75;BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DAG;So;0;L;;;;;N;;;;; +1B76;BALINESE MUSICAL SYMBOL RIGHT-HAND CLOSED TUK;So;0;L;;;;;N;;;;; +1B77;BALINESE MUSICAL SYMBOL RIGHT-HAND CLOSED TAK;So;0;L;;;;;N;;;;; +1B78;BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PANG;So;0;L;;;;;N;;;;; +1B79;BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PUNG;So;0;L;;;;;N;;;;; +1B7A;BALINESE MUSICAL SYMBOL LEFT-HAND CLOSED PLAK;So;0;L;;;;;N;;;;; +1B7B;BALINESE MUSICAL SYMBOL LEFT-HAND CLOSED PLUK;So;0;L;;;;;N;;;;; +1B7C;BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PING;So;0;L;;;;;N;;;;; +1B80;SUNDANESE SIGN PANYECEK;Mn;0;NSM;;;;;N;;;;; +1B81;SUNDANESE SIGN PANGLAYAR;Mn;0;NSM;;;;;N;;;;; +1B82;SUNDANESE SIGN PANGWISAD;Mc;0;L;;;;;N;;;;; +1B83;SUNDANESE LETTER A;Lo;0;L;;;;;N;;;;; +1B84;SUNDANESE LETTER I;Lo;0;L;;;;;N;;;;; +1B85;SUNDANESE LETTER U;Lo;0;L;;;;;N;;;;; +1B86;SUNDANESE LETTER AE;Lo;0;L;;;;;N;;;;; +1B87;SUNDANESE LETTER O;Lo;0;L;;;;;N;;;;; +1B88;SUNDANESE LETTER E;Lo;0;L;;;;;N;;;;; +1B89;SUNDANESE LETTER EU;Lo;0;L;;;;;N;;;;; +1B8A;SUNDANESE LETTER KA;Lo;0;L;;;;;N;;;;; +1B8B;SUNDANESE LETTER QA;Lo;0;L;;;;;N;;;;; +1B8C;SUNDANESE LETTER GA;Lo;0;L;;;;;N;;;;; +1B8D;SUNDANESE LETTER NGA;Lo;0;L;;;;;N;;;;; +1B8E;SUNDANESE LETTER CA;Lo;0;L;;;;;N;;;;; +1B8F;SUNDANESE LETTER JA;Lo;0;L;;;;;N;;;;; +1B90;SUNDANESE LETTER ZA;Lo;0;L;;;;;N;;;;; +1B91;SUNDANESE LETTER NYA;Lo;0;L;;;;;N;;;;; +1B92;SUNDANESE LETTER TA;Lo;0;L;;;;;N;;;;; +1B93;SUNDANESE LETTER DA;Lo;0;L;;;;;N;;;;; +1B94;SUNDANESE LETTER NA;Lo;0;L;;;;;N;;;;; +1B95;SUNDANESE LETTER PA;Lo;0;L;;;;;N;;;;; +1B96;SUNDANESE LETTER FA;Lo;0;L;;;;;N;;;;; +1B97;SUNDANESE LETTER VA;Lo;0;L;;;;;N;;;;; +1B98;SUNDANESE LETTER BA;Lo;0;L;;;;;N;;;;; +1B99;SUNDANESE LETTER MA;Lo;0;L;;;;;N;;;;; +1B9A;SUNDANESE LETTER YA;Lo;0;L;;;;;N;;;;; +1B9B;SUNDANESE LETTER RA;Lo;0;L;;;;;N;;;;; +1B9C;SUNDANESE LETTER LA;Lo;0;L;;;;;N;;;;; +1B9D;SUNDANESE LETTER WA;Lo;0;L;;;;;N;;;;; +1B9E;SUNDANESE LETTER SA;Lo;0;L;;;;;N;;;;; +1B9F;SUNDANESE LETTER XA;Lo;0;L;;;;;N;;;;; +1BA0;SUNDANESE LETTER HA;Lo;0;L;;;;;N;;;;; +1BA1;SUNDANESE CONSONANT SIGN PAMINGKAL;Mc;0;L;;;;;N;;;;; +1BA2;SUNDANESE CONSONANT SIGN PANYAKRA;Mn;0;NSM;;;;;N;;;;; +1BA3;SUNDANESE CONSONANT SIGN PANYIKU;Mn;0;NSM;;;;;N;;;;; +1BA4;SUNDANESE VOWEL SIGN PANGHULU;Mn;0;NSM;;;;;N;;;;; +1BA5;SUNDANESE VOWEL SIGN PANYUKU;Mn;0;NSM;;;;;N;;;;; +1BA6;SUNDANESE VOWEL SIGN PANAELAENG;Mc;0;L;;;;;N;;;;; +1BA7;SUNDANESE VOWEL SIGN PANOLONG;Mc;0;L;;;;;N;;;;; +1BA8;SUNDANESE VOWEL SIGN PAMEPET;Mn;0;NSM;;;;;N;;;;; +1BA9;SUNDANESE VOWEL SIGN PANEULEUNG;Mn;0;NSM;;;;;N;;;;; +1BAA;SUNDANESE SIGN PAMAAEH;Mc;9;L;;;;;N;;;;; +1BAB;SUNDANESE SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +1BAC;SUNDANESE CONSONANT SIGN PASANGAN MA;Mn;0;NSM;;;;;N;;;;; +1BAD;SUNDANESE CONSONANT SIGN PASANGAN WA;Mn;0;NSM;;;;;N;;;;; +1BAE;SUNDANESE LETTER KHA;Lo;0;L;;;;;N;;;;; +1BAF;SUNDANESE LETTER SYA;Lo;0;L;;;;;N;;;;; +1BB0;SUNDANESE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +1BB1;SUNDANESE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +1BB2;SUNDANESE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +1BB3;SUNDANESE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +1BB4;SUNDANESE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +1BB5;SUNDANESE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +1BB6;SUNDANESE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +1BB7;SUNDANESE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1BB8;SUNDANESE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1BB9;SUNDANESE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1BBA;SUNDANESE AVAGRAHA;Lo;0;L;;;;;N;;;;; +1BBB;SUNDANESE LETTER REU;Lo;0;L;;;;;N;;;;; +1BBC;SUNDANESE LETTER LEU;Lo;0;L;;;;;N;;;;; +1BBD;SUNDANESE LETTER BHA;Lo;0;L;;;;;N;;;;; +1BBE;SUNDANESE LETTER FINAL K;Lo;0;L;;;;;N;;;;; +1BBF;SUNDANESE LETTER FINAL M;Lo;0;L;;;;;N;;;;; +1BC0;BATAK LETTER A;Lo;0;L;;;;;N;;;;; +1BC1;BATAK LETTER SIMALUNGUN A;Lo;0;L;;;;;N;;;;; +1BC2;BATAK LETTER HA;Lo;0;L;;;;;N;;;;; +1BC3;BATAK LETTER SIMALUNGUN HA;Lo;0;L;;;;;N;;;;; +1BC4;BATAK LETTER MANDAILING HA;Lo;0;L;;;;;N;;;;; +1BC5;BATAK LETTER BA;Lo;0;L;;;;;N;;;;; +1BC6;BATAK LETTER KARO BA;Lo;0;L;;;;;N;;;;; +1BC7;BATAK LETTER PA;Lo;0;L;;;;;N;;;;; +1BC8;BATAK LETTER SIMALUNGUN PA;Lo;0;L;;;;;N;;;;; +1BC9;BATAK LETTER NA;Lo;0;L;;;;;N;;;;; +1BCA;BATAK LETTER MANDAILING NA;Lo;0;L;;;;;N;;;;; +1BCB;BATAK LETTER WA;Lo;0;L;;;;;N;;;;; +1BCC;BATAK LETTER SIMALUNGUN WA;Lo;0;L;;;;;N;;;;; +1BCD;BATAK LETTER PAKPAK WA;Lo;0;L;;;;;N;;;;; +1BCE;BATAK LETTER GA;Lo;0;L;;;;;N;;;;; +1BCF;BATAK LETTER SIMALUNGUN GA;Lo;0;L;;;;;N;;;;; +1BD0;BATAK LETTER JA;Lo;0;L;;;;;N;;;;; +1BD1;BATAK LETTER DA;Lo;0;L;;;;;N;;;;; +1BD2;BATAK LETTER RA;Lo;0;L;;;;;N;;;;; +1BD3;BATAK LETTER SIMALUNGUN RA;Lo;0;L;;;;;N;;;;; +1BD4;BATAK LETTER MA;Lo;0;L;;;;;N;;;;; +1BD5;BATAK LETTER SIMALUNGUN MA;Lo;0;L;;;;;N;;;;; +1BD6;BATAK LETTER SOUTHERN TA;Lo;0;L;;;;;N;;;;; +1BD7;BATAK LETTER NORTHERN TA;Lo;0;L;;;;;N;;;;; +1BD8;BATAK LETTER SA;Lo;0;L;;;;;N;;;;; +1BD9;BATAK LETTER SIMALUNGUN SA;Lo;0;L;;;;;N;;;;; +1BDA;BATAK LETTER MANDAILING SA;Lo;0;L;;;;;N;;;;; +1BDB;BATAK LETTER YA;Lo;0;L;;;;;N;;;;; +1BDC;BATAK LETTER SIMALUNGUN YA;Lo;0;L;;;;;N;;;;; +1BDD;BATAK LETTER NGA;Lo;0;L;;;;;N;;;;; +1BDE;BATAK LETTER LA;Lo;0;L;;;;;N;;;;; +1BDF;BATAK LETTER SIMALUNGUN LA;Lo;0;L;;;;;N;;;;; +1BE0;BATAK LETTER NYA;Lo;0;L;;;;;N;;;;; +1BE1;BATAK LETTER CA;Lo;0;L;;;;;N;;;;; +1BE2;BATAK LETTER NDA;Lo;0;L;;;;;N;;;;; +1BE3;BATAK LETTER MBA;Lo;0;L;;;;;N;;;;; +1BE4;BATAK LETTER I;Lo;0;L;;;;;N;;;;; +1BE5;BATAK LETTER U;Lo;0;L;;;;;N;;;;; +1BE6;BATAK SIGN TOMPI;Mn;7;NSM;;;;;N;;;;; +1BE7;BATAK VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +1BE8;BATAK VOWEL SIGN PAKPAK E;Mn;0;NSM;;;;;N;;;;; +1BE9;BATAK VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;; +1BEA;BATAK VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +1BEB;BATAK VOWEL SIGN KARO I;Mc;0;L;;;;;N;;;;; +1BEC;BATAK VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +1BED;BATAK VOWEL SIGN KARO O;Mn;0;NSM;;;;;N;;;;; +1BEE;BATAK VOWEL SIGN U;Mc;0;L;;;;;N;;;;; +1BEF;BATAK VOWEL SIGN U FOR SIMALUNGUN SA;Mn;0;NSM;;;;;N;;;;; +1BF0;BATAK CONSONANT SIGN NG;Mn;0;NSM;;;;;N;;;;; +1BF1;BATAK CONSONANT SIGN H;Mn;0;NSM;;;;;N;;;;; +1BF2;BATAK PANGOLAT;Mc;9;L;;;;;N;;;;; +1BF3;BATAK PANONGONAN;Mc;9;L;;;;;N;;;;; +1BFC;BATAK SYMBOL BINDU NA METEK;Po;0;L;;;;;N;;;;; +1BFD;BATAK SYMBOL BINDU PINARBORAS;Po;0;L;;;;;N;;;;; +1BFE;BATAK SYMBOL BINDU JUDUL;Po;0;L;;;;;N;;;;; +1BFF;BATAK SYMBOL BINDU PANGOLAT;Po;0;L;;;;;N;;;;; +1C00;LEPCHA LETTER KA;Lo;0;L;;;;;N;;;;; +1C01;LEPCHA LETTER KLA;Lo;0;L;;;;;N;;;;; +1C02;LEPCHA LETTER KHA;Lo;0;L;;;;;N;;;;; +1C03;LEPCHA LETTER GA;Lo;0;L;;;;;N;;;;; +1C04;LEPCHA LETTER GLA;Lo;0;L;;;;;N;;;;; +1C05;LEPCHA LETTER NGA;Lo;0;L;;;;;N;;;;; +1C06;LEPCHA LETTER CA;Lo;0;L;;;;;N;;;;; +1C07;LEPCHA LETTER CHA;Lo;0;L;;;;;N;;;;; +1C08;LEPCHA LETTER JA;Lo;0;L;;;;;N;;;;; +1C09;LEPCHA LETTER NYA;Lo;0;L;;;;;N;;;;; +1C0A;LEPCHA LETTER TA;Lo;0;L;;;;;N;;;;; +1C0B;LEPCHA LETTER THA;Lo;0;L;;;;;N;;;;; +1C0C;LEPCHA LETTER DA;Lo;0;L;;;;;N;;;;; +1C0D;LEPCHA LETTER NA;Lo;0;L;;;;;N;;;;; +1C0E;LEPCHA LETTER PA;Lo;0;L;;;;;N;;;;; +1C0F;LEPCHA LETTER PLA;Lo;0;L;;;;;N;;;;; +1C10;LEPCHA LETTER PHA;Lo;0;L;;;;;N;;;;; +1C11;LEPCHA LETTER FA;Lo;0;L;;;;;N;;;;; +1C12;LEPCHA LETTER FLA;Lo;0;L;;;;;N;;;;; +1C13;LEPCHA LETTER BA;Lo;0;L;;;;;N;;;;; +1C14;LEPCHA LETTER BLA;Lo;0;L;;;;;N;;;;; +1C15;LEPCHA LETTER MA;Lo;0;L;;;;;N;;;;; +1C16;LEPCHA LETTER MLA;Lo;0;L;;;;;N;;;;; +1C17;LEPCHA LETTER TSA;Lo;0;L;;;;;N;;;;; +1C18;LEPCHA LETTER TSHA;Lo;0;L;;;;;N;;;;; +1C19;LEPCHA LETTER DZA;Lo;0;L;;;;;N;;;;; +1C1A;LEPCHA LETTER YA;Lo;0;L;;;;;N;;;;; +1C1B;LEPCHA LETTER RA;Lo;0;L;;;;;N;;;;; +1C1C;LEPCHA LETTER LA;Lo;0;L;;;;;N;;;;; +1C1D;LEPCHA LETTER HA;Lo;0;L;;;;;N;;;;; +1C1E;LEPCHA LETTER HLA;Lo;0;L;;;;;N;;;;; +1C1F;LEPCHA LETTER VA;Lo;0;L;;;;;N;;;;; +1C20;LEPCHA LETTER SA;Lo;0;L;;;;;N;;;;; +1C21;LEPCHA LETTER SHA;Lo;0;L;;;;;N;;;;; +1C22;LEPCHA LETTER WA;Lo;0;L;;;;;N;;;;; +1C23;LEPCHA LETTER A;Lo;0;L;;;;;N;;;;; +1C24;LEPCHA SUBJOINED LETTER YA;Mc;0;L;;;;;N;;;;; +1C25;LEPCHA SUBJOINED LETTER RA;Mc;0;L;;;;;N;;;;; +1C26;LEPCHA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +1C27;LEPCHA VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +1C28;LEPCHA VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +1C29;LEPCHA VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; +1C2A;LEPCHA VOWEL SIGN U;Mc;0;L;;;;;N;;;;; +1C2B;LEPCHA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; +1C2C;LEPCHA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +1C2D;LEPCHA CONSONANT SIGN K;Mn;0;NSM;;;;;N;;;;; +1C2E;LEPCHA CONSONANT SIGN M;Mn;0;NSM;;;;;N;;;;; +1C2F;LEPCHA CONSONANT SIGN L;Mn;0;NSM;;;;;N;;;;; +1C30;LEPCHA CONSONANT SIGN N;Mn;0;NSM;;;;;N;;;;; +1C31;LEPCHA CONSONANT SIGN P;Mn;0;NSM;;;;;N;;;;; +1C32;LEPCHA CONSONANT SIGN R;Mn;0;NSM;;;;;N;;;;; +1C33;LEPCHA CONSONANT SIGN T;Mn;0;NSM;;;;;N;;;;; +1C34;LEPCHA CONSONANT SIGN NYIN-DO;Mc;0;L;;;;;N;;;;; +1C35;LEPCHA CONSONANT SIGN KANG;Mc;0;L;;;;;N;;;;; +1C36;LEPCHA SIGN RAN;Mn;0;NSM;;;;;N;;;;; +1C37;LEPCHA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +1C3B;LEPCHA PUNCTUATION TA-ROL;Po;0;L;;;;;N;;;;; +1C3C;LEPCHA PUNCTUATION NYET THYOOM TA-ROL;Po;0;L;;;;;N;;;;; +1C3D;LEPCHA PUNCTUATION CER-WA;Po;0;L;;;;;N;;;;; +1C3E;LEPCHA PUNCTUATION TSHOOK CER-WA;Po;0;L;;;;;N;;;;; +1C3F;LEPCHA PUNCTUATION TSHOOK;Po;0;L;;;;;N;;;;; +1C40;LEPCHA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +1C41;LEPCHA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +1C42;LEPCHA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +1C43;LEPCHA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +1C44;LEPCHA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +1C45;LEPCHA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +1C46;LEPCHA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +1C47;LEPCHA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1C48;LEPCHA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1C49;LEPCHA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1C4D;LEPCHA LETTER TTA;Lo;0;L;;;;;N;;;;; +1C4E;LEPCHA LETTER TTHA;Lo;0;L;;;;;N;;;;; +1C4F;LEPCHA LETTER DDA;Lo;0;L;;;;;N;;;;; +1C50;OL CHIKI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +1C51;OL CHIKI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +1C52;OL CHIKI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +1C53;OL CHIKI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +1C54;OL CHIKI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +1C55;OL CHIKI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +1C56;OL CHIKI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +1C57;OL CHIKI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1C58;OL CHIKI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1C59;OL CHIKI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1C5A;OL CHIKI LETTER LA;Lo;0;L;;;;;N;;;;; +1C5B;OL CHIKI LETTER AT;Lo;0;L;;;;;N;;;;; +1C5C;OL CHIKI LETTER AG;Lo;0;L;;;;;N;;;;; +1C5D;OL CHIKI LETTER ANG;Lo;0;L;;;;;N;;;;; +1C5E;OL CHIKI LETTER AL;Lo;0;L;;;;;N;;;;; +1C5F;OL CHIKI LETTER LAA;Lo;0;L;;;;;N;;;;; +1C60;OL CHIKI LETTER AAK;Lo;0;L;;;;;N;;;;; +1C61;OL CHIKI LETTER AAJ;Lo;0;L;;;;;N;;;;; +1C62;OL CHIKI LETTER AAM;Lo;0;L;;;;;N;;;;; +1C63;OL CHIKI LETTER AAW;Lo;0;L;;;;;N;;;;; +1C64;OL CHIKI LETTER LI;Lo;0;L;;;;;N;;;;; +1C65;OL CHIKI LETTER IS;Lo;0;L;;;;;N;;;;; +1C66;OL CHIKI LETTER IH;Lo;0;L;;;;;N;;;;; +1C67;OL CHIKI LETTER INY;Lo;0;L;;;;;N;;;;; +1C68;OL CHIKI LETTER IR;Lo;0;L;;;;;N;;;;; +1C69;OL CHIKI LETTER LU;Lo;0;L;;;;;N;;;;; +1C6A;OL CHIKI LETTER UC;Lo;0;L;;;;;N;;;;; +1C6B;OL CHIKI LETTER UD;Lo;0;L;;;;;N;;;;; +1C6C;OL CHIKI LETTER UNN;Lo;0;L;;;;;N;;;;; +1C6D;OL CHIKI LETTER UY;Lo;0;L;;;;;N;;;;; +1C6E;OL CHIKI LETTER LE;Lo;0;L;;;;;N;;;;; +1C6F;OL CHIKI LETTER EP;Lo;0;L;;;;;N;;;;; +1C70;OL CHIKI LETTER EDD;Lo;0;L;;;;;N;;;;; +1C71;OL CHIKI LETTER EN;Lo;0;L;;;;;N;;;;; +1C72;OL CHIKI LETTER ERR;Lo;0;L;;;;;N;;;;; +1C73;OL CHIKI LETTER LO;Lo;0;L;;;;;N;;;;; +1C74;OL CHIKI LETTER OTT;Lo;0;L;;;;;N;;;;; +1C75;OL CHIKI LETTER OB;Lo;0;L;;;;;N;;;;; +1C76;OL CHIKI LETTER OV;Lo;0;L;;;;;N;;;;; +1C77;OL CHIKI LETTER OH;Lo;0;L;;;;;N;;;;; +1C78;OL CHIKI MU TTUDDAG;Lm;0;L;;;;;N;;;;; +1C79;OL CHIKI GAAHLAA TTUDDAAG;Lm;0;L;;;;;N;;;;; +1C7A;OL CHIKI MU-GAAHLAA TTUDDAAG;Lm;0;L;;;;;N;;;;; +1C7B;OL CHIKI RELAA;Lm;0;L;;;;;N;;;;; +1C7C;OL CHIKI PHAARKAA;Lm;0;L;;;;;N;;;;; +1C7D;OL CHIKI AHAD;Lm;0;L;;;;;N;;;;; +1C7E;OL CHIKI PUNCTUATION MUCAAD;Po;0;L;;;;;N;;;;; +1C7F;OL CHIKI PUNCTUATION DOUBLE MUCAAD;Po;0;L;;;;;N;;;;; +1C80;CYRILLIC SMALL LETTER ROUNDED VE;Ll;0;L;;;;;N;;;0412;;0412 +1C81;CYRILLIC SMALL LETTER LONG-LEGGED DE;Ll;0;L;;;;;N;;;0414;;0414 +1C82;CYRILLIC SMALL LETTER NARROW O;Ll;0;L;;;;;N;;;041E;;041E +1C83;CYRILLIC SMALL LETTER WIDE ES;Ll;0;L;;;;;N;;;0421;;0421 +1C84;CYRILLIC SMALL LETTER TALL TE;Ll;0;L;;;;;N;;;0422;;0422 +1C85;CYRILLIC SMALL LETTER THREE-LEGGED TE;Ll;0;L;;;;;N;;;0422;;0422 +1C86;CYRILLIC SMALL LETTER TALL HARD SIGN;Ll;0;L;;;;;N;;;042A;;042A +1C87;CYRILLIC SMALL LETTER TALL YAT;Ll;0;L;;;;;N;;;0462;;0462 +1C88;CYRILLIC SMALL LETTER UNBLENDED UK;Ll;0;L;;;;;N;;;A64A;;A64A +1CC0;SUNDANESE PUNCTUATION BINDU SURYA;Po;0;L;;;;;N;;;;; +1CC1;SUNDANESE PUNCTUATION BINDU PANGLONG;Po;0;L;;;;;N;;;;; +1CC2;SUNDANESE PUNCTUATION BINDU PURNAMA;Po;0;L;;;;;N;;;;; +1CC3;SUNDANESE PUNCTUATION BINDU CAKRA;Po;0;L;;;;;N;;;;; +1CC4;SUNDANESE PUNCTUATION BINDU LEU SATANGA;Po;0;L;;;;;N;;;;; +1CC5;SUNDANESE PUNCTUATION BINDU KA SATANGA;Po;0;L;;;;;N;;;;; +1CC6;SUNDANESE PUNCTUATION BINDU DA SATANGA;Po;0;L;;;;;N;;;;; +1CC7;SUNDANESE PUNCTUATION BINDU BA SATANGA;Po;0;L;;;;;N;;;;; +1CD0;VEDIC TONE KARSHANA;Mn;230;NSM;;;;;N;;;;; +1CD1;VEDIC TONE SHARA;Mn;230;NSM;;;;;N;;;;; +1CD2;VEDIC TONE PRENKHA;Mn;230;NSM;;;;;N;;;;; +1CD3;VEDIC SIGN NIHSHVASA;Po;0;L;;;;;N;;;;; +1CD4;VEDIC SIGN YAJURVEDIC MIDLINE SVARITA;Mn;1;NSM;;;;;N;;;;; +1CD5;VEDIC TONE YAJURVEDIC AGGRAVATED INDEPENDENT SVARITA;Mn;220;NSM;;;;;N;;;;; +1CD6;VEDIC TONE YAJURVEDIC INDEPENDENT SVARITA;Mn;220;NSM;;;;;N;;;;; +1CD7;VEDIC TONE YAJURVEDIC KATHAKA INDEPENDENT SVARITA;Mn;220;NSM;;;;;N;;;;; +1CD8;VEDIC TONE CANDRA BELOW;Mn;220;NSM;;;;;N;;;;; +1CD9;VEDIC TONE YAJURVEDIC KATHAKA INDEPENDENT SVARITA SCHROEDER;Mn;220;NSM;;;;;N;;;;; +1CDA;VEDIC TONE DOUBLE SVARITA;Mn;230;NSM;;;;;N;;;;; +1CDB;VEDIC TONE TRIPLE SVARITA;Mn;230;NSM;;;;;N;;;;; +1CDC;VEDIC TONE KATHAKA ANUDATTA;Mn;220;NSM;;;;;N;;;;; +1CDD;VEDIC TONE DOT BELOW;Mn;220;NSM;;;;;N;;;;; +1CDE;VEDIC TONE TWO DOTS BELOW;Mn;220;NSM;;;;;N;;;;; +1CDF;VEDIC TONE THREE DOTS BELOW;Mn;220;NSM;;;;;N;;;;; +1CE0;VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA;Mn;230;NSM;;;;;N;;;;; +1CE1;VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA;Mc;0;L;;;;;N;;;;; +1CE2;VEDIC SIGN VISARGA SVARITA;Mn;1;NSM;;;;;N;;;;; +1CE3;VEDIC SIGN VISARGA UDATTA;Mn;1;NSM;;;;;N;;;;; +1CE4;VEDIC SIGN REVERSED VISARGA UDATTA;Mn;1;NSM;;;;;N;;;;; +1CE5;VEDIC SIGN VISARGA ANUDATTA;Mn;1;NSM;;;;;N;;;;; +1CE6;VEDIC SIGN REVERSED VISARGA ANUDATTA;Mn;1;NSM;;;;;N;;;;; +1CE7;VEDIC SIGN VISARGA UDATTA WITH TAIL;Mn;1;NSM;;;;;N;;;;; +1CE8;VEDIC SIGN VISARGA ANUDATTA WITH TAIL;Mn;1;NSM;;;;;N;;;;; +1CE9;VEDIC SIGN ANUSVARA ANTARGOMUKHA;Lo;0;L;;;;;N;;;;; +1CEA;VEDIC SIGN ANUSVARA BAHIRGOMUKHA;Lo;0;L;;;;;N;;;;; +1CEB;VEDIC SIGN ANUSVARA VAMAGOMUKHA;Lo;0;L;;;;;N;;;;; +1CEC;VEDIC SIGN ANUSVARA VAMAGOMUKHA WITH TAIL;Lo;0;L;;;;;N;;;;; +1CED;VEDIC SIGN TIRYAK;Mn;220;NSM;;;;;N;;;;; +1CEE;VEDIC SIGN HEXIFORM LONG ANUSVARA;Lo;0;L;;;;;N;;;;; +1CEF;VEDIC SIGN LONG ANUSVARA;Lo;0;L;;;;;N;;;;; +1CF0;VEDIC SIGN RTHANG LONG ANUSVARA;Lo;0;L;;;;;N;;;;; +1CF1;VEDIC SIGN ANUSVARA UBHAYATO MUKHA;Lo;0;L;;;;;N;;;;; +1CF2;VEDIC SIGN ARDHAVISARGA;Mc;0;L;;;;;N;;;;; +1CF3;VEDIC SIGN ROTATED ARDHAVISARGA;Mc;0;L;;;;;N;;;;; +1CF4;VEDIC TONE CANDRA ABOVE;Mn;230;NSM;;;;;N;;;;; +1CF5;VEDIC SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;; +1CF6;VEDIC SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;; +1CF8;VEDIC TONE RING ABOVE;Mn;230;NSM;;;;;N;;;;; +1CF9;VEDIC TONE DOUBLE RING ABOVE;Mn;230;NSM;;;;;N;;;;; +1D00;LATIN LETTER SMALL CAPITAL A;Ll;0;L;;;;;N;;;;; +1D01;LATIN LETTER SMALL CAPITAL AE;Ll;0;L;;;;;N;;;;; +1D02;LATIN SMALL LETTER TURNED AE;Ll;0;L;;;;;N;;;;; +1D03;LATIN LETTER SMALL CAPITAL BARRED B;Ll;0;L;;;;;N;;;;; +1D04;LATIN LETTER SMALL CAPITAL C;Ll;0;L;;;;;N;;;;; +1D05;LATIN LETTER SMALL CAPITAL D;Ll;0;L;;;;;N;;;;; +1D06;LATIN LETTER SMALL CAPITAL ETH;Ll;0;L;;;;;N;;;;; +1D07;LATIN LETTER SMALL CAPITAL E;Ll;0;L;;;;;N;;;;; +1D08;LATIN SMALL LETTER TURNED OPEN E;Ll;0;L;;;;;N;;;;; +1D09;LATIN SMALL LETTER TURNED I;Ll;0;L;;;;;N;;;;; +1D0A;LATIN LETTER SMALL CAPITAL J;Ll;0;L;;;;;N;;;;; +1D0B;LATIN LETTER SMALL CAPITAL K;Ll;0;L;;;;;N;;;;; +1D0C;LATIN LETTER SMALL CAPITAL L WITH STROKE;Ll;0;L;;;;;N;;;;; +1D0D;LATIN LETTER SMALL CAPITAL M;Ll;0;L;;;;;N;;;;; +1D0E;LATIN LETTER SMALL CAPITAL REVERSED N;Ll;0;L;;;;;N;;;;; +1D0F;LATIN LETTER SMALL CAPITAL O;Ll;0;L;;;;;N;;;;; +1D10;LATIN LETTER SMALL CAPITAL OPEN O;Ll;0;L;;;;;N;;;;; +1D11;LATIN SMALL LETTER SIDEWAYS O;Ll;0;L;;;;;N;;;;; +1D12;LATIN SMALL LETTER SIDEWAYS OPEN O;Ll;0;L;;;;;N;;;;; +1D13;LATIN SMALL LETTER SIDEWAYS O WITH STROKE;Ll;0;L;;;;;N;;;;; +1D14;LATIN SMALL LETTER TURNED OE;Ll;0;L;;;;;N;;;;; +1D15;LATIN LETTER SMALL CAPITAL OU;Ll;0;L;;;;;N;;;;; +1D16;LATIN SMALL LETTER TOP HALF O;Ll;0;L;;;;;N;;;;; +1D17;LATIN SMALL LETTER BOTTOM HALF O;Ll;0;L;;;;;N;;;;; +1D18;LATIN LETTER SMALL CAPITAL P;Ll;0;L;;;;;N;;;;; +1D19;LATIN LETTER SMALL CAPITAL REVERSED R;Ll;0;L;;;;;N;;;;; +1D1A;LATIN LETTER SMALL CAPITAL TURNED R;Ll;0;L;;;;;N;;;;; +1D1B;LATIN LETTER SMALL CAPITAL T;Ll;0;L;;;;;N;;;;; +1D1C;LATIN LETTER SMALL CAPITAL U;Ll;0;L;;;;;N;;;;; +1D1D;LATIN SMALL LETTER SIDEWAYS U;Ll;0;L;;;;;N;;;;; +1D1E;LATIN SMALL LETTER SIDEWAYS DIAERESIZED U;Ll;0;L;;;;;N;;;;; +1D1F;LATIN SMALL LETTER SIDEWAYS TURNED M;Ll;0;L;;;;;N;;;;; +1D20;LATIN LETTER SMALL CAPITAL V;Ll;0;L;;;;;N;;;;; +1D21;LATIN LETTER SMALL CAPITAL W;Ll;0;L;;;;;N;;;;; +1D22;LATIN LETTER SMALL CAPITAL Z;Ll;0;L;;;;;N;;;;; +1D23;LATIN LETTER SMALL CAPITAL EZH;Ll;0;L;;;;;N;;;;; +1D24;LATIN LETTER VOICED LARYNGEAL SPIRANT;Ll;0;L;;;;;N;;;;; +1D25;LATIN LETTER AIN;Ll;0;L;;;;;N;;;;; +1D26;GREEK LETTER SMALL CAPITAL GAMMA;Ll;0;L;;;;;N;;;;; +1D27;GREEK LETTER SMALL CAPITAL LAMDA;Ll;0;L;;;;;N;;;;; +1D28;GREEK LETTER SMALL CAPITAL PI;Ll;0;L;;;;;N;;;;; +1D29;GREEK LETTER SMALL CAPITAL RHO;Ll;0;L;;;;;N;;;;; +1D2A;GREEK LETTER SMALL CAPITAL PSI;Ll;0;L;;;;;N;;;;; +1D2B;CYRILLIC LETTER SMALL CAPITAL EL;Ll;0;L;;;;;N;;;;; +1D2C;MODIFIER LETTER CAPITAL A;Lm;0;L;<super> 0041;;;;N;;;;; +1D2D;MODIFIER LETTER CAPITAL AE;Lm;0;L;<super> 00C6;;;;N;;;;; +1D2E;MODIFIER LETTER CAPITAL B;Lm;0;L;<super> 0042;;;;N;;;;; +1D2F;MODIFIER LETTER CAPITAL BARRED B;Lm;0;L;;;;;N;;;;; +1D30;MODIFIER LETTER CAPITAL D;Lm;0;L;<super> 0044;;;;N;;;;; +1D31;MODIFIER LETTER CAPITAL E;Lm;0;L;<super> 0045;;;;N;;;;; +1D32;MODIFIER LETTER CAPITAL REVERSED E;Lm;0;L;<super> 018E;;;;N;;;;; +1D33;MODIFIER LETTER CAPITAL G;Lm;0;L;<super> 0047;;;;N;;;;; +1D34;MODIFIER LETTER CAPITAL H;Lm;0;L;<super> 0048;;;;N;;;;; +1D35;MODIFIER LETTER CAPITAL I;Lm;0;L;<super> 0049;;;;N;;;;; +1D36;MODIFIER LETTER CAPITAL J;Lm;0;L;<super> 004A;;;;N;;;;; +1D37;MODIFIER LETTER CAPITAL K;Lm;0;L;<super> 004B;;;;N;;;;; +1D38;MODIFIER LETTER CAPITAL L;Lm;0;L;<super> 004C;;;;N;;;;; +1D39;MODIFIER LETTER CAPITAL M;Lm;0;L;<super> 004D;;;;N;;;;; +1D3A;MODIFIER LETTER CAPITAL N;Lm;0;L;<super> 004E;;;;N;;;;; +1D3B;MODIFIER LETTER CAPITAL REVERSED N;Lm;0;L;;;;;N;;;;; +1D3C;MODIFIER LETTER CAPITAL O;Lm;0;L;<super> 004F;;;;N;;;;; +1D3D;MODIFIER LETTER CAPITAL OU;Lm;0;L;<super> 0222;;;;N;;;;; +1D3E;MODIFIER LETTER CAPITAL P;Lm;0;L;<super> 0050;;;;N;;;;; +1D3F;MODIFIER LETTER CAPITAL R;Lm;0;L;<super> 0052;;;;N;;;;; +1D40;MODIFIER LETTER CAPITAL T;Lm;0;L;<super> 0054;;;;N;;;;; +1D41;MODIFIER LETTER CAPITAL U;Lm;0;L;<super> 0055;;;;N;;;;; +1D42;MODIFIER LETTER CAPITAL W;Lm;0;L;<super> 0057;;;;N;;;;; +1D43;MODIFIER LETTER SMALL A;Lm;0;L;<super> 0061;;;;N;;;;; +1D44;MODIFIER LETTER SMALL TURNED A;Lm;0;L;<super> 0250;;;;N;;;;; +1D45;MODIFIER LETTER SMALL ALPHA;Lm;0;L;<super> 0251;;;;N;;;;; +1D46;MODIFIER LETTER SMALL TURNED AE;Lm;0;L;<super> 1D02;;;;N;;;;; +1D47;MODIFIER LETTER SMALL B;Lm;0;L;<super> 0062;;;;N;;;;; +1D48;MODIFIER LETTER SMALL D;Lm;0;L;<super> 0064;;;;N;;;;; +1D49;MODIFIER LETTER SMALL E;Lm;0;L;<super> 0065;;;;N;;;;; +1D4A;MODIFIER LETTER SMALL SCHWA;Lm;0;L;<super> 0259;;;;N;;;;; +1D4B;MODIFIER LETTER SMALL OPEN E;Lm;0;L;<super> 025B;;;;N;;;;; +1D4C;MODIFIER LETTER SMALL TURNED OPEN E;Lm;0;L;<super> 025C;;;;N;;;;; +1D4D;MODIFIER LETTER SMALL G;Lm;0;L;<super> 0067;;;;N;;;;; +1D4E;MODIFIER LETTER SMALL TURNED I;Lm;0;L;;;;;N;;;;; +1D4F;MODIFIER LETTER SMALL K;Lm;0;L;<super> 006B;;;;N;;;;; +1D50;MODIFIER LETTER SMALL M;Lm;0;L;<super> 006D;;;;N;;;;; +1D51;MODIFIER LETTER SMALL ENG;Lm;0;L;<super> 014B;;;;N;;;;; +1D52;MODIFIER LETTER SMALL O;Lm;0;L;<super> 006F;;;;N;;;;; +1D53;MODIFIER LETTER SMALL OPEN O;Lm;0;L;<super> 0254;;;;N;;;;; +1D54;MODIFIER LETTER SMALL TOP HALF O;Lm;0;L;<super> 1D16;;;;N;;;;; +1D55;MODIFIER LETTER SMALL BOTTOM HALF O;Lm;0;L;<super> 1D17;;;;N;;;;; +1D56;MODIFIER LETTER SMALL P;Lm;0;L;<super> 0070;;;;N;;;;; +1D57;MODIFIER LETTER SMALL T;Lm;0;L;<super> 0074;;;;N;;;;; +1D58;MODIFIER LETTER SMALL U;Lm;0;L;<super> 0075;;;;N;;;;; +1D59;MODIFIER LETTER SMALL SIDEWAYS U;Lm;0;L;<super> 1D1D;;;;N;;;;; +1D5A;MODIFIER LETTER SMALL TURNED M;Lm;0;L;<super> 026F;;;;N;;;;; +1D5B;MODIFIER LETTER SMALL V;Lm;0;L;<super> 0076;;;;N;;;;; +1D5C;MODIFIER LETTER SMALL AIN;Lm;0;L;<super> 1D25;;;;N;;;;; +1D5D;MODIFIER LETTER SMALL BETA;Lm;0;L;<super> 03B2;;;;N;;;;; +1D5E;MODIFIER LETTER SMALL GREEK GAMMA;Lm;0;L;<super> 03B3;;;;N;;;;; +1D5F;MODIFIER LETTER SMALL DELTA;Lm;0;L;<super> 03B4;;;;N;;;;; +1D60;MODIFIER LETTER SMALL GREEK PHI;Lm;0;L;<super> 03C6;;;;N;;;;; +1D61;MODIFIER LETTER SMALL CHI;Lm;0;L;<super> 03C7;;;;N;;;;; +1D62;LATIN SUBSCRIPT SMALL LETTER I;Lm;0;L;<sub> 0069;;;;N;;;;; +1D63;LATIN SUBSCRIPT SMALL LETTER R;Lm;0;L;<sub> 0072;;;;N;;;;; +1D64;LATIN SUBSCRIPT SMALL LETTER U;Lm;0;L;<sub> 0075;;;;N;;;;; +1D65;LATIN SUBSCRIPT SMALL LETTER V;Lm;0;L;<sub> 0076;;;;N;;;;; +1D66;GREEK SUBSCRIPT SMALL LETTER BETA;Lm;0;L;<sub> 03B2;;;;N;;;;; +1D67;GREEK SUBSCRIPT SMALL LETTER GAMMA;Lm;0;L;<sub> 03B3;;;;N;;;;; +1D68;GREEK SUBSCRIPT SMALL LETTER RHO;Lm;0;L;<sub> 03C1;;;;N;;;;; +1D69;GREEK SUBSCRIPT SMALL LETTER PHI;Lm;0;L;<sub> 03C6;;;;N;;;;; +1D6A;GREEK SUBSCRIPT SMALL LETTER CHI;Lm;0;L;<sub> 03C7;;;;N;;;;; +1D6B;LATIN SMALL LETTER UE;Ll;0;L;;;;;N;;;;; +1D6C;LATIN SMALL LETTER B WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; +1D6D;LATIN SMALL LETTER D WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; +1D6E;LATIN SMALL LETTER F WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; +1D6F;LATIN SMALL LETTER M WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; +1D70;LATIN SMALL LETTER N WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; +1D71;LATIN SMALL LETTER P WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; +1D72;LATIN SMALL LETTER R WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; +1D73;LATIN SMALL LETTER R WITH FISHHOOK AND MIDDLE TILDE;Ll;0;L;;;;;N;;;;; +1D74;LATIN SMALL LETTER S WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; +1D75;LATIN SMALL LETTER T WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; +1D76;LATIN SMALL LETTER Z WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; +1D77;LATIN SMALL LETTER TURNED G;Ll;0;L;;;;;N;;;;; +1D78;MODIFIER LETTER CYRILLIC EN;Lm;0;L;<super> 043D;;;;N;;;;; +1D79;LATIN SMALL LETTER INSULAR G;Ll;0;L;;;;;N;;;A77D;;A77D +1D7A;LATIN SMALL LETTER TH WITH STRIKETHROUGH;Ll;0;L;;;;;N;;;;; +1D7B;LATIN SMALL CAPITAL LETTER I WITH STROKE;Ll;0;L;;;;;N;;;;; +1D7C;LATIN SMALL LETTER IOTA WITH STROKE;Ll;0;L;;;;;N;;;;; +1D7D;LATIN SMALL LETTER P WITH STROKE;Ll;0;L;;;;;N;;;2C63;;2C63 +1D7E;LATIN SMALL CAPITAL LETTER U WITH STROKE;Ll;0;L;;;;;N;;;;; +1D7F;LATIN SMALL LETTER UPSILON WITH STROKE;Ll;0;L;;;;;N;;;;; +1D80;LATIN SMALL LETTER B WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D81;LATIN SMALL LETTER D WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D82;LATIN SMALL LETTER F WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D83;LATIN SMALL LETTER G WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D84;LATIN SMALL LETTER K WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D85;LATIN SMALL LETTER L WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D86;LATIN SMALL LETTER M WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D87;LATIN SMALL LETTER N WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D88;LATIN SMALL LETTER P WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D89;LATIN SMALL LETTER R WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D8A;LATIN SMALL LETTER S WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D8B;LATIN SMALL LETTER ESH WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D8C;LATIN SMALL LETTER V WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D8D;LATIN SMALL LETTER X WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D8E;LATIN SMALL LETTER Z WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +1D8F;LATIN SMALL LETTER A WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; +1D90;LATIN SMALL LETTER ALPHA WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; +1D91;LATIN SMALL LETTER D WITH HOOK AND TAIL;Ll;0;L;;;;;N;;;;; +1D92;LATIN SMALL LETTER E WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; +1D93;LATIN SMALL LETTER OPEN E WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; +1D94;LATIN SMALL LETTER REVERSED OPEN E WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; +1D95;LATIN SMALL LETTER SCHWA WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; +1D96;LATIN SMALL LETTER I WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; +1D97;LATIN SMALL LETTER OPEN O WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; +1D98;LATIN SMALL LETTER ESH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; +1D99;LATIN SMALL LETTER U WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; +1D9A;LATIN SMALL LETTER EZH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;; +1D9B;MODIFIER LETTER SMALL TURNED ALPHA;Lm;0;L;<super> 0252;;;;N;;;;; +1D9C;MODIFIER LETTER SMALL C;Lm;0;L;<super> 0063;;;;N;;;;; +1D9D;MODIFIER LETTER SMALL C WITH CURL;Lm;0;L;<super> 0255;;;;N;;;;; +1D9E;MODIFIER LETTER SMALL ETH;Lm;0;L;<super> 00F0;;;;N;;;;; +1D9F;MODIFIER LETTER SMALL REVERSED OPEN E;Lm;0;L;<super> 025C;;;;N;;;;; +1DA0;MODIFIER LETTER SMALL F;Lm;0;L;<super> 0066;;;;N;;;;; +1DA1;MODIFIER LETTER SMALL DOTLESS J WITH STROKE;Lm;0;L;<super> 025F;;;;N;;;;; +1DA2;MODIFIER LETTER SMALL SCRIPT G;Lm;0;L;<super> 0261;;;;N;;;;; +1DA3;MODIFIER LETTER SMALL TURNED H;Lm;0;L;<super> 0265;;;;N;;;;; +1DA4;MODIFIER LETTER SMALL I WITH STROKE;Lm;0;L;<super> 0268;;;;N;;;;; +1DA5;MODIFIER LETTER SMALL IOTA;Lm;0;L;<super> 0269;;;;N;;;;; +1DA6;MODIFIER LETTER SMALL CAPITAL I;Lm;0;L;<super> 026A;;;;N;;;;; +1DA7;MODIFIER LETTER SMALL CAPITAL I WITH STROKE;Lm;0;L;<super> 1D7B;;;;N;;;;; +1DA8;MODIFIER LETTER SMALL J WITH CROSSED-TAIL;Lm;0;L;<super> 029D;;;;N;;;;; +1DA9;MODIFIER LETTER SMALL L WITH RETROFLEX HOOK;Lm;0;L;<super> 026D;;;;N;;;;; +1DAA;MODIFIER LETTER SMALL L WITH PALATAL HOOK;Lm;0;L;<super> 1D85;;;;N;;;;; +1DAB;MODIFIER LETTER SMALL CAPITAL L;Lm;0;L;<super> 029F;;;;N;;;;; +1DAC;MODIFIER LETTER SMALL M WITH HOOK;Lm;0;L;<super> 0271;;;;N;;;;; +1DAD;MODIFIER LETTER SMALL TURNED M WITH LONG LEG;Lm;0;L;<super> 0270;;;;N;;;;; +1DAE;MODIFIER LETTER SMALL N WITH LEFT HOOK;Lm;0;L;<super> 0272;;;;N;;;;; +1DAF;MODIFIER LETTER SMALL N WITH RETROFLEX HOOK;Lm;0;L;<super> 0273;;;;N;;;;; +1DB0;MODIFIER LETTER SMALL CAPITAL N;Lm;0;L;<super> 0274;;;;N;;;;; +1DB1;MODIFIER LETTER SMALL BARRED O;Lm;0;L;<super> 0275;;;;N;;;;; +1DB2;MODIFIER LETTER SMALL PHI;Lm;0;L;<super> 0278;;;;N;;;;; +1DB3;MODIFIER LETTER SMALL S WITH HOOK;Lm;0;L;<super> 0282;;;;N;;;;; +1DB4;MODIFIER LETTER SMALL ESH;Lm;0;L;<super> 0283;;;;N;;;;; +1DB5;MODIFIER LETTER SMALL T WITH PALATAL HOOK;Lm;0;L;<super> 01AB;;;;N;;;;; +1DB6;MODIFIER LETTER SMALL U BAR;Lm;0;L;<super> 0289;;;;N;;;;; +1DB7;MODIFIER LETTER SMALL UPSILON;Lm;0;L;<super> 028A;;;;N;;;;; +1DB8;MODIFIER LETTER SMALL CAPITAL U;Lm;0;L;<super> 1D1C;;;;N;;;;; +1DB9;MODIFIER LETTER SMALL V WITH HOOK;Lm;0;L;<super> 028B;;;;N;;;;; +1DBA;MODIFIER LETTER SMALL TURNED V;Lm;0;L;<super> 028C;;;;N;;;;; +1DBB;MODIFIER LETTER SMALL Z;Lm;0;L;<super> 007A;;;;N;;;;; +1DBC;MODIFIER LETTER SMALL Z WITH RETROFLEX HOOK;Lm;0;L;<super> 0290;;;;N;;;;; +1DBD;MODIFIER LETTER SMALL Z WITH CURL;Lm;0;L;<super> 0291;;;;N;;;;; +1DBE;MODIFIER LETTER SMALL EZH;Lm;0;L;<super> 0292;;;;N;;;;; +1DBF;MODIFIER LETTER SMALL THETA;Lm;0;L;<super> 03B8;;;;N;;;;; +1DC0;COMBINING DOTTED GRAVE ACCENT;Mn;230;NSM;;;;;N;;;;; +1DC1;COMBINING DOTTED ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;; +1DC2;COMBINING SNAKE BELOW;Mn;220;NSM;;;;;N;;;;; +1DC3;COMBINING SUSPENSION MARK;Mn;230;NSM;;;;;N;;;;; +1DC4;COMBINING MACRON-ACUTE;Mn;230;NSM;;;;;N;;;;; +1DC5;COMBINING GRAVE-MACRON;Mn;230;NSM;;;;;N;;;;; +1DC6;COMBINING MACRON-GRAVE;Mn;230;NSM;;;;;N;;;;; +1DC7;COMBINING ACUTE-MACRON;Mn;230;NSM;;;;;N;;;;; +1DC8;COMBINING GRAVE-ACUTE-GRAVE;Mn;230;NSM;;;;;N;;;;; +1DC9;COMBINING ACUTE-GRAVE-ACUTE;Mn;230;NSM;;;;;N;;;;; +1DCA;COMBINING LATIN SMALL LETTER R BELOW;Mn;220;NSM;;;;;N;;;;; +1DCB;COMBINING BREVE-MACRON;Mn;230;NSM;;;;;N;;;;; +1DCC;COMBINING MACRON-BREVE;Mn;230;NSM;;;;;N;;;;; +1DCD;COMBINING DOUBLE CIRCUMFLEX ABOVE;Mn;234;NSM;;;;;N;;;;; +1DCE;COMBINING OGONEK ABOVE;Mn;214;NSM;;;;;N;;;;; +1DCF;COMBINING ZIGZAG BELOW;Mn;220;NSM;;;;;N;;;;; +1DD0;COMBINING IS BELOW;Mn;202;NSM;;;;;N;;;;; +1DD1;COMBINING UR ABOVE;Mn;230;NSM;;;;;N;;;;; +1DD2;COMBINING US ABOVE;Mn;230;NSM;;;;;N;;;;; +1DD3;COMBINING LATIN SMALL LETTER FLATTENED OPEN A ABOVE;Mn;230;NSM;;;;;N;;;;; +1DD4;COMBINING LATIN SMALL LETTER AE;Mn;230;NSM;;;;;N;;;;; +1DD5;COMBINING LATIN SMALL LETTER AO;Mn;230;NSM;;;;;N;;;;; +1DD6;COMBINING LATIN SMALL LETTER AV;Mn;230;NSM;;;;;N;;;;; +1DD7;COMBINING LATIN SMALL LETTER C CEDILLA;Mn;230;NSM;;;;;N;;;;; +1DD8;COMBINING LATIN SMALL LETTER INSULAR D;Mn;230;NSM;;;;;N;;;;; +1DD9;COMBINING LATIN SMALL LETTER ETH;Mn;230;NSM;;;;;N;;;;; +1DDA;COMBINING LATIN SMALL LETTER G;Mn;230;NSM;;;;;N;;;;; +1DDB;COMBINING LATIN LETTER SMALL CAPITAL G;Mn;230;NSM;;;;;N;;;;; +1DDC;COMBINING LATIN SMALL LETTER K;Mn;230;NSM;;;;;N;;;;; +1DDD;COMBINING LATIN SMALL LETTER L;Mn;230;NSM;;;;;N;;;;; +1DDE;COMBINING LATIN LETTER SMALL CAPITAL L;Mn;230;NSM;;;;;N;;;;; +1DDF;COMBINING LATIN LETTER SMALL CAPITAL M;Mn;230;NSM;;;;;N;;;;; +1DE0;COMBINING LATIN SMALL LETTER N;Mn;230;NSM;;;;;N;;;;; +1DE1;COMBINING LATIN LETTER SMALL CAPITAL N;Mn;230;NSM;;;;;N;;;;; +1DE2;COMBINING LATIN LETTER SMALL CAPITAL R;Mn;230;NSM;;;;;N;;;;; +1DE3;COMBINING LATIN SMALL LETTER R ROTUNDA;Mn;230;NSM;;;;;N;;;;; +1DE4;COMBINING LATIN SMALL LETTER S;Mn;230;NSM;;;;;N;;;;; +1DE5;COMBINING LATIN SMALL LETTER LONG S;Mn;230;NSM;;;;;N;;;;; +1DE6;COMBINING LATIN SMALL LETTER Z;Mn;230;NSM;;;;;N;;;;; +1DE7;COMBINING LATIN SMALL LETTER ALPHA;Mn;230;NSM;;;;;N;;;;; +1DE8;COMBINING LATIN SMALL LETTER B;Mn;230;NSM;;;;;N;;;;; +1DE9;COMBINING LATIN SMALL LETTER BETA;Mn;230;NSM;;;;;N;;;;; +1DEA;COMBINING LATIN SMALL LETTER SCHWA;Mn;230;NSM;;;;;N;;;;; +1DEB;COMBINING LATIN SMALL LETTER F;Mn;230;NSM;;;;;N;;;;; +1DEC;COMBINING LATIN SMALL LETTER L WITH DOUBLE MIDDLE TILDE;Mn;230;NSM;;;;;N;;;;; +1DED;COMBINING LATIN SMALL LETTER O WITH LIGHT CENTRALIZATION STROKE;Mn;230;NSM;;;;;N;;;;; +1DEE;COMBINING LATIN SMALL LETTER P;Mn;230;NSM;;;;;N;;;;; +1DEF;COMBINING LATIN SMALL LETTER ESH;Mn;230;NSM;;;;;N;;;;; +1DF0;COMBINING LATIN SMALL LETTER U WITH LIGHT CENTRALIZATION STROKE;Mn;230;NSM;;;;;N;;;;; +1DF1;COMBINING LATIN SMALL LETTER W;Mn;230;NSM;;;;;N;;;;; +1DF2;COMBINING LATIN SMALL LETTER A WITH DIAERESIS;Mn;230;NSM;;;;;N;;;;; +1DF3;COMBINING LATIN SMALL LETTER O WITH DIAERESIS;Mn;230;NSM;;;;;N;;;;; +1DF4;COMBINING LATIN SMALL LETTER U WITH DIAERESIS;Mn;230;NSM;;;;;N;;;;; +1DF5;COMBINING UP TACK ABOVE;Mn;230;NSM;;;;;N;;;;; +1DFB;COMBINING DELETION MARK;Mn;230;NSM;;;;;N;;;;; +1DFC;COMBINING DOUBLE INVERTED BREVE BELOW;Mn;233;NSM;;;;;N;;;;; +1DFD;COMBINING ALMOST EQUAL TO BELOW;Mn;220;NSM;;;;;N;;;;; +1DFE;COMBINING LEFT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;; +1DFF;COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;; +1E00;LATIN CAPITAL LETTER A WITH RING BELOW;Lu;0;L;0041 0325;;;;N;;;;1E01; +1E01;LATIN SMALL LETTER A WITH RING BELOW;Ll;0;L;0061 0325;;;;N;;;1E00;;1E00 +1E02;LATIN CAPITAL LETTER B WITH DOT ABOVE;Lu;0;L;0042 0307;;;;N;;;;1E03; +1E03;LATIN SMALL LETTER B WITH DOT ABOVE;Ll;0;L;0062 0307;;;;N;;;1E02;;1E02 +1E04;LATIN CAPITAL LETTER B WITH DOT BELOW;Lu;0;L;0042 0323;;;;N;;;;1E05; +1E05;LATIN SMALL LETTER B WITH DOT BELOW;Ll;0;L;0062 0323;;;;N;;;1E04;;1E04 +1E06;LATIN CAPITAL LETTER B WITH LINE BELOW;Lu;0;L;0042 0331;;;;N;;;;1E07; +1E07;LATIN SMALL LETTER B WITH LINE BELOW;Ll;0;L;0062 0331;;;;N;;;1E06;;1E06 +1E08;LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE;Lu;0;L;00C7 0301;;;;N;;;;1E09; +1E09;LATIN SMALL LETTER C WITH CEDILLA AND ACUTE;Ll;0;L;00E7 0301;;;;N;;;1E08;;1E08 +1E0A;LATIN CAPITAL LETTER D WITH DOT ABOVE;Lu;0;L;0044 0307;;;;N;;;;1E0B; +1E0B;LATIN SMALL LETTER D WITH DOT ABOVE;Ll;0;L;0064 0307;;;;N;;;1E0A;;1E0A +1E0C;LATIN CAPITAL LETTER D WITH DOT BELOW;Lu;0;L;0044 0323;;;;N;;;;1E0D; +1E0D;LATIN SMALL LETTER D WITH DOT BELOW;Ll;0;L;0064 0323;;;;N;;;1E0C;;1E0C +1E0E;LATIN CAPITAL LETTER D WITH LINE BELOW;Lu;0;L;0044 0331;;;;N;;;;1E0F; +1E0F;LATIN SMALL LETTER D WITH LINE BELOW;Ll;0;L;0064 0331;;;;N;;;1E0E;;1E0E +1E10;LATIN CAPITAL LETTER D WITH CEDILLA;Lu;0;L;0044 0327;;;;N;;;;1E11; +1E11;LATIN SMALL LETTER D WITH CEDILLA;Ll;0;L;0064 0327;;;;N;;;1E10;;1E10 +1E12;LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW;Lu;0;L;0044 032D;;;;N;;;;1E13; +1E13;LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW;Ll;0;L;0064 032D;;;;N;;;1E12;;1E12 +1E14;LATIN CAPITAL LETTER E WITH MACRON AND GRAVE;Lu;0;L;0112 0300;;;;N;;;;1E15; +1E15;LATIN SMALL LETTER E WITH MACRON AND GRAVE;Ll;0;L;0113 0300;;;;N;;;1E14;;1E14 +1E16;LATIN CAPITAL LETTER E WITH MACRON AND ACUTE;Lu;0;L;0112 0301;;;;N;;;;1E17; +1E17;LATIN SMALL LETTER E WITH MACRON AND ACUTE;Ll;0;L;0113 0301;;;;N;;;1E16;;1E16 +1E18;LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW;Lu;0;L;0045 032D;;;;N;;;;1E19; +1E19;LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW;Ll;0;L;0065 032D;;;;N;;;1E18;;1E18 +1E1A;LATIN CAPITAL LETTER E WITH TILDE BELOW;Lu;0;L;0045 0330;;;;N;;;;1E1B; +1E1B;LATIN SMALL LETTER E WITH TILDE BELOW;Ll;0;L;0065 0330;;;;N;;;1E1A;;1E1A +1E1C;LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE;Lu;0;L;0228 0306;;;;N;;;;1E1D; +1E1D;LATIN SMALL LETTER E WITH CEDILLA AND BREVE;Ll;0;L;0229 0306;;;;N;;;1E1C;;1E1C +1E1E;LATIN CAPITAL LETTER F WITH DOT ABOVE;Lu;0;L;0046 0307;;;;N;;;;1E1F; +1E1F;LATIN SMALL LETTER F WITH DOT ABOVE;Ll;0;L;0066 0307;;;;N;;;1E1E;;1E1E +1E20;LATIN CAPITAL LETTER G WITH MACRON;Lu;0;L;0047 0304;;;;N;;;;1E21; +1E21;LATIN SMALL LETTER G WITH MACRON;Ll;0;L;0067 0304;;;;N;;;1E20;;1E20 +1E22;LATIN CAPITAL LETTER H WITH DOT ABOVE;Lu;0;L;0048 0307;;;;N;;;;1E23; +1E23;LATIN SMALL LETTER H WITH DOT ABOVE;Ll;0;L;0068 0307;;;;N;;;1E22;;1E22 +1E24;LATIN CAPITAL LETTER H WITH DOT BELOW;Lu;0;L;0048 0323;;;;N;;;;1E25; +1E25;LATIN SMALL LETTER H WITH DOT BELOW;Ll;0;L;0068 0323;;;;N;;;1E24;;1E24 +1E26;LATIN CAPITAL LETTER H WITH DIAERESIS;Lu;0;L;0048 0308;;;;N;;;;1E27; +1E27;LATIN SMALL LETTER H WITH DIAERESIS;Ll;0;L;0068 0308;;;;N;;;1E26;;1E26 +1E28;LATIN CAPITAL LETTER H WITH CEDILLA;Lu;0;L;0048 0327;;;;N;;;;1E29; +1E29;LATIN SMALL LETTER H WITH CEDILLA;Ll;0;L;0068 0327;;;;N;;;1E28;;1E28 +1E2A;LATIN CAPITAL LETTER H WITH BREVE BELOW;Lu;0;L;0048 032E;;;;N;;;;1E2B; +1E2B;LATIN SMALL LETTER H WITH BREVE BELOW;Ll;0;L;0068 032E;;;;N;;;1E2A;;1E2A +1E2C;LATIN CAPITAL LETTER I WITH TILDE BELOW;Lu;0;L;0049 0330;;;;N;;;;1E2D; +1E2D;LATIN SMALL LETTER I WITH TILDE BELOW;Ll;0;L;0069 0330;;;;N;;;1E2C;;1E2C +1E2E;LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE;Lu;0;L;00CF 0301;;;;N;;;;1E2F; +1E2F;LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE;Ll;0;L;00EF 0301;;;;N;;;1E2E;;1E2E +1E30;LATIN CAPITAL LETTER K WITH ACUTE;Lu;0;L;004B 0301;;;;N;;;;1E31; +1E31;LATIN SMALL LETTER K WITH ACUTE;Ll;0;L;006B 0301;;;;N;;;1E30;;1E30 +1E32;LATIN CAPITAL LETTER K WITH DOT BELOW;Lu;0;L;004B 0323;;;;N;;;;1E33; +1E33;LATIN SMALL LETTER K WITH DOT BELOW;Ll;0;L;006B 0323;;;;N;;;1E32;;1E32 +1E34;LATIN CAPITAL LETTER K WITH LINE BELOW;Lu;0;L;004B 0331;;;;N;;;;1E35; +1E35;LATIN SMALL LETTER K WITH LINE BELOW;Ll;0;L;006B 0331;;;;N;;;1E34;;1E34 +1E36;LATIN CAPITAL LETTER L WITH DOT BELOW;Lu;0;L;004C 0323;;;;N;;;;1E37; +1E37;LATIN SMALL LETTER L WITH DOT BELOW;Ll;0;L;006C 0323;;;;N;;;1E36;;1E36 +1E38;LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON;Lu;0;L;1E36 0304;;;;N;;;;1E39; +1E39;LATIN SMALL LETTER L WITH DOT BELOW AND MACRON;Ll;0;L;1E37 0304;;;;N;;;1E38;;1E38 +1E3A;LATIN CAPITAL LETTER L WITH LINE BELOW;Lu;0;L;004C 0331;;;;N;;;;1E3B; +1E3B;LATIN SMALL LETTER L WITH LINE BELOW;Ll;0;L;006C 0331;;;;N;;;1E3A;;1E3A +1E3C;LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW;Lu;0;L;004C 032D;;;;N;;;;1E3D; +1E3D;LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW;Ll;0;L;006C 032D;;;;N;;;1E3C;;1E3C +1E3E;LATIN CAPITAL LETTER M WITH ACUTE;Lu;0;L;004D 0301;;;;N;;;;1E3F; +1E3F;LATIN SMALL LETTER M WITH ACUTE;Ll;0;L;006D 0301;;;;N;;;1E3E;;1E3E +1E40;LATIN CAPITAL LETTER M WITH DOT ABOVE;Lu;0;L;004D 0307;;;;N;;;;1E41; +1E41;LATIN SMALL LETTER M WITH DOT ABOVE;Ll;0;L;006D 0307;;;;N;;;1E40;;1E40 +1E42;LATIN CAPITAL LETTER M WITH DOT BELOW;Lu;0;L;004D 0323;;;;N;;;;1E43; +1E43;LATIN SMALL LETTER M WITH DOT BELOW;Ll;0;L;006D 0323;;;;N;;;1E42;;1E42 +1E44;LATIN CAPITAL LETTER N WITH DOT ABOVE;Lu;0;L;004E 0307;;;;N;;;;1E45; +1E45;LATIN SMALL LETTER N WITH DOT ABOVE;Ll;0;L;006E 0307;;;;N;;;1E44;;1E44 +1E46;LATIN CAPITAL LETTER N WITH DOT BELOW;Lu;0;L;004E 0323;;;;N;;;;1E47; +1E47;LATIN SMALL LETTER N WITH DOT BELOW;Ll;0;L;006E 0323;;;;N;;;1E46;;1E46 +1E48;LATIN CAPITAL LETTER N WITH LINE BELOW;Lu;0;L;004E 0331;;;;N;;;;1E49; +1E49;LATIN SMALL LETTER N WITH LINE BELOW;Ll;0;L;006E 0331;;;;N;;;1E48;;1E48 +1E4A;LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW;Lu;0;L;004E 032D;;;;N;;;;1E4B; +1E4B;LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW;Ll;0;L;006E 032D;;;;N;;;1E4A;;1E4A +1E4C;LATIN CAPITAL LETTER O WITH TILDE AND ACUTE;Lu;0;L;00D5 0301;;;;N;;;;1E4D; +1E4D;LATIN SMALL LETTER O WITH TILDE AND ACUTE;Ll;0;L;00F5 0301;;;;N;;;1E4C;;1E4C +1E4E;LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS;Lu;0;L;00D5 0308;;;;N;;;;1E4F; +1E4F;LATIN SMALL LETTER O WITH TILDE AND DIAERESIS;Ll;0;L;00F5 0308;;;;N;;;1E4E;;1E4E +1E50;LATIN CAPITAL LETTER O WITH MACRON AND GRAVE;Lu;0;L;014C 0300;;;;N;;;;1E51; +1E51;LATIN SMALL LETTER O WITH MACRON AND GRAVE;Ll;0;L;014D 0300;;;;N;;;1E50;;1E50 +1E52;LATIN CAPITAL LETTER O WITH MACRON AND ACUTE;Lu;0;L;014C 0301;;;;N;;;;1E53; +1E53;LATIN SMALL LETTER O WITH MACRON AND ACUTE;Ll;0;L;014D 0301;;;;N;;;1E52;;1E52 +1E54;LATIN CAPITAL LETTER P WITH ACUTE;Lu;0;L;0050 0301;;;;N;;;;1E55; +1E55;LATIN SMALL LETTER P WITH ACUTE;Ll;0;L;0070 0301;;;;N;;;1E54;;1E54 +1E56;LATIN CAPITAL LETTER P WITH DOT ABOVE;Lu;0;L;0050 0307;;;;N;;;;1E57; +1E57;LATIN SMALL LETTER P WITH DOT ABOVE;Ll;0;L;0070 0307;;;;N;;;1E56;;1E56 +1E58;LATIN CAPITAL LETTER R WITH DOT ABOVE;Lu;0;L;0052 0307;;;;N;;;;1E59; +1E59;LATIN SMALL LETTER R WITH DOT ABOVE;Ll;0;L;0072 0307;;;;N;;;1E58;;1E58 +1E5A;LATIN CAPITAL LETTER R WITH DOT BELOW;Lu;0;L;0052 0323;;;;N;;;;1E5B; +1E5B;LATIN SMALL LETTER R WITH DOT BELOW;Ll;0;L;0072 0323;;;;N;;;1E5A;;1E5A +1E5C;LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON;Lu;0;L;1E5A 0304;;;;N;;;;1E5D; +1E5D;LATIN SMALL LETTER R WITH DOT BELOW AND MACRON;Ll;0;L;1E5B 0304;;;;N;;;1E5C;;1E5C +1E5E;LATIN CAPITAL LETTER R WITH LINE BELOW;Lu;0;L;0052 0331;;;;N;;;;1E5F; +1E5F;LATIN SMALL LETTER R WITH LINE BELOW;Ll;0;L;0072 0331;;;;N;;;1E5E;;1E5E +1E60;LATIN CAPITAL LETTER S WITH DOT ABOVE;Lu;0;L;0053 0307;;;;N;;;;1E61; +1E61;LATIN SMALL LETTER S WITH DOT ABOVE;Ll;0;L;0073 0307;;;;N;;;1E60;;1E60 +1E62;LATIN CAPITAL LETTER S WITH DOT BELOW;Lu;0;L;0053 0323;;;;N;;;;1E63; +1E63;LATIN SMALL LETTER S WITH DOT BELOW;Ll;0;L;0073 0323;;;;N;;;1E62;;1E62 +1E64;LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE;Lu;0;L;015A 0307;;;;N;;;;1E65; +1E65;LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE;Ll;0;L;015B 0307;;;;N;;;1E64;;1E64 +1E66;LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE;Lu;0;L;0160 0307;;;;N;;;;1E67; +1E67;LATIN SMALL LETTER S WITH CARON AND DOT ABOVE;Ll;0;L;0161 0307;;;;N;;;1E66;;1E66 +1E68;LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE;Lu;0;L;1E62 0307;;;;N;;;;1E69; +1E69;LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE;Ll;0;L;1E63 0307;;;;N;;;1E68;;1E68 +1E6A;LATIN CAPITAL LETTER T WITH DOT ABOVE;Lu;0;L;0054 0307;;;;N;;;;1E6B; +1E6B;LATIN SMALL LETTER T WITH DOT ABOVE;Ll;0;L;0074 0307;;;;N;;;1E6A;;1E6A +1E6C;LATIN CAPITAL LETTER T WITH DOT BELOW;Lu;0;L;0054 0323;;;;N;;;;1E6D; +1E6D;LATIN SMALL LETTER T WITH DOT BELOW;Ll;0;L;0074 0323;;;;N;;;1E6C;;1E6C +1E6E;LATIN CAPITAL LETTER T WITH LINE BELOW;Lu;0;L;0054 0331;;;;N;;;;1E6F; +1E6F;LATIN SMALL LETTER T WITH LINE BELOW;Ll;0;L;0074 0331;;;;N;;;1E6E;;1E6E +1E70;LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW;Lu;0;L;0054 032D;;;;N;;;;1E71; +1E71;LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW;Ll;0;L;0074 032D;;;;N;;;1E70;;1E70 +1E72;LATIN CAPITAL LETTER U WITH DIAERESIS BELOW;Lu;0;L;0055 0324;;;;N;;;;1E73; +1E73;LATIN SMALL LETTER U WITH DIAERESIS BELOW;Ll;0;L;0075 0324;;;;N;;;1E72;;1E72 +1E74;LATIN CAPITAL LETTER U WITH TILDE BELOW;Lu;0;L;0055 0330;;;;N;;;;1E75; +1E75;LATIN SMALL LETTER U WITH TILDE BELOW;Ll;0;L;0075 0330;;;;N;;;1E74;;1E74 +1E76;LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW;Lu;0;L;0055 032D;;;;N;;;;1E77; +1E77;LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW;Ll;0;L;0075 032D;;;;N;;;1E76;;1E76 +1E78;LATIN CAPITAL LETTER U WITH TILDE AND ACUTE;Lu;0;L;0168 0301;;;;N;;;;1E79; +1E79;LATIN SMALL LETTER U WITH TILDE AND ACUTE;Ll;0;L;0169 0301;;;;N;;;1E78;;1E78 +1E7A;LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS;Lu;0;L;016A 0308;;;;N;;;;1E7B; +1E7B;LATIN SMALL LETTER U WITH MACRON AND DIAERESIS;Ll;0;L;016B 0308;;;;N;;;1E7A;;1E7A +1E7C;LATIN CAPITAL LETTER V WITH TILDE;Lu;0;L;0056 0303;;;;N;;;;1E7D; +1E7D;LATIN SMALL LETTER V WITH TILDE;Ll;0;L;0076 0303;;;;N;;;1E7C;;1E7C +1E7E;LATIN CAPITAL LETTER V WITH DOT BELOW;Lu;0;L;0056 0323;;;;N;;;;1E7F; +1E7F;LATIN SMALL LETTER V WITH DOT BELOW;Ll;0;L;0076 0323;;;;N;;;1E7E;;1E7E +1E80;LATIN CAPITAL LETTER W WITH GRAVE;Lu;0;L;0057 0300;;;;N;;;;1E81; +1E81;LATIN SMALL LETTER W WITH GRAVE;Ll;0;L;0077 0300;;;;N;;;1E80;;1E80 +1E82;LATIN CAPITAL LETTER W WITH ACUTE;Lu;0;L;0057 0301;;;;N;;;;1E83; +1E83;LATIN SMALL LETTER W WITH ACUTE;Ll;0;L;0077 0301;;;;N;;;1E82;;1E82 +1E84;LATIN CAPITAL LETTER W WITH DIAERESIS;Lu;0;L;0057 0308;;;;N;;;;1E85; +1E85;LATIN SMALL LETTER W WITH DIAERESIS;Ll;0;L;0077 0308;;;;N;;;1E84;;1E84 +1E86;LATIN CAPITAL LETTER W WITH DOT ABOVE;Lu;0;L;0057 0307;;;;N;;;;1E87; +1E87;LATIN SMALL LETTER W WITH DOT ABOVE;Ll;0;L;0077 0307;;;;N;;;1E86;;1E86 +1E88;LATIN CAPITAL LETTER W WITH DOT BELOW;Lu;0;L;0057 0323;;;;N;;;;1E89; +1E89;LATIN SMALL LETTER W WITH DOT BELOW;Ll;0;L;0077 0323;;;;N;;;1E88;;1E88 +1E8A;LATIN CAPITAL LETTER X WITH DOT ABOVE;Lu;0;L;0058 0307;;;;N;;;;1E8B; +1E8B;LATIN SMALL LETTER X WITH DOT ABOVE;Ll;0;L;0078 0307;;;;N;;;1E8A;;1E8A +1E8C;LATIN CAPITAL LETTER X WITH DIAERESIS;Lu;0;L;0058 0308;;;;N;;;;1E8D; +1E8D;LATIN SMALL LETTER X WITH DIAERESIS;Ll;0;L;0078 0308;;;;N;;;1E8C;;1E8C +1E8E;LATIN CAPITAL LETTER Y WITH DOT ABOVE;Lu;0;L;0059 0307;;;;N;;;;1E8F; +1E8F;LATIN SMALL LETTER Y WITH DOT ABOVE;Ll;0;L;0079 0307;;;;N;;;1E8E;;1E8E +1E90;LATIN CAPITAL LETTER Z WITH CIRCUMFLEX;Lu;0;L;005A 0302;;;;N;;;;1E91; +1E91;LATIN SMALL LETTER Z WITH CIRCUMFLEX;Ll;0;L;007A 0302;;;;N;;;1E90;;1E90 +1E92;LATIN CAPITAL LETTER Z WITH DOT BELOW;Lu;0;L;005A 0323;;;;N;;;;1E93; +1E93;LATIN SMALL LETTER Z WITH DOT BELOW;Ll;0;L;007A 0323;;;;N;;;1E92;;1E92 +1E94;LATIN CAPITAL LETTER Z WITH LINE BELOW;Lu;0;L;005A 0331;;;;N;;;;1E95; +1E95;LATIN SMALL LETTER Z WITH LINE BELOW;Ll;0;L;007A 0331;;;;N;;;1E94;;1E94 +1E96;LATIN SMALL LETTER H WITH LINE BELOW;Ll;0;L;0068 0331;;;;N;;;;; +1E97;LATIN SMALL LETTER T WITH DIAERESIS;Ll;0;L;0074 0308;;;;N;;;;; +1E98;LATIN SMALL LETTER W WITH RING ABOVE;Ll;0;L;0077 030A;;;;N;;;;; +1E99;LATIN SMALL LETTER Y WITH RING ABOVE;Ll;0;L;0079 030A;;;;N;;;;; +1E9A;LATIN SMALL LETTER A WITH RIGHT HALF RING;Ll;0;L;<compat> 0061 02BE;;;;N;;;;; +1E9B;LATIN SMALL LETTER LONG S WITH DOT ABOVE;Ll;0;L;017F 0307;;;;N;;;1E60;;1E60 +1E9C;LATIN SMALL LETTER LONG S WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;;; +1E9D;LATIN SMALL LETTER LONG S WITH HIGH STROKE;Ll;0;L;;;;;N;;;;; +1E9E;LATIN CAPITAL LETTER SHARP S;Lu;0;L;;;;;N;;;;00DF; +1E9F;LATIN SMALL LETTER DELTA;Ll;0;L;;;;;N;;;;; +1EA0;LATIN CAPITAL LETTER A WITH DOT BELOW;Lu;0;L;0041 0323;;;;N;;;;1EA1; +1EA1;LATIN SMALL LETTER A WITH DOT BELOW;Ll;0;L;0061 0323;;;;N;;;1EA0;;1EA0 +1EA2;LATIN CAPITAL LETTER A WITH HOOK ABOVE;Lu;0;L;0041 0309;;;;N;;;;1EA3; +1EA3;LATIN SMALL LETTER A WITH HOOK ABOVE;Ll;0;L;0061 0309;;;;N;;;1EA2;;1EA2 +1EA4;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00C2 0301;;;;N;;;;1EA5; +1EA5;LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00E2 0301;;;;N;;;1EA4;;1EA4 +1EA6;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00C2 0300;;;;N;;;;1EA7; +1EA7;LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00E2 0300;;;;N;;;1EA6;;1EA6 +1EA8;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00C2 0309;;;;N;;;;1EA9; +1EA9;LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00E2 0309;;;;N;;;1EA8;;1EA8 +1EAA;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE;Lu;0;L;00C2 0303;;;;N;;;;1EAB; +1EAB;LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE;Ll;0;L;00E2 0303;;;;N;;;1EAA;;1EAA +1EAC;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1EA0 0302;;;;N;;;;1EAD; +1EAD;LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1EA1 0302;;;;N;;;1EAC;;1EAC +1EAE;LATIN CAPITAL LETTER A WITH BREVE AND ACUTE;Lu;0;L;0102 0301;;;;N;;;;1EAF; +1EAF;LATIN SMALL LETTER A WITH BREVE AND ACUTE;Ll;0;L;0103 0301;;;;N;;;1EAE;;1EAE +1EB0;LATIN CAPITAL LETTER A WITH BREVE AND GRAVE;Lu;0;L;0102 0300;;;;N;;;;1EB1; +1EB1;LATIN SMALL LETTER A WITH BREVE AND GRAVE;Ll;0;L;0103 0300;;;;N;;;1EB0;;1EB0 +1EB2;LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE;Lu;0;L;0102 0309;;;;N;;;;1EB3; +1EB3;LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE;Ll;0;L;0103 0309;;;;N;;;1EB2;;1EB2 +1EB4;LATIN CAPITAL LETTER A WITH BREVE AND TILDE;Lu;0;L;0102 0303;;;;N;;;;1EB5; +1EB5;LATIN SMALL LETTER A WITH BREVE AND TILDE;Ll;0;L;0103 0303;;;;N;;;1EB4;;1EB4 +1EB6;LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW;Lu;0;L;1EA0 0306;;;;N;;;;1EB7; +1EB7;LATIN SMALL LETTER A WITH BREVE AND DOT BELOW;Ll;0;L;1EA1 0306;;;;N;;;1EB6;;1EB6 +1EB8;LATIN CAPITAL LETTER E WITH DOT BELOW;Lu;0;L;0045 0323;;;;N;;;;1EB9; +1EB9;LATIN SMALL LETTER E WITH DOT BELOW;Ll;0;L;0065 0323;;;;N;;;1EB8;;1EB8 +1EBA;LATIN CAPITAL LETTER E WITH HOOK ABOVE;Lu;0;L;0045 0309;;;;N;;;;1EBB; +1EBB;LATIN SMALL LETTER E WITH HOOK ABOVE;Ll;0;L;0065 0309;;;;N;;;1EBA;;1EBA +1EBC;LATIN CAPITAL LETTER E WITH TILDE;Lu;0;L;0045 0303;;;;N;;;;1EBD; +1EBD;LATIN SMALL LETTER E WITH TILDE;Ll;0;L;0065 0303;;;;N;;;1EBC;;1EBC +1EBE;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00CA 0301;;;;N;;;;1EBF; +1EBF;LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00EA 0301;;;;N;;;1EBE;;1EBE +1EC0;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00CA 0300;;;;N;;;;1EC1; +1EC1;LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00EA 0300;;;;N;;;1EC0;;1EC0 +1EC2;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00CA 0309;;;;N;;;;1EC3; +1EC3;LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00EA 0309;;;;N;;;1EC2;;1EC2 +1EC4;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE;Lu;0;L;00CA 0303;;;;N;;;;1EC5; +1EC5;LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE;Ll;0;L;00EA 0303;;;;N;;;1EC4;;1EC4 +1EC6;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1EB8 0302;;;;N;;;;1EC7; +1EC7;LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1EB9 0302;;;;N;;;1EC6;;1EC6 +1EC8;LATIN CAPITAL LETTER I WITH HOOK ABOVE;Lu;0;L;0049 0309;;;;N;;;;1EC9; +1EC9;LATIN SMALL LETTER I WITH HOOK ABOVE;Ll;0;L;0069 0309;;;;N;;;1EC8;;1EC8 +1ECA;LATIN CAPITAL LETTER I WITH DOT BELOW;Lu;0;L;0049 0323;;;;N;;;;1ECB; +1ECB;LATIN SMALL LETTER I WITH DOT BELOW;Ll;0;L;0069 0323;;;;N;;;1ECA;;1ECA +1ECC;LATIN CAPITAL LETTER O WITH DOT BELOW;Lu;0;L;004F 0323;;;;N;;;;1ECD; +1ECD;LATIN SMALL LETTER O WITH DOT BELOW;Ll;0;L;006F 0323;;;;N;;;1ECC;;1ECC +1ECE;LATIN CAPITAL LETTER O WITH HOOK ABOVE;Lu;0;L;004F 0309;;;;N;;;;1ECF; +1ECF;LATIN SMALL LETTER O WITH HOOK ABOVE;Ll;0;L;006F 0309;;;;N;;;1ECE;;1ECE +1ED0;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00D4 0301;;;;N;;;;1ED1; +1ED1;LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00F4 0301;;;;N;;;1ED0;;1ED0 +1ED2;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00D4 0300;;;;N;;;;1ED3; +1ED3;LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00F4 0300;;;;N;;;1ED2;;1ED2 +1ED4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00D4 0309;;;;N;;;;1ED5; +1ED5;LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00F4 0309;;;;N;;;1ED4;;1ED4 +1ED6;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE;Lu;0;L;00D4 0303;;;;N;;;;1ED7; +1ED7;LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE;Ll;0;L;00F4 0303;;;;N;;;1ED6;;1ED6 +1ED8;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1ECC 0302;;;;N;;;;1ED9; +1ED9;LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1ECD 0302;;;;N;;;1ED8;;1ED8 +1EDA;LATIN CAPITAL LETTER O WITH HORN AND ACUTE;Lu;0;L;01A0 0301;;;;N;;;;1EDB; +1EDB;LATIN SMALL LETTER O WITH HORN AND ACUTE;Ll;0;L;01A1 0301;;;;N;;;1EDA;;1EDA +1EDC;LATIN CAPITAL LETTER O WITH HORN AND GRAVE;Lu;0;L;01A0 0300;;;;N;;;;1EDD; +1EDD;LATIN SMALL LETTER O WITH HORN AND GRAVE;Ll;0;L;01A1 0300;;;;N;;;1EDC;;1EDC +1EDE;LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE;Lu;0;L;01A0 0309;;;;N;;;;1EDF; +1EDF;LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE;Ll;0;L;01A1 0309;;;;N;;;1EDE;;1EDE +1EE0;LATIN CAPITAL LETTER O WITH HORN AND TILDE;Lu;0;L;01A0 0303;;;;N;;;;1EE1; +1EE1;LATIN SMALL LETTER O WITH HORN AND TILDE;Ll;0;L;01A1 0303;;;;N;;;1EE0;;1EE0 +1EE2;LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW;Lu;0;L;01A0 0323;;;;N;;;;1EE3; +1EE3;LATIN SMALL LETTER O WITH HORN AND DOT BELOW;Ll;0;L;01A1 0323;;;;N;;;1EE2;;1EE2 +1EE4;LATIN CAPITAL LETTER U WITH DOT BELOW;Lu;0;L;0055 0323;;;;N;;;;1EE5; +1EE5;LATIN SMALL LETTER U WITH DOT BELOW;Ll;0;L;0075 0323;;;;N;;;1EE4;;1EE4 +1EE6;LATIN CAPITAL LETTER U WITH HOOK ABOVE;Lu;0;L;0055 0309;;;;N;;;;1EE7; +1EE7;LATIN SMALL LETTER U WITH HOOK ABOVE;Ll;0;L;0075 0309;;;;N;;;1EE6;;1EE6 +1EE8;LATIN CAPITAL LETTER U WITH HORN AND ACUTE;Lu;0;L;01AF 0301;;;;N;;;;1EE9; +1EE9;LATIN SMALL LETTER U WITH HORN AND ACUTE;Ll;0;L;01B0 0301;;;;N;;;1EE8;;1EE8 +1EEA;LATIN CAPITAL LETTER U WITH HORN AND GRAVE;Lu;0;L;01AF 0300;;;;N;;;;1EEB; +1EEB;LATIN SMALL LETTER U WITH HORN AND GRAVE;Ll;0;L;01B0 0300;;;;N;;;1EEA;;1EEA +1EEC;LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE;Lu;0;L;01AF 0309;;;;N;;;;1EED; +1EED;LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE;Ll;0;L;01B0 0309;;;;N;;;1EEC;;1EEC +1EEE;LATIN CAPITAL LETTER U WITH HORN AND TILDE;Lu;0;L;01AF 0303;;;;N;;;;1EEF; +1EEF;LATIN SMALL LETTER U WITH HORN AND TILDE;Ll;0;L;01B0 0303;;;;N;;;1EEE;;1EEE +1EF0;LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW;Lu;0;L;01AF 0323;;;;N;;;;1EF1; +1EF1;LATIN SMALL LETTER U WITH HORN AND DOT BELOW;Ll;0;L;01B0 0323;;;;N;;;1EF0;;1EF0 +1EF2;LATIN CAPITAL LETTER Y WITH GRAVE;Lu;0;L;0059 0300;;;;N;;;;1EF3; +1EF3;LATIN SMALL LETTER Y WITH GRAVE;Ll;0;L;0079 0300;;;;N;;;1EF2;;1EF2 +1EF4;LATIN CAPITAL LETTER Y WITH DOT BELOW;Lu;0;L;0059 0323;;;;N;;;;1EF5; +1EF5;LATIN SMALL LETTER Y WITH DOT BELOW;Ll;0;L;0079 0323;;;;N;;;1EF4;;1EF4 +1EF6;LATIN CAPITAL LETTER Y WITH HOOK ABOVE;Lu;0;L;0059 0309;;;;N;;;;1EF7; +1EF7;LATIN SMALL LETTER Y WITH HOOK ABOVE;Ll;0;L;0079 0309;;;;N;;;1EF6;;1EF6 +1EF8;LATIN CAPITAL LETTER Y WITH TILDE;Lu;0;L;0059 0303;;;;N;;;;1EF9; +1EF9;LATIN SMALL LETTER Y WITH TILDE;Ll;0;L;0079 0303;;;;N;;;1EF8;;1EF8 +1EFA;LATIN CAPITAL LETTER MIDDLE-WELSH LL;Lu;0;L;;;;;N;;;;1EFB; +1EFB;LATIN SMALL LETTER MIDDLE-WELSH LL;Ll;0;L;;;;;N;;;1EFA;;1EFA +1EFC;LATIN CAPITAL LETTER MIDDLE-WELSH V;Lu;0;L;;;;;N;;;;1EFD; +1EFD;LATIN SMALL LETTER MIDDLE-WELSH V;Ll;0;L;;;;;N;;;1EFC;;1EFC +1EFE;LATIN CAPITAL LETTER Y WITH LOOP;Lu;0;L;;;;;N;;;;1EFF; +1EFF;LATIN SMALL LETTER Y WITH LOOP;Ll;0;L;;;;;N;;;1EFE;;1EFE +1F00;GREEK SMALL LETTER ALPHA WITH PSILI;Ll;0;L;03B1 0313;;;;N;;;1F08;;1F08 +1F01;GREEK SMALL LETTER ALPHA WITH DASIA;Ll;0;L;03B1 0314;;;;N;;;1F09;;1F09 +1F02;GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA;Ll;0;L;1F00 0300;;;;N;;;1F0A;;1F0A +1F03;GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA;Ll;0;L;1F01 0300;;;;N;;;1F0B;;1F0B +1F04;GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA;Ll;0;L;1F00 0301;;;;N;;;1F0C;;1F0C +1F05;GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA;Ll;0;L;1F01 0301;;;;N;;;1F0D;;1F0D +1F06;GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI;Ll;0;L;1F00 0342;;;;N;;;1F0E;;1F0E +1F07;GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI;Ll;0;L;1F01 0342;;;;N;;;1F0F;;1F0F +1F08;GREEK CAPITAL LETTER ALPHA WITH PSILI;Lu;0;L;0391 0313;;;;N;;;;1F00; +1F09;GREEK CAPITAL LETTER ALPHA WITH DASIA;Lu;0;L;0391 0314;;;;N;;;;1F01; +1F0A;GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA;Lu;0;L;1F08 0300;;;;N;;;;1F02; +1F0B;GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA;Lu;0;L;1F09 0300;;;;N;;;;1F03; +1F0C;GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA;Lu;0;L;1F08 0301;;;;N;;;;1F04; +1F0D;GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA;Lu;0;L;1F09 0301;;;;N;;;;1F05; +1F0E;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI;Lu;0;L;1F08 0342;;;;N;;;;1F06; +1F0F;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI;Lu;0;L;1F09 0342;;;;N;;;;1F07; +1F10;GREEK SMALL LETTER EPSILON WITH PSILI;Ll;0;L;03B5 0313;;;;N;;;1F18;;1F18 +1F11;GREEK SMALL LETTER EPSILON WITH DASIA;Ll;0;L;03B5 0314;;;;N;;;1F19;;1F19 +1F12;GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA;Ll;0;L;1F10 0300;;;;N;;;1F1A;;1F1A +1F13;GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA;Ll;0;L;1F11 0300;;;;N;;;1F1B;;1F1B +1F14;GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA;Ll;0;L;1F10 0301;;;;N;;;1F1C;;1F1C +1F15;GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA;Ll;0;L;1F11 0301;;;;N;;;1F1D;;1F1D +1F18;GREEK CAPITAL LETTER EPSILON WITH PSILI;Lu;0;L;0395 0313;;;;N;;;;1F10; +1F19;GREEK CAPITAL LETTER EPSILON WITH DASIA;Lu;0;L;0395 0314;;;;N;;;;1F11; +1F1A;GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA;Lu;0;L;1F18 0300;;;;N;;;;1F12; +1F1B;GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA;Lu;0;L;1F19 0300;;;;N;;;;1F13; +1F1C;GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA;Lu;0;L;1F18 0301;;;;N;;;;1F14; +1F1D;GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA;Lu;0;L;1F19 0301;;;;N;;;;1F15; +1F20;GREEK SMALL LETTER ETA WITH PSILI;Ll;0;L;03B7 0313;;;;N;;;1F28;;1F28 +1F21;GREEK SMALL LETTER ETA WITH DASIA;Ll;0;L;03B7 0314;;;;N;;;1F29;;1F29 +1F22;GREEK SMALL LETTER ETA WITH PSILI AND VARIA;Ll;0;L;1F20 0300;;;;N;;;1F2A;;1F2A +1F23;GREEK SMALL LETTER ETA WITH DASIA AND VARIA;Ll;0;L;1F21 0300;;;;N;;;1F2B;;1F2B +1F24;GREEK SMALL LETTER ETA WITH PSILI AND OXIA;Ll;0;L;1F20 0301;;;;N;;;1F2C;;1F2C +1F25;GREEK SMALL LETTER ETA WITH DASIA AND OXIA;Ll;0;L;1F21 0301;;;;N;;;1F2D;;1F2D +1F26;GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI;Ll;0;L;1F20 0342;;;;N;;;1F2E;;1F2E +1F27;GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI;Ll;0;L;1F21 0342;;;;N;;;1F2F;;1F2F +1F28;GREEK CAPITAL LETTER ETA WITH PSILI;Lu;0;L;0397 0313;;;;N;;;;1F20; +1F29;GREEK CAPITAL LETTER ETA WITH DASIA;Lu;0;L;0397 0314;;;;N;;;;1F21; +1F2A;GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA;Lu;0;L;1F28 0300;;;;N;;;;1F22; +1F2B;GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA;Lu;0;L;1F29 0300;;;;N;;;;1F23; +1F2C;GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA;Lu;0;L;1F28 0301;;;;N;;;;1F24; +1F2D;GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA;Lu;0;L;1F29 0301;;;;N;;;;1F25; +1F2E;GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI;Lu;0;L;1F28 0342;;;;N;;;;1F26; +1F2F;GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI;Lu;0;L;1F29 0342;;;;N;;;;1F27; +1F30;GREEK SMALL LETTER IOTA WITH PSILI;Ll;0;L;03B9 0313;;;;N;;;1F38;;1F38 +1F31;GREEK SMALL LETTER IOTA WITH DASIA;Ll;0;L;03B9 0314;;;;N;;;1F39;;1F39 +1F32;GREEK SMALL LETTER IOTA WITH PSILI AND VARIA;Ll;0;L;1F30 0300;;;;N;;;1F3A;;1F3A +1F33;GREEK SMALL LETTER IOTA WITH DASIA AND VARIA;Ll;0;L;1F31 0300;;;;N;;;1F3B;;1F3B +1F34;GREEK SMALL LETTER IOTA WITH PSILI AND OXIA;Ll;0;L;1F30 0301;;;;N;;;1F3C;;1F3C +1F35;GREEK SMALL LETTER IOTA WITH DASIA AND OXIA;Ll;0;L;1F31 0301;;;;N;;;1F3D;;1F3D +1F36;GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI;Ll;0;L;1F30 0342;;;;N;;;1F3E;;1F3E +1F37;GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI;Ll;0;L;1F31 0342;;;;N;;;1F3F;;1F3F +1F38;GREEK CAPITAL LETTER IOTA WITH PSILI;Lu;0;L;0399 0313;;;;N;;;;1F30; +1F39;GREEK CAPITAL LETTER IOTA WITH DASIA;Lu;0;L;0399 0314;;;;N;;;;1F31; +1F3A;GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA;Lu;0;L;1F38 0300;;;;N;;;;1F32; +1F3B;GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA;Lu;0;L;1F39 0300;;;;N;;;;1F33; +1F3C;GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA;Lu;0;L;1F38 0301;;;;N;;;;1F34; +1F3D;GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA;Lu;0;L;1F39 0301;;;;N;;;;1F35; +1F3E;GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI;Lu;0;L;1F38 0342;;;;N;;;;1F36; +1F3F;GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI;Lu;0;L;1F39 0342;;;;N;;;;1F37; +1F40;GREEK SMALL LETTER OMICRON WITH PSILI;Ll;0;L;03BF 0313;;;;N;;;1F48;;1F48 +1F41;GREEK SMALL LETTER OMICRON WITH DASIA;Ll;0;L;03BF 0314;;;;N;;;1F49;;1F49 +1F42;GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA;Ll;0;L;1F40 0300;;;;N;;;1F4A;;1F4A +1F43;GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA;Ll;0;L;1F41 0300;;;;N;;;1F4B;;1F4B +1F44;GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA;Ll;0;L;1F40 0301;;;;N;;;1F4C;;1F4C +1F45;GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA;Ll;0;L;1F41 0301;;;;N;;;1F4D;;1F4D +1F48;GREEK CAPITAL LETTER OMICRON WITH PSILI;Lu;0;L;039F 0313;;;;N;;;;1F40; +1F49;GREEK CAPITAL LETTER OMICRON WITH DASIA;Lu;0;L;039F 0314;;;;N;;;;1F41; +1F4A;GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA;Lu;0;L;1F48 0300;;;;N;;;;1F42; +1F4B;GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA;Lu;0;L;1F49 0300;;;;N;;;;1F43; +1F4C;GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA;Lu;0;L;1F48 0301;;;;N;;;;1F44; +1F4D;GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA;Lu;0;L;1F49 0301;;;;N;;;;1F45; +1F50;GREEK SMALL LETTER UPSILON WITH PSILI;Ll;0;L;03C5 0313;;;;N;;;;; +1F51;GREEK SMALL LETTER UPSILON WITH DASIA;Ll;0;L;03C5 0314;;;;N;;;1F59;;1F59 +1F52;GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA;Ll;0;L;1F50 0300;;;;N;;;;; +1F53;GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA;Ll;0;L;1F51 0300;;;;N;;;1F5B;;1F5B +1F54;GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA;Ll;0;L;1F50 0301;;;;N;;;;; +1F55;GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA;Ll;0;L;1F51 0301;;;;N;;;1F5D;;1F5D +1F56;GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI;Ll;0;L;1F50 0342;;;;N;;;;; +1F57;GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI;Ll;0;L;1F51 0342;;;;N;;;1F5F;;1F5F +1F59;GREEK CAPITAL LETTER UPSILON WITH DASIA;Lu;0;L;03A5 0314;;;;N;;;;1F51; +1F5B;GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA;Lu;0;L;1F59 0300;;;;N;;;;1F53; +1F5D;GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA;Lu;0;L;1F59 0301;;;;N;;;;1F55; +1F5F;GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI;Lu;0;L;1F59 0342;;;;N;;;;1F57; +1F60;GREEK SMALL LETTER OMEGA WITH PSILI;Ll;0;L;03C9 0313;;;;N;;;1F68;;1F68 +1F61;GREEK SMALL LETTER OMEGA WITH DASIA;Ll;0;L;03C9 0314;;;;N;;;1F69;;1F69 +1F62;GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA;Ll;0;L;1F60 0300;;;;N;;;1F6A;;1F6A +1F63;GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA;Ll;0;L;1F61 0300;;;;N;;;1F6B;;1F6B +1F64;GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA;Ll;0;L;1F60 0301;;;;N;;;1F6C;;1F6C +1F65;GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA;Ll;0;L;1F61 0301;;;;N;;;1F6D;;1F6D +1F66;GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI;Ll;0;L;1F60 0342;;;;N;;;1F6E;;1F6E +1F67;GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI;Ll;0;L;1F61 0342;;;;N;;;1F6F;;1F6F +1F68;GREEK CAPITAL LETTER OMEGA WITH PSILI;Lu;0;L;03A9 0313;;;;N;;;;1F60; +1F69;GREEK CAPITAL LETTER OMEGA WITH DASIA;Lu;0;L;03A9 0314;;;;N;;;;1F61; +1F6A;GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA;Lu;0;L;1F68 0300;;;;N;;;;1F62; +1F6B;GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA;Lu;0;L;1F69 0300;;;;N;;;;1F63; +1F6C;GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA;Lu;0;L;1F68 0301;;;;N;;;;1F64; +1F6D;GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA;Lu;0;L;1F69 0301;;;;N;;;;1F65; +1F6E;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI;Lu;0;L;1F68 0342;;;;N;;;;1F66; +1F6F;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI;Lu;0;L;1F69 0342;;;;N;;;;1F67; +1F70;GREEK SMALL LETTER ALPHA WITH VARIA;Ll;0;L;03B1 0300;;;;N;;;1FBA;;1FBA +1F71;GREEK SMALL LETTER ALPHA WITH OXIA;Ll;0;L;03AC;;;;N;;;1FBB;;1FBB +1F72;GREEK SMALL LETTER EPSILON WITH VARIA;Ll;0;L;03B5 0300;;;;N;;;1FC8;;1FC8 +1F73;GREEK SMALL LETTER EPSILON WITH OXIA;Ll;0;L;03AD;;;;N;;;1FC9;;1FC9 +1F74;GREEK SMALL LETTER ETA WITH VARIA;Ll;0;L;03B7 0300;;;;N;;;1FCA;;1FCA +1F75;GREEK SMALL LETTER ETA WITH OXIA;Ll;0;L;03AE;;;;N;;;1FCB;;1FCB +1F76;GREEK SMALL LETTER IOTA WITH VARIA;Ll;0;L;03B9 0300;;;;N;;;1FDA;;1FDA +1F77;GREEK SMALL LETTER IOTA WITH OXIA;Ll;0;L;03AF;;;;N;;;1FDB;;1FDB +1F78;GREEK SMALL LETTER OMICRON WITH VARIA;Ll;0;L;03BF 0300;;;;N;;;1FF8;;1FF8 +1F79;GREEK SMALL LETTER OMICRON WITH OXIA;Ll;0;L;03CC;;;;N;;;1FF9;;1FF9 +1F7A;GREEK SMALL LETTER UPSILON WITH VARIA;Ll;0;L;03C5 0300;;;;N;;;1FEA;;1FEA +1F7B;GREEK SMALL LETTER UPSILON WITH OXIA;Ll;0;L;03CD;;;;N;;;1FEB;;1FEB +1F7C;GREEK SMALL LETTER OMEGA WITH VARIA;Ll;0;L;03C9 0300;;;;N;;;1FFA;;1FFA +1F7D;GREEK SMALL LETTER OMEGA WITH OXIA;Ll;0;L;03CE;;;;N;;;1FFB;;1FFB +1F80;GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F00 0345;;;;N;;;1F88;;1F88 +1F81;GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F01 0345;;;;N;;;1F89;;1F89 +1F82;GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F02 0345;;;;N;;;1F8A;;1F8A +1F83;GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F03 0345;;;;N;;;1F8B;;1F8B +1F84;GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F04 0345;;;;N;;;1F8C;;1F8C +1F85;GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F05 0345;;;;N;;;1F8D;;1F8D +1F86;GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F06 0345;;;;N;;;1F8E;;1F8E +1F87;GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F07 0345;;;;N;;;1F8F;;1F8F +1F88;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F08 0345;;;;N;;;;1F80; +1F89;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F09 0345;;;;N;;;;1F81; +1F8A;GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F0A 0345;;;;N;;;;1F82; +1F8B;GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F0B 0345;;;;N;;;;1F83; +1F8C;GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F0C 0345;;;;N;;;;1F84; +1F8D;GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F0D 0345;;;;N;;;;1F85; +1F8E;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F0E 0345;;;;N;;;;1F86; +1F8F;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F0F 0345;;;;N;;;;1F87; +1F90;GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F20 0345;;;;N;;;1F98;;1F98 +1F91;GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F21 0345;;;;N;;;1F99;;1F99 +1F92;GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F22 0345;;;;N;;;1F9A;;1F9A +1F93;GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F23 0345;;;;N;;;1F9B;;1F9B +1F94;GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F24 0345;;;;N;;;1F9C;;1F9C +1F95;GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F25 0345;;;;N;;;1F9D;;1F9D +1F96;GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F26 0345;;;;N;;;1F9E;;1F9E +1F97;GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F27 0345;;;;N;;;1F9F;;1F9F +1F98;GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F28 0345;;;;N;;;;1F90; +1F99;GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F29 0345;;;;N;;;;1F91; +1F9A;GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F2A 0345;;;;N;;;;1F92; +1F9B;GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F2B 0345;;;;N;;;;1F93; +1F9C;GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F2C 0345;;;;N;;;;1F94; +1F9D;GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F2D 0345;;;;N;;;;1F95; +1F9E;GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F2E 0345;;;;N;;;;1F96; +1F9F;GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F2F 0345;;;;N;;;;1F97; +1FA0;GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F60 0345;;;;N;;;1FA8;;1FA8 +1FA1;GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F61 0345;;;;N;;;1FA9;;1FA9 +1FA2;GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F62 0345;;;;N;;;1FAA;;1FAA +1FA3;GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F63 0345;;;;N;;;1FAB;;1FAB +1FA4;GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F64 0345;;;;N;;;1FAC;;1FAC +1FA5;GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F65 0345;;;;N;;;1FAD;;1FAD +1FA6;GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F66 0345;;;;N;;;1FAE;;1FAE +1FA7;GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F67 0345;;;;N;;;1FAF;;1FAF +1FA8;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F68 0345;;;;N;;;;1FA0; +1FA9;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F69 0345;;;;N;;;;1FA1; +1FAA;GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F6A 0345;;;;N;;;;1FA2; +1FAB;GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F6B 0345;;;;N;;;;1FA3; +1FAC;GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F6C 0345;;;;N;;;;1FA4; +1FAD;GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F6D 0345;;;;N;;;;1FA5; +1FAE;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F6E 0345;;;;N;;;;1FA6; +1FAF;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F6F 0345;;;;N;;;;1FA7; +1FB0;GREEK SMALL LETTER ALPHA WITH VRACHY;Ll;0;L;03B1 0306;;;;N;;;1FB8;;1FB8 +1FB1;GREEK SMALL LETTER ALPHA WITH MACRON;Ll;0;L;03B1 0304;;;;N;;;1FB9;;1FB9 +1FB2;GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F70 0345;;;;N;;;;; +1FB3;GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI;Ll;0;L;03B1 0345;;;;N;;;1FBC;;1FBC +1FB4;GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AC 0345;;;;N;;;;; +1FB6;GREEK SMALL LETTER ALPHA WITH PERISPOMENI;Ll;0;L;03B1 0342;;;;N;;;;; +1FB7;GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FB6 0345;;;;N;;;;; +1FB8;GREEK CAPITAL LETTER ALPHA WITH VRACHY;Lu;0;L;0391 0306;;;;N;;;;1FB0; +1FB9;GREEK CAPITAL LETTER ALPHA WITH MACRON;Lu;0;L;0391 0304;;;;N;;;;1FB1; +1FBA;GREEK CAPITAL LETTER ALPHA WITH VARIA;Lu;0;L;0391 0300;;;;N;;;;1F70; +1FBB;GREEK CAPITAL LETTER ALPHA WITH OXIA;Lu;0;L;0386;;;;N;;;;1F71; +1FBC;GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI;Lt;0;L;0391 0345;;;;N;;;;1FB3; +1FBD;GREEK KORONIS;Sk;0;ON;<compat> 0020 0313;;;;N;;;;; +1FBE;GREEK PROSGEGRAMMENI;Ll;0;L;03B9;;;;N;;;0399;;0399 +1FBF;GREEK PSILI;Sk;0;ON;<compat> 0020 0313;;;;N;;;;; +1FC0;GREEK PERISPOMENI;Sk;0;ON;<compat> 0020 0342;;;;N;;;;; +1FC1;GREEK DIALYTIKA AND PERISPOMENI;Sk;0;ON;00A8 0342;;;;N;;;;; +1FC2;GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F74 0345;;;;N;;;;; +1FC3;GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI;Ll;0;L;03B7 0345;;;;N;;;1FCC;;1FCC +1FC4;GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AE 0345;;;;N;;;;; +1FC6;GREEK SMALL LETTER ETA WITH PERISPOMENI;Ll;0;L;03B7 0342;;;;N;;;;; +1FC7;GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FC6 0345;;;;N;;;;; +1FC8;GREEK CAPITAL LETTER EPSILON WITH VARIA;Lu;0;L;0395 0300;;;;N;;;;1F72; +1FC9;GREEK CAPITAL LETTER EPSILON WITH OXIA;Lu;0;L;0388;;;;N;;;;1F73; +1FCA;GREEK CAPITAL LETTER ETA WITH VARIA;Lu;0;L;0397 0300;;;;N;;;;1F74; +1FCB;GREEK CAPITAL LETTER ETA WITH OXIA;Lu;0;L;0389;;;;N;;;;1F75; +1FCC;GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI;Lt;0;L;0397 0345;;;;N;;;;1FC3; +1FCD;GREEK PSILI AND VARIA;Sk;0;ON;1FBF 0300;;;;N;;;;; +1FCE;GREEK PSILI AND OXIA;Sk;0;ON;1FBF 0301;;;;N;;;;; +1FCF;GREEK PSILI AND PERISPOMENI;Sk;0;ON;1FBF 0342;;;;N;;;;; +1FD0;GREEK SMALL LETTER IOTA WITH VRACHY;Ll;0;L;03B9 0306;;;;N;;;1FD8;;1FD8 +1FD1;GREEK SMALL LETTER IOTA WITH MACRON;Ll;0;L;03B9 0304;;;;N;;;1FD9;;1FD9 +1FD2;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA;Ll;0;L;03CA 0300;;;;N;;;;; +1FD3;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA;Ll;0;L;0390;;;;N;;;;; +1FD6;GREEK SMALL LETTER IOTA WITH PERISPOMENI;Ll;0;L;03B9 0342;;;;N;;;;; +1FD7;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI;Ll;0;L;03CA 0342;;;;N;;;;; +1FD8;GREEK CAPITAL LETTER IOTA WITH VRACHY;Lu;0;L;0399 0306;;;;N;;;;1FD0; +1FD9;GREEK CAPITAL LETTER IOTA WITH MACRON;Lu;0;L;0399 0304;;;;N;;;;1FD1; +1FDA;GREEK CAPITAL LETTER IOTA WITH VARIA;Lu;0;L;0399 0300;;;;N;;;;1F76; +1FDB;GREEK CAPITAL LETTER IOTA WITH OXIA;Lu;0;L;038A;;;;N;;;;1F77; +1FDD;GREEK DASIA AND VARIA;Sk;0;ON;1FFE 0300;;;;N;;;;; +1FDE;GREEK DASIA AND OXIA;Sk;0;ON;1FFE 0301;;;;N;;;;; +1FDF;GREEK DASIA AND PERISPOMENI;Sk;0;ON;1FFE 0342;;;;N;;;;; +1FE0;GREEK SMALL LETTER UPSILON WITH VRACHY;Ll;0;L;03C5 0306;;;;N;;;1FE8;;1FE8 +1FE1;GREEK SMALL LETTER UPSILON WITH MACRON;Ll;0;L;03C5 0304;;;;N;;;1FE9;;1FE9 +1FE2;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA;Ll;0;L;03CB 0300;;;;N;;;;; +1FE3;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA;Ll;0;L;03B0;;;;N;;;;; +1FE4;GREEK SMALL LETTER RHO WITH PSILI;Ll;0;L;03C1 0313;;;;N;;;;; +1FE5;GREEK SMALL LETTER RHO WITH DASIA;Ll;0;L;03C1 0314;;;;N;;;1FEC;;1FEC +1FE6;GREEK SMALL LETTER UPSILON WITH PERISPOMENI;Ll;0;L;03C5 0342;;;;N;;;;; +1FE7;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI;Ll;0;L;03CB 0342;;;;N;;;;; +1FE8;GREEK CAPITAL LETTER UPSILON WITH VRACHY;Lu;0;L;03A5 0306;;;;N;;;;1FE0; +1FE9;GREEK CAPITAL LETTER UPSILON WITH MACRON;Lu;0;L;03A5 0304;;;;N;;;;1FE1; +1FEA;GREEK CAPITAL LETTER UPSILON WITH VARIA;Lu;0;L;03A5 0300;;;;N;;;;1F7A; +1FEB;GREEK CAPITAL LETTER UPSILON WITH OXIA;Lu;0;L;038E;;;;N;;;;1F7B; +1FEC;GREEK CAPITAL LETTER RHO WITH DASIA;Lu;0;L;03A1 0314;;;;N;;;;1FE5; +1FED;GREEK DIALYTIKA AND VARIA;Sk;0;ON;00A8 0300;;;;N;;;;; +1FEE;GREEK DIALYTIKA AND OXIA;Sk;0;ON;0385;;;;N;;;;; +1FEF;GREEK VARIA;Sk;0;ON;0060;;;;N;;;;; +1FF2;GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F7C 0345;;;;N;;;;; +1FF3;GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI;Ll;0;L;03C9 0345;;;;N;;;1FFC;;1FFC +1FF4;GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03CE 0345;;;;N;;;;; +1FF6;GREEK SMALL LETTER OMEGA WITH PERISPOMENI;Ll;0;L;03C9 0342;;;;N;;;;; +1FF7;GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FF6 0345;;;;N;;;;; +1FF8;GREEK CAPITAL LETTER OMICRON WITH VARIA;Lu;0;L;039F 0300;;;;N;;;;1F78; +1FF9;GREEK CAPITAL LETTER OMICRON WITH OXIA;Lu;0;L;038C;;;;N;;;;1F79; +1FFA;GREEK CAPITAL LETTER OMEGA WITH VARIA;Lu;0;L;03A9 0300;;;;N;;;;1F7C; +1FFB;GREEK CAPITAL LETTER OMEGA WITH OXIA;Lu;0;L;038F;;;;N;;;;1F7D; +1FFC;GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI;Lt;0;L;03A9 0345;;;;N;;;;1FF3; +1FFD;GREEK OXIA;Sk;0;ON;00B4;;;;N;;;;; +1FFE;GREEK DASIA;Sk;0;ON;<compat> 0020 0314;;;;N;;;;; +2000;EN QUAD;Zs;0;WS;2002;;;;N;;;;; +2001;EM QUAD;Zs;0;WS;2003;;;;N;;;;; +2002;EN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +2003;EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +2004;THREE-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +2005;FOUR-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +2006;SIX-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +2007;FIGURE SPACE;Zs;0;WS;<noBreak> 0020;;;;N;;;;; +2008;PUNCTUATION SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +2009;THIN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +200A;HAIR SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +200B;ZERO WIDTH SPACE;Cf;0;BN;;;;;N;;;;; +200C;ZERO WIDTH NON-JOINER;Cf;0;BN;;;;;N;;;;; +200D;ZERO WIDTH JOINER;Cf;0;BN;;;;;N;;;;; +200E;LEFT-TO-RIGHT MARK;Cf;0;L;;;;;N;;;;; +200F;RIGHT-TO-LEFT MARK;Cf;0;R;;;;;N;;;;; +2010;HYPHEN;Pd;0;ON;;;;;N;;;;; +2011;NON-BREAKING HYPHEN;Pd;0;ON;<noBreak> 2010;;;;N;;;;; +2012;FIGURE DASH;Pd;0;ON;;;;;N;;;;; +2013;EN DASH;Pd;0;ON;;;;;N;;;;; +2014;EM DASH;Pd;0;ON;;;;;N;;;;; +2015;HORIZONTAL BAR;Pd;0;ON;;;;;N;QUOTATION DASH;;;; +2016;DOUBLE VERTICAL LINE;Po;0;ON;;;;;N;DOUBLE VERTICAL BAR;;;; +2017;DOUBLE LOW LINE;Po;0;ON;<compat> 0020 0333;;;;N;SPACING DOUBLE UNDERSCORE;;;; +2018;LEFT SINGLE QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE TURNED COMMA QUOTATION MARK;;;; +2019;RIGHT SINGLE QUOTATION MARK;Pf;0;ON;;;;;N;SINGLE COMMA QUOTATION MARK;;;; +201A;SINGLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW SINGLE COMMA QUOTATION MARK;;;; +201B;SINGLE HIGH-REVERSED-9 QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE REVERSED COMMA QUOTATION MARK;;;; +201C;LEFT DOUBLE QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE TURNED COMMA QUOTATION MARK;;;; +201D;RIGHT DOUBLE QUOTATION MARK;Pf;0;ON;;;;;N;DOUBLE COMMA QUOTATION MARK;;;; +201E;DOUBLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW DOUBLE COMMA QUOTATION MARK;;;; +201F;DOUBLE HIGH-REVERSED-9 QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE REVERSED COMMA QUOTATION MARK;;;; +2020;DAGGER;Po;0;ON;;;;;N;;;;; +2021;DOUBLE DAGGER;Po;0;ON;;;;;N;;;;; +2022;BULLET;Po;0;ON;;;;;N;;;;; +2023;TRIANGULAR BULLET;Po;0;ON;;;;;N;;;;; +2024;ONE DOT LEADER;Po;0;ON;<compat> 002E;;;;N;;;;; +2025;TWO DOT LEADER;Po;0;ON;<compat> 002E 002E;;;;N;;;;; +2026;HORIZONTAL ELLIPSIS;Po;0;ON;<compat> 002E 002E 002E;;;;N;;;;; +2027;HYPHENATION POINT;Po;0;ON;;;;;N;;;;; +2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; +2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; +202A;LEFT-TO-RIGHT EMBEDDING;Cf;0;LRE;;;;;N;;;;; +202B;RIGHT-TO-LEFT EMBEDDING;Cf;0;RLE;;;;;N;;;;; +202C;POP DIRECTIONAL FORMATTING;Cf;0;PDF;;;;;N;;;;; +202D;LEFT-TO-RIGHT OVERRIDE;Cf;0;LRO;;;;;N;;;;; +202E;RIGHT-TO-LEFT OVERRIDE;Cf;0;RLO;;;;;N;;;;; +202F;NARROW NO-BREAK SPACE;Zs;0;CS;<noBreak> 0020;;;;N;;;;; +2030;PER MILLE SIGN;Po;0;ET;;;;;N;;;;; +2031;PER TEN THOUSAND SIGN;Po;0;ET;;;;;N;;;;; +2032;PRIME;Po;0;ET;;;;;N;;;;; +2033;DOUBLE PRIME;Po;0;ET;<compat> 2032 2032;;;;N;;;;; +2034;TRIPLE PRIME;Po;0;ET;<compat> 2032 2032 2032;;;;N;;;;; +2035;REVERSED PRIME;Po;0;ON;;;;;N;;;;; +2036;REVERSED DOUBLE PRIME;Po;0;ON;<compat> 2035 2035;;;;N;;;;; +2037;REVERSED TRIPLE PRIME;Po;0;ON;<compat> 2035 2035 2035;;;;N;;;;; +2038;CARET;Po;0;ON;;;;;N;;;;; +2039;SINGLE LEFT-POINTING ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING SINGLE GUILLEMET;;;; +203A;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING SINGLE GUILLEMET;;;; +203B;REFERENCE MARK;Po;0;ON;;;;;N;;;;; +203C;DOUBLE EXCLAMATION MARK;Po;0;ON;<compat> 0021 0021;;;;N;;;;; +203D;INTERROBANG;Po;0;ON;;;;;N;;;;; +203E;OVERLINE;Po;0;ON;<compat> 0020 0305;;;;N;SPACING OVERSCORE;;;; +203F;UNDERTIE;Pc;0;ON;;;;;N;;;;; +2040;CHARACTER TIE;Pc;0;ON;;;;;N;;;;; +2041;CARET INSERTION POINT;Po;0;ON;;;;;N;;;;; +2042;ASTERISM;Po;0;ON;;;;;N;;;;; +2043;HYPHEN BULLET;Po;0;ON;;;;;N;;;;; +2044;FRACTION SLASH;Sm;0;CS;;;;;N;;;;; +2045;LEFT SQUARE BRACKET WITH QUILL;Ps;0;ON;;;;;Y;;;;; +2046;RIGHT SQUARE BRACKET WITH QUILL;Pe;0;ON;;;;;Y;;;;; +2047;DOUBLE QUESTION MARK;Po;0;ON;<compat> 003F 003F;;;;N;;;;; +2048;QUESTION EXCLAMATION MARK;Po;0;ON;<compat> 003F 0021;;;;N;;;;; +2049;EXCLAMATION QUESTION MARK;Po;0;ON;<compat> 0021 003F;;;;N;;;;; +204A;TIRONIAN SIGN ET;Po;0;ON;;;;;N;;;;; +204B;REVERSED PILCROW SIGN;Po;0;ON;;;;;N;;;;; +204C;BLACK LEFTWARDS BULLET;Po;0;ON;;;;;N;;;;; +204D;BLACK RIGHTWARDS BULLET;Po;0;ON;;;;;N;;;;; +204E;LOW ASTERISK;Po;0;ON;;;;;N;;;;; +204F;REVERSED SEMICOLON;Po;0;ON;;;;;N;;;;; +2050;CLOSE UP;Po;0;ON;;;;;N;;;;; +2051;TWO ASTERISKS ALIGNED VERTICALLY;Po;0;ON;;;;;N;;;;; +2052;COMMERCIAL MINUS SIGN;Sm;0;ON;;;;;N;;;;; +2053;SWUNG DASH;Po;0;ON;;;;;N;;;;; +2054;INVERTED UNDERTIE;Pc;0;ON;;;;;N;;;;; +2055;FLOWER PUNCTUATION MARK;Po;0;ON;;;;;N;;;;; +2056;THREE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; +2057;QUADRUPLE PRIME;Po;0;ON;<compat> 2032 2032 2032 2032;;;;N;;;;; +2058;FOUR DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; +2059;FIVE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; +205A;TWO DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; +205B;FOUR DOT MARK;Po;0;ON;;;;;N;;;;; +205C;DOTTED CROSS;Po;0;ON;;;;;N;;;;; +205D;TRICOLON;Po;0;ON;;;;;N;;;;; +205E;VERTICAL FOUR DOTS;Po;0;ON;;;;;N;;;;; +205F;MEDIUM MATHEMATICAL SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +2060;WORD JOINER;Cf;0;BN;;;;;N;;;;; +2061;FUNCTION APPLICATION;Cf;0;BN;;;;;N;;;;; +2062;INVISIBLE TIMES;Cf;0;BN;;;;;N;;;;; +2063;INVISIBLE SEPARATOR;Cf;0;BN;;;;;N;;;;; +2064;INVISIBLE PLUS;Cf;0;BN;;;;;N;;;;; +2066;LEFT-TO-RIGHT ISOLATE;Cf;0;LRI;;;;;N;;;;; +2067;RIGHT-TO-LEFT ISOLATE;Cf;0;RLI;;;;;N;;;;; +2068;FIRST STRONG ISOLATE;Cf;0;FSI;;;;;N;;;;; +2069;POP DIRECTIONAL ISOLATE;Cf;0;PDI;;;;;N;;;;; +206A;INHIBIT SYMMETRIC SWAPPING;Cf;0;BN;;;;;N;;;;; +206B;ACTIVATE SYMMETRIC SWAPPING;Cf;0;BN;;;;;N;;;;; +206C;INHIBIT ARABIC FORM SHAPING;Cf;0;BN;;;;;N;;;;; +206D;ACTIVATE ARABIC FORM SHAPING;Cf;0;BN;;;;;N;;;;; +206E;NATIONAL DIGIT SHAPES;Cf;0;BN;;;;;N;;;;; +206F;NOMINAL DIGIT SHAPES;Cf;0;BN;;;;;N;;;;; +2070;SUPERSCRIPT ZERO;No;0;EN;<super> 0030;;0;0;N;SUPERSCRIPT DIGIT ZERO;;;; +2071;SUPERSCRIPT LATIN SMALL LETTER I;Lm;0;L;<super> 0069;;;;N;;;;; +2074;SUPERSCRIPT FOUR;No;0;EN;<super> 0034;;4;4;N;SUPERSCRIPT DIGIT FOUR;;;; +2075;SUPERSCRIPT FIVE;No;0;EN;<super> 0035;;5;5;N;SUPERSCRIPT DIGIT FIVE;;;; +2076;SUPERSCRIPT SIX;No;0;EN;<super> 0036;;6;6;N;SUPERSCRIPT DIGIT SIX;;;; +2077;SUPERSCRIPT SEVEN;No;0;EN;<super> 0037;;7;7;N;SUPERSCRIPT DIGIT SEVEN;;;; +2078;SUPERSCRIPT EIGHT;No;0;EN;<super> 0038;;8;8;N;SUPERSCRIPT DIGIT EIGHT;;;; +2079;SUPERSCRIPT NINE;No;0;EN;<super> 0039;;9;9;N;SUPERSCRIPT DIGIT NINE;;;; +207A;SUPERSCRIPT PLUS SIGN;Sm;0;ES;<super> 002B;;;;N;;;;; +207B;SUPERSCRIPT MINUS;Sm;0;ES;<super> 2212;;;;N;SUPERSCRIPT HYPHEN-MINUS;;;; +207C;SUPERSCRIPT EQUALS SIGN;Sm;0;ON;<super> 003D;;;;N;;;;; +207D;SUPERSCRIPT LEFT PARENTHESIS;Ps;0;ON;<super> 0028;;;;Y;SUPERSCRIPT OPENING PARENTHESIS;;;; +207E;SUPERSCRIPT RIGHT PARENTHESIS;Pe;0;ON;<super> 0029;;;;Y;SUPERSCRIPT CLOSING PARENTHESIS;;;; +207F;SUPERSCRIPT LATIN SMALL LETTER N;Lm;0;L;<super> 006E;;;;N;;;;; +2080;SUBSCRIPT ZERO;No;0;EN;<sub> 0030;;0;0;N;SUBSCRIPT DIGIT ZERO;;;; +2081;SUBSCRIPT ONE;No;0;EN;<sub> 0031;;1;1;N;SUBSCRIPT DIGIT ONE;;;; +2082;SUBSCRIPT TWO;No;0;EN;<sub> 0032;;2;2;N;SUBSCRIPT DIGIT TWO;;;; +2083;SUBSCRIPT THREE;No;0;EN;<sub> 0033;;3;3;N;SUBSCRIPT DIGIT THREE;;;; +2084;SUBSCRIPT FOUR;No;0;EN;<sub> 0034;;4;4;N;SUBSCRIPT DIGIT FOUR;;;; +2085;SUBSCRIPT FIVE;No;0;EN;<sub> 0035;;5;5;N;SUBSCRIPT DIGIT FIVE;;;; +2086;SUBSCRIPT SIX;No;0;EN;<sub> 0036;;6;6;N;SUBSCRIPT DIGIT SIX;;;; +2087;SUBSCRIPT SEVEN;No;0;EN;<sub> 0037;;7;7;N;SUBSCRIPT DIGIT SEVEN;;;; +2088;SUBSCRIPT EIGHT;No;0;EN;<sub> 0038;;8;8;N;SUBSCRIPT DIGIT EIGHT;;;; +2089;SUBSCRIPT NINE;No;0;EN;<sub> 0039;;9;9;N;SUBSCRIPT DIGIT NINE;;;; +208A;SUBSCRIPT PLUS SIGN;Sm;0;ES;<sub> 002B;;;;N;;;;; +208B;SUBSCRIPT MINUS;Sm;0;ES;<sub> 2212;;;;N;SUBSCRIPT HYPHEN-MINUS;;;; +208C;SUBSCRIPT EQUALS SIGN;Sm;0;ON;<sub> 003D;;;;N;;;;; +208D;SUBSCRIPT LEFT PARENTHESIS;Ps;0;ON;<sub> 0028;;;;Y;SUBSCRIPT OPENING PARENTHESIS;;;; +208E;SUBSCRIPT RIGHT PARENTHESIS;Pe;0;ON;<sub> 0029;;;;Y;SUBSCRIPT CLOSING PARENTHESIS;;;; +2090;LATIN SUBSCRIPT SMALL LETTER A;Lm;0;L;<sub> 0061;;;;N;;;;; +2091;LATIN SUBSCRIPT SMALL LETTER E;Lm;0;L;<sub> 0065;;;;N;;;;; +2092;LATIN SUBSCRIPT SMALL LETTER O;Lm;0;L;<sub> 006F;;;;N;;;;; +2093;LATIN SUBSCRIPT SMALL LETTER X;Lm;0;L;<sub> 0078;;;;N;;;;; +2094;LATIN SUBSCRIPT SMALL LETTER SCHWA;Lm;0;L;<sub> 0259;;;;N;;;;; +2095;LATIN SUBSCRIPT SMALL LETTER H;Lm;0;L;<sub> 0068;;;;N;;;;; +2096;LATIN SUBSCRIPT SMALL LETTER K;Lm;0;L;<sub> 006B;;;;N;;;;; +2097;LATIN SUBSCRIPT SMALL LETTER L;Lm;0;L;<sub> 006C;;;;N;;;;; +2098;LATIN SUBSCRIPT SMALL LETTER M;Lm;0;L;<sub> 006D;;;;N;;;;; +2099;LATIN SUBSCRIPT SMALL LETTER N;Lm;0;L;<sub> 006E;;;;N;;;;; +209A;LATIN SUBSCRIPT SMALL LETTER P;Lm;0;L;<sub> 0070;;;;N;;;;; +209B;LATIN SUBSCRIPT SMALL LETTER S;Lm;0;L;<sub> 0073;;;;N;;;;; +209C;LATIN SUBSCRIPT SMALL LETTER T;Lm;0;L;<sub> 0074;;;;N;;;;; +20A0;EURO-CURRENCY SIGN;Sc;0;ET;;;;;N;;;;; +20A1;COLON SIGN;Sc;0;ET;;;;;N;;;;; +20A2;CRUZEIRO SIGN;Sc;0;ET;;;;;N;;;;; +20A3;FRENCH FRANC SIGN;Sc;0;ET;;;;;N;;;;; +20A4;LIRA SIGN;Sc;0;ET;;;;;N;;;;; +20A5;MILL SIGN;Sc;0;ET;;;;;N;;;;; +20A6;NAIRA SIGN;Sc;0;ET;;;;;N;;;;; +20A7;PESETA SIGN;Sc;0;ET;;;;;N;;;;; +20A8;RUPEE SIGN;Sc;0;ET;<compat> 0052 0073;;;;N;;;;; +20A9;WON SIGN;Sc;0;ET;;;;;N;;;;; +20AA;NEW SHEQEL SIGN;Sc;0;ET;;;;;N;;;;; +20AB;DONG SIGN;Sc;0;ET;;;;;N;;;;; +20AC;EURO SIGN;Sc;0;ET;;;;;N;;;;; +20AD;KIP SIGN;Sc;0;ET;;;;;N;;;;; +20AE;TUGRIK SIGN;Sc;0;ET;;;;;N;;;;; +20AF;DRACHMA SIGN;Sc;0;ET;;;;;N;;;;; +20B0;GERMAN PENNY SIGN;Sc;0;ET;;;;;N;;;;; +20B1;PESO SIGN;Sc;0;ET;;;;;N;;;;; +20B2;GUARANI SIGN;Sc;0;ET;;;;;N;;;;; +20B3;AUSTRAL SIGN;Sc;0;ET;;;;;N;;;;; +20B4;HRYVNIA SIGN;Sc;0;ET;;;;;N;;;;; +20B5;CEDI SIGN;Sc;0;ET;;;;;N;;;;; +20B6;LIVRE TOURNOIS SIGN;Sc;0;ET;;;;;N;;;;; +20B7;SPESMILO SIGN;Sc;0;ET;;;;;N;;;;; +20B8;TENGE SIGN;Sc;0;ET;;;;;N;;;;; +20B9;INDIAN RUPEE SIGN;Sc;0;ET;;;;;N;;;;; +20BA;TURKISH LIRA SIGN;Sc;0;ET;;;;;N;;;;; +20BB;NORDIC MARK SIGN;Sc;0;ET;;;;;N;;;;; +20BC;MANAT SIGN;Sc;0;ET;;;;;N;;;;; +20BD;RUBLE SIGN;Sc;0;ET;;;;;N;;;;; +20BE;LARI SIGN;Sc;0;ET;;;;;N;;;;; +20D0;COMBINING LEFT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT HARPOON ABOVE;;;; +20D1;COMBINING RIGHT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT HARPOON ABOVE;;;; +20D2;COMBINING LONG VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG VERTICAL BAR OVERLAY;;;; +20D3;COMBINING SHORT VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT VERTICAL BAR OVERLAY;;;; +20D4;COMBINING ANTICLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING ANTICLOCKWISE ARROW ABOVE;;;; +20D5;COMBINING CLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING CLOCKWISE ARROW ABOVE;;;; +20D6;COMBINING LEFT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT ARROW ABOVE;;;; +20D7;COMBINING RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT ARROW ABOVE;;;; +20D8;COMBINING RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING RING OVERLAY;;;; +20D9;COMBINING CLOCKWISE RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING CLOCKWISE RING OVERLAY;;;; +20DA;COMBINING ANTICLOCKWISE RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING ANTICLOCKWISE RING OVERLAY;;;; +20DB;COMBINING THREE DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING THREE DOTS ABOVE;;;; +20DC;COMBINING FOUR DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING FOUR DOTS ABOVE;;;; +20DD;COMBINING ENCLOSING CIRCLE;Me;0;NSM;;;;;N;ENCLOSING CIRCLE;;;; +20DE;COMBINING ENCLOSING SQUARE;Me;0;NSM;;;;;N;ENCLOSING SQUARE;;;; +20DF;COMBINING ENCLOSING DIAMOND;Me;0;NSM;;;;;N;ENCLOSING DIAMOND;;;; +20E0;COMBINING ENCLOSING CIRCLE BACKSLASH;Me;0;NSM;;;;;N;ENCLOSING CIRCLE SLASH;;;; +20E1;COMBINING LEFT RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT RIGHT ARROW ABOVE;;;; +20E2;COMBINING ENCLOSING SCREEN;Me;0;NSM;;;;;N;;;;; +20E3;COMBINING ENCLOSING KEYCAP;Me;0;NSM;;;;;N;;;;; +20E4;COMBINING ENCLOSING UPWARD POINTING TRIANGLE;Me;0;NSM;;;;;N;;;;; +20E5;COMBINING REVERSE SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;;;;; +20E6;COMBINING DOUBLE VERTICAL STROKE OVERLAY;Mn;1;NSM;;;;;N;;;;; +20E7;COMBINING ANNUITY SYMBOL;Mn;230;NSM;;;;;N;;;;; +20E8;COMBINING TRIPLE UNDERDOT;Mn;220;NSM;;;;;N;;;;; +20E9;COMBINING WIDE BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;; +20EA;COMBINING LEFTWARDS ARROW OVERLAY;Mn;1;NSM;;;;;N;;;;; +20EB;COMBINING LONG DOUBLE SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;;;;; +20EC;COMBINING RIGHTWARDS HARPOON WITH BARB DOWNWARDS;Mn;220;NSM;;;;;N;;;;; +20ED;COMBINING LEFTWARDS HARPOON WITH BARB DOWNWARDS;Mn;220;NSM;;;;;N;;;;; +20EE;COMBINING LEFT ARROW BELOW;Mn;220;NSM;;;;;N;;;;; +20EF;COMBINING RIGHT ARROW BELOW;Mn;220;NSM;;;;;N;;;;; +20F0;COMBINING ASTERISK ABOVE;Mn;230;NSM;;;;;N;;;;; +2100;ACCOUNT OF;So;0;ON;<compat> 0061 002F 0063;;;;N;;;;; +2101;ADDRESSED TO THE SUBJECT;So;0;ON;<compat> 0061 002F 0073;;;;N;;;;; +2102;DOUBLE-STRUCK CAPITAL C;Lu;0;L;<font> 0043;;;;N;DOUBLE-STRUCK C;;;; +2103;DEGREE CELSIUS;So;0;ON;<compat> 00B0 0043;;;;N;DEGREES CENTIGRADE;;;; +2104;CENTRE LINE SYMBOL;So;0;ON;;;;;N;C L SYMBOL;;;; +2105;CARE OF;So;0;ON;<compat> 0063 002F 006F;;;;N;;;;; +2106;CADA UNA;So;0;ON;<compat> 0063 002F 0075;;;;N;;;;; +2107;EULER CONSTANT;Lu;0;L;<compat> 0190;;;;N;EULERS;;;; +2108;SCRUPLE;So;0;ON;;;;;N;;;;; +2109;DEGREE FAHRENHEIT;So;0;ON;<compat> 00B0 0046;;;;N;DEGREES FAHRENHEIT;;;; +210A;SCRIPT SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +210B;SCRIPT CAPITAL H;Lu;0;L;<font> 0048;;;;N;SCRIPT H;;;; +210C;BLACK-LETTER CAPITAL H;Lu;0;L;<font> 0048;;;;N;BLACK-LETTER H;;;; +210D;DOUBLE-STRUCK CAPITAL H;Lu;0;L;<font> 0048;;;;N;DOUBLE-STRUCK H;;;; +210E;PLANCK CONSTANT;Ll;0;L;<font> 0068;;;;N;;;;; +210F;PLANCK CONSTANT OVER TWO PI;Ll;0;L;<font> 0127;;;;N;PLANCK CONSTANT OVER 2 PI;;;; +2110;SCRIPT CAPITAL I;Lu;0;L;<font> 0049;;;;N;SCRIPT I;;;; +2111;BLACK-LETTER CAPITAL I;Lu;0;L;<font> 0049;;;;N;BLACK-LETTER I;;;; +2112;SCRIPT CAPITAL L;Lu;0;L;<font> 004C;;;;N;SCRIPT L;;;; +2113;SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +2114;L B BAR SYMBOL;So;0;ON;;;;;N;;;;; +2115;DOUBLE-STRUCK CAPITAL N;Lu;0;L;<font> 004E;;;;N;DOUBLE-STRUCK N;;;; +2116;NUMERO SIGN;So;0;ON;<compat> 004E 006F;;;;N;NUMERO;;;; +2117;SOUND RECORDING COPYRIGHT;So;0;ON;;;;;N;;;;; +2118;SCRIPT CAPITAL P;Sm;0;ON;;;;;N;SCRIPT P;;;; +2119;DOUBLE-STRUCK CAPITAL P;Lu;0;L;<font> 0050;;;;N;DOUBLE-STRUCK P;;;; +211A;DOUBLE-STRUCK CAPITAL Q;Lu;0;L;<font> 0051;;;;N;DOUBLE-STRUCK Q;;;; +211B;SCRIPT CAPITAL R;Lu;0;L;<font> 0052;;;;N;SCRIPT R;;;; +211C;BLACK-LETTER CAPITAL R;Lu;0;L;<font> 0052;;;;N;BLACK-LETTER R;;;; +211D;DOUBLE-STRUCK CAPITAL R;Lu;0;L;<font> 0052;;;;N;DOUBLE-STRUCK R;;;; +211E;PRESCRIPTION TAKE;So;0;ON;;;;;N;;;;; +211F;RESPONSE;So;0;ON;;;;;N;;;;; +2120;SERVICE MARK;So;0;ON;<super> 0053 004D;;;;N;;;;; +2121;TELEPHONE SIGN;So;0;ON;<compat> 0054 0045 004C;;;;N;T E L SYMBOL;;;; +2122;TRADE MARK SIGN;So;0;ON;<super> 0054 004D;;;;N;TRADEMARK;;;; +2123;VERSICLE;So;0;ON;;;;;N;;;;; +2124;DOUBLE-STRUCK CAPITAL Z;Lu;0;L;<font> 005A;;;;N;DOUBLE-STRUCK Z;;;; +2125;OUNCE SIGN;So;0;ON;;;;;N;OUNCE;;;; +2126;OHM SIGN;Lu;0;L;03A9;;;;N;OHM;;;03C9; +2127;INVERTED OHM SIGN;So;0;ON;;;;;N;MHO;;;; +2128;BLACK-LETTER CAPITAL Z;Lu;0;L;<font> 005A;;;;N;BLACK-LETTER Z;;;; +2129;TURNED GREEK SMALL LETTER IOTA;So;0;ON;;;;;N;;;;; +212A;KELVIN SIGN;Lu;0;L;004B;;;;N;DEGREES KELVIN;;;006B; +212B;ANGSTROM SIGN;Lu;0;L;00C5;;;;N;ANGSTROM UNIT;;;00E5; +212C;SCRIPT CAPITAL B;Lu;0;L;<font> 0042;;;;N;SCRIPT B;;;; +212D;BLACK-LETTER CAPITAL C;Lu;0;L;<font> 0043;;;;N;BLACK-LETTER C;;;; +212E;ESTIMATED SYMBOL;So;0;ET;;;;;N;;;;; +212F;SCRIPT SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +2130;SCRIPT CAPITAL E;Lu;0;L;<font> 0045;;;;N;SCRIPT E;;;; +2131;SCRIPT CAPITAL F;Lu;0;L;<font> 0046;;;;N;SCRIPT F;;;; +2132;TURNED CAPITAL F;Lu;0;L;;;;;N;TURNED F;;;214E; +2133;SCRIPT CAPITAL M;Lu;0;L;<font> 004D;;;;N;SCRIPT M;;;; +2134;SCRIPT SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +2135;ALEF SYMBOL;Lo;0;L;<compat> 05D0;;;;N;FIRST TRANSFINITE CARDINAL;;;; +2136;BET SYMBOL;Lo;0;L;<compat> 05D1;;;;N;SECOND TRANSFINITE CARDINAL;;;; +2137;GIMEL SYMBOL;Lo;0;L;<compat> 05D2;;;;N;THIRD TRANSFINITE CARDINAL;;;; +2138;DALET SYMBOL;Lo;0;L;<compat> 05D3;;;;N;FOURTH TRANSFINITE CARDINAL;;;; +2139;INFORMATION SOURCE;Ll;0;L;<font> 0069;;;;N;;;;; +213A;ROTATED CAPITAL Q;So;0;ON;;;;;N;;;;; +213B;FACSIMILE SIGN;So;0;ON;<compat> 0046 0041 0058;;;;N;;;;; +213C;DOUBLE-STRUCK SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;; +213D;DOUBLE-STRUCK SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;; +213E;DOUBLE-STRUCK CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;; +213F;DOUBLE-STRUCK CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;; +2140;DOUBLE-STRUCK N-ARY SUMMATION;Sm;0;ON;<font> 2211;;;;Y;;;;; +2141;TURNED SANS-SERIF CAPITAL G;Sm;0;ON;;;;;N;;;;; +2142;TURNED SANS-SERIF CAPITAL L;Sm;0;ON;;;;;N;;;;; +2143;REVERSED SANS-SERIF CAPITAL L;Sm;0;ON;;;;;N;;;;; +2144;TURNED SANS-SERIF CAPITAL Y;Sm;0;ON;;;;;N;;;;; +2145;DOUBLE-STRUCK ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +2146;DOUBLE-STRUCK ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +2147;DOUBLE-STRUCK ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +2148;DOUBLE-STRUCK ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +2149;DOUBLE-STRUCK ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +214A;PROPERTY LINE;So;0;ON;;;;;N;;;;; +214B;TURNED AMPERSAND;Sm;0;ON;;;;;N;;;;; +214C;PER SIGN;So;0;ON;;;;;N;;;;; +214D;AKTIESELSKAB;So;0;ON;;;;;N;;;;; +214E;TURNED SMALL F;Ll;0;L;;;;;N;;;2132;;2132 +214F;SYMBOL FOR SAMARITAN SOURCE;So;0;L;;;;;N;;;;; +2150;VULGAR FRACTION ONE SEVENTH;No;0;ON;<fraction> 0031 2044 0037;;;1/7;N;;;;; +2151;VULGAR FRACTION ONE NINTH;No;0;ON;<fraction> 0031 2044 0039;;;1/9;N;;;;; +2152;VULGAR FRACTION ONE TENTH;No;0;ON;<fraction> 0031 2044 0031 0030;;;1/10;N;;;;; +2153;VULGAR FRACTION ONE THIRD;No;0;ON;<fraction> 0031 2044 0033;;;1/3;N;FRACTION ONE THIRD;;;; +2154;VULGAR FRACTION TWO THIRDS;No;0;ON;<fraction> 0032 2044 0033;;;2/3;N;FRACTION TWO THIRDS;;;; +2155;VULGAR FRACTION ONE FIFTH;No;0;ON;<fraction> 0031 2044 0035;;;1/5;N;FRACTION ONE FIFTH;;;; +2156;VULGAR FRACTION TWO FIFTHS;No;0;ON;<fraction> 0032 2044 0035;;;2/5;N;FRACTION TWO FIFTHS;;;; +2157;VULGAR FRACTION THREE FIFTHS;No;0;ON;<fraction> 0033 2044 0035;;;3/5;N;FRACTION THREE FIFTHS;;;; +2158;VULGAR FRACTION FOUR FIFTHS;No;0;ON;<fraction> 0034 2044 0035;;;4/5;N;FRACTION FOUR FIFTHS;;;; +2159;VULGAR FRACTION ONE SIXTH;No;0;ON;<fraction> 0031 2044 0036;;;1/6;N;FRACTION ONE SIXTH;;;; +215A;VULGAR FRACTION FIVE SIXTHS;No;0;ON;<fraction> 0035 2044 0036;;;5/6;N;FRACTION FIVE SIXTHS;;;; +215B;VULGAR FRACTION ONE EIGHTH;No;0;ON;<fraction> 0031 2044 0038;;;1/8;N;FRACTION ONE EIGHTH;;;; +215C;VULGAR FRACTION THREE EIGHTHS;No;0;ON;<fraction> 0033 2044 0038;;;3/8;N;FRACTION THREE EIGHTHS;;;; +215D;VULGAR FRACTION FIVE EIGHTHS;No;0;ON;<fraction> 0035 2044 0038;;;5/8;N;FRACTION FIVE EIGHTHS;;;; +215E;VULGAR FRACTION SEVEN EIGHTHS;No;0;ON;<fraction> 0037 2044 0038;;;7/8;N;FRACTION SEVEN EIGHTHS;;;; +215F;FRACTION NUMERATOR ONE;No;0;ON;<fraction> 0031 2044;;;1;N;;;;; +2160;ROMAN NUMERAL ONE;Nl;0;L;<compat> 0049;;;1;N;;;;2170; +2161;ROMAN NUMERAL TWO;Nl;0;L;<compat> 0049 0049;;;2;N;;;;2171; +2162;ROMAN NUMERAL THREE;Nl;0;L;<compat> 0049 0049 0049;;;3;N;;;;2172; +2163;ROMAN NUMERAL FOUR;Nl;0;L;<compat> 0049 0056;;;4;N;;;;2173; +2164;ROMAN NUMERAL FIVE;Nl;0;L;<compat> 0056;;;5;N;;;;2174; +2165;ROMAN NUMERAL SIX;Nl;0;L;<compat> 0056 0049;;;6;N;;;;2175; +2166;ROMAN NUMERAL SEVEN;Nl;0;L;<compat> 0056 0049 0049;;;7;N;;;;2176; +2167;ROMAN NUMERAL EIGHT;Nl;0;L;<compat> 0056 0049 0049 0049;;;8;N;;;;2177; +2168;ROMAN NUMERAL NINE;Nl;0;L;<compat> 0049 0058;;;9;N;;;;2178; +2169;ROMAN NUMERAL TEN;Nl;0;L;<compat> 0058;;;10;N;;;;2179; +216A;ROMAN NUMERAL ELEVEN;Nl;0;L;<compat> 0058 0049;;;11;N;;;;217A; +216B;ROMAN NUMERAL TWELVE;Nl;0;L;<compat> 0058 0049 0049;;;12;N;;;;217B; +216C;ROMAN NUMERAL FIFTY;Nl;0;L;<compat> 004C;;;50;N;;;;217C; +216D;ROMAN NUMERAL ONE HUNDRED;Nl;0;L;<compat> 0043;;;100;N;;;;217D; +216E;ROMAN NUMERAL FIVE HUNDRED;Nl;0;L;<compat> 0044;;;500;N;;;;217E; +216F;ROMAN NUMERAL ONE THOUSAND;Nl;0;L;<compat> 004D;;;1000;N;;;;217F; +2170;SMALL ROMAN NUMERAL ONE;Nl;0;L;<compat> 0069;;;1;N;;;2160;;2160 +2171;SMALL ROMAN NUMERAL TWO;Nl;0;L;<compat> 0069 0069;;;2;N;;;2161;;2161 +2172;SMALL ROMAN NUMERAL THREE;Nl;0;L;<compat> 0069 0069 0069;;;3;N;;;2162;;2162 +2173;SMALL ROMAN NUMERAL FOUR;Nl;0;L;<compat> 0069 0076;;;4;N;;;2163;;2163 +2174;SMALL ROMAN NUMERAL FIVE;Nl;0;L;<compat> 0076;;;5;N;;;2164;;2164 +2175;SMALL ROMAN NUMERAL SIX;Nl;0;L;<compat> 0076 0069;;;6;N;;;2165;;2165 +2176;SMALL ROMAN NUMERAL SEVEN;Nl;0;L;<compat> 0076 0069 0069;;;7;N;;;2166;;2166 +2177;SMALL ROMAN NUMERAL EIGHT;Nl;0;L;<compat> 0076 0069 0069 0069;;;8;N;;;2167;;2167 +2178;SMALL ROMAN NUMERAL NINE;Nl;0;L;<compat> 0069 0078;;;9;N;;;2168;;2168 +2179;SMALL ROMAN NUMERAL TEN;Nl;0;L;<compat> 0078;;;10;N;;;2169;;2169 +217A;SMALL ROMAN NUMERAL ELEVEN;Nl;0;L;<compat> 0078 0069;;;11;N;;;216A;;216A +217B;SMALL ROMAN NUMERAL TWELVE;Nl;0;L;<compat> 0078 0069 0069;;;12;N;;;216B;;216B +217C;SMALL ROMAN NUMERAL FIFTY;Nl;0;L;<compat> 006C;;;50;N;;;216C;;216C +217D;SMALL ROMAN NUMERAL ONE HUNDRED;Nl;0;L;<compat> 0063;;;100;N;;;216D;;216D +217E;SMALL ROMAN NUMERAL FIVE HUNDRED;Nl;0;L;<compat> 0064;;;500;N;;;216E;;216E +217F;SMALL ROMAN NUMERAL ONE THOUSAND;Nl;0;L;<compat> 006D;;;1000;N;;;216F;;216F +2180;ROMAN NUMERAL ONE THOUSAND C D;Nl;0;L;;;;1000;N;;;;; +2181;ROMAN NUMERAL FIVE THOUSAND;Nl;0;L;;;;5000;N;;;;; +2182;ROMAN NUMERAL TEN THOUSAND;Nl;0;L;;;;10000;N;;;;; +2183;ROMAN NUMERAL REVERSED ONE HUNDRED;Lu;0;L;;;;;N;;;;2184; +2184;LATIN SMALL LETTER REVERSED C;Ll;0;L;;;;;N;;;2183;;2183 +2185;ROMAN NUMERAL SIX LATE FORM;Nl;0;L;;;;6;N;;;;; +2186;ROMAN NUMERAL FIFTY EARLY FORM;Nl;0;L;;;;50;N;;;;; +2187;ROMAN NUMERAL FIFTY THOUSAND;Nl;0;L;;;;50000;N;;;;; +2188;ROMAN NUMERAL ONE HUNDRED THOUSAND;Nl;0;L;;;;100000;N;;;;; +2189;VULGAR FRACTION ZERO THIRDS;No;0;ON;<fraction> 0030 2044 0033;;;0;N;;;;; +218A;TURNED DIGIT TWO;So;0;ON;;;;;N;;;;; +218B;TURNED DIGIT THREE;So;0;ON;;;;;N;;;;; +2190;LEFTWARDS ARROW;Sm;0;ON;;;;;N;LEFT ARROW;;;; +2191;UPWARDS ARROW;Sm;0;ON;;;;;N;UP ARROW;;;; +2192;RIGHTWARDS ARROW;Sm;0;ON;;;;;N;RIGHT ARROW;;;; +2193;DOWNWARDS ARROW;Sm;0;ON;;;;;N;DOWN ARROW;;;; +2194;LEFT RIGHT ARROW;Sm;0;ON;;;;;N;;;;; +2195;UP DOWN ARROW;So;0;ON;;;;;N;;;;; +2196;NORTH WEST ARROW;So;0;ON;;;;;N;UPPER LEFT ARROW;;;; +2197;NORTH EAST ARROW;So;0;ON;;;;;N;UPPER RIGHT ARROW;;;; +2198;SOUTH EAST ARROW;So;0;ON;;;;;N;LOWER RIGHT ARROW;;;; +2199;SOUTH WEST ARROW;So;0;ON;;;;;N;LOWER LEFT ARROW;;;; +219A;LEFTWARDS ARROW WITH STROKE;Sm;0;ON;2190 0338;;;;N;LEFT ARROW WITH STROKE;;;; +219B;RIGHTWARDS ARROW WITH STROKE;Sm;0;ON;2192 0338;;;;N;RIGHT ARROW WITH STROKE;;;; +219C;LEFTWARDS WAVE ARROW;So;0;ON;;;;;N;LEFT WAVE ARROW;;;; +219D;RIGHTWARDS WAVE ARROW;So;0;ON;;;;;N;RIGHT WAVE ARROW;;;; +219E;LEFTWARDS TWO HEADED ARROW;So;0;ON;;;;;N;LEFT TWO HEADED ARROW;;;; +219F;UPWARDS TWO HEADED ARROW;So;0;ON;;;;;N;UP TWO HEADED ARROW;;;; +21A0;RIGHTWARDS TWO HEADED ARROW;Sm;0;ON;;;;;N;RIGHT TWO HEADED ARROW;;;; +21A1;DOWNWARDS TWO HEADED ARROW;So;0;ON;;;;;N;DOWN TWO HEADED ARROW;;;; +21A2;LEFTWARDS ARROW WITH TAIL;So;0;ON;;;;;N;LEFT ARROW WITH TAIL;;;; +21A3;RIGHTWARDS ARROW WITH TAIL;Sm;0;ON;;;;;N;RIGHT ARROW WITH TAIL;;;; +21A4;LEFTWARDS ARROW FROM BAR;So;0;ON;;;;;N;LEFT ARROW FROM BAR;;;; +21A5;UPWARDS ARROW FROM BAR;So;0;ON;;;;;N;UP ARROW FROM BAR;;;; +21A6;RIGHTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;RIGHT ARROW FROM BAR;;;; +21A7;DOWNWARDS ARROW FROM BAR;So;0;ON;;;;;N;DOWN ARROW FROM BAR;;;; +21A8;UP DOWN ARROW WITH BASE;So;0;ON;;;;;N;;;;; +21A9;LEFTWARDS ARROW WITH HOOK;So;0;ON;;;;;N;LEFT ARROW WITH HOOK;;;; +21AA;RIGHTWARDS ARROW WITH HOOK;So;0;ON;;;;;N;RIGHT ARROW WITH HOOK;;;; +21AB;LEFTWARDS ARROW WITH LOOP;So;0;ON;;;;;N;LEFT ARROW WITH LOOP;;;; +21AC;RIGHTWARDS ARROW WITH LOOP;So;0;ON;;;;;N;RIGHT ARROW WITH LOOP;;;; +21AD;LEFT RIGHT WAVE ARROW;So;0;ON;;;;;N;;;;; +21AE;LEFT RIGHT ARROW WITH STROKE;Sm;0;ON;2194 0338;;;;N;;;;; +21AF;DOWNWARDS ZIGZAG ARROW;So;0;ON;;;;;N;DOWN ZIGZAG ARROW;;;; +21B0;UPWARDS ARROW WITH TIP LEFTWARDS;So;0;ON;;;;;N;UP ARROW WITH TIP LEFT;;;; +21B1;UPWARDS ARROW WITH TIP RIGHTWARDS;So;0;ON;;;;;N;UP ARROW WITH TIP RIGHT;;;; +21B2;DOWNWARDS ARROW WITH TIP LEFTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH TIP LEFT;;;; +21B3;DOWNWARDS ARROW WITH TIP RIGHTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH TIP RIGHT;;;; +21B4;RIGHTWARDS ARROW WITH CORNER DOWNWARDS;So;0;ON;;;;;N;RIGHT ARROW WITH CORNER DOWN;;;; +21B5;DOWNWARDS ARROW WITH CORNER LEFTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH CORNER LEFT;;;; +21B6;ANTICLOCKWISE TOP SEMICIRCLE ARROW;So;0;ON;;;;;N;;;;; +21B7;CLOCKWISE TOP SEMICIRCLE ARROW;So;0;ON;;;;;N;;;;; +21B8;NORTH WEST ARROW TO LONG BAR;So;0;ON;;;;;N;UPPER LEFT ARROW TO LONG BAR;;;; +21B9;LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR;So;0;ON;;;;;N;LEFT ARROW TO BAR OVER RIGHT ARROW TO BAR;;;; +21BA;ANTICLOCKWISE OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;; +21BB;CLOCKWISE OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;; +21BC;LEFTWARDS HARPOON WITH BARB UPWARDS;So;0;ON;;;;;N;LEFT HARPOON WITH BARB UP;;;; +21BD;LEFTWARDS HARPOON WITH BARB DOWNWARDS;So;0;ON;;;;;N;LEFT HARPOON WITH BARB DOWN;;;; +21BE;UPWARDS HARPOON WITH BARB RIGHTWARDS;So;0;ON;;;;;N;UP HARPOON WITH BARB RIGHT;;;; +21BF;UPWARDS HARPOON WITH BARB LEFTWARDS;So;0;ON;;;;;N;UP HARPOON WITH BARB LEFT;;;; +21C0;RIGHTWARDS HARPOON WITH BARB UPWARDS;So;0;ON;;;;;N;RIGHT HARPOON WITH BARB UP;;;; +21C1;RIGHTWARDS HARPOON WITH BARB DOWNWARDS;So;0;ON;;;;;N;RIGHT HARPOON WITH BARB DOWN;;;; +21C2;DOWNWARDS HARPOON WITH BARB RIGHTWARDS;So;0;ON;;;;;N;DOWN HARPOON WITH BARB RIGHT;;;; +21C3;DOWNWARDS HARPOON WITH BARB LEFTWARDS;So;0;ON;;;;;N;DOWN HARPOON WITH BARB LEFT;;;; +21C4;RIGHTWARDS ARROW OVER LEFTWARDS ARROW;So;0;ON;;;;;N;RIGHT ARROW OVER LEFT ARROW;;;; +21C5;UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW;So;0;ON;;;;;N;UP ARROW LEFT OF DOWN ARROW;;;; +21C6;LEFTWARDS ARROW OVER RIGHTWARDS ARROW;So;0;ON;;;;;N;LEFT ARROW OVER RIGHT ARROW;;;; +21C7;LEFTWARDS PAIRED ARROWS;So;0;ON;;;;;N;LEFT PAIRED ARROWS;;;; +21C8;UPWARDS PAIRED ARROWS;So;0;ON;;;;;N;UP PAIRED ARROWS;;;; +21C9;RIGHTWARDS PAIRED ARROWS;So;0;ON;;;;;N;RIGHT PAIRED ARROWS;;;; +21CA;DOWNWARDS PAIRED ARROWS;So;0;ON;;;;;N;DOWN PAIRED ARROWS;;;; +21CB;LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON;So;0;ON;;;;;N;LEFT HARPOON OVER RIGHT HARPOON;;;; +21CC;RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON;So;0;ON;;;;;N;RIGHT HARPOON OVER LEFT HARPOON;;;; +21CD;LEFTWARDS DOUBLE ARROW WITH STROKE;So;0;ON;21D0 0338;;;;N;LEFT DOUBLE ARROW WITH STROKE;;;; +21CE;LEFT RIGHT DOUBLE ARROW WITH STROKE;Sm;0;ON;21D4 0338;;;;N;;;;; +21CF;RIGHTWARDS DOUBLE ARROW WITH STROKE;Sm;0;ON;21D2 0338;;;;N;RIGHT DOUBLE ARROW WITH STROKE;;;; +21D0;LEFTWARDS DOUBLE ARROW;So;0;ON;;;;;N;LEFT DOUBLE ARROW;;;; +21D1;UPWARDS DOUBLE ARROW;So;0;ON;;;;;N;UP DOUBLE ARROW;;;; +21D2;RIGHTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;RIGHT DOUBLE ARROW;;;; +21D3;DOWNWARDS DOUBLE ARROW;So;0;ON;;;;;N;DOWN DOUBLE ARROW;;;; +21D4;LEFT RIGHT DOUBLE ARROW;Sm;0;ON;;;;;N;;;;; +21D5;UP DOWN DOUBLE ARROW;So;0;ON;;;;;N;;;;; +21D6;NORTH WEST DOUBLE ARROW;So;0;ON;;;;;N;UPPER LEFT DOUBLE ARROW;;;; +21D7;NORTH EAST DOUBLE ARROW;So;0;ON;;;;;N;UPPER RIGHT DOUBLE ARROW;;;; +21D8;SOUTH EAST DOUBLE ARROW;So;0;ON;;;;;N;LOWER RIGHT DOUBLE ARROW;;;; +21D9;SOUTH WEST DOUBLE ARROW;So;0;ON;;;;;N;LOWER LEFT DOUBLE ARROW;;;; +21DA;LEFTWARDS TRIPLE ARROW;So;0;ON;;;;;N;LEFT TRIPLE ARROW;;;; +21DB;RIGHTWARDS TRIPLE ARROW;So;0;ON;;;;;N;RIGHT TRIPLE ARROW;;;; +21DC;LEFTWARDS SQUIGGLE ARROW;So;0;ON;;;;;N;LEFT SQUIGGLE ARROW;;;; +21DD;RIGHTWARDS SQUIGGLE ARROW;So;0;ON;;;;;N;RIGHT SQUIGGLE ARROW;;;; +21DE;UPWARDS ARROW WITH DOUBLE STROKE;So;0;ON;;;;;N;UP ARROW WITH DOUBLE STROKE;;;; +21DF;DOWNWARDS ARROW WITH DOUBLE STROKE;So;0;ON;;;;;N;DOWN ARROW WITH DOUBLE STROKE;;;; +21E0;LEFTWARDS DASHED ARROW;So;0;ON;;;;;N;LEFT DASHED ARROW;;;; +21E1;UPWARDS DASHED ARROW;So;0;ON;;;;;N;UP DASHED ARROW;;;; +21E2;RIGHTWARDS DASHED ARROW;So;0;ON;;;;;N;RIGHT DASHED ARROW;;;; +21E3;DOWNWARDS DASHED ARROW;So;0;ON;;;;;N;DOWN DASHED ARROW;;;; +21E4;LEFTWARDS ARROW TO BAR;So;0;ON;;;;;N;LEFT ARROW TO BAR;;;; +21E5;RIGHTWARDS ARROW TO BAR;So;0;ON;;;;;N;RIGHT ARROW TO BAR;;;; +21E6;LEFTWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE LEFT ARROW;;;; +21E7;UPWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE UP ARROW;;;; +21E8;RIGHTWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE RIGHT ARROW;;;; +21E9;DOWNWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE DOWN ARROW;;;; +21EA;UPWARDS WHITE ARROW FROM BAR;So;0;ON;;;;;N;WHITE UP ARROW FROM BAR;;;; +21EB;UPWARDS WHITE ARROW ON PEDESTAL;So;0;ON;;;;;N;;;;; +21EC;UPWARDS WHITE ARROW ON PEDESTAL WITH HORIZONTAL BAR;So;0;ON;;;;;N;;;;; +21ED;UPWARDS WHITE ARROW ON PEDESTAL WITH VERTICAL BAR;So;0;ON;;;;;N;;;;; +21EE;UPWARDS WHITE DOUBLE ARROW;So;0;ON;;;;;N;;;;; +21EF;UPWARDS WHITE DOUBLE ARROW ON PEDESTAL;So;0;ON;;;;;N;;;;; +21F0;RIGHTWARDS WHITE ARROW FROM WALL;So;0;ON;;;;;N;;;;; +21F1;NORTH WEST ARROW TO CORNER;So;0;ON;;;;;N;;;;; +21F2;SOUTH EAST ARROW TO CORNER;So;0;ON;;;;;N;;;;; +21F3;UP DOWN WHITE ARROW;So;0;ON;;;;;N;;;;; +21F4;RIGHT ARROW WITH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;; +21F5;DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW;Sm;0;ON;;;;;N;;;;; +21F6;THREE RIGHTWARDS ARROWS;Sm;0;ON;;;;;N;;;;; +21F7;LEFTWARDS ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +21F8;RIGHTWARDS ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +21F9;LEFT RIGHT ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +21FA;LEFTWARDS ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +21FB;RIGHTWARDS ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +21FC;LEFT RIGHT ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +21FD;LEFTWARDS OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;; +21FE;RIGHTWARDS OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;; +21FF;LEFT RIGHT OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;; +2200;FOR ALL;Sm;0;ON;;;;;N;;;;; +2201;COMPLEMENT;Sm;0;ON;;;;;Y;;;;; +2202;PARTIAL DIFFERENTIAL;Sm;0;ON;;;;;Y;;;;; +2203;THERE EXISTS;Sm;0;ON;;;;;Y;;;;; +2204;THERE DOES NOT EXIST;Sm;0;ON;2203 0338;;;;Y;;;;; +2205;EMPTY SET;Sm;0;ON;;;;;N;;;;; +2206;INCREMENT;Sm;0;ON;;;;;N;;;;; +2207;NABLA;Sm;0;ON;;;;;N;;;;; +2208;ELEMENT OF;Sm;0;ON;;;;;Y;;;;; +2209;NOT AN ELEMENT OF;Sm;0;ON;2208 0338;;;;Y;;;;; +220A;SMALL ELEMENT OF;Sm;0;ON;;;;;Y;;;;; +220B;CONTAINS AS MEMBER;Sm;0;ON;;;;;Y;;;;; +220C;DOES NOT CONTAIN AS MEMBER;Sm;0;ON;220B 0338;;;;Y;;;;; +220D;SMALL CONTAINS AS MEMBER;Sm;0;ON;;;;;Y;;;;; +220E;END OF PROOF;Sm;0;ON;;;;;N;;;;; +220F;N-ARY PRODUCT;Sm;0;ON;;;;;N;;;;; +2210;N-ARY COPRODUCT;Sm;0;ON;;;;;N;;;;; +2211;N-ARY SUMMATION;Sm;0;ON;;;;;Y;;;;; +2212;MINUS SIGN;Sm;0;ES;;;;;N;;;;; +2213;MINUS-OR-PLUS SIGN;Sm;0;ET;;;;;N;;;;; +2214;DOT PLUS;Sm;0;ON;;;;;N;;;;; +2215;DIVISION SLASH;Sm;0;ON;;;;;Y;;;;; +2216;SET MINUS;Sm;0;ON;;;;;Y;;;;; +2217;ASTERISK OPERATOR;Sm;0;ON;;;;;N;;;;; +2218;RING OPERATOR;Sm;0;ON;;;;;N;;;;; +2219;BULLET OPERATOR;Sm;0;ON;;;;;N;;;;; +221A;SQUARE ROOT;Sm;0;ON;;;;;Y;;;;; +221B;CUBE ROOT;Sm;0;ON;;;;;Y;;;;; +221C;FOURTH ROOT;Sm;0;ON;;;;;Y;;;;; +221D;PROPORTIONAL TO;Sm;0;ON;;;;;Y;;;;; +221E;INFINITY;Sm;0;ON;;;;;N;;;;; +221F;RIGHT ANGLE;Sm;0;ON;;;;;Y;;;;; +2220;ANGLE;Sm;0;ON;;;;;Y;;;;; +2221;MEASURED ANGLE;Sm;0;ON;;;;;Y;;;;; +2222;SPHERICAL ANGLE;Sm;0;ON;;;;;Y;;;;; +2223;DIVIDES;Sm;0;ON;;;;;N;;;;; +2224;DOES NOT DIVIDE;Sm;0;ON;2223 0338;;;;Y;;;;; +2225;PARALLEL TO;Sm;0;ON;;;;;N;;;;; +2226;NOT PARALLEL TO;Sm;0;ON;2225 0338;;;;Y;;;;; +2227;LOGICAL AND;Sm;0;ON;;;;;N;;;;; +2228;LOGICAL OR;Sm;0;ON;;;;;N;;;;; +2229;INTERSECTION;Sm;0;ON;;;;;N;;;;; +222A;UNION;Sm;0;ON;;;;;N;;;;; +222B;INTEGRAL;Sm;0;ON;;;;;Y;;;;; +222C;DOUBLE INTEGRAL;Sm;0;ON;<compat> 222B 222B;;;;Y;;;;; +222D;TRIPLE INTEGRAL;Sm;0;ON;<compat> 222B 222B 222B;;;;Y;;;;; +222E;CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;; +222F;SURFACE INTEGRAL;Sm;0;ON;<compat> 222E 222E;;;;Y;;;;; +2230;VOLUME INTEGRAL;Sm;0;ON;<compat> 222E 222E 222E;;;;Y;;;;; +2231;CLOCKWISE INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2232;CLOCKWISE CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2233;ANTICLOCKWISE CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2234;THEREFORE;Sm;0;ON;;;;;N;;;;; +2235;BECAUSE;Sm;0;ON;;;;;N;;;;; +2236;RATIO;Sm;0;ON;;;;;N;;;;; +2237;PROPORTION;Sm;0;ON;;;;;N;;;;; +2238;DOT MINUS;Sm;0;ON;;;;;N;;;;; +2239;EXCESS;Sm;0;ON;;;;;Y;;;;; +223A;GEOMETRIC PROPORTION;Sm;0;ON;;;;;N;;;;; +223B;HOMOTHETIC;Sm;0;ON;;;;;Y;;;;; +223C;TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; +223D;REVERSED TILDE;Sm;0;ON;;;;;Y;;;;; +223E;INVERTED LAZY S;Sm;0;ON;;;;;Y;;;;; +223F;SINE WAVE;Sm;0;ON;;;;;Y;;;;; +2240;WREATH PRODUCT;Sm;0;ON;;;;;Y;;;;; +2241;NOT TILDE;Sm;0;ON;223C 0338;;;;Y;;;;; +2242;MINUS TILDE;Sm;0;ON;;;;;Y;;;;; +2243;ASYMPTOTICALLY EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2244;NOT ASYMPTOTICALLY EQUAL TO;Sm;0;ON;2243 0338;;;;Y;;;;; +2245;APPROXIMATELY EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2246;APPROXIMATELY BUT NOT ACTUALLY EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2247;NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO;Sm;0;ON;2245 0338;;;;Y;;;;; +2248;ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2249;NOT ALMOST EQUAL TO;Sm;0;ON;2248 0338;;;;Y;;;;; +224A;ALMOST EQUAL OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +224B;TRIPLE TILDE;Sm;0;ON;;;;;Y;;;;; +224C;ALL EQUAL TO;Sm;0;ON;;;;;Y;;;;; +224D;EQUIVALENT TO;Sm;0;ON;;;;;N;;;;; +224E;GEOMETRICALLY EQUIVALENT TO;Sm;0;ON;;;;;N;;;;; +224F;DIFFERENCE BETWEEN;Sm;0;ON;;;;;N;;;;; +2250;APPROACHES THE LIMIT;Sm;0;ON;;;;;N;;;;; +2251;GEOMETRICALLY EQUAL TO;Sm;0;ON;;;;;N;;;;; +2252;APPROXIMATELY EQUAL TO OR THE IMAGE OF;Sm;0;ON;;;;;Y;;;;; +2253;IMAGE OF OR APPROXIMATELY EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2254;COLON EQUALS;Sm;0;ON;;;;;Y;COLON EQUAL;;;; +2255;EQUALS COLON;Sm;0;ON;;;;;Y;EQUAL COLON;;;; +2256;RING IN EQUAL TO;Sm;0;ON;;;;;N;;;;; +2257;RING EQUAL TO;Sm;0;ON;;;;;N;;;;; +2258;CORRESPONDS TO;Sm;0;ON;;;;;N;;;;; +2259;ESTIMATES;Sm;0;ON;;;;;N;;;;; +225A;EQUIANGULAR TO;Sm;0;ON;;;;;N;;;;; +225B;STAR EQUALS;Sm;0;ON;;;;;N;;;;; +225C;DELTA EQUAL TO;Sm;0;ON;;;;;N;;;;; +225D;EQUAL TO BY DEFINITION;Sm;0;ON;;;;;N;;;;; +225E;MEASURED BY;Sm;0;ON;;;;;N;;;;; +225F;QUESTIONED EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2260;NOT EQUAL TO;Sm;0;ON;003D 0338;;;;Y;;;;; +2261;IDENTICAL TO;Sm;0;ON;;;;;N;;;;; +2262;NOT IDENTICAL TO;Sm;0;ON;2261 0338;;;;Y;;;;; +2263;STRICTLY EQUIVALENT TO;Sm;0;ON;;;;;N;;;;; +2264;LESS-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUAL TO;;;; +2265;GREATER-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUAL TO;;;; +2266;LESS-THAN OVER EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OVER EQUAL TO;;;; +2267;GREATER-THAN OVER EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OVER EQUAL TO;;;; +2268;LESS-THAN BUT NOT EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN BUT NOT EQUAL TO;;;; +2269;GREATER-THAN BUT NOT EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN BUT NOT EQUAL TO;;;; +226A;MUCH LESS-THAN;Sm;0;ON;;;;;Y;MUCH LESS THAN;;;; +226B;MUCH GREATER-THAN;Sm;0;ON;;;;;Y;MUCH GREATER THAN;;;; +226C;BETWEEN;Sm;0;ON;;;;;N;;;;; +226D;NOT EQUIVALENT TO;Sm;0;ON;224D 0338;;;;N;;;;; +226E;NOT LESS-THAN;Sm;0;ON;003C 0338;;;;Y;NOT LESS THAN;;;; +226F;NOT GREATER-THAN;Sm;0;ON;003E 0338;;;;Y;NOT GREATER THAN;;;; +2270;NEITHER LESS-THAN NOR EQUAL TO;Sm;0;ON;2264 0338;;;;Y;NEITHER LESS THAN NOR EQUAL TO;;;; +2271;NEITHER GREATER-THAN NOR EQUAL TO;Sm;0;ON;2265 0338;;;;Y;NEITHER GREATER THAN NOR EQUAL TO;;;; +2272;LESS-THAN OR EQUIVALENT TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUIVALENT TO;;;; +2273;GREATER-THAN OR EQUIVALENT TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUIVALENT TO;;;; +2274;NEITHER LESS-THAN NOR EQUIVALENT TO;Sm;0;ON;2272 0338;;;;Y;NEITHER LESS THAN NOR EQUIVALENT TO;;;; +2275;NEITHER GREATER-THAN NOR EQUIVALENT TO;Sm;0;ON;2273 0338;;;;Y;NEITHER GREATER THAN NOR EQUIVALENT TO;;;; +2276;LESS-THAN OR GREATER-THAN;Sm;0;ON;;;;;Y;LESS THAN OR GREATER THAN;;;; +2277;GREATER-THAN OR LESS-THAN;Sm;0;ON;;;;;Y;GREATER THAN OR LESS THAN;;;; +2278;NEITHER LESS-THAN NOR GREATER-THAN;Sm;0;ON;2276 0338;;;;Y;NEITHER LESS THAN NOR GREATER THAN;;;; +2279;NEITHER GREATER-THAN NOR LESS-THAN;Sm;0;ON;2277 0338;;;;Y;NEITHER GREATER THAN NOR LESS THAN;;;; +227A;PRECEDES;Sm;0;ON;;;;;Y;;;;; +227B;SUCCEEDS;Sm;0;ON;;;;;Y;;;;; +227C;PRECEDES OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +227D;SUCCEEDS OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +227E;PRECEDES OR EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;; +227F;SUCCEEDS OR EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;; +2280;DOES NOT PRECEDE;Sm;0;ON;227A 0338;;;;Y;;;;; +2281;DOES NOT SUCCEED;Sm;0;ON;227B 0338;;;;Y;;;;; +2282;SUBSET OF;Sm;0;ON;;;;;Y;;;;; +2283;SUPERSET OF;Sm;0;ON;;;;;Y;;;;; +2284;NOT A SUBSET OF;Sm;0;ON;2282 0338;;;;Y;;;;; +2285;NOT A SUPERSET OF;Sm;0;ON;2283 0338;;;;Y;;;;; +2286;SUBSET OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2287;SUPERSET OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2288;NEITHER A SUBSET OF NOR EQUAL TO;Sm;0;ON;2286 0338;;;;Y;;;;; +2289;NEITHER A SUPERSET OF NOR EQUAL TO;Sm;0;ON;2287 0338;;;;Y;;;;; +228A;SUBSET OF WITH NOT EQUAL TO;Sm;0;ON;;;;;Y;SUBSET OF OR NOT EQUAL TO;;;; +228B;SUPERSET OF WITH NOT EQUAL TO;Sm;0;ON;;;;;Y;SUPERSET OF OR NOT EQUAL TO;;;; +228C;MULTISET;Sm;0;ON;;;;;Y;;;;; +228D;MULTISET MULTIPLICATION;Sm;0;ON;;;;;N;;;;; +228E;MULTISET UNION;Sm;0;ON;;;;;N;;;;; +228F;SQUARE IMAGE OF;Sm;0;ON;;;;;Y;;;;; +2290;SQUARE ORIGINAL OF;Sm;0;ON;;;;;Y;;;;; +2291;SQUARE IMAGE OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2292;SQUARE ORIGINAL OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2293;SQUARE CAP;Sm;0;ON;;;;;N;;;;; +2294;SQUARE CUP;Sm;0;ON;;;;;N;;;;; +2295;CIRCLED PLUS;Sm;0;ON;;;;;N;;;;; +2296;CIRCLED MINUS;Sm;0;ON;;;;;N;;;;; +2297;CIRCLED TIMES;Sm;0;ON;;;;;N;;;;; +2298;CIRCLED DIVISION SLASH;Sm;0;ON;;;;;Y;;;;; +2299;CIRCLED DOT OPERATOR;Sm;0;ON;;;;;N;;;;; +229A;CIRCLED RING OPERATOR;Sm;0;ON;;;;;N;;;;; +229B;CIRCLED ASTERISK OPERATOR;Sm;0;ON;;;;;N;;;;; +229C;CIRCLED EQUALS;Sm;0;ON;;;;;N;;;;; +229D;CIRCLED DASH;Sm;0;ON;;;;;N;;;;; +229E;SQUARED PLUS;Sm;0;ON;;;;;N;;;;; +229F;SQUARED MINUS;Sm;0;ON;;;;;N;;;;; +22A0;SQUARED TIMES;Sm;0;ON;;;;;N;;;;; +22A1;SQUARED DOT OPERATOR;Sm;0;ON;;;;;N;;;;; +22A2;RIGHT TACK;Sm;0;ON;;;;;Y;;;;; +22A3;LEFT TACK;Sm;0;ON;;;;;Y;;;;; +22A4;DOWN TACK;Sm;0;ON;;;;;N;;;;; +22A5;UP TACK;Sm;0;ON;;;;;N;;;;; +22A6;ASSERTION;Sm;0;ON;;;;;Y;;;;; +22A7;MODELS;Sm;0;ON;;;;;Y;;;;; +22A8;TRUE;Sm;0;ON;;;;;Y;;;;; +22A9;FORCES;Sm;0;ON;;;;;Y;;;;; +22AA;TRIPLE VERTICAL BAR RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;; +22AB;DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;; +22AC;DOES NOT PROVE;Sm;0;ON;22A2 0338;;;;Y;;;;; +22AD;NOT TRUE;Sm;0;ON;22A8 0338;;;;Y;;;;; +22AE;DOES NOT FORCE;Sm;0;ON;22A9 0338;;;;Y;;;;; +22AF;NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE;Sm;0;ON;22AB 0338;;;;Y;;;;; +22B0;PRECEDES UNDER RELATION;Sm;0;ON;;;;;Y;;;;; +22B1;SUCCEEDS UNDER RELATION;Sm;0;ON;;;;;Y;;;;; +22B2;NORMAL SUBGROUP OF;Sm;0;ON;;;;;Y;;;;; +22B3;CONTAINS AS NORMAL SUBGROUP;Sm;0;ON;;;;;Y;;;;; +22B4;NORMAL SUBGROUP OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +22B5;CONTAINS AS NORMAL SUBGROUP OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +22B6;ORIGINAL OF;Sm;0;ON;;;;;Y;;;;; +22B7;IMAGE OF;Sm;0;ON;;;;;Y;;;;; +22B8;MULTIMAP;Sm;0;ON;;;;;Y;;;;; +22B9;HERMITIAN CONJUGATE MATRIX;Sm;0;ON;;;;;N;;;;; +22BA;INTERCALATE;Sm;0;ON;;;;;N;;;;; +22BB;XOR;Sm;0;ON;;;;;N;;;;; +22BC;NAND;Sm;0;ON;;;;;N;;;;; +22BD;NOR;Sm;0;ON;;;;;N;;;;; +22BE;RIGHT ANGLE WITH ARC;Sm;0;ON;;;;;Y;;;;; +22BF;RIGHT TRIANGLE;Sm;0;ON;;;;;Y;;;;; +22C0;N-ARY LOGICAL AND;Sm;0;ON;;;;;N;;;;; +22C1;N-ARY LOGICAL OR;Sm;0;ON;;;;;N;;;;; +22C2;N-ARY INTERSECTION;Sm;0;ON;;;;;N;;;;; +22C3;N-ARY UNION;Sm;0;ON;;;;;N;;;;; +22C4;DIAMOND OPERATOR;Sm;0;ON;;;;;N;;;;; +22C5;DOT OPERATOR;Sm;0;ON;;;;;N;;;;; +22C6;STAR OPERATOR;Sm;0;ON;;;;;N;;;;; +22C7;DIVISION TIMES;Sm;0;ON;;;;;N;;;;; +22C8;BOWTIE;Sm;0;ON;;;;;N;;;;; +22C9;LEFT NORMAL FACTOR SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;; +22CA;RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;; +22CB;LEFT SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;; +22CC;RIGHT SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;; +22CD;REVERSED TILDE EQUALS;Sm;0;ON;;;;;Y;;;;; +22CE;CURLY LOGICAL OR;Sm;0;ON;;;;;N;;;;; +22CF;CURLY LOGICAL AND;Sm;0;ON;;;;;N;;;;; +22D0;DOUBLE SUBSET;Sm;0;ON;;;;;Y;;;;; +22D1;DOUBLE SUPERSET;Sm;0;ON;;;;;Y;;;;; +22D2;DOUBLE INTERSECTION;Sm;0;ON;;;;;N;;;;; +22D3;DOUBLE UNION;Sm;0;ON;;;;;N;;;;; +22D4;PITCHFORK;Sm;0;ON;;;;;N;;;;; +22D5;EQUAL AND PARALLEL TO;Sm;0;ON;;;;;N;;;;; +22D6;LESS-THAN WITH DOT;Sm;0;ON;;;;;Y;LESS THAN WITH DOT;;;; +22D7;GREATER-THAN WITH DOT;Sm;0;ON;;;;;Y;GREATER THAN WITH DOT;;;; +22D8;VERY MUCH LESS-THAN;Sm;0;ON;;;;;Y;VERY MUCH LESS THAN;;;; +22D9;VERY MUCH GREATER-THAN;Sm;0;ON;;;;;Y;VERY MUCH GREATER THAN;;;; +22DA;LESS-THAN EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;LESS THAN EQUAL TO OR GREATER THAN;;;; +22DB;GREATER-THAN EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;GREATER THAN EQUAL TO OR LESS THAN;;;; +22DC;EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;EQUAL TO OR LESS THAN;;;; +22DD;EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;EQUAL TO OR GREATER THAN;;;; +22DE;EQUAL TO OR PRECEDES;Sm;0;ON;;;;;Y;;;;; +22DF;EQUAL TO OR SUCCEEDS;Sm;0;ON;;;;;Y;;;;; +22E0;DOES NOT PRECEDE OR EQUAL;Sm;0;ON;227C 0338;;;;Y;;;;; +22E1;DOES NOT SUCCEED OR EQUAL;Sm;0;ON;227D 0338;;;;Y;;;;; +22E2;NOT SQUARE IMAGE OF OR EQUAL TO;Sm;0;ON;2291 0338;;;;Y;;;;; +22E3;NOT SQUARE ORIGINAL OF OR EQUAL TO;Sm;0;ON;2292 0338;;;;Y;;;;; +22E4;SQUARE IMAGE OF OR NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +22E5;SQUARE ORIGINAL OF OR NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +22E6;LESS-THAN BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;LESS THAN BUT NOT EQUIVALENT TO;;;; +22E7;GREATER-THAN BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;GREATER THAN BUT NOT EQUIVALENT TO;;;; +22E8;PRECEDES BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;; +22E9;SUCCEEDS BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;; +22EA;NOT NORMAL SUBGROUP OF;Sm;0;ON;22B2 0338;;;;Y;;;;; +22EB;DOES NOT CONTAIN AS NORMAL SUBGROUP;Sm;0;ON;22B3 0338;;;;Y;;;;; +22EC;NOT NORMAL SUBGROUP OF OR EQUAL TO;Sm;0;ON;22B4 0338;;;;Y;;;;; +22ED;DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL;Sm;0;ON;22B5 0338;;;;Y;;;;; +22EE;VERTICAL ELLIPSIS;Sm;0;ON;;;;;N;;;;; +22EF;MIDLINE HORIZONTAL ELLIPSIS;Sm;0;ON;;;;;N;;;;; +22F0;UP RIGHT DIAGONAL ELLIPSIS;Sm;0;ON;;;;;Y;;;;; +22F1;DOWN RIGHT DIAGONAL ELLIPSIS;Sm;0;ON;;;;;Y;;;;; +22F2;ELEMENT OF WITH LONG HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; +22F3;ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; +22F4;SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; +22F5;ELEMENT OF WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; +22F6;ELEMENT OF WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; +22F7;SMALL ELEMENT OF WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; +22F8;ELEMENT OF WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; +22F9;ELEMENT OF WITH TWO HORIZONTAL STROKES;Sm;0;ON;;;;;Y;;;;; +22FA;CONTAINS WITH LONG HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; +22FB;CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; +22FC;SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; +22FD;CONTAINS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; +22FE;SMALL CONTAINS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; +22FF;Z NOTATION BAG MEMBERSHIP;Sm;0;ON;;;;;Y;;;;; +2300;DIAMETER SIGN;So;0;ON;;;;;N;;;;; +2301;ELECTRIC ARROW;So;0;ON;;;;;N;;;;; +2302;HOUSE;So;0;ON;;;;;N;;;;; +2303;UP ARROWHEAD;So;0;ON;;;;;N;;;;; +2304;DOWN ARROWHEAD;So;0;ON;;;;;N;;;;; +2305;PROJECTIVE;So;0;ON;;;;;N;;;;; +2306;PERSPECTIVE;So;0;ON;;;;;N;;;;; +2307;WAVY LINE;So;0;ON;;;;;N;;;;; +2308;LEFT CEILING;Ps;0;ON;;;;;Y;;;;; +2309;RIGHT CEILING;Pe;0;ON;;;;;Y;;;;; +230A;LEFT FLOOR;Ps;0;ON;;;;;Y;;;;; +230B;RIGHT FLOOR;Pe;0;ON;;;;;Y;;;;; +230C;BOTTOM RIGHT CROP;So;0;ON;;;;;N;;;;; +230D;BOTTOM LEFT CROP;So;0;ON;;;;;N;;;;; +230E;TOP RIGHT CROP;So;0;ON;;;;;N;;;;; +230F;TOP LEFT CROP;So;0;ON;;;;;N;;;;; +2310;REVERSED NOT SIGN;So;0;ON;;;;;N;;;;; +2311;SQUARE LOZENGE;So;0;ON;;;;;N;;;;; +2312;ARC;So;0;ON;;;;;N;;;;; +2313;SEGMENT;So;0;ON;;;;;N;;;;; +2314;SECTOR;So;0;ON;;;;;N;;;;; +2315;TELEPHONE RECORDER;So;0;ON;;;;;N;;;;; +2316;POSITION INDICATOR;So;0;ON;;;;;N;;;;; +2317;VIEWDATA SQUARE;So;0;ON;;;;;N;;;;; +2318;PLACE OF INTEREST SIGN;So;0;ON;;;;;N;COMMAND KEY;;;; +2319;TURNED NOT SIGN;So;0;ON;;;;;N;;;;; +231A;WATCH;So;0;ON;;;;;N;;;;; +231B;HOURGLASS;So;0;ON;;;;;N;;;;; +231C;TOP LEFT CORNER;So;0;ON;;;;;N;;;;; +231D;TOP RIGHT CORNER;So;0;ON;;;;;N;;;;; +231E;BOTTOM LEFT CORNER;So;0;ON;;;;;N;;;;; +231F;BOTTOM RIGHT CORNER;So;0;ON;;;;;N;;;;; +2320;TOP HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2321;BOTTOM HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2322;FROWN;So;0;ON;;;;;N;;;;; +2323;SMILE;So;0;ON;;;;;N;;;;; +2324;UP ARROWHEAD BETWEEN TWO HORIZONTAL BARS;So;0;ON;;;;;N;ENTER KEY;;;; +2325;OPTION KEY;So;0;ON;;;;;N;;;;; +2326;ERASE TO THE RIGHT;So;0;ON;;;;;N;DELETE TO THE RIGHT KEY;;;; +2327;X IN A RECTANGLE BOX;So;0;ON;;;;;N;CLEAR KEY;;;; +2328;KEYBOARD;So;0;ON;;;;;N;;;;; +2329;LEFT-POINTING ANGLE BRACKET;Ps;0;ON;3008;;;;Y;BRA;;;; +232A;RIGHT-POINTING ANGLE BRACKET;Pe;0;ON;3009;;;;Y;KET;;;; +232B;ERASE TO THE LEFT;So;0;ON;;;;;N;DELETE TO THE LEFT KEY;;;; +232C;BENZENE RING;So;0;ON;;;;;N;;;;; +232D;CYLINDRICITY;So;0;ON;;;;;N;;;;; +232E;ALL AROUND-PROFILE;So;0;ON;;;;;N;;;;; +232F;SYMMETRY;So;0;ON;;;;;N;;;;; +2330;TOTAL RUNOUT;So;0;ON;;;;;N;;;;; +2331;DIMENSION ORIGIN;So;0;ON;;;;;N;;;;; +2332;CONICAL TAPER;So;0;ON;;;;;N;;;;; +2333;SLOPE;So;0;ON;;;;;N;;;;; +2334;COUNTERBORE;So;0;ON;;;;;N;;;;; +2335;COUNTERSINK;So;0;ON;;;;;N;;;;; +2336;APL FUNCTIONAL SYMBOL I-BEAM;So;0;L;;;;;N;;;;; +2337;APL FUNCTIONAL SYMBOL SQUISH QUAD;So;0;L;;;;;N;;;;; +2338;APL FUNCTIONAL SYMBOL QUAD EQUAL;So;0;L;;;;;N;;;;; +2339;APL FUNCTIONAL SYMBOL QUAD DIVIDE;So;0;L;;;;;N;;;;; +233A;APL FUNCTIONAL SYMBOL QUAD DIAMOND;So;0;L;;;;;N;;;;; +233B;APL FUNCTIONAL SYMBOL QUAD JOT;So;0;L;;;;;N;;;;; +233C;APL FUNCTIONAL SYMBOL QUAD CIRCLE;So;0;L;;;;;N;;;;; +233D;APL FUNCTIONAL SYMBOL CIRCLE STILE;So;0;L;;;;;N;;;;; +233E;APL FUNCTIONAL SYMBOL CIRCLE JOT;So;0;L;;;;;N;;;;; +233F;APL FUNCTIONAL SYMBOL SLASH BAR;So;0;L;;;;;N;;;;; +2340;APL FUNCTIONAL SYMBOL BACKSLASH BAR;So;0;L;;;;;N;;;;; +2341;APL FUNCTIONAL SYMBOL QUAD SLASH;So;0;L;;;;;N;;;;; +2342;APL FUNCTIONAL SYMBOL QUAD BACKSLASH;So;0;L;;;;;N;;;;; +2343;APL FUNCTIONAL SYMBOL QUAD LESS-THAN;So;0;L;;;;;N;;;;; +2344;APL FUNCTIONAL SYMBOL QUAD GREATER-THAN;So;0;L;;;;;N;;;;; +2345;APL FUNCTIONAL SYMBOL LEFTWARDS VANE;So;0;L;;;;;N;;;;; +2346;APL FUNCTIONAL SYMBOL RIGHTWARDS VANE;So;0;L;;;;;N;;;;; +2347;APL FUNCTIONAL SYMBOL QUAD LEFTWARDS ARROW;So;0;L;;;;;N;;;;; +2348;APL FUNCTIONAL SYMBOL QUAD RIGHTWARDS ARROW;So;0;L;;;;;N;;;;; +2349;APL FUNCTIONAL SYMBOL CIRCLE BACKSLASH;So;0;L;;;;;N;;;;; +234A;APL FUNCTIONAL SYMBOL DOWN TACK UNDERBAR;So;0;L;;;;;N;;;;; +234B;APL FUNCTIONAL SYMBOL DELTA STILE;So;0;L;;;;;N;;;;; +234C;APL FUNCTIONAL SYMBOL QUAD DOWN CARET;So;0;L;;;;;N;;;;; +234D;APL FUNCTIONAL SYMBOL QUAD DELTA;So;0;L;;;;;N;;;;; +234E;APL FUNCTIONAL SYMBOL DOWN TACK JOT;So;0;L;;;;;N;;;;; +234F;APL FUNCTIONAL SYMBOL UPWARDS VANE;So;0;L;;;;;N;;;;; +2350;APL FUNCTIONAL SYMBOL QUAD UPWARDS ARROW;So;0;L;;;;;N;;;;; +2351;APL FUNCTIONAL SYMBOL UP TACK OVERBAR;So;0;L;;;;;N;;;;; +2352;APL FUNCTIONAL SYMBOL DEL STILE;So;0;L;;;;;N;;;;; +2353;APL FUNCTIONAL SYMBOL QUAD UP CARET;So;0;L;;;;;N;;;;; +2354;APL FUNCTIONAL SYMBOL QUAD DEL;So;0;L;;;;;N;;;;; +2355;APL FUNCTIONAL SYMBOL UP TACK JOT;So;0;L;;;;;N;;;;; +2356;APL FUNCTIONAL SYMBOL DOWNWARDS VANE;So;0;L;;;;;N;;;;; +2357;APL FUNCTIONAL SYMBOL QUAD DOWNWARDS ARROW;So;0;L;;;;;N;;;;; +2358;APL FUNCTIONAL SYMBOL QUOTE UNDERBAR;So;0;L;;;;;N;;;;; +2359;APL FUNCTIONAL SYMBOL DELTA UNDERBAR;So;0;L;;;;;N;;;;; +235A;APL FUNCTIONAL SYMBOL DIAMOND UNDERBAR;So;0;L;;;;;N;;;;; +235B;APL FUNCTIONAL SYMBOL JOT UNDERBAR;So;0;L;;;;;N;;;;; +235C;APL FUNCTIONAL SYMBOL CIRCLE UNDERBAR;So;0;L;;;;;N;;;;; +235D;APL FUNCTIONAL SYMBOL UP SHOE JOT;So;0;L;;;;;N;;;;; +235E;APL FUNCTIONAL SYMBOL QUOTE QUAD;So;0;L;;;;;N;;;;; +235F;APL FUNCTIONAL SYMBOL CIRCLE STAR;So;0;L;;;;;N;;;;; +2360;APL FUNCTIONAL SYMBOL QUAD COLON;So;0;L;;;;;N;;;;; +2361;APL FUNCTIONAL SYMBOL UP TACK DIAERESIS;So;0;L;;;;;N;;;;; +2362;APL FUNCTIONAL SYMBOL DEL DIAERESIS;So;0;L;;;;;N;;;;; +2363;APL FUNCTIONAL SYMBOL STAR DIAERESIS;So;0;L;;;;;N;;;;; +2364;APL FUNCTIONAL SYMBOL JOT DIAERESIS;So;0;L;;;;;N;;;;; +2365;APL FUNCTIONAL SYMBOL CIRCLE DIAERESIS;So;0;L;;;;;N;;;;; +2366;APL FUNCTIONAL SYMBOL DOWN SHOE STILE;So;0;L;;;;;N;;;;; +2367;APL FUNCTIONAL SYMBOL LEFT SHOE STILE;So;0;L;;;;;N;;;;; +2368;APL FUNCTIONAL SYMBOL TILDE DIAERESIS;So;0;L;;;;;N;;;;; +2369;APL FUNCTIONAL SYMBOL GREATER-THAN DIAERESIS;So;0;L;;;;;N;;;;; +236A;APL FUNCTIONAL SYMBOL COMMA BAR;So;0;L;;;;;N;;;;; +236B;APL FUNCTIONAL SYMBOL DEL TILDE;So;0;L;;;;;N;;;;; +236C;APL FUNCTIONAL SYMBOL ZILDE;So;0;L;;;;;N;;;;; +236D;APL FUNCTIONAL SYMBOL STILE TILDE;So;0;L;;;;;N;;;;; +236E;APL FUNCTIONAL SYMBOL SEMICOLON UNDERBAR;So;0;L;;;;;N;;;;; +236F;APL FUNCTIONAL SYMBOL QUAD NOT EQUAL;So;0;L;;;;;N;;;;; +2370;APL FUNCTIONAL SYMBOL QUAD QUESTION;So;0;L;;;;;N;;;;; +2371;APL FUNCTIONAL SYMBOL DOWN CARET TILDE;So;0;L;;;;;N;;;;; +2372;APL FUNCTIONAL SYMBOL UP CARET TILDE;So;0;L;;;;;N;;;;; +2373;APL FUNCTIONAL SYMBOL IOTA;So;0;L;;;;;N;;;;; +2374;APL FUNCTIONAL SYMBOL RHO;So;0;L;;;;;N;;;;; +2375;APL FUNCTIONAL SYMBOL OMEGA;So;0;L;;;;;N;;;;; +2376;APL FUNCTIONAL SYMBOL ALPHA UNDERBAR;So;0;L;;;;;N;;;;; +2377;APL FUNCTIONAL SYMBOL EPSILON UNDERBAR;So;0;L;;;;;N;;;;; +2378;APL FUNCTIONAL SYMBOL IOTA UNDERBAR;So;0;L;;;;;N;;;;; +2379;APL FUNCTIONAL SYMBOL OMEGA UNDERBAR;So;0;L;;;;;N;;;;; +237A;APL FUNCTIONAL SYMBOL ALPHA;So;0;L;;;;;N;;;;; +237B;NOT CHECK MARK;So;0;ON;;;;;N;;;;; +237C;RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW;Sm;0;ON;;;;;N;;;;; +237D;SHOULDERED OPEN BOX;So;0;ON;;;;;N;;;;; +237E;BELL SYMBOL;So;0;ON;;;;;N;;;;; +237F;VERTICAL LINE WITH MIDDLE DOT;So;0;ON;;;;;N;;;;; +2380;INSERTION SYMBOL;So;0;ON;;;;;N;;;;; +2381;CONTINUOUS UNDERLINE SYMBOL;So;0;ON;;;;;N;;;;; +2382;DISCONTINUOUS UNDERLINE SYMBOL;So;0;ON;;;;;N;;;;; +2383;EMPHASIS SYMBOL;So;0;ON;;;;;N;;;;; +2384;COMPOSITION SYMBOL;So;0;ON;;;;;N;;;;; +2385;WHITE SQUARE WITH CENTRE VERTICAL LINE;So;0;ON;;;;;N;;;;; +2386;ENTER SYMBOL;So;0;ON;;;;;N;;;;; +2387;ALTERNATIVE KEY SYMBOL;So;0;ON;;;;;N;;;;; +2388;HELM SYMBOL;So;0;ON;;;;;N;;;;; +2389;CIRCLED HORIZONTAL BAR WITH NOTCH;So;0;ON;;;;;N;;;;; +238A;CIRCLED TRIANGLE DOWN;So;0;ON;;;;;N;;;;; +238B;BROKEN CIRCLE WITH NORTHWEST ARROW;So;0;ON;;;;;N;;;;; +238C;UNDO SYMBOL;So;0;ON;;;;;N;;;;; +238D;MONOSTABLE SYMBOL;So;0;ON;;;;;N;;;;; +238E;HYSTERESIS SYMBOL;So;0;ON;;;;;N;;;;; +238F;OPEN-CIRCUIT-OUTPUT H-TYPE SYMBOL;So;0;ON;;;;;N;;;;; +2390;OPEN-CIRCUIT-OUTPUT L-TYPE SYMBOL;So;0;ON;;;;;N;;;;; +2391;PASSIVE-PULL-DOWN-OUTPUT SYMBOL;So;0;ON;;;;;N;;;;; +2392;PASSIVE-PULL-UP-OUTPUT SYMBOL;So;0;ON;;;;;N;;;;; +2393;DIRECT CURRENT SYMBOL FORM TWO;So;0;ON;;;;;N;;;;; +2394;SOFTWARE-FUNCTION SYMBOL;So;0;ON;;;;;N;;;;; +2395;APL FUNCTIONAL SYMBOL QUAD;So;0;L;;;;;N;;;;; +2396;DECIMAL SEPARATOR KEY SYMBOL;So;0;ON;;;;;N;;;;; +2397;PREVIOUS PAGE;So;0;ON;;;;;N;;;;; +2398;NEXT PAGE;So;0;ON;;;;;N;;;;; +2399;PRINT SCREEN SYMBOL;So;0;ON;;;;;N;;;;; +239A;CLEAR SCREEN SYMBOL;So;0;ON;;;;;N;;;;; +239B;LEFT PARENTHESIS UPPER HOOK;Sm;0;ON;;;;;N;;;;; +239C;LEFT PARENTHESIS EXTENSION;Sm;0;ON;;;;;N;;;;; +239D;LEFT PARENTHESIS LOWER HOOK;Sm;0;ON;;;;;N;;;;; +239E;RIGHT PARENTHESIS UPPER HOOK;Sm;0;ON;;;;;N;;;;; +239F;RIGHT PARENTHESIS EXTENSION;Sm;0;ON;;;;;N;;;;; +23A0;RIGHT PARENTHESIS LOWER HOOK;Sm;0;ON;;;;;N;;;;; +23A1;LEFT SQUARE BRACKET UPPER CORNER;Sm;0;ON;;;;;N;;;;; +23A2;LEFT SQUARE BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;; +23A3;LEFT SQUARE BRACKET LOWER CORNER;Sm;0;ON;;;;;N;;;;; +23A4;RIGHT SQUARE BRACKET UPPER CORNER;Sm;0;ON;;;;;N;;;;; +23A5;RIGHT SQUARE BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;; +23A6;RIGHT SQUARE BRACKET LOWER CORNER;Sm;0;ON;;;;;N;;;;; +23A7;LEFT CURLY BRACKET UPPER HOOK;Sm;0;ON;;;;;N;;;;; +23A8;LEFT CURLY BRACKET MIDDLE PIECE;Sm;0;ON;;;;;N;;;;; +23A9;LEFT CURLY BRACKET LOWER HOOK;Sm;0;ON;;;;;N;;;;; +23AA;CURLY BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;; +23AB;RIGHT CURLY BRACKET UPPER HOOK;Sm;0;ON;;;;;N;;;;; +23AC;RIGHT CURLY BRACKET MIDDLE PIECE;Sm;0;ON;;;;;N;;;;; +23AD;RIGHT CURLY BRACKET LOWER HOOK;Sm;0;ON;;;;;N;;;;; +23AE;INTEGRAL EXTENSION;Sm;0;ON;;;;;N;;;;; +23AF;HORIZONTAL LINE EXTENSION;Sm;0;ON;;;;;N;;;;; +23B0;UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION;Sm;0;ON;;;;;N;;;;; +23B1;UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION;Sm;0;ON;;;;;N;;;;; +23B2;SUMMATION TOP;Sm;0;ON;;;;;N;;;;; +23B3;SUMMATION BOTTOM;Sm;0;ON;;;;;N;;;;; +23B4;TOP SQUARE BRACKET;So;0;ON;;;;;N;;;;; +23B5;BOTTOM SQUARE BRACKET;So;0;ON;;;;;N;;;;; +23B6;BOTTOM SQUARE BRACKET OVER TOP SQUARE BRACKET;So;0;ON;;;;;N;;;;; +23B7;RADICAL SYMBOL BOTTOM;So;0;ON;;;;;N;;;;; +23B8;LEFT VERTICAL BOX LINE;So;0;ON;;;;;N;;;;; +23B9;RIGHT VERTICAL BOX LINE;So;0;ON;;;;;N;;;;; +23BA;HORIZONTAL SCAN LINE-1;So;0;ON;;;;;N;;;;; +23BB;HORIZONTAL SCAN LINE-3;So;0;ON;;;;;N;;;;; +23BC;HORIZONTAL SCAN LINE-7;So;0;ON;;;;;N;;;;; +23BD;HORIZONTAL SCAN LINE-9;So;0;ON;;;;;N;;;;; +23BE;DENTISTRY SYMBOL LIGHT VERTICAL AND TOP RIGHT;So;0;ON;;;;;N;;;;; +23BF;DENTISTRY SYMBOL LIGHT VERTICAL AND BOTTOM RIGHT;So;0;ON;;;;;N;;;;; +23C0;DENTISTRY SYMBOL LIGHT VERTICAL WITH CIRCLE;So;0;ON;;;;;N;;;;; +23C1;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH CIRCLE;So;0;ON;;;;;N;;;;; +23C2;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH CIRCLE;So;0;ON;;;;;N;;;;; +23C3;DENTISTRY SYMBOL LIGHT VERTICAL WITH TRIANGLE;So;0;ON;;;;;N;;;;; +23C4;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH TRIANGLE;So;0;ON;;;;;N;;;;; +23C5;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH TRIANGLE;So;0;ON;;;;;N;;;;; +23C6;DENTISTRY SYMBOL LIGHT VERTICAL AND WAVE;So;0;ON;;;;;N;;;;; +23C7;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH WAVE;So;0;ON;;;;;N;;;;; +23C8;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH WAVE;So;0;ON;;;;;N;;;;; +23C9;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL;So;0;ON;;;;;N;;;;; +23CA;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL;So;0;ON;;;;;N;;;;; +23CB;DENTISTRY SYMBOL LIGHT VERTICAL AND TOP LEFT;So;0;ON;;;;;N;;;;; +23CC;DENTISTRY SYMBOL LIGHT VERTICAL AND BOTTOM LEFT;So;0;ON;;;;;N;;;;; +23CD;SQUARE FOOT;So;0;ON;;;;;N;;;;; +23CE;RETURN SYMBOL;So;0;ON;;;;;N;;;;; +23CF;EJECT SYMBOL;So;0;ON;;;;;N;;;;; +23D0;VERTICAL LINE EXTENSION;So;0;ON;;;;;N;;;;; +23D1;METRICAL BREVE;So;0;ON;;;;;N;;;;; +23D2;METRICAL LONG OVER SHORT;So;0;ON;;;;;N;;;;; +23D3;METRICAL SHORT OVER LONG;So;0;ON;;;;;N;;;;; +23D4;METRICAL LONG OVER TWO SHORTS;So;0;ON;;;;;N;;;;; +23D5;METRICAL TWO SHORTS OVER LONG;So;0;ON;;;;;N;;;;; +23D6;METRICAL TWO SHORTS JOINED;So;0;ON;;;;;N;;;;; +23D7;METRICAL TRISEME;So;0;ON;;;;;N;;;;; +23D8;METRICAL TETRASEME;So;0;ON;;;;;N;;;;; +23D9;METRICAL PENTASEME;So;0;ON;;;;;N;;;;; +23DA;EARTH GROUND;So;0;ON;;;;;N;;;;; +23DB;FUSE;So;0;ON;;;;;N;;;;; +23DC;TOP PARENTHESIS;Sm;0;ON;;;;;N;;;;; +23DD;BOTTOM PARENTHESIS;Sm;0;ON;;;;;N;;;;; +23DE;TOP CURLY BRACKET;Sm;0;ON;;;;;N;;;;; +23DF;BOTTOM CURLY BRACKET;Sm;0;ON;;;;;N;;;;; +23E0;TOP TORTOISE SHELL BRACKET;Sm;0;ON;;;;;N;;;;; +23E1;BOTTOM TORTOISE SHELL BRACKET;Sm;0;ON;;;;;N;;;;; +23E2;WHITE TRAPEZIUM;So;0;ON;;;;;N;;;;; +23E3;BENZENE RING WITH CIRCLE;So;0;ON;;;;;N;;;;; +23E4;STRAIGHTNESS;So;0;ON;;;;;N;;;;; +23E5;FLATNESS;So;0;ON;;;;;N;;;;; +23E6;AC CURRENT;So;0;ON;;;;;N;;;;; +23E7;ELECTRICAL INTERSECTION;So;0;ON;;;;;N;;;;; +23E8;DECIMAL EXPONENT SYMBOL;So;0;ON;;;;;N;;;;; +23E9;BLACK RIGHT-POINTING DOUBLE TRIANGLE;So;0;ON;;;;;N;;;;; +23EA;BLACK LEFT-POINTING DOUBLE TRIANGLE;So;0;ON;;;;;N;;;;; +23EB;BLACK UP-POINTING DOUBLE TRIANGLE;So;0;ON;;;;;N;;;;; +23EC;BLACK DOWN-POINTING DOUBLE TRIANGLE;So;0;ON;;;;;N;;;;; +23ED;BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR;So;0;ON;;;;;N;;;;; +23EE;BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR;So;0;ON;;;;;N;;;;; +23EF;BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR;So;0;ON;;;;;N;;;;; +23F0;ALARM CLOCK;So;0;ON;;;;;N;;;;; +23F1;STOPWATCH;So;0;ON;;;;;N;;;;; +23F2;TIMER CLOCK;So;0;ON;;;;;N;;;;; +23F3;HOURGLASS WITH FLOWING SAND;So;0;ON;;;;;N;;;;; +23F4;BLACK MEDIUM LEFT-POINTING TRIANGLE;So;0;ON;;;;;N;;;;; +23F5;BLACK MEDIUM RIGHT-POINTING TRIANGLE;So;0;ON;;;;;N;;;;; +23F6;BLACK MEDIUM UP-POINTING TRIANGLE;So;0;ON;;;;;N;;;;; +23F7;BLACK MEDIUM DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;;;;; +23F8;DOUBLE VERTICAL BAR;So;0;ON;;;;;N;;;;; +23F9;BLACK SQUARE FOR STOP;So;0;ON;;;;;N;;;;; +23FA;BLACK CIRCLE FOR RECORD;So;0;ON;;;;;N;;;;; +23FB;POWER SYMBOL;So;0;ON;;;;;N;;;;; +23FC;POWER ON-OFF SYMBOL;So;0;ON;;;;;N;;;;; +23FD;POWER ON SYMBOL;So;0;ON;;;;;N;;;;; +23FE;POWER SLEEP SYMBOL;So;0;ON;;;;;N;;;;; +2400;SYMBOL FOR NULL;So;0;ON;;;;;N;GRAPHIC FOR NULL;;;; +2401;SYMBOL FOR START OF HEADING;So;0;ON;;;;;N;GRAPHIC FOR START OF HEADING;;;; +2402;SYMBOL FOR START OF TEXT;So;0;ON;;;;;N;GRAPHIC FOR START OF TEXT;;;; +2403;SYMBOL FOR END OF TEXT;So;0;ON;;;;;N;GRAPHIC FOR END OF TEXT;;;; +2404;SYMBOL FOR END OF TRANSMISSION;So;0;ON;;;;;N;GRAPHIC FOR END OF TRANSMISSION;;;; +2405;SYMBOL FOR ENQUIRY;So;0;ON;;;;;N;GRAPHIC FOR ENQUIRY;;;; +2406;SYMBOL FOR ACKNOWLEDGE;So;0;ON;;;;;N;GRAPHIC FOR ACKNOWLEDGE;;;; +2407;SYMBOL FOR BELL;So;0;ON;;;;;N;GRAPHIC FOR BELL;;;; +2408;SYMBOL FOR BACKSPACE;So;0;ON;;;;;N;GRAPHIC FOR BACKSPACE;;;; +2409;SYMBOL FOR HORIZONTAL TABULATION;So;0;ON;;;;;N;GRAPHIC FOR HORIZONTAL TABULATION;;;; +240A;SYMBOL FOR LINE FEED;So;0;ON;;;;;N;GRAPHIC FOR LINE FEED;;;; +240B;SYMBOL FOR VERTICAL TABULATION;So;0;ON;;;;;N;GRAPHIC FOR VERTICAL TABULATION;;;; +240C;SYMBOL FOR FORM FEED;So;0;ON;;;;;N;GRAPHIC FOR FORM FEED;;;; +240D;SYMBOL FOR CARRIAGE RETURN;So;0;ON;;;;;N;GRAPHIC FOR CARRIAGE RETURN;;;; +240E;SYMBOL FOR SHIFT OUT;So;0;ON;;;;;N;GRAPHIC FOR SHIFT OUT;;;; +240F;SYMBOL FOR SHIFT IN;So;0;ON;;;;;N;GRAPHIC FOR SHIFT IN;;;; +2410;SYMBOL FOR DATA LINK ESCAPE;So;0;ON;;;;;N;GRAPHIC FOR DATA LINK ESCAPE;;;; +2411;SYMBOL FOR DEVICE CONTROL ONE;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL ONE;;;; +2412;SYMBOL FOR DEVICE CONTROL TWO;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL TWO;;;; +2413;SYMBOL FOR DEVICE CONTROL THREE;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL THREE;;;; +2414;SYMBOL FOR DEVICE CONTROL FOUR;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL FOUR;;;; +2415;SYMBOL FOR NEGATIVE ACKNOWLEDGE;So;0;ON;;;;;N;GRAPHIC FOR NEGATIVE ACKNOWLEDGE;;;; +2416;SYMBOL FOR SYNCHRONOUS IDLE;So;0;ON;;;;;N;GRAPHIC FOR SYNCHRONOUS IDLE;;;; +2417;SYMBOL FOR END OF TRANSMISSION BLOCK;So;0;ON;;;;;N;GRAPHIC FOR END OF TRANSMISSION BLOCK;;;; +2418;SYMBOL FOR CANCEL;So;0;ON;;;;;N;GRAPHIC FOR CANCEL;;;; +2419;SYMBOL FOR END OF MEDIUM;So;0;ON;;;;;N;GRAPHIC FOR END OF MEDIUM;;;; +241A;SYMBOL FOR SUBSTITUTE;So;0;ON;;;;;N;GRAPHIC FOR SUBSTITUTE;;;; +241B;SYMBOL FOR ESCAPE;So;0;ON;;;;;N;GRAPHIC FOR ESCAPE;;;; +241C;SYMBOL FOR FILE SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR FILE SEPARATOR;;;; +241D;SYMBOL FOR GROUP SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR GROUP SEPARATOR;;;; +241E;SYMBOL FOR RECORD SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR RECORD SEPARATOR;;;; +241F;SYMBOL FOR UNIT SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR UNIT SEPARATOR;;;; +2420;SYMBOL FOR SPACE;So;0;ON;;;;;N;GRAPHIC FOR SPACE;;;; +2421;SYMBOL FOR DELETE;So;0;ON;;;;;N;GRAPHIC FOR DELETE;;;; +2422;BLANK SYMBOL;So;0;ON;;;;;N;BLANK;;;; +2423;OPEN BOX;So;0;ON;;;;;N;;;;; +2424;SYMBOL FOR NEWLINE;So;0;ON;;;;;N;GRAPHIC FOR NEWLINE;;;; +2425;SYMBOL FOR DELETE FORM TWO;So;0;ON;;;;;N;;;;; +2426;SYMBOL FOR SUBSTITUTE FORM TWO;So;0;ON;;;;;N;;;;; +2440;OCR HOOK;So;0;ON;;;;;N;;;;; +2441;OCR CHAIR;So;0;ON;;;;;N;;;;; +2442;OCR FORK;So;0;ON;;;;;N;;;;; +2443;OCR INVERTED FORK;So;0;ON;;;;;N;;;;; +2444;OCR BELT BUCKLE;So;0;ON;;;;;N;;;;; +2445;OCR BOW TIE;So;0;ON;;;;;N;;;;; +2446;OCR BRANCH BANK IDENTIFICATION;So;0;ON;;;;;N;;;;; +2447;OCR AMOUNT OF CHECK;So;0;ON;;;;;N;;;;; +2448;OCR DASH;So;0;ON;;;;;N;;;;; +2449;OCR CUSTOMER ACCOUNT NUMBER;So;0;ON;;;;;N;;;;; +244A;OCR DOUBLE BACKSLASH;So;0;ON;;;;;N;;;;; +2460;CIRCLED DIGIT ONE;No;0;ON;<circle> 0031;;1;1;N;;;;; +2461;CIRCLED DIGIT TWO;No;0;ON;<circle> 0032;;2;2;N;;;;; +2462;CIRCLED DIGIT THREE;No;0;ON;<circle> 0033;;3;3;N;;;;; +2463;CIRCLED DIGIT FOUR;No;0;ON;<circle> 0034;;4;4;N;;;;; +2464;CIRCLED DIGIT FIVE;No;0;ON;<circle> 0035;;5;5;N;;;;; +2465;CIRCLED DIGIT SIX;No;0;ON;<circle> 0036;;6;6;N;;;;; +2466;CIRCLED DIGIT SEVEN;No;0;ON;<circle> 0037;;7;7;N;;;;; +2467;CIRCLED DIGIT EIGHT;No;0;ON;<circle> 0038;;8;8;N;;;;; +2468;CIRCLED DIGIT NINE;No;0;ON;<circle> 0039;;9;9;N;;;;; +2469;CIRCLED NUMBER TEN;No;0;ON;<circle> 0031 0030;;;10;N;;;;; +246A;CIRCLED NUMBER ELEVEN;No;0;ON;<circle> 0031 0031;;;11;N;;;;; +246B;CIRCLED NUMBER TWELVE;No;0;ON;<circle> 0031 0032;;;12;N;;;;; +246C;CIRCLED NUMBER THIRTEEN;No;0;ON;<circle> 0031 0033;;;13;N;;;;; +246D;CIRCLED NUMBER FOURTEEN;No;0;ON;<circle> 0031 0034;;;14;N;;;;; +246E;CIRCLED NUMBER FIFTEEN;No;0;ON;<circle> 0031 0035;;;15;N;;;;; +246F;CIRCLED NUMBER SIXTEEN;No;0;ON;<circle> 0031 0036;;;16;N;;;;; +2470;CIRCLED NUMBER SEVENTEEN;No;0;ON;<circle> 0031 0037;;;17;N;;;;; +2471;CIRCLED NUMBER EIGHTEEN;No;0;ON;<circle> 0031 0038;;;18;N;;;;; +2472;CIRCLED NUMBER NINETEEN;No;0;ON;<circle> 0031 0039;;;19;N;;;;; +2473;CIRCLED NUMBER TWENTY;No;0;ON;<circle> 0032 0030;;;20;N;;;;; +2474;PARENTHESIZED DIGIT ONE;No;0;ON;<compat> 0028 0031 0029;;1;1;N;;;;; +2475;PARENTHESIZED DIGIT TWO;No;0;ON;<compat> 0028 0032 0029;;2;2;N;;;;; +2476;PARENTHESIZED DIGIT THREE;No;0;ON;<compat> 0028 0033 0029;;3;3;N;;;;; +2477;PARENTHESIZED DIGIT FOUR;No;0;ON;<compat> 0028 0034 0029;;4;4;N;;;;; +2478;PARENTHESIZED DIGIT FIVE;No;0;ON;<compat> 0028 0035 0029;;5;5;N;;;;; +2479;PARENTHESIZED DIGIT SIX;No;0;ON;<compat> 0028 0036 0029;;6;6;N;;;;; +247A;PARENTHESIZED DIGIT SEVEN;No;0;ON;<compat> 0028 0037 0029;;7;7;N;;;;; +247B;PARENTHESIZED DIGIT EIGHT;No;0;ON;<compat> 0028 0038 0029;;8;8;N;;;;; +247C;PARENTHESIZED DIGIT NINE;No;0;ON;<compat> 0028 0039 0029;;9;9;N;;;;; +247D;PARENTHESIZED NUMBER TEN;No;0;ON;<compat> 0028 0031 0030 0029;;;10;N;;;;; +247E;PARENTHESIZED NUMBER ELEVEN;No;0;ON;<compat> 0028 0031 0031 0029;;;11;N;;;;; +247F;PARENTHESIZED NUMBER TWELVE;No;0;ON;<compat> 0028 0031 0032 0029;;;12;N;;;;; +2480;PARENTHESIZED NUMBER THIRTEEN;No;0;ON;<compat> 0028 0031 0033 0029;;;13;N;;;;; +2481;PARENTHESIZED NUMBER FOURTEEN;No;0;ON;<compat> 0028 0031 0034 0029;;;14;N;;;;; +2482;PARENTHESIZED NUMBER FIFTEEN;No;0;ON;<compat> 0028 0031 0035 0029;;;15;N;;;;; +2483;PARENTHESIZED NUMBER SIXTEEN;No;0;ON;<compat> 0028 0031 0036 0029;;;16;N;;;;; +2484;PARENTHESIZED NUMBER SEVENTEEN;No;0;ON;<compat> 0028 0031 0037 0029;;;17;N;;;;; +2485;PARENTHESIZED NUMBER EIGHTEEN;No;0;ON;<compat> 0028 0031 0038 0029;;;18;N;;;;; +2486;PARENTHESIZED NUMBER NINETEEN;No;0;ON;<compat> 0028 0031 0039 0029;;;19;N;;;;; +2487;PARENTHESIZED NUMBER TWENTY;No;0;ON;<compat> 0028 0032 0030 0029;;;20;N;;;;; +2488;DIGIT ONE FULL STOP;No;0;EN;<compat> 0031 002E;;1;1;N;DIGIT ONE PERIOD;;;; +2489;DIGIT TWO FULL STOP;No;0;EN;<compat> 0032 002E;;2;2;N;DIGIT TWO PERIOD;;;; +248A;DIGIT THREE FULL STOP;No;0;EN;<compat> 0033 002E;;3;3;N;DIGIT THREE PERIOD;;;; +248B;DIGIT FOUR FULL STOP;No;0;EN;<compat> 0034 002E;;4;4;N;DIGIT FOUR PERIOD;;;; +248C;DIGIT FIVE FULL STOP;No;0;EN;<compat> 0035 002E;;5;5;N;DIGIT FIVE PERIOD;;;; +248D;DIGIT SIX FULL STOP;No;0;EN;<compat> 0036 002E;;6;6;N;DIGIT SIX PERIOD;;;; +248E;DIGIT SEVEN FULL STOP;No;0;EN;<compat> 0037 002E;;7;7;N;DIGIT SEVEN PERIOD;;;; +248F;DIGIT EIGHT FULL STOP;No;0;EN;<compat> 0038 002E;;8;8;N;DIGIT EIGHT PERIOD;;;; +2490;DIGIT NINE FULL STOP;No;0;EN;<compat> 0039 002E;;9;9;N;DIGIT NINE PERIOD;;;; +2491;NUMBER TEN FULL STOP;No;0;EN;<compat> 0031 0030 002E;;;10;N;NUMBER TEN PERIOD;;;; +2492;NUMBER ELEVEN FULL STOP;No;0;EN;<compat> 0031 0031 002E;;;11;N;NUMBER ELEVEN PERIOD;;;; +2493;NUMBER TWELVE FULL STOP;No;0;EN;<compat> 0031 0032 002E;;;12;N;NUMBER TWELVE PERIOD;;;; +2494;NUMBER THIRTEEN FULL STOP;No;0;EN;<compat> 0031 0033 002E;;;13;N;NUMBER THIRTEEN PERIOD;;;; +2495;NUMBER FOURTEEN FULL STOP;No;0;EN;<compat> 0031 0034 002E;;;14;N;NUMBER FOURTEEN PERIOD;;;; +2496;NUMBER FIFTEEN FULL STOP;No;0;EN;<compat> 0031 0035 002E;;;15;N;NUMBER FIFTEEN PERIOD;;;; +2497;NUMBER SIXTEEN FULL STOP;No;0;EN;<compat> 0031 0036 002E;;;16;N;NUMBER SIXTEEN PERIOD;;;; +2498;NUMBER SEVENTEEN FULL STOP;No;0;EN;<compat> 0031 0037 002E;;;17;N;NUMBER SEVENTEEN PERIOD;;;; +2499;NUMBER EIGHTEEN FULL STOP;No;0;EN;<compat> 0031 0038 002E;;;18;N;NUMBER EIGHTEEN PERIOD;;;; +249A;NUMBER NINETEEN FULL STOP;No;0;EN;<compat> 0031 0039 002E;;;19;N;NUMBER NINETEEN PERIOD;;;; +249B;NUMBER TWENTY FULL STOP;No;0;EN;<compat> 0032 0030 002E;;;20;N;NUMBER TWENTY PERIOD;;;; +249C;PARENTHESIZED LATIN SMALL LETTER A;So;0;L;<compat> 0028 0061 0029;;;;N;;;;; +249D;PARENTHESIZED LATIN SMALL LETTER B;So;0;L;<compat> 0028 0062 0029;;;;N;;;;; +249E;PARENTHESIZED LATIN SMALL LETTER C;So;0;L;<compat> 0028 0063 0029;;;;N;;;;; +249F;PARENTHESIZED LATIN SMALL LETTER D;So;0;L;<compat> 0028 0064 0029;;;;N;;;;; +24A0;PARENTHESIZED LATIN SMALL LETTER E;So;0;L;<compat> 0028 0065 0029;;;;N;;;;; +24A1;PARENTHESIZED LATIN SMALL LETTER F;So;0;L;<compat> 0028 0066 0029;;;;N;;;;; +24A2;PARENTHESIZED LATIN SMALL LETTER G;So;0;L;<compat> 0028 0067 0029;;;;N;;;;; +24A3;PARENTHESIZED LATIN SMALL LETTER H;So;0;L;<compat> 0028 0068 0029;;;;N;;;;; +24A4;PARENTHESIZED LATIN SMALL LETTER I;So;0;L;<compat> 0028 0069 0029;;;;N;;;;; +24A5;PARENTHESIZED LATIN SMALL LETTER J;So;0;L;<compat> 0028 006A 0029;;;;N;;;;; +24A6;PARENTHESIZED LATIN SMALL LETTER K;So;0;L;<compat> 0028 006B 0029;;;;N;;;;; +24A7;PARENTHESIZED LATIN SMALL LETTER L;So;0;L;<compat> 0028 006C 0029;;;;N;;;;; +24A8;PARENTHESIZED LATIN SMALL LETTER M;So;0;L;<compat> 0028 006D 0029;;;;N;;;;; +24A9;PARENTHESIZED LATIN SMALL LETTER N;So;0;L;<compat> 0028 006E 0029;;;;N;;;;; +24AA;PARENTHESIZED LATIN SMALL LETTER O;So;0;L;<compat> 0028 006F 0029;;;;N;;;;; +24AB;PARENTHESIZED LATIN SMALL LETTER P;So;0;L;<compat> 0028 0070 0029;;;;N;;;;; +24AC;PARENTHESIZED LATIN SMALL LETTER Q;So;0;L;<compat> 0028 0071 0029;;;;N;;;;; +24AD;PARENTHESIZED LATIN SMALL LETTER R;So;0;L;<compat> 0028 0072 0029;;;;N;;;;; +24AE;PARENTHESIZED LATIN SMALL LETTER S;So;0;L;<compat> 0028 0073 0029;;;;N;;;;; +24AF;PARENTHESIZED LATIN SMALL LETTER T;So;0;L;<compat> 0028 0074 0029;;;;N;;;;; +24B0;PARENTHESIZED LATIN SMALL LETTER U;So;0;L;<compat> 0028 0075 0029;;;;N;;;;; +24B1;PARENTHESIZED LATIN SMALL LETTER V;So;0;L;<compat> 0028 0076 0029;;;;N;;;;; +24B2;PARENTHESIZED LATIN SMALL LETTER W;So;0;L;<compat> 0028 0077 0029;;;;N;;;;; +24B3;PARENTHESIZED LATIN SMALL LETTER X;So;0;L;<compat> 0028 0078 0029;;;;N;;;;; +24B4;PARENTHESIZED LATIN SMALL LETTER Y;So;0;L;<compat> 0028 0079 0029;;;;N;;;;; +24B5;PARENTHESIZED LATIN SMALL LETTER Z;So;0;L;<compat> 0028 007A 0029;;;;N;;;;; +24B6;CIRCLED LATIN CAPITAL LETTER A;So;0;L;<circle> 0041;;;;N;;;;24D0; +24B7;CIRCLED LATIN CAPITAL LETTER B;So;0;L;<circle> 0042;;;;N;;;;24D1; +24B8;CIRCLED LATIN CAPITAL LETTER C;So;0;L;<circle> 0043;;;;N;;;;24D2; +24B9;CIRCLED LATIN CAPITAL LETTER D;So;0;L;<circle> 0044;;;;N;;;;24D3; +24BA;CIRCLED LATIN CAPITAL LETTER E;So;0;L;<circle> 0045;;;;N;;;;24D4; +24BB;CIRCLED LATIN CAPITAL LETTER F;So;0;L;<circle> 0046;;;;N;;;;24D5; +24BC;CIRCLED LATIN CAPITAL LETTER G;So;0;L;<circle> 0047;;;;N;;;;24D6; +24BD;CIRCLED LATIN CAPITAL LETTER H;So;0;L;<circle> 0048;;;;N;;;;24D7; +24BE;CIRCLED LATIN CAPITAL LETTER I;So;0;L;<circle> 0049;;;;N;;;;24D8; +24BF;CIRCLED LATIN CAPITAL LETTER J;So;0;L;<circle> 004A;;;;N;;;;24D9; +24C0;CIRCLED LATIN CAPITAL LETTER K;So;0;L;<circle> 004B;;;;N;;;;24DA; +24C1;CIRCLED LATIN CAPITAL LETTER L;So;0;L;<circle> 004C;;;;N;;;;24DB; +24C2;CIRCLED LATIN CAPITAL LETTER M;So;0;L;<circle> 004D;;;;N;;;;24DC; +24C3;CIRCLED LATIN CAPITAL LETTER N;So;0;L;<circle> 004E;;;;N;;;;24DD; +24C4;CIRCLED LATIN CAPITAL LETTER O;So;0;L;<circle> 004F;;;;N;;;;24DE; +24C5;CIRCLED LATIN CAPITAL LETTER P;So;0;L;<circle> 0050;;;;N;;;;24DF; +24C6;CIRCLED LATIN CAPITAL LETTER Q;So;0;L;<circle> 0051;;;;N;;;;24E0; +24C7;CIRCLED LATIN CAPITAL LETTER R;So;0;L;<circle> 0052;;;;N;;;;24E1; +24C8;CIRCLED LATIN CAPITAL LETTER S;So;0;L;<circle> 0053;;;;N;;;;24E2; +24C9;CIRCLED LATIN CAPITAL LETTER T;So;0;L;<circle> 0054;;;;N;;;;24E3; +24CA;CIRCLED LATIN CAPITAL LETTER U;So;0;L;<circle> 0055;;;;N;;;;24E4; +24CB;CIRCLED LATIN CAPITAL LETTER V;So;0;L;<circle> 0056;;;;N;;;;24E5; +24CC;CIRCLED LATIN CAPITAL LETTER W;So;0;L;<circle> 0057;;;;N;;;;24E6; +24CD;CIRCLED LATIN CAPITAL LETTER X;So;0;L;<circle> 0058;;;;N;;;;24E7; +24CE;CIRCLED LATIN CAPITAL LETTER Y;So;0;L;<circle> 0059;;;;N;;;;24E8; +24CF;CIRCLED LATIN CAPITAL LETTER Z;So;0;L;<circle> 005A;;;;N;;;;24E9; +24D0;CIRCLED LATIN SMALL LETTER A;So;0;L;<circle> 0061;;;;N;;;24B6;;24B6 +24D1;CIRCLED LATIN SMALL LETTER B;So;0;L;<circle> 0062;;;;N;;;24B7;;24B7 +24D2;CIRCLED LATIN SMALL LETTER C;So;0;L;<circle> 0063;;;;N;;;24B8;;24B8 +24D3;CIRCLED LATIN SMALL LETTER D;So;0;L;<circle> 0064;;;;N;;;24B9;;24B9 +24D4;CIRCLED LATIN SMALL LETTER E;So;0;L;<circle> 0065;;;;N;;;24BA;;24BA +24D5;CIRCLED LATIN SMALL LETTER F;So;0;L;<circle> 0066;;;;N;;;24BB;;24BB +24D6;CIRCLED LATIN SMALL LETTER G;So;0;L;<circle> 0067;;;;N;;;24BC;;24BC +24D7;CIRCLED LATIN SMALL LETTER H;So;0;L;<circle> 0068;;;;N;;;24BD;;24BD +24D8;CIRCLED LATIN SMALL LETTER I;So;0;L;<circle> 0069;;;;N;;;24BE;;24BE +24D9;CIRCLED LATIN SMALL LETTER J;So;0;L;<circle> 006A;;;;N;;;24BF;;24BF +24DA;CIRCLED LATIN SMALL LETTER K;So;0;L;<circle> 006B;;;;N;;;24C0;;24C0 +24DB;CIRCLED LATIN SMALL LETTER L;So;0;L;<circle> 006C;;;;N;;;24C1;;24C1 +24DC;CIRCLED LATIN SMALL LETTER M;So;0;L;<circle> 006D;;;;N;;;24C2;;24C2 +24DD;CIRCLED LATIN SMALL LETTER N;So;0;L;<circle> 006E;;;;N;;;24C3;;24C3 +24DE;CIRCLED LATIN SMALL LETTER O;So;0;L;<circle> 006F;;;;N;;;24C4;;24C4 +24DF;CIRCLED LATIN SMALL LETTER P;So;0;L;<circle> 0070;;;;N;;;24C5;;24C5 +24E0;CIRCLED LATIN SMALL LETTER Q;So;0;L;<circle> 0071;;;;N;;;24C6;;24C6 +24E1;CIRCLED LATIN SMALL LETTER R;So;0;L;<circle> 0072;;;;N;;;24C7;;24C7 +24E2;CIRCLED LATIN SMALL LETTER S;So;0;L;<circle> 0073;;;;N;;;24C8;;24C8 +24E3;CIRCLED LATIN SMALL LETTER T;So;0;L;<circle> 0074;;;;N;;;24C9;;24C9 +24E4;CIRCLED LATIN SMALL LETTER U;So;0;L;<circle> 0075;;;;N;;;24CA;;24CA +24E5;CIRCLED LATIN SMALL LETTER V;So;0;L;<circle> 0076;;;;N;;;24CB;;24CB +24E6;CIRCLED LATIN SMALL LETTER W;So;0;L;<circle> 0077;;;;N;;;24CC;;24CC +24E7;CIRCLED LATIN SMALL LETTER X;So;0;L;<circle> 0078;;;;N;;;24CD;;24CD +24E8;CIRCLED LATIN SMALL LETTER Y;So;0;L;<circle> 0079;;;;N;;;24CE;;24CE +24E9;CIRCLED LATIN SMALL LETTER Z;So;0;L;<circle> 007A;;;;N;;;24CF;;24CF +24EA;CIRCLED DIGIT ZERO;No;0;ON;<circle> 0030;;0;0;N;;;;; +24EB;NEGATIVE CIRCLED NUMBER ELEVEN;No;0;ON;;;;11;N;;;;; +24EC;NEGATIVE CIRCLED NUMBER TWELVE;No;0;ON;;;;12;N;;;;; +24ED;NEGATIVE CIRCLED NUMBER THIRTEEN;No;0;ON;;;;13;N;;;;; +24EE;NEGATIVE CIRCLED NUMBER FOURTEEN;No;0;ON;;;;14;N;;;;; +24EF;NEGATIVE CIRCLED NUMBER FIFTEEN;No;0;ON;;;;15;N;;;;; +24F0;NEGATIVE CIRCLED NUMBER SIXTEEN;No;0;ON;;;;16;N;;;;; +24F1;NEGATIVE CIRCLED NUMBER SEVENTEEN;No;0;ON;;;;17;N;;;;; +24F2;NEGATIVE CIRCLED NUMBER EIGHTEEN;No;0;ON;;;;18;N;;;;; +24F3;NEGATIVE CIRCLED NUMBER NINETEEN;No;0;ON;;;;19;N;;;;; +24F4;NEGATIVE CIRCLED NUMBER TWENTY;No;0;ON;;;;20;N;;;;; +24F5;DOUBLE CIRCLED DIGIT ONE;No;0;ON;;;1;1;N;;;;; +24F6;DOUBLE CIRCLED DIGIT TWO;No;0;ON;;;2;2;N;;;;; +24F7;DOUBLE CIRCLED DIGIT THREE;No;0;ON;;;3;3;N;;;;; +24F8;DOUBLE CIRCLED DIGIT FOUR;No;0;ON;;;4;4;N;;;;; +24F9;DOUBLE CIRCLED DIGIT FIVE;No;0;ON;;;5;5;N;;;;; +24FA;DOUBLE CIRCLED DIGIT SIX;No;0;ON;;;6;6;N;;;;; +24FB;DOUBLE CIRCLED DIGIT SEVEN;No;0;ON;;;7;7;N;;;;; +24FC;DOUBLE CIRCLED DIGIT EIGHT;No;0;ON;;;8;8;N;;;;; +24FD;DOUBLE CIRCLED DIGIT NINE;No;0;ON;;;9;9;N;;;;; +24FE;DOUBLE CIRCLED NUMBER TEN;No;0;ON;;;;10;N;;;;; +24FF;NEGATIVE CIRCLED DIGIT ZERO;No;0;ON;;;0;0;N;;;;; +2500;BOX DRAWINGS LIGHT HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT HORIZONTAL;;;; +2501;BOX DRAWINGS HEAVY HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY HORIZONTAL;;;; +2502;BOX DRAWINGS LIGHT VERTICAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL;;;; +2503;BOX DRAWINGS HEAVY VERTICAL;So;0;ON;;;;;N;FORMS HEAVY VERTICAL;;;; +2504;BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT TRIPLE DASH HORIZONTAL;;;; +2505;BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY TRIPLE DASH HORIZONTAL;;;; +2506;BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT TRIPLE DASH VERTICAL;;;; +2507;BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY TRIPLE DASH VERTICAL;;;; +2508;BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT QUADRUPLE DASH HORIZONTAL;;;; +2509;BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY QUADRUPLE DASH HORIZONTAL;;;; +250A;BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT QUADRUPLE DASH VERTICAL;;;; +250B;BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY QUADRUPLE DASH VERTICAL;;;; +250C;BOX DRAWINGS LIGHT DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND RIGHT;;;; +250D;BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND RIGHT HEAVY;;;; +250E;BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND RIGHT LIGHT;;;; +250F;BOX DRAWINGS HEAVY DOWN AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY DOWN AND RIGHT;;;; +2510;BOX DRAWINGS LIGHT DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND LEFT;;;; +2511;BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND LEFT HEAVY;;;; +2512;BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND LEFT LIGHT;;;; +2513;BOX DRAWINGS HEAVY DOWN AND LEFT;So;0;ON;;;;;N;FORMS HEAVY DOWN AND LEFT;;;; +2514;BOX DRAWINGS LIGHT UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT UP AND RIGHT;;;; +2515;BOX DRAWINGS UP LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND RIGHT HEAVY;;;; +2516;BOX DRAWINGS UP HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND RIGHT LIGHT;;;; +2517;BOX DRAWINGS HEAVY UP AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY UP AND RIGHT;;;; +2518;BOX DRAWINGS LIGHT UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT UP AND LEFT;;;; +2519;BOX DRAWINGS UP LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND LEFT HEAVY;;;; +251A;BOX DRAWINGS UP HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND LEFT LIGHT;;;; +251B;BOX DRAWINGS HEAVY UP AND LEFT;So;0;ON;;;;;N;FORMS HEAVY UP AND LEFT;;;; +251C;BOX DRAWINGS LIGHT VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND RIGHT;;;; +251D;BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND RIGHT HEAVY;;;; +251E;BOX DRAWINGS UP HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND RIGHT DOWN LIGHT;;;; +251F;BOX DRAWINGS DOWN HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND RIGHT UP LIGHT;;;; +2520;BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND RIGHT LIGHT;;;; +2521;BOX DRAWINGS DOWN LIGHT AND RIGHT UP HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND RIGHT UP HEAVY;;;; +2522;BOX DRAWINGS UP LIGHT AND RIGHT DOWN HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND RIGHT DOWN HEAVY;;;; +2523;BOX DRAWINGS HEAVY VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND RIGHT;;;; +2524;BOX DRAWINGS LIGHT VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND LEFT;;;; +2525;BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND LEFT HEAVY;;;; +2526;BOX DRAWINGS UP HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND LEFT DOWN LIGHT;;;; +2527;BOX DRAWINGS DOWN HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND LEFT UP LIGHT;;;; +2528;BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND LEFT LIGHT;;;; +2529;BOX DRAWINGS DOWN LIGHT AND LEFT UP HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND LEFT UP HEAVY;;;; +252A;BOX DRAWINGS UP LIGHT AND LEFT DOWN HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND LEFT DOWN HEAVY;;;; +252B;BOX DRAWINGS HEAVY VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND LEFT;;;; +252C;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOWN AND HORIZONTAL;;;; +252D;BOX DRAWINGS LEFT HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT DOWN LIGHT;;;; +252E;BOX DRAWINGS RIGHT HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT DOWN LIGHT;;;; +252F;BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND HORIZONTAL HEAVY;;;; +2530;BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND HORIZONTAL LIGHT;;;; +2531;BOX DRAWINGS RIGHT LIGHT AND LEFT DOWN HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT DOWN HEAVY;;;; +2532;BOX DRAWINGS LEFT LIGHT AND RIGHT DOWN HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT DOWN HEAVY;;;; +2533;BOX DRAWINGS HEAVY DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY DOWN AND HORIZONTAL;;;; +2534;BOX DRAWINGS LIGHT UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT UP AND HORIZONTAL;;;; +2535;BOX DRAWINGS LEFT HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT UP LIGHT;;;; +2536;BOX DRAWINGS RIGHT HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT UP LIGHT;;;; +2537;BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND HORIZONTAL HEAVY;;;; +2538;BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND HORIZONTAL LIGHT;;;; +2539;BOX DRAWINGS RIGHT LIGHT AND LEFT UP HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT UP HEAVY;;;; +253A;BOX DRAWINGS LEFT LIGHT AND RIGHT UP HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT UP HEAVY;;;; +253B;BOX DRAWINGS HEAVY UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY UP AND HORIZONTAL;;;; +253C;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND HORIZONTAL;;;; +253D;BOX DRAWINGS LEFT HEAVY AND RIGHT VERTICAL LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT VERTICAL LIGHT;;;; +253E;BOX DRAWINGS RIGHT HEAVY AND LEFT VERTICAL LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT VERTICAL LIGHT;;;; +253F;BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND HORIZONTAL HEAVY;;;; +2540;BOX DRAWINGS UP HEAVY AND DOWN HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND DOWN HORIZONTAL LIGHT;;;; +2541;BOX DRAWINGS DOWN HEAVY AND UP HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND UP HORIZONTAL LIGHT;;;; +2542;BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND HORIZONTAL LIGHT;;;; +2543;BOX DRAWINGS LEFT UP HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS LEFT UP HEAVY AND RIGHT DOWN LIGHT;;;; +2544;BOX DRAWINGS RIGHT UP HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS RIGHT UP HEAVY AND LEFT DOWN LIGHT;;;; +2545;BOX DRAWINGS LEFT DOWN HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS LEFT DOWN HEAVY AND RIGHT UP LIGHT;;;; +2546;BOX DRAWINGS RIGHT DOWN HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS RIGHT DOWN HEAVY AND LEFT UP LIGHT;;;; +2547;BOX DRAWINGS DOWN LIGHT AND UP HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND UP HORIZONTAL HEAVY;;;; +2548;BOX DRAWINGS UP LIGHT AND DOWN HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND DOWN HORIZONTAL HEAVY;;;; +2549;BOX DRAWINGS RIGHT LIGHT AND LEFT VERTICAL HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT VERTICAL HEAVY;;;; +254A;BOX DRAWINGS LEFT LIGHT AND RIGHT VERTICAL HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT VERTICAL HEAVY;;;; +254B;BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND HORIZONTAL;;;; +254C;BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOUBLE DASH HORIZONTAL;;;; +254D;BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY DOUBLE DASH HORIZONTAL;;;; +254E;BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT DOUBLE DASH VERTICAL;;;; +254F;BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY DOUBLE DASH VERTICAL;;;; +2550;BOX DRAWINGS DOUBLE HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE HORIZONTAL;;;; +2551;BOX DRAWINGS DOUBLE VERTICAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL;;;; +2552;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND RIGHT DOUBLE;;;; +2553;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND RIGHT SINGLE;;;; +2554;BOX DRAWINGS DOUBLE DOWN AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND RIGHT;;;; +2555;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND LEFT DOUBLE;;;; +2556;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND LEFT SINGLE;;;; +2557;BOX DRAWINGS DOUBLE DOWN AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND LEFT;;;; +2558;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND RIGHT DOUBLE;;;; +2559;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND RIGHT SINGLE;;;; +255A;BOX DRAWINGS DOUBLE UP AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE UP AND RIGHT;;;; +255B;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND LEFT DOUBLE;;;; +255C;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND LEFT SINGLE;;;; +255D;BOX DRAWINGS DOUBLE UP AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE UP AND LEFT;;;; +255E;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND RIGHT DOUBLE;;;; +255F;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND RIGHT SINGLE;;;; +2560;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND RIGHT;;;; +2561;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND LEFT DOUBLE;;;; +2562;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND LEFT SINGLE;;;; +2563;BOX DRAWINGS DOUBLE VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND LEFT;;;; +2564;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND HORIZONTAL DOUBLE;;;; +2565;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND HORIZONTAL SINGLE;;;; +2566;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND HORIZONTAL;;;; +2567;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND HORIZONTAL DOUBLE;;;; +2568;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND HORIZONTAL SINGLE;;;; +2569;BOX DRAWINGS DOUBLE UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE UP AND HORIZONTAL;;;; +256A;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND HORIZONTAL DOUBLE;;;; +256B;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND HORIZONTAL SINGLE;;;; +256C;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND HORIZONTAL;;;; +256D;BOX DRAWINGS LIGHT ARC DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT ARC DOWN AND RIGHT;;;; +256E;BOX DRAWINGS LIGHT ARC DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT ARC DOWN AND LEFT;;;; +256F;BOX DRAWINGS LIGHT ARC UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT ARC UP AND LEFT;;;; +2570;BOX DRAWINGS LIGHT ARC UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT ARC UP AND RIGHT;;;; +2571;BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT;;;; +2572;BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT;;;; +2573;BOX DRAWINGS LIGHT DIAGONAL CROSS;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL CROSS;;;; +2574;BOX DRAWINGS LIGHT LEFT;So;0;ON;;;;;N;FORMS LIGHT LEFT;;;; +2575;BOX DRAWINGS LIGHT UP;So;0;ON;;;;;N;FORMS LIGHT UP;;;; +2576;BOX DRAWINGS LIGHT RIGHT;So;0;ON;;;;;N;FORMS LIGHT RIGHT;;;; +2577;BOX DRAWINGS LIGHT DOWN;So;0;ON;;;;;N;FORMS LIGHT DOWN;;;; +2578;BOX DRAWINGS HEAVY LEFT;So;0;ON;;;;;N;FORMS HEAVY LEFT;;;; +2579;BOX DRAWINGS HEAVY UP;So;0;ON;;;;;N;FORMS HEAVY UP;;;; +257A;BOX DRAWINGS HEAVY RIGHT;So;0;ON;;;;;N;FORMS HEAVY RIGHT;;;; +257B;BOX DRAWINGS HEAVY DOWN;So;0;ON;;;;;N;FORMS HEAVY DOWN;;;; +257C;BOX DRAWINGS LIGHT LEFT AND HEAVY RIGHT;So;0;ON;;;;;N;FORMS LIGHT LEFT AND HEAVY RIGHT;;;; +257D;BOX DRAWINGS LIGHT UP AND HEAVY DOWN;So;0;ON;;;;;N;FORMS LIGHT UP AND HEAVY DOWN;;;; +257E;BOX DRAWINGS HEAVY LEFT AND LIGHT RIGHT;So;0;ON;;;;;N;FORMS HEAVY LEFT AND LIGHT RIGHT;;;; +257F;BOX DRAWINGS HEAVY UP AND LIGHT DOWN;So;0;ON;;;;;N;FORMS HEAVY UP AND LIGHT DOWN;;;; +2580;UPPER HALF BLOCK;So;0;ON;;;;;N;;;;; +2581;LOWER ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;; +2582;LOWER ONE QUARTER BLOCK;So;0;ON;;;;;N;;;;; +2583;LOWER THREE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; +2584;LOWER HALF BLOCK;So;0;ON;;;;;N;;;;; +2585;LOWER FIVE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; +2586;LOWER THREE QUARTERS BLOCK;So;0;ON;;;;;N;LOWER THREE QUARTER BLOCK;;;; +2587;LOWER SEVEN EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; +2588;FULL BLOCK;So;0;ON;;;;;N;;;;; +2589;LEFT SEVEN EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; +258A;LEFT THREE QUARTERS BLOCK;So;0;ON;;;;;N;LEFT THREE QUARTER BLOCK;;;; +258B;LEFT FIVE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; +258C;LEFT HALF BLOCK;So;0;ON;;;;;N;;;;; +258D;LEFT THREE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; +258E;LEFT ONE QUARTER BLOCK;So;0;ON;;;;;N;;;;; +258F;LEFT ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;; +2590;RIGHT HALF BLOCK;So;0;ON;;;;;N;;;;; +2591;LIGHT SHADE;So;0;ON;;;;;N;;;;; +2592;MEDIUM SHADE;So;0;ON;;;;;N;;;;; +2593;DARK SHADE;So;0;ON;;;;;N;;;;; +2594;UPPER ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;; +2595;RIGHT ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;; +2596;QUADRANT LOWER LEFT;So;0;ON;;;;;N;;;;; +2597;QUADRANT LOWER RIGHT;So;0;ON;;;;;N;;;;; +2598;QUADRANT UPPER LEFT;So;0;ON;;;;;N;;;;; +2599;QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;; +259A;QUADRANT UPPER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;; +259B;QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT;So;0;ON;;;;;N;;;;; +259C;QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT;So;0;ON;;;;;N;;;;; +259D;QUADRANT UPPER RIGHT;So;0;ON;;;;;N;;;;; +259E;QUADRANT UPPER RIGHT AND LOWER LEFT;So;0;ON;;;;;N;;;;; +259F;QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;; +25A0;BLACK SQUARE;So;0;ON;;;;;N;;;;; +25A1;WHITE SQUARE;So;0;ON;;;;;N;;;;; +25A2;WHITE SQUARE WITH ROUNDED CORNERS;So;0;ON;;;;;N;;;;; +25A3;WHITE SQUARE CONTAINING BLACK SMALL SQUARE;So;0;ON;;;;;N;;;;; +25A4;SQUARE WITH HORIZONTAL FILL;So;0;ON;;;;;N;;;;; +25A5;SQUARE WITH VERTICAL FILL;So;0;ON;;;;;N;;;;; +25A6;SQUARE WITH ORTHOGONAL CROSSHATCH FILL;So;0;ON;;;;;N;;;;; +25A7;SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL;So;0;ON;;;;;N;;;;; +25A8;SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL;So;0;ON;;;;;N;;;;; +25A9;SQUARE WITH DIAGONAL CROSSHATCH FILL;So;0;ON;;;;;N;;;;; +25AA;BLACK SMALL SQUARE;So;0;ON;;;;;N;;;;; +25AB;WHITE SMALL SQUARE;So;0;ON;;;;;N;;;;; +25AC;BLACK RECTANGLE;So;0;ON;;;;;N;;;;; +25AD;WHITE RECTANGLE;So;0;ON;;;;;N;;;;; +25AE;BLACK VERTICAL RECTANGLE;So;0;ON;;;;;N;;;;; +25AF;WHITE VERTICAL RECTANGLE;So;0;ON;;;;;N;;;;; +25B0;BLACK PARALLELOGRAM;So;0;ON;;;;;N;;;;; +25B1;WHITE PARALLELOGRAM;So;0;ON;;;;;N;;;;; +25B2;BLACK UP-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK UP POINTING TRIANGLE;;;; +25B3;WHITE UP-POINTING TRIANGLE;So;0;ON;;;;;N;WHITE UP POINTING TRIANGLE;;;; +25B4;BLACK UP-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK UP POINTING SMALL TRIANGLE;;;; +25B5;WHITE UP-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE UP POINTING SMALL TRIANGLE;;;; +25B6;BLACK RIGHT-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK RIGHT POINTING TRIANGLE;;;; +25B7;WHITE RIGHT-POINTING TRIANGLE;Sm;0;ON;;;;;N;WHITE RIGHT POINTING TRIANGLE;;;; +25B8;BLACK RIGHT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK RIGHT POINTING SMALL TRIANGLE;;;; +25B9;WHITE RIGHT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE RIGHT POINTING SMALL TRIANGLE;;;; +25BA;BLACK RIGHT-POINTING POINTER;So;0;ON;;;;;N;BLACK RIGHT POINTING POINTER;;;; +25BB;WHITE RIGHT-POINTING POINTER;So;0;ON;;;;;N;WHITE RIGHT POINTING POINTER;;;; +25BC;BLACK DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK DOWN POINTING TRIANGLE;;;; +25BD;WHITE DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;WHITE DOWN POINTING TRIANGLE;;;; +25BE;BLACK DOWN-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK DOWN POINTING SMALL TRIANGLE;;;; +25BF;WHITE DOWN-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE DOWN POINTING SMALL TRIANGLE;;;; +25C0;BLACK LEFT-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK LEFT POINTING TRIANGLE;;;; +25C1;WHITE LEFT-POINTING TRIANGLE;Sm;0;ON;;;;;N;WHITE LEFT POINTING TRIANGLE;;;; +25C2;BLACK LEFT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK LEFT POINTING SMALL TRIANGLE;;;; +25C3;WHITE LEFT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE LEFT POINTING SMALL TRIANGLE;;;; +25C4;BLACK LEFT-POINTING POINTER;So;0;ON;;;;;N;BLACK LEFT POINTING POINTER;;;; +25C5;WHITE LEFT-POINTING POINTER;So;0;ON;;;;;N;WHITE LEFT POINTING POINTER;;;; +25C6;BLACK DIAMOND;So;0;ON;;;;;N;;;;; +25C7;WHITE DIAMOND;So;0;ON;;;;;N;;;;; +25C8;WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND;So;0;ON;;;;;N;;;;; +25C9;FISHEYE;So;0;ON;;;;;N;;;;; +25CA;LOZENGE;So;0;ON;;;;;N;;;;; +25CB;WHITE CIRCLE;So;0;ON;;;;;N;;;;; +25CC;DOTTED CIRCLE;So;0;ON;;;;;N;;;;; +25CD;CIRCLE WITH VERTICAL FILL;So;0;ON;;;;;N;;;;; +25CE;BULLSEYE;So;0;ON;;;;;N;;;;; +25CF;BLACK CIRCLE;So;0;ON;;;;;N;;;;; +25D0;CIRCLE WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;; +25D1;CIRCLE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;; +25D2;CIRCLE WITH LOWER HALF BLACK;So;0;ON;;;;;N;;;;; +25D3;CIRCLE WITH UPPER HALF BLACK;So;0;ON;;;;;N;;;;; +25D4;CIRCLE WITH UPPER RIGHT QUADRANT BLACK;So;0;ON;;;;;N;;;;; +25D5;CIRCLE WITH ALL BUT UPPER LEFT QUADRANT BLACK;So;0;ON;;;;;N;;;;; +25D6;LEFT HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;; +25D7;RIGHT HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;; +25D8;INVERSE BULLET;So;0;ON;;;;;N;;;;; +25D9;INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;; +25DA;UPPER HALF INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;; +25DB;LOWER HALF INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;; +25DC;UPPER LEFT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;; +25DD;UPPER RIGHT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;; +25DE;LOWER RIGHT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;; +25DF;LOWER LEFT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;; +25E0;UPPER HALF CIRCLE;So;0;ON;;;;;N;;;;; +25E1;LOWER HALF CIRCLE;So;0;ON;;;;;N;;;;; +25E2;BLACK LOWER RIGHT TRIANGLE;So;0;ON;;;;;N;;;;; +25E3;BLACK LOWER LEFT TRIANGLE;So;0;ON;;;;;N;;;;; +25E4;BLACK UPPER LEFT TRIANGLE;So;0;ON;;;;;N;;;;; +25E5;BLACK UPPER RIGHT TRIANGLE;So;0;ON;;;;;N;;;;; +25E6;WHITE BULLET;So;0;ON;;;;;N;;;;; +25E7;SQUARE WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;; +25E8;SQUARE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;; +25E9;SQUARE WITH UPPER LEFT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;; +25EA;SQUARE WITH LOWER RIGHT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;; +25EB;WHITE SQUARE WITH VERTICAL BISECTING LINE;So;0;ON;;;;;N;;;;; +25EC;WHITE UP-POINTING TRIANGLE WITH DOT;So;0;ON;;;;;N;WHITE UP POINTING TRIANGLE WITH DOT;;;; +25ED;UP-POINTING TRIANGLE WITH LEFT HALF BLACK;So;0;ON;;;;;N;UP POINTING TRIANGLE WITH LEFT HALF BLACK;;;; +25EE;UP-POINTING TRIANGLE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;UP POINTING TRIANGLE WITH RIGHT HALF BLACK;;;; +25EF;LARGE CIRCLE;So;0;ON;;;;;N;;;;; +25F0;WHITE SQUARE WITH UPPER LEFT QUADRANT;So;0;ON;;;;;N;;;;; +25F1;WHITE SQUARE WITH LOWER LEFT QUADRANT;So;0;ON;;;;;N;;;;; +25F2;WHITE SQUARE WITH LOWER RIGHT QUADRANT;So;0;ON;;;;;N;;;;; +25F3;WHITE SQUARE WITH UPPER RIGHT QUADRANT;So;0;ON;;;;;N;;;;; +25F4;WHITE CIRCLE WITH UPPER LEFT QUADRANT;So;0;ON;;;;;N;;;;; +25F5;WHITE CIRCLE WITH LOWER LEFT QUADRANT;So;0;ON;;;;;N;;;;; +25F6;WHITE CIRCLE WITH LOWER RIGHT QUADRANT;So;0;ON;;;;;N;;;;; +25F7;WHITE CIRCLE WITH UPPER RIGHT QUADRANT;So;0;ON;;;;;N;;;;; +25F8;UPPER LEFT TRIANGLE;Sm;0;ON;;;;;N;;;;; +25F9;UPPER RIGHT TRIANGLE;Sm;0;ON;;;;;N;;;;; +25FA;LOWER LEFT TRIANGLE;Sm;0;ON;;;;;N;;;;; +25FB;WHITE MEDIUM SQUARE;Sm;0;ON;;;;;N;;;;; +25FC;BLACK MEDIUM SQUARE;Sm;0;ON;;;;;N;;;;; +25FD;WHITE MEDIUM SMALL SQUARE;Sm;0;ON;;;;;N;;;;; +25FE;BLACK MEDIUM SMALL SQUARE;Sm;0;ON;;;;;N;;;;; +25FF;LOWER RIGHT TRIANGLE;Sm;0;ON;;;;;N;;;;; +2600;BLACK SUN WITH RAYS;So;0;ON;;;;;N;;;;; +2601;CLOUD;So;0;ON;;;;;N;;;;; +2602;UMBRELLA;So;0;ON;;;;;N;;;;; +2603;SNOWMAN;So;0;ON;;;;;N;;;;; +2604;COMET;So;0;ON;;;;;N;;;;; +2605;BLACK STAR;So;0;ON;;;;;N;;;;; +2606;WHITE STAR;So;0;ON;;;;;N;;;;; +2607;LIGHTNING;So;0;ON;;;;;N;;;;; +2608;THUNDERSTORM;So;0;ON;;;;;N;;;;; +2609;SUN;So;0;ON;;;;;N;;;;; +260A;ASCENDING NODE;So;0;ON;;;;;N;;;;; +260B;DESCENDING NODE;So;0;ON;;;;;N;;;;; +260C;CONJUNCTION;So;0;ON;;;;;N;;;;; +260D;OPPOSITION;So;0;ON;;;;;N;;;;; +260E;BLACK TELEPHONE;So;0;ON;;;;;N;;;;; +260F;WHITE TELEPHONE;So;0;ON;;;;;N;;;;; +2610;BALLOT BOX;So;0;ON;;;;;N;;;;; +2611;BALLOT BOX WITH CHECK;So;0;ON;;;;;N;;;;; +2612;BALLOT BOX WITH X;So;0;ON;;;;;N;;;;; +2613;SALTIRE;So;0;ON;;;;;N;;;;; +2614;UMBRELLA WITH RAIN DROPS;So;0;ON;;;;;N;;;;; +2615;HOT BEVERAGE;So;0;ON;;;;;N;;;;; +2616;WHITE SHOGI PIECE;So;0;ON;;;;;N;;;;; +2617;BLACK SHOGI PIECE;So;0;ON;;;;;N;;;;; +2618;SHAMROCK;So;0;ON;;;;;N;;;;; +2619;REVERSED ROTATED FLORAL HEART BULLET;So;0;ON;;;;;N;;;;; +261A;BLACK LEFT POINTING INDEX;So;0;ON;;;;;N;;;;; +261B;BLACK RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;; +261C;WHITE LEFT POINTING INDEX;So;0;ON;;;;;N;;;;; +261D;WHITE UP POINTING INDEX;So;0;ON;;;;;N;;;;; +261E;WHITE RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;; +261F;WHITE DOWN POINTING INDEX;So;0;ON;;;;;N;;;;; +2620;SKULL AND CROSSBONES;So;0;ON;;;;;N;;;;; +2621;CAUTION SIGN;So;0;ON;;;;;N;;;;; +2622;RADIOACTIVE SIGN;So;0;ON;;;;;N;;;;; +2623;BIOHAZARD SIGN;So;0;ON;;;;;N;;;;; +2624;CADUCEUS;So;0;ON;;;;;N;;;;; +2625;ANKH;So;0;ON;;;;;N;;;;; +2626;ORTHODOX CROSS;So;0;ON;;;;;N;;;;; +2627;CHI RHO;So;0;ON;;;;;N;;;;; +2628;CROSS OF LORRAINE;So;0;ON;;;;;N;;;;; +2629;CROSS OF JERUSALEM;So;0;ON;;;;;N;;;;; +262A;STAR AND CRESCENT;So;0;ON;;;;;N;;;;; +262B;FARSI SYMBOL;So;0;ON;;;;;N;SYMBOL OF IRAN;;;; +262C;ADI SHAKTI;So;0;ON;;;;;N;;;;; +262D;HAMMER AND SICKLE;So;0;ON;;;;;N;;;;; +262E;PEACE SYMBOL;So;0;ON;;;;;N;;;;; +262F;YIN YANG;So;0;ON;;;;;N;;;;; +2630;TRIGRAM FOR HEAVEN;So;0;ON;;;;;N;;;;; +2631;TRIGRAM FOR LAKE;So;0;ON;;;;;N;;;;; +2632;TRIGRAM FOR FIRE;So;0;ON;;;;;N;;;;; +2633;TRIGRAM FOR THUNDER;So;0;ON;;;;;N;;;;; +2634;TRIGRAM FOR WIND;So;0;ON;;;;;N;;;;; +2635;TRIGRAM FOR WATER;So;0;ON;;;;;N;;;;; +2636;TRIGRAM FOR MOUNTAIN;So;0;ON;;;;;N;;;;; +2637;TRIGRAM FOR EARTH;So;0;ON;;;;;N;;;;; +2638;WHEEL OF DHARMA;So;0;ON;;;;;N;;;;; +2639;WHITE FROWNING FACE;So;0;ON;;;;;N;;;;; +263A;WHITE SMILING FACE;So;0;ON;;;;;N;;;;; +263B;BLACK SMILING FACE;So;0;ON;;;;;N;;;;; +263C;WHITE SUN WITH RAYS;So;0;ON;;;;;N;;;;; +263D;FIRST QUARTER MOON;So;0;ON;;;;;N;;;;; +263E;LAST QUARTER MOON;So;0;ON;;;;;N;;;;; +263F;MERCURY;So;0;ON;;;;;N;;;;; +2640;FEMALE SIGN;So;0;ON;;;;;N;;;;; +2641;EARTH;So;0;ON;;;;;N;;;;; +2642;MALE SIGN;So;0;ON;;;;;N;;;;; +2643;JUPITER;So;0;ON;;;;;N;;;;; +2644;SATURN;So;0;ON;;;;;N;;;;; +2645;URANUS;So;0;ON;;;;;N;;;;; +2646;NEPTUNE;So;0;ON;;;;;N;;;;; +2647;PLUTO;So;0;ON;;;;;N;;;;; +2648;ARIES;So;0;ON;;;;;N;;;;; +2649;TAURUS;So;0;ON;;;;;N;;;;; +264A;GEMINI;So;0;ON;;;;;N;;;;; +264B;CANCER;So;0;ON;;;;;N;;;;; +264C;LEO;So;0;ON;;;;;N;;;;; +264D;VIRGO;So;0;ON;;;;;N;;;;; +264E;LIBRA;So;0;ON;;;;;N;;;;; +264F;SCORPIUS;So;0;ON;;;;;N;;;;; +2650;SAGITTARIUS;So;0;ON;;;;;N;;;;; +2651;CAPRICORN;So;0;ON;;;;;N;;;;; +2652;AQUARIUS;So;0;ON;;;;;N;;;;; +2653;PISCES;So;0;ON;;;;;N;;;;; +2654;WHITE CHESS KING;So;0;ON;;;;;N;;;;; +2655;WHITE CHESS QUEEN;So;0;ON;;;;;N;;;;; +2656;WHITE CHESS ROOK;So;0;ON;;;;;N;;;;; +2657;WHITE CHESS BISHOP;So;0;ON;;;;;N;;;;; +2658;WHITE CHESS KNIGHT;So;0;ON;;;;;N;;;;; +2659;WHITE CHESS PAWN;So;0;ON;;;;;N;;;;; +265A;BLACK CHESS KING;So;0;ON;;;;;N;;;;; +265B;BLACK CHESS QUEEN;So;0;ON;;;;;N;;;;; +265C;BLACK CHESS ROOK;So;0;ON;;;;;N;;;;; +265D;BLACK CHESS BISHOP;So;0;ON;;;;;N;;;;; +265E;BLACK CHESS KNIGHT;So;0;ON;;;;;N;;;;; +265F;BLACK CHESS PAWN;So;0;ON;;;;;N;;;;; +2660;BLACK SPADE SUIT;So;0;ON;;;;;N;;;;; +2661;WHITE HEART SUIT;So;0;ON;;;;;N;;;;; +2662;WHITE DIAMOND SUIT;So;0;ON;;;;;N;;;;; +2663;BLACK CLUB SUIT;So;0;ON;;;;;N;;;;; +2664;WHITE SPADE SUIT;So;0;ON;;;;;N;;;;; +2665;BLACK HEART SUIT;So;0;ON;;;;;N;;;;; +2666;BLACK DIAMOND SUIT;So;0;ON;;;;;N;;;;; +2667;WHITE CLUB SUIT;So;0;ON;;;;;N;;;;; +2668;HOT SPRINGS;So;0;ON;;;;;N;;;;; +2669;QUARTER NOTE;So;0;ON;;;;;N;;;;; +266A;EIGHTH NOTE;So;0;ON;;;;;N;;;;; +266B;BEAMED EIGHTH NOTES;So;0;ON;;;;;N;BARRED EIGHTH NOTES;;;; +266C;BEAMED SIXTEENTH NOTES;So;0;ON;;;;;N;BARRED SIXTEENTH NOTES;;;; +266D;MUSIC FLAT SIGN;So;0;ON;;;;;N;FLAT;;;; +266E;MUSIC NATURAL SIGN;So;0;ON;;;;;N;NATURAL;;;; +266F;MUSIC SHARP SIGN;Sm;0;ON;;;;;N;SHARP;;;; +2670;WEST SYRIAC CROSS;So;0;ON;;;;;N;;;;; +2671;EAST SYRIAC CROSS;So;0;ON;;;;;N;;;;; +2672;UNIVERSAL RECYCLING SYMBOL;So;0;ON;;;;;N;;;;; +2673;RECYCLING SYMBOL FOR TYPE-1 PLASTICS;So;0;ON;;;;;N;;;;; +2674;RECYCLING SYMBOL FOR TYPE-2 PLASTICS;So;0;ON;;;;;N;;;;; +2675;RECYCLING SYMBOL FOR TYPE-3 PLASTICS;So;0;ON;;;;;N;;;;; +2676;RECYCLING SYMBOL FOR TYPE-4 PLASTICS;So;0;ON;;;;;N;;;;; +2677;RECYCLING SYMBOL FOR TYPE-5 PLASTICS;So;0;ON;;;;;N;;;;; +2678;RECYCLING SYMBOL FOR TYPE-6 PLASTICS;So;0;ON;;;;;N;;;;; +2679;RECYCLING SYMBOL FOR TYPE-7 PLASTICS;So;0;ON;;;;;N;;;;; +267A;RECYCLING SYMBOL FOR GENERIC MATERIALS;So;0;ON;;;;;N;;;;; +267B;BLACK UNIVERSAL RECYCLING SYMBOL;So;0;ON;;;;;N;;;;; +267C;RECYCLED PAPER SYMBOL;So;0;ON;;;;;N;;;;; +267D;PARTIALLY-RECYCLED PAPER SYMBOL;So;0;ON;;;;;N;;;;; +267E;PERMANENT PAPER SIGN;So;0;ON;;;;;N;;;;; +267F;WHEELCHAIR SYMBOL;So;0;ON;;;;;N;;;;; +2680;DIE FACE-1;So;0;ON;;;;;N;;;;; +2681;DIE FACE-2;So;0;ON;;;;;N;;;;; +2682;DIE FACE-3;So;0;ON;;;;;N;;;;; +2683;DIE FACE-4;So;0;ON;;;;;N;;;;; +2684;DIE FACE-5;So;0;ON;;;;;N;;;;; +2685;DIE FACE-6;So;0;ON;;;;;N;;;;; +2686;WHITE CIRCLE WITH DOT RIGHT;So;0;ON;;;;;N;;;;; +2687;WHITE CIRCLE WITH TWO DOTS;So;0;ON;;;;;N;;;;; +2688;BLACK CIRCLE WITH WHITE DOT RIGHT;So;0;ON;;;;;N;;;;; +2689;BLACK CIRCLE WITH TWO WHITE DOTS;So;0;ON;;;;;N;;;;; +268A;MONOGRAM FOR YANG;So;0;ON;;;;;N;;;;; +268B;MONOGRAM FOR YIN;So;0;ON;;;;;N;;;;; +268C;DIGRAM FOR GREATER YANG;So;0;ON;;;;;N;;;;; +268D;DIGRAM FOR LESSER YIN;So;0;ON;;;;;N;;;;; +268E;DIGRAM FOR LESSER YANG;So;0;ON;;;;;N;;;;; +268F;DIGRAM FOR GREATER YIN;So;0;ON;;;;;N;;;;; +2690;WHITE FLAG;So;0;ON;;;;;N;;;;; +2691;BLACK FLAG;So;0;ON;;;;;N;;;;; +2692;HAMMER AND PICK;So;0;ON;;;;;N;;;;; +2693;ANCHOR;So;0;ON;;;;;N;;;;; +2694;CROSSED SWORDS;So;0;ON;;;;;N;;;;; +2695;STAFF OF AESCULAPIUS;So;0;ON;;;;;N;;;;; +2696;SCALES;So;0;ON;;;;;N;;;;; +2697;ALEMBIC;So;0;ON;;;;;N;;;;; +2698;FLOWER;So;0;ON;;;;;N;;;;; +2699;GEAR;So;0;ON;;;;;N;;;;; +269A;STAFF OF HERMES;So;0;ON;;;;;N;;;;; +269B;ATOM SYMBOL;So;0;ON;;;;;N;;;;; +269C;FLEUR-DE-LIS;So;0;ON;;;;;N;;;;; +269D;OUTLINED WHITE STAR;So;0;ON;;;;;N;;;;; +269E;THREE LINES CONVERGING RIGHT;So;0;ON;;;;;N;;;;; +269F;THREE LINES CONVERGING LEFT;So;0;ON;;;;;N;;;;; +26A0;WARNING SIGN;So;0;ON;;;;;N;;;;; +26A1;HIGH VOLTAGE SIGN;So;0;ON;;;;;N;;;;; +26A2;DOUBLED FEMALE SIGN;So;0;ON;;;;;N;;;;; +26A3;DOUBLED MALE SIGN;So;0;ON;;;;;N;;;;; +26A4;INTERLOCKED FEMALE AND MALE SIGN;So;0;ON;;;;;N;;;;; +26A5;MALE AND FEMALE SIGN;So;0;ON;;;;;N;;;;; +26A6;MALE WITH STROKE SIGN;So;0;ON;;;;;N;;;;; +26A7;MALE WITH STROKE AND MALE AND FEMALE SIGN;So;0;ON;;;;;N;;;;; +26A8;VERTICAL MALE WITH STROKE SIGN;So;0;ON;;;;;N;;;;; +26A9;HORIZONTAL MALE WITH STROKE SIGN;So;0;ON;;;;;N;;;;; +26AA;MEDIUM WHITE CIRCLE;So;0;ON;;;;;N;;;;; +26AB;MEDIUM BLACK CIRCLE;So;0;ON;;;;;N;;;;; +26AC;MEDIUM SMALL WHITE CIRCLE;So;0;L;;;;;N;;;;; +26AD;MARRIAGE SYMBOL;So;0;ON;;;;;N;;;;; +26AE;DIVORCE SYMBOL;So;0;ON;;;;;N;;;;; +26AF;UNMARRIED PARTNERSHIP SYMBOL;So;0;ON;;;;;N;;;;; +26B0;COFFIN;So;0;ON;;;;;N;;;;; +26B1;FUNERAL URN;So;0;ON;;;;;N;;;;; +26B2;NEUTER;So;0;ON;;;;;N;;;;; +26B3;CERES;So;0;ON;;;;;N;;;;; +26B4;PALLAS;So;0;ON;;;;;N;;;;; +26B5;JUNO;So;0;ON;;;;;N;;;;; +26B6;VESTA;So;0;ON;;;;;N;;;;; +26B7;CHIRON;So;0;ON;;;;;N;;;;; +26B8;BLACK MOON LILITH;So;0;ON;;;;;N;;;;; +26B9;SEXTILE;So;0;ON;;;;;N;;;;; +26BA;SEMISEXTILE;So;0;ON;;;;;N;;;;; +26BB;QUINCUNX;So;0;ON;;;;;N;;;;; +26BC;SESQUIQUADRATE;So;0;ON;;;;;N;;;;; +26BD;SOCCER BALL;So;0;ON;;;;;N;;;;; +26BE;BASEBALL;So;0;ON;;;;;N;;;;; +26BF;SQUARED KEY;So;0;ON;;;;;N;;;;; +26C0;WHITE DRAUGHTS MAN;So;0;ON;;;;;N;;;;; +26C1;WHITE DRAUGHTS KING;So;0;ON;;;;;N;;;;; +26C2;BLACK DRAUGHTS MAN;So;0;ON;;;;;N;;;;; +26C3;BLACK DRAUGHTS KING;So;0;ON;;;;;N;;;;; +26C4;SNOWMAN WITHOUT SNOW;So;0;ON;;;;;N;;;;; +26C5;SUN BEHIND CLOUD;So;0;ON;;;;;N;;;;; +26C6;RAIN;So;0;ON;;;;;N;;;;; +26C7;BLACK SNOWMAN;So;0;ON;;;;;N;;;;; +26C8;THUNDER CLOUD AND RAIN;So;0;ON;;;;;N;;;;; +26C9;TURNED WHITE SHOGI PIECE;So;0;ON;;;;;N;;;;; +26CA;TURNED BLACK SHOGI PIECE;So;0;ON;;;;;N;;;;; +26CB;WHITE DIAMOND IN SQUARE;So;0;ON;;;;;N;;;;; +26CC;CROSSING LANES;So;0;ON;;;;;N;;;;; +26CD;DISABLED CAR;So;0;ON;;;;;N;;;;; +26CE;OPHIUCHUS;So;0;ON;;;;;N;;;;; +26CF;PICK;So;0;ON;;;;;N;;;;; +26D0;CAR SLIDING;So;0;ON;;;;;N;;;;; +26D1;HELMET WITH WHITE CROSS;So;0;ON;;;;;N;;;;; +26D2;CIRCLED CROSSING LANES;So;0;ON;;;;;N;;;;; +26D3;CHAINS;So;0;ON;;;;;N;;;;; +26D4;NO ENTRY;So;0;ON;;;;;N;;;;; +26D5;ALTERNATE ONE-WAY LEFT WAY TRAFFIC;So;0;ON;;;;;N;;;;; +26D6;BLACK TWO-WAY LEFT WAY TRAFFIC;So;0;ON;;;;;N;;;;; +26D7;WHITE TWO-WAY LEFT WAY TRAFFIC;So;0;ON;;;;;N;;;;; +26D8;BLACK LEFT LANE MERGE;So;0;ON;;;;;N;;;;; +26D9;WHITE LEFT LANE MERGE;So;0;ON;;;;;N;;;;; +26DA;DRIVE SLOW SIGN;So;0;ON;;;;;N;;;;; +26DB;HEAVY WHITE DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;;;;; +26DC;LEFT CLOSED ENTRY;So;0;ON;;;;;N;;;;; +26DD;SQUARED SALTIRE;So;0;ON;;;;;N;;;;; +26DE;FALLING DIAGONAL IN WHITE CIRCLE IN BLACK SQUARE;So;0;ON;;;;;N;;;;; +26DF;BLACK TRUCK;So;0;ON;;;;;N;;;;; +26E0;RESTRICTED LEFT ENTRY-1;So;0;ON;;;;;N;;;;; +26E1;RESTRICTED LEFT ENTRY-2;So;0;ON;;;;;N;;;;; +26E2;ASTRONOMICAL SYMBOL FOR URANUS;So;0;ON;;;;;N;;;;; +26E3;HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE;So;0;ON;;;;;N;;;;; +26E4;PENTAGRAM;So;0;ON;;;;;N;;;;; +26E5;RIGHT-HANDED INTERLACED PENTAGRAM;So;0;ON;;;;;N;;;;; +26E6;LEFT-HANDED INTERLACED PENTAGRAM;So;0;ON;;;;;N;;;;; +26E7;INVERTED PENTAGRAM;So;0;ON;;;;;N;;;;; +26E8;BLACK CROSS ON SHIELD;So;0;ON;;;;;N;;;;; +26E9;SHINTO SHRINE;So;0;ON;;;;;N;;;;; +26EA;CHURCH;So;0;ON;;;;;N;;;;; +26EB;CASTLE;So;0;ON;;;;;N;;;;; +26EC;HISTORIC SITE;So;0;ON;;;;;N;;;;; +26ED;GEAR WITHOUT HUB;So;0;ON;;;;;N;;;;; +26EE;GEAR WITH HANDLES;So;0;ON;;;;;N;;;;; +26EF;MAP SYMBOL FOR LIGHTHOUSE;So;0;ON;;;;;N;;;;; +26F0;MOUNTAIN;So;0;ON;;;;;N;;;;; +26F1;UMBRELLA ON GROUND;So;0;ON;;;;;N;;;;; +26F2;FOUNTAIN;So;0;ON;;;;;N;;;;; +26F3;FLAG IN HOLE;So;0;ON;;;;;N;;;;; +26F4;FERRY;So;0;ON;;;;;N;;;;; +26F5;SAILBOAT;So;0;ON;;;;;N;;;;; +26F6;SQUARE FOUR CORNERS;So;0;ON;;;;;N;;;;; +26F7;SKIER;So;0;ON;;;;;N;;;;; +26F8;ICE SKATE;So;0;ON;;;;;N;;;;; +26F9;PERSON WITH BALL;So;0;ON;;;;;N;;;;; +26FA;TENT;So;0;ON;;;;;N;;;;; +26FB;JAPANESE BANK SYMBOL;So;0;ON;;;;;N;;;;; +26FC;HEADSTONE GRAVEYARD SYMBOL;So;0;ON;;;;;N;;;;; +26FD;FUEL PUMP;So;0;ON;;;;;N;;;;; +26FE;CUP ON BLACK SQUARE;So;0;ON;;;;;N;;;;; +26FF;WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE;So;0;ON;;;;;N;;;;; +2700;BLACK SAFETY SCISSORS;So;0;ON;;;;;N;;;;; +2701;UPPER BLADE SCISSORS;So;0;ON;;;;;N;;;;; +2702;BLACK SCISSORS;So;0;ON;;;;;N;;;;; +2703;LOWER BLADE SCISSORS;So;0;ON;;;;;N;;;;; +2704;WHITE SCISSORS;So;0;ON;;;;;N;;;;; +2705;WHITE HEAVY CHECK MARK;So;0;ON;;;;;N;;;;; +2706;TELEPHONE LOCATION SIGN;So;0;ON;;;;;N;;;;; +2707;TAPE DRIVE;So;0;ON;;;;;N;;;;; +2708;AIRPLANE;So;0;ON;;;;;N;;;;; +2709;ENVELOPE;So;0;ON;;;;;N;;;;; +270A;RAISED FIST;So;0;ON;;;;;N;;;;; +270B;RAISED HAND;So;0;ON;;;;;N;;;;; +270C;VICTORY HAND;So;0;ON;;;;;N;;;;; +270D;WRITING HAND;So;0;ON;;;;;N;;;;; +270E;LOWER RIGHT PENCIL;So;0;ON;;;;;N;;;;; +270F;PENCIL;So;0;ON;;;;;N;;;;; +2710;UPPER RIGHT PENCIL;So;0;ON;;;;;N;;;;; +2711;WHITE NIB;So;0;ON;;;;;N;;;;; +2712;BLACK NIB;So;0;ON;;;;;N;;;;; +2713;CHECK MARK;So;0;ON;;;;;N;;;;; +2714;HEAVY CHECK MARK;So;0;ON;;;;;N;;;;; +2715;MULTIPLICATION X;So;0;ON;;;;;N;;;;; +2716;HEAVY MULTIPLICATION X;So;0;ON;;;;;N;;;;; +2717;BALLOT X;So;0;ON;;;;;N;;;;; +2718;HEAVY BALLOT X;So;0;ON;;;;;N;;;;; +2719;OUTLINED GREEK CROSS;So;0;ON;;;;;N;;;;; +271A;HEAVY GREEK CROSS;So;0;ON;;;;;N;;;;; +271B;OPEN CENTRE CROSS;So;0;ON;;;;;N;OPEN CENTER CROSS;;;; +271C;HEAVY OPEN CENTRE CROSS;So;0;ON;;;;;N;HEAVY OPEN CENTER CROSS;;;; +271D;LATIN CROSS;So;0;ON;;;;;N;;;;; +271E;SHADOWED WHITE LATIN CROSS;So;0;ON;;;;;N;;;;; +271F;OUTLINED LATIN CROSS;So;0;ON;;;;;N;;;;; +2720;MALTESE CROSS;So;0;ON;;;;;N;;;;; +2721;STAR OF DAVID;So;0;ON;;;;;N;;;;; +2722;FOUR TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +2723;FOUR BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +2724;HEAVY FOUR BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +2725;FOUR CLUB-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +2726;BLACK FOUR POINTED STAR;So;0;ON;;;;;N;;;;; +2727;WHITE FOUR POINTED STAR;So;0;ON;;;;;N;;;;; +2728;SPARKLES;So;0;ON;;;;;N;;;;; +2729;STRESS OUTLINED WHITE STAR;So;0;ON;;;;;N;;;;; +272A;CIRCLED WHITE STAR;So;0;ON;;;;;N;;;;; +272B;OPEN CENTRE BLACK STAR;So;0;ON;;;;;N;OPEN CENTER BLACK STAR;;;; +272C;BLACK CENTRE WHITE STAR;So;0;ON;;;;;N;BLACK CENTER WHITE STAR;;;; +272D;OUTLINED BLACK STAR;So;0;ON;;;;;N;;;;; +272E;HEAVY OUTLINED BLACK STAR;So;0;ON;;;;;N;;;;; +272F;PINWHEEL STAR;So;0;ON;;;;;N;;;;; +2730;SHADOWED WHITE STAR;So;0;ON;;;;;N;;;;; +2731;HEAVY ASTERISK;So;0;ON;;;;;N;;;;; +2732;OPEN CENTRE ASTERISK;So;0;ON;;;;;N;OPEN CENTER ASTERISK;;;; +2733;EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +2734;EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +2735;EIGHT POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; +2736;SIX POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +2737;EIGHT POINTED RECTILINEAR BLACK STAR;So;0;ON;;;;;N;;;;; +2738;HEAVY EIGHT POINTED RECTILINEAR BLACK STAR;So;0;ON;;;;;N;;;;; +2739;TWELVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +273A;SIXTEEN POINTED ASTERISK;So;0;ON;;;;;N;;;;; +273B;TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +273C;OPEN CENTRE TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;OPEN CENTER TEARDROP-SPOKED ASTERISK;;;; +273D;HEAVY TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +273E;SIX PETALLED BLACK AND WHITE FLORETTE;So;0;ON;;;;;N;;;;; +273F;BLACK FLORETTE;So;0;ON;;;;;N;;;;; +2740;WHITE FLORETTE;So;0;ON;;;;;N;;;;; +2741;EIGHT PETALLED OUTLINED BLACK FLORETTE;So;0;ON;;;;;N;;;;; +2742;CIRCLED OPEN CENTRE EIGHT POINTED STAR;So;0;ON;;;;;N;CIRCLED OPEN CENTER EIGHT POINTED STAR;;;; +2743;HEAVY TEARDROP-SPOKED PINWHEEL ASTERISK;So;0;ON;;;;;N;;;;; +2744;SNOWFLAKE;So;0;ON;;;;;N;;;;; +2745;TIGHT TRIFOLIATE SNOWFLAKE;So;0;ON;;;;;N;;;;; +2746;HEAVY CHEVRON SNOWFLAKE;So;0;ON;;;;;N;;;;; +2747;SPARKLE;So;0;ON;;;;;N;;;;; +2748;HEAVY SPARKLE;So;0;ON;;;;;N;;;;; +2749;BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +274A;EIGHT TEARDROP-SPOKED PROPELLER ASTERISK;So;0;ON;;;;;N;;;;; +274B;HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK;So;0;ON;;;;;N;;;;; +274C;CROSS MARK;So;0;ON;;;;;N;;;;; +274D;SHADOWED WHITE CIRCLE;So;0;ON;;;;;N;;;;; +274E;NEGATIVE SQUARED CROSS MARK;So;0;ON;;;;;N;;;;; +274F;LOWER RIGHT DROP-SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;; +2750;UPPER RIGHT DROP-SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;; +2751;LOWER RIGHT SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;; +2752;UPPER RIGHT SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;; +2753;BLACK QUESTION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +2754;WHITE QUESTION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +2755;WHITE EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +2756;BLACK DIAMOND MINUS WHITE X;So;0;ON;;;;;N;;;;; +2757;HEAVY EXCLAMATION MARK SYMBOL;So;0;ON;;;;;N;;;;; +2758;LIGHT VERTICAL BAR;So;0;ON;;;;;N;;;;; +2759;MEDIUM VERTICAL BAR;So;0;ON;;;;;N;;;;; +275A;HEAVY VERTICAL BAR;So;0;ON;;;;;N;;;;; +275B;HEAVY SINGLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +275C;HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +275D;HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +275E;HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +275F;HEAVY LOW SINGLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +2760;HEAVY LOW DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +2761;CURVED STEM PARAGRAPH SIGN ORNAMENT;So;0;ON;;;;;N;;;;; +2762;HEAVY EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +2763;HEAVY HEART EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +2764;HEAVY BLACK HEART;So;0;ON;;;;;N;;;;; +2765;ROTATED HEAVY BLACK HEART BULLET;So;0;ON;;;;;N;;;;; +2766;FLORAL HEART;So;0;ON;;;;;N;;;;; +2767;ROTATED FLORAL HEART BULLET;So;0;ON;;;;;N;;;;; +2768;MEDIUM LEFT PARENTHESIS ORNAMENT;Ps;0;ON;;;;;Y;;;;; +2769;MEDIUM RIGHT PARENTHESIS ORNAMENT;Pe;0;ON;;;;;Y;;;;; +276A;MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT;Ps;0;ON;;;;;Y;;;;; +276B;MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT;Pe;0;ON;;;;;Y;;;;; +276C;MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;; +276D;MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;; +276E;HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT;Ps;0;ON;;;;;Y;;;;; +276F;HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT;Pe;0;ON;;;;;Y;;;;; +2770;HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;; +2771;HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;; +2772;LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;; +2773;LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;; +2774;MEDIUM LEFT CURLY BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;; +2775;MEDIUM RIGHT CURLY BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;; +2776;DINGBAT NEGATIVE CIRCLED DIGIT ONE;No;0;ON;;;1;1;N;INVERSE CIRCLED DIGIT ONE;;;; +2777;DINGBAT NEGATIVE CIRCLED DIGIT TWO;No;0;ON;;;2;2;N;INVERSE CIRCLED DIGIT TWO;;;; +2778;DINGBAT NEGATIVE CIRCLED DIGIT THREE;No;0;ON;;;3;3;N;INVERSE CIRCLED DIGIT THREE;;;; +2779;DINGBAT NEGATIVE CIRCLED DIGIT FOUR;No;0;ON;;;4;4;N;INVERSE CIRCLED DIGIT FOUR;;;; +277A;DINGBAT NEGATIVE CIRCLED DIGIT FIVE;No;0;ON;;;5;5;N;INVERSE CIRCLED DIGIT FIVE;;;; +277B;DINGBAT NEGATIVE CIRCLED DIGIT SIX;No;0;ON;;;6;6;N;INVERSE CIRCLED DIGIT SIX;;;; +277C;DINGBAT NEGATIVE CIRCLED DIGIT SEVEN;No;0;ON;;;7;7;N;INVERSE CIRCLED DIGIT SEVEN;;;; +277D;DINGBAT NEGATIVE CIRCLED DIGIT EIGHT;No;0;ON;;;8;8;N;INVERSE CIRCLED DIGIT EIGHT;;;; +277E;DINGBAT NEGATIVE CIRCLED DIGIT NINE;No;0;ON;;;9;9;N;INVERSE CIRCLED DIGIT NINE;;;; +277F;DINGBAT NEGATIVE CIRCLED NUMBER TEN;No;0;ON;;;;10;N;INVERSE CIRCLED NUMBER TEN;;;; +2780;DINGBAT CIRCLED SANS-SERIF DIGIT ONE;No;0;ON;;;1;1;N;CIRCLED SANS-SERIF DIGIT ONE;;;; +2781;DINGBAT CIRCLED SANS-SERIF DIGIT TWO;No;0;ON;;;2;2;N;CIRCLED SANS-SERIF DIGIT TWO;;;; +2782;DINGBAT CIRCLED SANS-SERIF DIGIT THREE;No;0;ON;;;3;3;N;CIRCLED SANS-SERIF DIGIT THREE;;;; +2783;DINGBAT CIRCLED SANS-SERIF DIGIT FOUR;No;0;ON;;;4;4;N;CIRCLED SANS-SERIF DIGIT FOUR;;;; +2784;DINGBAT CIRCLED SANS-SERIF DIGIT FIVE;No;0;ON;;;5;5;N;CIRCLED SANS-SERIF DIGIT FIVE;;;; +2785;DINGBAT CIRCLED SANS-SERIF DIGIT SIX;No;0;ON;;;6;6;N;CIRCLED SANS-SERIF DIGIT SIX;;;; +2786;DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN;No;0;ON;;;7;7;N;CIRCLED SANS-SERIF DIGIT SEVEN;;;; +2787;DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT;No;0;ON;;;8;8;N;CIRCLED SANS-SERIF DIGIT EIGHT;;;; +2788;DINGBAT CIRCLED SANS-SERIF DIGIT NINE;No;0;ON;;;9;9;N;CIRCLED SANS-SERIF DIGIT NINE;;;; +2789;DINGBAT CIRCLED SANS-SERIF NUMBER TEN;No;0;ON;;;;10;N;CIRCLED SANS-SERIF NUMBER TEN;;;; +278A;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ONE;No;0;ON;;;1;1;N;INVERSE CIRCLED SANS-SERIF DIGIT ONE;;;; +278B;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT TWO;No;0;ON;;;2;2;N;INVERSE CIRCLED SANS-SERIF DIGIT TWO;;;; +278C;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT THREE;No;0;ON;;;3;3;N;INVERSE CIRCLED SANS-SERIF DIGIT THREE;;;; +278D;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FOUR;No;0;ON;;;4;4;N;INVERSE CIRCLED SANS-SERIF DIGIT FOUR;;;; +278E;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FIVE;No;0;ON;;;5;5;N;INVERSE CIRCLED SANS-SERIF DIGIT FIVE;;;; +278F;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SIX;No;0;ON;;;6;6;N;INVERSE CIRCLED SANS-SERIF DIGIT SIX;;;; +2790;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SEVEN;No;0;ON;;;7;7;N;INVERSE CIRCLED SANS-SERIF DIGIT SEVEN;;;; +2791;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT EIGHT;No;0;ON;;;8;8;N;INVERSE CIRCLED SANS-SERIF DIGIT EIGHT;;;; +2792;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT NINE;No;0;ON;;;9;9;N;INVERSE CIRCLED SANS-SERIF DIGIT NINE;;;; +2793;DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN;No;0;ON;;;;10;N;INVERSE CIRCLED SANS-SERIF NUMBER TEN;;;; +2794;HEAVY WIDE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY WIDE-HEADED RIGHT ARROW;;;; +2795;HEAVY PLUS SIGN;So;0;ON;;;;;N;;;;; +2796;HEAVY MINUS SIGN;So;0;ON;;;;;N;;;;; +2797;HEAVY DIVISION SIGN;So;0;ON;;;;;N;;;;; +2798;HEAVY SOUTH EAST ARROW;So;0;ON;;;;;N;HEAVY LOWER RIGHT ARROW;;;; +2799;HEAVY RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY RIGHT ARROW;;;; +279A;HEAVY NORTH EAST ARROW;So;0;ON;;;;;N;HEAVY UPPER RIGHT ARROW;;;; +279B;DRAFTING POINT RIGHTWARDS ARROW;So;0;ON;;;;;N;DRAFTING POINT RIGHT ARROW;;;; +279C;HEAVY ROUND-TIPPED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY ROUND-TIPPED RIGHT ARROW;;;; +279D;TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;TRIANGLE-HEADED RIGHT ARROW;;;; +279E;HEAVY TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY TRIANGLE-HEADED RIGHT ARROW;;;; +279F;DASHED TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;DASHED TRIANGLE-HEADED RIGHT ARROW;;;; +27A0;HEAVY DASHED TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY DASHED TRIANGLE-HEADED RIGHT ARROW;;;; +27A1;BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;BLACK RIGHT ARROW;;;; +27A2;THREE-D TOP-LIGHTED RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;THREE-D TOP-LIGHTED RIGHT ARROWHEAD;;;; +27A3;THREE-D BOTTOM-LIGHTED RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;THREE-D BOTTOM-LIGHTED RIGHT ARROWHEAD;;;; +27A4;BLACK RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;BLACK RIGHT ARROWHEAD;;;; +27A5;HEAVY BLACK CURVED DOWNWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK CURVED DOWN AND RIGHT ARROW;;;; +27A6;HEAVY BLACK CURVED UPWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK CURVED UP AND RIGHT ARROW;;;; +27A7;SQUAT BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;SQUAT BLACK RIGHT ARROW;;;; +27A8;HEAVY CONCAVE-POINTED BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY CONCAVE-POINTED BLACK RIGHT ARROW;;;; +27A9;RIGHT-SHADED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;RIGHT-SHADED WHITE RIGHT ARROW;;;; +27AA;LEFT-SHADED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;LEFT-SHADED WHITE RIGHT ARROW;;;; +27AB;BACK-TILTED SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;BACK-TILTED SHADOWED WHITE RIGHT ARROW;;;; +27AC;FRONT-TILTED SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;FRONT-TILTED SHADOWED WHITE RIGHT ARROW;;;; +27AD;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHT ARROW;;;; +27AE;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHT ARROW;;;; +27AF;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHT ARROW;;;; +27B0;CURLY LOOP;So;0;ON;;;;;N;;;;; +27B1;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHT ARROW;;;; +27B2;CIRCLED HEAVY WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;CIRCLED HEAVY WHITE RIGHT ARROW;;;; +27B3;WHITE-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;WHITE-FEATHERED RIGHT ARROW;;;; +27B4;BLACK-FEATHERED SOUTH EAST ARROW;So;0;ON;;;;;N;BLACK-FEATHERED LOWER RIGHT ARROW;;;; +27B5;BLACK-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;BLACK-FEATHERED RIGHT ARROW;;;; +27B6;BLACK-FEATHERED NORTH EAST ARROW;So;0;ON;;;;;N;BLACK-FEATHERED UPPER RIGHT ARROW;;;; +27B7;HEAVY BLACK-FEATHERED SOUTH EAST ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED LOWER RIGHT ARROW;;;; +27B8;HEAVY BLACK-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED RIGHT ARROW;;;; +27B9;HEAVY BLACK-FEATHERED NORTH EAST ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED UPPER RIGHT ARROW;;;; +27BA;TEARDROP-BARBED RIGHTWARDS ARROW;So;0;ON;;;;;N;TEARDROP-BARBED RIGHT ARROW;;;; +27BB;HEAVY TEARDROP-SHANKED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY TEARDROP-SHANKED RIGHT ARROW;;;; +27BC;WEDGE-TAILED RIGHTWARDS ARROW;So;0;ON;;;;;N;WEDGE-TAILED RIGHT ARROW;;;; +27BD;HEAVY WEDGE-TAILED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY WEDGE-TAILED RIGHT ARROW;;;; +27BE;OPEN-OUTLINED RIGHTWARDS ARROW;So;0;ON;;;;;N;OPEN-OUTLINED RIGHT ARROW;;;; +27BF;DOUBLE CURLY LOOP;So;0;ON;;;;;N;;;;; +27C0;THREE DIMENSIONAL ANGLE;Sm;0;ON;;;;;Y;;;;; +27C1;WHITE TRIANGLE CONTAINING SMALL WHITE TRIANGLE;Sm;0;ON;;;;;N;;;;; +27C2;PERPENDICULAR;Sm;0;ON;;;;;N;;;;; +27C3;OPEN SUBSET;Sm;0;ON;;;;;Y;;;;; +27C4;OPEN SUPERSET;Sm;0;ON;;;;;Y;;;;; +27C5;LEFT S-SHAPED BAG DELIMITER;Ps;0;ON;;;;;Y;;;;; +27C6;RIGHT S-SHAPED BAG DELIMITER;Pe;0;ON;;;;;Y;;;;; +27C7;OR WITH DOT INSIDE;Sm;0;ON;;;;;N;;;;; +27C8;REVERSE SOLIDUS PRECEDING SUBSET;Sm;0;ON;;;;;Y;;;;; +27C9;SUPERSET PRECEDING SOLIDUS;Sm;0;ON;;;;;Y;;;;; +27CA;VERTICAL BAR WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; +27CB;MATHEMATICAL RISING DIAGONAL;Sm;0;ON;;;;;Y;;;;; +27CC;LONG DIVISION;Sm;0;ON;;;;;Y;;;;; +27CD;MATHEMATICAL FALLING DIAGONAL;Sm;0;ON;;;;;Y;;;;; +27CE;SQUARED LOGICAL AND;Sm;0;ON;;;;;N;;;;; +27CF;SQUARED LOGICAL OR;Sm;0;ON;;;;;N;;;;; +27D0;WHITE DIAMOND WITH CENTRED DOT;Sm;0;ON;;;;;N;;;;; +27D1;AND WITH DOT;Sm;0;ON;;;;;N;;;;; +27D2;ELEMENT OF OPENING UPWARDS;Sm;0;ON;;;;;N;;;;; +27D3;LOWER RIGHT CORNER WITH DOT;Sm;0;ON;;;;;Y;;;;; +27D4;UPPER LEFT CORNER WITH DOT;Sm;0;ON;;;;;Y;;;;; +27D5;LEFT OUTER JOIN;Sm;0;ON;;;;;Y;;;;; +27D6;RIGHT OUTER JOIN;Sm;0;ON;;;;;Y;;;;; +27D7;FULL OUTER JOIN;Sm;0;ON;;;;;N;;;;; +27D8;LARGE UP TACK;Sm;0;ON;;;;;N;;;;; +27D9;LARGE DOWN TACK;Sm;0;ON;;;;;N;;;;; +27DA;LEFT AND RIGHT DOUBLE TURNSTILE;Sm;0;ON;;;;;N;;;;; +27DB;LEFT AND RIGHT TACK;Sm;0;ON;;;;;N;;;;; +27DC;LEFT MULTIMAP;Sm;0;ON;;;;;Y;;;;; +27DD;LONG RIGHT TACK;Sm;0;ON;;;;;Y;;;;; +27DE;LONG LEFT TACK;Sm;0;ON;;;;;Y;;;;; +27DF;UP TACK WITH CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;; +27E0;LOZENGE DIVIDED BY HORIZONTAL RULE;Sm;0;ON;;;;;N;;;;; +27E1;WHITE CONCAVE-SIDED DIAMOND;Sm;0;ON;;;;;N;;;;; +27E2;WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK;Sm;0;ON;;;;;Y;;;;; +27E3;WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK;Sm;0;ON;;;;;Y;;;;; +27E4;WHITE SQUARE WITH LEFTWARDS TICK;Sm;0;ON;;;;;Y;;;;; +27E5;WHITE SQUARE WITH RIGHTWARDS TICK;Sm;0;ON;;;;;Y;;;;; +27E6;MATHEMATICAL LEFT WHITE SQUARE BRACKET;Ps;0;ON;;;;;Y;;;;; +27E7;MATHEMATICAL RIGHT WHITE SQUARE BRACKET;Pe;0;ON;;;;;Y;;;;; +27E8;MATHEMATICAL LEFT ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;; +27E9;MATHEMATICAL RIGHT ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;; +27EA;MATHEMATICAL LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;; +27EB;MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;; +27EC;MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;;;;; +27ED;MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;;;;; +27EE;MATHEMATICAL LEFT FLATTENED PARENTHESIS;Ps;0;ON;;;;;Y;;;;; +27EF;MATHEMATICAL RIGHT FLATTENED PARENTHESIS;Pe;0;ON;;;;;Y;;;;; +27F0;UPWARDS QUADRUPLE ARROW;Sm;0;ON;;;;;N;;;;; +27F1;DOWNWARDS QUADRUPLE ARROW;Sm;0;ON;;;;;N;;;;; +27F2;ANTICLOCKWISE GAPPED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;; +27F3;CLOCKWISE GAPPED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;; +27F4;RIGHT ARROW WITH CIRCLED PLUS;Sm;0;ON;;;;;N;;;;; +27F5;LONG LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +27F6;LONG RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +27F7;LONG LEFT RIGHT ARROW;Sm;0;ON;;;;;N;;;;; +27F8;LONG LEFTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;;;;; +27F9;LONG RIGHTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;;;;; +27FA;LONG LEFT RIGHT DOUBLE ARROW;Sm;0;ON;;;;;N;;;;; +27FB;LONG LEFTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +27FC;LONG RIGHTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +27FD;LONG LEFTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +27FE;LONG RIGHTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +27FF;LONG RIGHTWARDS SQUIGGLE ARROW;Sm;0;ON;;;;;N;;;;; +2800;BRAILLE PATTERN BLANK;So;0;L;;;;;N;;;;; +2801;BRAILLE PATTERN DOTS-1;So;0;L;;;;;N;;;;; +2802;BRAILLE PATTERN DOTS-2;So;0;L;;;;;N;;;;; +2803;BRAILLE PATTERN DOTS-12;So;0;L;;;;;N;;;;; +2804;BRAILLE PATTERN DOTS-3;So;0;L;;;;;N;;;;; +2805;BRAILLE PATTERN DOTS-13;So;0;L;;;;;N;;;;; +2806;BRAILLE PATTERN DOTS-23;So;0;L;;;;;N;;;;; +2807;BRAILLE PATTERN DOTS-123;So;0;L;;;;;N;;;;; +2808;BRAILLE PATTERN DOTS-4;So;0;L;;;;;N;;;;; +2809;BRAILLE PATTERN DOTS-14;So;0;L;;;;;N;;;;; +280A;BRAILLE PATTERN DOTS-24;So;0;L;;;;;N;;;;; +280B;BRAILLE PATTERN DOTS-124;So;0;L;;;;;N;;;;; +280C;BRAILLE PATTERN DOTS-34;So;0;L;;;;;N;;;;; +280D;BRAILLE PATTERN DOTS-134;So;0;L;;;;;N;;;;; +280E;BRAILLE PATTERN DOTS-234;So;0;L;;;;;N;;;;; +280F;BRAILLE PATTERN DOTS-1234;So;0;L;;;;;N;;;;; +2810;BRAILLE PATTERN DOTS-5;So;0;L;;;;;N;;;;; +2811;BRAILLE PATTERN DOTS-15;So;0;L;;;;;N;;;;; +2812;BRAILLE PATTERN DOTS-25;So;0;L;;;;;N;;;;; +2813;BRAILLE PATTERN DOTS-125;So;0;L;;;;;N;;;;; +2814;BRAILLE PATTERN DOTS-35;So;0;L;;;;;N;;;;; +2815;BRAILLE PATTERN DOTS-135;So;0;L;;;;;N;;;;; +2816;BRAILLE PATTERN DOTS-235;So;0;L;;;;;N;;;;; +2817;BRAILLE PATTERN DOTS-1235;So;0;L;;;;;N;;;;; +2818;BRAILLE PATTERN DOTS-45;So;0;L;;;;;N;;;;; +2819;BRAILLE PATTERN DOTS-145;So;0;L;;;;;N;;;;; +281A;BRAILLE PATTERN DOTS-245;So;0;L;;;;;N;;;;; +281B;BRAILLE PATTERN DOTS-1245;So;0;L;;;;;N;;;;; +281C;BRAILLE PATTERN DOTS-345;So;0;L;;;;;N;;;;; +281D;BRAILLE PATTERN DOTS-1345;So;0;L;;;;;N;;;;; +281E;BRAILLE PATTERN DOTS-2345;So;0;L;;;;;N;;;;; +281F;BRAILLE PATTERN DOTS-12345;So;0;L;;;;;N;;;;; +2820;BRAILLE PATTERN DOTS-6;So;0;L;;;;;N;;;;; +2821;BRAILLE PATTERN DOTS-16;So;0;L;;;;;N;;;;; +2822;BRAILLE PATTERN DOTS-26;So;0;L;;;;;N;;;;; +2823;BRAILLE PATTERN DOTS-126;So;0;L;;;;;N;;;;; +2824;BRAILLE PATTERN DOTS-36;So;0;L;;;;;N;;;;; +2825;BRAILLE PATTERN DOTS-136;So;0;L;;;;;N;;;;; +2826;BRAILLE PATTERN DOTS-236;So;0;L;;;;;N;;;;; +2827;BRAILLE PATTERN DOTS-1236;So;0;L;;;;;N;;;;; +2828;BRAILLE PATTERN DOTS-46;So;0;L;;;;;N;;;;; +2829;BRAILLE PATTERN DOTS-146;So;0;L;;;;;N;;;;; +282A;BRAILLE PATTERN DOTS-246;So;0;L;;;;;N;;;;; +282B;BRAILLE PATTERN DOTS-1246;So;0;L;;;;;N;;;;; +282C;BRAILLE PATTERN DOTS-346;So;0;L;;;;;N;;;;; +282D;BRAILLE PATTERN DOTS-1346;So;0;L;;;;;N;;;;; +282E;BRAILLE PATTERN DOTS-2346;So;0;L;;;;;N;;;;; +282F;BRAILLE PATTERN DOTS-12346;So;0;L;;;;;N;;;;; +2830;BRAILLE PATTERN DOTS-56;So;0;L;;;;;N;;;;; +2831;BRAILLE PATTERN DOTS-156;So;0;L;;;;;N;;;;; +2832;BRAILLE PATTERN DOTS-256;So;0;L;;;;;N;;;;; +2833;BRAILLE PATTERN DOTS-1256;So;0;L;;;;;N;;;;; +2834;BRAILLE PATTERN DOTS-356;So;0;L;;;;;N;;;;; +2835;BRAILLE PATTERN DOTS-1356;So;0;L;;;;;N;;;;; +2836;BRAILLE PATTERN DOTS-2356;So;0;L;;;;;N;;;;; +2837;BRAILLE PATTERN DOTS-12356;So;0;L;;;;;N;;;;; +2838;BRAILLE PATTERN DOTS-456;So;0;L;;;;;N;;;;; +2839;BRAILLE PATTERN DOTS-1456;So;0;L;;;;;N;;;;; +283A;BRAILLE PATTERN DOTS-2456;So;0;L;;;;;N;;;;; +283B;BRAILLE PATTERN DOTS-12456;So;0;L;;;;;N;;;;; +283C;BRAILLE PATTERN DOTS-3456;So;0;L;;;;;N;;;;; +283D;BRAILLE PATTERN DOTS-13456;So;0;L;;;;;N;;;;; +283E;BRAILLE PATTERN DOTS-23456;So;0;L;;;;;N;;;;; +283F;BRAILLE PATTERN DOTS-123456;So;0;L;;;;;N;;;;; +2840;BRAILLE PATTERN DOTS-7;So;0;L;;;;;N;;;;; +2841;BRAILLE PATTERN DOTS-17;So;0;L;;;;;N;;;;; +2842;BRAILLE PATTERN DOTS-27;So;0;L;;;;;N;;;;; +2843;BRAILLE PATTERN DOTS-127;So;0;L;;;;;N;;;;; +2844;BRAILLE PATTERN DOTS-37;So;0;L;;;;;N;;;;; +2845;BRAILLE PATTERN DOTS-137;So;0;L;;;;;N;;;;; +2846;BRAILLE PATTERN DOTS-237;So;0;L;;;;;N;;;;; +2847;BRAILLE PATTERN DOTS-1237;So;0;L;;;;;N;;;;; +2848;BRAILLE PATTERN DOTS-47;So;0;L;;;;;N;;;;; +2849;BRAILLE PATTERN DOTS-147;So;0;L;;;;;N;;;;; +284A;BRAILLE PATTERN DOTS-247;So;0;L;;;;;N;;;;; +284B;BRAILLE PATTERN DOTS-1247;So;0;L;;;;;N;;;;; +284C;BRAILLE PATTERN DOTS-347;So;0;L;;;;;N;;;;; +284D;BRAILLE PATTERN DOTS-1347;So;0;L;;;;;N;;;;; +284E;BRAILLE PATTERN DOTS-2347;So;0;L;;;;;N;;;;; +284F;BRAILLE PATTERN DOTS-12347;So;0;L;;;;;N;;;;; +2850;BRAILLE PATTERN DOTS-57;So;0;L;;;;;N;;;;; +2851;BRAILLE PATTERN DOTS-157;So;0;L;;;;;N;;;;; +2852;BRAILLE PATTERN DOTS-257;So;0;L;;;;;N;;;;; +2853;BRAILLE PATTERN DOTS-1257;So;0;L;;;;;N;;;;; +2854;BRAILLE PATTERN DOTS-357;So;0;L;;;;;N;;;;; +2855;BRAILLE PATTERN DOTS-1357;So;0;L;;;;;N;;;;; +2856;BRAILLE PATTERN DOTS-2357;So;0;L;;;;;N;;;;; +2857;BRAILLE PATTERN DOTS-12357;So;0;L;;;;;N;;;;; +2858;BRAILLE PATTERN DOTS-457;So;0;L;;;;;N;;;;; +2859;BRAILLE PATTERN DOTS-1457;So;0;L;;;;;N;;;;; +285A;BRAILLE PATTERN DOTS-2457;So;0;L;;;;;N;;;;; +285B;BRAILLE PATTERN DOTS-12457;So;0;L;;;;;N;;;;; +285C;BRAILLE PATTERN DOTS-3457;So;0;L;;;;;N;;;;; +285D;BRAILLE PATTERN DOTS-13457;So;0;L;;;;;N;;;;; +285E;BRAILLE PATTERN DOTS-23457;So;0;L;;;;;N;;;;; +285F;BRAILLE PATTERN DOTS-123457;So;0;L;;;;;N;;;;; +2860;BRAILLE PATTERN DOTS-67;So;0;L;;;;;N;;;;; +2861;BRAILLE PATTERN DOTS-167;So;0;L;;;;;N;;;;; +2862;BRAILLE PATTERN DOTS-267;So;0;L;;;;;N;;;;; +2863;BRAILLE PATTERN DOTS-1267;So;0;L;;;;;N;;;;; +2864;BRAILLE PATTERN DOTS-367;So;0;L;;;;;N;;;;; +2865;BRAILLE PATTERN DOTS-1367;So;0;L;;;;;N;;;;; +2866;BRAILLE PATTERN DOTS-2367;So;0;L;;;;;N;;;;; +2867;BRAILLE PATTERN DOTS-12367;So;0;L;;;;;N;;;;; +2868;BRAILLE PATTERN DOTS-467;So;0;L;;;;;N;;;;; +2869;BRAILLE PATTERN DOTS-1467;So;0;L;;;;;N;;;;; +286A;BRAILLE PATTERN DOTS-2467;So;0;L;;;;;N;;;;; +286B;BRAILLE PATTERN DOTS-12467;So;0;L;;;;;N;;;;; +286C;BRAILLE PATTERN DOTS-3467;So;0;L;;;;;N;;;;; +286D;BRAILLE PATTERN DOTS-13467;So;0;L;;;;;N;;;;; +286E;BRAILLE PATTERN DOTS-23467;So;0;L;;;;;N;;;;; +286F;BRAILLE PATTERN DOTS-123467;So;0;L;;;;;N;;;;; +2870;BRAILLE PATTERN DOTS-567;So;0;L;;;;;N;;;;; +2871;BRAILLE PATTERN DOTS-1567;So;0;L;;;;;N;;;;; +2872;BRAILLE PATTERN DOTS-2567;So;0;L;;;;;N;;;;; +2873;BRAILLE PATTERN DOTS-12567;So;0;L;;;;;N;;;;; +2874;BRAILLE PATTERN DOTS-3567;So;0;L;;;;;N;;;;; +2875;BRAILLE PATTERN DOTS-13567;So;0;L;;;;;N;;;;; +2876;BRAILLE PATTERN DOTS-23567;So;0;L;;;;;N;;;;; +2877;BRAILLE PATTERN DOTS-123567;So;0;L;;;;;N;;;;; +2878;BRAILLE PATTERN DOTS-4567;So;0;L;;;;;N;;;;; +2879;BRAILLE PATTERN DOTS-14567;So;0;L;;;;;N;;;;; +287A;BRAILLE PATTERN DOTS-24567;So;0;L;;;;;N;;;;; +287B;BRAILLE PATTERN DOTS-124567;So;0;L;;;;;N;;;;; +287C;BRAILLE PATTERN DOTS-34567;So;0;L;;;;;N;;;;; +287D;BRAILLE PATTERN DOTS-134567;So;0;L;;;;;N;;;;; +287E;BRAILLE PATTERN DOTS-234567;So;0;L;;;;;N;;;;; +287F;BRAILLE PATTERN DOTS-1234567;So;0;L;;;;;N;;;;; +2880;BRAILLE PATTERN DOTS-8;So;0;L;;;;;N;;;;; +2881;BRAILLE PATTERN DOTS-18;So;0;L;;;;;N;;;;; +2882;BRAILLE PATTERN DOTS-28;So;0;L;;;;;N;;;;; +2883;BRAILLE PATTERN DOTS-128;So;0;L;;;;;N;;;;; +2884;BRAILLE PATTERN DOTS-38;So;0;L;;;;;N;;;;; +2885;BRAILLE PATTERN DOTS-138;So;0;L;;;;;N;;;;; +2886;BRAILLE PATTERN DOTS-238;So;0;L;;;;;N;;;;; +2887;BRAILLE PATTERN DOTS-1238;So;0;L;;;;;N;;;;; +2888;BRAILLE PATTERN DOTS-48;So;0;L;;;;;N;;;;; +2889;BRAILLE PATTERN DOTS-148;So;0;L;;;;;N;;;;; +288A;BRAILLE PATTERN DOTS-248;So;0;L;;;;;N;;;;; +288B;BRAILLE PATTERN DOTS-1248;So;0;L;;;;;N;;;;; +288C;BRAILLE PATTERN DOTS-348;So;0;L;;;;;N;;;;; +288D;BRAILLE PATTERN DOTS-1348;So;0;L;;;;;N;;;;; +288E;BRAILLE PATTERN DOTS-2348;So;0;L;;;;;N;;;;; +288F;BRAILLE PATTERN DOTS-12348;So;0;L;;;;;N;;;;; +2890;BRAILLE PATTERN DOTS-58;So;0;L;;;;;N;;;;; +2891;BRAILLE PATTERN DOTS-158;So;0;L;;;;;N;;;;; +2892;BRAILLE PATTERN DOTS-258;So;0;L;;;;;N;;;;; +2893;BRAILLE PATTERN DOTS-1258;So;0;L;;;;;N;;;;; +2894;BRAILLE PATTERN DOTS-358;So;0;L;;;;;N;;;;; +2895;BRAILLE PATTERN DOTS-1358;So;0;L;;;;;N;;;;; +2896;BRAILLE PATTERN DOTS-2358;So;0;L;;;;;N;;;;; +2897;BRAILLE PATTERN DOTS-12358;So;0;L;;;;;N;;;;; +2898;BRAILLE PATTERN DOTS-458;So;0;L;;;;;N;;;;; +2899;BRAILLE PATTERN DOTS-1458;So;0;L;;;;;N;;;;; +289A;BRAILLE PATTERN DOTS-2458;So;0;L;;;;;N;;;;; +289B;BRAILLE PATTERN DOTS-12458;So;0;L;;;;;N;;;;; +289C;BRAILLE PATTERN DOTS-3458;So;0;L;;;;;N;;;;; +289D;BRAILLE PATTERN DOTS-13458;So;0;L;;;;;N;;;;; +289E;BRAILLE PATTERN DOTS-23458;So;0;L;;;;;N;;;;; +289F;BRAILLE PATTERN DOTS-123458;So;0;L;;;;;N;;;;; +28A0;BRAILLE PATTERN DOTS-68;So;0;L;;;;;N;;;;; +28A1;BRAILLE PATTERN DOTS-168;So;0;L;;;;;N;;;;; +28A2;BRAILLE PATTERN DOTS-268;So;0;L;;;;;N;;;;; +28A3;BRAILLE PATTERN DOTS-1268;So;0;L;;;;;N;;;;; +28A4;BRAILLE PATTERN DOTS-368;So;0;L;;;;;N;;;;; +28A5;BRAILLE PATTERN DOTS-1368;So;0;L;;;;;N;;;;; +28A6;BRAILLE PATTERN DOTS-2368;So;0;L;;;;;N;;;;; +28A7;BRAILLE PATTERN DOTS-12368;So;0;L;;;;;N;;;;; +28A8;BRAILLE PATTERN DOTS-468;So;0;L;;;;;N;;;;; +28A9;BRAILLE PATTERN DOTS-1468;So;0;L;;;;;N;;;;; +28AA;BRAILLE PATTERN DOTS-2468;So;0;L;;;;;N;;;;; +28AB;BRAILLE PATTERN DOTS-12468;So;0;L;;;;;N;;;;; +28AC;BRAILLE PATTERN DOTS-3468;So;0;L;;;;;N;;;;; +28AD;BRAILLE PATTERN DOTS-13468;So;0;L;;;;;N;;;;; +28AE;BRAILLE PATTERN DOTS-23468;So;0;L;;;;;N;;;;; +28AF;BRAILLE PATTERN DOTS-123468;So;0;L;;;;;N;;;;; +28B0;BRAILLE PATTERN DOTS-568;So;0;L;;;;;N;;;;; +28B1;BRAILLE PATTERN DOTS-1568;So;0;L;;;;;N;;;;; +28B2;BRAILLE PATTERN DOTS-2568;So;0;L;;;;;N;;;;; +28B3;BRAILLE PATTERN DOTS-12568;So;0;L;;;;;N;;;;; +28B4;BRAILLE PATTERN DOTS-3568;So;0;L;;;;;N;;;;; +28B5;BRAILLE PATTERN DOTS-13568;So;0;L;;;;;N;;;;; +28B6;BRAILLE PATTERN DOTS-23568;So;0;L;;;;;N;;;;; +28B7;BRAILLE PATTERN DOTS-123568;So;0;L;;;;;N;;;;; +28B8;BRAILLE PATTERN DOTS-4568;So;0;L;;;;;N;;;;; +28B9;BRAILLE PATTERN DOTS-14568;So;0;L;;;;;N;;;;; +28BA;BRAILLE PATTERN DOTS-24568;So;0;L;;;;;N;;;;; +28BB;BRAILLE PATTERN DOTS-124568;So;0;L;;;;;N;;;;; +28BC;BRAILLE PATTERN DOTS-34568;So;0;L;;;;;N;;;;; +28BD;BRAILLE PATTERN DOTS-134568;So;0;L;;;;;N;;;;; +28BE;BRAILLE PATTERN DOTS-234568;So;0;L;;;;;N;;;;; +28BF;BRAILLE PATTERN DOTS-1234568;So;0;L;;;;;N;;;;; +28C0;BRAILLE PATTERN DOTS-78;So;0;L;;;;;N;;;;; +28C1;BRAILLE PATTERN DOTS-178;So;0;L;;;;;N;;;;; +28C2;BRAILLE PATTERN DOTS-278;So;0;L;;;;;N;;;;; +28C3;BRAILLE PATTERN DOTS-1278;So;0;L;;;;;N;;;;; +28C4;BRAILLE PATTERN DOTS-378;So;0;L;;;;;N;;;;; +28C5;BRAILLE PATTERN DOTS-1378;So;0;L;;;;;N;;;;; +28C6;BRAILLE PATTERN DOTS-2378;So;0;L;;;;;N;;;;; +28C7;BRAILLE PATTERN DOTS-12378;So;0;L;;;;;N;;;;; +28C8;BRAILLE PATTERN DOTS-478;So;0;L;;;;;N;;;;; +28C9;BRAILLE PATTERN DOTS-1478;So;0;L;;;;;N;;;;; +28CA;BRAILLE PATTERN DOTS-2478;So;0;L;;;;;N;;;;; +28CB;BRAILLE PATTERN DOTS-12478;So;0;L;;;;;N;;;;; +28CC;BRAILLE PATTERN DOTS-3478;So;0;L;;;;;N;;;;; +28CD;BRAILLE PATTERN DOTS-13478;So;0;L;;;;;N;;;;; +28CE;BRAILLE PATTERN DOTS-23478;So;0;L;;;;;N;;;;; +28CF;BRAILLE PATTERN DOTS-123478;So;0;L;;;;;N;;;;; +28D0;BRAILLE PATTERN DOTS-578;So;0;L;;;;;N;;;;; +28D1;BRAILLE PATTERN DOTS-1578;So;0;L;;;;;N;;;;; +28D2;BRAILLE PATTERN DOTS-2578;So;0;L;;;;;N;;;;; +28D3;BRAILLE PATTERN DOTS-12578;So;0;L;;;;;N;;;;; +28D4;BRAILLE PATTERN DOTS-3578;So;0;L;;;;;N;;;;; +28D5;BRAILLE PATTERN DOTS-13578;So;0;L;;;;;N;;;;; +28D6;BRAILLE PATTERN DOTS-23578;So;0;L;;;;;N;;;;; +28D7;BRAILLE PATTERN DOTS-123578;So;0;L;;;;;N;;;;; +28D8;BRAILLE PATTERN DOTS-4578;So;0;L;;;;;N;;;;; +28D9;BRAILLE PATTERN DOTS-14578;So;0;L;;;;;N;;;;; +28DA;BRAILLE PATTERN DOTS-24578;So;0;L;;;;;N;;;;; +28DB;BRAILLE PATTERN DOTS-124578;So;0;L;;;;;N;;;;; +28DC;BRAILLE PATTERN DOTS-34578;So;0;L;;;;;N;;;;; +28DD;BRAILLE PATTERN DOTS-134578;So;0;L;;;;;N;;;;; +28DE;BRAILLE PATTERN DOTS-234578;So;0;L;;;;;N;;;;; +28DF;BRAILLE PATTERN DOTS-1234578;So;0;L;;;;;N;;;;; +28E0;BRAILLE PATTERN DOTS-678;So;0;L;;;;;N;;;;; +28E1;BRAILLE PATTERN DOTS-1678;So;0;L;;;;;N;;;;; +28E2;BRAILLE PATTERN DOTS-2678;So;0;L;;;;;N;;;;; +28E3;BRAILLE PATTERN DOTS-12678;So;0;L;;;;;N;;;;; +28E4;BRAILLE PATTERN DOTS-3678;So;0;L;;;;;N;;;;; +28E5;BRAILLE PATTERN DOTS-13678;So;0;L;;;;;N;;;;; +28E6;BRAILLE PATTERN DOTS-23678;So;0;L;;;;;N;;;;; +28E7;BRAILLE PATTERN DOTS-123678;So;0;L;;;;;N;;;;; +28E8;BRAILLE PATTERN DOTS-4678;So;0;L;;;;;N;;;;; +28E9;BRAILLE PATTERN DOTS-14678;So;0;L;;;;;N;;;;; +28EA;BRAILLE PATTERN DOTS-24678;So;0;L;;;;;N;;;;; +28EB;BRAILLE PATTERN DOTS-124678;So;0;L;;;;;N;;;;; +28EC;BRAILLE PATTERN DOTS-34678;So;0;L;;;;;N;;;;; +28ED;BRAILLE PATTERN DOTS-134678;So;0;L;;;;;N;;;;; +28EE;BRAILLE PATTERN DOTS-234678;So;0;L;;;;;N;;;;; +28EF;BRAILLE PATTERN DOTS-1234678;So;0;L;;;;;N;;;;; +28F0;BRAILLE PATTERN DOTS-5678;So;0;L;;;;;N;;;;; +28F1;BRAILLE PATTERN DOTS-15678;So;0;L;;;;;N;;;;; +28F2;BRAILLE PATTERN DOTS-25678;So;0;L;;;;;N;;;;; +28F3;BRAILLE PATTERN DOTS-125678;So;0;L;;;;;N;;;;; +28F4;BRAILLE PATTERN DOTS-35678;So;0;L;;;;;N;;;;; +28F5;BRAILLE PATTERN DOTS-135678;So;0;L;;;;;N;;;;; +28F6;BRAILLE PATTERN DOTS-235678;So;0;L;;;;;N;;;;; +28F7;BRAILLE PATTERN DOTS-1235678;So;0;L;;;;;N;;;;; +28F8;BRAILLE PATTERN DOTS-45678;So;0;L;;;;;N;;;;; +28F9;BRAILLE PATTERN DOTS-145678;So;0;L;;;;;N;;;;; +28FA;BRAILLE PATTERN DOTS-245678;So;0;L;;;;;N;;;;; +28FB;BRAILLE PATTERN DOTS-1245678;So;0;L;;;;;N;;;;; +28FC;BRAILLE PATTERN DOTS-345678;So;0;L;;;;;N;;;;; +28FD;BRAILLE PATTERN DOTS-1345678;So;0;L;;;;;N;;;;; +28FE;BRAILLE PATTERN DOTS-2345678;So;0;L;;;;;N;;;;; +28FF;BRAILLE PATTERN DOTS-12345678;So;0;L;;;;;N;;;;; +2900;RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2901;RIGHTWARDS TWO-HEADED ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2902;LEFTWARDS DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2903;RIGHTWARDS DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2904;LEFT RIGHT DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2905;RIGHTWARDS TWO-HEADED ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +2906;LEFTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +2907;RIGHTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +2908;DOWNWARDS ARROW WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; +2909;UPWARDS ARROW WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; +290A;UPWARDS TRIPLE ARROW;Sm;0;ON;;;;;N;;;;; +290B;DOWNWARDS TRIPLE ARROW;Sm;0;ON;;;;;N;;;;; +290C;LEFTWARDS DOUBLE DASH ARROW;Sm;0;ON;;;;;N;;;;; +290D;RIGHTWARDS DOUBLE DASH ARROW;Sm;0;ON;;;;;N;;;;; +290E;LEFTWARDS TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;; +290F;RIGHTWARDS TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;; +2910;RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;; +2911;RIGHTWARDS ARROW WITH DOTTED STEM;Sm;0;ON;;;;;N;;;;; +2912;UPWARDS ARROW TO BAR;Sm;0;ON;;;;;N;;;;; +2913;DOWNWARDS ARROW TO BAR;Sm;0;ON;;;;;N;;;;; +2914;RIGHTWARDS ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2915;RIGHTWARDS ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2916;RIGHTWARDS TWO-HEADED ARROW WITH TAIL;Sm;0;ON;;;;;N;;;;; +2917;RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2918;RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2919;LEFTWARDS ARROW-TAIL;Sm;0;ON;;;;;N;;;;; +291A;RIGHTWARDS ARROW-TAIL;Sm;0;ON;;;;;N;;;;; +291B;LEFTWARDS DOUBLE ARROW-TAIL;Sm;0;ON;;;;;N;;;;; +291C;RIGHTWARDS DOUBLE ARROW-TAIL;Sm;0;ON;;;;;N;;;;; +291D;LEFTWARDS ARROW TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; +291E;RIGHTWARDS ARROW TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; +291F;LEFTWARDS ARROW FROM BAR TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; +2920;RIGHTWARDS ARROW FROM BAR TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; +2921;NORTH WEST AND SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +2922;NORTH EAST AND SOUTH WEST ARROW;Sm;0;ON;;;;;N;;;;; +2923;NORTH WEST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;; +2924;NORTH EAST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;; +2925;SOUTH EAST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;; +2926;SOUTH WEST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;; +2927;NORTH WEST ARROW AND NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +2928;NORTH EAST ARROW AND SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +2929;SOUTH EAST ARROW AND SOUTH WEST ARROW;Sm;0;ON;;;;;N;;;;; +292A;SOUTH WEST ARROW AND NORTH WEST ARROW;Sm;0;ON;;;;;N;;;;; +292B;RISING DIAGONAL CROSSING FALLING DIAGONAL;Sm;0;ON;;;;;N;;;;; +292C;FALLING DIAGONAL CROSSING RISING DIAGONAL;Sm;0;ON;;;;;N;;;;; +292D;SOUTH EAST ARROW CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +292E;NORTH EAST ARROW CROSSING SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +292F;FALLING DIAGONAL CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +2930;RISING DIAGONAL CROSSING SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +2931;NORTH EAST ARROW CROSSING NORTH WEST ARROW;Sm;0;ON;;;;;N;;;;; +2932;NORTH WEST ARROW CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +2933;WAVE ARROW POINTING DIRECTLY RIGHT;Sm;0;ON;;;;;N;;;;; +2934;ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS;Sm;0;ON;;;;;N;;;;; +2935;ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS;Sm;0;ON;;;;;N;;;;; +2936;ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS;Sm;0;ON;;;;;N;;;;; +2937;ARROW POINTING DOWNWARDS THEN CURVING RIGHTWARDS;Sm;0;ON;;;;;N;;;;; +2938;RIGHT-SIDE ARC CLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; +2939;LEFT-SIDE ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; +293A;TOP ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; +293B;BOTTOM ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; +293C;TOP ARC CLOCKWISE ARROW WITH MINUS;Sm;0;ON;;;;;N;;;;; +293D;TOP ARC ANTICLOCKWISE ARROW WITH PLUS;Sm;0;ON;;;;;N;;;;; +293E;LOWER RIGHT SEMICIRCULAR CLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; +293F;LOWER LEFT SEMICIRCULAR ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; +2940;ANTICLOCKWISE CLOSED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;; +2941;CLOCKWISE CLOSED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;; +2942;RIGHTWARDS ARROW ABOVE SHORT LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2943;LEFTWARDS ARROW ABOVE SHORT RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2944;SHORT RIGHTWARDS ARROW ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2945;RIGHTWARDS ARROW WITH PLUS BELOW;Sm;0;ON;;;;;N;;;;; +2946;LEFTWARDS ARROW WITH PLUS BELOW;Sm;0;ON;;;;;N;;;;; +2947;RIGHTWARDS ARROW THROUGH X;Sm;0;ON;;;;;N;;;;; +2948;LEFT RIGHT ARROW THROUGH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;; +2949;UPWARDS TWO-HEADED ARROW FROM SMALL CIRCLE;Sm;0;ON;;;;;N;;;;; +294A;LEFT BARB UP RIGHT BARB DOWN HARPOON;Sm;0;ON;;;;;N;;;;; +294B;LEFT BARB DOWN RIGHT BARB UP HARPOON;Sm;0;ON;;;;;N;;;;; +294C;UP BARB RIGHT DOWN BARB LEFT HARPOON;Sm;0;ON;;;;;N;;;;; +294D;UP BARB LEFT DOWN BARB RIGHT HARPOON;Sm;0;ON;;;;;N;;;;; +294E;LEFT BARB UP RIGHT BARB UP HARPOON;Sm;0;ON;;;;;N;;;;; +294F;UP BARB RIGHT DOWN BARB RIGHT HARPOON;Sm;0;ON;;;;;N;;;;; +2950;LEFT BARB DOWN RIGHT BARB DOWN HARPOON;Sm;0;ON;;;;;N;;;;; +2951;UP BARB LEFT DOWN BARB LEFT HARPOON;Sm;0;ON;;;;;N;;;;; +2952;LEFTWARDS HARPOON WITH BARB UP TO BAR;Sm;0;ON;;;;;N;;;;; +2953;RIGHTWARDS HARPOON WITH BARB UP TO BAR;Sm;0;ON;;;;;N;;;;; +2954;UPWARDS HARPOON WITH BARB RIGHT TO BAR;Sm;0;ON;;;;;N;;;;; +2955;DOWNWARDS HARPOON WITH BARB RIGHT TO BAR;Sm;0;ON;;;;;N;;;;; +2956;LEFTWARDS HARPOON WITH BARB DOWN TO BAR;Sm;0;ON;;;;;N;;;;; +2957;RIGHTWARDS HARPOON WITH BARB DOWN TO BAR;Sm;0;ON;;;;;N;;;;; +2958;UPWARDS HARPOON WITH BARB LEFT TO BAR;Sm;0;ON;;;;;N;;;;; +2959;DOWNWARDS HARPOON WITH BARB LEFT TO BAR;Sm;0;ON;;;;;N;;;;; +295A;LEFTWARDS HARPOON WITH BARB UP FROM BAR;Sm;0;ON;;;;;N;;;;; +295B;RIGHTWARDS HARPOON WITH BARB UP FROM BAR;Sm;0;ON;;;;;N;;;;; +295C;UPWARDS HARPOON WITH BARB RIGHT FROM BAR;Sm;0;ON;;;;;N;;;;; +295D;DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR;Sm;0;ON;;;;;N;;;;; +295E;LEFTWARDS HARPOON WITH BARB DOWN FROM BAR;Sm;0;ON;;;;;N;;;;; +295F;RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR;Sm;0;ON;;;;;N;;;;; +2960;UPWARDS HARPOON WITH BARB LEFT FROM BAR;Sm;0;ON;;;;;N;;;;; +2961;DOWNWARDS HARPOON WITH BARB LEFT FROM BAR;Sm;0;ON;;;;;N;;;;; +2962;LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;; +2963;UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;; +2964;RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;; +2965;DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;; +2966;LEFTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB UP;Sm;0;ON;;;;;N;;;;; +2967;LEFTWARDS HARPOON WITH BARB DOWN ABOVE RIGHTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;; +2968;RIGHTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB UP;Sm;0;ON;;;;;N;;;;; +2969;RIGHTWARDS HARPOON WITH BARB DOWN ABOVE LEFTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;; +296A;LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH;Sm;0;ON;;;;;N;;;;; +296B;LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH;Sm;0;ON;;;;;N;;;;; +296C;RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH;Sm;0;ON;;;;;N;;;;; +296D;RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH;Sm;0;ON;;;;;N;;;;; +296E;UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;; +296F;DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;; +2970;RIGHT DOUBLE ARROW WITH ROUNDED HEAD;Sm;0;ON;;;;;N;;;;; +2971;EQUALS SIGN ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2972;TILDE OPERATOR ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2973;LEFTWARDS ARROW ABOVE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;; +2974;RIGHTWARDS ARROW ABOVE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;; +2975;RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;; +2976;LESS-THAN ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2977;LEFTWARDS ARROW THROUGH LESS-THAN;Sm;0;ON;;;;;N;;;;; +2978;GREATER-THAN ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2979;SUBSET ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +297A;LEFTWARDS ARROW THROUGH SUBSET;Sm;0;ON;;;;;N;;;;; +297B;SUPERSET ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +297C;LEFT FISH TAIL;Sm;0;ON;;;;;N;;;;; +297D;RIGHT FISH TAIL;Sm;0;ON;;;;;N;;;;; +297E;UP FISH TAIL;Sm;0;ON;;;;;N;;;;; +297F;DOWN FISH TAIL;Sm;0;ON;;;;;N;;;;; +2980;TRIPLE VERTICAL BAR DELIMITER;Sm;0;ON;;;;;N;;;;; +2981;Z NOTATION SPOT;Sm;0;ON;;;;;N;;;;; +2982;Z NOTATION TYPE COLON;Sm;0;ON;;;;;N;;;;; +2983;LEFT WHITE CURLY BRACKET;Ps;0;ON;;;;;Y;;;;; +2984;RIGHT WHITE CURLY BRACKET;Pe;0;ON;;;;;Y;;;;; +2985;LEFT WHITE PARENTHESIS;Ps;0;ON;;;;;Y;;;;; +2986;RIGHT WHITE PARENTHESIS;Pe;0;ON;;;;;Y;;;;; +2987;Z NOTATION LEFT IMAGE BRACKET;Ps;0;ON;;;;;Y;;;;; +2988;Z NOTATION RIGHT IMAGE BRACKET;Pe;0;ON;;;;;Y;;;;; +2989;Z NOTATION LEFT BINDING BRACKET;Ps;0;ON;;;;;Y;;;;; +298A;Z NOTATION RIGHT BINDING BRACKET;Pe;0;ON;;;;;Y;;;;; +298B;LEFT SQUARE BRACKET WITH UNDERBAR;Ps;0;ON;;;;;Y;;;;; +298C;RIGHT SQUARE BRACKET WITH UNDERBAR;Pe;0;ON;;;;;Y;;;;; +298D;LEFT SQUARE BRACKET WITH TICK IN TOP CORNER;Ps;0;ON;;;;;Y;;;;; +298E;RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER;Pe;0;ON;;;;;Y;;;;; +298F;LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER;Ps;0;ON;;;;;Y;;;;; +2990;RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER;Pe;0;ON;;;;;Y;;;;; +2991;LEFT ANGLE BRACKET WITH DOT;Ps;0;ON;;;;;Y;;;;; +2992;RIGHT ANGLE BRACKET WITH DOT;Pe;0;ON;;;;;Y;;;;; +2993;LEFT ARC LESS-THAN BRACKET;Ps;0;ON;;;;;Y;;;;; +2994;RIGHT ARC GREATER-THAN BRACKET;Pe;0;ON;;;;;Y;;;;; +2995;DOUBLE LEFT ARC GREATER-THAN BRACKET;Ps;0;ON;;;;;Y;;;;; +2996;DOUBLE RIGHT ARC LESS-THAN BRACKET;Pe;0;ON;;;;;Y;;;;; +2997;LEFT BLACK TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;;;;; +2998;RIGHT BLACK TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;;;;; +2999;DOTTED FENCE;Sm;0;ON;;;;;N;;;;; +299A;VERTICAL ZIGZAG LINE;Sm;0;ON;;;;;N;;;;; +299B;MEASURED ANGLE OPENING LEFT;Sm;0;ON;;;;;Y;;;;; +299C;RIGHT ANGLE VARIANT WITH SQUARE;Sm;0;ON;;;;;Y;;;;; +299D;MEASURED RIGHT ANGLE WITH DOT;Sm;0;ON;;;;;Y;;;;; +299E;ANGLE WITH S INSIDE;Sm;0;ON;;;;;Y;;;;; +299F;ACUTE ANGLE;Sm;0;ON;;;;;Y;;;;; +29A0;SPHERICAL ANGLE OPENING LEFT;Sm;0;ON;;;;;Y;;;;; +29A1;SPHERICAL ANGLE OPENING UP;Sm;0;ON;;;;;Y;;;;; +29A2;TURNED ANGLE;Sm;0;ON;;;;;Y;;;;; +29A3;REVERSED ANGLE;Sm;0;ON;;;;;Y;;;;; +29A4;ANGLE WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; +29A5;REVERSED ANGLE WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; +29A6;OBLIQUE ANGLE OPENING UP;Sm;0;ON;;;;;Y;;;;; +29A7;OBLIQUE ANGLE OPENING DOWN;Sm;0;ON;;;;;Y;;;;; +29A8;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT;Sm;0;ON;;;;;Y;;;;; +29A9;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT;Sm;0;ON;;;;;Y;;;;; +29AA;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT;Sm;0;ON;;;;;Y;;;;; +29AB;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT;Sm;0;ON;;;;;Y;;;;; +29AC;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP;Sm;0;ON;;;;;Y;;;;; +29AD;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP;Sm;0;ON;;;;;Y;;;;; +29AE;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN;Sm;0;ON;;;;;Y;;;;; +29AF;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN;Sm;0;ON;;;;;Y;;;;; +29B0;REVERSED EMPTY SET;Sm;0;ON;;;;;N;;;;; +29B1;EMPTY SET WITH OVERBAR;Sm;0;ON;;;;;N;;;;; +29B2;EMPTY SET WITH SMALL CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;; +29B3;EMPTY SET WITH RIGHT ARROW ABOVE;Sm;0;ON;;;;;N;;;;; +29B4;EMPTY SET WITH LEFT ARROW ABOVE;Sm;0;ON;;;;;N;;;;; +29B5;CIRCLE WITH HORIZONTAL BAR;Sm;0;ON;;;;;N;;;;; +29B6;CIRCLED VERTICAL BAR;Sm;0;ON;;;;;N;;;;; +29B7;CIRCLED PARALLEL;Sm;0;ON;;;;;N;;;;; +29B8;CIRCLED REVERSE SOLIDUS;Sm;0;ON;;;;;Y;;;;; +29B9;CIRCLED PERPENDICULAR;Sm;0;ON;;;;;N;;;;; +29BA;CIRCLE DIVIDED BY HORIZONTAL BAR AND TOP HALF DIVIDED BY VERTICAL BAR;Sm;0;ON;;;;;N;;;;; +29BB;CIRCLE WITH SUPERIMPOSED X;Sm;0;ON;;;;;N;;;;; +29BC;CIRCLED ANTICLOCKWISE-ROTATED DIVISION SIGN;Sm;0;ON;;;;;N;;;;; +29BD;UP ARROW THROUGH CIRCLE;Sm;0;ON;;;;;N;;;;; +29BE;CIRCLED WHITE BULLET;Sm;0;ON;;;;;N;;;;; +29BF;CIRCLED BULLET;Sm;0;ON;;;;;N;;;;; +29C0;CIRCLED LESS-THAN;Sm;0;ON;;;;;Y;;;;; +29C1;CIRCLED GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +29C2;CIRCLE WITH SMALL CIRCLE TO THE RIGHT;Sm;0;ON;;;;;Y;;;;; +29C3;CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT;Sm;0;ON;;;;;Y;;;;; +29C4;SQUARED RISING DIAGONAL SLASH;Sm;0;ON;;;;;Y;;;;; +29C5;SQUARED FALLING DIAGONAL SLASH;Sm;0;ON;;;;;Y;;;;; +29C6;SQUARED ASTERISK;Sm;0;ON;;;;;N;;;;; +29C7;SQUARED SMALL CIRCLE;Sm;0;ON;;;;;N;;;;; +29C8;SQUARED SQUARE;Sm;0;ON;;;;;N;;;;; +29C9;TWO JOINED SQUARES;Sm;0;ON;;;;;Y;;;;; +29CA;TRIANGLE WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; +29CB;TRIANGLE WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; +29CC;S IN TRIANGLE;Sm;0;ON;;;;;N;;;;; +29CD;TRIANGLE WITH SERIFS AT BOTTOM;Sm;0;ON;;;;;N;;;;; +29CE;RIGHT TRIANGLE ABOVE LEFT TRIANGLE;Sm;0;ON;;;;;Y;;;;; +29CF;LEFT TRIANGLE BESIDE VERTICAL BAR;Sm;0;ON;;;;;Y;;;;; +29D0;VERTICAL BAR BESIDE RIGHT TRIANGLE;Sm;0;ON;;;;;Y;;;;; +29D1;BOWTIE WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;; +29D2;BOWTIE WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;; +29D3;BLACK BOWTIE;Sm;0;ON;;;;;N;;;;; +29D4;TIMES WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;; +29D5;TIMES WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;; +29D6;WHITE HOURGLASS;Sm;0;ON;;;;;N;;;;; +29D7;BLACK HOURGLASS;Sm;0;ON;;;;;N;;;;; +29D8;LEFT WIGGLY FENCE;Ps;0;ON;;;;;Y;;;;; +29D9;RIGHT WIGGLY FENCE;Pe;0;ON;;;;;Y;;;;; +29DA;LEFT DOUBLE WIGGLY FENCE;Ps;0;ON;;;;;Y;;;;; +29DB;RIGHT DOUBLE WIGGLY FENCE;Pe;0;ON;;;;;Y;;;;; +29DC;INCOMPLETE INFINITY;Sm;0;ON;;;;;Y;;;;; +29DD;TIE OVER INFINITY;Sm;0;ON;;;;;N;;;;; +29DE;INFINITY NEGATED WITH VERTICAL BAR;Sm;0;ON;;;;;N;;;;; +29DF;DOUBLE-ENDED MULTIMAP;Sm;0;ON;;;;;N;;;;; +29E0;SQUARE WITH CONTOURED OUTLINE;Sm;0;ON;;;;;N;;;;; +29E1;INCREASES AS;Sm;0;ON;;;;;Y;;;;; +29E2;SHUFFLE PRODUCT;Sm;0;ON;;;;;N;;;;; +29E3;EQUALS SIGN AND SLANTED PARALLEL;Sm;0;ON;;;;;Y;;;;; +29E4;EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE;Sm;0;ON;;;;;Y;;;;; +29E5;IDENTICAL TO AND SLANTED PARALLEL;Sm;0;ON;;;;;Y;;;;; +29E6;GLEICH STARK;Sm;0;ON;;;;;N;;;;; +29E7;THERMODYNAMIC;Sm;0;ON;;;;;N;;;;; +29E8;DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;; +29E9;DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;; +29EA;BLACK DIAMOND WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;; +29EB;BLACK LOZENGE;Sm;0;ON;;;;;N;;;;; +29EC;WHITE CIRCLE WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;; +29ED;BLACK CIRCLE WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;; +29EE;ERROR-BARRED WHITE SQUARE;Sm;0;ON;;;;;N;;;;; +29EF;ERROR-BARRED BLACK SQUARE;Sm;0;ON;;;;;N;;;;; +29F0;ERROR-BARRED WHITE DIAMOND;Sm;0;ON;;;;;N;;;;; +29F1;ERROR-BARRED BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; +29F2;ERROR-BARRED WHITE CIRCLE;Sm;0;ON;;;;;N;;;;; +29F3;ERROR-BARRED BLACK CIRCLE;Sm;0;ON;;;;;N;;;;; +29F4;RULE-DELAYED;Sm;0;ON;;;;;Y;;;;; +29F5;REVERSE SOLIDUS OPERATOR;Sm;0;ON;;;;;Y;;;;; +29F6;SOLIDUS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; +29F7;REVERSE SOLIDUS WITH HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; +29F8;BIG SOLIDUS;Sm;0;ON;;;;;Y;;;;; +29F9;BIG REVERSE SOLIDUS;Sm;0;ON;;;;;Y;;;;; +29FA;DOUBLE PLUS;Sm;0;ON;;;;;N;;;;; +29FB;TRIPLE PLUS;Sm;0;ON;;;;;N;;;;; +29FC;LEFT-POINTING CURVED ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;; +29FD;RIGHT-POINTING CURVED ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;; +29FE;TINY;Sm;0;ON;;;;;N;;;;; +29FF;MINY;Sm;0;ON;;;;;N;;;;; +2A00;N-ARY CIRCLED DOT OPERATOR;Sm;0;ON;;;;;N;;;;; +2A01;N-ARY CIRCLED PLUS OPERATOR;Sm;0;ON;;;;;N;;;;; +2A02;N-ARY CIRCLED TIMES OPERATOR;Sm;0;ON;;;;;N;;;;; +2A03;N-ARY UNION OPERATOR WITH DOT;Sm;0;ON;;;;;N;;;;; +2A04;N-ARY UNION OPERATOR WITH PLUS;Sm;0;ON;;;;;N;;;;; +2A05;N-ARY SQUARE INTERSECTION OPERATOR;Sm;0;ON;;;;;N;;;;; +2A06;N-ARY SQUARE UNION OPERATOR;Sm;0;ON;;;;;N;;;;; +2A07;TWO LOGICAL AND OPERATOR;Sm;0;ON;;;;;N;;;;; +2A08;TWO LOGICAL OR OPERATOR;Sm;0;ON;;;;;N;;;;; +2A09;N-ARY TIMES OPERATOR;Sm;0;ON;;;;;N;;;;; +2A0A;MODULO TWO SUM;Sm;0;ON;;;;;Y;;;;; +2A0B;SUMMATION WITH INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2A0C;QUADRUPLE INTEGRAL OPERATOR;Sm;0;ON;<compat> 222B 222B 222B 222B;;;;Y;;;;; +2A0D;FINITE PART INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2A0E;INTEGRAL WITH DOUBLE STROKE;Sm;0;ON;;;;;Y;;;;; +2A0F;INTEGRAL AVERAGE WITH SLASH;Sm;0;ON;;;;;Y;;;;; +2A10;CIRCULATION FUNCTION;Sm;0;ON;;;;;Y;;;;; +2A11;ANTICLOCKWISE INTEGRATION;Sm;0;ON;;;;;Y;;;;; +2A12;LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE;Sm;0;ON;;;;;Y;;;;; +2A13;LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE;Sm;0;ON;;;;;Y;;;;; +2A14;LINE INTEGRATION NOT INCLUDING THE POLE;Sm;0;ON;;;;;Y;;;;; +2A15;INTEGRAL AROUND A POINT OPERATOR;Sm;0;ON;;;;;Y;;;;; +2A16;QUATERNION INTEGRAL OPERATOR;Sm;0;ON;;;;;Y;;;;; +2A17;INTEGRAL WITH LEFTWARDS ARROW WITH HOOK;Sm;0;ON;;;;;Y;;;;; +2A18;INTEGRAL WITH TIMES SIGN;Sm;0;ON;;;;;Y;;;;; +2A19;INTEGRAL WITH INTERSECTION;Sm;0;ON;;;;;Y;;;;; +2A1A;INTEGRAL WITH UNION;Sm;0;ON;;;;;Y;;;;; +2A1B;INTEGRAL WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; +2A1C;INTEGRAL WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; +2A1D;JOIN;Sm;0;ON;;;;;N;;;;; +2A1E;LARGE LEFT TRIANGLE OPERATOR;Sm;0;ON;;;;;Y;;;;; +2A1F;Z NOTATION SCHEMA COMPOSITION;Sm;0;ON;;;;;Y;;;;; +2A20;Z NOTATION SCHEMA PIPING;Sm;0;ON;;;;;Y;;;;; +2A21;Z NOTATION SCHEMA PROJECTION;Sm;0;ON;;;;;Y;;;;; +2A22;PLUS SIGN WITH SMALL CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;; +2A23;PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE;Sm;0;ON;;;;;N;;;;; +2A24;PLUS SIGN WITH TILDE ABOVE;Sm;0;ON;;;;;Y;;;;; +2A25;PLUS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;; +2A26;PLUS SIGN WITH TILDE BELOW;Sm;0;ON;;;;;Y;;;;; +2A27;PLUS SIGN WITH SUBSCRIPT TWO;Sm;0;ON;;;;;N;;;;; +2A28;PLUS SIGN WITH BLACK TRIANGLE;Sm;0;ON;;;;;N;;;;; +2A29;MINUS SIGN WITH COMMA ABOVE;Sm;0;ON;;;;;Y;;;;; +2A2A;MINUS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;; +2A2B;MINUS SIGN WITH FALLING DOTS;Sm;0;ON;;;;;Y;;;;; +2A2C;MINUS SIGN WITH RISING DOTS;Sm;0;ON;;;;;Y;;;;; +2A2D;PLUS SIGN IN LEFT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;; +2A2E;PLUS SIGN IN RIGHT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;; +2A2F;VECTOR OR CROSS PRODUCT;Sm;0;ON;;;;;N;;;;; +2A30;MULTIPLICATION SIGN WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; +2A31;MULTIPLICATION SIGN WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; +2A32;SEMIDIRECT PRODUCT WITH BOTTOM CLOSED;Sm;0;ON;;;;;N;;;;; +2A33;SMASH PRODUCT;Sm;0;ON;;;;;N;;;;; +2A34;MULTIPLICATION SIGN IN LEFT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;; +2A35;MULTIPLICATION SIGN IN RIGHT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;; +2A36;CIRCLED MULTIPLICATION SIGN WITH CIRCUMFLEX ACCENT;Sm;0;ON;;;;;N;;;;; +2A37;MULTIPLICATION SIGN IN DOUBLE CIRCLE;Sm;0;ON;;;;;N;;;;; +2A38;CIRCLED DIVISION SIGN;Sm;0;ON;;;;;N;;;;; +2A39;PLUS SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;; +2A3A;MINUS SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;; +2A3B;MULTIPLICATION SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;; +2A3C;INTERIOR PRODUCT;Sm;0;ON;;;;;Y;;;;; +2A3D;RIGHTHAND INTERIOR PRODUCT;Sm;0;ON;;;;;Y;;;;; +2A3E;Z NOTATION RELATIONAL COMPOSITION;Sm;0;ON;;;;;Y;;;;; +2A3F;AMALGAMATION OR COPRODUCT;Sm;0;ON;;;;;N;;;;; +2A40;INTERSECTION WITH DOT;Sm;0;ON;;;;;N;;;;; +2A41;UNION WITH MINUS SIGN;Sm;0;ON;;;;;N;;;;; +2A42;UNION WITH OVERBAR;Sm;0;ON;;;;;N;;;;; +2A43;INTERSECTION WITH OVERBAR;Sm;0;ON;;;;;N;;;;; +2A44;INTERSECTION WITH LOGICAL AND;Sm;0;ON;;;;;N;;;;; +2A45;UNION WITH LOGICAL OR;Sm;0;ON;;;;;N;;;;; +2A46;UNION ABOVE INTERSECTION;Sm;0;ON;;;;;N;;;;; +2A47;INTERSECTION ABOVE UNION;Sm;0;ON;;;;;N;;;;; +2A48;UNION ABOVE BAR ABOVE INTERSECTION;Sm;0;ON;;;;;N;;;;; +2A49;INTERSECTION ABOVE BAR ABOVE UNION;Sm;0;ON;;;;;N;;;;; +2A4A;UNION BESIDE AND JOINED WITH UNION;Sm;0;ON;;;;;N;;;;; +2A4B;INTERSECTION BESIDE AND JOINED WITH INTERSECTION;Sm;0;ON;;;;;N;;;;; +2A4C;CLOSED UNION WITH SERIFS;Sm;0;ON;;;;;N;;;;; +2A4D;CLOSED INTERSECTION WITH SERIFS;Sm;0;ON;;;;;N;;;;; +2A4E;DOUBLE SQUARE INTERSECTION;Sm;0;ON;;;;;N;;;;; +2A4F;DOUBLE SQUARE UNION;Sm;0;ON;;;;;N;;;;; +2A50;CLOSED UNION WITH SERIFS AND SMASH PRODUCT;Sm;0;ON;;;;;N;;;;; +2A51;LOGICAL AND WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; +2A52;LOGICAL OR WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; +2A53;DOUBLE LOGICAL AND;Sm;0;ON;;;;;N;;;;; +2A54;DOUBLE LOGICAL OR;Sm;0;ON;;;;;N;;;;; +2A55;TWO INTERSECTING LOGICAL AND;Sm;0;ON;;;;;N;;;;; +2A56;TWO INTERSECTING LOGICAL OR;Sm;0;ON;;;;;N;;;;; +2A57;SLOPING LARGE OR;Sm;0;ON;;;;;Y;;;;; +2A58;SLOPING LARGE AND;Sm;0;ON;;;;;Y;;;;; +2A59;LOGICAL OR OVERLAPPING LOGICAL AND;Sm;0;ON;;;;;N;;;;; +2A5A;LOGICAL AND WITH MIDDLE STEM;Sm;0;ON;;;;;N;;;;; +2A5B;LOGICAL OR WITH MIDDLE STEM;Sm;0;ON;;;;;N;;;;; +2A5C;LOGICAL AND WITH HORIZONTAL DASH;Sm;0;ON;;;;;N;;;;; +2A5D;LOGICAL OR WITH HORIZONTAL DASH;Sm;0;ON;;;;;N;;;;; +2A5E;LOGICAL AND WITH DOUBLE OVERBAR;Sm;0;ON;;;;;N;;;;; +2A5F;LOGICAL AND WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; +2A60;LOGICAL AND WITH DOUBLE UNDERBAR;Sm;0;ON;;;;;N;;;;; +2A61;SMALL VEE WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; +2A62;LOGICAL OR WITH DOUBLE OVERBAR;Sm;0;ON;;;;;N;;;;; +2A63;LOGICAL OR WITH DOUBLE UNDERBAR;Sm;0;ON;;;;;N;;;;; +2A64;Z NOTATION DOMAIN ANTIRESTRICTION;Sm;0;ON;;;;;Y;;;;; +2A65;Z NOTATION RANGE ANTIRESTRICTION;Sm;0;ON;;;;;Y;;;;; +2A66;EQUALS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;; +2A67;IDENTICAL WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; +2A68;TRIPLE HORIZONTAL BAR WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2A69;TRIPLE HORIZONTAL BAR WITH TRIPLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2A6A;TILDE OPERATOR WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; +2A6B;TILDE OPERATOR WITH RISING DOTS;Sm;0;ON;;;;;Y;;;;; +2A6C;SIMILAR MINUS SIMILAR;Sm;0;ON;;;;;Y;;;;; +2A6D;CONGRUENT WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; +2A6E;EQUALS WITH ASTERISK;Sm;0;ON;;;;;N;;;;; +2A6F;ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT;Sm;0;ON;;;;;Y;;;;; +2A70;APPROXIMATELY EQUAL OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2A71;EQUALS SIGN ABOVE PLUS SIGN;Sm;0;ON;;;;;N;;;;; +2A72;PLUS SIGN ABOVE EQUALS SIGN;Sm;0;ON;;;;;N;;;;; +2A73;EQUALS SIGN ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; +2A74;DOUBLE COLON EQUAL;Sm;0;ON;<compat> 003A 003A 003D;;;;Y;;;;; +2A75;TWO CONSECUTIVE EQUALS SIGNS;Sm;0;ON;<compat> 003D 003D;;;;N;;;;; +2A76;THREE CONSECUTIVE EQUALS SIGNS;Sm;0;ON;<compat> 003D 003D 003D;;;;N;;;;; +2A77;EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW;Sm;0;ON;;;;;N;;;;; +2A78;EQUIVALENT WITH FOUR DOTS ABOVE;Sm;0;ON;;;;;N;;;;; +2A79;LESS-THAN WITH CIRCLE INSIDE;Sm;0;ON;;;;;Y;;;;; +2A7A;GREATER-THAN WITH CIRCLE INSIDE;Sm;0;ON;;;;;Y;;;;; +2A7B;LESS-THAN WITH QUESTION MARK ABOVE;Sm;0;ON;;;;;Y;;;;; +2A7C;GREATER-THAN WITH QUESTION MARK ABOVE;Sm;0;ON;;;;;Y;;;;; +2A7D;LESS-THAN OR SLANTED EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2A7E;GREATER-THAN OR SLANTED EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2A7F;LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;; +2A80;GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;; +2A81;LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; +2A82;GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; +2A83;LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT;Sm;0;ON;;;;;Y;;;;; +2A84;GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT;Sm;0;ON;;;;;Y;;;;; +2A85;LESS-THAN OR APPROXIMATE;Sm;0;ON;;;;;Y;;;;; +2A86;GREATER-THAN OR APPROXIMATE;Sm;0;ON;;;;;Y;;;;; +2A87;LESS-THAN AND SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2A88;GREATER-THAN AND SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2A89;LESS-THAN AND NOT APPROXIMATE;Sm;0;ON;;;;;Y;;;;; +2A8A;GREATER-THAN AND NOT APPROXIMATE;Sm;0;ON;;;;;Y;;;;; +2A8B;LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2A8C;GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2A8D;LESS-THAN ABOVE SIMILAR OR EQUAL;Sm;0;ON;;;;;Y;;;;; +2A8E;GREATER-THAN ABOVE SIMILAR OR EQUAL;Sm;0;ON;;;;;Y;;;;; +2A8F;LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2A90;GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2A91;LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL;Sm;0;ON;;;;;Y;;;;; +2A92;GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL;Sm;0;ON;;;;;Y;;;;; +2A93;LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;; +2A94;GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;; +2A95;SLANTED EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2A96;SLANTED EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2A97;SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;; +2A98;SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;; +2A99;DOUBLE-LINE EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2A9A;DOUBLE-LINE EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2A9B;DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2A9C;DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2A9D;SIMILAR OR LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2A9E;SIMILAR OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2A9F;SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AA0;SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AA1;DOUBLE NESTED LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2AA2;DOUBLE NESTED GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2AA3;DOUBLE NESTED LESS-THAN WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; +2AA4;GREATER-THAN OVERLAPPING LESS-THAN;Sm;0;ON;;;;;N;;;;; +2AA5;GREATER-THAN BESIDE LESS-THAN;Sm;0;ON;;;;;N;;;;; +2AA6;LESS-THAN CLOSED BY CURVE;Sm;0;ON;;;;;Y;;;;; +2AA7;GREATER-THAN CLOSED BY CURVE;Sm;0;ON;;;;;Y;;;;; +2AA8;LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;; +2AA9;GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;; +2AAA;SMALLER THAN;Sm;0;ON;;;;;Y;;;;; +2AAB;LARGER THAN;Sm;0;ON;;;;;Y;;;;; +2AAC;SMALLER THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AAD;LARGER THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AAE;EQUALS SIGN WITH BUMPY ABOVE;Sm;0;ON;;;;;N;;;;; +2AAF;PRECEDES ABOVE SINGLE-LINE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AB0;SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AB1;PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AB2;SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AB3;PRECEDES ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AB4;SUCCEEDS ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AB5;PRECEDES ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AB6;SUCCEEDS ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AB7;PRECEDES ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AB8;SUCCEEDS ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AB9;PRECEDES ABOVE NOT ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2ABA;SUCCEEDS ABOVE NOT ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2ABB;DOUBLE PRECEDES;Sm;0;ON;;;;;Y;;;;; +2ABC;DOUBLE SUCCEEDS;Sm;0;ON;;;;;Y;;;;; +2ABD;SUBSET WITH DOT;Sm;0;ON;;;;;Y;;;;; +2ABE;SUPERSET WITH DOT;Sm;0;ON;;;;;Y;;;;; +2ABF;SUBSET WITH PLUS SIGN BELOW;Sm;0;ON;;;;;Y;;;;; +2AC0;SUPERSET WITH PLUS SIGN BELOW;Sm;0;ON;;;;;Y;;;;; +2AC1;SUBSET WITH MULTIPLICATION SIGN BELOW;Sm;0;ON;;;;;Y;;;;; +2AC2;SUPERSET WITH MULTIPLICATION SIGN BELOW;Sm;0;ON;;;;;Y;;;;; +2AC3;SUBSET OF OR EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; +2AC4;SUPERSET OF OR EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; +2AC5;SUBSET OF ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AC6;SUPERSET OF ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AC7;SUBSET OF ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; +2AC8;SUPERSET OF ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; +2AC9;SUBSET OF ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2ACA;SUPERSET OF ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2ACB;SUBSET OF ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2ACC;SUPERSET OF ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2ACD;SQUARE LEFT OPEN BOX OPERATOR;Sm;0;ON;;;;;Y;;;;; +2ACE;SQUARE RIGHT OPEN BOX OPERATOR;Sm;0;ON;;;;;Y;;;;; +2ACF;CLOSED SUBSET;Sm;0;ON;;;;;Y;;;;; +2AD0;CLOSED SUPERSET;Sm;0;ON;;;;;Y;;;;; +2AD1;CLOSED SUBSET OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AD2;CLOSED SUPERSET OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AD3;SUBSET ABOVE SUPERSET;Sm;0;ON;;;;;Y;;;;; +2AD4;SUPERSET ABOVE SUBSET;Sm;0;ON;;;;;Y;;;;; +2AD5;SUBSET ABOVE SUBSET;Sm;0;ON;;;;;Y;;;;; +2AD6;SUPERSET ABOVE SUPERSET;Sm;0;ON;;;;;Y;;;;; +2AD7;SUPERSET BESIDE SUBSET;Sm;0;ON;;;;;N;;;;; +2AD8;SUPERSET BESIDE AND JOINED BY DASH WITH SUBSET;Sm;0;ON;;;;;N;;;;; +2AD9;ELEMENT OF OPENING DOWNWARDS;Sm;0;ON;;;;;N;;;;; +2ADA;PITCHFORK WITH TEE TOP;Sm;0;ON;;;;;N;;;;; +2ADB;TRANSVERSAL INTERSECTION;Sm;0;ON;;;;;N;;;;; +2ADC;FORKING;Sm;0;ON;2ADD 0338;;;;Y;;;;; +2ADD;NONFORKING;Sm;0;ON;;;;;N;;;;; +2ADE;SHORT LEFT TACK;Sm;0;ON;;;;;Y;;;;; +2ADF;SHORT DOWN TACK;Sm;0;ON;;;;;N;;;;; +2AE0;SHORT UP TACK;Sm;0;ON;;;;;N;;;;; +2AE1;PERPENDICULAR WITH S;Sm;0;ON;;;;;N;;;;; +2AE2;VERTICAL BAR TRIPLE RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;; +2AE3;DOUBLE VERTICAL BAR LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;; +2AE4;VERTICAL BAR DOUBLE LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;; +2AE5;DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;; +2AE6;LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL;Sm;0;ON;;;;;Y;;;;; +2AE7;SHORT DOWN TACK WITH OVERBAR;Sm;0;ON;;;;;N;;;;; +2AE8;SHORT UP TACK WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; +2AE9;SHORT UP TACK ABOVE SHORT DOWN TACK;Sm;0;ON;;;;;N;;;;; +2AEA;DOUBLE DOWN TACK;Sm;0;ON;;;;;N;;;;; +2AEB;DOUBLE UP TACK;Sm;0;ON;;;;;N;;;;; +2AEC;DOUBLE STROKE NOT SIGN;Sm;0;ON;;;;;Y;;;;; +2AED;REVERSED DOUBLE STROKE NOT SIGN;Sm;0;ON;;;;;Y;;;;; +2AEE;DOES NOT DIVIDE WITH REVERSED NEGATION SLASH;Sm;0;ON;;;;;Y;;;;; +2AEF;VERTICAL LINE WITH CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;; +2AF0;VERTICAL LINE WITH CIRCLE BELOW;Sm;0;ON;;;;;N;;;;; +2AF1;DOWN TACK WITH CIRCLE BELOW;Sm;0;ON;;;;;N;;;;; +2AF2;PARALLEL WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; +2AF3;PARALLEL WITH TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; +2AF4;TRIPLE VERTICAL BAR BINARY RELATION;Sm;0;ON;;;;;N;;;;; +2AF5;TRIPLE VERTICAL BAR WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; +2AF6;TRIPLE COLON OPERATOR;Sm;0;ON;;;;;N;;;;; +2AF7;TRIPLE NESTED LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2AF8;TRIPLE NESTED GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2AF9;DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AFA;DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AFB;TRIPLE SOLIDUS BINARY RELATION;Sm;0;ON;;;;;Y;;;;; +2AFC;LARGE TRIPLE VERTICAL BAR OPERATOR;Sm;0;ON;;;;;N;;;;; +2AFD;DOUBLE SOLIDUS OPERATOR;Sm;0;ON;;;;;Y;;;;; +2AFE;WHITE VERTICAL BAR;Sm;0;ON;;;;;N;;;;; +2AFF;N-ARY WHITE VERTICAL BAR;Sm;0;ON;;;;;N;;;;; +2B00;NORTH EAST WHITE ARROW;So;0;ON;;;;;N;;;;; +2B01;NORTH WEST WHITE ARROW;So;0;ON;;;;;N;;;;; +2B02;SOUTH EAST WHITE ARROW;So;0;ON;;;;;N;;;;; +2B03;SOUTH WEST WHITE ARROW;So;0;ON;;;;;N;;;;; +2B04;LEFT RIGHT WHITE ARROW;So;0;ON;;;;;N;;;;; +2B05;LEFTWARDS BLACK ARROW;So;0;ON;;;;;N;;;;; +2B06;UPWARDS BLACK ARROW;So;0;ON;;;;;N;;;;; +2B07;DOWNWARDS BLACK ARROW;So;0;ON;;;;;N;;;;; +2B08;NORTH EAST BLACK ARROW;So;0;ON;;;;;N;;;;; +2B09;NORTH WEST BLACK ARROW;So;0;ON;;;;;N;;;;; +2B0A;SOUTH EAST BLACK ARROW;So;0;ON;;;;;N;;;;; +2B0B;SOUTH WEST BLACK ARROW;So;0;ON;;;;;N;;;;; +2B0C;LEFT RIGHT BLACK ARROW;So;0;ON;;;;;N;;;;; +2B0D;UP DOWN BLACK ARROW;So;0;ON;;;;;N;;;;; +2B0E;RIGHTWARDS ARROW WITH TIP DOWNWARDS;So;0;ON;;;;;N;;;;; +2B0F;RIGHTWARDS ARROW WITH TIP UPWARDS;So;0;ON;;;;;N;;;;; +2B10;LEFTWARDS ARROW WITH TIP DOWNWARDS;So;0;ON;;;;;N;;;;; +2B11;LEFTWARDS ARROW WITH TIP UPWARDS;So;0;ON;;;;;N;;;;; +2B12;SQUARE WITH TOP HALF BLACK;So;0;ON;;;;;N;;;;; +2B13;SQUARE WITH BOTTOM HALF BLACK;So;0;ON;;;;;N;;;;; +2B14;SQUARE WITH UPPER RIGHT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;; +2B15;SQUARE WITH LOWER LEFT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;; +2B16;DIAMOND WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;; +2B17;DIAMOND WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;; +2B18;DIAMOND WITH TOP HALF BLACK;So;0;ON;;;;;N;;;;; +2B19;DIAMOND WITH BOTTOM HALF BLACK;So;0;ON;;;;;N;;;;; +2B1A;DOTTED SQUARE;So;0;ON;;;;;N;;;;; +2B1B;BLACK LARGE SQUARE;So;0;ON;;;;;N;;;;; +2B1C;WHITE LARGE SQUARE;So;0;ON;;;;;N;;;;; +2B1D;BLACK VERY SMALL SQUARE;So;0;ON;;;;;N;;;;; +2B1E;WHITE VERY SMALL SQUARE;So;0;ON;;;;;N;;;;; +2B1F;BLACK PENTAGON;So;0;ON;;;;;N;;;;; +2B20;WHITE PENTAGON;So;0;ON;;;;;N;;;;; +2B21;WHITE HEXAGON;So;0;ON;;;;;N;;;;; +2B22;BLACK HEXAGON;So;0;ON;;;;;N;;;;; +2B23;HORIZONTAL BLACK HEXAGON;So;0;ON;;;;;N;;;;; +2B24;BLACK LARGE CIRCLE;So;0;ON;;;;;N;;;;; +2B25;BLACK MEDIUM DIAMOND;So;0;ON;;;;;N;;;;; +2B26;WHITE MEDIUM DIAMOND;So;0;ON;;;;;N;;;;; +2B27;BLACK MEDIUM LOZENGE;So;0;ON;;;;;N;;;;; +2B28;WHITE MEDIUM LOZENGE;So;0;ON;;;;;N;;;;; +2B29;BLACK SMALL DIAMOND;So;0;ON;;;;;N;;;;; +2B2A;BLACK SMALL LOZENGE;So;0;ON;;;;;N;;;;; +2B2B;WHITE SMALL LOZENGE;So;0;ON;;;;;N;;;;; +2B2C;BLACK HORIZONTAL ELLIPSE;So;0;ON;;;;;N;;;;; +2B2D;WHITE HORIZONTAL ELLIPSE;So;0;ON;;;;;N;;;;; +2B2E;BLACK VERTICAL ELLIPSE;So;0;ON;;;;;N;;;;; +2B2F;WHITE VERTICAL ELLIPSE;So;0;ON;;;;;N;;;;; +2B30;LEFT ARROW WITH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;; +2B31;THREE LEFTWARDS ARROWS;Sm;0;ON;;;;;N;;;;; +2B32;LEFT ARROW WITH CIRCLED PLUS;Sm;0;ON;;;;;N;;;;; +2B33;LONG LEFTWARDS SQUIGGLE ARROW;Sm;0;ON;;;;;N;;;;; +2B34;LEFTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2B35;LEFTWARDS TWO-HEADED ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2B36;LEFTWARDS TWO-HEADED ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +2B37;LEFTWARDS TWO-HEADED TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;; +2B38;LEFTWARDS ARROW WITH DOTTED STEM;Sm;0;ON;;;;;N;;;;; +2B39;LEFTWARDS ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2B3A;LEFTWARDS ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2B3B;LEFTWARDS TWO-HEADED ARROW WITH TAIL;Sm;0;ON;;;;;N;;;;; +2B3C;LEFTWARDS TWO-HEADED ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2B3D;LEFTWARDS TWO-HEADED ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2B3E;LEFTWARDS ARROW THROUGH X;Sm;0;ON;;;;;N;;;;; +2B3F;WAVE ARROW POINTING DIRECTLY LEFT;Sm;0;ON;;;;;N;;;;; +2B40;EQUALS SIGN ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2B41;REVERSE TILDE OPERATOR ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2B42;LEFTWARDS ARROW ABOVE REVERSE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;; +2B43;RIGHTWARDS ARROW THROUGH GREATER-THAN;Sm;0;ON;;;;;N;;;;; +2B44;RIGHTWARDS ARROW THROUGH SUPERSET;Sm;0;ON;;;;;N;;;;; +2B45;LEFTWARDS QUADRUPLE ARROW;So;0;ON;;;;;N;;;;; +2B46;RIGHTWARDS QUADRUPLE ARROW;So;0;ON;;;;;N;;;;; +2B47;REVERSE TILDE OPERATOR ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2B48;RIGHTWARDS ARROW ABOVE REVERSE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;; +2B49;TILDE OPERATOR ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2B4A;LEFTWARDS ARROW ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;; +2B4B;LEFTWARDS ARROW ABOVE REVERSE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;; +2B4C;RIGHTWARDS ARROW ABOVE REVERSE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;; +2B4D;DOWNWARDS TRIANGLE-HEADED ZIGZAG ARROW;So;0;ON;;;;;N;;;;; +2B4E;SHORT SLANTED NORTH ARROW;So;0;ON;;;;;N;;;;; +2B4F;SHORT BACKSLANTED SOUTH ARROW;So;0;ON;;;;;N;;;;; +2B50;WHITE MEDIUM STAR;So;0;ON;;;;;N;;;;; +2B51;BLACK SMALL STAR;So;0;ON;;;;;N;;;;; +2B52;WHITE SMALL STAR;So;0;ON;;;;;N;;;;; +2B53;BLACK RIGHT-POINTING PENTAGON;So;0;ON;;;;;N;;;;; +2B54;WHITE RIGHT-POINTING PENTAGON;So;0;ON;;;;;N;;;;; +2B55;HEAVY LARGE CIRCLE;So;0;ON;;;;;N;;;;; +2B56;HEAVY OVAL WITH OVAL INSIDE;So;0;ON;;;;;N;;;;; +2B57;HEAVY CIRCLE WITH CIRCLE INSIDE;So;0;ON;;;;;N;;;;; +2B58;HEAVY CIRCLE;So;0;ON;;;;;N;;;;; +2B59;HEAVY CIRCLED SALTIRE;So;0;ON;;;;;N;;;;; +2B5A;SLANTED NORTH ARROW WITH HOOKED HEAD;So;0;ON;;;;;N;;;;; +2B5B;BACKSLANTED SOUTH ARROW WITH HOOKED TAIL;So;0;ON;;;;;N;;;;; +2B5C;SLANTED NORTH ARROW WITH HORIZONTAL TAIL;So;0;ON;;;;;N;;;;; +2B5D;BACKSLANTED SOUTH ARROW WITH HORIZONTAL TAIL;So;0;ON;;;;;N;;;;; +2B5E;BENT ARROW POINTING DOWNWARDS THEN NORTH EAST;So;0;ON;;;;;N;;;;; +2B5F;SHORT BENT ARROW POINTING DOWNWARDS THEN NORTH EAST;So;0;ON;;;;;N;;;;; +2B60;LEFTWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; +2B61;UPWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; +2B62;RIGHTWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; +2B63;DOWNWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; +2B64;LEFT RIGHT TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; +2B65;UP DOWN TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; +2B66;NORTH WEST TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; +2B67;NORTH EAST TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; +2B68;SOUTH EAST TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; +2B69;SOUTH WEST TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; +2B6A;LEFTWARDS TRIANGLE-HEADED DASHED ARROW;So;0;ON;;;;;N;;;;; +2B6B;UPWARDS TRIANGLE-HEADED DASHED ARROW;So;0;ON;;;;;N;;;;; +2B6C;RIGHTWARDS TRIANGLE-HEADED DASHED ARROW;So;0;ON;;;;;N;;;;; +2B6D;DOWNWARDS TRIANGLE-HEADED DASHED ARROW;So;0;ON;;;;;N;;;;; +2B6E;CLOCKWISE TRIANGLE-HEADED OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;; +2B6F;ANTICLOCKWISE TRIANGLE-HEADED OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;; +2B70;LEFTWARDS TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; +2B71;UPWARDS TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; +2B72;RIGHTWARDS TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; +2B73;DOWNWARDS TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; +2B76;NORTH WEST TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; +2B77;NORTH EAST TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; +2B78;SOUTH EAST TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; +2B79;SOUTH WEST TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;; +2B7A;LEFTWARDS TRIANGLE-HEADED ARROW WITH DOUBLE HORIZONTAL STROKE;So;0;ON;;;;;N;;;;; +2B7B;UPWARDS TRIANGLE-HEADED ARROW WITH DOUBLE HORIZONTAL STROKE;So;0;ON;;;;;N;;;;; +2B7C;RIGHTWARDS TRIANGLE-HEADED ARROW WITH DOUBLE HORIZONTAL STROKE;So;0;ON;;;;;N;;;;; +2B7D;DOWNWARDS TRIANGLE-HEADED ARROW WITH DOUBLE HORIZONTAL STROKE;So;0;ON;;;;;N;;;;; +2B7E;HORIZONTAL TAB KEY;So;0;ON;;;;;N;;;;; +2B7F;VERTICAL TAB KEY;So;0;ON;;;;;N;;;;; +2B80;LEFTWARDS TRIANGLE-HEADED ARROW OVER RIGHTWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; +2B81;UPWARDS TRIANGLE-HEADED ARROW LEFTWARDS OF DOWNWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; +2B82;RIGHTWARDS TRIANGLE-HEADED ARROW OVER LEFTWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; +2B83;DOWNWARDS TRIANGLE-HEADED ARROW LEFTWARDS OF UPWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;; +2B84;LEFTWARDS TRIANGLE-HEADED PAIRED ARROWS;So;0;ON;;;;;N;;;;; +2B85;UPWARDS TRIANGLE-HEADED PAIRED ARROWS;So;0;ON;;;;;N;;;;; +2B86;RIGHTWARDS TRIANGLE-HEADED PAIRED ARROWS;So;0;ON;;;;;N;;;;; +2B87;DOWNWARDS TRIANGLE-HEADED PAIRED ARROWS;So;0;ON;;;;;N;;;;; +2B88;LEFTWARDS BLACK CIRCLED WHITE ARROW;So;0;ON;;;;;N;;;;; +2B89;UPWARDS BLACK CIRCLED WHITE ARROW;So;0;ON;;;;;N;;;;; +2B8A;RIGHTWARDS BLACK CIRCLED WHITE ARROW;So;0;ON;;;;;N;;;;; +2B8B;DOWNWARDS BLACK CIRCLED WHITE ARROW;So;0;ON;;;;;N;;;;; +2B8C;ANTICLOCKWISE TRIANGLE-HEADED RIGHT U-SHAPED ARROW;So;0;ON;;;;;N;;;;; +2B8D;ANTICLOCKWISE TRIANGLE-HEADED BOTTOM U-SHAPED ARROW;So;0;ON;;;;;N;;;;; +2B8E;ANTICLOCKWISE TRIANGLE-HEADED LEFT U-SHAPED ARROW;So;0;ON;;;;;N;;;;; +2B8F;ANTICLOCKWISE TRIANGLE-HEADED TOP U-SHAPED ARROW;So;0;ON;;;;;N;;;;; +2B90;RETURN LEFT;So;0;ON;;;;;N;;;;; +2B91;RETURN RIGHT;So;0;ON;;;;;N;;;;; +2B92;NEWLINE LEFT;So;0;ON;;;;;N;;;;; +2B93;NEWLINE RIGHT;So;0;ON;;;;;N;;;;; +2B94;FOUR CORNER ARROWS CIRCLING ANTICLOCKWISE;So;0;ON;;;;;N;;;;; +2B95;RIGHTWARDS BLACK ARROW;So;0;ON;;;;;N;;;;; +2B98;THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +2B99;THREE-D RIGHT-LIGHTED UPWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +2B9A;THREE-D TOP-LIGHTED RIGHTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +2B9B;THREE-D LEFT-LIGHTED DOWNWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +2B9C;BLACK LEFTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +2B9D;BLACK UPWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +2B9E;BLACK RIGHTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +2B9F;BLACK DOWNWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +2BA0;DOWNWARDS TRIANGLE-HEADED ARROW WITH LONG TIP LEFTWARDS;So;0;ON;;;;;N;;;;; +2BA1;DOWNWARDS TRIANGLE-HEADED ARROW WITH LONG TIP RIGHTWARDS;So;0;ON;;;;;N;;;;; +2BA2;UPWARDS TRIANGLE-HEADED ARROW WITH LONG TIP LEFTWARDS;So;0;ON;;;;;N;;;;; +2BA3;UPWARDS TRIANGLE-HEADED ARROW WITH LONG TIP RIGHTWARDS;So;0;ON;;;;;N;;;;; +2BA4;LEFTWARDS TRIANGLE-HEADED ARROW WITH LONG TIP UPWARDS;So;0;ON;;;;;N;;;;; +2BA5;RIGHTWARDS TRIANGLE-HEADED ARROW WITH LONG TIP UPWARDS;So;0;ON;;;;;N;;;;; +2BA6;LEFTWARDS TRIANGLE-HEADED ARROW WITH LONG TIP DOWNWARDS;So;0;ON;;;;;N;;;;; +2BA7;RIGHTWARDS TRIANGLE-HEADED ARROW WITH LONG TIP DOWNWARDS;So;0;ON;;;;;N;;;;; +2BA8;BLACK CURVED DOWNWARDS AND LEFTWARDS ARROW;So;0;ON;;;;;N;;;;; +2BA9;BLACK CURVED DOWNWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;;;;; +2BAA;BLACK CURVED UPWARDS AND LEFTWARDS ARROW;So;0;ON;;;;;N;;;;; +2BAB;BLACK CURVED UPWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;;;;; +2BAC;BLACK CURVED LEFTWARDS AND UPWARDS ARROW;So;0;ON;;;;;N;;;;; +2BAD;BLACK CURVED RIGHTWARDS AND UPWARDS ARROW;So;0;ON;;;;;N;;;;; +2BAE;BLACK CURVED LEFTWARDS AND DOWNWARDS ARROW;So;0;ON;;;;;N;;;;; +2BAF;BLACK CURVED RIGHTWARDS AND DOWNWARDS ARROW;So;0;ON;;;;;N;;;;; +2BB0;RIBBON ARROW DOWN LEFT;So;0;ON;;;;;N;;;;; +2BB1;RIBBON ARROW DOWN RIGHT;So;0;ON;;;;;N;;;;; +2BB2;RIBBON ARROW UP LEFT;So;0;ON;;;;;N;;;;; +2BB3;RIBBON ARROW UP RIGHT;So;0;ON;;;;;N;;;;; +2BB4;RIBBON ARROW LEFT UP;So;0;ON;;;;;N;;;;; +2BB5;RIBBON ARROW RIGHT UP;So;0;ON;;;;;N;;;;; +2BB6;RIBBON ARROW LEFT DOWN;So;0;ON;;;;;N;;;;; +2BB7;RIBBON ARROW RIGHT DOWN;So;0;ON;;;;;N;;;;; +2BB8;UPWARDS WHITE ARROW FROM BAR WITH HORIZONTAL BAR;So;0;ON;;;;;N;;;;; +2BB9;UP ARROWHEAD IN A RECTANGLE BOX;So;0;ON;;;;;N;;;;; +2BBD;BALLOT BOX WITH LIGHT X;So;0;ON;;;;;N;;;;; +2BBE;CIRCLED X;So;0;ON;;;;;N;;;;; +2BBF;CIRCLED BOLD X;So;0;ON;;;;;N;;;;; +2BC0;BLACK SQUARE CENTRED;So;0;ON;;;;;N;;;;; +2BC1;BLACK DIAMOND CENTRED;So;0;ON;;;;;N;;;;; +2BC2;TURNED BLACK PENTAGON;So;0;ON;;;;;N;;;;; +2BC3;HORIZONTAL BLACK OCTAGON;So;0;ON;;;;;N;;;;; +2BC4;BLACK OCTAGON;So;0;ON;;;;;N;;;;; +2BC5;BLACK MEDIUM UP-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;; +2BC6;BLACK MEDIUM DOWN-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;; +2BC7;BLACK MEDIUM LEFT-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;; +2BC8;BLACK MEDIUM RIGHT-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;; +2BCA;TOP HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;; +2BCB;BOTTOM HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;; +2BCC;LIGHT FOUR POINTED BLACK CUSP;So;0;ON;;;;;N;;;;; +2BCD;ROTATED LIGHT FOUR POINTED BLACK CUSP;So;0;ON;;;;;N;;;;; +2BCE;WHITE FOUR POINTED CUSP;So;0;ON;;;;;N;;;;; +2BCF;ROTATED WHITE FOUR POINTED CUSP;So;0;ON;;;;;N;;;;; +2BD0;SQUARE POSITION INDICATOR;So;0;ON;;;;;N;;;;; +2BD1;UNCERTAINTY SIGN;So;0;ON;;;;;N;;;;; +2BEC;LEFTWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS;So;0;ON;;;;;N;;;;; +2BED;UPWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS;So;0;ON;;;;;N;;;;; +2BEE;RIGHTWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS;So;0;ON;;;;;N;;;;; +2BEF;DOWNWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS;So;0;ON;;;;;N;;;;; +2C00;GLAGOLITIC CAPITAL LETTER AZU;Lu;0;L;;;;;N;;;;2C30; +2C01;GLAGOLITIC CAPITAL LETTER BUKY;Lu;0;L;;;;;N;;;;2C31; +2C02;GLAGOLITIC CAPITAL LETTER VEDE;Lu;0;L;;;;;N;;;;2C32; +2C03;GLAGOLITIC CAPITAL LETTER GLAGOLI;Lu;0;L;;;;;N;;;;2C33; +2C04;GLAGOLITIC CAPITAL LETTER DOBRO;Lu;0;L;;;;;N;;;;2C34; +2C05;GLAGOLITIC CAPITAL LETTER YESTU;Lu;0;L;;;;;N;;;;2C35; +2C06;GLAGOLITIC CAPITAL LETTER ZHIVETE;Lu;0;L;;;;;N;;;;2C36; +2C07;GLAGOLITIC CAPITAL LETTER DZELO;Lu;0;L;;;;;N;;;;2C37; +2C08;GLAGOLITIC CAPITAL LETTER ZEMLJA;Lu;0;L;;;;;N;;;;2C38; +2C09;GLAGOLITIC CAPITAL LETTER IZHE;Lu;0;L;;;;;N;;;;2C39; +2C0A;GLAGOLITIC CAPITAL LETTER INITIAL IZHE;Lu;0;L;;;;;N;;;;2C3A; +2C0B;GLAGOLITIC CAPITAL LETTER I;Lu;0;L;;;;;N;;;;2C3B; +2C0C;GLAGOLITIC CAPITAL LETTER DJERVI;Lu;0;L;;;;;N;;;;2C3C; +2C0D;GLAGOLITIC CAPITAL LETTER KAKO;Lu;0;L;;;;;N;;;;2C3D; +2C0E;GLAGOLITIC CAPITAL LETTER LJUDIJE;Lu;0;L;;;;;N;;;;2C3E; +2C0F;GLAGOLITIC CAPITAL LETTER MYSLITE;Lu;0;L;;;;;N;;;;2C3F; +2C10;GLAGOLITIC CAPITAL LETTER NASHI;Lu;0;L;;;;;N;;;;2C40; +2C11;GLAGOLITIC CAPITAL LETTER ONU;Lu;0;L;;;;;N;;;;2C41; +2C12;GLAGOLITIC CAPITAL LETTER POKOJI;Lu;0;L;;;;;N;;;;2C42; +2C13;GLAGOLITIC CAPITAL LETTER RITSI;Lu;0;L;;;;;N;;;;2C43; +2C14;GLAGOLITIC CAPITAL LETTER SLOVO;Lu;0;L;;;;;N;;;;2C44; +2C15;GLAGOLITIC CAPITAL LETTER TVRIDO;Lu;0;L;;;;;N;;;;2C45; +2C16;GLAGOLITIC CAPITAL LETTER UKU;Lu;0;L;;;;;N;;;;2C46; +2C17;GLAGOLITIC CAPITAL LETTER FRITU;Lu;0;L;;;;;N;;;;2C47; +2C18;GLAGOLITIC CAPITAL LETTER HERU;Lu;0;L;;;;;N;;;;2C48; +2C19;GLAGOLITIC CAPITAL LETTER OTU;Lu;0;L;;;;;N;;;;2C49; +2C1A;GLAGOLITIC CAPITAL LETTER PE;Lu;0;L;;;;;N;;;;2C4A; +2C1B;GLAGOLITIC CAPITAL LETTER SHTA;Lu;0;L;;;;;N;;;;2C4B; +2C1C;GLAGOLITIC CAPITAL LETTER TSI;Lu;0;L;;;;;N;;;;2C4C; +2C1D;GLAGOLITIC CAPITAL LETTER CHRIVI;Lu;0;L;;;;;N;;;;2C4D; +2C1E;GLAGOLITIC CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;2C4E; +2C1F;GLAGOLITIC CAPITAL LETTER YERU;Lu;0;L;;;;;N;;;;2C4F; +2C20;GLAGOLITIC CAPITAL LETTER YERI;Lu;0;L;;;;;N;;;;2C50; +2C21;GLAGOLITIC CAPITAL LETTER YATI;Lu;0;L;;;;;N;;;;2C51; +2C22;GLAGOLITIC CAPITAL LETTER SPIDERY HA;Lu;0;L;;;;;N;;;;2C52; +2C23;GLAGOLITIC CAPITAL LETTER YU;Lu;0;L;;;;;N;;;;2C53; +2C24;GLAGOLITIC CAPITAL LETTER SMALL YUS;Lu;0;L;;;;;N;;;;2C54; +2C25;GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL;Lu;0;L;;;;;N;;;;2C55; +2C26;GLAGOLITIC CAPITAL LETTER YO;Lu;0;L;;;;;N;;;;2C56; +2C27;GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS;Lu;0;L;;;;;N;;;;2C57; +2C28;GLAGOLITIC CAPITAL LETTER BIG YUS;Lu;0;L;;;;;N;;;;2C58; +2C29;GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS;Lu;0;L;;;;;N;;;;2C59; +2C2A;GLAGOLITIC CAPITAL LETTER FITA;Lu;0;L;;;;;N;;;;2C5A; +2C2B;GLAGOLITIC CAPITAL LETTER IZHITSA;Lu;0;L;;;;;N;;;;2C5B; +2C2C;GLAGOLITIC CAPITAL LETTER SHTAPIC;Lu;0;L;;;;;N;;;;2C5C; +2C2D;GLAGOLITIC CAPITAL LETTER TROKUTASTI A;Lu;0;L;;;;;N;;;;2C5D; +2C2E;GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE;Lu;0;L;;;;;N;;;;2C5E; +2C30;GLAGOLITIC SMALL LETTER AZU;Ll;0;L;;;;;N;;;2C00;;2C00 +2C31;GLAGOLITIC SMALL LETTER BUKY;Ll;0;L;;;;;N;;;2C01;;2C01 +2C32;GLAGOLITIC SMALL LETTER VEDE;Ll;0;L;;;;;N;;;2C02;;2C02 +2C33;GLAGOLITIC SMALL LETTER GLAGOLI;Ll;0;L;;;;;N;;;2C03;;2C03 +2C34;GLAGOLITIC SMALL LETTER DOBRO;Ll;0;L;;;;;N;;;2C04;;2C04 +2C35;GLAGOLITIC SMALL LETTER YESTU;Ll;0;L;;;;;N;;;2C05;;2C05 +2C36;GLAGOLITIC SMALL LETTER ZHIVETE;Ll;0;L;;;;;N;;;2C06;;2C06 +2C37;GLAGOLITIC SMALL LETTER DZELO;Ll;0;L;;;;;N;;;2C07;;2C07 +2C38;GLAGOLITIC SMALL LETTER ZEMLJA;Ll;0;L;;;;;N;;;2C08;;2C08 +2C39;GLAGOLITIC SMALL LETTER IZHE;Ll;0;L;;;;;N;;;2C09;;2C09 +2C3A;GLAGOLITIC SMALL LETTER INITIAL IZHE;Ll;0;L;;;;;N;;;2C0A;;2C0A +2C3B;GLAGOLITIC SMALL LETTER I;Ll;0;L;;;;;N;;;2C0B;;2C0B +2C3C;GLAGOLITIC SMALL LETTER DJERVI;Ll;0;L;;;;;N;;;2C0C;;2C0C +2C3D;GLAGOLITIC SMALL LETTER KAKO;Ll;0;L;;;;;N;;;2C0D;;2C0D +2C3E;GLAGOLITIC SMALL LETTER LJUDIJE;Ll;0;L;;;;;N;;;2C0E;;2C0E +2C3F;GLAGOLITIC SMALL LETTER MYSLITE;Ll;0;L;;;;;N;;;2C0F;;2C0F +2C40;GLAGOLITIC SMALL LETTER NASHI;Ll;0;L;;;;;N;;;2C10;;2C10 +2C41;GLAGOLITIC SMALL LETTER ONU;Ll;0;L;;;;;N;;;2C11;;2C11 +2C42;GLAGOLITIC SMALL LETTER POKOJI;Ll;0;L;;;;;N;;;2C12;;2C12 +2C43;GLAGOLITIC SMALL LETTER RITSI;Ll;0;L;;;;;N;;;2C13;;2C13 +2C44;GLAGOLITIC SMALL LETTER SLOVO;Ll;0;L;;;;;N;;;2C14;;2C14 +2C45;GLAGOLITIC SMALL LETTER TVRIDO;Ll;0;L;;;;;N;;;2C15;;2C15 +2C46;GLAGOLITIC SMALL LETTER UKU;Ll;0;L;;;;;N;;;2C16;;2C16 +2C47;GLAGOLITIC SMALL LETTER FRITU;Ll;0;L;;;;;N;;;2C17;;2C17 +2C48;GLAGOLITIC SMALL LETTER HERU;Ll;0;L;;;;;N;;;2C18;;2C18 +2C49;GLAGOLITIC SMALL LETTER OTU;Ll;0;L;;;;;N;;;2C19;;2C19 +2C4A;GLAGOLITIC SMALL LETTER PE;Ll;0;L;;;;;N;;;2C1A;;2C1A +2C4B;GLAGOLITIC SMALL LETTER SHTA;Ll;0;L;;;;;N;;;2C1B;;2C1B +2C4C;GLAGOLITIC SMALL LETTER TSI;Ll;0;L;;;;;N;;;2C1C;;2C1C +2C4D;GLAGOLITIC SMALL LETTER CHRIVI;Ll;0;L;;;;;N;;;2C1D;;2C1D +2C4E;GLAGOLITIC SMALL LETTER SHA;Ll;0;L;;;;;N;;;2C1E;;2C1E +2C4F;GLAGOLITIC SMALL LETTER YERU;Ll;0;L;;;;;N;;;2C1F;;2C1F +2C50;GLAGOLITIC SMALL LETTER YERI;Ll;0;L;;;;;N;;;2C20;;2C20 +2C51;GLAGOLITIC SMALL LETTER YATI;Ll;0;L;;;;;N;;;2C21;;2C21 +2C52;GLAGOLITIC SMALL LETTER SPIDERY HA;Ll;0;L;;;;;N;;;2C22;;2C22 +2C53;GLAGOLITIC SMALL LETTER YU;Ll;0;L;;;;;N;;;2C23;;2C23 +2C54;GLAGOLITIC SMALL LETTER SMALL YUS;Ll;0;L;;;;;N;;;2C24;;2C24 +2C55;GLAGOLITIC SMALL LETTER SMALL YUS WITH TAIL;Ll;0;L;;;;;N;;;2C25;;2C25 +2C56;GLAGOLITIC SMALL LETTER YO;Ll;0;L;;;;;N;;;2C26;;2C26 +2C57;GLAGOLITIC SMALL LETTER IOTATED SMALL YUS;Ll;0;L;;;;;N;;;2C27;;2C27 +2C58;GLAGOLITIC SMALL LETTER BIG YUS;Ll;0;L;;;;;N;;;2C28;;2C28 +2C59;GLAGOLITIC SMALL LETTER IOTATED BIG YUS;Ll;0;L;;;;;N;;;2C29;;2C29 +2C5A;GLAGOLITIC SMALL LETTER FITA;Ll;0;L;;;;;N;;;2C2A;;2C2A +2C5B;GLAGOLITIC SMALL LETTER IZHITSA;Ll;0;L;;;;;N;;;2C2B;;2C2B +2C5C;GLAGOLITIC SMALL LETTER SHTAPIC;Ll;0;L;;;;;N;;;2C2C;;2C2C +2C5D;GLAGOLITIC SMALL LETTER TROKUTASTI A;Ll;0;L;;;;;N;;;2C2D;;2C2D +2C5E;GLAGOLITIC SMALL LETTER LATINATE MYSLITE;Ll;0;L;;;;;N;;;2C2E;;2C2E +2C60;LATIN CAPITAL LETTER L WITH DOUBLE BAR;Lu;0;L;;;;;N;;;;2C61; +2C61;LATIN SMALL LETTER L WITH DOUBLE BAR;Ll;0;L;;;;;N;;;2C60;;2C60 +2C62;LATIN CAPITAL LETTER L WITH MIDDLE TILDE;Lu;0;L;;;;;N;;;;026B; +2C63;LATIN CAPITAL LETTER P WITH STROKE;Lu;0;L;;;;;N;;;;1D7D; +2C64;LATIN CAPITAL LETTER R WITH TAIL;Lu;0;L;;;;;N;;;;027D; +2C65;LATIN SMALL LETTER A WITH STROKE;Ll;0;L;;;;;N;;;023A;;023A +2C66;LATIN SMALL LETTER T WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;023E;;023E +2C67;LATIN CAPITAL LETTER H WITH DESCENDER;Lu;0;L;;;;;N;;;;2C68; +2C68;LATIN SMALL LETTER H WITH DESCENDER;Ll;0;L;;;;;N;;;2C67;;2C67 +2C69;LATIN CAPITAL LETTER K WITH DESCENDER;Lu;0;L;;;;;N;;;;2C6A; +2C6A;LATIN SMALL LETTER K WITH DESCENDER;Ll;0;L;;;;;N;;;2C69;;2C69 +2C6B;LATIN CAPITAL LETTER Z WITH DESCENDER;Lu;0;L;;;;;N;;;;2C6C; +2C6C;LATIN SMALL LETTER Z WITH DESCENDER;Ll;0;L;;;;;N;;;2C6B;;2C6B +2C6D;LATIN CAPITAL LETTER ALPHA;Lu;0;L;;;;;N;;;;0251; +2C6E;LATIN CAPITAL LETTER M WITH HOOK;Lu;0;L;;;;;N;;;;0271; +2C6F;LATIN CAPITAL LETTER TURNED A;Lu;0;L;;;;;N;;;;0250; +2C70;LATIN CAPITAL LETTER TURNED ALPHA;Lu;0;L;;;;;N;;;;0252; +2C71;LATIN SMALL LETTER V WITH RIGHT HOOK;Ll;0;L;;;;;N;;;;; +2C72;LATIN CAPITAL LETTER W WITH HOOK;Lu;0;L;;;;;N;;;;2C73; +2C73;LATIN SMALL LETTER W WITH HOOK;Ll;0;L;;;;;N;;;2C72;;2C72 +2C74;LATIN SMALL LETTER V WITH CURL;Ll;0;L;;;;;N;;;;; +2C75;LATIN CAPITAL LETTER HALF H;Lu;0;L;;;;;N;;;;2C76; +2C76;LATIN SMALL LETTER HALF H;Ll;0;L;;;;;N;;;2C75;;2C75 +2C77;LATIN SMALL LETTER TAILLESS PHI;Ll;0;L;;;;;N;;;;; +2C78;LATIN SMALL LETTER E WITH NOTCH;Ll;0;L;;;;;N;;;;; +2C79;LATIN SMALL LETTER TURNED R WITH TAIL;Ll;0;L;;;;;N;;;;; +2C7A;LATIN SMALL LETTER O WITH LOW RING INSIDE;Ll;0;L;;;;;N;;;;; +2C7B;LATIN LETTER SMALL CAPITAL TURNED E;Ll;0;L;;;;;N;;;;; +2C7C;LATIN SUBSCRIPT SMALL LETTER J;Lm;0;L;<sub> 006A;;;;N;;;;; +2C7D;MODIFIER LETTER CAPITAL V;Lm;0;L;<super> 0056;;;;N;;;;; +2C7E;LATIN CAPITAL LETTER S WITH SWASH TAIL;Lu;0;L;;;;;N;;;;023F; +2C7F;LATIN CAPITAL LETTER Z WITH SWASH TAIL;Lu;0;L;;;;;N;;;;0240; +2C80;COPTIC CAPITAL LETTER ALFA;Lu;0;L;;;;;N;;;;2C81; +2C81;COPTIC SMALL LETTER ALFA;Ll;0;L;;;;;N;;;2C80;;2C80 +2C82;COPTIC CAPITAL LETTER VIDA;Lu;0;L;;;;;N;;;;2C83; +2C83;COPTIC SMALL LETTER VIDA;Ll;0;L;;;;;N;;;2C82;;2C82 +2C84;COPTIC CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;2C85; +2C85;COPTIC SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;2C84;;2C84 +2C86;COPTIC CAPITAL LETTER DALDA;Lu;0;L;;;;;N;;;;2C87; +2C87;COPTIC SMALL LETTER DALDA;Ll;0;L;;;;;N;;;2C86;;2C86 +2C88;COPTIC CAPITAL LETTER EIE;Lu;0;L;;;;;N;;;;2C89; +2C89;COPTIC SMALL LETTER EIE;Ll;0;L;;;;;N;;;2C88;;2C88 +2C8A;COPTIC CAPITAL LETTER SOU;Lu;0;L;;;;;N;;;;2C8B; +2C8B;COPTIC SMALL LETTER SOU;Ll;0;L;;;;;N;;;2C8A;;2C8A +2C8C;COPTIC CAPITAL LETTER ZATA;Lu;0;L;;;;;N;;;;2C8D; +2C8D;COPTIC SMALL LETTER ZATA;Ll;0;L;;;;;N;;;2C8C;;2C8C +2C8E;COPTIC CAPITAL LETTER HATE;Lu;0;L;;;;;N;;;;2C8F; +2C8F;COPTIC SMALL LETTER HATE;Ll;0;L;;;;;N;;;2C8E;;2C8E +2C90;COPTIC CAPITAL LETTER THETHE;Lu;0;L;;;;;N;;;;2C91; +2C91;COPTIC SMALL LETTER THETHE;Ll;0;L;;;;;N;;;2C90;;2C90 +2C92;COPTIC CAPITAL LETTER IAUDA;Lu;0;L;;;;;N;;;;2C93; +2C93;COPTIC SMALL LETTER IAUDA;Ll;0;L;;;;;N;;;2C92;;2C92 +2C94;COPTIC CAPITAL LETTER KAPA;Lu;0;L;;;;;N;;;;2C95; +2C95;COPTIC SMALL LETTER KAPA;Ll;0;L;;;;;N;;;2C94;;2C94 +2C96;COPTIC CAPITAL LETTER LAULA;Lu;0;L;;;;;N;;;;2C97; +2C97;COPTIC SMALL LETTER LAULA;Ll;0;L;;;;;N;;;2C96;;2C96 +2C98;COPTIC CAPITAL LETTER MI;Lu;0;L;;;;;N;;;;2C99; +2C99;COPTIC SMALL LETTER MI;Ll;0;L;;;;;N;;;2C98;;2C98 +2C9A;COPTIC CAPITAL LETTER NI;Lu;0;L;;;;;N;;;;2C9B; +2C9B;COPTIC SMALL LETTER NI;Ll;0;L;;;;;N;;;2C9A;;2C9A +2C9C;COPTIC CAPITAL LETTER KSI;Lu;0;L;;;;;N;;;;2C9D; +2C9D;COPTIC SMALL LETTER KSI;Ll;0;L;;;;;N;;;2C9C;;2C9C +2C9E;COPTIC CAPITAL LETTER O;Lu;0;L;;;;;N;;;;2C9F; +2C9F;COPTIC SMALL LETTER O;Ll;0;L;;;;;N;;;2C9E;;2C9E +2CA0;COPTIC CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;2CA1; +2CA1;COPTIC SMALL LETTER PI;Ll;0;L;;;;;N;;;2CA0;;2CA0 +2CA2;COPTIC CAPITAL LETTER RO;Lu;0;L;;;;;N;;;;2CA3; +2CA3;COPTIC SMALL LETTER RO;Ll;0;L;;;;;N;;;2CA2;;2CA2 +2CA4;COPTIC CAPITAL LETTER SIMA;Lu;0;L;;;;;N;;;;2CA5; +2CA5;COPTIC SMALL LETTER SIMA;Ll;0;L;;;;;N;;;2CA4;;2CA4 +2CA6;COPTIC CAPITAL LETTER TAU;Lu;0;L;;;;;N;;;;2CA7; +2CA7;COPTIC SMALL LETTER TAU;Ll;0;L;;;;;N;;;2CA6;;2CA6 +2CA8;COPTIC CAPITAL LETTER UA;Lu;0;L;;;;;N;;;;2CA9; +2CA9;COPTIC SMALL LETTER UA;Ll;0;L;;;;;N;;;2CA8;;2CA8 +2CAA;COPTIC CAPITAL LETTER FI;Lu;0;L;;;;;N;;;;2CAB; +2CAB;COPTIC SMALL LETTER FI;Ll;0;L;;;;;N;;;2CAA;;2CAA +2CAC;COPTIC CAPITAL LETTER KHI;Lu;0;L;;;;;N;;;;2CAD; +2CAD;COPTIC SMALL LETTER KHI;Ll;0;L;;;;;N;;;2CAC;;2CAC +2CAE;COPTIC CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;2CAF; +2CAF;COPTIC SMALL LETTER PSI;Ll;0;L;;;;;N;;;2CAE;;2CAE +2CB0;COPTIC CAPITAL LETTER OOU;Lu;0;L;;;;;N;;;;2CB1; +2CB1;COPTIC SMALL LETTER OOU;Ll;0;L;;;;;N;;;2CB0;;2CB0 +2CB2;COPTIC CAPITAL LETTER DIALECT-P ALEF;Lu;0;L;;;;;N;;;;2CB3; +2CB3;COPTIC SMALL LETTER DIALECT-P ALEF;Ll;0;L;;;;;N;;;2CB2;;2CB2 +2CB4;COPTIC CAPITAL LETTER OLD COPTIC AIN;Lu;0;L;;;;;N;;;;2CB5; +2CB5;COPTIC SMALL LETTER OLD COPTIC AIN;Ll;0;L;;;;;N;;;2CB4;;2CB4 +2CB6;COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE;Lu;0;L;;;;;N;;;;2CB7; +2CB7;COPTIC SMALL LETTER CRYPTOGRAMMIC EIE;Ll;0;L;;;;;N;;;2CB6;;2CB6 +2CB8;COPTIC CAPITAL LETTER DIALECT-P KAPA;Lu;0;L;;;;;N;;;;2CB9; +2CB9;COPTIC SMALL LETTER DIALECT-P KAPA;Ll;0;L;;;;;N;;;2CB8;;2CB8 +2CBA;COPTIC CAPITAL LETTER DIALECT-P NI;Lu;0;L;;;;;N;;;;2CBB; +2CBB;COPTIC SMALL LETTER DIALECT-P NI;Ll;0;L;;;;;N;;;2CBA;;2CBA +2CBC;COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI;Lu;0;L;;;;;N;;;;2CBD; +2CBD;COPTIC SMALL LETTER CRYPTOGRAMMIC NI;Ll;0;L;;;;;N;;;2CBC;;2CBC +2CBE;COPTIC CAPITAL LETTER OLD COPTIC OOU;Lu;0;L;;;;;N;;;;2CBF; +2CBF;COPTIC SMALL LETTER OLD COPTIC OOU;Ll;0;L;;;;;N;;;2CBE;;2CBE +2CC0;COPTIC CAPITAL LETTER SAMPI;Lu;0;L;;;;;N;;;;2CC1; +2CC1;COPTIC SMALL LETTER SAMPI;Ll;0;L;;;;;N;;;2CC0;;2CC0 +2CC2;COPTIC CAPITAL LETTER CROSSED SHEI;Lu;0;L;;;;;N;;;;2CC3; +2CC3;COPTIC SMALL LETTER CROSSED SHEI;Ll;0;L;;;;;N;;;2CC2;;2CC2 +2CC4;COPTIC CAPITAL LETTER OLD COPTIC SHEI;Lu;0;L;;;;;N;;;;2CC5; +2CC5;COPTIC SMALL LETTER OLD COPTIC SHEI;Ll;0;L;;;;;N;;;2CC4;;2CC4 +2CC6;COPTIC CAPITAL LETTER OLD COPTIC ESH;Lu;0;L;;;;;N;;;;2CC7; +2CC7;COPTIC SMALL LETTER OLD COPTIC ESH;Ll;0;L;;;;;N;;;2CC6;;2CC6 +2CC8;COPTIC CAPITAL LETTER AKHMIMIC KHEI;Lu;0;L;;;;;N;;;;2CC9; +2CC9;COPTIC SMALL LETTER AKHMIMIC KHEI;Ll;0;L;;;;;N;;;2CC8;;2CC8 +2CCA;COPTIC CAPITAL LETTER DIALECT-P HORI;Lu;0;L;;;;;N;;;;2CCB; +2CCB;COPTIC SMALL LETTER DIALECT-P HORI;Ll;0;L;;;;;N;;;2CCA;;2CCA +2CCC;COPTIC CAPITAL LETTER OLD COPTIC HORI;Lu;0;L;;;;;N;;;;2CCD; +2CCD;COPTIC SMALL LETTER OLD COPTIC HORI;Ll;0;L;;;;;N;;;2CCC;;2CCC +2CCE;COPTIC CAPITAL LETTER OLD COPTIC HA;Lu;0;L;;;;;N;;;;2CCF; +2CCF;COPTIC SMALL LETTER OLD COPTIC HA;Ll;0;L;;;;;N;;;2CCE;;2CCE +2CD0;COPTIC CAPITAL LETTER L-SHAPED HA;Lu;0;L;;;;;N;;;;2CD1; +2CD1;COPTIC SMALL LETTER L-SHAPED HA;Ll;0;L;;;;;N;;;2CD0;;2CD0 +2CD2;COPTIC CAPITAL LETTER OLD COPTIC HEI;Lu;0;L;;;;;N;;;;2CD3; +2CD3;COPTIC SMALL LETTER OLD COPTIC HEI;Ll;0;L;;;;;N;;;2CD2;;2CD2 +2CD4;COPTIC CAPITAL LETTER OLD COPTIC HAT;Lu;0;L;;;;;N;;;;2CD5; +2CD5;COPTIC SMALL LETTER OLD COPTIC HAT;Ll;0;L;;;;;N;;;2CD4;;2CD4 +2CD6;COPTIC CAPITAL LETTER OLD COPTIC GANGIA;Lu;0;L;;;;;N;;;;2CD7; +2CD7;COPTIC SMALL LETTER OLD COPTIC GANGIA;Ll;0;L;;;;;N;;;2CD6;;2CD6 +2CD8;COPTIC CAPITAL LETTER OLD COPTIC DJA;Lu;0;L;;;;;N;;;;2CD9; +2CD9;COPTIC SMALL LETTER OLD COPTIC DJA;Ll;0;L;;;;;N;;;2CD8;;2CD8 +2CDA;COPTIC CAPITAL LETTER OLD COPTIC SHIMA;Lu;0;L;;;;;N;;;;2CDB; +2CDB;COPTIC SMALL LETTER OLD COPTIC SHIMA;Ll;0;L;;;;;N;;;2CDA;;2CDA +2CDC;COPTIC CAPITAL LETTER OLD NUBIAN SHIMA;Lu;0;L;;;;;N;;;;2CDD; +2CDD;COPTIC SMALL LETTER OLD NUBIAN SHIMA;Ll;0;L;;;;;N;;;2CDC;;2CDC +2CDE;COPTIC CAPITAL LETTER OLD NUBIAN NGI;Lu;0;L;;;;;N;;;;2CDF; +2CDF;COPTIC SMALL LETTER OLD NUBIAN NGI;Ll;0;L;;;;;N;;;2CDE;;2CDE +2CE0;COPTIC CAPITAL LETTER OLD NUBIAN NYI;Lu;0;L;;;;;N;;;;2CE1; +2CE1;COPTIC SMALL LETTER OLD NUBIAN NYI;Ll;0;L;;;;;N;;;2CE0;;2CE0 +2CE2;COPTIC CAPITAL LETTER OLD NUBIAN WAU;Lu;0;L;;;;;N;;;;2CE3; +2CE3;COPTIC SMALL LETTER OLD NUBIAN WAU;Ll;0;L;;;;;N;;;2CE2;;2CE2 +2CE4;COPTIC SYMBOL KAI;Ll;0;L;;;;;N;;;;; +2CE5;COPTIC SYMBOL MI RO;So;0;ON;;;;;N;;;;; +2CE6;COPTIC SYMBOL PI RO;So;0;ON;;;;;N;;;;; +2CE7;COPTIC SYMBOL STAUROS;So;0;ON;;;;;N;;;;; +2CE8;COPTIC SYMBOL TAU RO;So;0;ON;;;;;N;;;;; +2CE9;COPTIC SYMBOL KHI RO;So;0;ON;;;;;N;;;;; +2CEA;COPTIC SYMBOL SHIMA SIMA;So;0;ON;;;;;N;;;;; +2CEB;COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI;Lu;0;L;;;;;N;;;;2CEC; +2CEC;COPTIC SMALL LETTER CRYPTOGRAMMIC SHEI;Ll;0;L;;;;;N;;;2CEB;;2CEB +2CED;COPTIC CAPITAL LETTER CRYPTOGRAMMIC GANGIA;Lu;0;L;;;;;N;;;;2CEE; +2CEE;COPTIC SMALL LETTER CRYPTOGRAMMIC GANGIA;Ll;0;L;;;;;N;;;2CED;;2CED +2CEF;COPTIC COMBINING NI ABOVE;Mn;230;NSM;;;;;N;;;;; +2CF0;COPTIC COMBINING SPIRITUS ASPER;Mn;230;NSM;;;;;N;;;;; +2CF1;COPTIC COMBINING SPIRITUS LENIS;Mn;230;NSM;;;;;N;;;;; +2CF2;COPTIC CAPITAL LETTER BOHAIRIC KHEI;Lu;0;L;;;;;N;;;;2CF3; +2CF3;COPTIC SMALL LETTER BOHAIRIC KHEI;Ll;0;L;;;;;N;;;2CF2;;2CF2 +2CF9;COPTIC OLD NUBIAN FULL STOP;Po;0;ON;;;;;N;;;;; +2CFA;COPTIC OLD NUBIAN DIRECT QUESTION MARK;Po;0;ON;;;;;N;;;;; +2CFB;COPTIC OLD NUBIAN INDIRECT QUESTION MARK;Po;0;ON;;;;;N;;;;; +2CFC;COPTIC OLD NUBIAN VERSE DIVIDER;Po;0;ON;;;;;N;;;;; +2CFD;COPTIC FRACTION ONE HALF;No;0;ON;;;;1/2;N;;;;; +2CFE;COPTIC FULL STOP;Po;0;ON;;;;;N;;;;; +2CFF;COPTIC MORPHOLOGICAL DIVIDER;Po;0;ON;;;;;N;;;;; +2D00;GEORGIAN SMALL LETTER AN;Ll;0;L;;;;;N;;;10A0;;10A0 +2D01;GEORGIAN SMALL LETTER BAN;Ll;0;L;;;;;N;;;10A1;;10A1 +2D02;GEORGIAN SMALL LETTER GAN;Ll;0;L;;;;;N;;;10A2;;10A2 +2D03;GEORGIAN SMALL LETTER DON;Ll;0;L;;;;;N;;;10A3;;10A3 +2D04;GEORGIAN SMALL LETTER EN;Ll;0;L;;;;;N;;;10A4;;10A4 +2D05;GEORGIAN SMALL LETTER VIN;Ll;0;L;;;;;N;;;10A5;;10A5 +2D06;GEORGIAN SMALL LETTER ZEN;Ll;0;L;;;;;N;;;10A6;;10A6 +2D07;GEORGIAN SMALL LETTER TAN;Ll;0;L;;;;;N;;;10A7;;10A7 +2D08;GEORGIAN SMALL LETTER IN;Ll;0;L;;;;;N;;;10A8;;10A8 +2D09;GEORGIAN SMALL LETTER KAN;Ll;0;L;;;;;N;;;10A9;;10A9 +2D0A;GEORGIAN SMALL LETTER LAS;Ll;0;L;;;;;N;;;10AA;;10AA +2D0B;GEORGIAN SMALL LETTER MAN;Ll;0;L;;;;;N;;;10AB;;10AB +2D0C;GEORGIAN SMALL LETTER NAR;Ll;0;L;;;;;N;;;10AC;;10AC +2D0D;GEORGIAN SMALL LETTER ON;Ll;0;L;;;;;N;;;10AD;;10AD +2D0E;GEORGIAN SMALL LETTER PAR;Ll;0;L;;;;;N;;;10AE;;10AE +2D0F;GEORGIAN SMALL LETTER ZHAR;Ll;0;L;;;;;N;;;10AF;;10AF +2D10;GEORGIAN SMALL LETTER RAE;Ll;0;L;;;;;N;;;10B0;;10B0 +2D11;GEORGIAN SMALL LETTER SAN;Ll;0;L;;;;;N;;;10B1;;10B1 +2D12;GEORGIAN SMALL LETTER TAR;Ll;0;L;;;;;N;;;10B2;;10B2 +2D13;GEORGIAN SMALL LETTER UN;Ll;0;L;;;;;N;;;10B3;;10B3 +2D14;GEORGIAN SMALL LETTER PHAR;Ll;0;L;;;;;N;;;10B4;;10B4 +2D15;GEORGIAN SMALL LETTER KHAR;Ll;0;L;;;;;N;;;10B5;;10B5 +2D16;GEORGIAN SMALL LETTER GHAN;Ll;0;L;;;;;N;;;10B6;;10B6 +2D17;GEORGIAN SMALL LETTER QAR;Ll;0;L;;;;;N;;;10B7;;10B7 +2D18;GEORGIAN SMALL LETTER SHIN;Ll;0;L;;;;;N;;;10B8;;10B8 +2D19;GEORGIAN SMALL LETTER CHIN;Ll;0;L;;;;;N;;;10B9;;10B9 +2D1A;GEORGIAN SMALL LETTER CAN;Ll;0;L;;;;;N;;;10BA;;10BA +2D1B;GEORGIAN SMALL LETTER JIL;Ll;0;L;;;;;N;;;10BB;;10BB +2D1C;GEORGIAN SMALL LETTER CIL;Ll;0;L;;;;;N;;;10BC;;10BC +2D1D;GEORGIAN SMALL LETTER CHAR;Ll;0;L;;;;;N;;;10BD;;10BD +2D1E;GEORGIAN SMALL LETTER XAN;Ll;0;L;;;;;N;;;10BE;;10BE +2D1F;GEORGIAN SMALL LETTER JHAN;Ll;0;L;;;;;N;;;10BF;;10BF +2D20;GEORGIAN SMALL LETTER HAE;Ll;0;L;;;;;N;;;10C0;;10C0 +2D21;GEORGIAN SMALL LETTER HE;Ll;0;L;;;;;N;;;10C1;;10C1 +2D22;GEORGIAN SMALL LETTER HIE;Ll;0;L;;;;;N;;;10C2;;10C2 +2D23;GEORGIAN SMALL LETTER WE;Ll;0;L;;;;;N;;;10C3;;10C3 +2D24;GEORGIAN SMALL LETTER HAR;Ll;0;L;;;;;N;;;10C4;;10C4 +2D25;GEORGIAN SMALL LETTER HOE;Ll;0;L;;;;;N;;;10C5;;10C5 +2D27;GEORGIAN SMALL LETTER YN;Ll;0;L;;;;;N;;;10C7;;10C7 +2D2D;GEORGIAN SMALL LETTER AEN;Ll;0;L;;;;;N;;;10CD;;10CD +2D30;TIFINAGH LETTER YA;Lo;0;L;;;;;N;;;;; +2D31;TIFINAGH LETTER YAB;Lo;0;L;;;;;N;;;;; +2D32;TIFINAGH LETTER YABH;Lo;0;L;;;;;N;;;;; +2D33;TIFINAGH LETTER YAG;Lo;0;L;;;;;N;;;;; +2D34;TIFINAGH LETTER YAGHH;Lo;0;L;;;;;N;;;;; +2D35;TIFINAGH LETTER BERBER ACADEMY YAJ;Lo;0;L;;;;;N;;;;; +2D36;TIFINAGH LETTER YAJ;Lo;0;L;;;;;N;;;;; +2D37;TIFINAGH LETTER YAD;Lo;0;L;;;;;N;;;;; +2D38;TIFINAGH LETTER YADH;Lo;0;L;;;;;N;;;;; +2D39;TIFINAGH LETTER YADD;Lo;0;L;;;;;N;;;;; +2D3A;TIFINAGH LETTER YADDH;Lo;0;L;;;;;N;;;;; +2D3B;TIFINAGH LETTER YEY;Lo;0;L;;;;;N;;;;; +2D3C;TIFINAGH LETTER YAF;Lo;0;L;;;;;N;;;;; +2D3D;TIFINAGH LETTER YAK;Lo;0;L;;;;;N;;;;; +2D3E;TIFINAGH LETTER TUAREG YAK;Lo;0;L;;;;;N;;;;; +2D3F;TIFINAGH LETTER YAKHH;Lo;0;L;;;;;N;;;;; +2D40;TIFINAGH LETTER YAH;Lo;0;L;;;;;N;;;;; +2D41;TIFINAGH LETTER BERBER ACADEMY YAH;Lo;0;L;;;;;N;;;;; +2D42;TIFINAGH LETTER TUAREG YAH;Lo;0;L;;;;;N;;;;; +2D43;TIFINAGH LETTER YAHH;Lo;0;L;;;;;N;;;;; +2D44;TIFINAGH LETTER YAA;Lo;0;L;;;;;N;;;;; +2D45;TIFINAGH LETTER YAKH;Lo;0;L;;;;;N;;;;; +2D46;TIFINAGH LETTER TUAREG YAKH;Lo;0;L;;;;;N;;;;; +2D47;TIFINAGH LETTER YAQ;Lo;0;L;;;;;N;;;;; +2D48;TIFINAGH LETTER TUAREG YAQ;Lo;0;L;;;;;N;;;;; +2D49;TIFINAGH LETTER YI;Lo;0;L;;;;;N;;;;; +2D4A;TIFINAGH LETTER YAZH;Lo;0;L;;;;;N;;;;; +2D4B;TIFINAGH LETTER AHAGGAR YAZH;Lo;0;L;;;;;N;;;;; +2D4C;TIFINAGH LETTER TUAREG YAZH;Lo;0;L;;;;;N;;;;; +2D4D;TIFINAGH LETTER YAL;Lo;0;L;;;;;N;;;;; +2D4E;TIFINAGH LETTER YAM;Lo;0;L;;;;;N;;;;; +2D4F;TIFINAGH LETTER YAN;Lo;0;L;;;;;N;;;;; +2D50;TIFINAGH LETTER TUAREG YAGN;Lo;0;L;;;;;N;;;;; +2D51;TIFINAGH LETTER TUAREG YANG;Lo;0;L;;;;;N;;;;; +2D52;TIFINAGH LETTER YAP;Lo;0;L;;;;;N;;;;; +2D53;TIFINAGH LETTER YU;Lo;0;L;;;;;N;;;;; +2D54;TIFINAGH LETTER YAR;Lo;0;L;;;;;N;;;;; +2D55;TIFINAGH LETTER YARR;Lo;0;L;;;;;N;;;;; +2D56;TIFINAGH LETTER YAGH;Lo;0;L;;;;;N;;;;; +2D57;TIFINAGH LETTER TUAREG YAGH;Lo;0;L;;;;;N;;;;; +2D58;TIFINAGH LETTER AYER YAGH;Lo;0;L;;;;;N;;;;; +2D59;TIFINAGH LETTER YAS;Lo;0;L;;;;;N;;;;; +2D5A;TIFINAGH LETTER YASS;Lo;0;L;;;;;N;;;;; +2D5B;TIFINAGH LETTER YASH;Lo;0;L;;;;;N;;;;; +2D5C;TIFINAGH LETTER YAT;Lo;0;L;;;;;N;;;;; +2D5D;TIFINAGH LETTER YATH;Lo;0;L;;;;;N;;;;; +2D5E;TIFINAGH LETTER YACH;Lo;0;L;;;;;N;;;;; +2D5F;TIFINAGH LETTER YATT;Lo;0;L;;;;;N;;;;; +2D60;TIFINAGH LETTER YAV;Lo;0;L;;;;;N;;;;; +2D61;TIFINAGH LETTER YAW;Lo;0;L;;;;;N;;;;; +2D62;TIFINAGH LETTER YAY;Lo;0;L;;;;;N;;;;; +2D63;TIFINAGH LETTER YAZ;Lo;0;L;;;;;N;;;;; +2D64;TIFINAGH LETTER TAWELLEMET YAZ;Lo;0;L;;;;;N;;;;; +2D65;TIFINAGH LETTER YAZZ;Lo;0;L;;;;;N;;;;; +2D66;TIFINAGH LETTER YE;Lo;0;L;;;;;N;;;;; +2D67;TIFINAGH LETTER YO;Lo;0;L;;;;;N;;;;; +2D6F;TIFINAGH MODIFIER LETTER LABIALIZATION MARK;Lm;0;L;<super> 2D61;;;;N;;;;; +2D70;TIFINAGH SEPARATOR MARK;Po;0;L;;;;;N;;;;; +2D7F;TIFINAGH CONSONANT JOINER;Mn;9;NSM;;;;;N;;;;; +2D80;ETHIOPIC SYLLABLE LOA;Lo;0;L;;;;;N;;;;; +2D81;ETHIOPIC SYLLABLE MOA;Lo;0;L;;;;;N;;;;; +2D82;ETHIOPIC SYLLABLE ROA;Lo;0;L;;;;;N;;;;; +2D83;ETHIOPIC SYLLABLE SOA;Lo;0;L;;;;;N;;;;; +2D84;ETHIOPIC SYLLABLE SHOA;Lo;0;L;;;;;N;;;;; +2D85;ETHIOPIC SYLLABLE BOA;Lo;0;L;;;;;N;;;;; +2D86;ETHIOPIC SYLLABLE TOA;Lo;0;L;;;;;N;;;;; +2D87;ETHIOPIC SYLLABLE COA;Lo;0;L;;;;;N;;;;; +2D88;ETHIOPIC SYLLABLE NOA;Lo;0;L;;;;;N;;;;; +2D89;ETHIOPIC SYLLABLE NYOA;Lo;0;L;;;;;N;;;;; +2D8A;ETHIOPIC SYLLABLE GLOTTAL OA;Lo;0;L;;;;;N;;;;; +2D8B;ETHIOPIC SYLLABLE ZOA;Lo;0;L;;;;;N;;;;; +2D8C;ETHIOPIC SYLLABLE DOA;Lo;0;L;;;;;N;;;;; +2D8D;ETHIOPIC SYLLABLE DDOA;Lo;0;L;;;;;N;;;;; +2D8E;ETHIOPIC SYLLABLE JOA;Lo;0;L;;;;;N;;;;; +2D8F;ETHIOPIC SYLLABLE THOA;Lo;0;L;;;;;N;;;;; +2D90;ETHIOPIC SYLLABLE CHOA;Lo;0;L;;;;;N;;;;; +2D91;ETHIOPIC SYLLABLE PHOA;Lo;0;L;;;;;N;;;;; +2D92;ETHIOPIC SYLLABLE POA;Lo;0;L;;;;;N;;;;; +2D93;ETHIOPIC SYLLABLE GGWA;Lo;0;L;;;;;N;;;;; +2D94;ETHIOPIC SYLLABLE GGWI;Lo;0;L;;;;;N;;;;; +2D95;ETHIOPIC SYLLABLE GGWEE;Lo;0;L;;;;;N;;;;; +2D96;ETHIOPIC SYLLABLE GGWE;Lo;0;L;;;;;N;;;;; +2DA0;ETHIOPIC SYLLABLE SSA;Lo;0;L;;;;;N;;;;; +2DA1;ETHIOPIC SYLLABLE SSU;Lo;0;L;;;;;N;;;;; +2DA2;ETHIOPIC SYLLABLE SSI;Lo;0;L;;;;;N;;;;; +2DA3;ETHIOPIC SYLLABLE SSAA;Lo;0;L;;;;;N;;;;; +2DA4;ETHIOPIC SYLLABLE SSEE;Lo;0;L;;;;;N;;;;; +2DA5;ETHIOPIC SYLLABLE SSE;Lo;0;L;;;;;N;;;;; +2DA6;ETHIOPIC SYLLABLE SSO;Lo;0;L;;;;;N;;;;; +2DA8;ETHIOPIC SYLLABLE CCA;Lo;0;L;;;;;N;;;;; +2DA9;ETHIOPIC SYLLABLE CCU;Lo;0;L;;;;;N;;;;; +2DAA;ETHIOPIC SYLLABLE CCI;Lo;0;L;;;;;N;;;;; +2DAB;ETHIOPIC SYLLABLE CCAA;Lo;0;L;;;;;N;;;;; +2DAC;ETHIOPIC SYLLABLE CCEE;Lo;0;L;;;;;N;;;;; +2DAD;ETHIOPIC SYLLABLE CCE;Lo;0;L;;;;;N;;;;; +2DAE;ETHIOPIC SYLLABLE CCO;Lo;0;L;;;;;N;;;;; +2DB0;ETHIOPIC SYLLABLE ZZA;Lo;0;L;;;;;N;;;;; +2DB1;ETHIOPIC SYLLABLE ZZU;Lo;0;L;;;;;N;;;;; +2DB2;ETHIOPIC SYLLABLE ZZI;Lo;0;L;;;;;N;;;;; +2DB3;ETHIOPIC SYLLABLE ZZAA;Lo;0;L;;;;;N;;;;; +2DB4;ETHIOPIC SYLLABLE ZZEE;Lo;0;L;;;;;N;;;;; +2DB5;ETHIOPIC SYLLABLE ZZE;Lo;0;L;;;;;N;;;;; +2DB6;ETHIOPIC SYLLABLE ZZO;Lo;0;L;;;;;N;;;;; +2DB8;ETHIOPIC SYLLABLE CCHA;Lo;0;L;;;;;N;;;;; +2DB9;ETHIOPIC SYLLABLE CCHU;Lo;0;L;;;;;N;;;;; +2DBA;ETHIOPIC SYLLABLE CCHI;Lo;0;L;;;;;N;;;;; +2DBB;ETHIOPIC SYLLABLE CCHAA;Lo;0;L;;;;;N;;;;; +2DBC;ETHIOPIC SYLLABLE CCHEE;Lo;0;L;;;;;N;;;;; +2DBD;ETHIOPIC SYLLABLE CCHE;Lo;0;L;;;;;N;;;;; +2DBE;ETHIOPIC SYLLABLE CCHO;Lo;0;L;;;;;N;;;;; +2DC0;ETHIOPIC SYLLABLE QYA;Lo;0;L;;;;;N;;;;; +2DC1;ETHIOPIC SYLLABLE QYU;Lo;0;L;;;;;N;;;;; +2DC2;ETHIOPIC SYLLABLE QYI;Lo;0;L;;;;;N;;;;; +2DC3;ETHIOPIC SYLLABLE QYAA;Lo;0;L;;;;;N;;;;; +2DC4;ETHIOPIC SYLLABLE QYEE;Lo;0;L;;;;;N;;;;; +2DC5;ETHIOPIC SYLLABLE QYE;Lo;0;L;;;;;N;;;;; +2DC6;ETHIOPIC SYLLABLE QYO;Lo;0;L;;;;;N;;;;; +2DC8;ETHIOPIC SYLLABLE KYA;Lo;0;L;;;;;N;;;;; +2DC9;ETHIOPIC SYLLABLE KYU;Lo;0;L;;;;;N;;;;; +2DCA;ETHIOPIC SYLLABLE KYI;Lo;0;L;;;;;N;;;;; +2DCB;ETHIOPIC SYLLABLE KYAA;Lo;0;L;;;;;N;;;;; +2DCC;ETHIOPIC SYLLABLE KYEE;Lo;0;L;;;;;N;;;;; +2DCD;ETHIOPIC SYLLABLE KYE;Lo;0;L;;;;;N;;;;; +2DCE;ETHIOPIC SYLLABLE KYO;Lo;0;L;;;;;N;;;;; +2DD0;ETHIOPIC SYLLABLE XYA;Lo;0;L;;;;;N;;;;; +2DD1;ETHIOPIC SYLLABLE XYU;Lo;0;L;;;;;N;;;;; +2DD2;ETHIOPIC SYLLABLE XYI;Lo;0;L;;;;;N;;;;; +2DD3;ETHIOPIC SYLLABLE XYAA;Lo;0;L;;;;;N;;;;; +2DD4;ETHIOPIC SYLLABLE XYEE;Lo;0;L;;;;;N;;;;; +2DD5;ETHIOPIC SYLLABLE XYE;Lo;0;L;;;;;N;;;;; +2DD6;ETHIOPIC SYLLABLE XYO;Lo;0;L;;;;;N;;;;; +2DD8;ETHIOPIC SYLLABLE GYA;Lo;0;L;;;;;N;;;;; +2DD9;ETHIOPIC SYLLABLE GYU;Lo;0;L;;;;;N;;;;; +2DDA;ETHIOPIC SYLLABLE GYI;Lo;0;L;;;;;N;;;;; +2DDB;ETHIOPIC SYLLABLE GYAA;Lo;0;L;;;;;N;;;;; +2DDC;ETHIOPIC SYLLABLE GYEE;Lo;0;L;;;;;N;;;;; +2DDD;ETHIOPIC SYLLABLE GYE;Lo;0;L;;;;;N;;;;; +2DDE;ETHIOPIC SYLLABLE GYO;Lo;0;L;;;;;N;;;;; +2DE0;COMBINING CYRILLIC LETTER BE;Mn;230;NSM;;;;;N;;;;; +2DE1;COMBINING CYRILLIC LETTER VE;Mn;230;NSM;;;;;N;;;;; +2DE2;COMBINING CYRILLIC LETTER GHE;Mn;230;NSM;;;;;N;;;;; +2DE3;COMBINING CYRILLIC LETTER DE;Mn;230;NSM;;;;;N;;;;; +2DE4;COMBINING CYRILLIC LETTER ZHE;Mn;230;NSM;;;;;N;;;;; +2DE5;COMBINING CYRILLIC LETTER ZE;Mn;230;NSM;;;;;N;;;;; +2DE6;COMBINING CYRILLIC LETTER KA;Mn;230;NSM;;;;;N;;;;; +2DE7;COMBINING CYRILLIC LETTER EL;Mn;230;NSM;;;;;N;;;;; +2DE8;COMBINING CYRILLIC LETTER EM;Mn;230;NSM;;;;;N;;;;; +2DE9;COMBINING CYRILLIC LETTER EN;Mn;230;NSM;;;;;N;;;;; +2DEA;COMBINING CYRILLIC LETTER O;Mn;230;NSM;;;;;N;;;;; +2DEB;COMBINING CYRILLIC LETTER PE;Mn;230;NSM;;;;;N;;;;; +2DEC;COMBINING CYRILLIC LETTER ER;Mn;230;NSM;;;;;N;;;;; +2DED;COMBINING CYRILLIC LETTER ES;Mn;230;NSM;;;;;N;;;;; +2DEE;COMBINING CYRILLIC LETTER TE;Mn;230;NSM;;;;;N;;;;; +2DEF;COMBINING CYRILLIC LETTER HA;Mn;230;NSM;;;;;N;;;;; +2DF0;COMBINING CYRILLIC LETTER TSE;Mn;230;NSM;;;;;N;;;;; +2DF1;COMBINING CYRILLIC LETTER CHE;Mn;230;NSM;;;;;N;;;;; +2DF2;COMBINING CYRILLIC LETTER SHA;Mn;230;NSM;;;;;N;;;;; +2DF3;COMBINING CYRILLIC LETTER SHCHA;Mn;230;NSM;;;;;N;;;;; +2DF4;COMBINING CYRILLIC LETTER FITA;Mn;230;NSM;;;;;N;;;;; +2DF5;COMBINING CYRILLIC LETTER ES-TE;Mn;230;NSM;;;;;N;;;;; +2DF6;COMBINING CYRILLIC LETTER A;Mn;230;NSM;;;;;N;;;;; +2DF7;COMBINING CYRILLIC LETTER IE;Mn;230;NSM;;;;;N;;;;; +2DF8;COMBINING CYRILLIC LETTER DJERV;Mn;230;NSM;;;;;N;;;;; +2DF9;COMBINING CYRILLIC LETTER MONOGRAPH UK;Mn;230;NSM;;;;;N;;;;; +2DFA;COMBINING CYRILLIC LETTER YAT;Mn;230;NSM;;;;;N;;;;; +2DFB;COMBINING CYRILLIC LETTER YU;Mn;230;NSM;;;;;N;;;;; +2DFC;COMBINING CYRILLIC LETTER IOTIFIED A;Mn;230;NSM;;;;;N;;;;; +2DFD;COMBINING CYRILLIC LETTER LITTLE YUS;Mn;230;NSM;;;;;N;;;;; +2DFE;COMBINING CYRILLIC LETTER BIG YUS;Mn;230;NSM;;;;;N;;;;; +2DFF;COMBINING CYRILLIC LETTER IOTIFIED BIG YUS;Mn;230;NSM;;;;;N;;;;; +2E00;RIGHT ANGLE SUBSTITUTION MARKER;Po;0;ON;;;;;N;;;;; +2E01;RIGHT ANGLE DOTTED SUBSTITUTION MARKER;Po;0;ON;;;;;N;;;;; +2E02;LEFT SUBSTITUTION BRACKET;Pi;0;ON;;;;;Y;;;;; +2E03;RIGHT SUBSTITUTION BRACKET;Pf;0;ON;;;;;Y;;;;; +2E04;LEFT DOTTED SUBSTITUTION BRACKET;Pi;0;ON;;;;;Y;;;;; +2E05;RIGHT DOTTED SUBSTITUTION BRACKET;Pf;0;ON;;;;;Y;;;;; +2E06;RAISED INTERPOLATION MARKER;Po;0;ON;;;;;N;;;;; +2E07;RAISED DOTTED INTERPOLATION MARKER;Po;0;ON;;;;;N;;;;; +2E08;DOTTED TRANSPOSITION MARKER;Po;0;ON;;;;;N;;;;; +2E09;LEFT TRANSPOSITION BRACKET;Pi;0;ON;;;;;Y;;;;; +2E0A;RIGHT TRANSPOSITION BRACKET;Pf;0;ON;;;;;Y;;;;; +2E0B;RAISED SQUARE;Po;0;ON;;;;;N;;;;; +2E0C;LEFT RAISED OMISSION BRACKET;Pi;0;ON;;;;;Y;;;;; +2E0D;RIGHT RAISED OMISSION BRACKET;Pf;0;ON;;;;;Y;;;;; +2E0E;EDITORIAL CORONIS;Po;0;ON;;;;;N;;;;; +2E0F;PARAGRAPHOS;Po;0;ON;;;;;N;;;;; +2E10;FORKED PARAGRAPHOS;Po;0;ON;;;;;N;;;;; +2E11;REVERSED FORKED PARAGRAPHOS;Po;0;ON;;;;;N;;;;; +2E12;HYPODIASTOLE;Po;0;ON;;;;;N;;;;; +2E13;DOTTED OBELOS;Po;0;ON;;;;;N;;;;; +2E14;DOWNWARDS ANCORA;Po;0;ON;;;;;N;;;;; +2E15;UPWARDS ANCORA;Po;0;ON;;;;;N;;;;; +2E16;DOTTED RIGHT-POINTING ANGLE;Po;0;ON;;;;;N;;;;; +2E17;DOUBLE OBLIQUE HYPHEN;Pd;0;ON;;;;;N;;;;; +2E18;INVERTED INTERROBANG;Po;0;ON;;;;;N;;;;; +2E19;PALM BRANCH;Po;0;ON;;;;;N;;;;; +2E1A;HYPHEN WITH DIAERESIS;Pd;0;ON;;;;;N;;;;; +2E1B;TILDE WITH RING ABOVE;Po;0;ON;;;;;N;;;;; +2E1C;LEFT LOW PARAPHRASE BRACKET;Pi;0;ON;;;;;Y;;;;; +2E1D;RIGHT LOW PARAPHRASE BRACKET;Pf;0;ON;;;;;Y;;;;; +2E1E;TILDE WITH DOT ABOVE;Po;0;ON;;;;;N;;;;; +2E1F;TILDE WITH DOT BELOW;Po;0;ON;;;;;N;;;;; +2E20;LEFT VERTICAL BAR WITH QUILL;Pi;0;ON;;;;;Y;;;;; +2E21;RIGHT VERTICAL BAR WITH QUILL;Pf;0;ON;;;;;Y;;;;; +2E22;TOP LEFT HALF BRACKET;Ps;0;ON;;;;;Y;;;;; +2E23;TOP RIGHT HALF BRACKET;Pe;0;ON;;;;;Y;;;;; +2E24;BOTTOM LEFT HALF BRACKET;Ps;0;ON;;;;;Y;;;;; +2E25;BOTTOM RIGHT HALF BRACKET;Pe;0;ON;;;;;Y;;;;; +2E26;LEFT SIDEWAYS U BRACKET;Ps;0;ON;;;;;Y;;;;; +2E27;RIGHT SIDEWAYS U BRACKET;Pe;0;ON;;;;;Y;;;;; +2E28;LEFT DOUBLE PARENTHESIS;Ps;0;ON;;;;;Y;;;;; +2E29;RIGHT DOUBLE PARENTHESIS;Pe;0;ON;;;;;Y;;;;; +2E2A;TWO DOTS OVER ONE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; +2E2B;ONE DOT OVER TWO DOTS PUNCTUATION;Po;0;ON;;;;;N;;;;; +2E2C;SQUARED FOUR DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; +2E2D;FIVE DOT MARK;Po;0;ON;;;;;N;;;;; +2E2E;REVERSED QUESTION MARK;Po;0;ON;;;;;N;;;;; +2E2F;VERTICAL TILDE;Lm;0;ON;;;;;N;;;;; +2E30;RING POINT;Po;0;ON;;;;;N;;;;; +2E31;WORD SEPARATOR MIDDLE DOT;Po;0;ON;;;;;N;;;;; +2E32;TURNED COMMA;Po;0;ON;;;;;N;;;;; +2E33;RAISED DOT;Po;0;ON;;;;;N;;;;; +2E34;RAISED COMMA;Po;0;ON;;;;;N;;;;; +2E35;TURNED SEMICOLON;Po;0;ON;;;;;N;;;;; +2E36;DAGGER WITH LEFT GUARD;Po;0;ON;;;;;N;;;;; +2E37;DAGGER WITH RIGHT GUARD;Po;0;ON;;;;;N;;;;; +2E38;TURNED DAGGER;Po;0;ON;;;;;N;;;;; +2E39;TOP HALF SECTION SIGN;Po;0;ON;;;;;N;;;;; +2E3A;TWO-EM DASH;Pd;0;ON;;;;;N;;;;; +2E3B;THREE-EM DASH;Pd;0;ON;;;;;N;;;;; +2E3C;STENOGRAPHIC FULL STOP;Po;0;ON;;;;;N;;;;; +2E3D;VERTICAL SIX DOTS;Po;0;ON;;;;;N;;;;; +2E3E;WIGGLY VERTICAL LINE;Po;0;ON;;;;;N;;;;; +2E3F;CAPITULUM;Po;0;ON;;;;;N;;;;; +2E40;DOUBLE HYPHEN;Pd;0;ON;;;;;N;;;;; +2E41;REVERSED COMMA;Po;0;ON;;;;;N;;;;; +2E42;DOUBLE LOW-REVERSED-9 QUOTATION MARK;Ps;0;ON;;;;;N;;;;; +2E43;DASH WITH LEFT UPTURN;Po;0;ON;;;;;N;;;;; +2E44;DOUBLE SUSPENSION MARK;Po;0;ON;;;;;N;;;;; +2E80;CJK RADICAL REPEAT;So;0;ON;;;;;N;;;;; +2E81;CJK RADICAL CLIFF;So;0;ON;;;;;N;;;;; +2E82;CJK RADICAL SECOND ONE;So;0;ON;;;;;N;;;;; +2E83;CJK RADICAL SECOND TWO;So;0;ON;;;;;N;;;;; +2E84;CJK RADICAL SECOND THREE;So;0;ON;;;;;N;;;;; +2E85;CJK RADICAL PERSON;So;0;ON;;;;;N;;;;; +2E86;CJK RADICAL BOX;So;0;ON;;;;;N;;;;; +2E87;CJK RADICAL TABLE;So;0;ON;;;;;N;;;;; +2E88;CJK RADICAL KNIFE ONE;So;0;ON;;;;;N;;;;; +2E89;CJK RADICAL KNIFE TWO;So;0;ON;;;;;N;;;;; +2E8A;CJK RADICAL DIVINATION;So;0;ON;;;;;N;;;;; +2E8B;CJK RADICAL SEAL;So;0;ON;;;;;N;;;;; +2E8C;CJK RADICAL SMALL ONE;So;0;ON;;;;;N;;;;; +2E8D;CJK RADICAL SMALL TWO;So;0;ON;;;;;N;;;;; +2E8E;CJK RADICAL LAME ONE;So;0;ON;;;;;N;;;;; +2E8F;CJK RADICAL LAME TWO;So;0;ON;;;;;N;;;;; +2E90;CJK RADICAL LAME THREE;So;0;ON;;;;;N;;;;; +2E91;CJK RADICAL LAME FOUR;So;0;ON;;;;;N;;;;; +2E92;CJK RADICAL SNAKE;So;0;ON;;;;;N;;;;; +2E93;CJK RADICAL THREAD;So;0;ON;;;;;N;;;;; +2E94;CJK RADICAL SNOUT ONE;So;0;ON;;;;;N;;;;; +2E95;CJK RADICAL SNOUT TWO;So;0;ON;;;;;N;;;;; +2E96;CJK RADICAL HEART ONE;So;0;ON;;;;;N;;;;; +2E97;CJK RADICAL HEART TWO;So;0;ON;;;;;N;;;;; +2E98;CJK RADICAL HAND;So;0;ON;;;;;N;;;;; +2E99;CJK RADICAL RAP;So;0;ON;;;;;N;;;;; +2E9B;CJK RADICAL CHOKE;So;0;ON;;;;;N;;;;; +2E9C;CJK RADICAL SUN;So;0;ON;;;;;N;;;;; +2E9D;CJK RADICAL MOON;So;0;ON;;;;;N;;;;; +2E9E;CJK RADICAL DEATH;So;0;ON;;;;;N;;;;; +2E9F;CJK RADICAL MOTHER;So;0;ON;<compat> 6BCD;;;;N;;;;; +2EA0;CJK RADICAL CIVILIAN;So;0;ON;;;;;N;;;;; +2EA1;CJK RADICAL WATER ONE;So;0;ON;;;;;N;;;;; +2EA2;CJK RADICAL WATER TWO;So;0;ON;;;;;N;;;;; +2EA3;CJK RADICAL FIRE;So;0;ON;;;;;N;;;;; +2EA4;CJK RADICAL PAW ONE;So;0;ON;;;;;N;;;;; +2EA5;CJK RADICAL PAW TWO;So;0;ON;;;;;N;;;;; +2EA6;CJK RADICAL SIMPLIFIED HALF TREE TRUNK;So;0;ON;;;;;N;;;;; +2EA7;CJK RADICAL COW;So;0;ON;;;;;N;;;;; +2EA8;CJK RADICAL DOG;So;0;ON;;;;;N;;;;; +2EA9;CJK RADICAL JADE;So;0;ON;;;;;N;;;;; +2EAA;CJK RADICAL BOLT OF CLOTH;So;0;ON;;;;;N;;;;; +2EAB;CJK RADICAL EYE;So;0;ON;;;;;N;;;;; +2EAC;CJK RADICAL SPIRIT ONE;So;0;ON;;;;;N;;;;; +2EAD;CJK RADICAL SPIRIT TWO;So;0;ON;;;;;N;;;;; +2EAE;CJK RADICAL BAMBOO;So;0;ON;;;;;N;;;;; +2EAF;CJK RADICAL SILK;So;0;ON;;;;;N;;;;; +2EB0;CJK RADICAL C-SIMPLIFIED SILK;So;0;ON;;;;;N;;;;; +2EB1;CJK RADICAL NET ONE;So;0;ON;;;;;N;;;;; +2EB2;CJK RADICAL NET TWO;So;0;ON;;;;;N;;;;; +2EB3;CJK RADICAL NET THREE;So;0;ON;;;;;N;;;;; +2EB4;CJK RADICAL NET FOUR;So;0;ON;;;;;N;;;;; +2EB5;CJK RADICAL MESH;So;0;ON;;;;;N;;;;; +2EB6;CJK RADICAL SHEEP;So;0;ON;;;;;N;;;;; +2EB7;CJK RADICAL RAM;So;0;ON;;;;;N;;;;; +2EB8;CJK RADICAL EWE;So;0;ON;;;;;N;;;;; +2EB9;CJK RADICAL OLD;So;0;ON;;;;;N;;;;; +2EBA;CJK RADICAL BRUSH ONE;So;0;ON;;;;;N;;;;; +2EBB;CJK RADICAL BRUSH TWO;So;0;ON;;;;;N;;;;; +2EBC;CJK RADICAL MEAT;So;0;ON;;;;;N;;;;; +2EBD;CJK RADICAL MORTAR;So;0;ON;;;;;N;;;;; +2EBE;CJK RADICAL GRASS ONE;So;0;ON;;;;;N;;;;; +2EBF;CJK RADICAL GRASS TWO;So;0;ON;;;;;N;;;;; +2EC0;CJK RADICAL GRASS THREE;So;0;ON;;;;;N;;;;; +2EC1;CJK RADICAL TIGER;So;0;ON;;;;;N;;;;; +2EC2;CJK RADICAL CLOTHES;So;0;ON;;;;;N;;;;; +2EC3;CJK RADICAL WEST ONE;So;0;ON;;;;;N;;;;; +2EC4;CJK RADICAL WEST TWO;So;0;ON;;;;;N;;;;; +2EC5;CJK RADICAL C-SIMPLIFIED SEE;So;0;ON;;;;;N;;;;; +2EC6;CJK RADICAL SIMPLIFIED HORN;So;0;ON;;;;;N;;;;; +2EC7;CJK RADICAL HORN;So;0;ON;;;;;N;;;;; +2EC8;CJK RADICAL C-SIMPLIFIED SPEECH;So;0;ON;;;;;N;;;;; +2EC9;CJK RADICAL C-SIMPLIFIED SHELL;So;0;ON;;;;;N;;;;; +2ECA;CJK RADICAL FOOT;So;0;ON;;;;;N;;;;; +2ECB;CJK RADICAL C-SIMPLIFIED CART;So;0;ON;;;;;N;;;;; +2ECC;CJK RADICAL SIMPLIFIED WALK;So;0;ON;;;;;N;;;;; +2ECD;CJK RADICAL WALK ONE;So;0;ON;;;;;N;;;;; +2ECE;CJK RADICAL WALK TWO;So;0;ON;;;;;N;;;;; +2ECF;CJK RADICAL CITY;So;0;ON;;;;;N;;;;; +2ED0;CJK RADICAL C-SIMPLIFIED GOLD;So;0;ON;;;;;N;;;;; +2ED1;CJK RADICAL LONG ONE;So;0;ON;;;;;N;;;;; +2ED2;CJK RADICAL LONG TWO;So;0;ON;;;;;N;;;;; +2ED3;CJK RADICAL C-SIMPLIFIED LONG;So;0;ON;;;;;N;;;;; +2ED4;CJK RADICAL C-SIMPLIFIED GATE;So;0;ON;;;;;N;;;;; +2ED5;CJK RADICAL MOUND ONE;So;0;ON;;;;;N;;;;; +2ED6;CJK RADICAL MOUND TWO;So;0;ON;;;;;N;;;;; +2ED7;CJK RADICAL RAIN;So;0;ON;;;;;N;;;;; +2ED8;CJK RADICAL BLUE;So;0;ON;;;;;N;;;;; +2ED9;CJK RADICAL C-SIMPLIFIED TANNED LEATHER;So;0;ON;;;;;N;;;;; +2EDA;CJK RADICAL C-SIMPLIFIED LEAF;So;0;ON;;;;;N;;;;; +2EDB;CJK RADICAL C-SIMPLIFIED WIND;So;0;ON;;;;;N;;;;; +2EDC;CJK RADICAL C-SIMPLIFIED FLY;So;0;ON;;;;;N;;;;; +2EDD;CJK RADICAL EAT ONE;So;0;ON;;;;;N;;;;; +2EDE;CJK RADICAL EAT TWO;So;0;ON;;;;;N;;;;; +2EDF;CJK RADICAL EAT THREE;So;0;ON;;;;;N;;;;; +2EE0;CJK RADICAL C-SIMPLIFIED EAT;So;0;ON;;;;;N;;;;; +2EE1;CJK RADICAL HEAD;So;0;ON;;;;;N;;;;; +2EE2;CJK RADICAL C-SIMPLIFIED HORSE;So;0;ON;;;;;N;;;;; +2EE3;CJK RADICAL BONE;So;0;ON;;;;;N;;;;; +2EE4;CJK RADICAL GHOST;So;0;ON;;;;;N;;;;; +2EE5;CJK RADICAL C-SIMPLIFIED FISH;So;0;ON;;;;;N;;;;; +2EE6;CJK RADICAL C-SIMPLIFIED BIRD;So;0;ON;;;;;N;;;;; +2EE7;CJK RADICAL C-SIMPLIFIED SALT;So;0;ON;;;;;N;;;;; +2EE8;CJK RADICAL SIMPLIFIED WHEAT;So;0;ON;;;;;N;;;;; +2EE9;CJK RADICAL SIMPLIFIED YELLOW;So;0;ON;;;;;N;;;;; +2EEA;CJK RADICAL C-SIMPLIFIED FROG;So;0;ON;;;;;N;;;;; +2EEB;CJK RADICAL J-SIMPLIFIED EVEN;So;0;ON;;;;;N;;;;; +2EEC;CJK RADICAL C-SIMPLIFIED EVEN;So;0;ON;;;;;N;;;;; +2EED;CJK RADICAL J-SIMPLIFIED TOOTH;So;0;ON;;;;;N;;;;; +2EEE;CJK RADICAL C-SIMPLIFIED TOOTH;So;0;ON;;;;;N;;;;; +2EEF;CJK RADICAL J-SIMPLIFIED DRAGON;So;0;ON;;;;;N;;;;; +2EF0;CJK RADICAL C-SIMPLIFIED DRAGON;So;0;ON;;;;;N;;;;; +2EF1;CJK RADICAL TURTLE;So;0;ON;;;;;N;;;;; +2EF2;CJK RADICAL J-SIMPLIFIED TURTLE;So;0;ON;;;;;N;;;;; +2EF3;CJK RADICAL C-SIMPLIFIED TURTLE;So;0;ON;<compat> 9F9F;;;;N;;;;; +2F00;KANGXI RADICAL ONE;So;0;ON;<compat> 4E00;;;;N;;;;; +2F01;KANGXI RADICAL LINE;So;0;ON;<compat> 4E28;;;;N;;;;; +2F02;KANGXI RADICAL DOT;So;0;ON;<compat> 4E36;;;;N;;;;; +2F03;KANGXI RADICAL SLASH;So;0;ON;<compat> 4E3F;;;;N;;;;; +2F04;KANGXI RADICAL SECOND;So;0;ON;<compat> 4E59;;;;N;;;;; +2F05;KANGXI RADICAL HOOK;So;0;ON;<compat> 4E85;;;;N;;;;; +2F06;KANGXI RADICAL TWO;So;0;ON;<compat> 4E8C;;;;N;;;;; +2F07;KANGXI RADICAL LID;So;0;ON;<compat> 4EA0;;;;N;;;;; +2F08;KANGXI RADICAL MAN;So;0;ON;<compat> 4EBA;;;;N;;;;; +2F09;KANGXI RADICAL LEGS;So;0;ON;<compat> 513F;;;;N;;;;; +2F0A;KANGXI RADICAL ENTER;So;0;ON;<compat> 5165;;;;N;;;;; +2F0B;KANGXI RADICAL EIGHT;So;0;ON;<compat> 516B;;;;N;;;;; +2F0C;KANGXI RADICAL DOWN BOX;So;0;ON;<compat> 5182;;;;N;;;;; +2F0D;KANGXI RADICAL COVER;So;0;ON;<compat> 5196;;;;N;;;;; +2F0E;KANGXI RADICAL ICE;So;0;ON;<compat> 51AB;;;;N;;;;; +2F0F;KANGXI RADICAL TABLE;So;0;ON;<compat> 51E0;;;;N;;;;; +2F10;KANGXI RADICAL OPEN BOX;So;0;ON;<compat> 51F5;;;;N;;;;; +2F11;KANGXI RADICAL KNIFE;So;0;ON;<compat> 5200;;;;N;;;;; +2F12;KANGXI RADICAL POWER;So;0;ON;<compat> 529B;;;;N;;;;; +2F13;KANGXI RADICAL WRAP;So;0;ON;<compat> 52F9;;;;N;;;;; +2F14;KANGXI RADICAL SPOON;So;0;ON;<compat> 5315;;;;N;;;;; +2F15;KANGXI RADICAL RIGHT OPEN BOX;So;0;ON;<compat> 531A;;;;N;;;;; +2F16;KANGXI RADICAL HIDING ENCLOSURE;So;0;ON;<compat> 5338;;;;N;;;;; +2F17;KANGXI RADICAL TEN;So;0;ON;<compat> 5341;;;;N;;;;; +2F18;KANGXI RADICAL DIVINATION;So;0;ON;<compat> 535C;;;;N;;;;; +2F19;KANGXI RADICAL SEAL;So;0;ON;<compat> 5369;;;;N;;;;; +2F1A;KANGXI RADICAL CLIFF;So;0;ON;<compat> 5382;;;;N;;;;; +2F1B;KANGXI RADICAL PRIVATE;So;0;ON;<compat> 53B6;;;;N;;;;; +2F1C;KANGXI RADICAL AGAIN;So;0;ON;<compat> 53C8;;;;N;;;;; +2F1D;KANGXI RADICAL MOUTH;So;0;ON;<compat> 53E3;;;;N;;;;; +2F1E;KANGXI RADICAL ENCLOSURE;So;0;ON;<compat> 56D7;;;;N;;;;; +2F1F;KANGXI RADICAL EARTH;So;0;ON;<compat> 571F;;;;N;;;;; +2F20;KANGXI RADICAL SCHOLAR;So;0;ON;<compat> 58EB;;;;N;;;;; +2F21;KANGXI RADICAL GO;So;0;ON;<compat> 5902;;;;N;;;;; +2F22;KANGXI RADICAL GO SLOWLY;So;0;ON;<compat> 590A;;;;N;;;;; +2F23;KANGXI RADICAL EVENING;So;0;ON;<compat> 5915;;;;N;;;;; +2F24;KANGXI RADICAL BIG;So;0;ON;<compat> 5927;;;;N;;;;; +2F25;KANGXI RADICAL WOMAN;So;0;ON;<compat> 5973;;;;N;;;;; +2F26;KANGXI RADICAL CHILD;So;0;ON;<compat> 5B50;;;;N;;;;; +2F27;KANGXI RADICAL ROOF;So;0;ON;<compat> 5B80;;;;N;;;;; +2F28;KANGXI RADICAL INCH;So;0;ON;<compat> 5BF8;;;;N;;;;; +2F29;KANGXI RADICAL SMALL;So;0;ON;<compat> 5C0F;;;;N;;;;; +2F2A;KANGXI RADICAL LAME;So;0;ON;<compat> 5C22;;;;N;;;;; +2F2B;KANGXI RADICAL CORPSE;So;0;ON;<compat> 5C38;;;;N;;;;; +2F2C;KANGXI RADICAL SPROUT;So;0;ON;<compat> 5C6E;;;;N;;;;; +2F2D;KANGXI RADICAL MOUNTAIN;So;0;ON;<compat> 5C71;;;;N;;;;; +2F2E;KANGXI RADICAL RIVER;So;0;ON;<compat> 5DDB;;;;N;;;;; +2F2F;KANGXI RADICAL WORK;So;0;ON;<compat> 5DE5;;;;N;;;;; +2F30;KANGXI RADICAL ONESELF;So;0;ON;<compat> 5DF1;;;;N;;;;; +2F31;KANGXI RADICAL TURBAN;So;0;ON;<compat> 5DFE;;;;N;;;;; +2F32;KANGXI RADICAL DRY;So;0;ON;<compat> 5E72;;;;N;;;;; +2F33;KANGXI RADICAL SHORT THREAD;So;0;ON;<compat> 5E7A;;;;N;;;;; +2F34;KANGXI RADICAL DOTTED CLIFF;So;0;ON;<compat> 5E7F;;;;N;;;;; +2F35;KANGXI RADICAL LONG STRIDE;So;0;ON;<compat> 5EF4;;;;N;;;;; +2F36;KANGXI RADICAL TWO HANDS;So;0;ON;<compat> 5EFE;;;;N;;;;; +2F37;KANGXI RADICAL SHOOT;So;0;ON;<compat> 5F0B;;;;N;;;;; +2F38;KANGXI RADICAL BOW;So;0;ON;<compat> 5F13;;;;N;;;;; +2F39;KANGXI RADICAL SNOUT;So;0;ON;<compat> 5F50;;;;N;;;;; +2F3A;KANGXI RADICAL BRISTLE;So;0;ON;<compat> 5F61;;;;N;;;;; +2F3B;KANGXI RADICAL STEP;So;0;ON;<compat> 5F73;;;;N;;;;; +2F3C;KANGXI RADICAL HEART;So;0;ON;<compat> 5FC3;;;;N;;;;; +2F3D;KANGXI RADICAL HALBERD;So;0;ON;<compat> 6208;;;;N;;;;; +2F3E;KANGXI RADICAL DOOR;So;0;ON;<compat> 6236;;;;N;;;;; +2F3F;KANGXI RADICAL HAND;So;0;ON;<compat> 624B;;;;N;;;;; +2F40;KANGXI RADICAL BRANCH;So;0;ON;<compat> 652F;;;;N;;;;; +2F41;KANGXI RADICAL RAP;So;0;ON;<compat> 6534;;;;N;;;;; +2F42;KANGXI RADICAL SCRIPT;So;0;ON;<compat> 6587;;;;N;;;;; +2F43;KANGXI RADICAL DIPPER;So;0;ON;<compat> 6597;;;;N;;;;; +2F44;KANGXI RADICAL AXE;So;0;ON;<compat> 65A4;;;;N;;;;; +2F45;KANGXI RADICAL SQUARE;So;0;ON;<compat> 65B9;;;;N;;;;; +2F46;KANGXI RADICAL NOT;So;0;ON;<compat> 65E0;;;;N;;;;; +2F47;KANGXI RADICAL SUN;So;0;ON;<compat> 65E5;;;;N;;;;; +2F48;KANGXI RADICAL SAY;So;0;ON;<compat> 66F0;;;;N;;;;; +2F49;KANGXI RADICAL MOON;So;0;ON;<compat> 6708;;;;N;;;;; +2F4A;KANGXI RADICAL TREE;So;0;ON;<compat> 6728;;;;N;;;;; +2F4B;KANGXI RADICAL LACK;So;0;ON;<compat> 6B20;;;;N;;;;; +2F4C;KANGXI RADICAL STOP;So;0;ON;<compat> 6B62;;;;N;;;;; +2F4D;KANGXI RADICAL DEATH;So;0;ON;<compat> 6B79;;;;N;;;;; +2F4E;KANGXI RADICAL WEAPON;So;0;ON;<compat> 6BB3;;;;N;;;;; +2F4F;KANGXI RADICAL DO NOT;So;0;ON;<compat> 6BCB;;;;N;;;;; +2F50;KANGXI RADICAL COMPARE;So;0;ON;<compat> 6BD4;;;;N;;;;; +2F51;KANGXI RADICAL FUR;So;0;ON;<compat> 6BDB;;;;N;;;;; +2F52;KANGXI RADICAL CLAN;So;0;ON;<compat> 6C0F;;;;N;;;;; +2F53;KANGXI RADICAL STEAM;So;0;ON;<compat> 6C14;;;;N;;;;; +2F54;KANGXI RADICAL WATER;So;0;ON;<compat> 6C34;;;;N;;;;; +2F55;KANGXI RADICAL FIRE;So;0;ON;<compat> 706B;;;;N;;;;; +2F56;KANGXI RADICAL CLAW;So;0;ON;<compat> 722A;;;;N;;;;; +2F57;KANGXI RADICAL FATHER;So;0;ON;<compat> 7236;;;;N;;;;; +2F58;KANGXI RADICAL DOUBLE X;So;0;ON;<compat> 723B;;;;N;;;;; +2F59;KANGXI RADICAL HALF TREE TRUNK;So;0;ON;<compat> 723F;;;;N;;;;; +2F5A;KANGXI RADICAL SLICE;So;0;ON;<compat> 7247;;;;N;;;;; +2F5B;KANGXI RADICAL FANG;So;0;ON;<compat> 7259;;;;N;;;;; +2F5C;KANGXI RADICAL COW;So;0;ON;<compat> 725B;;;;N;;;;; +2F5D;KANGXI RADICAL DOG;So;0;ON;<compat> 72AC;;;;N;;;;; +2F5E;KANGXI RADICAL PROFOUND;So;0;ON;<compat> 7384;;;;N;;;;; +2F5F;KANGXI RADICAL JADE;So;0;ON;<compat> 7389;;;;N;;;;; +2F60;KANGXI RADICAL MELON;So;0;ON;<compat> 74DC;;;;N;;;;; +2F61;KANGXI RADICAL TILE;So;0;ON;<compat> 74E6;;;;N;;;;; +2F62;KANGXI RADICAL SWEET;So;0;ON;<compat> 7518;;;;N;;;;; +2F63;KANGXI RADICAL LIFE;So;0;ON;<compat> 751F;;;;N;;;;; +2F64;KANGXI RADICAL USE;So;0;ON;<compat> 7528;;;;N;;;;; +2F65;KANGXI RADICAL FIELD;So;0;ON;<compat> 7530;;;;N;;;;; +2F66;KANGXI RADICAL BOLT OF CLOTH;So;0;ON;<compat> 758B;;;;N;;;;; +2F67;KANGXI RADICAL SICKNESS;So;0;ON;<compat> 7592;;;;N;;;;; +2F68;KANGXI RADICAL DOTTED TENT;So;0;ON;<compat> 7676;;;;N;;;;; +2F69;KANGXI RADICAL WHITE;So;0;ON;<compat> 767D;;;;N;;;;; +2F6A;KANGXI RADICAL SKIN;So;0;ON;<compat> 76AE;;;;N;;;;; +2F6B;KANGXI RADICAL DISH;So;0;ON;<compat> 76BF;;;;N;;;;; +2F6C;KANGXI RADICAL EYE;So;0;ON;<compat> 76EE;;;;N;;;;; +2F6D;KANGXI RADICAL SPEAR;So;0;ON;<compat> 77DB;;;;N;;;;; +2F6E;KANGXI RADICAL ARROW;So;0;ON;<compat> 77E2;;;;N;;;;; +2F6F;KANGXI RADICAL STONE;So;0;ON;<compat> 77F3;;;;N;;;;; +2F70;KANGXI RADICAL SPIRIT;So;0;ON;<compat> 793A;;;;N;;;;; +2F71;KANGXI RADICAL TRACK;So;0;ON;<compat> 79B8;;;;N;;;;; +2F72;KANGXI RADICAL GRAIN;So;0;ON;<compat> 79BE;;;;N;;;;; +2F73;KANGXI RADICAL CAVE;So;0;ON;<compat> 7A74;;;;N;;;;; +2F74;KANGXI RADICAL STAND;So;0;ON;<compat> 7ACB;;;;N;;;;; +2F75;KANGXI RADICAL BAMBOO;So;0;ON;<compat> 7AF9;;;;N;;;;; +2F76;KANGXI RADICAL RICE;So;0;ON;<compat> 7C73;;;;N;;;;; +2F77;KANGXI RADICAL SILK;So;0;ON;<compat> 7CF8;;;;N;;;;; +2F78;KANGXI RADICAL JAR;So;0;ON;<compat> 7F36;;;;N;;;;; +2F79;KANGXI RADICAL NET;So;0;ON;<compat> 7F51;;;;N;;;;; +2F7A;KANGXI RADICAL SHEEP;So;0;ON;<compat> 7F8A;;;;N;;;;; +2F7B;KANGXI RADICAL FEATHER;So;0;ON;<compat> 7FBD;;;;N;;;;; +2F7C;KANGXI RADICAL OLD;So;0;ON;<compat> 8001;;;;N;;;;; +2F7D;KANGXI RADICAL AND;So;0;ON;<compat> 800C;;;;N;;;;; +2F7E;KANGXI RADICAL PLOW;So;0;ON;<compat> 8012;;;;N;;;;; +2F7F;KANGXI RADICAL EAR;So;0;ON;<compat> 8033;;;;N;;;;; +2F80;KANGXI RADICAL BRUSH;So;0;ON;<compat> 807F;;;;N;;;;; +2F81;KANGXI RADICAL MEAT;So;0;ON;<compat> 8089;;;;N;;;;; +2F82;KANGXI RADICAL MINISTER;So;0;ON;<compat> 81E3;;;;N;;;;; +2F83;KANGXI RADICAL SELF;So;0;ON;<compat> 81EA;;;;N;;;;; +2F84;KANGXI RADICAL ARRIVE;So;0;ON;<compat> 81F3;;;;N;;;;; +2F85;KANGXI RADICAL MORTAR;So;0;ON;<compat> 81FC;;;;N;;;;; +2F86;KANGXI RADICAL TONGUE;So;0;ON;<compat> 820C;;;;N;;;;; +2F87;KANGXI RADICAL OPPOSE;So;0;ON;<compat> 821B;;;;N;;;;; +2F88;KANGXI RADICAL BOAT;So;0;ON;<compat> 821F;;;;N;;;;; +2F89;KANGXI RADICAL STOPPING;So;0;ON;<compat> 826E;;;;N;;;;; +2F8A;KANGXI RADICAL COLOR;So;0;ON;<compat> 8272;;;;N;;;;; +2F8B;KANGXI RADICAL GRASS;So;0;ON;<compat> 8278;;;;N;;;;; +2F8C;KANGXI RADICAL TIGER;So;0;ON;<compat> 864D;;;;N;;;;; +2F8D;KANGXI RADICAL INSECT;So;0;ON;<compat> 866B;;;;N;;;;; +2F8E;KANGXI RADICAL BLOOD;So;0;ON;<compat> 8840;;;;N;;;;; +2F8F;KANGXI RADICAL WALK ENCLOSURE;So;0;ON;<compat> 884C;;;;N;;;;; +2F90;KANGXI RADICAL CLOTHES;So;0;ON;<compat> 8863;;;;N;;;;; +2F91;KANGXI RADICAL WEST;So;0;ON;<compat> 897E;;;;N;;;;; +2F92;KANGXI RADICAL SEE;So;0;ON;<compat> 898B;;;;N;;;;; +2F93;KANGXI RADICAL HORN;So;0;ON;<compat> 89D2;;;;N;;;;; +2F94;KANGXI RADICAL SPEECH;So;0;ON;<compat> 8A00;;;;N;;;;; +2F95;KANGXI RADICAL VALLEY;So;0;ON;<compat> 8C37;;;;N;;;;; +2F96;KANGXI RADICAL BEAN;So;0;ON;<compat> 8C46;;;;N;;;;; +2F97;KANGXI RADICAL PIG;So;0;ON;<compat> 8C55;;;;N;;;;; +2F98;KANGXI RADICAL BADGER;So;0;ON;<compat> 8C78;;;;N;;;;; +2F99;KANGXI RADICAL SHELL;So;0;ON;<compat> 8C9D;;;;N;;;;; +2F9A;KANGXI RADICAL RED;So;0;ON;<compat> 8D64;;;;N;;;;; +2F9B;KANGXI RADICAL RUN;So;0;ON;<compat> 8D70;;;;N;;;;; +2F9C;KANGXI RADICAL FOOT;So;0;ON;<compat> 8DB3;;;;N;;;;; +2F9D;KANGXI RADICAL BODY;So;0;ON;<compat> 8EAB;;;;N;;;;; +2F9E;KANGXI RADICAL CART;So;0;ON;<compat> 8ECA;;;;N;;;;; +2F9F;KANGXI RADICAL BITTER;So;0;ON;<compat> 8F9B;;;;N;;;;; +2FA0;KANGXI RADICAL MORNING;So;0;ON;<compat> 8FB0;;;;N;;;;; +2FA1;KANGXI RADICAL WALK;So;0;ON;<compat> 8FB5;;;;N;;;;; +2FA2;KANGXI RADICAL CITY;So;0;ON;<compat> 9091;;;;N;;;;; +2FA3;KANGXI RADICAL WINE;So;0;ON;<compat> 9149;;;;N;;;;; +2FA4;KANGXI RADICAL DISTINGUISH;So;0;ON;<compat> 91C6;;;;N;;;;; +2FA5;KANGXI RADICAL VILLAGE;So;0;ON;<compat> 91CC;;;;N;;;;; +2FA6;KANGXI RADICAL GOLD;So;0;ON;<compat> 91D1;;;;N;;;;; +2FA7;KANGXI RADICAL LONG;So;0;ON;<compat> 9577;;;;N;;;;; +2FA8;KANGXI RADICAL GATE;So;0;ON;<compat> 9580;;;;N;;;;; +2FA9;KANGXI RADICAL MOUND;So;0;ON;<compat> 961C;;;;N;;;;; +2FAA;KANGXI RADICAL SLAVE;So;0;ON;<compat> 96B6;;;;N;;;;; +2FAB;KANGXI RADICAL SHORT TAILED BIRD;So;0;ON;<compat> 96B9;;;;N;;;;; +2FAC;KANGXI RADICAL RAIN;So;0;ON;<compat> 96E8;;;;N;;;;; +2FAD;KANGXI RADICAL BLUE;So;0;ON;<compat> 9751;;;;N;;;;; +2FAE;KANGXI RADICAL WRONG;So;0;ON;<compat> 975E;;;;N;;;;; +2FAF;KANGXI RADICAL FACE;So;0;ON;<compat> 9762;;;;N;;;;; +2FB0;KANGXI RADICAL LEATHER;So;0;ON;<compat> 9769;;;;N;;;;; +2FB1;KANGXI RADICAL TANNED LEATHER;So;0;ON;<compat> 97CB;;;;N;;;;; +2FB2;KANGXI RADICAL LEEK;So;0;ON;<compat> 97ED;;;;N;;;;; +2FB3;KANGXI RADICAL SOUND;So;0;ON;<compat> 97F3;;;;N;;;;; +2FB4;KANGXI RADICAL LEAF;So;0;ON;<compat> 9801;;;;N;;;;; +2FB5;KANGXI RADICAL WIND;So;0;ON;<compat> 98A8;;;;N;;;;; +2FB6;KANGXI RADICAL FLY;So;0;ON;<compat> 98DB;;;;N;;;;; +2FB7;KANGXI RADICAL EAT;So;0;ON;<compat> 98DF;;;;N;;;;; +2FB8;KANGXI RADICAL HEAD;So;0;ON;<compat> 9996;;;;N;;;;; +2FB9;KANGXI RADICAL FRAGRANT;So;0;ON;<compat> 9999;;;;N;;;;; +2FBA;KANGXI RADICAL HORSE;So;0;ON;<compat> 99AC;;;;N;;;;; +2FBB;KANGXI RADICAL BONE;So;0;ON;<compat> 9AA8;;;;N;;;;; +2FBC;KANGXI RADICAL TALL;So;0;ON;<compat> 9AD8;;;;N;;;;; +2FBD;KANGXI RADICAL HAIR;So;0;ON;<compat> 9ADF;;;;N;;;;; +2FBE;KANGXI RADICAL FIGHT;So;0;ON;<compat> 9B25;;;;N;;;;; +2FBF;KANGXI RADICAL SACRIFICIAL WINE;So;0;ON;<compat> 9B2F;;;;N;;;;; +2FC0;KANGXI RADICAL CAULDRON;So;0;ON;<compat> 9B32;;;;N;;;;; +2FC1;KANGXI RADICAL GHOST;So;0;ON;<compat> 9B3C;;;;N;;;;; +2FC2;KANGXI RADICAL FISH;So;0;ON;<compat> 9B5A;;;;N;;;;; +2FC3;KANGXI RADICAL BIRD;So;0;ON;<compat> 9CE5;;;;N;;;;; +2FC4;KANGXI RADICAL SALT;So;0;ON;<compat> 9E75;;;;N;;;;; +2FC5;KANGXI RADICAL DEER;So;0;ON;<compat> 9E7F;;;;N;;;;; +2FC6;KANGXI RADICAL WHEAT;So;0;ON;<compat> 9EA5;;;;N;;;;; +2FC7;KANGXI RADICAL HEMP;So;0;ON;<compat> 9EBB;;;;N;;;;; +2FC8;KANGXI RADICAL YELLOW;So;0;ON;<compat> 9EC3;;;;N;;;;; +2FC9;KANGXI RADICAL MILLET;So;0;ON;<compat> 9ECD;;;;N;;;;; +2FCA;KANGXI RADICAL BLACK;So;0;ON;<compat> 9ED1;;;;N;;;;; +2FCB;KANGXI RADICAL EMBROIDERY;So;0;ON;<compat> 9EF9;;;;N;;;;; +2FCC;KANGXI RADICAL FROG;So;0;ON;<compat> 9EFD;;;;N;;;;; +2FCD;KANGXI RADICAL TRIPOD;So;0;ON;<compat> 9F0E;;;;N;;;;; +2FCE;KANGXI RADICAL DRUM;So;0;ON;<compat> 9F13;;;;N;;;;; +2FCF;KANGXI RADICAL RAT;So;0;ON;<compat> 9F20;;;;N;;;;; +2FD0;KANGXI RADICAL NOSE;So;0;ON;<compat> 9F3B;;;;N;;;;; +2FD1;KANGXI RADICAL EVEN;So;0;ON;<compat> 9F4A;;;;N;;;;; +2FD2;KANGXI RADICAL TOOTH;So;0;ON;<compat> 9F52;;;;N;;;;; +2FD3;KANGXI RADICAL DRAGON;So;0;ON;<compat> 9F8D;;;;N;;;;; +2FD4;KANGXI RADICAL TURTLE;So;0;ON;<compat> 9F9C;;;;N;;;;; +2FD5;KANGXI RADICAL FLUTE;So;0;ON;<compat> 9FA0;;;;N;;;;; +2FF0;IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT;So;0;ON;;;;;N;;;;; +2FF1;IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO BELOW;So;0;ON;;;;;N;;;;; +2FF2;IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO MIDDLE AND RIGHT;So;0;ON;;;;;N;;;;; +2FF3;IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO MIDDLE AND BELOW;So;0;ON;;;;;N;;;;; +2FF4;IDEOGRAPHIC DESCRIPTION CHARACTER FULL SURROUND;So;0;ON;;;;;N;;;;; +2FF5;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM ABOVE;So;0;ON;;;;;N;;;;; +2FF6;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM BELOW;So;0;ON;;;;;N;;;;; +2FF7;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LEFT;So;0;ON;;;;;N;;;;; +2FF8;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER LEFT;So;0;ON;;;;;N;;;;; +2FF9;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER RIGHT;So;0;ON;;;;;N;;;;; +2FFA;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LOWER LEFT;So;0;ON;;;;;N;;;;; +2FFB;IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID;So;0;ON;;;;;N;;;;; +3000;IDEOGRAPHIC SPACE;Zs;0;WS;<wide> 0020;;;;N;;;;; +3001;IDEOGRAPHIC COMMA;Po;0;ON;;;;;N;;;;; +3002;IDEOGRAPHIC FULL STOP;Po;0;ON;;;;;N;IDEOGRAPHIC PERIOD;;;; +3003;DITTO MARK;Po;0;ON;;;;;N;;;;; +3004;JAPANESE INDUSTRIAL STANDARD SYMBOL;So;0;ON;;;;;N;;;;; +3005;IDEOGRAPHIC ITERATION MARK;Lm;0;L;;;;;N;;;;; +3006;IDEOGRAPHIC CLOSING MARK;Lo;0;L;;;;;N;;;;; +3007;IDEOGRAPHIC NUMBER ZERO;Nl;0;L;;;;0;N;;;;; +3008;LEFT ANGLE BRACKET;Ps;0;ON;;;;;Y;OPENING ANGLE BRACKET;;;; +3009;RIGHT ANGLE BRACKET;Pe;0;ON;;;;;Y;CLOSING ANGLE BRACKET;;;; +300A;LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;;;;;Y;OPENING DOUBLE ANGLE BRACKET;;;; +300B;RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;;;;;Y;CLOSING DOUBLE ANGLE BRACKET;;;; +300C;LEFT CORNER BRACKET;Ps;0;ON;;;;;Y;OPENING CORNER BRACKET;;;; +300D;RIGHT CORNER BRACKET;Pe;0;ON;;;;;Y;CLOSING CORNER BRACKET;;;; +300E;LEFT WHITE CORNER BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE CORNER BRACKET;;;; +300F;RIGHT WHITE CORNER BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE CORNER BRACKET;;;; +3010;LEFT BLACK LENTICULAR BRACKET;Ps;0;ON;;;;;Y;OPENING BLACK LENTICULAR BRACKET;;;; +3011;RIGHT BLACK LENTICULAR BRACKET;Pe;0;ON;;;;;Y;CLOSING BLACK LENTICULAR BRACKET;;;; +3012;POSTAL MARK;So;0;ON;;;;;N;;;;; +3013;GETA MARK;So;0;ON;;;;;N;;;;; +3014;LEFT TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;OPENING TORTOISE SHELL BRACKET;;;; +3015;RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;CLOSING TORTOISE SHELL BRACKET;;;; +3016;LEFT WHITE LENTICULAR BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE LENTICULAR BRACKET;;;; +3017;RIGHT WHITE LENTICULAR BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE LENTICULAR BRACKET;;;; +3018;LEFT WHITE TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE TORTOISE SHELL BRACKET;;;; +3019;RIGHT WHITE TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE TORTOISE SHELL BRACKET;;;; +301A;LEFT WHITE SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE SQUARE BRACKET;;;; +301B;RIGHT WHITE SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE SQUARE BRACKET;;;; +301C;WAVE DASH;Pd;0;ON;;;;;N;;;;; +301D;REVERSED DOUBLE PRIME QUOTATION MARK;Ps;0;ON;;;;;N;;;;; +301E;DOUBLE PRIME QUOTATION MARK;Pe;0;ON;;;;;N;;;;; +301F;LOW DOUBLE PRIME QUOTATION MARK;Pe;0;ON;;;;;N;;;;; +3020;POSTAL MARK FACE;So;0;ON;;;;;N;;;;; +3021;HANGZHOU NUMERAL ONE;Nl;0;L;;;;1;N;;;;; +3022;HANGZHOU NUMERAL TWO;Nl;0;L;;;;2;N;;;;; +3023;HANGZHOU NUMERAL THREE;Nl;0;L;;;;3;N;;;;; +3024;HANGZHOU NUMERAL FOUR;Nl;0;L;;;;4;N;;;;; +3025;HANGZHOU NUMERAL FIVE;Nl;0;L;;;;5;N;;;;; +3026;HANGZHOU NUMERAL SIX;Nl;0;L;;;;6;N;;;;; +3027;HANGZHOU NUMERAL SEVEN;Nl;0;L;;;;7;N;;;;; +3028;HANGZHOU NUMERAL EIGHT;Nl;0;L;;;;8;N;;;;; +3029;HANGZHOU NUMERAL NINE;Nl;0;L;;;;9;N;;;;; +302A;IDEOGRAPHIC LEVEL TONE MARK;Mn;218;NSM;;;;;N;;;;; +302B;IDEOGRAPHIC RISING TONE MARK;Mn;228;NSM;;;;;N;;;;; +302C;IDEOGRAPHIC DEPARTING TONE MARK;Mn;232;NSM;;;;;N;;;;; +302D;IDEOGRAPHIC ENTERING TONE MARK;Mn;222;NSM;;;;;N;;;;; +302E;HANGUL SINGLE DOT TONE MARK;Mc;224;L;;;;;N;;;;; +302F;HANGUL DOUBLE DOT TONE MARK;Mc;224;L;;;;;N;;;;; +3030;WAVY DASH;Pd;0;ON;;;;;N;;;;; +3031;VERTICAL KANA REPEAT MARK;Lm;0;L;;;;;N;;;;; +3032;VERTICAL KANA REPEAT WITH VOICED SOUND MARK;Lm;0;L;;;;;N;;;;; +3033;VERTICAL KANA REPEAT MARK UPPER HALF;Lm;0;L;;;;;N;;;;; +3034;VERTICAL KANA REPEAT WITH VOICED SOUND MARK UPPER HALF;Lm;0;L;;;;;N;;;;; +3035;VERTICAL KANA REPEAT MARK LOWER HALF;Lm;0;L;;;;;N;;;;; +3036;CIRCLED POSTAL MARK;So;0;ON;<compat> 3012;;;;N;;;;; +3037;IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL;So;0;ON;;;;;N;;;;; +3038;HANGZHOU NUMERAL TEN;Nl;0;L;<compat> 5341;;;10;N;;;;; +3039;HANGZHOU NUMERAL TWENTY;Nl;0;L;<compat> 5344;;;20;N;;;;; +303A;HANGZHOU NUMERAL THIRTY;Nl;0;L;<compat> 5345;;;30;N;;;;; +303B;VERTICAL IDEOGRAPHIC ITERATION MARK;Lm;0;L;;;;;N;;;;; +303C;MASU MARK;Lo;0;L;;;;;N;;;;; +303D;PART ALTERNATION MARK;Po;0;ON;;;;;N;;;;; +303E;IDEOGRAPHIC VARIATION INDICATOR;So;0;ON;;;;;N;;;;; +303F;IDEOGRAPHIC HALF FILL SPACE;So;0;ON;;;;;N;;;;; +3041;HIRAGANA LETTER SMALL A;Lo;0;L;;;;;N;;;;; +3042;HIRAGANA LETTER A;Lo;0;L;;;;;N;;;;; +3043;HIRAGANA LETTER SMALL I;Lo;0;L;;;;;N;;;;; +3044;HIRAGANA LETTER I;Lo;0;L;;;;;N;;;;; +3045;HIRAGANA LETTER SMALL U;Lo;0;L;;;;;N;;;;; +3046;HIRAGANA LETTER U;Lo;0;L;;;;;N;;;;; +3047;HIRAGANA LETTER SMALL E;Lo;0;L;;;;;N;;;;; +3048;HIRAGANA LETTER E;Lo;0;L;;;;;N;;;;; +3049;HIRAGANA LETTER SMALL O;Lo;0;L;;;;;N;;;;; +304A;HIRAGANA LETTER O;Lo;0;L;;;;;N;;;;; +304B;HIRAGANA LETTER KA;Lo;0;L;;;;;N;;;;; +304C;HIRAGANA LETTER GA;Lo;0;L;304B 3099;;;;N;;;;; +304D;HIRAGANA LETTER KI;Lo;0;L;;;;;N;;;;; +304E;HIRAGANA LETTER GI;Lo;0;L;304D 3099;;;;N;;;;; +304F;HIRAGANA LETTER KU;Lo;0;L;;;;;N;;;;; +3050;HIRAGANA LETTER GU;Lo;0;L;304F 3099;;;;N;;;;; +3051;HIRAGANA LETTER KE;Lo;0;L;;;;;N;;;;; +3052;HIRAGANA LETTER GE;Lo;0;L;3051 3099;;;;N;;;;; +3053;HIRAGANA LETTER KO;Lo;0;L;;;;;N;;;;; +3054;HIRAGANA LETTER GO;Lo;0;L;3053 3099;;;;N;;;;; +3055;HIRAGANA LETTER SA;Lo;0;L;;;;;N;;;;; +3056;HIRAGANA LETTER ZA;Lo;0;L;3055 3099;;;;N;;;;; +3057;HIRAGANA LETTER SI;Lo;0;L;;;;;N;;;;; +3058;HIRAGANA LETTER ZI;Lo;0;L;3057 3099;;;;N;;;;; +3059;HIRAGANA LETTER SU;Lo;0;L;;;;;N;;;;; +305A;HIRAGANA LETTER ZU;Lo;0;L;3059 3099;;;;N;;;;; +305B;HIRAGANA LETTER SE;Lo;0;L;;;;;N;;;;; +305C;HIRAGANA LETTER ZE;Lo;0;L;305B 3099;;;;N;;;;; +305D;HIRAGANA LETTER SO;Lo;0;L;;;;;N;;;;; +305E;HIRAGANA LETTER ZO;Lo;0;L;305D 3099;;;;N;;;;; +305F;HIRAGANA LETTER TA;Lo;0;L;;;;;N;;;;; +3060;HIRAGANA LETTER DA;Lo;0;L;305F 3099;;;;N;;;;; +3061;HIRAGANA LETTER TI;Lo;0;L;;;;;N;;;;; +3062;HIRAGANA LETTER DI;Lo;0;L;3061 3099;;;;N;;;;; +3063;HIRAGANA LETTER SMALL TU;Lo;0;L;;;;;N;;;;; +3064;HIRAGANA LETTER TU;Lo;0;L;;;;;N;;;;; +3065;HIRAGANA LETTER DU;Lo;0;L;3064 3099;;;;N;;;;; +3066;HIRAGANA LETTER TE;Lo;0;L;;;;;N;;;;; +3067;HIRAGANA LETTER DE;Lo;0;L;3066 3099;;;;N;;;;; +3068;HIRAGANA LETTER TO;Lo;0;L;;;;;N;;;;; +3069;HIRAGANA LETTER DO;Lo;0;L;3068 3099;;;;N;;;;; +306A;HIRAGANA LETTER NA;Lo;0;L;;;;;N;;;;; +306B;HIRAGANA LETTER NI;Lo;0;L;;;;;N;;;;; +306C;HIRAGANA LETTER NU;Lo;0;L;;;;;N;;;;; +306D;HIRAGANA LETTER NE;Lo;0;L;;;;;N;;;;; +306E;HIRAGANA LETTER NO;Lo;0;L;;;;;N;;;;; +306F;HIRAGANA LETTER HA;Lo;0;L;;;;;N;;;;; +3070;HIRAGANA LETTER BA;Lo;0;L;306F 3099;;;;N;;;;; +3071;HIRAGANA LETTER PA;Lo;0;L;306F 309A;;;;N;;;;; +3072;HIRAGANA LETTER HI;Lo;0;L;;;;;N;;;;; +3073;HIRAGANA LETTER BI;Lo;0;L;3072 3099;;;;N;;;;; +3074;HIRAGANA LETTER PI;Lo;0;L;3072 309A;;;;N;;;;; +3075;HIRAGANA LETTER HU;Lo;0;L;;;;;N;;;;; +3076;HIRAGANA LETTER BU;Lo;0;L;3075 3099;;;;N;;;;; +3077;HIRAGANA LETTER PU;Lo;0;L;3075 309A;;;;N;;;;; +3078;HIRAGANA LETTER HE;Lo;0;L;;;;;N;;;;; +3079;HIRAGANA LETTER BE;Lo;0;L;3078 3099;;;;N;;;;; +307A;HIRAGANA LETTER PE;Lo;0;L;3078 309A;;;;N;;;;; +307B;HIRAGANA LETTER HO;Lo;0;L;;;;;N;;;;; +307C;HIRAGANA LETTER BO;Lo;0;L;307B 3099;;;;N;;;;; +307D;HIRAGANA LETTER PO;Lo;0;L;307B 309A;;;;N;;;;; +307E;HIRAGANA LETTER MA;Lo;0;L;;;;;N;;;;; +307F;HIRAGANA LETTER MI;Lo;0;L;;;;;N;;;;; +3080;HIRAGANA LETTER MU;Lo;0;L;;;;;N;;;;; +3081;HIRAGANA LETTER ME;Lo;0;L;;;;;N;;;;; +3082;HIRAGANA LETTER MO;Lo;0;L;;;;;N;;;;; +3083;HIRAGANA LETTER SMALL YA;Lo;0;L;;;;;N;;;;; +3084;HIRAGANA LETTER YA;Lo;0;L;;;;;N;;;;; +3085;HIRAGANA LETTER SMALL YU;Lo;0;L;;;;;N;;;;; +3086;HIRAGANA LETTER YU;Lo;0;L;;;;;N;;;;; +3087;HIRAGANA LETTER SMALL YO;Lo;0;L;;;;;N;;;;; +3088;HIRAGANA LETTER YO;Lo;0;L;;;;;N;;;;; +3089;HIRAGANA LETTER RA;Lo;0;L;;;;;N;;;;; +308A;HIRAGANA LETTER RI;Lo;0;L;;;;;N;;;;; +308B;HIRAGANA LETTER RU;Lo;0;L;;;;;N;;;;; +308C;HIRAGANA LETTER RE;Lo;0;L;;;;;N;;;;; +308D;HIRAGANA LETTER RO;Lo;0;L;;;;;N;;;;; +308E;HIRAGANA LETTER SMALL WA;Lo;0;L;;;;;N;;;;; +308F;HIRAGANA LETTER WA;Lo;0;L;;;;;N;;;;; +3090;HIRAGANA LETTER WI;Lo;0;L;;;;;N;;;;; +3091;HIRAGANA LETTER WE;Lo;0;L;;;;;N;;;;; +3092;HIRAGANA LETTER WO;Lo;0;L;;;;;N;;;;; +3093;HIRAGANA LETTER N;Lo;0;L;;;;;N;;;;; +3094;HIRAGANA LETTER VU;Lo;0;L;3046 3099;;;;N;;;;; +3095;HIRAGANA LETTER SMALL KA;Lo;0;L;;;;;N;;;;; +3096;HIRAGANA LETTER SMALL KE;Lo;0;L;;;;;N;;;;; +3099;COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK;Mn;8;NSM;;;;;N;NON-SPACING KATAKANA-HIRAGANA VOICED SOUND MARK;;;; +309A;COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;Mn;8;NSM;;;;;N;NON-SPACING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;;;; +309B;KATAKANA-HIRAGANA VOICED SOUND MARK;Sk;0;ON;<compat> 0020 3099;;;;N;;;;; +309C;KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;Sk;0;ON;<compat> 0020 309A;;;;N;;;;; +309D;HIRAGANA ITERATION MARK;Lm;0;L;;;;;N;;;;; +309E;HIRAGANA VOICED ITERATION MARK;Lm;0;L;309D 3099;;;;N;;;;; +309F;HIRAGANA DIGRAPH YORI;Lo;0;L;<vertical> 3088 308A;;;;N;;;;; +30A0;KATAKANA-HIRAGANA DOUBLE HYPHEN;Pd;0;ON;;;;;N;;;;; +30A1;KATAKANA LETTER SMALL A;Lo;0;L;;;;;N;;;;; +30A2;KATAKANA LETTER A;Lo;0;L;;;;;N;;;;; +30A3;KATAKANA LETTER SMALL I;Lo;0;L;;;;;N;;;;; +30A4;KATAKANA LETTER I;Lo;0;L;;;;;N;;;;; +30A5;KATAKANA LETTER SMALL U;Lo;0;L;;;;;N;;;;; +30A6;KATAKANA LETTER U;Lo;0;L;;;;;N;;;;; +30A7;KATAKANA LETTER SMALL E;Lo;0;L;;;;;N;;;;; +30A8;KATAKANA LETTER E;Lo;0;L;;;;;N;;;;; +30A9;KATAKANA LETTER SMALL O;Lo;0;L;;;;;N;;;;; +30AA;KATAKANA LETTER O;Lo;0;L;;;;;N;;;;; +30AB;KATAKANA LETTER KA;Lo;0;L;;;;;N;;;;; +30AC;KATAKANA LETTER GA;Lo;0;L;30AB 3099;;;;N;;;;; +30AD;KATAKANA LETTER KI;Lo;0;L;;;;;N;;;;; +30AE;KATAKANA LETTER GI;Lo;0;L;30AD 3099;;;;N;;;;; +30AF;KATAKANA LETTER KU;Lo;0;L;;;;;N;;;;; +30B0;KATAKANA LETTER GU;Lo;0;L;30AF 3099;;;;N;;;;; +30B1;KATAKANA LETTER KE;Lo;0;L;;;;;N;;;;; +30B2;KATAKANA LETTER GE;Lo;0;L;30B1 3099;;;;N;;;;; +30B3;KATAKANA LETTER KO;Lo;0;L;;;;;N;;;;; +30B4;KATAKANA LETTER GO;Lo;0;L;30B3 3099;;;;N;;;;; +30B5;KATAKANA LETTER SA;Lo;0;L;;;;;N;;;;; +30B6;KATAKANA LETTER ZA;Lo;0;L;30B5 3099;;;;N;;;;; +30B7;KATAKANA LETTER SI;Lo;0;L;;;;;N;;;;; +30B8;KATAKANA LETTER ZI;Lo;0;L;30B7 3099;;;;N;;;;; +30B9;KATAKANA LETTER SU;Lo;0;L;;;;;N;;;;; +30BA;KATAKANA LETTER ZU;Lo;0;L;30B9 3099;;;;N;;;;; +30BB;KATAKANA LETTER SE;Lo;0;L;;;;;N;;;;; +30BC;KATAKANA LETTER ZE;Lo;0;L;30BB 3099;;;;N;;;;; +30BD;KATAKANA LETTER SO;Lo;0;L;;;;;N;;;;; +30BE;KATAKANA LETTER ZO;Lo;0;L;30BD 3099;;;;N;;;;; +30BF;KATAKANA LETTER TA;Lo;0;L;;;;;N;;;;; +30C0;KATAKANA LETTER DA;Lo;0;L;30BF 3099;;;;N;;;;; +30C1;KATAKANA LETTER TI;Lo;0;L;;;;;N;;;;; +30C2;KATAKANA LETTER DI;Lo;0;L;30C1 3099;;;;N;;;;; +30C3;KATAKANA LETTER SMALL TU;Lo;0;L;;;;;N;;;;; +30C4;KATAKANA LETTER TU;Lo;0;L;;;;;N;;;;; +30C5;KATAKANA LETTER DU;Lo;0;L;30C4 3099;;;;N;;;;; +30C6;KATAKANA LETTER TE;Lo;0;L;;;;;N;;;;; +30C7;KATAKANA LETTER DE;Lo;0;L;30C6 3099;;;;N;;;;; +30C8;KATAKANA LETTER TO;Lo;0;L;;;;;N;;;;; +30C9;KATAKANA LETTER DO;Lo;0;L;30C8 3099;;;;N;;;;; +30CA;KATAKANA LETTER NA;Lo;0;L;;;;;N;;;;; +30CB;KATAKANA LETTER NI;Lo;0;L;;;;;N;;;;; +30CC;KATAKANA LETTER NU;Lo;0;L;;;;;N;;;;; +30CD;KATAKANA LETTER NE;Lo;0;L;;;;;N;;;;; +30CE;KATAKANA LETTER NO;Lo;0;L;;;;;N;;;;; +30CF;KATAKANA LETTER HA;Lo;0;L;;;;;N;;;;; +30D0;KATAKANA LETTER BA;Lo;0;L;30CF 3099;;;;N;;;;; +30D1;KATAKANA LETTER PA;Lo;0;L;30CF 309A;;;;N;;;;; +30D2;KATAKANA LETTER HI;Lo;0;L;;;;;N;;;;; +30D3;KATAKANA LETTER BI;Lo;0;L;30D2 3099;;;;N;;;;; +30D4;KATAKANA LETTER PI;Lo;0;L;30D2 309A;;;;N;;;;; +30D5;KATAKANA LETTER HU;Lo;0;L;;;;;N;;;;; +30D6;KATAKANA LETTER BU;Lo;0;L;30D5 3099;;;;N;;;;; +30D7;KATAKANA LETTER PU;Lo;0;L;30D5 309A;;;;N;;;;; +30D8;KATAKANA LETTER HE;Lo;0;L;;;;;N;;;;; +30D9;KATAKANA LETTER BE;Lo;0;L;30D8 3099;;;;N;;;;; +30DA;KATAKANA LETTER PE;Lo;0;L;30D8 309A;;;;N;;;;; +30DB;KATAKANA LETTER HO;Lo;0;L;;;;;N;;;;; +30DC;KATAKANA LETTER BO;Lo;0;L;30DB 3099;;;;N;;;;; +30DD;KATAKANA LETTER PO;Lo;0;L;30DB 309A;;;;N;;;;; +30DE;KATAKANA LETTER MA;Lo;0;L;;;;;N;;;;; +30DF;KATAKANA LETTER MI;Lo;0;L;;;;;N;;;;; +30E0;KATAKANA LETTER MU;Lo;0;L;;;;;N;;;;; +30E1;KATAKANA LETTER ME;Lo;0;L;;;;;N;;;;; +30E2;KATAKANA LETTER MO;Lo;0;L;;;;;N;;;;; +30E3;KATAKANA LETTER SMALL YA;Lo;0;L;;;;;N;;;;; +30E4;KATAKANA LETTER YA;Lo;0;L;;;;;N;;;;; +30E5;KATAKANA LETTER SMALL YU;Lo;0;L;;;;;N;;;;; +30E6;KATAKANA LETTER YU;Lo;0;L;;;;;N;;;;; +30E7;KATAKANA LETTER SMALL YO;Lo;0;L;;;;;N;;;;; +30E8;KATAKANA LETTER YO;Lo;0;L;;;;;N;;;;; +30E9;KATAKANA LETTER RA;Lo;0;L;;;;;N;;;;; +30EA;KATAKANA LETTER RI;Lo;0;L;;;;;N;;;;; +30EB;KATAKANA LETTER RU;Lo;0;L;;;;;N;;;;; +30EC;KATAKANA LETTER RE;Lo;0;L;;;;;N;;;;; +30ED;KATAKANA LETTER RO;Lo;0;L;;;;;N;;;;; +30EE;KATAKANA LETTER SMALL WA;Lo;0;L;;;;;N;;;;; +30EF;KATAKANA LETTER WA;Lo;0;L;;;;;N;;;;; +30F0;KATAKANA LETTER WI;Lo;0;L;;;;;N;;;;; +30F1;KATAKANA LETTER WE;Lo;0;L;;;;;N;;;;; +30F2;KATAKANA LETTER WO;Lo;0;L;;;;;N;;;;; +30F3;KATAKANA LETTER N;Lo;0;L;;;;;N;;;;; +30F4;KATAKANA LETTER VU;Lo;0;L;30A6 3099;;;;N;;;;; +30F5;KATAKANA LETTER SMALL KA;Lo;0;L;;;;;N;;;;; +30F6;KATAKANA LETTER SMALL KE;Lo;0;L;;;;;N;;;;; +30F7;KATAKANA LETTER VA;Lo;0;L;30EF 3099;;;;N;;;;; +30F8;KATAKANA LETTER VI;Lo;0;L;30F0 3099;;;;N;;;;; +30F9;KATAKANA LETTER VE;Lo;0;L;30F1 3099;;;;N;;;;; +30FA;KATAKANA LETTER VO;Lo;0;L;30F2 3099;;;;N;;;;; +30FB;KATAKANA MIDDLE DOT;Po;0;ON;;;;;N;;;;; +30FC;KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L;;;;;N;;;;; +30FD;KATAKANA ITERATION MARK;Lm;0;L;;;;;N;;;;; +30FE;KATAKANA VOICED ITERATION MARK;Lm;0;L;30FD 3099;;;;N;;;;; +30FF;KATAKANA DIGRAPH KOTO;Lo;0;L;<vertical> 30B3 30C8;;;;N;;;;; +3105;BOPOMOFO LETTER B;Lo;0;L;;;;;N;;;;; +3106;BOPOMOFO LETTER P;Lo;0;L;;;;;N;;;;; +3107;BOPOMOFO LETTER M;Lo;0;L;;;;;N;;;;; +3108;BOPOMOFO LETTER F;Lo;0;L;;;;;N;;;;; +3109;BOPOMOFO LETTER D;Lo;0;L;;;;;N;;;;; +310A;BOPOMOFO LETTER T;Lo;0;L;;;;;N;;;;; +310B;BOPOMOFO LETTER N;Lo;0;L;;;;;N;;;;; +310C;BOPOMOFO LETTER L;Lo;0;L;;;;;N;;;;; +310D;BOPOMOFO LETTER G;Lo;0;L;;;;;N;;;;; +310E;BOPOMOFO LETTER K;Lo;0;L;;;;;N;;;;; +310F;BOPOMOFO LETTER H;Lo;0;L;;;;;N;;;;; +3110;BOPOMOFO LETTER J;Lo;0;L;;;;;N;;;;; +3111;BOPOMOFO LETTER Q;Lo;0;L;;;;;N;;;;; +3112;BOPOMOFO LETTER X;Lo;0;L;;;;;N;;;;; +3113;BOPOMOFO LETTER ZH;Lo;0;L;;;;;N;;;;; +3114;BOPOMOFO LETTER CH;Lo;0;L;;;;;N;;;;; +3115;BOPOMOFO LETTER SH;Lo;0;L;;;;;N;;;;; +3116;BOPOMOFO LETTER R;Lo;0;L;;;;;N;;;;; +3117;BOPOMOFO LETTER Z;Lo;0;L;;;;;N;;;;; +3118;BOPOMOFO LETTER C;Lo;0;L;;;;;N;;;;; +3119;BOPOMOFO LETTER S;Lo;0;L;;;;;N;;;;; +311A;BOPOMOFO LETTER A;Lo;0;L;;;;;N;;;;; +311B;BOPOMOFO LETTER O;Lo;0;L;;;;;N;;;;; +311C;BOPOMOFO LETTER E;Lo;0;L;;;;;N;;;;; +311D;BOPOMOFO LETTER EH;Lo;0;L;;;;;N;;;;; +311E;BOPOMOFO LETTER AI;Lo;0;L;;;;;N;;;;; +311F;BOPOMOFO LETTER EI;Lo;0;L;;;;;N;;;;; +3120;BOPOMOFO LETTER AU;Lo;0;L;;;;;N;;;;; +3121;BOPOMOFO LETTER OU;Lo;0;L;;;;;N;;;;; +3122;BOPOMOFO LETTER AN;Lo;0;L;;;;;N;;;;; +3123;BOPOMOFO LETTER EN;Lo;0;L;;;;;N;;;;; +3124;BOPOMOFO LETTER ANG;Lo;0;L;;;;;N;;;;; +3125;BOPOMOFO LETTER ENG;Lo;0;L;;;;;N;;;;; +3126;BOPOMOFO LETTER ER;Lo;0;L;;;;;N;;;;; +3127;BOPOMOFO LETTER I;Lo;0;L;;;;;N;;;;; +3128;BOPOMOFO LETTER U;Lo;0;L;;;;;N;;;;; +3129;BOPOMOFO LETTER IU;Lo;0;L;;;;;N;;;;; +312A;BOPOMOFO LETTER V;Lo;0;L;;;;;N;;;;; +312B;BOPOMOFO LETTER NG;Lo;0;L;;;;;N;;;;; +312C;BOPOMOFO LETTER GN;Lo;0;L;;;;;N;;;;; +312D;BOPOMOFO LETTER IH;Lo;0;L;;;;;N;;;;; +3131;HANGUL LETTER KIYEOK;Lo;0;L;<compat> 1100;;;;N;HANGUL LETTER GIYEOG;;;; +3132;HANGUL LETTER SSANGKIYEOK;Lo;0;L;<compat> 1101;;;;N;HANGUL LETTER SSANG GIYEOG;;;; +3133;HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<compat> 11AA;;;;N;HANGUL LETTER GIYEOG SIOS;;;; +3134;HANGUL LETTER NIEUN;Lo;0;L;<compat> 1102;;;;N;;;;; +3135;HANGUL LETTER NIEUN-CIEUC;Lo;0;L;<compat> 11AC;;;;N;HANGUL LETTER NIEUN JIEUJ;;;; +3136;HANGUL LETTER NIEUN-HIEUH;Lo;0;L;<compat> 11AD;;;;N;HANGUL LETTER NIEUN HIEUH;;;; +3137;HANGUL LETTER TIKEUT;Lo;0;L;<compat> 1103;;;;N;HANGUL LETTER DIGEUD;;;; +3138;HANGUL LETTER SSANGTIKEUT;Lo;0;L;<compat> 1104;;;;N;HANGUL LETTER SSANG DIGEUD;;;; +3139;HANGUL LETTER RIEUL;Lo;0;L;<compat> 1105;;;;N;HANGUL LETTER LIEUL;;;; +313A;HANGUL LETTER RIEUL-KIYEOK;Lo;0;L;<compat> 11B0;;;;N;HANGUL LETTER LIEUL GIYEOG;;;; +313B;HANGUL LETTER RIEUL-MIEUM;Lo;0;L;<compat> 11B1;;;;N;HANGUL LETTER LIEUL MIEUM;;;; +313C;HANGUL LETTER RIEUL-PIEUP;Lo;0;L;<compat> 11B2;;;;N;HANGUL LETTER LIEUL BIEUB;;;; +313D;HANGUL LETTER RIEUL-SIOS;Lo;0;L;<compat> 11B3;;;;N;HANGUL LETTER LIEUL SIOS;;;; +313E;HANGUL LETTER RIEUL-THIEUTH;Lo;0;L;<compat> 11B4;;;;N;HANGUL LETTER LIEUL TIEUT;;;; +313F;HANGUL LETTER RIEUL-PHIEUPH;Lo;0;L;<compat> 11B5;;;;N;HANGUL LETTER LIEUL PIEUP;;;; +3140;HANGUL LETTER RIEUL-HIEUH;Lo;0;L;<compat> 111A;;;;N;HANGUL LETTER LIEUL HIEUH;;;; +3141;HANGUL LETTER MIEUM;Lo;0;L;<compat> 1106;;;;N;;;;; +3142;HANGUL LETTER PIEUP;Lo;0;L;<compat> 1107;;;;N;HANGUL LETTER BIEUB;;;; +3143;HANGUL LETTER SSANGPIEUP;Lo;0;L;<compat> 1108;;;;N;HANGUL LETTER SSANG BIEUB;;;; +3144;HANGUL LETTER PIEUP-SIOS;Lo;0;L;<compat> 1121;;;;N;HANGUL LETTER BIEUB SIOS;;;; +3145;HANGUL LETTER SIOS;Lo;0;L;<compat> 1109;;;;N;;;;; +3146;HANGUL LETTER SSANGSIOS;Lo;0;L;<compat> 110A;;;;N;HANGUL LETTER SSANG SIOS;;;; +3147;HANGUL LETTER IEUNG;Lo;0;L;<compat> 110B;;;;N;;;;; +3148;HANGUL LETTER CIEUC;Lo;0;L;<compat> 110C;;;;N;HANGUL LETTER JIEUJ;;;; +3149;HANGUL LETTER SSANGCIEUC;Lo;0;L;<compat> 110D;;;;N;HANGUL LETTER SSANG JIEUJ;;;; +314A;HANGUL LETTER CHIEUCH;Lo;0;L;<compat> 110E;;;;N;HANGUL LETTER CIEUC;;;; +314B;HANGUL LETTER KHIEUKH;Lo;0;L;<compat> 110F;;;;N;HANGUL LETTER KIYEOK;;;; +314C;HANGUL LETTER THIEUTH;Lo;0;L;<compat> 1110;;;;N;HANGUL LETTER TIEUT;;;; +314D;HANGUL LETTER PHIEUPH;Lo;0;L;<compat> 1111;;;;N;HANGUL LETTER PIEUP;;;; +314E;HANGUL LETTER HIEUH;Lo;0;L;<compat> 1112;;;;N;;;;; +314F;HANGUL LETTER A;Lo;0;L;<compat> 1161;;;;N;;;;; +3150;HANGUL LETTER AE;Lo;0;L;<compat> 1162;;;;N;;;;; +3151;HANGUL LETTER YA;Lo;0;L;<compat> 1163;;;;N;;;;; +3152;HANGUL LETTER YAE;Lo;0;L;<compat> 1164;;;;N;;;;; +3153;HANGUL LETTER EO;Lo;0;L;<compat> 1165;;;;N;;;;; +3154;HANGUL LETTER E;Lo;0;L;<compat> 1166;;;;N;;;;; +3155;HANGUL LETTER YEO;Lo;0;L;<compat> 1167;;;;N;;;;; +3156;HANGUL LETTER YE;Lo;0;L;<compat> 1168;;;;N;;;;; +3157;HANGUL LETTER O;Lo;0;L;<compat> 1169;;;;N;;;;; +3158;HANGUL LETTER WA;Lo;0;L;<compat> 116A;;;;N;;;;; +3159;HANGUL LETTER WAE;Lo;0;L;<compat> 116B;;;;N;;;;; +315A;HANGUL LETTER OE;Lo;0;L;<compat> 116C;;;;N;;;;; +315B;HANGUL LETTER YO;Lo;0;L;<compat> 116D;;;;N;;;;; +315C;HANGUL LETTER U;Lo;0;L;<compat> 116E;;;;N;;;;; +315D;HANGUL LETTER WEO;Lo;0;L;<compat> 116F;;;;N;;;;; +315E;HANGUL LETTER WE;Lo;0;L;<compat> 1170;;;;N;;;;; +315F;HANGUL LETTER WI;Lo;0;L;<compat> 1171;;;;N;;;;; +3160;HANGUL LETTER YU;Lo;0;L;<compat> 1172;;;;N;;;;; +3161;HANGUL LETTER EU;Lo;0;L;<compat> 1173;;;;N;;;;; +3162;HANGUL LETTER YI;Lo;0;L;<compat> 1174;;;;N;;;;; +3163;HANGUL LETTER I;Lo;0;L;<compat> 1175;;;;N;;;;; +3164;HANGUL FILLER;Lo;0;L;<compat> 1160;;;;N;HANGUL CAE OM;;;; +3165;HANGUL LETTER SSANGNIEUN;Lo;0;L;<compat> 1114;;;;N;HANGUL LETTER SSANG NIEUN;;;; +3166;HANGUL LETTER NIEUN-TIKEUT;Lo;0;L;<compat> 1115;;;;N;HANGUL LETTER NIEUN DIGEUD;;;; +3167;HANGUL LETTER NIEUN-SIOS;Lo;0;L;<compat> 11C7;;;;N;HANGUL LETTER NIEUN SIOS;;;; +3168;HANGUL LETTER NIEUN-PANSIOS;Lo;0;L;<compat> 11C8;;;;N;HANGUL LETTER NIEUN BAN CHI EUM;;;; +3169;HANGUL LETTER RIEUL-KIYEOK-SIOS;Lo;0;L;<compat> 11CC;;;;N;HANGUL LETTER LIEUL GIYEOG SIOS;;;; +316A;HANGUL LETTER RIEUL-TIKEUT;Lo;0;L;<compat> 11CE;;;;N;HANGUL LETTER LIEUL DIGEUD;;;; +316B;HANGUL LETTER RIEUL-PIEUP-SIOS;Lo;0;L;<compat> 11D3;;;;N;HANGUL LETTER LIEUL BIEUB SIOS;;;; +316C;HANGUL LETTER RIEUL-PANSIOS;Lo;0;L;<compat> 11D7;;;;N;HANGUL LETTER LIEUL BAN CHI EUM;;;; +316D;HANGUL LETTER RIEUL-YEORINHIEUH;Lo;0;L;<compat> 11D9;;;;N;HANGUL LETTER LIEUL YEOLIN HIEUH;;;; +316E;HANGUL LETTER MIEUM-PIEUP;Lo;0;L;<compat> 111C;;;;N;HANGUL LETTER MIEUM BIEUB;;;; +316F;HANGUL LETTER MIEUM-SIOS;Lo;0;L;<compat> 11DD;;;;N;HANGUL LETTER MIEUM SIOS;;;; +3170;HANGUL LETTER MIEUM-PANSIOS;Lo;0;L;<compat> 11DF;;;;N;HANGUL LETTER BIEUB BAN CHI EUM;;;; +3171;HANGUL LETTER KAPYEOUNMIEUM;Lo;0;L;<compat> 111D;;;;N;HANGUL LETTER MIEUM SUN GYEONG EUM;;;; +3172;HANGUL LETTER PIEUP-KIYEOK;Lo;0;L;<compat> 111E;;;;N;HANGUL LETTER BIEUB GIYEOG;;;; +3173;HANGUL LETTER PIEUP-TIKEUT;Lo;0;L;<compat> 1120;;;;N;HANGUL LETTER BIEUB DIGEUD;;;; +3174;HANGUL LETTER PIEUP-SIOS-KIYEOK;Lo;0;L;<compat> 1122;;;;N;HANGUL LETTER BIEUB SIOS GIYEOG;;;; +3175;HANGUL LETTER PIEUP-SIOS-TIKEUT;Lo;0;L;<compat> 1123;;;;N;HANGUL LETTER BIEUB SIOS DIGEUD;;;; +3176;HANGUL LETTER PIEUP-CIEUC;Lo;0;L;<compat> 1127;;;;N;HANGUL LETTER BIEUB JIEUJ;;;; +3177;HANGUL LETTER PIEUP-THIEUTH;Lo;0;L;<compat> 1129;;;;N;HANGUL LETTER BIEUB TIEUT;;;; +3178;HANGUL LETTER KAPYEOUNPIEUP;Lo;0;L;<compat> 112B;;;;N;HANGUL LETTER BIEUB SUN GYEONG EUM;;;; +3179;HANGUL LETTER KAPYEOUNSSANGPIEUP;Lo;0;L;<compat> 112C;;;;N;HANGUL LETTER SSANG BIEUB SUN GYEONG EUM;;;; +317A;HANGUL LETTER SIOS-KIYEOK;Lo;0;L;<compat> 112D;;;;N;HANGUL LETTER SIOS GIYEOG;;;; +317B;HANGUL LETTER SIOS-NIEUN;Lo;0;L;<compat> 112E;;;;N;HANGUL LETTER SIOS NIEUN;;;; +317C;HANGUL LETTER SIOS-TIKEUT;Lo;0;L;<compat> 112F;;;;N;HANGUL LETTER SIOS DIGEUD;;;; +317D;HANGUL LETTER SIOS-PIEUP;Lo;0;L;<compat> 1132;;;;N;HANGUL LETTER SIOS BIEUB;;;; +317E;HANGUL LETTER SIOS-CIEUC;Lo;0;L;<compat> 1136;;;;N;HANGUL LETTER SIOS JIEUJ;;;; +317F;HANGUL LETTER PANSIOS;Lo;0;L;<compat> 1140;;;;N;HANGUL LETTER BAN CHI EUM;;;; +3180;HANGUL LETTER SSANGIEUNG;Lo;0;L;<compat> 1147;;;;N;HANGUL LETTER SSANG IEUNG;;;; +3181;HANGUL LETTER YESIEUNG;Lo;0;L;<compat> 114C;;;;N;HANGUL LETTER NGIEUNG;;;; +3182;HANGUL LETTER YESIEUNG-SIOS;Lo;0;L;<compat> 11F1;;;;N;HANGUL LETTER NGIEUNG SIOS;;;; +3183;HANGUL LETTER YESIEUNG-PANSIOS;Lo;0;L;<compat> 11F2;;;;N;HANGUL LETTER NGIEUNG BAN CHI EUM;;;; +3184;HANGUL LETTER KAPYEOUNPHIEUPH;Lo;0;L;<compat> 1157;;;;N;HANGUL LETTER PIEUP SUN GYEONG EUM;;;; +3185;HANGUL LETTER SSANGHIEUH;Lo;0;L;<compat> 1158;;;;N;HANGUL LETTER SSANG HIEUH;;;; +3186;HANGUL LETTER YEORINHIEUH;Lo;0;L;<compat> 1159;;;;N;HANGUL LETTER YEOLIN HIEUH;;;; +3187;HANGUL LETTER YO-YA;Lo;0;L;<compat> 1184;;;;N;HANGUL LETTER YOYA;;;; +3188;HANGUL LETTER YO-YAE;Lo;0;L;<compat> 1185;;;;N;HANGUL LETTER YOYAE;;;; +3189;HANGUL LETTER YO-I;Lo;0;L;<compat> 1188;;;;N;HANGUL LETTER YOI;;;; +318A;HANGUL LETTER YU-YEO;Lo;0;L;<compat> 1191;;;;N;HANGUL LETTER YUYEO;;;; +318B;HANGUL LETTER YU-YE;Lo;0;L;<compat> 1192;;;;N;HANGUL LETTER YUYE;;;; +318C;HANGUL LETTER YU-I;Lo;0;L;<compat> 1194;;;;N;HANGUL LETTER YUI;;;; +318D;HANGUL LETTER ARAEA;Lo;0;L;<compat> 119E;;;;N;HANGUL LETTER ALAE A;;;; +318E;HANGUL LETTER ARAEAE;Lo;0;L;<compat> 11A1;;;;N;HANGUL LETTER ALAE AE;;;; +3190;IDEOGRAPHIC ANNOTATION LINKING MARK;So;0;L;;;;;N;KANBUN TATETEN;;;; +3191;IDEOGRAPHIC ANNOTATION REVERSE MARK;So;0;L;;;;;N;KAERITEN RE;;;; +3192;IDEOGRAPHIC ANNOTATION ONE MARK;No;0;L;<super> 4E00;;;1;N;KAERITEN ITI;;;; +3193;IDEOGRAPHIC ANNOTATION TWO MARK;No;0;L;<super> 4E8C;;;2;N;KAERITEN NI;;;; +3194;IDEOGRAPHIC ANNOTATION THREE MARK;No;0;L;<super> 4E09;;;3;N;KAERITEN SAN;;;; +3195;IDEOGRAPHIC ANNOTATION FOUR MARK;No;0;L;<super> 56DB;;;4;N;KAERITEN SI;;;; +3196;IDEOGRAPHIC ANNOTATION TOP MARK;So;0;L;<super> 4E0A;;;;N;KAERITEN ZYOU;;;; +3197;IDEOGRAPHIC ANNOTATION MIDDLE MARK;So;0;L;<super> 4E2D;;;;N;KAERITEN TYUU;;;; +3198;IDEOGRAPHIC ANNOTATION BOTTOM MARK;So;0;L;<super> 4E0B;;;;N;KAERITEN GE;;;; +3199;IDEOGRAPHIC ANNOTATION FIRST MARK;So;0;L;<super> 7532;;;;N;KAERITEN KOU;;;; +319A;IDEOGRAPHIC ANNOTATION SECOND MARK;So;0;L;<super> 4E59;;;;N;KAERITEN OTU;;;; +319B;IDEOGRAPHIC ANNOTATION THIRD MARK;So;0;L;<super> 4E19;;;;N;KAERITEN HEI;;;; +319C;IDEOGRAPHIC ANNOTATION FOURTH MARK;So;0;L;<super> 4E01;;;;N;KAERITEN TEI;;;; +319D;IDEOGRAPHIC ANNOTATION HEAVEN MARK;So;0;L;<super> 5929;;;;N;KAERITEN TEN;;;; +319E;IDEOGRAPHIC ANNOTATION EARTH MARK;So;0;L;<super> 5730;;;;N;KAERITEN TI;;;; +319F;IDEOGRAPHIC ANNOTATION MAN MARK;So;0;L;<super> 4EBA;;;;N;KAERITEN ZIN;;;; +31A0;BOPOMOFO LETTER BU;Lo;0;L;;;;;N;;;;; +31A1;BOPOMOFO LETTER ZI;Lo;0;L;;;;;N;;;;; +31A2;BOPOMOFO LETTER JI;Lo;0;L;;;;;N;;;;; +31A3;BOPOMOFO LETTER GU;Lo;0;L;;;;;N;;;;; +31A4;BOPOMOFO LETTER EE;Lo;0;L;;;;;N;;;;; +31A5;BOPOMOFO LETTER ENN;Lo;0;L;;;;;N;;;;; +31A6;BOPOMOFO LETTER OO;Lo;0;L;;;;;N;;;;; +31A7;BOPOMOFO LETTER ONN;Lo;0;L;;;;;N;;;;; +31A8;BOPOMOFO LETTER IR;Lo;0;L;;;;;N;;;;; +31A9;BOPOMOFO LETTER ANN;Lo;0;L;;;;;N;;;;; +31AA;BOPOMOFO LETTER INN;Lo;0;L;;;;;N;;;;; +31AB;BOPOMOFO LETTER UNN;Lo;0;L;;;;;N;;;;; +31AC;BOPOMOFO LETTER IM;Lo;0;L;;;;;N;;;;; +31AD;BOPOMOFO LETTER NGG;Lo;0;L;;;;;N;;;;; +31AE;BOPOMOFO LETTER AINN;Lo;0;L;;;;;N;;;;; +31AF;BOPOMOFO LETTER AUNN;Lo;0;L;;;;;N;;;;; +31B0;BOPOMOFO LETTER AM;Lo;0;L;;;;;N;;;;; +31B1;BOPOMOFO LETTER OM;Lo;0;L;;;;;N;;;;; +31B2;BOPOMOFO LETTER ONG;Lo;0;L;;;;;N;;;;; +31B3;BOPOMOFO LETTER INNN;Lo;0;L;;;;;N;;;;; +31B4;BOPOMOFO FINAL LETTER P;Lo;0;L;;;;;N;;;;; +31B5;BOPOMOFO FINAL LETTER T;Lo;0;L;;;;;N;;;;; +31B6;BOPOMOFO FINAL LETTER K;Lo;0;L;;;;;N;;;;; +31B7;BOPOMOFO FINAL LETTER H;Lo;0;L;;;;;N;;;;; +31B8;BOPOMOFO LETTER GH;Lo;0;L;;;;;N;;;;; +31B9;BOPOMOFO LETTER LH;Lo;0;L;;;;;N;;;;; +31BA;BOPOMOFO LETTER ZY;Lo;0;L;;;;;N;;;;; +31C0;CJK STROKE T;So;0;ON;;;;;N;;;;; +31C1;CJK STROKE WG;So;0;ON;;;;;N;;;;; +31C2;CJK STROKE XG;So;0;ON;;;;;N;;;;; +31C3;CJK STROKE BXG;So;0;ON;;;;;N;;;;; +31C4;CJK STROKE SW;So;0;ON;;;;;N;;;;; +31C5;CJK STROKE HZZ;So;0;ON;;;;;N;;;;; +31C6;CJK STROKE HZG;So;0;ON;;;;;N;;;;; +31C7;CJK STROKE HP;So;0;ON;;;;;N;;;;; +31C8;CJK STROKE HZWG;So;0;ON;;;;;N;;;;; +31C9;CJK STROKE SZWG;So;0;ON;;;;;N;;;;; +31CA;CJK STROKE HZT;So;0;ON;;;;;N;;;;; +31CB;CJK STROKE HZZP;So;0;ON;;;;;N;;;;; +31CC;CJK STROKE HPWG;So;0;ON;;;;;N;;;;; +31CD;CJK STROKE HZW;So;0;ON;;;;;N;;;;; +31CE;CJK STROKE HZZZ;So;0;ON;;;;;N;;;;; +31CF;CJK STROKE N;So;0;ON;;;;;N;;;;; +31D0;CJK STROKE H;So;0;ON;;;;;N;;;;; +31D1;CJK STROKE S;So;0;ON;;;;;N;;;;; +31D2;CJK STROKE P;So;0;ON;;;;;N;;;;; +31D3;CJK STROKE SP;So;0;ON;;;;;N;;;;; +31D4;CJK STROKE D;So;0;ON;;;;;N;;;;; +31D5;CJK STROKE HZ;So;0;ON;;;;;N;;;;; +31D6;CJK STROKE HG;So;0;ON;;;;;N;;;;; +31D7;CJK STROKE SZ;So;0;ON;;;;;N;;;;; +31D8;CJK STROKE SWZ;So;0;ON;;;;;N;;;;; +31D9;CJK STROKE ST;So;0;ON;;;;;N;;;;; +31DA;CJK STROKE SG;So;0;ON;;;;;N;;;;; +31DB;CJK STROKE PD;So;0;ON;;;;;N;;;;; +31DC;CJK STROKE PZ;So;0;ON;;;;;N;;;;; +31DD;CJK STROKE TN;So;0;ON;;;;;N;;;;; +31DE;CJK STROKE SZZ;So;0;ON;;;;;N;;;;; +31DF;CJK STROKE SWG;So;0;ON;;;;;N;;;;; +31E0;CJK STROKE HXWG;So;0;ON;;;;;N;;;;; +31E1;CJK STROKE HZZZG;So;0;ON;;;;;N;;;;; +31E2;CJK STROKE PG;So;0;ON;;;;;N;;;;; +31E3;CJK STROKE Q;So;0;ON;;;;;N;;;;; +31F0;KATAKANA LETTER SMALL KU;Lo;0;L;;;;;N;;;;; +31F1;KATAKANA LETTER SMALL SI;Lo;0;L;;;;;N;;;;; +31F2;KATAKANA LETTER SMALL SU;Lo;0;L;;;;;N;;;;; +31F3;KATAKANA LETTER SMALL TO;Lo;0;L;;;;;N;;;;; +31F4;KATAKANA LETTER SMALL NU;Lo;0;L;;;;;N;;;;; +31F5;KATAKANA LETTER SMALL HA;Lo;0;L;;;;;N;;;;; +31F6;KATAKANA LETTER SMALL HI;Lo;0;L;;;;;N;;;;; +31F7;KATAKANA LETTER SMALL HU;Lo;0;L;;;;;N;;;;; +31F8;KATAKANA LETTER SMALL HE;Lo;0;L;;;;;N;;;;; +31F9;KATAKANA LETTER SMALL HO;Lo;0;L;;;;;N;;;;; +31FA;KATAKANA LETTER SMALL MU;Lo;0;L;;;;;N;;;;; +31FB;KATAKANA LETTER SMALL RA;Lo;0;L;;;;;N;;;;; +31FC;KATAKANA LETTER SMALL RI;Lo;0;L;;;;;N;;;;; +31FD;KATAKANA LETTER SMALL RU;Lo;0;L;;;;;N;;;;; +31FE;KATAKANA LETTER SMALL RE;Lo;0;L;;;;;N;;;;; +31FF;KATAKANA LETTER SMALL RO;Lo;0;L;;;;;N;;;;; +3200;PARENTHESIZED HANGUL KIYEOK;So;0;L;<compat> 0028 1100 0029;;;;N;PARENTHESIZED HANGUL GIYEOG;;;; +3201;PARENTHESIZED HANGUL NIEUN;So;0;L;<compat> 0028 1102 0029;;;;N;;;;; +3202;PARENTHESIZED HANGUL TIKEUT;So;0;L;<compat> 0028 1103 0029;;;;N;PARENTHESIZED HANGUL DIGEUD;;;; +3203;PARENTHESIZED HANGUL RIEUL;So;0;L;<compat> 0028 1105 0029;;;;N;PARENTHESIZED HANGUL LIEUL;;;; +3204;PARENTHESIZED HANGUL MIEUM;So;0;L;<compat> 0028 1106 0029;;;;N;;;;; +3205;PARENTHESIZED HANGUL PIEUP;So;0;L;<compat> 0028 1107 0029;;;;N;PARENTHESIZED HANGUL BIEUB;;;; +3206;PARENTHESIZED HANGUL SIOS;So;0;L;<compat> 0028 1109 0029;;;;N;;;;; +3207;PARENTHESIZED HANGUL IEUNG;So;0;L;<compat> 0028 110B 0029;;;;N;;;;; +3208;PARENTHESIZED HANGUL CIEUC;So;0;L;<compat> 0028 110C 0029;;;;N;PARENTHESIZED HANGUL JIEUJ;;;; +3209;PARENTHESIZED HANGUL CHIEUCH;So;0;L;<compat> 0028 110E 0029;;;;N;PARENTHESIZED HANGUL CIEUC;;;; +320A;PARENTHESIZED HANGUL KHIEUKH;So;0;L;<compat> 0028 110F 0029;;;;N;PARENTHESIZED HANGUL KIYEOK;;;; +320B;PARENTHESIZED HANGUL THIEUTH;So;0;L;<compat> 0028 1110 0029;;;;N;PARENTHESIZED HANGUL TIEUT;;;; +320C;PARENTHESIZED HANGUL PHIEUPH;So;0;L;<compat> 0028 1111 0029;;;;N;PARENTHESIZED HANGUL PIEUP;;;; +320D;PARENTHESIZED HANGUL HIEUH;So;0;L;<compat> 0028 1112 0029;;;;N;;;;; +320E;PARENTHESIZED HANGUL KIYEOK A;So;0;L;<compat> 0028 1100 1161 0029;;;;N;PARENTHESIZED HANGUL GA;;;; +320F;PARENTHESIZED HANGUL NIEUN A;So;0;L;<compat> 0028 1102 1161 0029;;;;N;PARENTHESIZED HANGUL NA;;;; +3210;PARENTHESIZED HANGUL TIKEUT A;So;0;L;<compat> 0028 1103 1161 0029;;;;N;PARENTHESIZED HANGUL DA;;;; +3211;PARENTHESIZED HANGUL RIEUL A;So;0;L;<compat> 0028 1105 1161 0029;;;;N;PARENTHESIZED HANGUL LA;;;; +3212;PARENTHESIZED HANGUL MIEUM A;So;0;L;<compat> 0028 1106 1161 0029;;;;N;PARENTHESIZED HANGUL MA;;;; +3213;PARENTHESIZED HANGUL PIEUP A;So;0;L;<compat> 0028 1107 1161 0029;;;;N;PARENTHESIZED HANGUL BA;;;; +3214;PARENTHESIZED HANGUL SIOS A;So;0;L;<compat> 0028 1109 1161 0029;;;;N;PARENTHESIZED HANGUL SA;;;; +3215;PARENTHESIZED HANGUL IEUNG A;So;0;L;<compat> 0028 110B 1161 0029;;;;N;PARENTHESIZED HANGUL A;;;; +3216;PARENTHESIZED HANGUL CIEUC A;So;0;L;<compat> 0028 110C 1161 0029;;;;N;PARENTHESIZED HANGUL JA;;;; +3217;PARENTHESIZED HANGUL CHIEUCH A;So;0;L;<compat> 0028 110E 1161 0029;;;;N;PARENTHESIZED HANGUL CA;;;; +3218;PARENTHESIZED HANGUL KHIEUKH A;So;0;L;<compat> 0028 110F 1161 0029;;;;N;PARENTHESIZED HANGUL KA;;;; +3219;PARENTHESIZED HANGUL THIEUTH A;So;0;L;<compat> 0028 1110 1161 0029;;;;N;PARENTHESIZED HANGUL TA;;;; +321A;PARENTHESIZED HANGUL PHIEUPH A;So;0;L;<compat> 0028 1111 1161 0029;;;;N;PARENTHESIZED HANGUL PA;;;; +321B;PARENTHESIZED HANGUL HIEUH A;So;0;L;<compat> 0028 1112 1161 0029;;;;N;PARENTHESIZED HANGUL HA;;;; +321C;PARENTHESIZED HANGUL CIEUC U;So;0;L;<compat> 0028 110C 116E 0029;;;;N;PARENTHESIZED HANGUL JU;;;; +321D;PARENTHESIZED KOREAN CHARACTER OJEON;So;0;ON;<compat> 0028 110B 1169 110C 1165 11AB 0029;;;;N;;;;; +321E;PARENTHESIZED KOREAN CHARACTER O HU;So;0;ON;<compat> 0028 110B 1169 1112 116E 0029;;;;N;;;;; +3220;PARENTHESIZED IDEOGRAPH ONE;No;0;L;<compat> 0028 4E00 0029;;;1;N;;;;; +3221;PARENTHESIZED IDEOGRAPH TWO;No;0;L;<compat> 0028 4E8C 0029;;;2;N;;;;; +3222;PARENTHESIZED IDEOGRAPH THREE;No;0;L;<compat> 0028 4E09 0029;;;3;N;;;;; +3223;PARENTHESIZED IDEOGRAPH FOUR;No;0;L;<compat> 0028 56DB 0029;;;4;N;;;;; +3224;PARENTHESIZED IDEOGRAPH FIVE;No;0;L;<compat> 0028 4E94 0029;;;5;N;;;;; +3225;PARENTHESIZED IDEOGRAPH SIX;No;0;L;<compat> 0028 516D 0029;;;6;N;;;;; +3226;PARENTHESIZED IDEOGRAPH SEVEN;No;0;L;<compat> 0028 4E03 0029;;;7;N;;;;; +3227;PARENTHESIZED IDEOGRAPH EIGHT;No;0;L;<compat> 0028 516B 0029;;;8;N;;;;; +3228;PARENTHESIZED IDEOGRAPH NINE;No;0;L;<compat> 0028 4E5D 0029;;;9;N;;;;; +3229;PARENTHESIZED IDEOGRAPH TEN;No;0;L;<compat> 0028 5341 0029;;;10;N;;;;; +322A;PARENTHESIZED IDEOGRAPH MOON;So;0;L;<compat> 0028 6708 0029;;;;N;;;;; +322B;PARENTHESIZED IDEOGRAPH FIRE;So;0;L;<compat> 0028 706B 0029;;;;N;;;;; +322C;PARENTHESIZED IDEOGRAPH WATER;So;0;L;<compat> 0028 6C34 0029;;;;N;;;;; +322D;PARENTHESIZED IDEOGRAPH WOOD;So;0;L;<compat> 0028 6728 0029;;;;N;;;;; +322E;PARENTHESIZED IDEOGRAPH METAL;So;0;L;<compat> 0028 91D1 0029;;;;N;;;;; +322F;PARENTHESIZED IDEOGRAPH EARTH;So;0;L;<compat> 0028 571F 0029;;;;N;;;;; +3230;PARENTHESIZED IDEOGRAPH SUN;So;0;L;<compat> 0028 65E5 0029;;;;N;;;;; +3231;PARENTHESIZED IDEOGRAPH STOCK;So;0;L;<compat> 0028 682A 0029;;;;N;;;;; +3232;PARENTHESIZED IDEOGRAPH HAVE;So;0;L;<compat> 0028 6709 0029;;;;N;;;;; +3233;PARENTHESIZED IDEOGRAPH SOCIETY;So;0;L;<compat> 0028 793E 0029;;;;N;;;;; +3234;PARENTHESIZED IDEOGRAPH NAME;So;0;L;<compat> 0028 540D 0029;;;;N;;;;; +3235;PARENTHESIZED IDEOGRAPH SPECIAL;So;0;L;<compat> 0028 7279 0029;;;;N;;;;; +3236;PARENTHESIZED IDEOGRAPH FINANCIAL;So;0;L;<compat> 0028 8CA1 0029;;;;N;;;;; +3237;PARENTHESIZED IDEOGRAPH CONGRATULATION;So;0;L;<compat> 0028 795D 0029;;;;N;;;;; +3238;PARENTHESIZED IDEOGRAPH LABOR;So;0;L;<compat> 0028 52B4 0029;;;;N;;;;; +3239;PARENTHESIZED IDEOGRAPH REPRESENT;So;0;L;<compat> 0028 4EE3 0029;;;;N;;;;; +323A;PARENTHESIZED IDEOGRAPH CALL;So;0;L;<compat> 0028 547C 0029;;;;N;;;;; +323B;PARENTHESIZED IDEOGRAPH STUDY;So;0;L;<compat> 0028 5B66 0029;;;;N;;;;; +323C;PARENTHESIZED IDEOGRAPH SUPERVISE;So;0;L;<compat> 0028 76E3 0029;;;;N;;;;; +323D;PARENTHESIZED IDEOGRAPH ENTERPRISE;So;0;L;<compat> 0028 4F01 0029;;;;N;;;;; +323E;PARENTHESIZED IDEOGRAPH RESOURCE;So;0;L;<compat> 0028 8CC7 0029;;;;N;;;;; +323F;PARENTHESIZED IDEOGRAPH ALLIANCE;So;0;L;<compat> 0028 5354 0029;;;;N;;;;; +3240;PARENTHESIZED IDEOGRAPH FESTIVAL;So;0;L;<compat> 0028 796D 0029;;;;N;;;;; +3241;PARENTHESIZED IDEOGRAPH REST;So;0;L;<compat> 0028 4F11 0029;;;;N;;;;; +3242;PARENTHESIZED IDEOGRAPH SELF;So;0;L;<compat> 0028 81EA 0029;;;;N;;;;; +3243;PARENTHESIZED IDEOGRAPH REACH;So;0;L;<compat> 0028 81F3 0029;;;;N;;;;; +3244;CIRCLED IDEOGRAPH QUESTION;So;0;L;<circle> 554F;;;;N;;;;; +3245;CIRCLED IDEOGRAPH KINDERGARTEN;So;0;L;<circle> 5E7C;;;;N;;;;; +3246;CIRCLED IDEOGRAPH SCHOOL;So;0;L;<circle> 6587;;;;N;;;;; +3247;CIRCLED IDEOGRAPH KOTO;So;0;L;<circle> 7B8F;;;;N;;;;; +3248;CIRCLED NUMBER TEN ON BLACK SQUARE;No;0;L;;;;10;N;;;;; +3249;CIRCLED NUMBER TWENTY ON BLACK SQUARE;No;0;L;;;;20;N;;;;; +324A;CIRCLED NUMBER THIRTY ON BLACK SQUARE;No;0;L;;;;30;N;;;;; +324B;CIRCLED NUMBER FORTY ON BLACK SQUARE;No;0;L;;;;40;N;;;;; +324C;CIRCLED NUMBER FIFTY ON BLACK SQUARE;No;0;L;;;;50;N;;;;; +324D;CIRCLED NUMBER SIXTY ON BLACK SQUARE;No;0;L;;;;60;N;;;;; +324E;CIRCLED NUMBER SEVENTY ON BLACK SQUARE;No;0;L;;;;70;N;;;;; +324F;CIRCLED NUMBER EIGHTY ON BLACK SQUARE;No;0;L;;;;80;N;;;;; +3250;PARTNERSHIP SIGN;So;0;ON;<square> 0050 0054 0045;;;;N;;;;; +3251;CIRCLED NUMBER TWENTY ONE;No;0;ON;<circle> 0032 0031;;;21;N;;;;; +3252;CIRCLED NUMBER TWENTY TWO;No;0;ON;<circle> 0032 0032;;;22;N;;;;; +3253;CIRCLED NUMBER TWENTY THREE;No;0;ON;<circle> 0032 0033;;;23;N;;;;; +3254;CIRCLED NUMBER TWENTY FOUR;No;0;ON;<circle> 0032 0034;;;24;N;;;;; +3255;CIRCLED NUMBER TWENTY FIVE;No;0;ON;<circle> 0032 0035;;;25;N;;;;; +3256;CIRCLED NUMBER TWENTY SIX;No;0;ON;<circle> 0032 0036;;;26;N;;;;; +3257;CIRCLED NUMBER TWENTY SEVEN;No;0;ON;<circle> 0032 0037;;;27;N;;;;; +3258;CIRCLED NUMBER TWENTY EIGHT;No;0;ON;<circle> 0032 0038;;;28;N;;;;; +3259;CIRCLED NUMBER TWENTY NINE;No;0;ON;<circle> 0032 0039;;;29;N;;;;; +325A;CIRCLED NUMBER THIRTY;No;0;ON;<circle> 0033 0030;;;30;N;;;;; +325B;CIRCLED NUMBER THIRTY ONE;No;0;ON;<circle> 0033 0031;;;31;N;;;;; +325C;CIRCLED NUMBER THIRTY TWO;No;0;ON;<circle> 0033 0032;;;32;N;;;;; +325D;CIRCLED NUMBER THIRTY THREE;No;0;ON;<circle> 0033 0033;;;33;N;;;;; +325E;CIRCLED NUMBER THIRTY FOUR;No;0;ON;<circle> 0033 0034;;;34;N;;;;; +325F;CIRCLED NUMBER THIRTY FIVE;No;0;ON;<circle> 0033 0035;;;35;N;;;;; +3260;CIRCLED HANGUL KIYEOK;So;0;L;<circle> 1100;;;;N;CIRCLED HANGUL GIYEOG;;;; +3261;CIRCLED HANGUL NIEUN;So;0;L;<circle> 1102;;;;N;;;;; +3262;CIRCLED HANGUL TIKEUT;So;0;L;<circle> 1103;;;;N;CIRCLED HANGUL DIGEUD;;;; +3263;CIRCLED HANGUL RIEUL;So;0;L;<circle> 1105;;;;N;CIRCLED HANGUL LIEUL;;;; +3264;CIRCLED HANGUL MIEUM;So;0;L;<circle> 1106;;;;N;;;;; +3265;CIRCLED HANGUL PIEUP;So;0;L;<circle> 1107;;;;N;CIRCLED HANGUL BIEUB;;;; +3266;CIRCLED HANGUL SIOS;So;0;L;<circle> 1109;;;;N;;;;; +3267;CIRCLED HANGUL IEUNG;So;0;L;<circle> 110B;;;;N;;;;; +3268;CIRCLED HANGUL CIEUC;So;0;L;<circle> 110C;;;;N;CIRCLED HANGUL JIEUJ;;;; +3269;CIRCLED HANGUL CHIEUCH;So;0;L;<circle> 110E;;;;N;CIRCLED HANGUL CIEUC;;;; +326A;CIRCLED HANGUL KHIEUKH;So;0;L;<circle> 110F;;;;N;CIRCLED HANGUL KIYEOK;;;; +326B;CIRCLED HANGUL THIEUTH;So;0;L;<circle> 1110;;;;N;CIRCLED HANGUL TIEUT;;;; +326C;CIRCLED HANGUL PHIEUPH;So;0;L;<circle> 1111;;;;N;CIRCLED HANGUL PIEUP;;;; +326D;CIRCLED HANGUL HIEUH;So;0;L;<circle> 1112;;;;N;;;;; +326E;CIRCLED HANGUL KIYEOK A;So;0;L;<circle> 1100 1161;;;;N;CIRCLED HANGUL GA;;;; +326F;CIRCLED HANGUL NIEUN A;So;0;L;<circle> 1102 1161;;;;N;CIRCLED HANGUL NA;;;; +3270;CIRCLED HANGUL TIKEUT A;So;0;L;<circle> 1103 1161;;;;N;CIRCLED HANGUL DA;;;; +3271;CIRCLED HANGUL RIEUL A;So;0;L;<circle> 1105 1161;;;;N;CIRCLED HANGUL LA;;;; +3272;CIRCLED HANGUL MIEUM A;So;0;L;<circle> 1106 1161;;;;N;CIRCLED HANGUL MA;;;; +3273;CIRCLED HANGUL PIEUP A;So;0;L;<circle> 1107 1161;;;;N;CIRCLED HANGUL BA;;;; +3274;CIRCLED HANGUL SIOS A;So;0;L;<circle> 1109 1161;;;;N;CIRCLED HANGUL SA;;;; +3275;CIRCLED HANGUL IEUNG A;So;0;L;<circle> 110B 1161;;;;N;CIRCLED HANGUL A;;;; +3276;CIRCLED HANGUL CIEUC A;So;0;L;<circle> 110C 1161;;;;N;CIRCLED HANGUL JA;;;; +3277;CIRCLED HANGUL CHIEUCH A;So;0;L;<circle> 110E 1161;;;;N;CIRCLED HANGUL CA;;;; +3278;CIRCLED HANGUL KHIEUKH A;So;0;L;<circle> 110F 1161;;;;N;CIRCLED HANGUL KA;;;; +3279;CIRCLED HANGUL THIEUTH A;So;0;L;<circle> 1110 1161;;;;N;CIRCLED HANGUL TA;;;; +327A;CIRCLED HANGUL PHIEUPH A;So;0;L;<circle> 1111 1161;;;;N;CIRCLED HANGUL PA;;;; +327B;CIRCLED HANGUL HIEUH A;So;0;L;<circle> 1112 1161;;;;N;CIRCLED HANGUL HA;;;; +327C;CIRCLED KOREAN CHARACTER CHAMKO;So;0;ON;<circle> 110E 1161 11B7 1100 1169;;;;N;;;;; +327D;CIRCLED KOREAN CHARACTER JUEUI;So;0;ON;<circle> 110C 116E 110B 1174;;;;N;;;;; +327E;CIRCLED HANGUL IEUNG U;So;0;ON;<circle> 110B 116E;;;;N;;;;; +327F;KOREAN STANDARD SYMBOL;So;0;L;;;;;N;;;;; +3280;CIRCLED IDEOGRAPH ONE;No;0;L;<circle> 4E00;;;1;N;;;;; +3281;CIRCLED IDEOGRAPH TWO;No;0;L;<circle> 4E8C;;;2;N;;;;; +3282;CIRCLED IDEOGRAPH THREE;No;0;L;<circle> 4E09;;;3;N;;;;; +3283;CIRCLED IDEOGRAPH FOUR;No;0;L;<circle> 56DB;;;4;N;;;;; +3284;CIRCLED IDEOGRAPH FIVE;No;0;L;<circle> 4E94;;;5;N;;;;; +3285;CIRCLED IDEOGRAPH SIX;No;0;L;<circle> 516D;;;6;N;;;;; +3286;CIRCLED IDEOGRAPH SEVEN;No;0;L;<circle> 4E03;;;7;N;;;;; +3287;CIRCLED IDEOGRAPH EIGHT;No;0;L;<circle> 516B;;;8;N;;;;; +3288;CIRCLED IDEOGRAPH NINE;No;0;L;<circle> 4E5D;;;9;N;;;;; +3289;CIRCLED IDEOGRAPH TEN;No;0;L;<circle> 5341;;;10;N;;;;; +328A;CIRCLED IDEOGRAPH MOON;So;0;L;<circle> 6708;;;;N;;;;; +328B;CIRCLED IDEOGRAPH FIRE;So;0;L;<circle> 706B;;;;N;;;;; +328C;CIRCLED IDEOGRAPH WATER;So;0;L;<circle> 6C34;;;;N;;;;; +328D;CIRCLED IDEOGRAPH WOOD;So;0;L;<circle> 6728;;;;N;;;;; +328E;CIRCLED IDEOGRAPH METAL;So;0;L;<circle> 91D1;;;;N;;;;; +328F;CIRCLED IDEOGRAPH EARTH;So;0;L;<circle> 571F;;;;N;;;;; +3290;CIRCLED IDEOGRAPH SUN;So;0;L;<circle> 65E5;;;;N;;;;; +3291;CIRCLED IDEOGRAPH STOCK;So;0;L;<circle> 682A;;;;N;;;;; +3292;CIRCLED IDEOGRAPH HAVE;So;0;L;<circle> 6709;;;;N;;;;; +3293;CIRCLED IDEOGRAPH SOCIETY;So;0;L;<circle> 793E;;;;N;;;;; +3294;CIRCLED IDEOGRAPH NAME;So;0;L;<circle> 540D;;;;N;;;;; +3295;CIRCLED IDEOGRAPH SPECIAL;So;0;L;<circle> 7279;;;;N;;;;; +3296;CIRCLED IDEOGRAPH FINANCIAL;So;0;L;<circle> 8CA1;;;;N;;;;; +3297;CIRCLED IDEOGRAPH CONGRATULATION;So;0;L;<circle> 795D;;;;N;;;;; +3298;CIRCLED IDEOGRAPH LABOR;So;0;L;<circle> 52B4;;;;N;;;;; +3299;CIRCLED IDEOGRAPH SECRET;So;0;L;<circle> 79D8;;;;N;;;;; +329A;CIRCLED IDEOGRAPH MALE;So;0;L;<circle> 7537;;;;N;;;;; +329B;CIRCLED IDEOGRAPH FEMALE;So;0;L;<circle> 5973;;;;N;;;;; +329C;CIRCLED IDEOGRAPH SUITABLE;So;0;L;<circle> 9069;;;;N;;;;; +329D;CIRCLED IDEOGRAPH EXCELLENT;So;0;L;<circle> 512A;;;;N;;;;; +329E;CIRCLED IDEOGRAPH PRINT;So;0;L;<circle> 5370;;;;N;;;;; +329F;CIRCLED IDEOGRAPH ATTENTION;So;0;L;<circle> 6CE8;;;;N;;;;; +32A0;CIRCLED IDEOGRAPH ITEM;So;0;L;<circle> 9805;;;;N;;;;; +32A1;CIRCLED IDEOGRAPH REST;So;0;L;<circle> 4F11;;;;N;;;;; +32A2;CIRCLED IDEOGRAPH COPY;So;0;L;<circle> 5199;;;;N;;;;; +32A3;CIRCLED IDEOGRAPH CORRECT;So;0;L;<circle> 6B63;;;;N;;;;; +32A4;CIRCLED IDEOGRAPH HIGH;So;0;L;<circle> 4E0A;;;;N;;;;; +32A5;CIRCLED IDEOGRAPH CENTRE;So;0;L;<circle> 4E2D;;;;N;CIRCLED IDEOGRAPH CENTER;;;; +32A6;CIRCLED IDEOGRAPH LOW;So;0;L;<circle> 4E0B;;;;N;;;;; +32A7;CIRCLED IDEOGRAPH LEFT;So;0;L;<circle> 5DE6;;;;N;;;;; +32A8;CIRCLED IDEOGRAPH RIGHT;So;0;L;<circle> 53F3;;;;N;;;;; +32A9;CIRCLED IDEOGRAPH MEDICINE;So;0;L;<circle> 533B;;;;N;;;;; +32AA;CIRCLED IDEOGRAPH RELIGION;So;0;L;<circle> 5B97;;;;N;;;;; +32AB;CIRCLED IDEOGRAPH STUDY;So;0;L;<circle> 5B66;;;;N;;;;; +32AC;CIRCLED IDEOGRAPH SUPERVISE;So;0;L;<circle> 76E3;;;;N;;;;; +32AD;CIRCLED IDEOGRAPH ENTERPRISE;So;0;L;<circle> 4F01;;;;N;;;;; +32AE;CIRCLED IDEOGRAPH RESOURCE;So;0;L;<circle> 8CC7;;;;N;;;;; +32AF;CIRCLED IDEOGRAPH ALLIANCE;So;0;L;<circle> 5354;;;;N;;;;; +32B0;CIRCLED IDEOGRAPH NIGHT;So;0;L;<circle> 591C;;;;N;;;;; +32B1;CIRCLED NUMBER THIRTY SIX;No;0;ON;<circle> 0033 0036;;;36;N;;;;; +32B2;CIRCLED NUMBER THIRTY SEVEN;No;0;ON;<circle> 0033 0037;;;37;N;;;;; +32B3;CIRCLED NUMBER THIRTY EIGHT;No;0;ON;<circle> 0033 0038;;;38;N;;;;; +32B4;CIRCLED NUMBER THIRTY NINE;No;0;ON;<circle> 0033 0039;;;39;N;;;;; +32B5;CIRCLED NUMBER FORTY;No;0;ON;<circle> 0034 0030;;;40;N;;;;; +32B6;CIRCLED NUMBER FORTY ONE;No;0;ON;<circle> 0034 0031;;;41;N;;;;; +32B7;CIRCLED NUMBER FORTY TWO;No;0;ON;<circle> 0034 0032;;;42;N;;;;; +32B8;CIRCLED NUMBER FORTY THREE;No;0;ON;<circle> 0034 0033;;;43;N;;;;; +32B9;CIRCLED NUMBER FORTY FOUR;No;0;ON;<circle> 0034 0034;;;44;N;;;;; +32BA;CIRCLED NUMBER FORTY FIVE;No;0;ON;<circle> 0034 0035;;;45;N;;;;; +32BB;CIRCLED NUMBER FORTY SIX;No;0;ON;<circle> 0034 0036;;;46;N;;;;; +32BC;CIRCLED NUMBER FORTY SEVEN;No;0;ON;<circle> 0034 0037;;;47;N;;;;; +32BD;CIRCLED NUMBER FORTY EIGHT;No;0;ON;<circle> 0034 0038;;;48;N;;;;; +32BE;CIRCLED NUMBER FORTY NINE;No;0;ON;<circle> 0034 0039;;;49;N;;;;; +32BF;CIRCLED NUMBER FIFTY;No;0;ON;<circle> 0035 0030;;;50;N;;;;; +32C0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY;So;0;L;<compat> 0031 6708;;;;N;;;;; +32C1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUARY;So;0;L;<compat> 0032 6708;;;;N;;;;; +32C2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH;So;0;L;<compat> 0033 6708;;;;N;;;;; +32C3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL;So;0;L;<compat> 0034 6708;;;;N;;;;; +32C4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY;So;0;L;<compat> 0035 6708;;;;N;;;;; +32C5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE;So;0;L;<compat> 0036 6708;;;;N;;;;; +32C6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY;So;0;L;<compat> 0037 6708;;;;N;;;;; +32C7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST;So;0;L;<compat> 0038 6708;;;;N;;;;; +32C8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEMBER;So;0;L;<compat> 0039 6708;;;;N;;;;; +32C9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBER;So;0;L;<compat> 0031 0030 6708;;;;N;;;;; +32CA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMBER;So;0;L;<compat> 0031 0031 6708;;;;N;;;;; +32CB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMBER;So;0;L;<compat> 0031 0032 6708;;;;N;;;;; +32CC;SQUARE HG;So;0;ON;<square> 0048 0067;;;;N;;;;; +32CD;SQUARE ERG;So;0;ON;<square> 0065 0072 0067;;;;N;;;;; +32CE;SQUARE EV;So;0;ON;<square> 0065 0056;;;;N;;;;; +32CF;LIMITED LIABILITY SIGN;So;0;ON;<square> 004C 0054 0044;;;;N;;;;; +32D0;CIRCLED KATAKANA A;So;0;L;<circle> 30A2;;;;N;;;;; +32D1;CIRCLED KATAKANA I;So;0;L;<circle> 30A4;;;;N;;;;; +32D2;CIRCLED KATAKANA U;So;0;L;<circle> 30A6;;;;N;;;;; +32D3;CIRCLED KATAKANA E;So;0;L;<circle> 30A8;;;;N;;;;; +32D4;CIRCLED KATAKANA O;So;0;L;<circle> 30AA;;;;N;;;;; +32D5;CIRCLED KATAKANA KA;So;0;L;<circle> 30AB;;;;N;;;;; +32D6;CIRCLED KATAKANA KI;So;0;L;<circle> 30AD;;;;N;;;;; +32D7;CIRCLED KATAKANA KU;So;0;L;<circle> 30AF;;;;N;;;;; +32D8;CIRCLED KATAKANA KE;So;0;L;<circle> 30B1;;;;N;;;;; +32D9;CIRCLED KATAKANA KO;So;0;L;<circle> 30B3;;;;N;;;;; +32DA;CIRCLED KATAKANA SA;So;0;L;<circle> 30B5;;;;N;;;;; +32DB;CIRCLED KATAKANA SI;So;0;L;<circle> 30B7;;;;N;;;;; +32DC;CIRCLED KATAKANA SU;So;0;L;<circle> 30B9;;;;N;;;;; +32DD;CIRCLED KATAKANA SE;So;0;L;<circle> 30BB;;;;N;;;;; +32DE;CIRCLED KATAKANA SO;So;0;L;<circle> 30BD;;;;N;;;;; +32DF;CIRCLED KATAKANA TA;So;0;L;<circle> 30BF;;;;N;;;;; +32E0;CIRCLED KATAKANA TI;So;0;L;<circle> 30C1;;;;N;;;;; +32E1;CIRCLED KATAKANA TU;So;0;L;<circle> 30C4;;;;N;;;;; +32E2;CIRCLED KATAKANA TE;So;0;L;<circle> 30C6;;;;N;;;;; +32E3;CIRCLED KATAKANA TO;So;0;L;<circle> 30C8;;;;N;;;;; +32E4;CIRCLED KATAKANA NA;So;0;L;<circle> 30CA;;;;N;;;;; +32E5;CIRCLED KATAKANA NI;So;0;L;<circle> 30CB;;;;N;;;;; +32E6;CIRCLED KATAKANA NU;So;0;L;<circle> 30CC;;;;N;;;;; +32E7;CIRCLED KATAKANA NE;So;0;L;<circle> 30CD;;;;N;;;;; +32E8;CIRCLED KATAKANA NO;So;0;L;<circle> 30CE;;;;N;;;;; +32E9;CIRCLED KATAKANA HA;So;0;L;<circle> 30CF;;;;N;;;;; +32EA;CIRCLED KATAKANA HI;So;0;L;<circle> 30D2;;;;N;;;;; +32EB;CIRCLED KATAKANA HU;So;0;L;<circle> 30D5;;;;N;;;;; +32EC;CIRCLED KATAKANA HE;So;0;L;<circle> 30D8;;;;N;;;;; +32ED;CIRCLED KATAKANA HO;So;0;L;<circle> 30DB;;;;N;;;;; +32EE;CIRCLED KATAKANA MA;So;0;L;<circle> 30DE;;;;N;;;;; +32EF;CIRCLED KATAKANA MI;So;0;L;<circle> 30DF;;;;N;;;;; +32F0;CIRCLED KATAKANA MU;So;0;L;<circle> 30E0;;;;N;;;;; +32F1;CIRCLED KATAKANA ME;So;0;L;<circle> 30E1;;;;N;;;;; +32F2;CIRCLED KATAKANA MO;So;0;L;<circle> 30E2;;;;N;;;;; +32F3;CIRCLED KATAKANA YA;So;0;L;<circle> 30E4;;;;N;;;;; +32F4;CIRCLED KATAKANA YU;So;0;L;<circle> 30E6;;;;N;;;;; +32F5;CIRCLED KATAKANA YO;So;0;L;<circle> 30E8;;;;N;;;;; +32F6;CIRCLED KATAKANA RA;So;0;L;<circle> 30E9;;;;N;;;;; +32F7;CIRCLED KATAKANA RI;So;0;L;<circle> 30EA;;;;N;;;;; +32F8;CIRCLED KATAKANA RU;So;0;L;<circle> 30EB;;;;N;;;;; +32F9;CIRCLED KATAKANA RE;So;0;L;<circle> 30EC;;;;N;;;;; +32FA;CIRCLED KATAKANA RO;So;0;L;<circle> 30ED;;;;N;;;;; +32FB;CIRCLED KATAKANA WA;So;0;L;<circle> 30EF;;;;N;;;;; +32FC;CIRCLED KATAKANA WI;So;0;L;<circle> 30F0;;;;N;;;;; +32FD;CIRCLED KATAKANA WE;So;0;L;<circle> 30F1;;;;N;;;;; +32FE;CIRCLED KATAKANA WO;So;0;L;<circle> 30F2;;;;N;;;;; +3300;SQUARE APAATO;So;0;L;<square> 30A2 30D1 30FC 30C8;;;;N;SQUARED APAATO;;;; +3301;SQUARE ARUHUA;So;0;L;<square> 30A2 30EB 30D5 30A1;;;;N;SQUARED ARUHUA;;;; +3302;SQUARE ANPEA;So;0;L;<square> 30A2 30F3 30DA 30A2;;;;N;SQUARED ANPEA;;;; +3303;SQUARE AARU;So;0;L;<square> 30A2 30FC 30EB;;;;N;SQUARED AARU;;;; +3304;SQUARE ININGU;So;0;L;<square> 30A4 30CB 30F3 30B0;;;;N;SQUARED ININGU;;;; +3305;SQUARE INTI;So;0;L;<square> 30A4 30F3 30C1;;;;N;SQUARED INTI;;;; +3306;SQUARE UON;So;0;L;<square> 30A6 30A9 30F3;;;;N;SQUARED UON;;;; +3307;SQUARE ESUKUUDO;So;0;L;<square> 30A8 30B9 30AF 30FC 30C9;;;;N;SQUARED ESUKUUDO;;;; +3308;SQUARE EEKAA;So;0;L;<square> 30A8 30FC 30AB 30FC;;;;N;SQUARED EEKAA;;;; +3309;SQUARE ONSU;So;0;L;<square> 30AA 30F3 30B9;;;;N;SQUARED ONSU;;;; +330A;SQUARE OOMU;So;0;L;<square> 30AA 30FC 30E0;;;;N;SQUARED OOMU;;;; +330B;SQUARE KAIRI;So;0;L;<square> 30AB 30A4 30EA;;;;N;SQUARED KAIRI;;;; +330C;SQUARE KARATTO;So;0;L;<square> 30AB 30E9 30C3 30C8;;;;N;SQUARED KARATTO;;;; +330D;SQUARE KARORII;So;0;L;<square> 30AB 30ED 30EA 30FC;;;;N;SQUARED KARORII;;;; +330E;SQUARE GARON;So;0;L;<square> 30AC 30ED 30F3;;;;N;SQUARED GARON;;;; +330F;SQUARE GANMA;So;0;L;<square> 30AC 30F3 30DE;;;;N;SQUARED GANMA;;;; +3310;SQUARE GIGA;So;0;L;<square> 30AE 30AC;;;;N;SQUARED GIGA;;;; +3311;SQUARE GINII;So;0;L;<square> 30AE 30CB 30FC;;;;N;SQUARED GINII;;;; +3312;SQUARE KYURII;So;0;L;<square> 30AD 30E5 30EA 30FC;;;;N;SQUARED KYURII;;;; +3313;SQUARE GIRUDAA;So;0;L;<square> 30AE 30EB 30C0 30FC;;;;N;SQUARED GIRUDAA;;;; +3314;SQUARE KIRO;So;0;L;<square> 30AD 30ED;;;;N;SQUARED KIRO;;;; +3315;SQUARE KIROGURAMU;So;0;L;<square> 30AD 30ED 30B0 30E9 30E0;;;;N;SQUARED KIROGURAMU;;;; +3316;SQUARE KIROMEETORU;So;0;L;<square> 30AD 30ED 30E1 30FC 30C8 30EB;;;;N;SQUARED KIROMEETORU;;;; +3317;SQUARE KIROWATTO;So;0;L;<square> 30AD 30ED 30EF 30C3 30C8;;;;N;SQUARED KIROWATTO;;;; +3318;SQUARE GURAMU;So;0;L;<square> 30B0 30E9 30E0;;;;N;SQUARED GURAMU;;;; +3319;SQUARE GURAMUTON;So;0;L;<square> 30B0 30E9 30E0 30C8 30F3;;;;N;SQUARED GURAMUTON;;;; +331A;SQUARE KURUZEIRO;So;0;L;<square> 30AF 30EB 30BC 30A4 30ED;;;;N;SQUARED KURUZEIRO;;;; +331B;SQUARE KUROONE;So;0;L;<square> 30AF 30ED 30FC 30CD;;;;N;SQUARED KUROONE;;;; +331C;SQUARE KEESU;So;0;L;<square> 30B1 30FC 30B9;;;;N;SQUARED KEESU;;;; +331D;SQUARE KORUNA;So;0;L;<square> 30B3 30EB 30CA;;;;N;SQUARED KORUNA;;;; +331E;SQUARE KOOPO;So;0;L;<square> 30B3 30FC 30DD;;;;N;SQUARED KOOPO;;;; +331F;SQUARE SAIKURU;So;0;L;<square> 30B5 30A4 30AF 30EB;;;;N;SQUARED SAIKURU;;;; +3320;SQUARE SANTIIMU;So;0;L;<square> 30B5 30F3 30C1 30FC 30E0;;;;N;SQUARED SANTIIMU;;;; +3321;SQUARE SIRINGU;So;0;L;<square> 30B7 30EA 30F3 30B0;;;;N;SQUARED SIRINGU;;;; +3322;SQUARE SENTI;So;0;L;<square> 30BB 30F3 30C1;;;;N;SQUARED SENTI;;;; +3323;SQUARE SENTO;So;0;L;<square> 30BB 30F3 30C8;;;;N;SQUARED SENTO;;;; +3324;SQUARE DAASU;So;0;L;<square> 30C0 30FC 30B9;;;;N;SQUARED DAASU;;;; +3325;SQUARE DESI;So;0;L;<square> 30C7 30B7;;;;N;SQUARED DESI;;;; +3326;SQUARE DORU;So;0;L;<square> 30C9 30EB;;;;N;SQUARED DORU;;;; +3327;SQUARE TON;So;0;L;<square> 30C8 30F3;;;;N;SQUARED TON;;;; +3328;SQUARE NANO;So;0;L;<square> 30CA 30CE;;;;N;SQUARED NANO;;;; +3329;SQUARE NOTTO;So;0;L;<square> 30CE 30C3 30C8;;;;N;SQUARED NOTTO;;;; +332A;SQUARE HAITU;So;0;L;<square> 30CF 30A4 30C4;;;;N;SQUARED HAITU;;;; +332B;SQUARE PAASENTO;So;0;L;<square> 30D1 30FC 30BB 30F3 30C8;;;;N;SQUARED PAASENTO;;;; +332C;SQUARE PAATU;So;0;L;<square> 30D1 30FC 30C4;;;;N;SQUARED PAATU;;;; +332D;SQUARE BAARERU;So;0;L;<square> 30D0 30FC 30EC 30EB;;;;N;SQUARED BAARERU;;;; +332E;SQUARE PIASUTORU;So;0;L;<square> 30D4 30A2 30B9 30C8 30EB;;;;N;SQUARED PIASUTORU;;;; +332F;SQUARE PIKURU;So;0;L;<square> 30D4 30AF 30EB;;;;N;SQUARED PIKURU;;;; +3330;SQUARE PIKO;So;0;L;<square> 30D4 30B3;;;;N;SQUARED PIKO;;;; +3331;SQUARE BIRU;So;0;L;<square> 30D3 30EB;;;;N;SQUARED BIRU;;;; +3332;SQUARE HUARADDO;So;0;L;<square> 30D5 30A1 30E9 30C3 30C9;;;;N;SQUARED HUARADDO;;;; +3333;SQUARE HUIITO;So;0;L;<square> 30D5 30A3 30FC 30C8;;;;N;SQUARED HUIITO;;;; +3334;SQUARE BUSSYERU;So;0;L;<square> 30D6 30C3 30B7 30A7 30EB;;;;N;SQUARED BUSSYERU;;;; +3335;SQUARE HURAN;So;0;L;<square> 30D5 30E9 30F3;;;;N;SQUARED HURAN;;;; +3336;SQUARE HEKUTAARU;So;0;L;<square> 30D8 30AF 30BF 30FC 30EB;;;;N;SQUARED HEKUTAARU;;;; +3337;SQUARE PESO;So;0;L;<square> 30DA 30BD;;;;N;SQUARED PESO;;;; +3338;SQUARE PENIHI;So;0;L;<square> 30DA 30CB 30D2;;;;N;SQUARED PENIHI;;;; +3339;SQUARE HERUTU;So;0;L;<square> 30D8 30EB 30C4;;;;N;SQUARED HERUTU;;;; +333A;SQUARE PENSU;So;0;L;<square> 30DA 30F3 30B9;;;;N;SQUARED PENSU;;;; +333B;SQUARE PEEZI;So;0;L;<square> 30DA 30FC 30B8;;;;N;SQUARED PEEZI;;;; +333C;SQUARE BEETA;So;0;L;<square> 30D9 30FC 30BF;;;;N;SQUARED BEETA;;;; +333D;SQUARE POINTO;So;0;L;<square> 30DD 30A4 30F3 30C8;;;;N;SQUARED POINTO;;;; +333E;SQUARE BORUTO;So;0;L;<square> 30DC 30EB 30C8;;;;N;SQUARED BORUTO;;;; +333F;SQUARE HON;So;0;L;<square> 30DB 30F3;;;;N;SQUARED HON;;;; +3340;SQUARE PONDO;So;0;L;<square> 30DD 30F3 30C9;;;;N;SQUARED PONDO;;;; +3341;SQUARE HOORU;So;0;L;<square> 30DB 30FC 30EB;;;;N;SQUARED HOORU;;;; +3342;SQUARE HOON;So;0;L;<square> 30DB 30FC 30F3;;;;N;SQUARED HOON;;;; +3343;SQUARE MAIKURO;So;0;L;<square> 30DE 30A4 30AF 30ED;;;;N;SQUARED MAIKURO;;;; +3344;SQUARE MAIRU;So;0;L;<square> 30DE 30A4 30EB;;;;N;SQUARED MAIRU;;;; +3345;SQUARE MAHHA;So;0;L;<square> 30DE 30C3 30CF;;;;N;SQUARED MAHHA;;;; +3346;SQUARE MARUKU;So;0;L;<square> 30DE 30EB 30AF;;;;N;SQUARED MARUKU;;;; +3347;SQUARE MANSYON;So;0;L;<square> 30DE 30F3 30B7 30E7 30F3;;;;N;SQUARED MANSYON;;;; +3348;SQUARE MIKURON;So;0;L;<square> 30DF 30AF 30ED 30F3;;;;N;SQUARED MIKURON;;;; +3349;SQUARE MIRI;So;0;L;<square> 30DF 30EA;;;;N;SQUARED MIRI;;;; +334A;SQUARE MIRIBAARU;So;0;L;<square> 30DF 30EA 30D0 30FC 30EB;;;;N;SQUARED MIRIBAARU;;;; +334B;SQUARE MEGA;So;0;L;<square> 30E1 30AC;;;;N;SQUARED MEGA;;;; +334C;SQUARE MEGATON;So;0;L;<square> 30E1 30AC 30C8 30F3;;;;N;SQUARED MEGATON;;;; +334D;SQUARE MEETORU;So;0;L;<square> 30E1 30FC 30C8 30EB;;;;N;SQUARED MEETORU;;;; +334E;SQUARE YAADO;So;0;L;<square> 30E4 30FC 30C9;;;;N;SQUARED YAADO;;;; +334F;SQUARE YAARU;So;0;L;<square> 30E4 30FC 30EB;;;;N;SQUARED YAARU;;;; +3350;SQUARE YUAN;So;0;L;<square> 30E6 30A2 30F3;;;;N;SQUARED YUAN;;;; +3351;SQUARE RITTORU;So;0;L;<square> 30EA 30C3 30C8 30EB;;;;N;SQUARED RITTORU;;;; +3352;SQUARE RIRA;So;0;L;<square> 30EA 30E9;;;;N;SQUARED RIRA;;;; +3353;SQUARE RUPII;So;0;L;<square> 30EB 30D4 30FC;;;;N;SQUARED RUPII;;;; +3354;SQUARE RUUBURU;So;0;L;<square> 30EB 30FC 30D6 30EB;;;;N;SQUARED RUUBURU;;;; +3355;SQUARE REMU;So;0;L;<square> 30EC 30E0;;;;N;SQUARED REMU;;;; +3356;SQUARE RENTOGEN;So;0;L;<square> 30EC 30F3 30C8 30B2 30F3;;;;N;SQUARED RENTOGEN;;;; +3357;SQUARE WATTO;So;0;L;<square> 30EF 30C3 30C8;;;;N;SQUARED WATTO;;;; +3358;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ZERO;So;0;L;<compat> 0030 70B9;;;;N;;;;; +3359;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ONE;So;0;L;<compat> 0031 70B9;;;;N;;;;; +335A;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWO;So;0;L;<compat> 0032 70B9;;;;N;;;;; +335B;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THREE;So;0;L;<compat> 0033 70B9;;;;N;;;;; +335C;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOUR;So;0;L;<compat> 0034 70B9;;;;N;;;;; +335D;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIVE;So;0;L;<compat> 0035 70B9;;;;N;;;;; +335E;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIX;So;0;L;<compat> 0036 70B9;;;;N;;;;; +335F;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVEN;So;0;L;<compat> 0037 70B9;;;;N;;;;; +3360;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHT;So;0;L;<compat> 0038 70B9;;;;N;;;;; +3361;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINE;So;0;L;<compat> 0039 70B9;;;;N;;;;; +3362;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TEN;So;0;L;<compat> 0031 0030 70B9;;;;N;;;;; +3363;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ELEVEN;So;0;L;<compat> 0031 0031 70B9;;;;N;;;;; +3364;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWELVE;So;0;L;<compat> 0031 0032 70B9;;;;N;;;;; +3365;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THIRTEEN;So;0;L;<compat> 0031 0033 70B9;;;;N;;;;; +3366;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOURTEEN;So;0;L;<compat> 0031 0034 70B9;;;;N;;;;; +3367;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIFTEEN;So;0;L;<compat> 0031 0035 70B9;;;;N;;;;; +3368;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIXTEEN;So;0;L;<compat> 0031 0036 70B9;;;;N;;;;; +3369;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVENTEEN;So;0;L;<compat> 0031 0037 70B9;;;;N;;;;; +336A;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHTEEN;So;0;L;<compat> 0031 0038 70B9;;;;N;;;;; +336B;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINETEEN;So;0;L;<compat> 0031 0039 70B9;;;;N;;;;; +336C;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY;So;0;L;<compat> 0032 0030 70B9;;;;N;;;;; +336D;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-ONE;So;0;L;<compat> 0032 0031 70B9;;;;N;;;;; +336E;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-TWO;So;0;L;<compat> 0032 0032 70B9;;;;N;;;;; +336F;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-THREE;So;0;L;<compat> 0032 0033 70B9;;;;N;;;;; +3370;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-FOUR;So;0;L;<compat> 0032 0034 70B9;;;;N;;;;; +3371;SQUARE HPA;So;0;L;<square> 0068 0050 0061;;;;N;;;;; +3372;SQUARE DA;So;0;L;<square> 0064 0061;;;;N;;;;; +3373;SQUARE AU;So;0;L;<square> 0041 0055;;;;N;;;;; +3374;SQUARE BAR;So;0;L;<square> 0062 0061 0072;;;;N;;;;; +3375;SQUARE OV;So;0;L;<square> 006F 0056;;;;N;;;;; +3376;SQUARE PC;So;0;L;<square> 0070 0063;;;;N;;;;; +3377;SQUARE DM;So;0;ON;<square> 0064 006D;;;;N;;;;; +3378;SQUARE DM SQUARED;So;0;ON;<square> 0064 006D 00B2;;;;N;;;;; +3379;SQUARE DM CUBED;So;0;ON;<square> 0064 006D 00B3;;;;N;;;;; +337A;SQUARE IU;So;0;ON;<square> 0049 0055;;;;N;;;;; +337B;SQUARE ERA NAME HEISEI;So;0;L;<square> 5E73 6210;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME HEISEI;;;; +337C;SQUARE ERA NAME SYOUWA;So;0;L;<square> 662D 548C;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME SYOUWA;;;; +337D;SQUARE ERA NAME TAISYOU;So;0;L;<square> 5927 6B63;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME TAISYOU;;;; +337E;SQUARE ERA NAME MEIZI;So;0;L;<square> 660E 6CBB;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME MEIZI;;;; +337F;SQUARE CORPORATION;So;0;L;<square> 682A 5F0F 4F1A 793E;;;;N;SQUARED FOUR IDEOGRAPHS CORPORATION;;;; +3380;SQUARE PA AMPS;So;0;L;<square> 0070 0041;;;;N;SQUARED PA AMPS;;;; +3381;SQUARE NA;So;0;L;<square> 006E 0041;;;;N;SQUARED NA;;;; +3382;SQUARE MU A;So;0;L;<square> 03BC 0041;;;;N;SQUARED MU A;;;; +3383;SQUARE MA;So;0;L;<square> 006D 0041;;;;N;SQUARED MA;;;; +3384;SQUARE KA;So;0;L;<square> 006B 0041;;;;N;SQUARED KA;;;; +3385;SQUARE KB;So;0;L;<square> 004B 0042;;;;N;SQUARED KB;;;; +3386;SQUARE MB;So;0;L;<square> 004D 0042;;;;N;SQUARED MB;;;; +3387;SQUARE GB;So;0;L;<square> 0047 0042;;;;N;SQUARED GB;;;; +3388;SQUARE CAL;So;0;L;<square> 0063 0061 006C;;;;N;SQUARED CAL;;;; +3389;SQUARE KCAL;So;0;L;<square> 006B 0063 0061 006C;;;;N;SQUARED KCAL;;;; +338A;SQUARE PF;So;0;L;<square> 0070 0046;;;;N;SQUARED PF;;;; +338B;SQUARE NF;So;0;L;<square> 006E 0046;;;;N;SQUARED NF;;;; +338C;SQUARE MU F;So;0;L;<square> 03BC 0046;;;;N;SQUARED MU F;;;; +338D;SQUARE MU G;So;0;L;<square> 03BC 0067;;;;N;SQUARED MU G;;;; +338E;SQUARE MG;So;0;L;<square> 006D 0067;;;;N;SQUARED MG;;;; +338F;SQUARE KG;So;0;L;<square> 006B 0067;;;;N;SQUARED KG;;;; +3390;SQUARE HZ;So;0;L;<square> 0048 007A;;;;N;SQUARED HZ;;;; +3391;SQUARE KHZ;So;0;L;<square> 006B 0048 007A;;;;N;SQUARED KHZ;;;; +3392;SQUARE MHZ;So;0;L;<square> 004D 0048 007A;;;;N;SQUARED MHZ;;;; +3393;SQUARE GHZ;So;0;L;<square> 0047 0048 007A;;;;N;SQUARED GHZ;;;; +3394;SQUARE THZ;So;0;L;<square> 0054 0048 007A;;;;N;SQUARED THZ;;;; +3395;SQUARE MU L;So;0;L;<square> 03BC 2113;;;;N;SQUARED MU L;;;; +3396;SQUARE ML;So;0;L;<square> 006D 2113;;;;N;SQUARED ML;;;; +3397;SQUARE DL;So;0;L;<square> 0064 2113;;;;N;SQUARED DL;;;; +3398;SQUARE KL;So;0;L;<square> 006B 2113;;;;N;SQUARED KL;;;; +3399;SQUARE FM;So;0;L;<square> 0066 006D;;;;N;SQUARED FM;;;; +339A;SQUARE NM;So;0;L;<square> 006E 006D;;;;N;SQUARED NM;;;; +339B;SQUARE MU M;So;0;L;<square> 03BC 006D;;;;N;SQUARED MU M;;;; +339C;SQUARE MM;So;0;L;<square> 006D 006D;;;;N;SQUARED MM;;;; +339D;SQUARE CM;So;0;L;<square> 0063 006D;;;;N;SQUARED CM;;;; +339E;SQUARE KM;So;0;L;<square> 006B 006D;;;;N;SQUARED KM;;;; +339F;SQUARE MM SQUARED;So;0;L;<square> 006D 006D 00B2;;;;N;SQUARED MM SQUARED;;;; +33A0;SQUARE CM SQUARED;So;0;L;<square> 0063 006D 00B2;;;;N;SQUARED CM SQUARED;;;; +33A1;SQUARE M SQUARED;So;0;L;<square> 006D 00B2;;;;N;SQUARED M SQUARED;;;; +33A2;SQUARE KM SQUARED;So;0;L;<square> 006B 006D 00B2;;;;N;SQUARED KM SQUARED;;;; +33A3;SQUARE MM CUBED;So;0;L;<square> 006D 006D 00B3;;;;N;SQUARED MM CUBED;;;; +33A4;SQUARE CM CUBED;So;0;L;<square> 0063 006D 00B3;;;;N;SQUARED CM CUBED;;;; +33A5;SQUARE M CUBED;So;0;L;<square> 006D 00B3;;;;N;SQUARED M CUBED;;;; +33A6;SQUARE KM CUBED;So;0;L;<square> 006B 006D 00B3;;;;N;SQUARED KM CUBED;;;; +33A7;SQUARE M OVER S;So;0;L;<square> 006D 2215 0073;;;;N;SQUARED M OVER S;;;; +33A8;SQUARE M OVER S SQUARED;So;0;L;<square> 006D 2215 0073 00B2;;;;N;SQUARED M OVER S SQUARED;;;; +33A9;SQUARE PA;So;0;L;<square> 0050 0061;;;;N;SQUARED PA;;;; +33AA;SQUARE KPA;So;0;L;<square> 006B 0050 0061;;;;N;SQUARED KPA;;;; +33AB;SQUARE MPA;So;0;L;<square> 004D 0050 0061;;;;N;SQUARED MPA;;;; +33AC;SQUARE GPA;So;0;L;<square> 0047 0050 0061;;;;N;SQUARED GPA;;;; +33AD;SQUARE RAD;So;0;L;<square> 0072 0061 0064;;;;N;SQUARED RAD;;;; +33AE;SQUARE RAD OVER S;So;0;L;<square> 0072 0061 0064 2215 0073;;;;N;SQUARED RAD OVER S;;;; +33AF;SQUARE RAD OVER S SQUARED;So;0;L;<square> 0072 0061 0064 2215 0073 00B2;;;;N;SQUARED RAD OVER S SQUARED;;;; +33B0;SQUARE PS;So;0;L;<square> 0070 0073;;;;N;SQUARED PS;;;; +33B1;SQUARE NS;So;0;L;<square> 006E 0073;;;;N;SQUARED NS;;;; +33B2;SQUARE MU S;So;0;L;<square> 03BC 0073;;;;N;SQUARED MU S;;;; +33B3;SQUARE MS;So;0;L;<square> 006D 0073;;;;N;SQUARED MS;;;; +33B4;SQUARE PV;So;0;L;<square> 0070 0056;;;;N;SQUARED PV;;;; +33B5;SQUARE NV;So;0;L;<square> 006E 0056;;;;N;SQUARED NV;;;; +33B6;SQUARE MU V;So;0;L;<square> 03BC 0056;;;;N;SQUARED MU V;;;; +33B7;SQUARE MV;So;0;L;<square> 006D 0056;;;;N;SQUARED MV;;;; +33B8;SQUARE KV;So;0;L;<square> 006B 0056;;;;N;SQUARED KV;;;; +33B9;SQUARE MV MEGA;So;0;L;<square> 004D 0056;;;;N;SQUARED MV MEGA;;;; +33BA;SQUARE PW;So;0;L;<square> 0070 0057;;;;N;SQUARED PW;;;; +33BB;SQUARE NW;So;0;L;<square> 006E 0057;;;;N;SQUARED NW;;;; +33BC;SQUARE MU W;So;0;L;<square> 03BC 0057;;;;N;SQUARED MU W;;;; +33BD;SQUARE MW;So;0;L;<square> 006D 0057;;;;N;SQUARED MW;;;; +33BE;SQUARE KW;So;0;L;<square> 006B 0057;;;;N;SQUARED KW;;;; +33BF;SQUARE MW MEGA;So;0;L;<square> 004D 0057;;;;N;SQUARED MW MEGA;;;; +33C0;SQUARE K OHM;So;0;L;<square> 006B 03A9;;;;N;SQUARED K OHM;;;; +33C1;SQUARE M OHM;So;0;L;<square> 004D 03A9;;;;N;SQUARED M OHM;;;; +33C2;SQUARE AM;So;0;L;<square> 0061 002E 006D 002E;;;;N;SQUARED AM;;;; +33C3;SQUARE BQ;So;0;L;<square> 0042 0071;;;;N;SQUARED BQ;;;; +33C4;SQUARE CC;So;0;L;<square> 0063 0063;;;;N;SQUARED CC;;;; +33C5;SQUARE CD;So;0;L;<square> 0063 0064;;;;N;SQUARED CD;;;; +33C6;SQUARE C OVER KG;So;0;L;<square> 0043 2215 006B 0067;;;;N;SQUARED C OVER KG;;;; +33C7;SQUARE CO;So;0;L;<square> 0043 006F 002E;;;;N;SQUARED CO;;;; +33C8;SQUARE DB;So;0;L;<square> 0064 0042;;;;N;SQUARED DB;;;; +33C9;SQUARE GY;So;0;L;<square> 0047 0079;;;;N;SQUARED GY;;;; +33CA;SQUARE HA;So;0;L;<square> 0068 0061;;;;N;SQUARED HA;;;; +33CB;SQUARE HP;So;0;L;<square> 0048 0050;;;;N;SQUARED HP;;;; +33CC;SQUARE IN;So;0;L;<square> 0069 006E;;;;N;SQUARED IN;;;; +33CD;SQUARE KK;So;0;L;<square> 004B 004B;;;;N;SQUARED KK;;;; +33CE;SQUARE KM CAPITAL;So;0;L;<square> 004B 004D;;;;N;SQUARED KM CAPITAL;;;; +33CF;SQUARE KT;So;0;L;<square> 006B 0074;;;;N;SQUARED KT;;;; +33D0;SQUARE LM;So;0;L;<square> 006C 006D;;;;N;SQUARED LM;;;; +33D1;SQUARE LN;So;0;L;<square> 006C 006E;;;;N;SQUARED LN;;;; +33D2;SQUARE LOG;So;0;L;<square> 006C 006F 0067;;;;N;SQUARED LOG;;;; +33D3;SQUARE LX;So;0;L;<square> 006C 0078;;;;N;SQUARED LX;;;; +33D4;SQUARE MB SMALL;So;0;L;<square> 006D 0062;;;;N;SQUARED MB SMALL;;;; +33D5;SQUARE MIL;So;0;L;<square> 006D 0069 006C;;;;N;SQUARED MIL;;;; +33D6;SQUARE MOL;So;0;L;<square> 006D 006F 006C;;;;N;SQUARED MOL;;;; +33D7;SQUARE PH;So;0;L;<square> 0050 0048;;;;N;SQUARED PH;;;; +33D8;SQUARE PM;So;0;L;<square> 0070 002E 006D 002E;;;;N;SQUARED PM;;;; +33D9;SQUARE PPM;So;0;L;<square> 0050 0050 004D;;;;N;SQUARED PPM;;;; +33DA;SQUARE PR;So;0;L;<square> 0050 0052;;;;N;SQUARED PR;;;; +33DB;SQUARE SR;So;0;L;<square> 0073 0072;;;;N;SQUARED SR;;;; +33DC;SQUARE SV;So;0;L;<square> 0053 0076;;;;N;SQUARED SV;;;; +33DD;SQUARE WB;So;0;L;<square> 0057 0062;;;;N;SQUARED WB;;;; +33DE;SQUARE V OVER M;So;0;ON;<square> 0056 2215 006D;;;;N;;;;; +33DF;SQUARE A OVER M;So;0;ON;<square> 0041 2215 006D;;;;N;;;;; +33E0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ONE;So;0;L;<compat> 0031 65E5;;;;N;;;;; +33E1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWO;So;0;L;<compat> 0032 65E5;;;;N;;;;; +33E2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THREE;So;0;L;<compat> 0033 65E5;;;;N;;;;; +33E3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOUR;So;0;L;<compat> 0034 65E5;;;;N;;;;; +33E4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIVE;So;0;L;<compat> 0035 65E5;;;;N;;;;; +33E5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIX;So;0;L;<compat> 0036 65E5;;;;N;;;;; +33E6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVEN;So;0;L;<compat> 0037 65E5;;;;N;;;;; +33E7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHT;So;0;L;<compat> 0038 65E5;;;;N;;;;; +33E8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINE;So;0;L;<compat> 0039 65E5;;;;N;;;;; +33E9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TEN;So;0;L;<compat> 0031 0030 65E5;;;;N;;;;; +33EA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ELEVEN;So;0;L;<compat> 0031 0031 65E5;;;;N;;;;; +33EB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWELVE;So;0;L;<compat> 0031 0032 65E5;;;;N;;;;; +33EC;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTEEN;So;0;L;<compat> 0031 0033 65E5;;;;N;;;;; +33ED;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOURTEEN;So;0;L;<compat> 0031 0034 65E5;;;;N;;;;; +33EE;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIFTEEN;So;0;L;<compat> 0031 0035 65E5;;;;N;;;;; +33EF;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIXTEEN;So;0;L;<compat> 0031 0036 65E5;;;;N;;;;; +33F0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVENTEEN;So;0;L;<compat> 0031 0037 65E5;;;;N;;;;; +33F1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHTEEN;So;0;L;<compat> 0031 0038 65E5;;;;N;;;;; +33F2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINETEEN;So;0;L;<compat> 0031 0039 65E5;;;;N;;;;; +33F3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY;So;0;L;<compat> 0032 0030 65E5;;;;N;;;;; +33F4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-ONE;So;0;L;<compat> 0032 0031 65E5;;;;N;;;;; +33F5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-TWO;So;0;L;<compat> 0032 0032 65E5;;;;N;;;;; +33F6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-THREE;So;0;L;<compat> 0032 0033 65E5;;;;N;;;;; +33F7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FOUR;So;0;L;<compat> 0032 0034 65E5;;;;N;;;;; +33F8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FIVE;So;0;L;<compat> 0032 0035 65E5;;;;N;;;;; +33F9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SIX;So;0;L;<compat> 0032 0036 65E5;;;;N;;;;; +33FA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SEVEN;So;0;L;<compat> 0032 0037 65E5;;;;N;;;;; +33FB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-EIGHT;So;0;L;<compat> 0032 0038 65E5;;;;N;;;;; +33FC;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-NINE;So;0;L;<compat> 0032 0039 65E5;;;;N;;;;; +33FD;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY;So;0;L;<compat> 0033 0030 65E5;;;;N;;;;; +33FE;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY-ONE;So;0;L;<compat> 0033 0031 65E5;;;;N;;;;; +33FF;SQUARE GAL;So;0;ON;<square> 0067 0061 006C;;;;N;;;;; +3400;<CJK Ideograph Extension A, First>;Lo;0;L;;;;;N;;;;; +4DB5;<CJK Ideograph Extension A, Last>;Lo;0;L;;;;;N;;;;; +4DC0;HEXAGRAM FOR THE CREATIVE HEAVEN;So;0;ON;;;;;N;;;;; +4DC1;HEXAGRAM FOR THE RECEPTIVE EARTH;So;0;ON;;;;;N;;;;; +4DC2;HEXAGRAM FOR DIFFICULTY AT THE BEGINNING;So;0;ON;;;;;N;;;;; +4DC3;HEXAGRAM FOR YOUTHFUL FOLLY;So;0;ON;;;;;N;;;;; +4DC4;HEXAGRAM FOR WAITING;So;0;ON;;;;;N;;;;; +4DC5;HEXAGRAM FOR CONFLICT;So;0;ON;;;;;N;;;;; +4DC6;HEXAGRAM FOR THE ARMY;So;0;ON;;;;;N;;;;; +4DC7;HEXAGRAM FOR HOLDING TOGETHER;So;0;ON;;;;;N;;;;; +4DC8;HEXAGRAM FOR SMALL TAMING;So;0;ON;;;;;N;;;;; +4DC9;HEXAGRAM FOR TREADING;So;0;ON;;;;;N;;;;; +4DCA;HEXAGRAM FOR PEACE;So;0;ON;;;;;N;;;;; +4DCB;HEXAGRAM FOR STANDSTILL;So;0;ON;;;;;N;;;;; +4DCC;HEXAGRAM FOR FELLOWSHIP;So;0;ON;;;;;N;;;;; +4DCD;HEXAGRAM FOR GREAT POSSESSION;So;0;ON;;;;;N;;;;; +4DCE;HEXAGRAM FOR MODESTY;So;0;ON;;;;;N;;;;; +4DCF;HEXAGRAM FOR ENTHUSIASM;So;0;ON;;;;;N;;;;; +4DD0;HEXAGRAM FOR FOLLOWING;So;0;ON;;;;;N;;;;; +4DD1;HEXAGRAM FOR WORK ON THE DECAYED;So;0;ON;;;;;N;;;;; +4DD2;HEXAGRAM FOR APPROACH;So;0;ON;;;;;N;;;;; +4DD3;HEXAGRAM FOR CONTEMPLATION;So;0;ON;;;;;N;;;;; +4DD4;HEXAGRAM FOR BITING THROUGH;So;0;ON;;;;;N;;;;; +4DD5;HEXAGRAM FOR GRACE;So;0;ON;;;;;N;;;;; +4DD6;HEXAGRAM FOR SPLITTING APART;So;0;ON;;;;;N;;;;; +4DD7;HEXAGRAM FOR RETURN;So;0;ON;;;;;N;;;;; +4DD8;HEXAGRAM FOR INNOCENCE;So;0;ON;;;;;N;;;;; +4DD9;HEXAGRAM FOR GREAT TAMING;So;0;ON;;;;;N;;;;; +4DDA;HEXAGRAM FOR MOUTH CORNERS;So;0;ON;;;;;N;;;;; +4DDB;HEXAGRAM FOR GREAT PREPONDERANCE;So;0;ON;;;;;N;;;;; +4DDC;HEXAGRAM FOR THE ABYSMAL WATER;So;0;ON;;;;;N;;;;; +4DDD;HEXAGRAM FOR THE CLINGING FIRE;So;0;ON;;;;;N;;;;; +4DDE;HEXAGRAM FOR INFLUENCE;So;0;ON;;;;;N;;;;; +4DDF;HEXAGRAM FOR DURATION;So;0;ON;;;;;N;;;;; +4DE0;HEXAGRAM FOR RETREAT;So;0;ON;;;;;N;;;;; +4DE1;HEXAGRAM FOR GREAT POWER;So;0;ON;;;;;N;;;;; +4DE2;HEXAGRAM FOR PROGRESS;So;0;ON;;;;;N;;;;; +4DE3;HEXAGRAM FOR DARKENING OF THE LIGHT;So;0;ON;;;;;N;;;;; +4DE4;HEXAGRAM FOR THE FAMILY;So;0;ON;;;;;N;;;;; +4DE5;HEXAGRAM FOR OPPOSITION;So;0;ON;;;;;N;;;;; +4DE6;HEXAGRAM FOR OBSTRUCTION;So;0;ON;;;;;N;;;;; +4DE7;HEXAGRAM FOR DELIVERANCE;So;0;ON;;;;;N;;;;; +4DE8;HEXAGRAM FOR DECREASE;So;0;ON;;;;;N;;;;; +4DE9;HEXAGRAM FOR INCREASE;So;0;ON;;;;;N;;;;; +4DEA;HEXAGRAM FOR BREAKTHROUGH;So;0;ON;;;;;N;;;;; +4DEB;HEXAGRAM FOR COMING TO MEET;So;0;ON;;;;;N;;;;; +4DEC;HEXAGRAM FOR GATHERING TOGETHER;So;0;ON;;;;;N;;;;; +4DED;HEXAGRAM FOR PUSHING UPWARD;So;0;ON;;;;;N;;;;; +4DEE;HEXAGRAM FOR OPPRESSION;So;0;ON;;;;;N;;;;; +4DEF;HEXAGRAM FOR THE WELL;So;0;ON;;;;;N;;;;; +4DF0;HEXAGRAM FOR REVOLUTION;So;0;ON;;;;;N;;;;; +4DF1;HEXAGRAM FOR THE CAULDRON;So;0;ON;;;;;N;;;;; +4DF2;HEXAGRAM FOR THE AROUSING THUNDER;So;0;ON;;;;;N;;;;; +4DF3;HEXAGRAM FOR THE KEEPING STILL MOUNTAIN;So;0;ON;;;;;N;;;;; +4DF4;HEXAGRAM FOR DEVELOPMENT;So;0;ON;;;;;N;;;;; +4DF5;HEXAGRAM FOR THE MARRYING MAIDEN;So;0;ON;;;;;N;;;;; +4DF6;HEXAGRAM FOR ABUNDANCE;So;0;ON;;;;;N;;;;; +4DF7;HEXAGRAM FOR THE WANDERER;So;0;ON;;;;;N;;;;; +4DF8;HEXAGRAM FOR THE GENTLE WIND;So;0;ON;;;;;N;;;;; +4DF9;HEXAGRAM FOR THE JOYOUS LAKE;So;0;ON;;;;;N;;;;; +4DFA;HEXAGRAM FOR DISPERSION;So;0;ON;;;;;N;;;;; +4DFB;HEXAGRAM FOR LIMITATION;So;0;ON;;;;;N;;;;; +4DFC;HEXAGRAM FOR INNER TRUTH;So;0;ON;;;;;N;;;;; +4DFD;HEXAGRAM FOR SMALL PREPONDERANCE;So;0;ON;;;;;N;;;;; +4DFE;HEXAGRAM FOR AFTER COMPLETION;So;0;ON;;;;;N;;;;; +4DFF;HEXAGRAM FOR BEFORE COMPLETION;So;0;ON;;;;;N;;;;; +4E00;<CJK Ideograph, First>;Lo;0;L;;;;;N;;;;; +9FD5;<CJK Ideograph, Last>;Lo;0;L;;;;;N;;;;; +A000;YI SYLLABLE IT;Lo;0;L;;;;;N;;;;; +A001;YI SYLLABLE IX;Lo;0;L;;;;;N;;;;; +A002;YI SYLLABLE I;Lo;0;L;;;;;N;;;;; +A003;YI SYLLABLE IP;Lo;0;L;;;;;N;;;;; +A004;YI SYLLABLE IET;Lo;0;L;;;;;N;;;;; +A005;YI SYLLABLE IEX;Lo;0;L;;;;;N;;;;; +A006;YI SYLLABLE IE;Lo;0;L;;;;;N;;;;; +A007;YI SYLLABLE IEP;Lo;0;L;;;;;N;;;;; +A008;YI SYLLABLE AT;Lo;0;L;;;;;N;;;;; +A009;YI SYLLABLE AX;Lo;0;L;;;;;N;;;;; +A00A;YI SYLLABLE A;Lo;0;L;;;;;N;;;;; +A00B;YI SYLLABLE AP;Lo;0;L;;;;;N;;;;; +A00C;YI SYLLABLE UOX;Lo;0;L;;;;;N;;;;; +A00D;YI SYLLABLE UO;Lo;0;L;;;;;N;;;;; +A00E;YI SYLLABLE UOP;Lo;0;L;;;;;N;;;;; +A00F;YI SYLLABLE OT;Lo;0;L;;;;;N;;;;; +A010;YI SYLLABLE OX;Lo;0;L;;;;;N;;;;; +A011;YI SYLLABLE O;Lo;0;L;;;;;N;;;;; +A012;YI SYLLABLE OP;Lo;0;L;;;;;N;;;;; +A013;YI SYLLABLE EX;Lo;0;L;;;;;N;;;;; +A014;YI SYLLABLE E;Lo;0;L;;;;;N;;;;; +A015;YI SYLLABLE WU;Lm;0;L;;;;;N;;;;; +A016;YI SYLLABLE BIT;Lo;0;L;;;;;N;;;;; +A017;YI SYLLABLE BIX;Lo;0;L;;;;;N;;;;; +A018;YI SYLLABLE BI;Lo;0;L;;;;;N;;;;; +A019;YI SYLLABLE BIP;Lo;0;L;;;;;N;;;;; +A01A;YI SYLLABLE BIET;Lo;0;L;;;;;N;;;;; +A01B;YI SYLLABLE BIEX;Lo;0;L;;;;;N;;;;; +A01C;YI SYLLABLE BIE;Lo;0;L;;;;;N;;;;; +A01D;YI SYLLABLE BIEP;Lo;0;L;;;;;N;;;;; +A01E;YI SYLLABLE BAT;Lo;0;L;;;;;N;;;;; +A01F;YI SYLLABLE BAX;Lo;0;L;;;;;N;;;;; +A020;YI SYLLABLE BA;Lo;0;L;;;;;N;;;;; +A021;YI SYLLABLE BAP;Lo;0;L;;;;;N;;;;; +A022;YI SYLLABLE BUOX;Lo;0;L;;;;;N;;;;; +A023;YI SYLLABLE BUO;Lo;0;L;;;;;N;;;;; +A024;YI SYLLABLE BUOP;Lo;0;L;;;;;N;;;;; +A025;YI SYLLABLE BOT;Lo;0;L;;;;;N;;;;; +A026;YI SYLLABLE BOX;Lo;0;L;;;;;N;;;;; +A027;YI SYLLABLE BO;Lo;0;L;;;;;N;;;;; +A028;YI SYLLABLE BOP;Lo;0;L;;;;;N;;;;; +A029;YI SYLLABLE BEX;Lo;0;L;;;;;N;;;;; +A02A;YI SYLLABLE BE;Lo;0;L;;;;;N;;;;; +A02B;YI SYLLABLE BEP;Lo;0;L;;;;;N;;;;; +A02C;YI SYLLABLE BUT;Lo;0;L;;;;;N;;;;; +A02D;YI SYLLABLE BUX;Lo;0;L;;;;;N;;;;; +A02E;YI SYLLABLE BU;Lo;0;L;;;;;N;;;;; +A02F;YI SYLLABLE BUP;Lo;0;L;;;;;N;;;;; +A030;YI SYLLABLE BURX;Lo;0;L;;;;;N;;;;; +A031;YI SYLLABLE BUR;Lo;0;L;;;;;N;;;;; +A032;YI SYLLABLE BYT;Lo;0;L;;;;;N;;;;; +A033;YI SYLLABLE BYX;Lo;0;L;;;;;N;;;;; +A034;YI SYLLABLE BY;Lo;0;L;;;;;N;;;;; +A035;YI SYLLABLE BYP;Lo;0;L;;;;;N;;;;; +A036;YI SYLLABLE BYRX;Lo;0;L;;;;;N;;;;; +A037;YI SYLLABLE BYR;Lo;0;L;;;;;N;;;;; +A038;YI SYLLABLE PIT;Lo;0;L;;;;;N;;;;; +A039;YI SYLLABLE PIX;Lo;0;L;;;;;N;;;;; +A03A;YI SYLLABLE PI;Lo;0;L;;;;;N;;;;; +A03B;YI SYLLABLE PIP;Lo;0;L;;;;;N;;;;; +A03C;YI SYLLABLE PIEX;Lo;0;L;;;;;N;;;;; +A03D;YI SYLLABLE PIE;Lo;0;L;;;;;N;;;;; +A03E;YI SYLLABLE PIEP;Lo;0;L;;;;;N;;;;; +A03F;YI SYLLABLE PAT;Lo;0;L;;;;;N;;;;; +A040;YI SYLLABLE PAX;Lo;0;L;;;;;N;;;;; +A041;YI SYLLABLE PA;Lo;0;L;;;;;N;;;;; +A042;YI SYLLABLE PAP;Lo;0;L;;;;;N;;;;; +A043;YI SYLLABLE PUOX;Lo;0;L;;;;;N;;;;; +A044;YI SYLLABLE PUO;Lo;0;L;;;;;N;;;;; +A045;YI SYLLABLE PUOP;Lo;0;L;;;;;N;;;;; +A046;YI SYLLABLE POT;Lo;0;L;;;;;N;;;;; +A047;YI SYLLABLE POX;Lo;0;L;;;;;N;;;;; +A048;YI SYLLABLE PO;Lo;0;L;;;;;N;;;;; +A049;YI SYLLABLE POP;Lo;0;L;;;;;N;;;;; +A04A;YI SYLLABLE PUT;Lo;0;L;;;;;N;;;;; +A04B;YI SYLLABLE PUX;Lo;0;L;;;;;N;;;;; +A04C;YI SYLLABLE PU;Lo;0;L;;;;;N;;;;; +A04D;YI SYLLABLE PUP;Lo;0;L;;;;;N;;;;; +A04E;YI SYLLABLE PURX;Lo;0;L;;;;;N;;;;; +A04F;YI SYLLABLE PUR;Lo;0;L;;;;;N;;;;; +A050;YI SYLLABLE PYT;Lo;0;L;;;;;N;;;;; +A051;YI SYLLABLE PYX;Lo;0;L;;;;;N;;;;; +A052;YI SYLLABLE PY;Lo;0;L;;;;;N;;;;; +A053;YI SYLLABLE PYP;Lo;0;L;;;;;N;;;;; +A054;YI SYLLABLE PYRX;Lo;0;L;;;;;N;;;;; +A055;YI SYLLABLE PYR;Lo;0;L;;;;;N;;;;; +A056;YI SYLLABLE BBIT;Lo;0;L;;;;;N;;;;; +A057;YI SYLLABLE BBIX;Lo;0;L;;;;;N;;;;; +A058;YI SYLLABLE BBI;Lo;0;L;;;;;N;;;;; +A059;YI SYLLABLE BBIP;Lo;0;L;;;;;N;;;;; +A05A;YI SYLLABLE BBIET;Lo;0;L;;;;;N;;;;; +A05B;YI SYLLABLE BBIEX;Lo;0;L;;;;;N;;;;; +A05C;YI SYLLABLE BBIE;Lo;0;L;;;;;N;;;;; +A05D;YI SYLLABLE BBIEP;Lo;0;L;;;;;N;;;;; +A05E;YI SYLLABLE BBAT;Lo;0;L;;;;;N;;;;; +A05F;YI SYLLABLE BBAX;Lo;0;L;;;;;N;;;;; +A060;YI SYLLABLE BBA;Lo;0;L;;;;;N;;;;; +A061;YI SYLLABLE BBAP;Lo;0;L;;;;;N;;;;; +A062;YI SYLLABLE BBUOX;Lo;0;L;;;;;N;;;;; +A063;YI SYLLABLE BBUO;Lo;0;L;;;;;N;;;;; +A064;YI SYLLABLE BBUOP;Lo;0;L;;;;;N;;;;; +A065;YI SYLLABLE BBOT;Lo;0;L;;;;;N;;;;; +A066;YI SYLLABLE BBOX;Lo;0;L;;;;;N;;;;; +A067;YI SYLLABLE BBO;Lo;0;L;;;;;N;;;;; +A068;YI SYLLABLE BBOP;Lo;0;L;;;;;N;;;;; +A069;YI SYLLABLE BBEX;Lo;0;L;;;;;N;;;;; +A06A;YI SYLLABLE BBE;Lo;0;L;;;;;N;;;;; +A06B;YI SYLLABLE BBEP;Lo;0;L;;;;;N;;;;; +A06C;YI SYLLABLE BBUT;Lo;0;L;;;;;N;;;;; +A06D;YI SYLLABLE BBUX;Lo;0;L;;;;;N;;;;; +A06E;YI SYLLABLE BBU;Lo;0;L;;;;;N;;;;; +A06F;YI SYLLABLE BBUP;Lo;0;L;;;;;N;;;;; +A070;YI SYLLABLE BBURX;Lo;0;L;;;;;N;;;;; +A071;YI SYLLABLE BBUR;Lo;0;L;;;;;N;;;;; +A072;YI SYLLABLE BBYT;Lo;0;L;;;;;N;;;;; +A073;YI SYLLABLE BBYX;Lo;0;L;;;;;N;;;;; +A074;YI SYLLABLE BBY;Lo;0;L;;;;;N;;;;; +A075;YI SYLLABLE BBYP;Lo;0;L;;;;;N;;;;; +A076;YI SYLLABLE NBIT;Lo;0;L;;;;;N;;;;; +A077;YI SYLLABLE NBIX;Lo;0;L;;;;;N;;;;; +A078;YI SYLLABLE NBI;Lo;0;L;;;;;N;;;;; +A079;YI SYLLABLE NBIP;Lo;0;L;;;;;N;;;;; +A07A;YI SYLLABLE NBIEX;Lo;0;L;;;;;N;;;;; +A07B;YI SYLLABLE NBIE;Lo;0;L;;;;;N;;;;; +A07C;YI SYLLABLE NBIEP;Lo;0;L;;;;;N;;;;; +A07D;YI SYLLABLE NBAT;Lo;0;L;;;;;N;;;;; +A07E;YI SYLLABLE NBAX;Lo;0;L;;;;;N;;;;; +A07F;YI SYLLABLE NBA;Lo;0;L;;;;;N;;;;; +A080;YI SYLLABLE NBAP;Lo;0;L;;;;;N;;;;; +A081;YI SYLLABLE NBOT;Lo;0;L;;;;;N;;;;; +A082;YI SYLLABLE NBOX;Lo;0;L;;;;;N;;;;; +A083;YI SYLLABLE NBO;Lo;0;L;;;;;N;;;;; +A084;YI SYLLABLE NBOP;Lo;0;L;;;;;N;;;;; +A085;YI SYLLABLE NBUT;Lo;0;L;;;;;N;;;;; +A086;YI SYLLABLE NBUX;Lo;0;L;;;;;N;;;;; +A087;YI SYLLABLE NBU;Lo;0;L;;;;;N;;;;; +A088;YI SYLLABLE NBUP;Lo;0;L;;;;;N;;;;; +A089;YI SYLLABLE NBURX;Lo;0;L;;;;;N;;;;; +A08A;YI SYLLABLE NBUR;Lo;0;L;;;;;N;;;;; +A08B;YI SYLLABLE NBYT;Lo;0;L;;;;;N;;;;; +A08C;YI SYLLABLE NBYX;Lo;0;L;;;;;N;;;;; +A08D;YI SYLLABLE NBY;Lo;0;L;;;;;N;;;;; +A08E;YI SYLLABLE NBYP;Lo;0;L;;;;;N;;;;; +A08F;YI SYLLABLE NBYRX;Lo;0;L;;;;;N;;;;; +A090;YI SYLLABLE NBYR;Lo;0;L;;;;;N;;;;; +A091;YI SYLLABLE HMIT;Lo;0;L;;;;;N;;;;; +A092;YI SYLLABLE HMIX;Lo;0;L;;;;;N;;;;; +A093;YI SYLLABLE HMI;Lo;0;L;;;;;N;;;;; +A094;YI SYLLABLE HMIP;Lo;0;L;;;;;N;;;;; +A095;YI SYLLABLE HMIEX;Lo;0;L;;;;;N;;;;; +A096;YI SYLLABLE HMIE;Lo;0;L;;;;;N;;;;; +A097;YI SYLLABLE HMIEP;Lo;0;L;;;;;N;;;;; +A098;YI SYLLABLE HMAT;Lo;0;L;;;;;N;;;;; +A099;YI SYLLABLE HMAX;Lo;0;L;;;;;N;;;;; +A09A;YI SYLLABLE HMA;Lo;0;L;;;;;N;;;;; +A09B;YI SYLLABLE HMAP;Lo;0;L;;;;;N;;;;; +A09C;YI SYLLABLE HMUOX;Lo;0;L;;;;;N;;;;; +A09D;YI SYLLABLE HMUO;Lo;0;L;;;;;N;;;;; +A09E;YI SYLLABLE HMUOP;Lo;0;L;;;;;N;;;;; +A09F;YI SYLLABLE HMOT;Lo;0;L;;;;;N;;;;; +A0A0;YI SYLLABLE HMOX;Lo;0;L;;;;;N;;;;; +A0A1;YI SYLLABLE HMO;Lo;0;L;;;;;N;;;;; +A0A2;YI SYLLABLE HMOP;Lo;0;L;;;;;N;;;;; +A0A3;YI SYLLABLE HMUT;Lo;0;L;;;;;N;;;;; +A0A4;YI SYLLABLE HMUX;Lo;0;L;;;;;N;;;;; +A0A5;YI SYLLABLE HMU;Lo;0;L;;;;;N;;;;; +A0A6;YI SYLLABLE HMUP;Lo;0;L;;;;;N;;;;; +A0A7;YI SYLLABLE HMURX;Lo;0;L;;;;;N;;;;; +A0A8;YI SYLLABLE HMUR;Lo;0;L;;;;;N;;;;; +A0A9;YI SYLLABLE HMYX;Lo;0;L;;;;;N;;;;; +A0AA;YI SYLLABLE HMY;Lo;0;L;;;;;N;;;;; +A0AB;YI SYLLABLE HMYP;Lo;0;L;;;;;N;;;;; +A0AC;YI SYLLABLE HMYRX;Lo;0;L;;;;;N;;;;; +A0AD;YI SYLLABLE HMYR;Lo;0;L;;;;;N;;;;; +A0AE;YI SYLLABLE MIT;Lo;0;L;;;;;N;;;;; +A0AF;YI SYLLABLE MIX;Lo;0;L;;;;;N;;;;; +A0B0;YI SYLLABLE MI;Lo;0;L;;;;;N;;;;; +A0B1;YI SYLLABLE MIP;Lo;0;L;;;;;N;;;;; +A0B2;YI SYLLABLE MIEX;Lo;0;L;;;;;N;;;;; +A0B3;YI SYLLABLE MIE;Lo;0;L;;;;;N;;;;; +A0B4;YI SYLLABLE MIEP;Lo;0;L;;;;;N;;;;; +A0B5;YI SYLLABLE MAT;Lo;0;L;;;;;N;;;;; +A0B6;YI SYLLABLE MAX;Lo;0;L;;;;;N;;;;; +A0B7;YI SYLLABLE MA;Lo;0;L;;;;;N;;;;; +A0B8;YI SYLLABLE MAP;Lo;0;L;;;;;N;;;;; +A0B9;YI SYLLABLE MUOT;Lo;0;L;;;;;N;;;;; +A0BA;YI SYLLABLE MUOX;Lo;0;L;;;;;N;;;;; +A0BB;YI SYLLABLE MUO;Lo;0;L;;;;;N;;;;; +A0BC;YI SYLLABLE MUOP;Lo;0;L;;;;;N;;;;; +A0BD;YI SYLLABLE MOT;Lo;0;L;;;;;N;;;;; +A0BE;YI SYLLABLE MOX;Lo;0;L;;;;;N;;;;; +A0BF;YI SYLLABLE MO;Lo;0;L;;;;;N;;;;; +A0C0;YI SYLLABLE MOP;Lo;0;L;;;;;N;;;;; +A0C1;YI SYLLABLE MEX;Lo;0;L;;;;;N;;;;; +A0C2;YI SYLLABLE ME;Lo;0;L;;;;;N;;;;; +A0C3;YI SYLLABLE MUT;Lo;0;L;;;;;N;;;;; +A0C4;YI SYLLABLE MUX;Lo;0;L;;;;;N;;;;; +A0C5;YI SYLLABLE MU;Lo;0;L;;;;;N;;;;; +A0C6;YI SYLLABLE MUP;Lo;0;L;;;;;N;;;;; +A0C7;YI SYLLABLE MURX;Lo;0;L;;;;;N;;;;; +A0C8;YI SYLLABLE MUR;Lo;0;L;;;;;N;;;;; +A0C9;YI SYLLABLE MYT;Lo;0;L;;;;;N;;;;; +A0CA;YI SYLLABLE MYX;Lo;0;L;;;;;N;;;;; +A0CB;YI SYLLABLE MY;Lo;0;L;;;;;N;;;;; +A0CC;YI SYLLABLE MYP;Lo;0;L;;;;;N;;;;; +A0CD;YI SYLLABLE FIT;Lo;0;L;;;;;N;;;;; +A0CE;YI SYLLABLE FIX;Lo;0;L;;;;;N;;;;; +A0CF;YI SYLLABLE FI;Lo;0;L;;;;;N;;;;; +A0D0;YI SYLLABLE FIP;Lo;0;L;;;;;N;;;;; +A0D1;YI SYLLABLE FAT;Lo;0;L;;;;;N;;;;; +A0D2;YI SYLLABLE FAX;Lo;0;L;;;;;N;;;;; +A0D3;YI SYLLABLE FA;Lo;0;L;;;;;N;;;;; +A0D4;YI SYLLABLE FAP;Lo;0;L;;;;;N;;;;; +A0D5;YI SYLLABLE FOX;Lo;0;L;;;;;N;;;;; +A0D6;YI SYLLABLE FO;Lo;0;L;;;;;N;;;;; +A0D7;YI SYLLABLE FOP;Lo;0;L;;;;;N;;;;; +A0D8;YI SYLLABLE FUT;Lo;0;L;;;;;N;;;;; +A0D9;YI SYLLABLE FUX;Lo;0;L;;;;;N;;;;; +A0DA;YI SYLLABLE FU;Lo;0;L;;;;;N;;;;; +A0DB;YI SYLLABLE FUP;Lo;0;L;;;;;N;;;;; +A0DC;YI SYLLABLE FURX;Lo;0;L;;;;;N;;;;; +A0DD;YI SYLLABLE FUR;Lo;0;L;;;;;N;;;;; +A0DE;YI SYLLABLE FYT;Lo;0;L;;;;;N;;;;; +A0DF;YI SYLLABLE FYX;Lo;0;L;;;;;N;;;;; +A0E0;YI SYLLABLE FY;Lo;0;L;;;;;N;;;;; +A0E1;YI SYLLABLE FYP;Lo;0;L;;;;;N;;;;; +A0E2;YI SYLLABLE VIT;Lo;0;L;;;;;N;;;;; +A0E3;YI SYLLABLE VIX;Lo;0;L;;;;;N;;;;; +A0E4;YI SYLLABLE VI;Lo;0;L;;;;;N;;;;; +A0E5;YI SYLLABLE VIP;Lo;0;L;;;;;N;;;;; +A0E6;YI SYLLABLE VIET;Lo;0;L;;;;;N;;;;; +A0E7;YI SYLLABLE VIEX;Lo;0;L;;;;;N;;;;; +A0E8;YI SYLLABLE VIE;Lo;0;L;;;;;N;;;;; +A0E9;YI SYLLABLE VIEP;Lo;0;L;;;;;N;;;;; +A0EA;YI SYLLABLE VAT;Lo;0;L;;;;;N;;;;; +A0EB;YI SYLLABLE VAX;Lo;0;L;;;;;N;;;;; +A0EC;YI SYLLABLE VA;Lo;0;L;;;;;N;;;;; +A0ED;YI SYLLABLE VAP;Lo;0;L;;;;;N;;;;; +A0EE;YI SYLLABLE VOT;Lo;0;L;;;;;N;;;;; +A0EF;YI SYLLABLE VOX;Lo;0;L;;;;;N;;;;; +A0F0;YI SYLLABLE VO;Lo;0;L;;;;;N;;;;; +A0F1;YI SYLLABLE VOP;Lo;0;L;;;;;N;;;;; +A0F2;YI SYLLABLE VEX;Lo;0;L;;;;;N;;;;; +A0F3;YI SYLLABLE VEP;Lo;0;L;;;;;N;;;;; +A0F4;YI SYLLABLE VUT;Lo;0;L;;;;;N;;;;; +A0F5;YI SYLLABLE VUX;Lo;0;L;;;;;N;;;;; +A0F6;YI SYLLABLE VU;Lo;0;L;;;;;N;;;;; +A0F7;YI SYLLABLE VUP;Lo;0;L;;;;;N;;;;; +A0F8;YI SYLLABLE VURX;Lo;0;L;;;;;N;;;;; +A0F9;YI SYLLABLE VUR;Lo;0;L;;;;;N;;;;; +A0FA;YI SYLLABLE VYT;Lo;0;L;;;;;N;;;;; +A0FB;YI SYLLABLE VYX;Lo;0;L;;;;;N;;;;; +A0FC;YI SYLLABLE VY;Lo;0;L;;;;;N;;;;; +A0FD;YI SYLLABLE VYP;Lo;0;L;;;;;N;;;;; +A0FE;YI SYLLABLE VYRX;Lo;0;L;;;;;N;;;;; +A0FF;YI SYLLABLE VYR;Lo;0;L;;;;;N;;;;; +A100;YI SYLLABLE DIT;Lo;0;L;;;;;N;;;;; +A101;YI SYLLABLE DIX;Lo;0;L;;;;;N;;;;; +A102;YI SYLLABLE DI;Lo;0;L;;;;;N;;;;; +A103;YI SYLLABLE DIP;Lo;0;L;;;;;N;;;;; +A104;YI SYLLABLE DIEX;Lo;0;L;;;;;N;;;;; +A105;YI SYLLABLE DIE;Lo;0;L;;;;;N;;;;; +A106;YI SYLLABLE DIEP;Lo;0;L;;;;;N;;;;; +A107;YI SYLLABLE DAT;Lo;0;L;;;;;N;;;;; +A108;YI SYLLABLE DAX;Lo;0;L;;;;;N;;;;; +A109;YI SYLLABLE DA;Lo;0;L;;;;;N;;;;; +A10A;YI SYLLABLE DAP;Lo;0;L;;;;;N;;;;; +A10B;YI SYLLABLE DUOX;Lo;0;L;;;;;N;;;;; +A10C;YI SYLLABLE DUO;Lo;0;L;;;;;N;;;;; +A10D;YI SYLLABLE DOT;Lo;0;L;;;;;N;;;;; +A10E;YI SYLLABLE DOX;Lo;0;L;;;;;N;;;;; +A10F;YI SYLLABLE DO;Lo;0;L;;;;;N;;;;; +A110;YI SYLLABLE DOP;Lo;0;L;;;;;N;;;;; +A111;YI SYLLABLE DEX;Lo;0;L;;;;;N;;;;; +A112;YI SYLLABLE DE;Lo;0;L;;;;;N;;;;; +A113;YI SYLLABLE DEP;Lo;0;L;;;;;N;;;;; +A114;YI SYLLABLE DUT;Lo;0;L;;;;;N;;;;; +A115;YI SYLLABLE DUX;Lo;0;L;;;;;N;;;;; +A116;YI SYLLABLE DU;Lo;0;L;;;;;N;;;;; +A117;YI SYLLABLE DUP;Lo;0;L;;;;;N;;;;; +A118;YI SYLLABLE DURX;Lo;0;L;;;;;N;;;;; +A119;YI SYLLABLE DUR;Lo;0;L;;;;;N;;;;; +A11A;YI SYLLABLE TIT;Lo;0;L;;;;;N;;;;; +A11B;YI SYLLABLE TIX;Lo;0;L;;;;;N;;;;; +A11C;YI SYLLABLE TI;Lo;0;L;;;;;N;;;;; +A11D;YI SYLLABLE TIP;Lo;0;L;;;;;N;;;;; +A11E;YI SYLLABLE TIEX;Lo;0;L;;;;;N;;;;; +A11F;YI SYLLABLE TIE;Lo;0;L;;;;;N;;;;; +A120;YI SYLLABLE TIEP;Lo;0;L;;;;;N;;;;; +A121;YI SYLLABLE TAT;Lo;0;L;;;;;N;;;;; +A122;YI SYLLABLE TAX;Lo;0;L;;;;;N;;;;; +A123;YI SYLLABLE TA;Lo;0;L;;;;;N;;;;; +A124;YI SYLLABLE TAP;Lo;0;L;;;;;N;;;;; +A125;YI SYLLABLE TUOT;Lo;0;L;;;;;N;;;;; +A126;YI SYLLABLE TUOX;Lo;0;L;;;;;N;;;;; +A127;YI SYLLABLE TUO;Lo;0;L;;;;;N;;;;; +A128;YI SYLLABLE TUOP;Lo;0;L;;;;;N;;;;; +A129;YI SYLLABLE TOT;Lo;0;L;;;;;N;;;;; +A12A;YI SYLLABLE TOX;Lo;0;L;;;;;N;;;;; +A12B;YI SYLLABLE TO;Lo;0;L;;;;;N;;;;; +A12C;YI SYLLABLE TOP;Lo;0;L;;;;;N;;;;; +A12D;YI SYLLABLE TEX;Lo;0;L;;;;;N;;;;; +A12E;YI SYLLABLE TE;Lo;0;L;;;;;N;;;;; +A12F;YI SYLLABLE TEP;Lo;0;L;;;;;N;;;;; +A130;YI SYLLABLE TUT;Lo;0;L;;;;;N;;;;; +A131;YI SYLLABLE TUX;Lo;0;L;;;;;N;;;;; +A132;YI SYLLABLE TU;Lo;0;L;;;;;N;;;;; +A133;YI SYLLABLE TUP;Lo;0;L;;;;;N;;;;; +A134;YI SYLLABLE TURX;Lo;0;L;;;;;N;;;;; +A135;YI SYLLABLE TUR;Lo;0;L;;;;;N;;;;; +A136;YI SYLLABLE DDIT;Lo;0;L;;;;;N;;;;; +A137;YI SYLLABLE DDIX;Lo;0;L;;;;;N;;;;; +A138;YI SYLLABLE DDI;Lo;0;L;;;;;N;;;;; +A139;YI SYLLABLE DDIP;Lo;0;L;;;;;N;;;;; +A13A;YI SYLLABLE DDIEX;Lo;0;L;;;;;N;;;;; +A13B;YI SYLLABLE DDIE;Lo;0;L;;;;;N;;;;; +A13C;YI SYLLABLE DDIEP;Lo;0;L;;;;;N;;;;; +A13D;YI SYLLABLE DDAT;Lo;0;L;;;;;N;;;;; +A13E;YI SYLLABLE DDAX;Lo;0;L;;;;;N;;;;; +A13F;YI SYLLABLE DDA;Lo;0;L;;;;;N;;;;; +A140;YI SYLLABLE DDAP;Lo;0;L;;;;;N;;;;; +A141;YI SYLLABLE DDUOX;Lo;0;L;;;;;N;;;;; +A142;YI SYLLABLE DDUO;Lo;0;L;;;;;N;;;;; +A143;YI SYLLABLE DDUOP;Lo;0;L;;;;;N;;;;; +A144;YI SYLLABLE DDOT;Lo;0;L;;;;;N;;;;; +A145;YI SYLLABLE DDOX;Lo;0;L;;;;;N;;;;; +A146;YI SYLLABLE DDO;Lo;0;L;;;;;N;;;;; +A147;YI SYLLABLE DDOP;Lo;0;L;;;;;N;;;;; +A148;YI SYLLABLE DDEX;Lo;0;L;;;;;N;;;;; +A149;YI SYLLABLE DDE;Lo;0;L;;;;;N;;;;; +A14A;YI SYLLABLE DDEP;Lo;0;L;;;;;N;;;;; +A14B;YI SYLLABLE DDUT;Lo;0;L;;;;;N;;;;; +A14C;YI SYLLABLE DDUX;Lo;0;L;;;;;N;;;;; +A14D;YI SYLLABLE DDU;Lo;0;L;;;;;N;;;;; +A14E;YI SYLLABLE DDUP;Lo;0;L;;;;;N;;;;; +A14F;YI SYLLABLE DDURX;Lo;0;L;;;;;N;;;;; +A150;YI SYLLABLE DDUR;Lo;0;L;;;;;N;;;;; +A151;YI SYLLABLE NDIT;Lo;0;L;;;;;N;;;;; +A152;YI SYLLABLE NDIX;Lo;0;L;;;;;N;;;;; +A153;YI SYLLABLE NDI;Lo;0;L;;;;;N;;;;; +A154;YI SYLLABLE NDIP;Lo;0;L;;;;;N;;;;; +A155;YI SYLLABLE NDIEX;Lo;0;L;;;;;N;;;;; +A156;YI SYLLABLE NDIE;Lo;0;L;;;;;N;;;;; +A157;YI SYLLABLE NDAT;Lo;0;L;;;;;N;;;;; +A158;YI SYLLABLE NDAX;Lo;0;L;;;;;N;;;;; +A159;YI SYLLABLE NDA;Lo;0;L;;;;;N;;;;; +A15A;YI SYLLABLE NDAP;Lo;0;L;;;;;N;;;;; +A15B;YI SYLLABLE NDOT;Lo;0;L;;;;;N;;;;; +A15C;YI SYLLABLE NDOX;Lo;0;L;;;;;N;;;;; +A15D;YI SYLLABLE NDO;Lo;0;L;;;;;N;;;;; +A15E;YI SYLLABLE NDOP;Lo;0;L;;;;;N;;;;; +A15F;YI SYLLABLE NDEX;Lo;0;L;;;;;N;;;;; +A160;YI SYLLABLE NDE;Lo;0;L;;;;;N;;;;; +A161;YI SYLLABLE NDEP;Lo;0;L;;;;;N;;;;; +A162;YI SYLLABLE NDUT;Lo;0;L;;;;;N;;;;; +A163;YI SYLLABLE NDUX;Lo;0;L;;;;;N;;;;; +A164;YI SYLLABLE NDU;Lo;0;L;;;;;N;;;;; +A165;YI SYLLABLE NDUP;Lo;0;L;;;;;N;;;;; +A166;YI SYLLABLE NDURX;Lo;0;L;;;;;N;;;;; +A167;YI SYLLABLE NDUR;Lo;0;L;;;;;N;;;;; +A168;YI SYLLABLE HNIT;Lo;0;L;;;;;N;;;;; +A169;YI SYLLABLE HNIX;Lo;0;L;;;;;N;;;;; +A16A;YI SYLLABLE HNI;Lo;0;L;;;;;N;;;;; +A16B;YI SYLLABLE HNIP;Lo;0;L;;;;;N;;;;; +A16C;YI SYLLABLE HNIET;Lo;0;L;;;;;N;;;;; +A16D;YI SYLLABLE HNIEX;Lo;0;L;;;;;N;;;;; +A16E;YI SYLLABLE HNIE;Lo;0;L;;;;;N;;;;; +A16F;YI SYLLABLE HNIEP;Lo;0;L;;;;;N;;;;; +A170;YI SYLLABLE HNAT;Lo;0;L;;;;;N;;;;; +A171;YI SYLLABLE HNAX;Lo;0;L;;;;;N;;;;; +A172;YI SYLLABLE HNA;Lo;0;L;;;;;N;;;;; +A173;YI SYLLABLE HNAP;Lo;0;L;;;;;N;;;;; +A174;YI SYLLABLE HNUOX;Lo;0;L;;;;;N;;;;; +A175;YI SYLLABLE HNUO;Lo;0;L;;;;;N;;;;; +A176;YI SYLLABLE HNOT;Lo;0;L;;;;;N;;;;; +A177;YI SYLLABLE HNOX;Lo;0;L;;;;;N;;;;; +A178;YI SYLLABLE HNOP;Lo;0;L;;;;;N;;;;; +A179;YI SYLLABLE HNEX;Lo;0;L;;;;;N;;;;; +A17A;YI SYLLABLE HNE;Lo;0;L;;;;;N;;;;; +A17B;YI SYLLABLE HNEP;Lo;0;L;;;;;N;;;;; +A17C;YI SYLLABLE HNUT;Lo;0;L;;;;;N;;;;; +A17D;YI SYLLABLE NIT;Lo;0;L;;;;;N;;;;; +A17E;YI SYLLABLE NIX;Lo;0;L;;;;;N;;;;; +A17F;YI SYLLABLE NI;Lo;0;L;;;;;N;;;;; +A180;YI SYLLABLE NIP;Lo;0;L;;;;;N;;;;; +A181;YI SYLLABLE NIEX;Lo;0;L;;;;;N;;;;; +A182;YI SYLLABLE NIE;Lo;0;L;;;;;N;;;;; +A183;YI SYLLABLE NIEP;Lo;0;L;;;;;N;;;;; +A184;YI SYLLABLE NAX;Lo;0;L;;;;;N;;;;; +A185;YI SYLLABLE NA;Lo;0;L;;;;;N;;;;; +A186;YI SYLLABLE NAP;Lo;0;L;;;;;N;;;;; +A187;YI SYLLABLE NUOX;Lo;0;L;;;;;N;;;;; +A188;YI SYLLABLE NUO;Lo;0;L;;;;;N;;;;; +A189;YI SYLLABLE NUOP;Lo;0;L;;;;;N;;;;; +A18A;YI SYLLABLE NOT;Lo;0;L;;;;;N;;;;; +A18B;YI SYLLABLE NOX;Lo;0;L;;;;;N;;;;; +A18C;YI SYLLABLE NO;Lo;0;L;;;;;N;;;;; +A18D;YI SYLLABLE NOP;Lo;0;L;;;;;N;;;;; +A18E;YI SYLLABLE NEX;Lo;0;L;;;;;N;;;;; +A18F;YI SYLLABLE NE;Lo;0;L;;;;;N;;;;; +A190;YI SYLLABLE NEP;Lo;0;L;;;;;N;;;;; +A191;YI SYLLABLE NUT;Lo;0;L;;;;;N;;;;; +A192;YI SYLLABLE NUX;Lo;0;L;;;;;N;;;;; +A193;YI SYLLABLE NU;Lo;0;L;;;;;N;;;;; +A194;YI SYLLABLE NUP;Lo;0;L;;;;;N;;;;; +A195;YI SYLLABLE NURX;Lo;0;L;;;;;N;;;;; +A196;YI SYLLABLE NUR;Lo;0;L;;;;;N;;;;; +A197;YI SYLLABLE HLIT;Lo;0;L;;;;;N;;;;; +A198;YI SYLLABLE HLIX;Lo;0;L;;;;;N;;;;; +A199;YI SYLLABLE HLI;Lo;0;L;;;;;N;;;;; +A19A;YI SYLLABLE HLIP;Lo;0;L;;;;;N;;;;; +A19B;YI SYLLABLE HLIEX;Lo;0;L;;;;;N;;;;; +A19C;YI SYLLABLE HLIE;Lo;0;L;;;;;N;;;;; +A19D;YI SYLLABLE HLIEP;Lo;0;L;;;;;N;;;;; +A19E;YI SYLLABLE HLAT;Lo;0;L;;;;;N;;;;; +A19F;YI SYLLABLE HLAX;Lo;0;L;;;;;N;;;;; +A1A0;YI SYLLABLE HLA;Lo;0;L;;;;;N;;;;; +A1A1;YI SYLLABLE HLAP;Lo;0;L;;;;;N;;;;; +A1A2;YI SYLLABLE HLUOX;Lo;0;L;;;;;N;;;;; +A1A3;YI SYLLABLE HLUO;Lo;0;L;;;;;N;;;;; +A1A4;YI SYLLABLE HLUOP;Lo;0;L;;;;;N;;;;; +A1A5;YI SYLLABLE HLOX;Lo;0;L;;;;;N;;;;; +A1A6;YI SYLLABLE HLO;Lo;0;L;;;;;N;;;;; +A1A7;YI SYLLABLE HLOP;Lo;0;L;;;;;N;;;;; +A1A8;YI SYLLABLE HLEX;Lo;0;L;;;;;N;;;;; +A1A9;YI SYLLABLE HLE;Lo;0;L;;;;;N;;;;; +A1AA;YI SYLLABLE HLEP;Lo;0;L;;;;;N;;;;; +A1AB;YI SYLLABLE HLUT;Lo;0;L;;;;;N;;;;; +A1AC;YI SYLLABLE HLUX;Lo;0;L;;;;;N;;;;; +A1AD;YI SYLLABLE HLU;Lo;0;L;;;;;N;;;;; +A1AE;YI SYLLABLE HLUP;Lo;0;L;;;;;N;;;;; +A1AF;YI SYLLABLE HLURX;Lo;0;L;;;;;N;;;;; +A1B0;YI SYLLABLE HLUR;Lo;0;L;;;;;N;;;;; +A1B1;YI SYLLABLE HLYT;Lo;0;L;;;;;N;;;;; +A1B2;YI SYLLABLE HLYX;Lo;0;L;;;;;N;;;;; +A1B3;YI SYLLABLE HLY;Lo;0;L;;;;;N;;;;; +A1B4;YI SYLLABLE HLYP;Lo;0;L;;;;;N;;;;; +A1B5;YI SYLLABLE HLYRX;Lo;0;L;;;;;N;;;;; +A1B6;YI SYLLABLE HLYR;Lo;0;L;;;;;N;;;;; +A1B7;YI SYLLABLE LIT;Lo;0;L;;;;;N;;;;; +A1B8;YI SYLLABLE LIX;Lo;0;L;;;;;N;;;;; +A1B9;YI SYLLABLE LI;Lo;0;L;;;;;N;;;;; +A1BA;YI SYLLABLE LIP;Lo;0;L;;;;;N;;;;; +A1BB;YI SYLLABLE LIET;Lo;0;L;;;;;N;;;;; +A1BC;YI SYLLABLE LIEX;Lo;0;L;;;;;N;;;;; +A1BD;YI SYLLABLE LIE;Lo;0;L;;;;;N;;;;; +A1BE;YI SYLLABLE LIEP;Lo;0;L;;;;;N;;;;; +A1BF;YI SYLLABLE LAT;Lo;0;L;;;;;N;;;;; +A1C0;YI SYLLABLE LAX;Lo;0;L;;;;;N;;;;; +A1C1;YI SYLLABLE LA;Lo;0;L;;;;;N;;;;; +A1C2;YI SYLLABLE LAP;Lo;0;L;;;;;N;;;;; +A1C3;YI SYLLABLE LUOT;Lo;0;L;;;;;N;;;;; +A1C4;YI SYLLABLE LUOX;Lo;0;L;;;;;N;;;;; +A1C5;YI SYLLABLE LUO;Lo;0;L;;;;;N;;;;; +A1C6;YI SYLLABLE LUOP;Lo;0;L;;;;;N;;;;; +A1C7;YI SYLLABLE LOT;Lo;0;L;;;;;N;;;;; +A1C8;YI SYLLABLE LOX;Lo;0;L;;;;;N;;;;; +A1C9;YI SYLLABLE LO;Lo;0;L;;;;;N;;;;; +A1CA;YI SYLLABLE LOP;Lo;0;L;;;;;N;;;;; +A1CB;YI SYLLABLE LEX;Lo;0;L;;;;;N;;;;; +A1CC;YI SYLLABLE LE;Lo;0;L;;;;;N;;;;; +A1CD;YI SYLLABLE LEP;Lo;0;L;;;;;N;;;;; +A1CE;YI SYLLABLE LUT;Lo;0;L;;;;;N;;;;; +A1CF;YI SYLLABLE LUX;Lo;0;L;;;;;N;;;;; +A1D0;YI SYLLABLE LU;Lo;0;L;;;;;N;;;;; +A1D1;YI SYLLABLE LUP;Lo;0;L;;;;;N;;;;; +A1D2;YI SYLLABLE LURX;Lo;0;L;;;;;N;;;;; +A1D3;YI SYLLABLE LUR;Lo;0;L;;;;;N;;;;; +A1D4;YI SYLLABLE LYT;Lo;0;L;;;;;N;;;;; +A1D5;YI SYLLABLE LYX;Lo;0;L;;;;;N;;;;; +A1D6;YI SYLLABLE LY;Lo;0;L;;;;;N;;;;; +A1D7;YI SYLLABLE LYP;Lo;0;L;;;;;N;;;;; +A1D8;YI SYLLABLE LYRX;Lo;0;L;;;;;N;;;;; +A1D9;YI SYLLABLE LYR;Lo;0;L;;;;;N;;;;; +A1DA;YI SYLLABLE GIT;Lo;0;L;;;;;N;;;;; +A1DB;YI SYLLABLE GIX;Lo;0;L;;;;;N;;;;; +A1DC;YI SYLLABLE GI;Lo;0;L;;;;;N;;;;; +A1DD;YI SYLLABLE GIP;Lo;0;L;;;;;N;;;;; +A1DE;YI SYLLABLE GIET;Lo;0;L;;;;;N;;;;; +A1DF;YI SYLLABLE GIEX;Lo;0;L;;;;;N;;;;; +A1E0;YI SYLLABLE GIE;Lo;0;L;;;;;N;;;;; +A1E1;YI SYLLABLE GIEP;Lo;0;L;;;;;N;;;;; +A1E2;YI SYLLABLE GAT;Lo;0;L;;;;;N;;;;; +A1E3;YI SYLLABLE GAX;Lo;0;L;;;;;N;;;;; +A1E4;YI SYLLABLE GA;Lo;0;L;;;;;N;;;;; +A1E5;YI SYLLABLE GAP;Lo;0;L;;;;;N;;;;; +A1E6;YI SYLLABLE GUOT;Lo;0;L;;;;;N;;;;; +A1E7;YI SYLLABLE GUOX;Lo;0;L;;;;;N;;;;; +A1E8;YI SYLLABLE GUO;Lo;0;L;;;;;N;;;;; +A1E9;YI SYLLABLE GUOP;Lo;0;L;;;;;N;;;;; +A1EA;YI SYLLABLE GOT;Lo;0;L;;;;;N;;;;; +A1EB;YI SYLLABLE GOX;Lo;0;L;;;;;N;;;;; +A1EC;YI SYLLABLE GO;Lo;0;L;;;;;N;;;;; +A1ED;YI SYLLABLE GOP;Lo;0;L;;;;;N;;;;; +A1EE;YI SYLLABLE GET;Lo;0;L;;;;;N;;;;; +A1EF;YI SYLLABLE GEX;Lo;0;L;;;;;N;;;;; +A1F0;YI SYLLABLE GE;Lo;0;L;;;;;N;;;;; +A1F1;YI SYLLABLE GEP;Lo;0;L;;;;;N;;;;; +A1F2;YI SYLLABLE GUT;Lo;0;L;;;;;N;;;;; +A1F3;YI SYLLABLE GUX;Lo;0;L;;;;;N;;;;; +A1F4;YI SYLLABLE GU;Lo;0;L;;;;;N;;;;; +A1F5;YI SYLLABLE GUP;Lo;0;L;;;;;N;;;;; +A1F6;YI SYLLABLE GURX;Lo;0;L;;;;;N;;;;; +A1F7;YI SYLLABLE GUR;Lo;0;L;;;;;N;;;;; +A1F8;YI SYLLABLE KIT;Lo;0;L;;;;;N;;;;; +A1F9;YI SYLLABLE KIX;Lo;0;L;;;;;N;;;;; +A1FA;YI SYLLABLE KI;Lo;0;L;;;;;N;;;;; +A1FB;YI SYLLABLE KIP;Lo;0;L;;;;;N;;;;; +A1FC;YI SYLLABLE KIEX;Lo;0;L;;;;;N;;;;; +A1FD;YI SYLLABLE KIE;Lo;0;L;;;;;N;;;;; +A1FE;YI SYLLABLE KIEP;Lo;0;L;;;;;N;;;;; +A1FF;YI SYLLABLE KAT;Lo;0;L;;;;;N;;;;; +A200;YI SYLLABLE KAX;Lo;0;L;;;;;N;;;;; +A201;YI SYLLABLE KA;Lo;0;L;;;;;N;;;;; +A202;YI SYLLABLE KAP;Lo;0;L;;;;;N;;;;; +A203;YI SYLLABLE KUOX;Lo;0;L;;;;;N;;;;; +A204;YI SYLLABLE KUO;Lo;0;L;;;;;N;;;;; +A205;YI SYLLABLE KUOP;Lo;0;L;;;;;N;;;;; +A206;YI SYLLABLE KOT;Lo;0;L;;;;;N;;;;; +A207;YI SYLLABLE KOX;Lo;0;L;;;;;N;;;;; +A208;YI SYLLABLE KO;Lo;0;L;;;;;N;;;;; +A209;YI SYLLABLE KOP;Lo;0;L;;;;;N;;;;; +A20A;YI SYLLABLE KET;Lo;0;L;;;;;N;;;;; +A20B;YI SYLLABLE KEX;Lo;0;L;;;;;N;;;;; +A20C;YI SYLLABLE KE;Lo;0;L;;;;;N;;;;; +A20D;YI SYLLABLE KEP;Lo;0;L;;;;;N;;;;; +A20E;YI SYLLABLE KUT;Lo;0;L;;;;;N;;;;; +A20F;YI SYLLABLE KUX;Lo;0;L;;;;;N;;;;; +A210;YI SYLLABLE KU;Lo;0;L;;;;;N;;;;; +A211;YI SYLLABLE KUP;Lo;0;L;;;;;N;;;;; +A212;YI SYLLABLE KURX;Lo;0;L;;;;;N;;;;; +A213;YI SYLLABLE KUR;Lo;0;L;;;;;N;;;;; +A214;YI SYLLABLE GGIT;Lo;0;L;;;;;N;;;;; +A215;YI SYLLABLE GGIX;Lo;0;L;;;;;N;;;;; +A216;YI SYLLABLE GGI;Lo;0;L;;;;;N;;;;; +A217;YI SYLLABLE GGIEX;Lo;0;L;;;;;N;;;;; +A218;YI SYLLABLE GGIE;Lo;0;L;;;;;N;;;;; +A219;YI SYLLABLE GGIEP;Lo;0;L;;;;;N;;;;; +A21A;YI SYLLABLE GGAT;Lo;0;L;;;;;N;;;;; +A21B;YI SYLLABLE GGAX;Lo;0;L;;;;;N;;;;; +A21C;YI SYLLABLE GGA;Lo;0;L;;;;;N;;;;; +A21D;YI SYLLABLE GGAP;Lo;0;L;;;;;N;;;;; +A21E;YI SYLLABLE GGUOT;Lo;0;L;;;;;N;;;;; +A21F;YI SYLLABLE GGUOX;Lo;0;L;;;;;N;;;;; +A220;YI SYLLABLE GGUO;Lo;0;L;;;;;N;;;;; +A221;YI SYLLABLE GGUOP;Lo;0;L;;;;;N;;;;; +A222;YI SYLLABLE GGOT;Lo;0;L;;;;;N;;;;; +A223;YI SYLLABLE GGOX;Lo;0;L;;;;;N;;;;; +A224;YI SYLLABLE GGO;Lo;0;L;;;;;N;;;;; +A225;YI SYLLABLE GGOP;Lo;0;L;;;;;N;;;;; +A226;YI SYLLABLE GGET;Lo;0;L;;;;;N;;;;; +A227;YI SYLLABLE GGEX;Lo;0;L;;;;;N;;;;; +A228;YI SYLLABLE GGE;Lo;0;L;;;;;N;;;;; +A229;YI SYLLABLE GGEP;Lo;0;L;;;;;N;;;;; +A22A;YI SYLLABLE GGUT;Lo;0;L;;;;;N;;;;; +A22B;YI SYLLABLE GGUX;Lo;0;L;;;;;N;;;;; +A22C;YI SYLLABLE GGU;Lo;0;L;;;;;N;;;;; +A22D;YI SYLLABLE GGUP;Lo;0;L;;;;;N;;;;; +A22E;YI SYLLABLE GGURX;Lo;0;L;;;;;N;;;;; +A22F;YI SYLLABLE GGUR;Lo;0;L;;;;;N;;;;; +A230;YI SYLLABLE MGIEX;Lo;0;L;;;;;N;;;;; +A231;YI SYLLABLE MGIE;Lo;0;L;;;;;N;;;;; +A232;YI SYLLABLE MGAT;Lo;0;L;;;;;N;;;;; +A233;YI SYLLABLE MGAX;Lo;0;L;;;;;N;;;;; +A234;YI SYLLABLE MGA;Lo;0;L;;;;;N;;;;; +A235;YI SYLLABLE MGAP;Lo;0;L;;;;;N;;;;; +A236;YI SYLLABLE MGUOX;Lo;0;L;;;;;N;;;;; +A237;YI SYLLABLE MGUO;Lo;0;L;;;;;N;;;;; +A238;YI SYLLABLE MGUOP;Lo;0;L;;;;;N;;;;; +A239;YI SYLLABLE MGOT;Lo;0;L;;;;;N;;;;; +A23A;YI SYLLABLE MGOX;Lo;0;L;;;;;N;;;;; +A23B;YI SYLLABLE MGO;Lo;0;L;;;;;N;;;;; +A23C;YI SYLLABLE MGOP;Lo;0;L;;;;;N;;;;; +A23D;YI SYLLABLE MGEX;Lo;0;L;;;;;N;;;;; +A23E;YI SYLLABLE MGE;Lo;0;L;;;;;N;;;;; +A23F;YI SYLLABLE MGEP;Lo;0;L;;;;;N;;;;; +A240;YI SYLLABLE MGUT;Lo;0;L;;;;;N;;;;; +A241;YI SYLLABLE MGUX;Lo;0;L;;;;;N;;;;; +A242;YI SYLLABLE MGU;Lo;0;L;;;;;N;;;;; +A243;YI SYLLABLE MGUP;Lo;0;L;;;;;N;;;;; +A244;YI SYLLABLE MGURX;Lo;0;L;;;;;N;;;;; +A245;YI SYLLABLE MGUR;Lo;0;L;;;;;N;;;;; +A246;YI SYLLABLE HXIT;Lo;0;L;;;;;N;;;;; +A247;YI SYLLABLE HXIX;Lo;0;L;;;;;N;;;;; +A248;YI SYLLABLE HXI;Lo;0;L;;;;;N;;;;; +A249;YI SYLLABLE HXIP;Lo;0;L;;;;;N;;;;; +A24A;YI SYLLABLE HXIET;Lo;0;L;;;;;N;;;;; +A24B;YI SYLLABLE HXIEX;Lo;0;L;;;;;N;;;;; +A24C;YI SYLLABLE HXIE;Lo;0;L;;;;;N;;;;; +A24D;YI SYLLABLE HXIEP;Lo;0;L;;;;;N;;;;; +A24E;YI SYLLABLE HXAT;Lo;0;L;;;;;N;;;;; +A24F;YI SYLLABLE HXAX;Lo;0;L;;;;;N;;;;; +A250;YI SYLLABLE HXA;Lo;0;L;;;;;N;;;;; +A251;YI SYLLABLE HXAP;Lo;0;L;;;;;N;;;;; +A252;YI SYLLABLE HXUOT;Lo;0;L;;;;;N;;;;; +A253;YI SYLLABLE HXUOX;Lo;0;L;;;;;N;;;;; +A254;YI SYLLABLE HXUO;Lo;0;L;;;;;N;;;;; +A255;YI SYLLABLE HXUOP;Lo;0;L;;;;;N;;;;; +A256;YI SYLLABLE HXOT;Lo;0;L;;;;;N;;;;; +A257;YI SYLLABLE HXOX;Lo;0;L;;;;;N;;;;; +A258;YI SYLLABLE HXO;Lo;0;L;;;;;N;;;;; +A259;YI SYLLABLE HXOP;Lo;0;L;;;;;N;;;;; +A25A;YI SYLLABLE HXEX;Lo;0;L;;;;;N;;;;; +A25B;YI SYLLABLE HXE;Lo;0;L;;;;;N;;;;; +A25C;YI SYLLABLE HXEP;Lo;0;L;;;;;N;;;;; +A25D;YI SYLLABLE NGIEX;Lo;0;L;;;;;N;;;;; +A25E;YI SYLLABLE NGIE;Lo;0;L;;;;;N;;;;; +A25F;YI SYLLABLE NGIEP;Lo;0;L;;;;;N;;;;; +A260;YI SYLLABLE NGAT;Lo;0;L;;;;;N;;;;; +A261;YI SYLLABLE NGAX;Lo;0;L;;;;;N;;;;; +A262;YI SYLLABLE NGA;Lo;0;L;;;;;N;;;;; +A263;YI SYLLABLE NGAP;Lo;0;L;;;;;N;;;;; +A264;YI SYLLABLE NGUOT;Lo;0;L;;;;;N;;;;; +A265;YI SYLLABLE NGUOX;Lo;0;L;;;;;N;;;;; +A266;YI SYLLABLE NGUO;Lo;0;L;;;;;N;;;;; +A267;YI SYLLABLE NGOT;Lo;0;L;;;;;N;;;;; +A268;YI SYLLABLE NGOX;Lo;0;L;;;;;N;;;;; +A269;YI SYLLABLE NGO;Lo;0;L;;;;;N;;;;; +A26A;YI SYLLABLE NGOP;Lo;0;L;;;;;N;;;;; +A26B;YI SYLLABLE NGEX;Lo;0;L;;;;;N;;;;; +A26C;YI SYLLABLE NGE;Lo;0;L;;;;;N;;;;; +A26D;YI SYLLABLE NGEP;Lo;0;L;;;;;N;;;;; +A26E;YI SYLLABLE HIT;Lo;0;L;;;;;N;;;;; +A26F;YI SYLLABLE HIEX;Lo;0;L;;;;;N;;;;; +A270;YI SYLLABLE HIE;Lo;0;L;;;;;N;;;;; +A271;YI SYLLABLE HAT;Lo;0;L;;;;;N;;;;; +A272;YI SYLLABLE HAX;Lo;0;L;;;;;N;;;;; +A273;YI SYLLABLE HA;Lo;0;L;;;;;N;;;;; +A274;YI SYLLABLE HAP;Lo;0;L;;;;;N;;;;; +A275;YI SYLLABLE HUOT;Lo;0;L;;;;;N;;;;; +A276;YI SYLLABLE HUOX;Lo;0;L;;;;;N;;;;; +A277;YI SYLLABLE HUO;Lo;0;L;;;;;N;;;;; +A278;YI SYLLABLE HUOP;Lo;0;L;;;;;N;;;;; +A279;YI SYLLABLE HOT;Lo;0;L;;;;;N;;;;; +A27A;YI SYLLABLE HOX;Lo;0;L;;;;;N;;;;; +A27B;YI SYLLABLE HO;Lo;0;L;;;;;N;;;;; +A27C;YI SYLLABLE HOP;Lo;0;L;;;;;N;;;;; +A27D;YI SYLLABLE HEX;Lo;0;L;;;;;N;;;;; +A27E;YI SYLLABLE HE;Lo;0;L;;;;;N;;;;; +A27F;YI SYLLABLE HEP;Lo;0;L;;;;;N;;;;; +A280;YI SYLLABLE WAT;Lo;0;L;;;;;N;;;;; +A281;YI SYLLABLE WAX;Lo;0;L;;;;;N;;;;; +A282;YI SYLLABLE WA;Lo;0;L;;;;;N;;;;; +A283;YI SYLLABLE WAP;Lo;0;L;;;;;N;;;;; +A284;YI SYLLABLE WUOX;Lo;0;L;;;;;N;;;;; +A285;YI SYLLABLE WUO;Lo;0;L;;;;;N;;;;; +A286;YI SYLLABLE WUOP;Lo;0;L;;;;;N;;;;; +A287;YI SYLLABLE WOX;Lo;0;L;;;;;N;;;;; +A288;YI SYLLABLE WO;Lo;0;L;;;;;N;;;;; +A289;YI SYLLABLE WOP;Lo;0;L;;;;;N;;;;; +A28A;YI SYLLABLE WEX;Lo;0;L;;;;;N;;;;; +A28B;YI SYLLABLE WE;Lo;0;L;;;;;N;;;;; +A28C;YI SYLLABLE WEP;Lo;0;L;;;;;N;;;;; +A28D;YI SYLLABLE ZIT;Lo;0;L;;;;;N;;;;; +A28E;YI SYLLABLE ZIX;Lo;0;L;;;;;N;;;;; +A28F;YI SYLLABLE ZI;Lo;0;L;;;;;N;;;;; +A290;YI SYLLABLE ZIP;Lo;0;L;;;;;N;;;;; +A291;YI SYLLABLE ZIEX;Lo;0;L;;;;;N;;;;; +A292;YI SYLLABLE ZIE;Lo;0;L;;;;;N;;;;; +A293;YI SYLLABLE ZIEP;Lo;0;L;;;;;N;;;;; +A294;YI SYLLABLE ZAT;Lo;0;L;;;;;N;;;;; +A295;YI SYLLABLE ZAX;Lo;0;L;;;;;N;;;;; +A296;YI SYLLABLE ZA;Lo;0;L;;;;;N;;;;; +A297;YI SYLLABLE ZAP;Lo;0;L;;;;;N;;;;; +A298;YI SYLLABLE ZUOX;Lo;0;L;;;;;N;;;;; +A299;YI SYLLABLE ZUO;Lo;0;L;;;;;N;;;;; +A29A;YI SYLLABLE ZUOP;Lo;0;L;;;;;N;;;;; +A29B;YI SYLLABLE ZOT;Lo;0;L;;;;;N;;;;; +A29C;YI SYLLABLE ZOX;Lo;0;L;;;;;N;;;;; +A29D;YI SYLLABLE ZO;Lo;0;L;;;;;N;;;;; +A29E;YI SYLLABLE ZOP;Lo;0;L;;;;;N;;;;; +A29F;YI SYLLABLE ZEX;Lo;0;L;;;;;N;;;;; +A2A0;YI SYLLABLE ZE;Lo;0;L;;;;;N;;;;; +A2A1;YI SYLLABLE ZEP;Lo;0;L;;;;;N;;;;; +A2A2;YI SYLLABLE ZUT;Lo;0;L;;;;;N;;;;; +A2A3;YI SYLLABLE ZUX;Lo;0;L;;;;;N;;;;; +A2A4;YI SYLLABLE ZU;Lo;0;L;;;;;N;;;;; +A2A5;YI SYLLABLE ZUP;Lo;0;L;;;;;N;;;;; +A2A6;YI SYLLABLE ZURX;Lo;0;L;;;;;N;;;;; +A2A7;YI SYLLABLE ZUR;Lo;0;L;;;;;N;;;;; +A2A8;YI SYLLABLE ZYT;Lo;0;L;;;;;N;;;;; +A2A9;YI SYLLABLE ZYX;Lo;0;L;;;;;N;;;;; +A2AA;YI SYLLABLE ZY;Lo;0;L;;;;;N;;;;; +A2AB;YI SYLLABLE ZYP;Lo;0;L;;;;;N;;;;; +A2AC;YI SYLLABLE ZYRX;Lo;0;L;;;;;N;;;;; +A2AD;YI SYLLABLE ZYR;Lo;0;L;;;;;N;;;;; +A2AE;YI SYLLABLE CIT;Lo;0;L;;;;;N;;;;; +A2AF;YI SYLLABLE CIX;Lo;0;L;;;;;N;;;;; +A2B0;YI SYLLABLE CI;Lo;0;L;;;;;N;;;;; +A2B1;YI SYLLABLE CIP;Lo;0;L;;;;;N;;;;; +A2B2;YI SYLLABLE CIET;Lo;0;L;;;;;N;;;;; +A2B3;YI SYLLABLE CIEX;Lo;0;L;;;;;N;;;;; +A2B4;YI SYLLABLE CIE;Lo;0;L;;;;;N;;;;; +A2B5;YI SYLLABLE CIEP;Lo;0;L;;;;;N;;;;; +A2B6;YI SYLLABLE CAT;Lo;0;L;;;;;N;;;;; +A2B7;YI SYLLABLE CAX;Lo;0;L;;;;;N;;;;; +A2B8;YI SYLLABLE CA;Lo;0;L;;;;;N;;;;; +A2B9;YI SYLLABLE CAP;Lo;0;L;;;;;N;;;;; +A2BA;YI SYLLABLE CUOX;Lo;0;L;;;;;N;;;;; +A2BB;YI SYLLABLE CUO;Lo;0;L;;;;;N;;;;; +A2BC;YI SYLLABLE CUOP;Lo;0;L;;;;;N;;;;; +A2BD;YI SYLLABLE COT;Lo;0;L;;;;;N;;;;; +A2BE;YI SYLLABLE COX;Lo;0;L;;;;;N;;;;; +A2BF;YI SYLLABLE CO;Lo;0;L;;;;;N;;;;; +A2C0;YI SYLLABLE COP;Lo;0;L;;;;;N;;;;; +A2C1;YI SYLLABLE CEX;Lo;0;L;;;;;N;;;;; +A2C2;YI SYLLABLE CE;Lo;0;L;;;;;N;;;;; +A2C3;YI SYLLABLE CEP;Lo;0;L;;;;;N;;;;; +A2C4;YI SYLLABLE CUT;Lo;0;L;;;;;N;;;;; +A2C5;YI SYLLABLE CUX;Lo;0;L;;;;;N;;;;; +A2C6;YI SYLLABLE CU;Lo;0;L;;;;;N;;;;; +A2C7;YI SYLLABLE CUP;Lo;0;L;;;;;N;;;;; +A2C8;YI SYLLABLE CURX;Lo;0;L;;;;;N;;;;; +A2C9;YI SYLLABLE CUR;Lo;0;L;;;;;N;;;;; +A2CA;YI SYLLABLE CYT;Lo;0;L;;;;;N;;;;; +A2CB;YI SYLLABLE CYX;Lo;0;L;;;;;N;;;;; +A2CC;YI SYLLABLE CY;Lo;0;L;;;;;N;;;;; +A2CD;YI SYLLABLE CYP;Lo;0;L;;;;;N;;;;; +A2CE;YI SYLLABLE CYRX;Lo;0;L;;;;;N;;;;; +A2CF;YI SYLLABLE CYR;Lo;0;L;;;;;N;;;;; +A2D0;YI SYLLABLE ZZIT;Lo;0;L;;;;;N;;;;; +A2D1;YI SYLLABLE ZZIX;Lo;0;L;;;;;N;;;;; +A2D2;YI SYLLABLE ZZI;Lo;0;L;;;;;N;;;;; +A2D3;YI SYLLABLE ZZIP;Lo;0;L;;;;;N;;;;; +A2D4;YI SYLLABLE ZZIET;Lo;0;L;;;;;N;;;;; +A2D5;YI SYLLABLE ZZIEX;Lo;0;L;;;;;N;;;;; +A2D6;YI SYLLABLE ZZIE;Lo;0;L;;;;;N;;;;; +A2D7;YI SYLLABLE ZZIEP;Lo;0;L;;;;;N;;;;; +A2D8;YI SYLLABLE ZZAT;Lo;0;L;;;;;N;;;;; +A2D9;YI SYLLABLE ZZAX;Lo;0;L;;;;;N;;;;; +A2DA;YI SYLLABLE ZZA;Lo;0;L;;;;;N;;;;; +A2DB;YI SYLLABLE ZZAP;Lo;0;L;;;;;N;;;;; +A2DC;YI SYLLABLE ZZOX;Lo;0;L;;;;;N;;;;; +A2DD;YI SYLLABLE ZZO;Lo;0;L;;;;;N;;;;; +A2DE;YI SYLLABLE ZZOP;Lo;0;L;;;;;N;;;;; +A2DF;YI SYLLABLE ZZEX;Lo;0;L;;;;;N;;;;; +A2E0;YI SYLLABLE ZZE;Lo;0;L;;;;;N;;;;; +A2E1;YI SYLLABLE ZZEP;Lo;0;L;;;;;N;;;;; +A2E2;YI SYLLABLE ZZUX;Lo;0;L;;;;;N;;;;; +A2E3;YI SYLLABLE ZZU;Lo;0;L;;;;;N;;;;; +A2E4;YI SYLLABLE ZZUP;Lo;0;L;;;;;N;;;;; +A2E5;YI SYLLABLE ZZURX;Lo;0;L;;;;;N;;;;; +A2E6;YI SYLLABLE ZZUR;Lo;0;L;;;;;N;;;;; +A2E7;YI SYLLABLE ZZYT;Lo;0;L;;;;;N;;;;; +A2E8;YI SYLLABLE ZZYX;Lo;0;L;;;;;N;;;;; +A2E9;YI SYLLABLE ZZY;Lo;0;L;;;;;N;;;;; +A2EA;YI SYLLABLE ZZYP;Lo;0;L;;;;;N;;;;; +A2EB;YI SYLLABLE ZZYRX;Lo;0;L;;;;;N;;;;; +A2EC;YI SYLLABLE ZZYR;Lo;0;L;;;;;N;;;;; +A2ED;YI SYLLABLE NZIT;Lo;0;L;;;;;N;;;;; +A2EE;YI SYLLABLE NZIX;Lo;0;L;;;;;N;;;;; +A2EF;YI SYLLABLE NZI;Lo;0;L;;;;;N;;;;; +A2F0;YI SYLLABLE NZIP;Lo;0;L;;;;;N;;;;; +A2F1;YI SYLLABLE NZIEX;Lo;0;L;;;;;N;;;;; +A2F2;YI SYLLABLE NZIE;Lo;0;L;;;;;N;;;;; +A2F3;YI SYLLABLE NZIEP;Lo;0;L;;;;;N;;;;; +A2F4;YI SYLLABLE NZAT;Lo;0;L;;;;;N;;;;; +A2F5;YI SYLLABLE NZAX;Lo;0;L;;;;;N;;;;; +A2F6;YI SYLLABLE NZA;Lo;0;L;;;;;N;;;;; +A2F7;YI SYLLABLE NZAP;Lo;0;L;;;;;N;;;;; +A2F8;YI SYLLABLE NZUOX;Lo;0;L;;;;;N;;;;; +A2F9;YI SYLLABLE NZUO;Lo;0;L;;;;;N;;;;; +A2FA;YI SYLLABLE NZOX;Lo;0;L;;;;;N;;;;; +A2FB;YI SYLLABLE NZOP;Lo;0;L;;;;;N;;;;; +A2FC;YI SYLLABLE NZEX;Lo;0;L;;;;;N;;;;; +A2FD;YI SYLLABLE NZE;Lo;0;L;;;;;N;;;;; +A2FE;YI SYLLABLE NZUX;Lo;0;L;;;;;N;;;;; +A2FF;YI SYLLABLE NZU;Lo;0;L;;;;;N;;;;; +A300;YI SYLLABLE NZUP;Lo;0;L;;;;;N;;;;; +A301;YI SYLLABLE NZURX;Lo;0;L;;;;;N;;;;; +A302;YI SYLLABLE NZUR;Lo;0;L;;;;;N;;;;; +A303;YI SYLLABLE NZYT;Lo;0;L;;;;;N;;;;; +A304;YI SYLLABLE NZYX;Lo;0;L;;;;;N;;;;; +A305;YI SYLLABLE NZY;Lo;0;L;;;;;N;;;;; +A306;YI SYLLABLE NZYP;Lo;0;L;;;;;N;;;;; +A307;YI SYLLABLE NZYRX;Lo;0;L;;;;;N;;;;; +A308;YI SYLLABLE NZYR;Lo;0;L;;;;;N;;;;; +A309;YI SYLLABLE SIT;Lo;0;L;;;;;N;;;;; +A30A;YI SYLLABLE SIX;Lo;0;L;;;;;N;;;;; +A30B;YI SYLLABLE SI;Lo;0;L;;;;;N;;;;; +A30C;YI SYLLABLE SIP;Lo;0;L;;;;;N;;;;; +A30D;YI SYLLABLE SIEX;Lo;0;L;;;;;N;;;;; +A30E;YI SYLLABLE SIE;Lo;0;L;;;;;N;;;;; +A30F;YI SYLLABLE SIEP;Lo;0;L;;;;;N;;;;; +A310;YI SYLLABLE SAT;Lo;0;L;;;;;N;;;;; +A311;YI SYLLABLE SAX;Lo;0;L;;;;;N;;;;; +A312;YI SYLLABLE SA;Lo;0;L;;;;;N;;;;; +A313;YI SYLLABLE SAP;Lo;0;L;;;;;N;;;;; +A314;YI SYLLABLE SUOX;Lo;0;L;;;;;N;;;;; +A315;YI SYLLABLE SUO;Lo;0;L;;;;;N;;;;; +A316;YI SYLLABLE SUOP;Lo;0;L;;;;;N;;;;; +A317;YI SYLLABLE SOT;Lo;0;L;;;;;N;;;;; +A318;YI SYLLABLE SOX;Lo;0;L;;;;;N;;;;; +A319;YI SYLLABLE SO;Lo;0;L;;;;;N;;;;; +A31A;YI SYLLABLE SOP;Lo;0;L;;;;;N;;;;; +A31B;YI SYLLABLE SEX;Lo;0;L;;;;;N;;;;; +A31C;YI SYLLABLE SE;Lo;0;L;;;;;N;;;;; +A31D;YI SYLLABLE SEP;Lo;0;L;;;;;N;;;;; +A31E;YI SYLLABLE SUT;Lo;0;L;;;;;N;;;;; +A31F;YI SYLLABLE SUX;Lo;0;L;;;;;N;;;;; +A320;YI SYLLABLE SU;Lo;0;L;;;;;N;;;;; +A321;YI SYLLABLE SUP;Lo;0;L;;;;;N;;;;; +A322;YI SYLLABLE SURX;Lo;0;L;;;;;N;;;;; +A323;YI SYLLABLE SUR;Lo;0;L;;;;;N;;;;; +A324;YI SYLLABLE SYT;Lo;0;L;;;;;N;;;;; +A325;YI SYLLABLE SYX;Lo;0;L;;;;;N;;;;; +A326;YI SYLLABLE SY;Lo;0;L;;;;;N;;;;; +A327;YI SYLLABLE SYP;Lo;0;L;;;;;N;;;;; +A328;YI SYLLABLE SYRX;Lo;0;L;;;;;N;;;;; +A329;YI SYLLABLE SYR;Lo;0;L;;;;;N;;;;; +A32A;YI SYLLABLE SSIT;Lo;0;L;;;;;N;;;;; +A32B;YI SYLLABLE SSIX;Lo;0;L;;;;;N;;;;; +A32C;YI SYLLABLE SSI;Lo;0;L;;;;;N;;;;; +A32D;YI SYLLABLE SSIP;Lo;0;L;;;;;N;;;;; +A32E;YI SYLLABLE SSIEX;Lo;0;L;;;;;N;;;;; +A32F;YI SYLLABLE SSIE;Lo;0;L;;;;;N;;;;; +A330;YI SYLLABLE SSIEP;Lo;0;L;;;;;N;;;;; +A331;YI SYLLABLE SSAT;Lo;0;L;;;;;N;;;;; +A332;YI SYLLABLE SSAX;Lo;0;L;;;;;N;;;;; +A333;YI SYLLABLE SSA;Lo;0;L;;;;;N;;;;; +A334;YI SYLLABLE SSAP;Lo;0;L;;;;;N;;;;; +A335;YI SYLLABLE SSOT;Lo;0;L;;;;;N;;;;; +A336;YI SYLLABLE SSOX;Lo;0;L;;;;;N;;;;; +A337;YI SYLLABLE SSO;Lo;0;L;;;;;N;;;;; +A338;YI SYLLABLE SSOP;Lo;0;L;;;;;N;;;;; +A339;YI SYLLABLE SSEX;Lo;0;L;;;;;N;;;;; +A33A;YI SYLLABLE SSE;Lo;0;L;;;;;N;;;;; +A33B;YI SYLLABLE SSEP;Lo;0;L;;;;;N;;;;; +A33C;YI SYLLABLE SSUT;Lo;0;L;;;;;N;;;;; +A33D;YI SYLLABLE SSUX;Lo;0;L;;;;;N;;;;; +A33E;YI SYLLABLE SSU;Lo;0;L;;;;;N;;;;; +A33F;YI SYLLABLE SSUP;Lo;0;L;;;;;N;;;;; +A340;YI SYLLABLE SSYT;Lo;0;L;;;;;N;;;;; +A341;YI SYLLABLE SSYX;Lo;0;L;;;;;N;;;;; +A342;YI SYLLABLE SSY;Lo;0;L;;;;;N;;;;; +A343;YI SYLLABLE SSYP;Lo;0;L;;;;;N;;;;; +A344;YI SYLLABLE SSYRX;Lo;0;L;;;;;N;;;;; +A345;YI SYLLABLE SSYR;Lo;0;L;;;;;N;;;;; +A346;YI SYLLABLE ZHAT;Lo;0;L;;;;;N;;;;; +A347;YI SYLLABLE ZHAX;Lo;0;L;;;;;N;;;;; +A348;YI SYLLABLE ZHA;Lo;0;L;;;;;N;;;;; +A349;YI SYLLABLE ZHAP;Lo;0;L;;;;;N;;;;; +A34A;YI SYLLABLE ZHUOX;Lo;0;L;;;;;N;;;;; +A34B;YI SYLLABLE ZHUO;Lo;0;L;;;;;N;;;;; +A34C;YI SYLLABLE ZHUOP;Lo;0;L;;;;;N;;;;; +A34D;YI SYLLABLE ZHOT;Lo;0;L;;;;;N;;;;; +A34E;YI SYLLABLE ZHOX;Lo;0;L;;;;;N;;;;; +A34F;YI SYLLABLE ZHO;Lo;0;L;;;;;N;;;;; +A350;YI SYLLABLE ZHOP;Lo;0;L;;;;;N;;;;; +A351;YI SYLLABLE ZHET;Lo;0;L;;;;;N;;;;; +A352;YI SYLLABLE ZHEX;Lo;0;L;;;;;N;;;;; +A353;YI SYLLABLE ZHE;Lo;0;L;;;;;N;;;;; +A354;YI SYLLABLE ZHEP;Lo;0;L;;;;;N;;;;; +A355;YI SYLLABLE ZHUT;Lo;0;L;;;;;N;;;;; +A356;YI SYLLABLE ZHUX;Lo;0;L;;;;;N;;;;; +A357;YI SYLLABLE ZHU;Lo;0;L;;;;;N;;;;; +A358;YI SYLLABLE ZHUP;Lo;0;L;;;;;N;;;;; +A359;YI SYLLABLE ZHURX;Lo;0;L;;;;;N;;;;; +A35A;YI SYLLABLE ZHUR;Lo;0;L;;;;;N;;;;; +A35B;YI SYLLABLE ZHYT;Lo;0;L;;;;;N;;;;; +A35C;YI SYLLABLE ZHYX;Lo;0;L;;;;;N;;;;; +A35D;YI SYLLABLE ZHY;Lo;0;L;;;;;N;;;;; +A35E;YI SYLLABLE ZHYP;Lo;0;L;;;;;N;;;;; +A35F;YI SYLLABLE ZHYRX;Lo;0;L;;;;;N;;;;; +A360;YI SYLLABLE ZHYR;Lo;0;L;;;;;N;;;;; +A361;YI SYLLABLE CHAT;Lo;0;L;;;;;N;;;;; +A362;YI SYLLABLE CHAX;Lo;0;L;;;;;N;;;;; +A363;YI SYLLABLE CHA;Lo;0;L;;;;;N;;;;; +A364;YI SYLLABLE CHAP;Lo;0;L;;;;;N;;;;; +A365;YI SYLLABLE CHUOT;Lo;0;L;;;;;N;;;;; +A366;YI SYLLABLE CHUOX;Lo;0;L;;;;;N;;;;; +A367;YI SYLLABLE CHUO;Lo;0;L;;;;;N;;;;; +A368;YI SYLLABLE CHUOP;Lo;0;L;;;;;N;;;;; +A369;YI SYLLABLE CHOT;Lo;0;L;;;;;N;;;;; +A36A;YI SYLLABLE CHOX;Lo;0;L;;;;;N;;;;; +A36B;YI SYLLABLE CHO;Lo;0;L;;;;;N;;;;; +A36C;YI SYLLABLE CHOP;Lo;0;L;;;;;N;;;;; +A36D;YI SYLLABLE CHET;Lo;0;L;;;;;N;;;;; +A36E;YI SYLLABLE CHEX;Lo;0;L;;;;;N;;;;; +A36F;YI SYLLABLE CHE;Lo;0;L;;;;;N;;;;; +A370;YI SYLLABLE CHEP;Lo;0;L;;;;;N;;;;; +A371;YI SYLLABLE CHUX;Lo;0;L;;;;;N;;;;; +A372;YI SYLLABLE CHU;Lo;0;L;;;;;N;;;;; +A373;YI SYLLABLE CHUP;Lo;0;L;;;;;N;;;;; +A374;YI SYLLABLE CHURX;Lo;0;L;;;;;N;;;;; +A375;YI SYLLABLE CHUR;Lo;0;L;;;;;N;;;;; +A376;YI SYLLABLE CHYT;Lo;0;L;;;;;N;;;;; +A377;YI SYLLABLE CHYX;Lo;0;L;;;;;N;;;;; +A378;YI SYLLABLE CHY;Lo;0;L;;;;;N;;;;; +A379;YI SYLLABLE CHYP;Lo;0;L;;;;;N;;;;; +A37A;YI SYLLABLE CHYRX;Lo;0;L;;;;;N;;;;; +A37B;YI SYLLABLE CHYR;Lo;0;L;;;;;N;;;;; +A37C;YI SYLLABLE RRAX;Lo;0;L;;;;;N;;;;; +A37D;YI SYLLABLE RRA;Lo;0;L;;;;;N;;;;; +A37E;YI SYLLABLE RRUOX;Lo;0;L;;;;;N;;;;; +A37F;YI SYLLABLE RRUO;Lo;0;L;;;;;N;;;;; +A380;YI SYLLABLE RROT;Lo;0;L;;;;;N;;;;; +A381;YI SYLLABLE RROX;Lo;0;L;;;;;N;;;;; +A382;YI SYLLABLE RRO;Lo;0;L;;;;;N;;;;; +A383;YI SYLLABLE RROP;Lo;0;L;;;;;N;;;;; +A384;YI SYLLABLE RRET;Lo;0;L;;;;;N;;;;; +A385;YI SYLLABLE RREX;Lo;0;L;;;;;N;;;;; +A386;YI SYLLABLE RRE;Lo;0;L;;;;;N;;;;; +A387;YI SYLLABLE RREP;Lo;0;L;;;;;N;;;;; +A388;YI SYLLABLE RRUT;Lo;0;L;;;;;N;;;;; +A389;YI SYLLABLE RRUX;Lo;0;L;;;;;N;;;;; +A38A;YI SYLLABLE RRU;Lo;0;L;;;;;N;;;;; +A38B;YI SYLLABLE RRUP;Lo;0;L;;;;;N;;;;; +A38C;YI SYLLABLE RRURX;Lo;0;L;;;;;N;;;;; +A38D;YI SYLLABLE RRUR;Lo;0;L;;;;;N;;;;; +A38E;YI SYLLABLE RRYT;Lo;0;L;;;;;N;;;;; +A38F;YI SYLLABLE RRYX;Lo;0;L;;;;;N;;;;; +A390;YI SYLLABLE RRY;Lo;0;L;;;;;N;;;;; +A391;YI SYLLABLE RRYP;Lo;0;L;;;;;N;;;;; +A392;YI SYLLABLE RRYRX;Lo;0;L;;;;;N;;;;; +A393;YI SYLLABLE RRYR;Lo;0;L;;;;;N;;;;; +A394;YI SYLLABLE NRAT;Lo;0;L;;;;;N;;;;; +A395;YI SYLLABLE NRAX;Lo;0;L;;;;;N;;;;; +A396;YI SYLLABLE NRA;Lo;0;L;;;;;N;;;;; +A397;YI SYLLABLE NRAP;Lo;0;L;;;;;N;;;;; +A398;YI SYLLABLE NROX;Lo;0;L;;;;;N;;;;; +A399;YI SYLLABLE NRO;Lo;0;L;;;;;N;;;;; +A39A;YI SYLLABLE NROP;Lo;0;L;;;;;N;;;;; +A39B;YI SYLLABLE NRET;Lo;0;L;;;;;N;;;;; +A39C;YI SYLLABLE NREX;Lo;0;L;;;;;N;;;;; +A39D;YI SYLLABLE NRE;Lo;0;L;;;;;N;;;;; +A39E;YI SYLLABLE NREP;Lo;0;L;;;;;N;;;;; +A39F;YI SYLLABLE NRUT;Lo;0;L;;;;;N;;;;; +A3A0;YI SYLLABLE NRUX;Lo;0;L;;;;;N;;;;; +A3A1;YI SYLLABLE NRU;Lo;0;L;;;;;N;;;;; +A3A2;YI SYLLABLE NRUP;Lo;0;L;;;;;N;;;;; +A3A3;YI SYLLABLE NRURX;Lo;0;L;;;;;N;;;;; +A3A4;YI SYLLABLE NRUR;Lo;0;L;;;;;N;;;;; +A3A5;YI SYLLABLE NRYT;Lo;0;L;;;;;N;;;;; +A3A6;YI SYLLABLE NRYX;Lo;0;L;;;;;N;;;;; +A3A7;YI SYLLABLE NRY;Lo;0;L;;;;;N;;;;; +A3A8;YI SYLLABLE NRYP;Lo;0;L;;;;;N;;;;; +A3A9;YI SYLLABLE NRYRX;Lo;0;L;;;;;N;;;;; +A3AA;YI SYLLABLE NRYR;Lo;0;L;;;;;N;;;;; +A3AB;YI SYLLABLE SHAT;Lo;0;L;;;;;N;;;;; +A3AC;YI SYLLABLE SHAX;Lo;0;L;;;;;N;;;;; +A3AD;YI SYLLABLE SHA;Lo;0;L;;;;;N;;;;; +A3AE;YI SYLLABLE SHAP;Lo;0;L;;;;;N;;;;; +A3AF;YI SYLLABLE SHUOX;Lo;0;L;;;;;N;;;;; +A3B0;YI SYLLABLE SHUO;Lo;0;L;;;;;N;;;;; +A3B1;YI SYLLABLE SHUOP;Lo;0;L;;;;;N;;;;; +A3B2;YI SYLLABLE SHOT;Lo;0;L;;;;;N;;;;; +A3B3;YI SYLLABLE SHOX;Lo;0;L;;;;;N;;;;; +A3B4;YI SYLLABLE SHO;Lo;0;L;;;;;N;;;;; +A3B5;YI SYLLABLE SHOP;Lo;0;L;;;;;N;;;;; +A3B6;YI SYLLABLE SHET;Lo;0;L;;;;;N;;;;; +A3B7;YI SYLLABLE SHEX;Lo;0;L;;;;;N;;;;; +A3B8;YI SYLLABLE SHE;Lo;0;L;;;;;N;;;;; +A3B9;YI SYLLABLE SHEP;Lo;0;L;;;;;N;;;;; +A3BA;YI SYLLABLE SHUT;Lo;0;L;;;;;N;;;;; +A3BB;YI SYLLABLE SHUX;Lo;0;L;;;;;N;;;;; +A3BC;YI SYLLABLE SHU;Lo;0;L;;;;;N;;;;; +A3BD;YI SYLLABLE SHUP;Lo;0;L;;;;;N;;;;; +A3BE;YI SYLLABLE SHURX;Lo;0;L;;;;;N;;;;; +A3BF;YI SYLLABLE SHUR;Lo;0;L;;;;;N;;;;; +A3C0;YI SYLLABLE SHYT;Lo;0;L;;;;;N;;;;; +A3C1;YI SYLLABLE SHYX;Lo;0;L;;;;;N;;;;; +A3C2;YI SYLLABLE SHY;Lo;0;L;;;;;N;;;;; +A3C3;YI SYLLABLE SHYP;Lo;0;L;;;;;N;;;;; +A3C4;YI SYLLABLE SHYRX;Lo;0;L;;;;;N;;;;; +A3C5;YI SYLLABLE SHYR;Lo;0;L;;;;;N;;;;; +A3C6;YI SYLLABLE RAT;Lo;0;L;;;;;N;;;;; +A3C7;YI SYLLABLE RAX;Lo;0;L;;;;;N;;;;; +A3C8;YI SYLLABLE RA;Lo;0;L;;;;;N;;;;; +A3C9;YI SYLLABLE RAP;Lo;0;L;;;;;N;;;;; +A3CA;YI SYLLABLE RUOX;Lo;0;L;;;;;N;;;;; +A3CB;YI SYLLABLE RUO;Lo;0;L;;;;;N;;;;; +A3CC;YI SYLLABLE RUOP;Lo;0;L;;;;;N;;;;; +A3CD;YI SYLLABLE ROT;Lo;0;L;;;;;N;;;;; +A3CE;YI SYLLABLE ROX;Lo;0;L;;;;;N;;;;; +A3CF;YI SYLLABLE RO;Lo;0;L;;;;;N;;;;; +A3D0;YI SYLLABLE ROP;Lo;0;L;;;;;N;;;;; +A3D1;YI SYLLABLE REX;Lo;0;L;;;;;N;;;;; +A3D2;YI SYLLABLE RE;Lo;0;L;;;;;N;;;;; +A3D3;YI SYLLABLE REP;Lo;0;L;;;;;N;;;;; +A3D4;YI SYLLABLE RUT;Lo;0;L;;;;;N;;;;; +A3D5;YI SYLLABLE RUX;Lo;0;L;;;;;N;;;;; +A3D6;YI SYLLABLE RU;Lo;0;L;;;;;N;;;;; +A3D7;YI SYLLABLE RUP;Lo;0;L;;;;;N;;;;; +A3D8;YI SYLLABLE RURX;Lo;0;L;;;;;N;;;;; +A3D9;YI SYLLABLE RUR;Lo;0;L;;;;;N;;;;; +A3DA;YI SYLLABLE RYT;Lo;0;L;;;;;N;;;;; +A3DB;YI SYLLABLE RYX;Lo;0;L;;;;;N;;;;; +A3DC;YI SYLLABLE RY;Lo;0;L;;;;;N;;;;; +A3DD;YI SYLLABLE RYP;Lo;0;L;;;;;N;;;;; +A3DE;YI SYLLABLE RYRX;Lo;0;L;;;;;N;;;;; +A3DF;YI SYLLABLE RYR;Lo;0;L;;;;;N;;;;; +A3E0;YI SYLLABLE JIT;Lo;0;L;;;;;N;;;;; +A3E1;YI SYLLABLE JIX;Lo;0;L;;;;;N;;;;; +A3E2;YI SYLLABLE JI;Lo;0;L;;;;;N;;;;; +A3E3;YI SYLLABLE JIP;Lo;0;L;;;;;N;;;;; +A3E4;YI SYLLABLE JIET;Lo;0;L;;;;;N;;;;; +A3E5;YI SYLLABLE JIEX;Lo;0;L;;;;;N;;;;; +A3E6;YI SYLLABLE JIE;Lo;0;L;;;;;N;;;;; +A3E7;YI SYLLABLE JIEP;Lo;0;L;;;;;N;;;;; +A3E8;YI SYLLABLE JUOT;Lo;0;L;;;;;N;;;;; +A3E9;YI SYLLABLE JUOX;Lo;0;L;;;;;N;;;;; +A3EA;YI SYLLABLE JUO;Lo;0;L;;;;;N;;;;; +A3EB;YI SYLLABLE JUOP;Lo;0;L;;;;;N;;;;; +A3EC;YI SYLLABLE JOT;Lo;0;L;;;;;N;;;;; +A3ED;YI SYLLABLE JOX;Lo;0;L;;;;;N;;;;; +A3EE;YI SYLLABLE JO;Lo;0;L;;;;;N;;;;; +A3EF;YI SYLLABLE JOP;Lo;0;L;;;;;N;;;;; +A3F0;YI SYLLABLE JUT;Lo;0;L;;;;;N;;;;; +A3F1;YI SYLLABLE JUX;Lo;0;L;;;;;N;;;;; +A3F2;YI SYLLABLE JU;Lo;0;L;;;;;N;;;;; +A3F3;YI SYLLABLE JUP;Lo;0;L;;;;;N;;;;; +A3F4;YI SYLLABLE JURX;Lo;0;L;;;;;N;;;;; +A3F5;YI SYLLABLE JUR;Lo;0;L;;;;;N;;;;; +A3F6;YI SYLLABLE JYT;Lo;0;L;;;;;N;;;;; +A3F7;YI SYLLABLE JYX;Lo;0;L;;;;;N;;;;; +A3F8;YI SYLLABLE JY;Lo;0;L;;;;;N;;;;; +A3F9;YI SYLLABLE JYP;Lo;0;L;;;;;N;;;;; +A3FA;YI SYLLABLE JYRX;Lo;0;L;;;;;N;;;;; +A3FB;YI SYLLABLE JYR;Lo;0;L;;;;;N;;;;; +A3FC;YI SYLLABLE QIT;Lo;0;L;;;;;N;;;;; +A3FD;YI SYLLABLE QIX;Lo;0;L;;;;;N;;;;; +A3FE;YI SYLLABLE QI;Lo;0;L;;;;;N;;;;; +A3FF;YI SYLLABLE QIP;Lo;0;L;;;;;N;;;;; +A400;YI SYLLABLE QIET;Lo;0;L;;;;;N;;;;; +A401;YI SYLLABLE QIEX;Lo;0;L;;;;;N;;;;; +A402;YI SYLLABLE QIE;Lo;0;L;;;;;N;;;;; +A403;YI SYLLABLE QIEP;Lo;0;L;;;;;N;;;;; +A404;YI SYLLABLE QUOT;Lo;0;L;;;;;N;;;;; +A405;YI SYLLABLE QUOX;Lo;0;L;;;;;N;;;;; +A406;YI SYLLABLE QUO;Lo;0;L;;;;;N;;;;; +A407;YI SYLLABLE QUOP;Lo;0;L;;;;;N;;;;; +A408;YI SYLLABLE QOT;Lo;0;L;;;;;N;;;;; +A409;YI SYLLABLE QOX;Lo;0;L;;;;;N;;;;; +A40A;YI SYLLABLE QO;Lo;0;L;;;;;N;;;;; +A40B;YI SYLLABLE QOP;Lo;0;L;;;;;N;;;;; +A40C;YI SYLLABLE QUT;Lo;0;L;;;;;N;;;;; +A40D;YI SYLLABLE QUX;Lo;0;L;;;;;N;;;;; +A40E;YI SYLLABLE QU;Lo;0;L;;;;;N;;;;; +A40F;YI SYLLABLE QUP;Lo;0;L;;;;;N;;;;; +A410;YI SYLLABLE QURX;Lo;0;L;;;;;N;;;;; +A411;YI SYLLABLE QUR;Lo;0;L;;;;;N;;;;; +A412;YI SYLLABLE QYT;Lo;0;L;;;;;N;;;;; +A413;YI SYLLABLE QYX;Lo;0;L;;;;;N;;;;; +A414;YI SYLLABLE QY;Lo;0;L;;;;;N;;;;; +A415;YI SYLLABLE QYP;Lo;0;L;;;;;N;;;;; +A416;YI SYLLABLE QYRX;Lo;0;L;;;;;N;;;;; +A417;YI SYLLABLE QYR;Lo;0;L;;;;;N;;;;; +A418;YI SYLLABLE JJIT;Lo;0;L;;;;;N;;;;; +A419;YI SYLLABLE JJIX;Lo;0;L;;;;;N;;;;; +A41A;YI SYLLABLE JJI;Lo;0;L;;;;;N;;;;; +A41B;YI SYLLABLE JJIP;Lo;0;L;;;;;N;;;;; +A41C;YI SYLLABLE JJIET;Lo;0;L;;;;;N;;;;; +A41D;YI SYLLABLE JJIEX;Lo;0;L;;;;;N;;;;; +A41E;YI SYLLABLE JJIE;Lo;0;L;;;;;N;;;;; +A41F;YI SYLLABLE JJIEP;Lo;0;L;;;;;N;;;;; +A420;YI SYLLABLE JJUOX;Lo;0;L;;;;;N;;;;; +A421;YI SYLLABLE JJUO;Lo;0;L;;;;;N;;;;; +A422;YI SYLLABLE JJUOP;Lo;0;L;;;;;N;;;;; +A423;YI SYLLABLE JJOT;Lo;0;L;;;;;N;;;;; +A424;YI SYLLABLE JJOX;Lo;0;L;;;;;N;;;;; +A425;YI SYLLABLE JJO;Lo;0;L;;;;;N;;;;; +A426;YI SYLLABLE JJOP;Lo;0;L;;;;;N;;;;; +A427;YI SYLLABLE JJUT;Lo;0;L;;;;;N;;;;; +A428;YI SYLLABLE JJUX;Lo;0;L;;;;;N;;;;; +A429;YI SYLLABLE JJU;Lo;0;L;;;;;N;;;;; +A42A;YI SYLLABLE JJUP;Lo;0;L;;;;;N;;;;; +A42B;YI SYLLABLE JJURX;Lo;0;L;;;;;N;;;;; +A42C;YI SYLLABLE JJUR;Lo;0;L;;;;;N;;;;; +A42D;YI SYLLABLE JJYT;Lo;0;L;;;;;N;;;;; +A42E;YI SYLLABLE JJYX;Lo;0;L;;;;;N;;;;; +A42F;YI SYLLABLE JJY;Lo;0;L;;;;;N;;;;; +A430;YI SYLLABLE JJYP;Lo;0;L;;;;;N;;;;; +A431;YI SYLLABLE NJIT;Lo;0;L;;;;;N;;;;; +A432;YI SYLLABLE NJIX;Lo;0;L;;;;;N;;;;; +A433;YI SYLLABLE NJI;Lo;0;L;;;;;N;;;;; +A434;YI SYLLABLE NJIP;Lo;0;L;;;;;N;;;;; +A435;YI SYLLABLE NJIET;Lo;0;L;;;;;N;;;;; +A436;YI SYLLABLE NJIEX;Lo;0;L;;;;;N;;;;; +A437;YI SYLLABLE NJIE;Lo;0;L;;;;;N;;;;; +A438;YI SYLLABLE NJIEP;Lo;0;L;;;;;N;;;;; +A439;YI SYLLABLE NJUOX;Lo;0;L;;;;;N;;;;; +A43A;YI SYLLABLE NJUO;Lo;0;L;;;;;N;;;;; +A43B;YI SYLLABLE NJOT;Lo;0;L;;;;;N;;;;; +A43C;YI SYLLABLE NJOX;Lo;0;L;;;;;N;;;;; +A43D;YI SYLLABLE NJO;Lo;0;L;;;;;N;;;;; +A43E;YI SYLLABLE NJOP;Lo;0;L;;;;;N;;;;; +A43F;YI SYLLABLE NJUX;Lo;0;L;;;;;N;;;;; +A440;YI SYLLABLE NJU;Lo;0;L;;;;;N;;;;; +A441;YI SYLLABLE NJUP;Lo;0;L;;;;;N;;;;; +A442;YI SYLLABLE NJURX;Lo;0;L;;;;;N;;;;; +A443;YI SYLLABLE NJUR;Lo;0;L;;;;;N;;;;; +A444;YI SYLLABLE NJYT;Lo;0;L;;;;;N;;;;; +A445;YI SYLLABLE NJYX;Lo;0;L;;;;;N;;;;; +A446;YI SYLLABLE NJY;Lo;0;L;;;;;N;;;;; +A447;YI SYLLABLE NJYP;Lo;0;L;;;;;N;;;;; +A448;YI SYLLABLE NJYRX;Lo;0;L;;;;;N;;;;; +A449;YI SYLLABLE NJYR;Lo;0;L;;;;;N;;;;; +A44A;YI SYLLABLE NYIT;Lo;0;L;;;;;N;;;;; +A44B;YI SYLLABLE NYIX;Lo;0;L;;;;;N;;;;; +A44C;YI SYLLABLE NYI;Lo;0;L;;;;;N;;;;; +A44D;YI SYLLABLE NYIP;Lo;0;L;;;;;N;;;;; +A44E;YI SYLLABLE NYIET;Lo;0;L;;;;;N;;;;; +A44F;YI SYLLABLE NYIEX;Lo;0;L;;;;;N;;;;; +A450;YI SYLLABLE NYIE;Lo;0;L;;;;;N;;;;; +A451;YI SYLLABLE NYIEP;Lo;0;L;;;;;N;;;;; +A452;YI SYLLABLE NYUOX;Lo;0;L;;;;;N;;;;; +A453;YI SYLLABLE NYUO;Lo;0;L;;;;;N;;;;; +A454;YI SYLLABLE NYUOP;Lo;0;L;;;;;N;;;;; +A455;YI SYLLABLE NYOT;Lo;0;L;;;;;N;;;;; +A456;YI SYLLABLE NYOX;Lo;0;L;;;;;N;;;;; +A457;YI SYLLABLE NYO;Lo;0;L;;;;;N;;;;; +A458;YI SYLLABLE NYOP;Lo;0;L;;;;;N;;;;; +A459;YI SYLLABLE NYUT;Lo;0;L;;;;;N;;;;; +A45A;YI SYLLABLE NYUX;Lo;0;L;;;;;N;;;;; +A45B;YI SYLLABLE NYU;Lo;0;L;;;;;N;;;;; +A45C;YI SYLLABLE NYUP;Lo;0;L;;;;;N;;;;; +A45D;YI SYLLABLE XIT;Lo;0;L;;;;;N;;;;; +A45E;YI SYLLABLE XIX;Lo;0;L;;;;;N;;;;; +A45F;YI SYLLABLE XI;Lo;0;L;;;;;N;;;;; +A460;YI SYLLABLE XIP;Lo;0;L;;;;;N;;;;; +A461;YI SYLLABLE XIET;Lo;0;L;;;;;N;;;;; +A462;YI SYLLABLE XIEX;Lo;0;L;;;;;N;;;;; +A463;YI SYLLABLE XIE;Lo;0;L;;;;;N;;;;; +A464;YI SYLLABLE XIEP;Lo;0;L;;;;;N;;;;; +A465;YI SYLLABLE XUOX;Lo;0;L;;;;;N;;;;; +A466;YI SYLLABLE XUO;Lo;0;L;;;;;N;;;;; +A467;YI SYLLABLE XOT;Lo;0;L;;;;;N;;;;; +A468;YI SYLLABLE XOX;Lo;0;L;;;;;N;;;;; +A469;YI SYLLABLE XO;Lo;0;L;;;;;N;;;;; +A46A;YI SYLLABLE XOP;Lo;0;L;;;;;N;;;;; +A46B;YI SYLLABLE XYT;Lo;0;L;;;;;N;;;;; +A46C;YI SYLLABLE XYX;Lo;0;L;;;;;N;;;;; +A46D;YI SYLLABLE XY;Lo;0;L;;;;;N;;;;; +A46E;YI SYLLABLE XYP;Lo;0;L;;;;;N;;;;; +A46F;YI SYLLABLE XYRX;Lo;0;L;;;;;N;;;;; +A470;YI SYLLABLE XYR;Lo;0;L;;;;;N;;;;; +A471;YI SYLLABLE YIT;Lo;0;L;;;;;N;;;;; +A472;YI SYLLABLE YIX;Lo;0;L;;;;;N;;;;; +A473;YI SYLLABLE YI;Lo;0;L;;;;;N;;;;; +A474;YI SYLLABLE YIP;Lo;0;L;;;;;N;;;;; +A475;YI SYLLABLE YIET;Lo;0;L;;;;;N;;;;; +A476;YI SYLLABLE YIEX;Lo;0;L;;;;;N;;;;; +A477;YI SYLLABLE YIE;Lo;0;L;;;;;N;;;;; +A478;YI SYLLABLE YIEP;Lo;0;L;;;;;N;;;;; +A479;YI SYLLABLE YUOT;Lo;0;L;;;;;N;;;;; +A47A;YI SYLLABLE YUOX;Lo;0;L;;;;;N;;;;; +A47B;YI SYLLABLE YUO;Lo;0;L;;;;;N;;;;; +A47C;YI SYLLABLE YUOP;Lo;0;L;;;;;N;;;;; +A47D;YI SYLLABLE YOT;Lo;0;L;;;;;N;;;;; +A47E;YI SYLLABLE YOX;Lo;0;L;;;;;N;;;;; +A47F;YI SYLLABLE YO;Lo;0;L;;;;;N;;;;; +A480;YI SYLLABLE YOP;Lo;0;L;;;;;N;;;;; +A481;YI SYLLABLE YUT;Lo;0;L;;;;;N;;;;; +A482;YI SYLLABLE YUX;Lo;0;L;;;;;N;;;;; +A483;YI SYLLABLE YU;Lo;0;L;;;;;N;;;;; +A484;YI SYLLABLE YUP;Lo;0;L;;;;;N;;;;; +A485;YI SYLLABLE YURX;Lo;0;L;;;;;N;;;;; +A486;YI SYLLABLE YUR;Lo;0;L;;;;;N;;;;; +A487;YI SYLLABLE YYT;Lo;0;L;;;;;N;;;;; +A488;YI SYLLABLE YYX;Lo;0;L;;;;;N;;;;; +A489;YI SYLLABLE YY;Lo;0;L;;;;;N;;;;; +A48A;YI SYLLABLE YYP;Lo;0;L;;;;;N;;;;; +A48B;YI SYLLABLE YYRX;Lo;0;L;;;;;N;;;;; +A48C;YI SYLLABLE YYR;Lo;0;L;;;;;N;;;;; +A490;YI RADICAL QOT;So;0;ON;;;;;N;;;;; +A491;YI RADICAL LI;So;0;ON;;;;;N;;;;; +A492;YI RADICAL KIT;So;0;ON;;;;;N;;;;; +A493;YI RADICAL NYIP;So;0;ON;;;;;N;;;;; +A494;YI RADICAL CYP;So;0;ON;;;;;N;;;;; +A495;YI RADICAL SSI;So;0;ON;;;;;N;;;;; +A496;YI RADICAL GGOP;So;0;ON;;;;;N;;;;; +A497;YI RADICAL GEP;So;0;ON;;;;;N;;;;; +A498;YI RADICAL MI;So;0;ON;;;;;N;;;;; +A499;YI RADICAL HXIT;So;0;ON;;;;;N;;;;; +A49A;YI RADICAL LYR;So;0;ON;;;;;N;;;;; +A49B;YI RADICAL BBUT;So;0;ON;;;;;N;;;;; +A49C;YI RADICAL MOP;So;0;ON;;;;;N;;;;; +A49D;YI RADICAL YO;So;0;ON;;;;;N;;;;; +A49E;YI RADICAL PUT;So;0;ON;;;;;N;;;;; +A49F;YI RADICAL HXUO;So;0;ON;;;;;N;;;;; +A4A0;YI RADICAL TAT;So;0;ON;;;;;N;;;;; +A4A1;YI RADICAL GA;So;0;ON;;;;;N;;;;; +A4A2;YI RADICAL ZUP;So;0;ON;;;;;N;;;;; +A4A3;YI RADICAL CYT;So;0;ON;;;;;N;;;;; +A4A4;YI RADICAL DDUR;So;0;ON;;;;;N;;;;; +A4A5;YI RADICAL BUR;So;0;ON;;;;;N;;;;; +A4A6;YI RADICAL GGUO;So;0;ON;;;;;N;;;;; +A4A7;YI RADICAL NYOP;So;0;ON;;;;;N;;;;; +A4A8;YI RADICAL TU;So;0;ON;;;;;N;;;;; +A4A9;YI RADICAL OP;So;0;ON;;;;;N;;;;; +A4AA;YI RADICAL JJUT;So;0;ON;;;;;N;;;;; +A4AB;YI RADICAL ZOT;So;0;ON;;;;;N;;;;; +A4AC;YI RADICAL PYT;So;0;ON;;;;;N;;;;; +A4AD;YI RADICAL HMO;So;0;ON;;;;;N;;;;; +A4AE;YI RADICAL YIT;So;0;ON;;;;;N;;;;; +A4AF;YI RADICAL VUR;So;0;ON;;;;;N;;;;; +A4B0;YI RADICAL SHY;So;0;ON;;;;;N;;;;; +A4B1;YI RADICAL VEP;So;0;ON;;;;;N;;;;; +A4B2;YI RADICAL ZA;So;0;ON;;;;;N;;;;; +A4B3;YI RADICAL JO;So;0;ON;;;;;N;;;;; +A4B4;YI RADICAL NZUP;So;0;ON;;;;;N;;;;; +A4B5;YI RADICAL JJY;So;0;ON;;;;;N;;;;; +A4B6;YI RADICAL GOT;So;0;ON;;;;;N;;;;; +A4B7;YI RADICAL JJIE;So;0;ON;;;;;N;;;;; +A4B8;YI RADICAL WO;So;0;ON;;;;;N;;;;; +A4B9;YI RADICAL DU;So;0;ON;;;;;N;;;;; +A4BA;YI RADICAL SHUR;So;0;ON;;;;;N;;;;; +A4BB;YI RADICAL LIE;So;0;ON;;;;;N;;;;; +A4BC;YI RADICAL CY;So;0;ON;;;;;N;;;;; +A4BD;YI RADICAL CUOP;So;0;ON;;;;;N;;;;; +A4BE;YI RADICAL CIP;So;0;ON;;;;;N;;;;; +A4BF;YI RADICAL HXOP;So;0;ON;;;;;N;;;;; +A4C0;YI RADICAL SHAT;So;0;ON;;;;;N;;;;; +A4C1;YI RADICAL ZUR;So;0;ON;;;;;N;;;;; +A4C2;YI RADICAL SHOP;So;0;ON;;;;;N;;;;; +A4C3;YI RADICAL CHE;So;0;ON;;;;;N;;;;; +A4C4;YI RADICAL ZZIET;So;0;ON;;;;;N;;;;; +A4C5;YI RADICAL NBIE;So;0;ON;;;;;N;;;;; +A4C6;YI RADICAL KE;So;0;ON;;;;;N;;;;; +A4D0;LISU LETTER BA;Lo;0;L;;;;;N;;;;; +A4D1;LISU LETTER PA;Lo;0;L;;;;;N;;;;; +A4D2;LISU LETTER PHA;Lo;0;L;;;;;N;;;;; +A4D3;LISU LETTER DA;Lo;0;L;;;;;N;;;;; +A4D4;LISU LETTER TA;Lo;0;L;;;;;N;;;;; +A4D5;LISU LETTER THA;Lo;0;L;;;;;N;;;;; +A4D6;LISU LETTER GA;Lo;0;L;;;;;N;;;;; +A4D7;LISU LETTER KA;Lo;0;L;;;;;N;;;;; +A4D8;LISU LETTER KHA;Lo;0;L;;;;;N;;;;; +A4D9;LISU LETTER JA;Lo;0;L;;;;;N;;;;; +A4DA;LISU LETTER CA;Lo;0;L;;;;;N;;;;; +A4DB;LISU LETTER CHA;Lo;0;L;;;;;N;;;;; +A4DC;LISU LETTER DZA;Lo;0;L;;;;;N;;;;; +A4DD;LISU LETTER TSA;Lo;0;L;;;;;N;;;;; +A4DE;LISU LETTER TSHA;Lo;0;L;;;;;N;;;;; +A4DF;LISU LETTER MA;Lo;0;L;;;;;N;;;;; +A4E0;LISU LETTER NA;Lo;0;L;;;;;N;;;;; +A4E1;LISU LETTER LA;Lo;0;L;;;;;N;;;;; +A4E2;LISU LETTER SA;Lo;0;L;;;;;N;;;;; +A4E3;LISU LETTER ZHA;Lo;0;L;;;;;N;;;;; +A4E4;LISU LETTER ZA;Lo;0;L;;;;;N;;;;; +A4E5;LISU LETTER NGA;Lo;0;L;;;;;N;;;;; +A4E6;LISU LETTER HA;Lo;0;L;;;;;N;;;;; +A4E7;LISU LETTER XA;Lo;0;L;;;;;N;;;;; +A4E8;LISU LETTER HHA;Lo;0;L;;;;;N;;;;; +A4E9;LISU LETTER FA;Lo;0;L;;;;;N;;;;; +A4EA;LISU LETTER WA;Lo;0;L;;;;;N;;;;; +A4EB;LISU LETTER SHA;Lo;0;L;;;;;N;;;;; +A4EC;LISU LETTER YA;Lo;0;L;;;;;N;;;;; +A4ED;LISU LETTER GHA;Lo;0;L;;;;;N;;;;; +A4EE;LISU LETTER A;Lo;0;L;;;;;N;;;;; +A4EF;LISU LETTER AE;Lo;0;L;;;;;N;;;;; +A4F0;LISU LETTER E;Lo;0;L;;;;;N;;;;; +A4F1;LISU LETTER EU;Lo;0;L;;;;;N;;;;; +A4F2;LISU LETTER I;Lo;0;L;;;;;N;;;;; +A4F3;LISU LETTER O;Lo;0;L;;;;;N;;;;; +A4F4;LISU LETTER U;Lo;0;L;;;;;N;;;;; +A4F5;LISU LETTER UE;Lo;0;L;;;;;N;;;;; +A4F6;LISU LETTER UH;Lo;0;L;;;;;N;;;;; +A4F7;LISU LETTER OE;Lo;0;L;;;;;N;;;;; +A4F8;LISU LETTER TONE MYA TI;Lm;0;L;;;;;N;;;;; +A4F9;LISU LETTER TONE NA PO;Lm;0;L;;;;;N;;;;; +A4FA;LISU LETTER TONE MYA CYA;Lm;0;L;;;;;N;;;;; +A4FB;LISU LETTER TONE MYA BO;Lm;0;L;;;;;N;;;;; +A4FC;LISU LETTER TONE MYA NA;Lm;0;L;;;;;N;;;;; +A4FD;LISU LETTER TONE MYA JEU;Lm;0;L;;;;;N;;;;; +A4FE;LISU PUNCTUATION COMMA;Po;0;L;;;;;N;;;;; +A4FF;LISU PUNCTUATION FULL STOP;Po;0;L;;;;;N;;;;; +A500;VAI SYLLABLE EE;Lo;0;L;;;;;N;;;;; +A501;VAI SYLLABLE EEN;Lo;0;L;;;;;N;;;;; +A502;VAI SYLLABLE HEE;Lo;0;L;;;;;N;;;;; +A503;VAI SYLLABLE WEE;Lo;0;L;;;;;N;;;;; +A504;VAI SYLLABLE WEEN;Lo;0;L;;;;;N;;;;; +A505;VAI SYLLABLE PEE;Lo;0;L;;;;;N;;;;; +A506;VAI SYLLABLE BHEE;Lo;0;L;;;;;N;;;;; +A507;VAI SYLLABLE BEE;Lo;0;L;;;;;N;;;;; +A508;VAI SYLLABLE MBEE;Lo;0;L;;;;;N;;;;; +A509;VAI SYLLABLE KPEE;Lo;0;L;;;;;N;;;;; +A50A;VAI SYLLABLE MGBEE;Lo;0;L;;;;;N;;;;; +A50B;VAI SYLLABLE GBEE;Lo;0;L;;;;;N;;;;; +A50C;VAI SYLLABLE FEE;Lo;0;L;;;;;N;;;;; +A50D;VAI SYLLABLE VEE;Lo;0;L;;;;;N;;;;; +A50E;VAI SYLLABLE TEE;Lo;0;L;;;;;N;;;;; +A50F;VAI SYLLABLE THEE;Lo;0;L;;;;;N;;;;; +A510;VAI SYLLABLE DHEE;Lo;0;L;;;;;N;;;;; +A511;VAI SYLLABLE DHHEE;Lo;0;L;;;;;N;;;;; +A512;VAI SYLLABLE LEE;Lo;0;L;;;;;N;;;;; +A513;VAI SYLLABLE REE;Lo;0;L;;;;;N;;;;; +A514;VAI SYLLABLE DEE;Lo;0;L;;;;;N;;;;; +A515;VAI SYLLABLE NDEE;Lo;0;L;;;;;N;;;;; +A516;VAI SYLLABLE SEE;Lo;0;L;;;;;N;;;;; +A517;VAI SYLLABLE SHEE;Lo;0;L;;;;;N;;;;; +A518;VAI SYLLABLE ZEE;Lo;0;L;;;;;N;;;;; +A519;VAI SYLLABLE ZHEE;Lo;0;L;;;;;N;;;;; +A51A;VAI SYLLABLE CEE;Lo;0;L;;;;;N;;;;; +A51B;VAI SYLLABLE JEE;Lo;0;L;;;;;N;;;;; +A51C;VAI SYLLABLE NJEE;Lo;0;L;;;;;N;;;;; +A51D;VAI SYLLABLE YEE;Lo;0;L;;;;;N;;;;; +A51E;VAI SYLLABLE KEE;Lo;0;L;;;;;N;;;;; +A51F;VAI SYLLABLE NGGEE;Lo;0;L;;;;;N;;;;; +A520;VAI SYLLABLE GEE;Lo;0;L;;;;;N;;;;; +A521;VAI SYLLABLE MEE;Lo;0;L;;;;;N;;;;; +A522;VAI SYLLABLE NEE;Lo;0;L;;;;;N;;;;; +A523;VAI SYLLABLE NYEE;Lo;0;L;;;;;N;;;;; +A524;VAI SYLLABLE I;Lo;0;L;;;;;N;;;;; +A525;VAI SYLLABLE IN;Lo;0;L;;;;;N;;;;; +A526;VAI SYLLABLE HI;Lo;0;L;;;;;N;;;;; +A527;VAI SYLLABLE HIN;Lo;0;L;;;;;N;;;;; +A528;VAI SYLLABLE WI;Lo;0;L;;;;;N;;;;; +A529;VAI SYLLABLE WIN;Lo;0;L;;;;;N;;;;; +A52A;VAI SYLLABLE PI;Lo;0;L;;;;;N;;;;; +A52B;VAI SYLLABLE BHI;Lo;0;L;;;;;N;;;;; +A52C;VAI SYLLABLE BI;Lo;0;L;;;;;N;;;;; +A52D;VAI SYLLABLE MBI;Lo;0;L;;;;;N;;;;; +A52E;VAI SYLLABLE KPI;Lo;0;L;;;;;N;;;;; +A52F;VAI SYLLABLE MGBI;Lo;0;L;;;;;N;;;;; +A530;VAI SYLLABLE GBI;Lo;0;L;;;;;N;;;;; +A531;VAI SYLLABLE FI;Lo;0;L;;;;;N;;;;; +A532;VAI SYLLABLE VI;Lo;0;L;;;;;N;;;;; +A533;VAI SYLLABLE TI;Lo;0;L;;;;;N;;;;; +A534;VAI SYLLABLE THI;Lo;0;L;;;;;N;;;;; +A535;VAI SYLLABLE DHI;Lo;0;L;;;;;N;;;;; +A536;VAI SYLLABLE DHHI;Lo;0;L;;;;;N;;;;; +A537;VAI SYLLABLE LI;Lo;0;L;;;;;N;;;;; +A538;VAI SYLLABLE RI;Lo;0;L;;;;;N;;;;; +A539;VAI SYLLABLE DI;Lo;0;L;;;;;N;;;;; +A53A;VAI SYLLABLE NDI;Lo;0;L;;;;;N;;;;; +A53B;VAI SYLLABLE SI;Lo;0;L;;;;;N;;;;; +A53C;VAI SYLLABLE SHI;Lo;0;L;;;;;N;;;;; +A53D;VAI SYLLABLE ZI;Lo;0;L;;;;;N;;;;; +A53E;VAI SYLLABLE ZHI;Lo;0;L;;;;;N;;;;; +A53F;VAI SYLLABLE CI;Lo;0;L;;;;;N;;;;; +A540;VAI SYLLABLE JI;Lo;0;L;;;;;N;;;;; +A541;VAI SYLLABLE NJI;Lo;0;L;;;;;N;;;;; +A542;VAI SYLLABLE YI;Lo;0;L;;;;;N;;;;; +A543;VAI SYLLABLE KI;Lo;0;L;;;;;N;;;;; +A544;VAI SYLLABLE NGGI;Lo;0;L;;;;;N;;;;; +A545;VAI SYLLABLE GI;Lo;0;L;;;;;N;;;;; +A546;VAI SYLLABLE MI;Lo;0;L;;;;;N;;;;; +A547;VAI SYLLABLE NI;Lo;0;L;;;;;N;;;;; +A548;VAI SYLLABLE NYI;Lo;0;L;;;;;N;;;;; +A549;VAI SYLLABLE A;Lo;0;L;;;;;N;;;;; +A54A;VAI SYLLABLE AN;Lo;0;L;;;;;N;;;;; +A54B;VAI SYLLABLE NGAN;Lo;0;L;;;;;N;;;;; +A54C;VAI SYLLABLE HA;Lo;0;L;;;;;N;;;;; +A54D;VAI SYLLABLE HAN;Lo;0;L;;;;;N;;;;; +A54E;VAI SYLLABLE WA;Lo;0;L;;;;;N;;;;; +A54F;VAI SYLLABLE WAN;Lo;0;L;;;;;N;;;;; +A550;VAI SYLLABLE PA;Lo;0;L;;;;;N;;;;; +A551;VAI SYLLABLE BHA;Lo;0;L;;;;;N;;;;; +A552;VAI SYLLABLE BA;Lo;0;L;;;;;N;;;;; +A553;VAI SYLLABLE MBA;Lo;0;L;;;;;N;;;;; +A554;VAI SYLLABLE KPA;Lo;0;L;;;;;N;;;;; +A555;VAI SYLLABLE KPAN;Lo;0;L;;;;;N;;;;; +A556;VAI SYLLABLE MGBA;Lo;0;L;;;;;N;;;;; +A557;VAI SYLLABLE GBA;Lo;0;L;;;;;N;;;;; +A558;VAI SYLLABLE FA;Lo;0;L;;;;;N;;;;; +A559;VAI SYLLABLE VA;Lo;0;L;;;;;N;;;;; +A55A;VAI SYLLABLE TA;Lo;0;L;;;;;N;;;;; +A55B;VAI SYLLABLE THA;Lo;0;L;;;;;N;;;;; +A55C;VAI SYLLABLE DHA;Lo;0;L;;;;;N;;;;; +A55D;VAI SYLLABLE DHHA;Lo;0;L;;;;;N;;;;; +A55E;VAI SYLLABLE LA;Lo;0;L;;;;;N;;;;; +A55F;VAI SYLLABLE RA;Lo;0;L;;;;;N;;;;; +A560;VAI SYLLABLE DA;Lo;0;L;;;;;N;;;;; +A561;VAI SYLLABLE NDA;Lo;0;L;;;;;N;;;;; +A562;VAI SYLLABLE SA;Lo;0;L;;;;;N;;;;; +A563;VAI SYLLABLE SHA;Lo;0;L;;;;;N;;;;; +A564;VAI SYLLABLE ZA;Lo;0;L;;;;;N;;;;; +A565;VAI SYLLABLE ZHA;Lo;0;L;;;;;N;;;;; +A566;VAI SYLLABLE CA;Lo;0;L;;;;;N;;;;; +A567;VAI SYLLABLE JA;Lo;0;L;;;;;N;;;;; +A568;VAI SYLLABLE NJA;Lo;0;L;;;;;N;;;;; +A569;VAI SYLLABLE YA;Lo;0;L;;;;;N;;;;; +A56A;VAI SYLLABLE KA;Lo;0;L;;;;;N;;;;; +A56B;VAI SYLLABLE KAN;Lo;0;L;;;;;N;;;;; +A56C;VAI SYLLABLE NGGA;Lo;0;L;;;;;N;;;;; +A56D;VAI SYLLABLE GA;Lo;0;L;;;;;N;;;;; +A56E;VAI SYLLABLE MA;Lo;0;L;;;;;N;;;;; +A56F;VAI SYLLABLE NA;Lo;0;L;;;;;N;;;;; +A570;VAI SYLLABLE NYA;Lo;0;L;;;;;N;;;;; +A571;VAI SYLLABLE OO;Lo;0;L;;;;;N;;;;; +A572;VAI SYLLABLE OON;Lo;0;L;;;;;N;;;;; +A573;VAI SYLLABLE HOO;Lo;0;L;;;;;N;;;;; +A574;VAI SYLLABLE WOO;Lo;0;L;;;;;N;;;;; +A575;VAI SYLLABLE WOON;Lo;0;L;;;;;N;;;;; +A576;VAI SYLLABLE POO;Lo;0;L;;;;;N;;;;; +A577;VAI SYLLABLE BHOO;Lo;0;L;;;;;N;;;;; +A578;VAI SYLLABLE BOO;Lo;0;L;;;;;N;;;;; +A579;VAI SYLLABLE MBOO;Lo;0;L;;;;;N;;;;; +A57A;VAI SYLLABLE KPOO;Lo;0;L;;;;;N;;;;; +A57B;VAI SYLLABLE MGBOO;Lo;0;L;;;;;N;;;;; +A57C;VAI SYLLABLE GBOO;Lo;0;L;;;;;N;;;;; +A57D;VAI SYLLABLE FOO;Lo;0;L;;;;;N;;;;; +A57E;VAI SYLLABLE VOO;Lo;0;L;;;;;N;;;;; +A57F;VAI SYLLABLE TOO;Lo;0;L;;;;;N;;;;; +A580;VAI SYLLABLE THOO;Lo;0;L;;;;;N;;;;; +A581;VAI SYLLABLE DHOO;Lo;0;L;;;;;N;;;;; +A582;VAI SYLLABLE DHHOO;Lo;0;L;;;;;N;;;;; +A583;VAI SYLLABLE LOO;Lo;0;L;;;;;N;;;;; +A584;VAI SYLLABLE ROO;Lo;0;L;;;;;N;;;;; +A585;VAI SYLLABLE DOO;Lo;0;L;;;;;N;;;;; +A586;VAI SYLLABLE NDOO;Lo;0;L;;;;;N;;;;; +A587;VAI SYLLABLE SOO;Lo;0;L;;;;;N;;;;; +A588;VAI SYLLABLE SHOO;Lo;0;L;;;;;N;;;;; +A589;VAI SYLLABLE ZOO;Lo;0;L;;;;;N;;;;; +A58A;VAI SYLLABLE ZHOO;Lo;0;L;;;;;N;;;;; +A58B;VAI SYLLABLE COO;Lo;0;L;;;;;N;;;;; +A58C;VAI SYLLABLE JOO;Lo;0;L;;;;;N;;;;; +A58D;VAI SYLLABLE NJOO;Lo;0;L;;;;;N;;;;; +A58E;VAI SYLLABLE YOO;Lo;0;L;;;;;N;;;;; +A58F;VAI SYLLABLE KOO;Lo;0;L;;;;;N;;;;; +A590;VAI SYLLABLE NGGOO;Lo;0;L;;;;;N;;;;; +A591;VAI SYLLABLE GOO;Lo;0;L;;;;;N;;;;; +A592;VAI SYLLABLE MOO;Lo;0;L;;;;;N;;;;; +A593;VAI SYLLABLE NOO;Lo;0;L;;;;;N;;;;; +A594;VAI SYLLABLE NYOO;Lo;0;L;;;;;N;;;;; +A595;VAI SYLLABLE U;Lo;0;L;;;;;N;;;;; +A596;VAI SYLLABLE UN;Lo;0;L;;;;;N;;;;; +A597;VAI SYLLABLE HU;Lo;0;L;;;;;N;;;;; +A598;VAI SYLLABLE HUN;Lo;0;L;;;;;N;;;;; +A599;VAI SYLLABLE WU;Lo;0;L;;;;;N;;;;; +A59A;VAI SYLLABLE WUN;Lo;0;L;;;;;N;;;;; +A59B;VAI SYLLABLE PU;Lo;0;L;;;;;N;;;;; +A59C;VAI SYLLABLE BHU;Lo;0;L;;;;;N;;;;; +A59D;VAI SYLLABLE BU;Lo;0;L;;;;;N;;;;; +A59E;VAI SYLLABLE MBU;Lo;0;L;;;;;N;;;;; +A59F;VAI SYLLABLE KPU;Lo;0;L;;;;;N;;;;; +A5A0;VAI SYLLABLE MGBU;Lo;0;L;;;;;N;;;;; +A5A1;VAI SYLLABLE GBU;Lo;0;L;;;;;N;;;;; +A5A2;VAI SYLLABLE FU;Lo;0;L;;;;;N;;;;; +A5A3;VAI SYLLABLE VU;Lo;0;L;;;;;N;;;;; +A5A4;VAI SYLLABLE TU;Lo;0;L;;;;;N;;;;; +A5A5;VAI SYLLABLE THU;Lo;0;L;;;;;N;;;;; +A5A6;VAI SYLLABLE DHU;Lo;0;L;;;;;N;;;;; +A5A7;VAI SYLLABLE DHHU;Lo;0;L;;;;;N;;;;; +A5A8;VAI SYLLABLE LU;Lo;0;L;;;;;N;;;;; +A5A9;VAI SYLLABLE RU;Lo;0;L;;;;;N;;;;; +A5AA;VAI SYLLABLE DU;Lo;0;L;;;;;N;;;;; +A5AB;VAI SYLLABLE NDU;Lo;0;L;;;;;N;;;;; +A5AC;VAI SYLLABLE SU;Lo;0;L;;;;;N;;;;; +A5AD;VAI SYLLABLE SHU;Lo;0;L;;;;;N;;;;; +A5AE;VAI SYLLABLE ZU;Lo;0;L;;;;;N;;;;; +A5AF;VAI SYLLABLE ZHU;Lo;0;L;;;;;N;;;;; +A5B0;VAI SYLLABLE CU;Lo;0;L;;;;;N;;;;; +A5B1;VAI SYLLABLE JU;Lo;0;L;;;;;N;;;;; +A5B2;VAI SYLLABLE NJU;Lo;0;L;;;;;N;;;;; +A5B3;VAI SYLLABLE YU;Lo;0;L;;;;;N;;;;; +A5B4;VAI SYLLABLE KU;Lo;0;L;;;;;N;;;;; +A5B5;VAI SYLLABLE NGGU;Lo;0;L;;;;;N;;;;; +A5B6;VAI SYLLABLE GU;Lo;0;L;;;;;N;;;;; +A5B7;VAI SYLLABLE MU;Lo;0;L;;;;;N;;;;; +A5B8;VAI SYLLABLE NU;Lo;0;L;;;;;N;;;;; +A5B9;VAI SYLLABLE NYU;Lo;0;L;;;;;N;;;;; +A5BA;VAI SYLLABLE O;Lo;0;L;;;;;N;;;;; +A5BB;VAI SYLLABLE ON;Lo;0;L;;;;;N;;;;; +A5BC;VAI SYLLABLE NGON;Lo;0;L;;;;;N;;;;; +A5BD;VAI SYLLABLE HO;Lo;0;L;;;;;N;;;;; +A5BE;VAI SYLLABLE HON;Lo;0;L;;;;;N;;;;; +A5BF;VAI SYLLABLE WO;Lo;0;L;;;;;N;;;;; +A5C0;VAI SYLLABLE WON;Lo;0;L;;;;;N;;;;; +A5C1;VAI SYLLABLE PO;Lo;0;L;;;;;N;;;;; +A5C2;VAI SYLLABLE BHO;Lo;0;L;;;;;N;;;;; +A5C3;VAI SYLLABLE BO;Lo;0;L;;;;;N;;;;; +A5C4;VAI SYLLABLE MBO;Lo;0;L;;;;;N;;;;; +A5C5;VAI SYLLABLE KPO;Lo;0;L;;;;;N;;;;; +A5C6;VAI SYLLABLE MGBO;Lo;0;L;;;;;N;;;;; +A5C7;VAI SYLLABLE GBO;Lo;0;L;;;;;N;;;;; +A5C8;VAI SYLLABLE GBON;Lo;0;L;;;;;N;;;;; +A5C9;VAI SYLLABLE FO;Lo;0;L;;;;;N;;;;; +A5CA;VAI SYLLABLE VO;Lo;0;L;;;;;N;;;;; +A5CB;VAI SYLLABLE TO;Lo;0;L;;;;;N;;;;; +A5CC;VAI SYLLABLE THO;Lo;0;L;;;;;N;;;;; +A5CD;VAI SYLLABLE DHO;Lo;0;L;;;;;N;;;;; +A5CE;VAI SYLLABLE DHHO;Lo;0;L;;;;;N;;;;; +A5CF;VAI SYLLABLE LO;Lo;0;L;;;;;N;;;;; +A5D0;VAI SYLLABLE RO;Lo;0;L;;;;;N;;;;; +A5D1;VAI SYLLABLE DO;Lo;0;L;;;;;N;;;;; +A5D2;VAI SYLLABLE NDO;Lo;0;L;;;;;N;;;;; +A5D3;VAI SYLLABLE SO;Lo;0;L;;;;;N;;;;; +A5D4;VAI SYLLABLE SHO;Lo;0;L;;;;;N;;;;; +A5D5;VAI SYLLABLE ZO;Lo;0;L;;;;;N;;;;; +A5D6;VAI SYLLABLE ZHO;Lo;0;L;;;;;N;;;;; +A5D7;VAI SYLLABLE CO;Lo;0;L;;;;;N;;;;; +A5D8;VAI SYLLABLE JO;Lo;0;L;;;;;N;;;;; +A5D9;VAI SYLLABLE NJO;Lo;0;L;;;;;N;;;;; +A5DA;VAI SYLLABLE YO;Lo;0;L;;;;;N;;;;; +A5DB;VAI SYLLABLE KO;Lo;0;L;;;;;N;;;;; +A5DC;VAI SYLLABLE NGGO;Lo;0;L;;;;;N;;;;; +A5DD;VAI SYLLABLE GO;Lo;0;L;;;;;N;;;;; +A5DE;VAI SYLLABLE MO;Lo;0;L;;;;;N;;;;; +A5DF;VAI SYLLABLE NO;Lo;0;L;;;;;N;;;;; +A5E0;VAI SYLLABLE NYO;Lo;0;L;;;;;N;;;;; +A5E1;VAI SYLLABLE E;Lo;0;L;;;;;N;;;;; +A5E2;VAI SYLLABLE EN;Lo;0;L;;;;;N;;;;; +A5E3;VAI SYLLABLE NGEN;Lo;0;L;;;;;N;;;;; +A5E4;VAI SYLLABLE HE;Lo;0;L;;;;;N;;;;; +A5E5;VAI SYLLABLE HEN;Lo;0;L;;;;;N;;;;; +A5E6;VAI SYLLABLE WE;Lo;0;L;;;;;N;;;;; +A5E7;VAI SYLLABLE WEN;Lo;0;L;;;;;N;;;;; +A5E8;VAI SYLLABLE PE;Lo;0;L;;;;;N;;;;; +A5E9;VAI SYLLABLE BHE;Lo;0;L;;;;;N;;;;; +A5EA;VAI SYLLABLE BE;Lo;0;L;;;;;N;;;;; +A5EB;VAI SYLLABLE MBE;Lo;0;L;;;;;N;;;;; +A5EC;VAI SYLLABLE KPE;Lo;0;L;;;;;N;;;;; +A5ED;VAI SYLLABLE KPEN;Lo;0;L;;;;;N;;;;; +A5EE;VAI SYLLABLE MGBE;Lo;0;L;;;;;N;;;;; +A5EF;VAI SYLLABLE GBE;Lo;0;L;;;;;N;;;;; +A5F0;VAI SYLLABLE GBEN;Lo;0;L;;;;;N;;;;; +A5F1;VAI SYLLABLE FE;Lo;0;L;;;;;N;;;;; +A5F2;VAI SYLLABLE VE;Lo;0;L;;;;;N;;;;; +A5F3;VAI SYLLABLE TE;Lo;0;L;;;;;N;;;;; +A5F4;VAI SYLLABLE THE;Lo;0;L;;;;;N;;;;; +A5F5;VAI SYLLABLE DHE;Lo;0;L;;;;;N;;;;; +A5F6;VAI SYLLABLE DHHE;Lo;0;L;;;;;N;;;;; +A5F7;VAI SYLLABLE LE;Lo;0;L;;;;;N;;;;; +A5F8;VAI SYLLABLE RE;Lo;0;L;;;;;N;;;;; +A5F9;VAI SYLLABLE DE;Lo;0;L;;;;;N;;;;; +A5FA;VAI SYLLABLE NDE;Lo;0;L;;;;;N;;;;; +A5FB;VAI SYLLABLE SE;Lo;0;L;;;;;N;;;;; +A5FC;VAI SYLLABLE SHE;Lo;0;L;;;;;N;;;;; +A5FD;VAI SYLLABLE ZE;Lo;0;L;;;;;N;;;;; +A5FE;VAI SYLLABLE ZHE;Lo;0;L;;;;;N;;;;; +A5FF;VAI SYLLABLE CE;Lo;0;L;;;;;N;;;;; +A600;VAI SYLLABLE JE;Lo;0;L;;;;;N;;;;; +A601;VAI SYLLABLE NJE;Lo;0;L;;;;;N;;;;; +A602;VAI SYLLABLE YE;Lo;0;L;;;;;N;;;;; +A603;VAI SYLLABLE KE;Lo;0;L;;;;;N;;;;; +A604;VAI SYLLABLE NGGE;Lo;0;L;;;;;N;;;;; +A605;VAI SYLLABLE NGGEN;Lo;0;L;;;;;N;;;;; +A606;VAI SYLLABLE GE;Lo;0;L;;;;;N;;;;; +A607;VAI SYLLABLE GEN;Lo;0;L;;;;;N;;;;; +A608;VAI SYLLABLE ME;Lo;0;L;;;;;N;;;;; +A609;VAI SYLLABLE NE;Lo;0;L;;;;;N;;;;; +A60A;VAI SYLLABLE NYE;Lo;0;L;;;;;N;;;;; +A60B;VAI SYLLABLE NG;Lo;0;L;;;;;N;;;;; +A60C;VAI SYLLABLE LENGTHENER;Lm;0;L;;;;;N;;;;; +A60D;VAI COMMA;Po;0;ON;;;;;N;;;;; +A60E;VAI FULL STOP;Po;0;ON;;;;;N;;;;; +A60F;VAI QUESTION MARK;Po;0;ON;;;;;N;;;;; +A610;VAI SYLLABLE NDOLE FA;Lo;0;L;;;;;N;;;;; +A611;VAI SYLLABLE NDOLE KA;Lo;0;L;;;;;N;;;;; +A612;VAI SYLLABLE NDOLE SOO;Lo;0;L;;;;;N;;;;; +A613;VAI SYMBOL FEENG;Lo;0;L;;;;;N;;;;; +A614;VAI SYMBOL KEENG;Lo;0;L;;;;;N;;;;; +A615;VAI SYMBOL TING;Lo;0;L;;;;;N;;;;; +A616;VAI SYMBOL NII;Lo;0;L;;;;;N;;;;; +A617;VAI SYMBOL BANG;Lo;0;L;;;;;N;;;;; +A618;VAI SYMBOL FAA;Lo;0;L;;;;;N;;;;; +A619;VAI SYMBOL TAA;Lo;0;L;;;;;N;;;;; +A61A;VAI SYMBOL DANG;Lo;0;L;;;;;N;;;;; +A61B;VAI SYMBOL DOONG;Lo;0;L;;;;;N;;;;; +A61C;VAI SYMBOL KUNG;Lo;0;L;;;;;N;;;;; +A61D;VAI SYMBOL TONG;Lo;0;L;;;;;N;;;;; +A61E;VAI SYMBOL DO-O;Lo;0;L;;;;;N;;;;; +A61F;VAI SYMBOL JONG;Lo;0;L;;;;;N;;;;; +A620;VAI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +A621;VAI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +A622;VAI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +A623;VAI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +A624;VAI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +A625;VAI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +A626;VAI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +A627;VAI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +A628;VAI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +A629;VAI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +A62A;VAI SYLLABLE NDOLE MA;Lo;0;L;;;;;N;;;;; +A62B;VAI SYLLABLE NDOLE DO;Lo;0;L;;;;;N;;;;; +A640;CYRILLIC CAPITAL LETTER ZEMLYA;Lu;0;L;;;;;N;;;;A641; +A641;CYRILLIC SMALL LETTER ZEMLYA;Ll;0;L;;;;;N;;;A640;;A640 +A642;CYRILLIC CAPITAL LETTER DZELO;Lu;0;L;;;;;N;;;;A643; +A643;CYRILLIC SMALL LETTER DZELO;Ll;0;L;;;;;N;;;A642;;A642 +A644;CYRILLIC CAPITAL LETTER REVERSED DZE;Lu;0;L;;;;;N;;;;A645; +A645;CYRILLIC SMALL LETTER REVERSED DZE;Ll;0;L;;;;;N;;;A644;;A644 +A646;CYRILLIC CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;A647; +A647;CYRILLIC SMALL LETTER IOTA;Ll;0;L;;;;;N;;;A646;;A646 +A648;CYRILLIC CAPITAL LETTER DJERV;Lu;0;L;;;;;N;;;;A649; +A649;CYRILLIC SMALL LETTER DJERV;Ll;0;L;;;;;N;;;A648;;A648 +A64A;CYRILLIC CAPITAL LETTER MONOGRAPH UK;Lu;0;L;;;;;N;;;;A64B; +A64B;CYRILLIC SMALL LETTER MONOGRAPH UK;Ll;0;L;;;;;N;;;A64A;;A64A +A64C;CYRILLIC CAPITAL LETTER BROAD OMEGA;Lu;0;L;;;;;N;;;;A64D; +A64D;CYRILLIC SMALL LETTER BROAD OMEGA;Ll;0;L;;;;;N;;;A64C;;A64C +A64E;CYRILLIC CAPITAL LETTER NEUTRAL YER;Lu;0;L;;;;;N;;;;A64F; +A64F;CYRILLIC SMALL LETTER NEUTRAL YER;Ll;0;L;;;;;N;;;A64E;;A64E +A650;CYRILLIC CAPITAL LETTER YERU WITH BACK YER;Lu;0;L;;;;;N;;;;A651; +A651;CYRILLIC SMALL LETTER YERU WITH BACK YER;Ll;0;L;;;;;N;;;A650;;A650 +A652;CYRILLIC CAPITAL LETTER IOTIFIED YAT;Lu;0;L;;;;;N;;;;A653; +A653;CYRILLIC SMALL LETTER IOTIFIED YAT;Ll;0;L;;;;;N;;;A652;;A652 +A654;CYRILLIC CAPITAL LETTER REVERSED YU;Lu;0;L;;;;;N;;;;A655; +A655;CYRILLIC SMALL LETTER REVERSED YU;Ll;0;L;;;;;N;;;A654;;A654 +A656;CYRILLIC CAPITAL LETTER IOTIFIED A;Lu;0;L;;;;;N;;;;A657; +A657;CYRILLIC SMALL LETTER IOTIFIED A;Ll;0;L;;;;;N;;;A656;;A656 +A658;CYRILLIC CAPITAL LETTER CLOSED LITTLE YUS;Lu;0;L;;;;;N;;;;A659; +A659;CYRILLIC SMALL LETTER CLOSED LITTLE YUS;Ll;0;L;;;;;N;;;A658;;A658 +A65A;CYRILLIC CAPITAL LETTER BLENDED YUS;Lu;0;L;;;;;N;;;;A65B; +A65B;CYRILLIC SMALL LETTER BLENDED YUS;Ll;0;L;;;;;N;;;A65A;;A65A +A65C;CYRILLIC CAPITAL LETTER IOTIFIED CLOSED LITTLE YUS;Lu;0;L;;;;;N;;;;A65D; +A65D;CYRILLIC SMALL LETTER IOTIFIED CLOSED LITTLE YUS;Ll;0;L;;;;;N;;;A65C;;A65C +A65E;CYRILLIC CAPITAL LETTER YN;Lu;0;L;;;;;N;;;;A65F; +A65F;CYRILLIC SMALL LETTER YN;Ll;0;L;;;;;N;;;A65E;;A65E +A660;CYRILLIC CAPITAL LETTER REVERSED TSE;Lu;0;L;;;;;N;;;;A661; +A661;CYRILLIC SMALL LETTER REVERSED TSE;Ll;0;L;;;;;N;;;A660;;A660 +A662;CYRILLIC CAPITAL LETTER SOFT DE;Lu;0;L;;;;;N;;;;A663; +A663;CYRILLIC SMALL LETTER SOFT DE;Ll;0;L;;;;;N;;;A662;;A662 +A664;CYRILLIC CAPITAL LETTER SOFT EL;Lu;0;L;;;;;N;;;;A665; +A665;CYRILLIC SMALL LETTER SOFT EL;Ll;0;L;;;;;N;;;A664;;A664 +A666;CYRILLIC CAPITAL LETTER SOFT EM;Lu;0;L;;;;;N;;;;A667; +A667;CYRILLIC SMALL LETTER SOFT EM;Ll;0;L;;;;;N;;;A666;;A666 +A668;CYRILLIC CAPITAL LETTER MONOCULAR O;Lu;0;L;;;;;N;;;;A669; +A669;CYRILLIC SMALL LETTER MONOCULAR O;Ll;0;L;;;;;N;;;A668;;A668 +A66A;CYRILLIC CAPITAL LETTER BINOCULAR O;Lu;0;L;;;;;N;;;;A66B; +A66B;CYRILLIC SMALL LETTER BINOCULAR O;Ll;0;L;;;;;N;;;A66A;;A66A +A66C;CYRILLIC CAPITAL LETTER DOUBLE MONOCULAR O;Lu;0;L;;;;;N;;;;A66D; +A66D;CYRILLIC SMALL LETTER DOUBLE MONOCULAR O;Ll;0;L;;;;;N;;;A66C;;A66C +A66E;CYRILLIC LETTER MULTIOCULAR O;Lo;0;L;;;;;N;;;;; +A66F;COMBINING CYRILLIC VZMET;Mn;230;NSM;;;;;N;;;;; +A670;COMBINING CYRILLIC TEN MILLIONS SIGN;Me;0;NSM;;;;;N;;;;; +A671;COMBINING CYRILLIC HUNDRED MILLIONS SIGN;Me;0;NSM;;;;;N;;;;; +A672;COMBINING CYRILLIC THOUSAND MILLIONS SIGN;Me;0;NSM;;;;;N;;;;; +A673;SLAVONIC ASTERISK;Po;0;ON;;;;;N;;;;; +A674;COMBINING CYRILLIC LETTER UKRAINIAN IE;Mn;230;NSM;;;;;N;;;;; +A675;COMBINING CYRILLIC LETTER I;Mn;230;NSM;;;;;N;;;;; +A676;COMBINING CYRILLIC LETTER YI;Mn;230;NSM;;;;;N;;;;; +A677;COMBINING CYRILLIC LETTER U;Mn;230;NSM;;;;;N;;;;; +A678;COMBINING CYRILLIC LETTER HARD SIGN;Mn;230;NSM;;;;;N;;;;; +A679;COMBINING CYRILLIC LETTER YERU;Mn;230;NSM;;;;;N;;;;; +A67A;COMBINING CYRILLIC LETTER SOFT SIGN;Mn;230;NSM;;;;;N;;;;; +A67B;COMBINING CYRILLIC LETTER OMEGA;Mn;230;NSM;;;;;N;;;;; +A67C;COMBINING CYRILLIC KAVYKA;Mn;230;NSM;;;;;N;;;;; +A67D;COMBINING CYRILLIC PAYEROK;Mn;230;NSM;;;;;N;;;;; +A67E;CYRILLIC KAVYKA;Po;0;ON;;;;;N;;;;; +A67F;CYRILLIC PAYEROK;Lm;0;ON;;;;;N;;;;; +A680;CYRILLIC CAPITAL LETTER DWE;Lu;0;L;;;;;N;;;;A681; +A681;CYRILLIC SMALL LETTER DWE;Ll;0;L;;;;;N;;;A680;;A680 +A682;CYRILLIC CAPITAL LETTER DZWE;Lu;0;L;;;;;N;;;;A683; +A683;CYRILLIC SMALL LETTER DZWE;Ll;0;L;;;;;N;;;A682;;A682 +A684;CYRILLIC CAPITAL LETTER ZHWE;Lu;0;L;;;;;N;;;;A685; +A685;CYRILLIC SMALL LETTER ZHWE;Ll;0;L;;;;;N;;;A684;;A684 +A686;CYRILLIC CAPITAL LETTER CCHE;Lu;0;L;;;;;N;;;;A687; +A687;CYRILLIC SMALL LETTER CCHE;Ll;0;L;;;;;N;;;A686;;A686 +A688;CYRILLIC CAPITAL LETTER DZZE;Lu;0;L;;;;;N;;;;A689; +A689;CYRILLIC SMALL LETTER DZZE;Ll;0;L;;;;;N;;;A688;;A688 +A68A;CYRILLIC CAPITAL LETTER TE WITH MIDDLE HOOK;Lu;0;L;;;;;N;;;;A68B; +A68B;CYRILLIC SMALL LETTER TE WITH MIDDLE HOOK;Ll;0;L;;;;;N;;;A68A;;A68A +A68C;CYRILLIC CAPITAL LETTER TWE;Lu;0;L;;;;;N;;;;A68D; +A68D;CYRILLIC SMALL LETTER TWE;Ll;0;L;;;;;N;;;A68C;;A68C +A68E;CYRILLIC CAPITAL LETTER TSWE;Lu;0;L;;;;;N;;;;A68F; +A68F;CYRILLIC SMALL LETTER TSWE;Ll;0;L;;;;;N;;;A68E;;A68E +A690;CYRILLIC CAPITAL LETTER TSSE;Lu;0;L;;;;;N;;;;A691; +A691;CYRILLIC SMALL LETTER TSSE;Ll;0;L;;;;;N;;;A690;;A690 +A692;CYRILLIC CAPITAL LETTER TCHE;Lu;0;L;;;;;N;;;;A693; +A693;CYRILLIC SMALL LETTER TCHE;Ll;0;L;;;;;N;;;A692;;A692 +A694;CYRILLIC CAPITAL LETTER HWE;Lu;0;L;;;;;N;;;;A695; +A695;CYRILLIC SMALL LETTER HWE;Ll;0;L;;;;;N;;;A694;;A694 +A696;CYRILLIC CAPITAL LETTER SHWE;Lu;0;L;;;;;N;;;;A697; +A697;CYRILLIC SMALL LETTER SHWE;Ll;0;L;;;;;N;;;A696;;A696 +A698;CYRILLIC CAPITAL LETTER DOUBLE O;Lu;0;L;;;;;N;;;;A699; +A699;CYRILLIC SMALL LETTER DOUBLE O;Ll;0;L;;;;;N;;;A698;;A698 +A69A;CYRILLIC CAPITAL LETTER CROSSED O;Lu;0;L;;;;;N;;;;A69B; +A69B;CYRILLIC SMALL LETTER CROSSED O;Ll;0;L;;;;;N;;;A69A;;A69A +A69C;MODIFIER LETTER CYRILLIC HARD SIGN;Lm;0;L;<super> 044A;;;;N;;;;; +A69D;MODIFIER LETTER CYRILLIC SOFT SIGN;Lm;0;L;<super> 044C;;;;N;;;;; +A69E;COMBINING CYRILLIC LETTER EF;Mn;230;NSM;;;;;N;;;;; +A69F;COMBINING CYRILLIC LETTER IOTIFIED E;Mn;230;NSM;;;;;N;;;;; +A6A0;BAMUM LETTER A;Lo;0;L;;;;;N;;;;; +A6A1;BAMUM LETTER KA;Lo;0;L;;;;;N;;;;; +A6A2;BAMUM LETTER U;Lo;0;L;;;;;N;;;;; +A6A3;BAMUM LETTER KU;Lo;0;L;;;;;N;;;;; +A6A4;BAMUM LETTER EE;Lo;0;L;;;;;N;;;;; +A6A5;BAMUM LETTER REE;Lo;0;L;;;;;N;;;;; +A6A6;BAMUM LETTER TAE;Lo;0;L;;;;;N;;;;; +A6A7;BAMUM LETTER O;Lo;0;L;;;;;N;;;;; +A6A8;BAMUM LETTER NYI;Lo;0;L;;;;;N;;;;; +A6A9;BAMUM LETTER I;Lo;0;L;;;;;N;;;;; +A6AA;BAMUM LETTER LA;Lo;0;L;;;;;N;;;;; +A6AB;BAMUM LETTER PA;Lo;0;L;;;;;N;;;;; +A6AC;BAMUM LETTER RII;Lo;0;L;;;;;N;;;;; +A6AD;BAMUM LETTER RIEE;Lo;0;L;;;;;N;;;;; +A6AE;BAMUM LETTER LEEEE;Lo;0;L;;;;;N;;;;; +A6AF;BAMUM LETTER MEEEE;Lo;0;L;;;;;N;;;;; +A6B0;BAMUM LETTER TAA;Lo;0;L;;;;;N;;;;; +A6B1;BAMUM LETTER NDAA;Lo;0;L;;;;;N;;;;; +A6B2;BAMUM LETTER NJAEM;Lo;0;L;;;;;N;;;;; +A6B3;BAMUM LETTER M;Lo;0;L;;;;;N;;;;; +A6B4;BAMUM LETTER SUU;Lo;0;L;;;;;N;;;;; +A6B5;BAMUM LETTER MU;Lo;0;L;;;;;N;;;;; +A6B6;BAMUM LETTER SHII;Lo;0;L;;;;;N;;;;; +A6B7;BAMUM LETTER SI;Lo;0;L;;;;;N;;;;; +A6B8;BAMUM LETTER SHEUX;Lo;0;L;;;;;N;;;;; +A6B9;BAMUM LETTER SEUX;Lo;0;L;;;;;N;;;;; +A6BA;BAMUM LETTER KYEE;Lo;0;L;;;;;N;;;;; +A6BB;BAMUM LETTER KET;Lo;0;L;;;;;N;;;;; +A6BC;BAMUM LETTER NUAE;Lo;0;L;;;;;N;;;;; +A6BD;BAMUM LETTER NU;Lo;0;L;;;;;N;;;;; +A6BE;BAMUM LETTER NJUAE;Lo;0;L;;;;;N;;;;; +A6BF;BAMUM LETTER YOQ;Lo;0;L;;;;;N;;;;; +A6C0;BAMUM LETTER SHU;Lo;0;L;;;;;N;;;;; +A6C1;BAMUM LETTER YUQ;Lo;0;L;;;;;N;;;;; +A6C2;BAMUM LETTER YA;Lo;0;L;;;;;N;;;;; +A6C3;BAMUM LETTER NSHA;Lo;0;L;;;;;N;;;;; +A6C4;BAMUM LETTER KEUX;Lo;0;L;;;;;N;;;;; +A6C5;BAMUM LETTER PEUX;Lo;0;L;;;;;N;;;;; +A6C6;BAMUM LETTER NJEE;Lo;0;L;;;;;N;;;;; +A6C7;BAMUM LETTER NTEE;Lo;0;L;;;;;N;;;;; +A6C8;BAMUM LETTER PUE;Lo;0;L;;;;;N;;;;; +A6C9;BAMUM LETTER WUE;Lo;0;L;;;;;N;;;;; +A6CA;BAMUM LETTER PEE;Lo;0;L;;;;;N;;;;; +A6CB;BAMUM LETTER FEE;Lo;0;L;;;;;N;;;;; +A6CC;BAMUM LETTER RU;Lo;0;L;;;;;N;;;;; +A6CD;BAMUM LETTER LU;Lo;0;L;;;;;N;;;;; +A6CE;BAMUM LETTER MI;Lo;0;L;;;;;N;;;;; +A6CF;BAMUM LETTER NI;Lo;0;L;;;;;N;;;;; +A6D0;BAMUM LETTER REUX;Lo;0;L;;;;;N;;;;; +A6D1;BAMUM LETTER RAE;Lo;0;L;;;;;N;;;;; +A6D2;BAMUM LETTER KEN;Lo;0;L;;;;;N;;;;; +A6D3;BAMUM LETTER NGKWAEN;Lo;0;L;;;;;N;;;;; +A6D4;BAMUM LETTER NGGA;Lo;0;L;;;;;N;;;;; +A6D5;BAMUM LETTER NGA;Lo;0;L;;;;;N;;;;; +A6D6;BAMUM LETTER SHO;Lo;0;L;;;;;N;;;;; +A6D7;BAMUM LETTER PUAE;Lo;0;L;;;;;N;;;;; +A6D8;BAMUM LETTER FU;Lo;0;L;;;;;N;;;;; +A6D9;BAMUM LETTER FOM;Lo;0;L;;;;;N;;;;; +A6DA;BAMUM LETTER WA;Lo;0;L;;;;;N;;;;; +A6DB;BAMUM LETTER NA;Lo;0;L;;;;;N;;;;; +A6DC;BAMUM LETTER LI;Lo;0;L;;;;;N;;;;; +A6DD;BAMUM LETTER PI;Lo;0;L;;;;;N;;;;; +A6DE;BAMUM LETTER LOQ;Lo;0;L;;;;;N;;;;; +A6DF;BAMUM LETTER KO;Lo;0;L;;;;;N;;;;; +A6E0;BAMUM LETTER MBEN;Lo;0;L;;;;;N;;;;; +A6E1;BAMUM LETTER REN;Lo;0;L;;;;;N;;;;; +A6E2;BAMUM LETTER MEN;Lo;0;L;;;;;N;;;;; +A6E3;BAMUM LETTER MA;Lo;0;L;;;;;N;;;;; +A6E4;BAMUM LETTER TI;Lo;0;L;;;;;N;;;;; +A6E5;BAMUM LETTER KI;Lo;0;L;;;;;N;;;;; +A6E6;BAMUM LETTER MO;Nl;0;L;;;;1;N;;;;; +A6E7;BAMUM LETTER MBAA;Nl;0;L;;;;2;N;;;;; +A6E8;BAMUM LETTER TET;Nl;0;L;;;;3;N;;;;; +A6E9;BAMUM LETTER KPA;Nl;0;L;;;;4;N;;;;; +A6EA;BAMUM LETTER TEN;Nl;0;L;;;;5;N;;;;; +A6EB;BAMUM LETTER NTUU;Nl;0;L;;;;6;N;;;;; +A6EC;BAMUM LETTER SAMBA;Nl;0;L;;;;7;N;;;;; +A6ED;BAMUM LETTER FAAMAE;Nl;0;L;;;;8;N;;;;; +A6EE;BAMUM LETTER KOVUU;Nl;0;L;;;;9;N;;;;; +A6EF;BAMUM LETTER KOGHOM;Nl;0;L;;;;0;N;;;;; +A6F0;BAMUM COMBINING MARK KOQNDON;Mn;230;NSM;;;;;N;;;;; +A6F1;BAMUM COMBINING MARK TUKWENTIS;Mn;230;NSM;;;;;N;;;;; +A6F2;BAMUM NJAEMLI;Po;0;L;;;;;N;;;;; +A6F3;BAMUM FULL STOP;Po;0;L;;;;;N;;;;; +A6F4;BAMUM COLON;Po;0;L;;;;;N;;;;; +A6F5;BAMUM COMMA;Po;0;L;;;;;N;;;;; +A6F6;BAMUM SEMICOLON;Po;0;L;;;;;N;;;;; +A6F7;BAMUM QUESTION MARK;Po;0;L;;;;;N;;;;; +A700;MODIFIER LETTER CHINESE TONE YIN PING;Sk;0;ON;;;;;N;;;;; +A701;MODIFIER LETTER CHINESE TONE YANG PING;Sk;0;ON;;;;;N;;;;; +A702;MODIFIER LETTER CHINESE TONE YIN SHANG;Sk;0;ON;;;;;N;;;;; +A703;MODIFIER LETTER CHINESE TONE YANG SHANG;Sk;0;ON;;;;;N;;;;; +A704;MODIFIER LETTER CHINESE TONE YIN QU;Sk;0;ON;;;;;N;;;;; +A705;MODIFIER LETTER CHINESE TONE YANG QU;Sk;0;ON;;;;;N;;;;; +A706;MODIFIER LETTER CHINESE TONE YIN RU;Sk;0;ON;;;;;N;;;;; +A707;MODIFIER LETTER CHINESE TONE YANG RU;Sk;0;ON;;;;;N;;;;; +A708;MODIFIER LETTER EXTRA-HIGH DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;; +A709;MODIFIER LETTER HIGH DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;; +A70A;MODIFIER LETTER MID DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;; +A70B;MODIFIER LETTER LOW DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;; +A70C;MODIFIER LETTER EXTRA-LOW DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;; +A70D;MODIFIER LETTER EXTRA-HIGH DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; +A70E;MODIFIER LETTER HIGH DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; +A70F;MODIFIER LETTER MID DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; +A710;MODIFIER LETTER LOW DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; +A711;MODIFIER LETTER EXTRA-LOW DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; +A712;MODIFIER LETTER EXTRA-HIGH LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; +A713;MODIFIER LETTER HIGH LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; +A714;MODIFIER LETTER MID LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; +A715;MODIFIER LETTER LOW LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; +A716;MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;; +A717;MODIFIER LETTER DOT VERTICAL BAR;Lm;0;ON;;;;;N;;;;; +A718;MODIFIER LETTER DOT SLASH;Lm;0;ON;;;;;N;;;;; +A719;MODIFIER LETTER DOT HORIZONTAL BAR;Lm;0;ON;;;;;N;;;;; +A71A;MODIFIER LETTER LOWER RIGHT CORNER ANGLE;Lm;0;ON;;;;;N;;;;; +A71B;MODIFIER LETTER RAISED UP ARROW;Lm;0;ON;;;;;N;;;;; +A71C;MODIFIER LETTER RAISED DOWN ARROW;Lm;0;ON;;;;;N;;;;; +A71D;MODIFIER LETTER RAISED EXCLAMATION MARK;Lm;0;ON;;;;;N;;;;; +A71E;MODIFIER LETTER RAISED INVERTED EXCLAMATION MARK;Lm;0;ON;;;;;N;;;;; +A71F;MODIFIER LETTER LOW INVERTED EXCLAMATION MARK;Lm;0;ON;;;;;N;;;;; +A720;MODIFIER LETTER STRESS AND HIGH TONE;Sk;0;ON;;;;;N;;;;; +A721;MODIFIER LETTER STRESS AND LOW TONE;Sk;0;ON;;;;;N;;;;; +A722;LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF;Lu;0;L;;;;;N;;;;A723; +A723;LATIN SMALL LETTER EGYPTOLOGICAL ALEF;Ll;0;L;;;;;N;;;A722;;A722 +A724;LATIN CAPITAL LETTER EGYPTOLOGICAL AIN;Lu;0;L;;;;;N;;;;A725; +A725;LATIN SMALL LETTER EGYPTOLOGICAL AIN;Ll;0;L;;;;;N;;;A724;;A724 +A726;LATIN CAPITAL LETTER HENG;Lu;0;L;;;;;N;;;;A727; +A727;LATIN SMALL LETTER HENG;Ll;0;L;;;;;N;;;A726;;A726 +A728;LATIN CAPITAL LETTER TZ;Lu;0;L;;;;;N;;;;A729; +A729;LATIN SMALL LETTER TZ;Ll;0;L;;;;;N;;;A728;;A728 +A72A;LATIN CAPITAL LETTER TRESILLO;Lu;0;L;;;;;N;;;;A72B; +A72B;LATIN SMALL LETTER TRESILLO;Ll;0;L;;;;;N;;;A72A;;A72A +A72C;LATIN CAPITAL LETTER CUATRILLO;Lu;0;L;;;;;N;;;;A72D; +A72D;LATIN SMALL LETTER CUATRILLO;Ll;0;L;;;;;N;;;A72C;;A72C +A72E;LATIN CAPITAL LETTER CUATRILLO WITH COMMA;Lu;0;L;;;;;N;;;;A72F; +A72F;LATIN SMALL LETTER CUATRILLO WITH COMMA;Ll;0;L;;;;;N;;;A72E;;A72E +A730;LATIN LETTER SMALL CAPITAL F;Ll;0;L;;;;;N;;;;; +A731;LATIN LETTER SMALL CAPITAL S;Ll;0;L;;;;;N;;;;; +A732;LATIN CAPITAL LETTER AA;Lu;0;L;;;;;N;;;;A733; +A733;LATIN SMALL LETTER AA;Ll;0;L;;;;;N;;;A732;;A732 +A734;LATIN CAPITAL LETTER AO;Lu;0;L;;;;;N;;;;A735; +A735;LATIN SMALL LETTER AO;Ll;0;L;;;;;N;;;A734;;A734 +A736;LATIN CAPITAL LETTER AU;Lu;0;L;;;;;N;;;;A737; +A737;LATIN SMALL LETTER AU;Ll;0;L;;;;;N;;;A736;;A736 +A738;LATIN CAPITAL LETTER AV;Lu;0;L;;;;;N;;;;A739; +A739;LATIN SMALL LETTER AV;Ll;0;L;;;;;N;;;A738;;A738 +A73A;LATIN CAPITAL LETTER AV WITH HORIZONTAL BAR;Lu;0;L;;;;;N;;;;A73B; +A73B;LATIN SMALL LETTER AV WITH HORIZONTAL BAR;Ll;0;L;;;;;N;;;A73A;;A73A +A73C;LATIN CAPITAL LETTER AY;Lu;0;L;;;;;N;;;;A73D; +A73D;LATIN SMALL LETTER AY;Ll;0;L;;;;;N;;;A73C;;A73C +A73E;LATIN CAPITAL LETTER REVERSED C WITH DOT;Lu;0;L;;;;;N;;;;A73F; +A73F;LATIN SMALL LETTER REVERSED C WITH DOT;Ll;0;L;;;;;N;;;A73E;;A73E +A740;LATIN CAPITAL LETTER K WITH STROKE;Lu;0;L;;;;;N;;;;A741; +A741;LATIN SMALL LETTER K WITH STROKE;Ll;0;L;;;;;N;;;A740;;A740 +A742;LATIN CAPITAL LETTER K WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A743; +A743;LATIN SMALL LETTER K WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;A742;;A742 +A744;LATIN CAPITAL LETTER K WITH STROKE AND DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A745; +A745;LATIN SMALL LETTER K WITH STROKE AND DIAGONAL STROKE;Ll;0;L;;;;;N;;;A744;;A744 +A746;LATIN CAPITAL LETTER BROKEN L;Lu;0;L;;;;;N;;;;A747; +A747;LATIN SMALL LETTER BROKEN L;Ll;0;L;;;;;N;;;A746;;A746 +A748;LATIN CAPITAL LETTER L WITH HIGH STROKE;Lu;0;L;;;;;N;;;;A749; +A749;LATIN SMALL LETTER L WITH HIGH STROKE;Ll;0;L;;;;;N;;;A748;;A748 +A74A;LATIN CAPITAL LETTER O WITH LONG STROKE OVERLAY;Lu;0;L;;;;;N;;;;A74B; +A74B;LATIN SMALL LETTER O WITH LONG STROKE OVERLAY;Ll;0;L;;;;;N;;;A74A;;A74A +A74C;LATIN CAPITAL LETTER O WITH LOOP;Lu;0;L;;;;;N;;;;A74D; +A74D;LATIN SMALL LETTER O WITH LOOP;Ll;0;L;;;;;N;;;A74C;;A74C +A74E;LATIN CAPITAL LETTER OO;Lu;0;L;;;;;N;;;;A74F; +A74F;LATIN SMALL LETTER OO;Ll;0;L;;;;;N;;;A74E;;A74E +A750;LATIN CAPITAL LETTER P WITH STROKE THROUGH DESCENDER;Lu;0;L;;;;;N;;;;A751; +A751;LATIN SMALL LETTER P WITH STROKE THROUGH DESCENDER;Ll;0;L;;;;;N;;;A750;;A750 +A752;LATIN CAPITAL LETTER P WITH FLOURISH;Lu;0;L;;;;;N;;;;A753; +A753;LATIN SMALL LETTER P WITH FLOURISH;Ll;0;L;;;;;N;;;A752;;A752 +A754;LATIN CAPITAL LETTER P WITH SQUIRREL TAIL;Lu;0;L;;;;;N;;;;A755; +A755;LATIN SMALL LETTER P WITH SQUIRREL TAIL;Ll;0;L;;;;;N;;;A754;;A754 +A756;LATIN CAPITAL LETTER Q WITH STROKE THROUGH DESCENDER;Lu;0;L;;;;;N;;;;A757; +A757;LATIN SMALL LETTER Q WITH STROKE THROUGH DESCENDER;Ll;0;L;;;;;N;;;A756;;A756 +A758;LATIN CAPITAL LETTER Q WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A759; +A759;LATIN SMALL LETTER Q WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;A758;;A758 +A75A;LATIN CAPITAL LETTER R ROTUNDA;Lu;0;L;;;;;N;;;;A75B; +A75B;LATIN SMALL LETTER R ROTUNDA;Ll;0;L;;;;;N;;;A75A;;A75A +A75C;LATIN CAPITAL LETTER RUM ROTUNDA;Lu;0;L;;;;;N;;;;A75D; +A75D;LATIN SMALL LETTER RUM ROTUNDA;Ll;0;L;;;;;N;;;A75C;;A75C +A75E;LATIN CAPITAL LETTER V WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A75F; +A75F;LATIN SMALL LETTER V WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;A75E;;A75E +A760;LATIN CAPITAL LETTER VY;Lu;0;L;;;;;N;;;;A761; +A761;LATIN SMALL LETTER VY;Ll;0;L;;;;;N;;;A760;;A760 +A762;LATIN CAPITAL LETTER VISIGOTHIC Z;Lu;0;L;;;;;N;;;;A763; +A763;LATIN SMALL LETTER VISIGOTHIC Z;Ll;0;L;;;;;N;;;A762;;A762 +A764;LATIN CAPITAL LETTER THORN WITH STROKE;Lu;0;L;;;;;N;;;;A765; +A765;LATIN SMALL LETTER THORN WITH STROKE;Ll;0;L;;;;;N;;;A764;;A764 +A766;LATIN CAPITAL LETTER THORN WITH STROKE THROUGH DESCENDER;Lu;0;L;;;;;N;;;;A767; +A767;LATIN SMALL LETTER THORN WITH STROKE THROUGH DESCENDER;Ll;0;L;;;;;N;;;A766;;A766 +A768;LATIN CAPITAL LETTER VEND;Lu;0;L;;;;;N;;;;A769; +A769;LATIN SMALL LETTER VEND;Ll;0;L;;;;;N;;;A768;;A768 +A76A;LATIN CAPITAL LETTER ET;Lu;0;L;;;;;N;;;;A76B; +A76B;LATIN SMALL LETTER ET;Ll;0;L;;;;;N;;;A76A;;A76A +A76C;LATIN CAPITAL LETTER IS;Lu;0;L;;;;;N;;;;A76D; +A76D;LATIN SMALL LETTER IS;Ll;0;L;;;;;N;;;A76C;;A76C +A76E;LATIN CAPITAL LETTER CON;Lu;0;L;;;;;N;;;;A76F; +A76F;LATIN SMALL LETTER CON;Ll;0;L;;;;;N;;;A76E;;A76E +A770;MODIFIER LETTER US;Lm;0;L;<super> A76F;;;;N;;;;; +A771;LATIN SMALL LETTER DUM;Ll;0;L;;;;;N;;;;; +A772;LATIN SMALL LETTER LUM;Ll;0;L;;;;;N;;;;; +A773;LATIN SMALL LETTER MUM;Ll;0;L;;;;;N;;;;; +A774;LATIN SMALL LETTER NUM;Ll;0;L;;;;;N;;;;; +A775;LATIN SMALL LETTER RUM;Ll;0;L;;;;;N;;;;; +A776;LATIN LETTER SMALL CAPITAL RUM;Ll;0;L;;;;;N;;;;; +A777;LATIN SMALL LETTER TUM;Ll;0;L;;;;;N;;;;; +A778;LATIN SMALL LETTER UM;Ll;0;L;;;;;N;;;;; +A779;LATIN CAPITAL LETTER INSULAR D;Lu;0;L;;;;;N;;;;A77A; +A77A;LATIN SMALL LETTER INSULAR D;Ll;0;L;;;;;N;;;A779;;A779 +A77B;LATIN CAPITAL LETTER INSULAR F;Lu;0;L;;;;;N;;;;A77C; +A77C;LATIN SMALL LETTER INSULAR F;Ll;0;L;;;;;N;;;A77B;;A77B +A77D;LATIN CAPITAL LETTER INSULAR G;Lu;0;L;;;;;N;;;;1D79; +A77E;LATIN CAPITAL LETTER TURNED INSULAR G;Lu;0;L;;;;;N;;;;A77F; +A77F;LATIN SMALL LETTER TURNED INSULAR G;Ll;0;L;;;;;N;;;A77E;;A77E +A780;LATIN CAPITAL LETTER TURNED L;Lu;0;L;;;;;N;;;;A781; +A781;LATIN SMALL LETTER TURNED L;Ll;0;L;;;;;N;;;A780;;A780 +A782;LATIN CAPITAL LETTER INSULAR R;Lu;0;L;;;;;N;;;;A783; +A783;LATIN SMALL LETTER INSULAR R;Ll;0;L;;;;;N;;;A782;;A782 +A784;LATIN CAPITAL LETTER INSULAR S;Lu;0;L;;;;;N;;;;A785; +A785;LATIN SMALL LETTER INSULAR S;Ll;0;L;;;;;N;;;A784;;A784 +A786;LATIN CAPITAL LETTER INSULAR T;Lu;0;L;;;;;N;;;;A787; +A787;LATIN SMALL LETTER INSULAR T;Ll;0;L;;;;;N;;;A786;;A786 +A788;MODIFIER LETTER LOW CIRCUMFLEX ACCENT;Lm;0;ON;;;;;N;;;;; +A789;MODIFIER LETTER COLON;Sk;0;L;;;;;N;;;;; +A78A;MODIFIER LETTER SHORT EQUALS SIGN;Sk;0;L;;;;;N;;;;; +A78B;LATIN CAPITAL LETTER SALTILLO;Lu;0;L;;;;;N;;;;A78C; +A78C;LATIN SMALL LETTER SALTILLO;Ll;0;L;;;;;N;;;A78B;;A78B +A78D;LATIN CAPITAL LETTER TURNED H;Lu;0;L;;;;;N;;;;0265; +A78E;LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT;Ll;0;L;;;;;N;;;;; +A78F;LATIN LETTER SINOLOGICAL DOT;Lo;0;L;;;;;N;;;;; +A790;LATIN CAPITAL LETTER N WITH DESCENDER;Lu;0;L;;;;;N;;;;A791; +A791;LATIN SMALL LETTER N WITH DESCENDER;Ll;0;L;;;;;N;;;A790;;A790 +A792;LATIN CAPITAL LETTER C WITH BAR;Lu;0;L;;;;;N;;;;A793; +A793;LATIN SMALL LETTER C WITH BAR;Ll;0;L;;;;;N;;;A792;;A792 +A794;LATIN SMALL LETTER C WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +A795;LATIN SMALL LETTER H WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;; +A796;LATIN CAPITAL LETTER B WITH FLOURISH;Lu;0;L;;;;;N;;;;A797; +A797;LATIN SMALL LETTER B WITH FLOURISH;Ll;0;L;;;;;N;;;A796;;A796 +A798;LATIN CAPITAL LETTER F WITH STROKE;Lu;0;L;;;;;N;;;;A799; +A799;LATIN SMALL LETTER F WITH STROKE;Ll;0;L;;;;;N;;;A798;;A798 +A79A;LATIN CAPITAL LETTER VOLAPUK AE;Lu;0;L;;;;;N;;;;A79B; +A79B;LATIN SMALL LETTER VOLAPUK AE;Ll;0;L;;;;;N;;;A79A;;A79A +A79C;LATIN CAPITAL LETTER VOLAPUK OE;Lu;0;L;;;;;N;;;;A79D; +A79D;LATIN SMALL LETTER VOLAPUK OE;Ll;0;L;;;;;N;;;A79C;;A79C +A79E;LATIN CAPITAL LETTER VOLAPUK UE;Lu;0;L;;;;;N;;;;A79F; +A79F;LATIN SMALL LETTER VOLAPUK UE;Ll;0;L;;;;;N;;;A79E;;A79E +A7A0;LATIN CAPITAL LETTER G WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A1; +A7A1;LATIN SMALL LETTER G WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A0;;A7A0 +A7A2;LATIN CAPITAL LETTER K WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A3; +A7A3;LATIN SMALL LETTER K WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A2;;A7A2 +A7A4;LATIN CAPITAL LETTER N WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A5; +A7A5;LATIN SMALL LETTER N WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A4;;A7A4 +A7A6;LATIN CAPITAL LETTER R WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A7; +A7A7;LATIN SMALL LETTER R WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A6;;A7A6 +A7A8;LATIN CAPITAL LETTER S WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A9; +A7A9;LATIN SMALL LETTER S WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A8;;A7A8 +A7AA;LATIN CAPITAL LETTER H WITH HOOK;Lu;0;L;;;;;N;;;;0266; +A7AB;LATIN CAPITAL LETTER REVERSED OPEN E;Lu;0;L;;;;;N;;;;025C; +A7AC;LATIN CAPITAL LETTER SCRIPT G;Lu;0;L;;;;;N;;;;0261; +A7AD;LATIN CAPITAL LETTER L WITH BELT;Lu;0;L;;;;;N;;;;026C; +A7AE;LATIN CAPITAL LETTER SMALL CAPITAL I;Lu;0;L;;;;;N;;;;026A; +A7B0;LATIN CAPITAL LETTER TURNED K;Lu;0;L;;;;;N;;;;029E; +A7B1;LATIN CAPITAL LETTER TURNED T;Lu;0;L;;;;;N;;;;0287; +A7B2;LATIN CAPITAL LETTER J WITH CROSSED-TAIL;Lu;0;L;;;;;N;;;;029D; +A7B3;LATIN CAPITAL LETTER CHI;Lu;0;L;;;;;N;;;;AB53; +A7B4;LATIN CAPITAL LETTER BETA;Lu;0;L;;;;;N;;;;A7B5; +A7B5;LATIN SMALL LETTER BETA;Ll;0;L;;;;;N;;;A7B4;;A7B4 +A7B6;LATIN CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;A7B7; +A7B7;LATIN SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;A7B6;;A7B6 +A7F7;LATIN EPIGRAPHIC LETTER SIDEWAYS I;Lo;0;L;;;;;N;;;;; +A7F8;MODIFIER LETTER CAPITAL H WITH STROKE;Lm;0;L;<super> 0126;;;;N;;;;; +A7F9;MODIFIER LETTER SMALL LIGATURE OE;Lm;0;L;<super> 0153;;;;N;;;;; +A7FA;LATIN LETTER SMALL CAPITAL TURNED M;Ll;0;L;;;;;N;;;;; +A7FB;LATIN EPIGRAPHIC LETTER REVERSED F;Lo;0;L;;;;;N;;;;; +A7FC;LATIN EPIGRAPHIC LETTER REVERSED P;Lo;0;L;;;;;N;;;;; +A7FD;LATIN EPIGRAPHIC LETTER INVERTED M;Lo;0;L;;;;;N;;;;; +A7FE;LATIN EPIGRAPHIC LETTER I LONGA;Lo;0;L;;;;;N;;;;; +A7FF;LATIN EPIGRAPHIC LETTER ARCHAIC M;Lo;0;L;;;;;N;;;;; +A800;SYLOTI NAGRI LETTER A;Lo;0;L;;;;;N;;;;; +A801;SYLOTI NAGRI LETTER I;Lo;0;L;;;;;N;;;;; +A802;SYLOTI NAGRI SIGN DVISVARA;Mn;0;NSM;;;;;N;;;;; +A803;SYLOTI NAGRI LETTER U;Lo;0;L;;;;;N;;;;; +A804;SYLOTI NAGRI LETTER E;Lo;0;L;;;;;N;;;;; +A805;SYLOTI NAGRI LETTER O;Lo;0;L;;;;;N;;;;; +A806;SYLOTI NAGRI SIGN HASANTA;Mn;9;NSM;;;;;N;;;;; +A807;SYLOTI NAGRI LETTER KO;Lo;0;L;;;;;N;;;;; +A808;SYLOTI NAGRI LETTER KHO;Lo;0;L;;;;;N;;;;; +A809;SYLOTI NAGRI LETTER GO;Lo;0;L;;;;;N;;;;; +A80A;SYLOTI NAGRI LETTER GHO;Lo;0;L;;;;;N;;;;; +A80B;SYLOTI NAGRI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +A80C;SYLOTI NAGRI LETTER CO;Lo;0;L;;;;;N;;;;; +A80D;SYLOTI NAGRI LETTER CHO;Lo;0;L;;;;;N;;;;; +A80E;SYLOTI NAGRI LETTER JO;Lo;0;L;;;;;N;;;;; +A80F;SYLOTI NAGRI LETTER JHO;Lo;0;L;;;;;N;;;;; +A810;SYLOTI NAGRI LETTER TTO;Lo;0;L;;;;;N;;;;; +A811;SYLOTI NAGRI LETTER TTHO;Lo;0;L;;;;;N;;;;; +A812;SYLOTI NAGRI LETTER DDO;Lo;0;L;;;;;N;;;;; +A813;SYLOTI NAGRI LETTER DDHO;Lo;0;L;;;;;N;;;;; +A814;SYLOTI NAGRI LETTER TO;Lo;0;L;;;;;N;;;;; +A815;SYLOTI NAGRI LETTER THO;Lo;0;L;;;;;N;;;;; +A816;SYLOTI NAGRI LETTER DO;Lo;0;L;;;;;N;;;;; +A817;SYLOTI NAGRI LETTER DHO;Lo;0;L;;;;;N;;;;; +A818;SYLOTI NAGRI LETTER NO;Lo;0;L;;;;;N;;;;; +A819;SYLOTI NAGRI LETTER PO;Lo;0;L;;;;;N;;;;; +A81A;SYLOTI NAGRI LETTER PHO;Lo;0;L;;;;;N;;;;; +A81B;SYLOTI NAGRI LETTER BO;Lo;0;L;;;;;N;;;;; +A81C;SYLOTI NAGRI LETTER BHO;Lo;0;L;;;;;N;;;;; +A81D;SYLOTI NAGRI LETTER MO;Lo;0;L;;;;;N;;;;; +A81E;SYLOTI NAGRI LETTER RO;Lo;0;L;;;;;N;;;;; +A81F;SYLOTI NAGRI LETTER LO;Lo;0;L;;;;;N;;;;; +A820;SYLOTI NAGRI LETTER RRO;Lo;0;L;;;;;N;;;;; +A821;SYLOTI NAGRI LETTER SO;Lo;0;L;;;;;N;;;;; +A822;SYLOTI NAGRI LETTER HO;Lo;0;L;;;;;N;;;;; +A823;SYLOTI NAGRI VOWEL SIGN A;Mc;0;L;;;;;N;;;;; +A824;SYLOTI NAGRI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +A825;SYLOTI NAGRI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +A826;SYLOTI NAGRI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +A827;SYLOTI NAGRI VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; +A828;SYLOTI NAGRI POETRY MARK-1;So;0;ON;;;;;N;;;;; +A829;SYLOTI NAGRI POETRY MARK-2;So;0;ON;;;;;N;;;;; +A82A;SYLOTI NAGRI POETRY MARK-3;So;0;ON;;;;;N;;;;; +A82B;SYLOTI NAGRI POETRY MARK-4;So;0;ON;;;;;N;;;;; +A830;NORTH INDIC FRACTION ONE QUARTER;No;0;L;;;;1/4;N;;;;; +A831;NORTH INDIC FRACTION ONE HALF;No;0;L;;;;1/2;N;;;;; +A832;NORTH INDIC FRACTION THREE QUARTERS;No;0;L;;;;3/4;N;;;;; +A833;NORTH INDIC FRACTION ONE SIXTEENTH;No;0;L;;;;1/16;N;;;;; +A834;NORTH INDIC FRACTION ONE EIGHTH;No;0;L;;;;1/8;N;;;;; +A835;NORTH INDIC FRACTION THREE SIXTEENTHS;No;0;L;;;;3/16;N;;;;; +A836;NORTH INDIC QUARTER MARK;So;0;L;;;;;N;;;;; +A837;NORTH INDIC PLACEHOLDER MARK;So;0;L;;;;;N;;;;; +A838;NORTH INDIC RUPEE MARK;Sc;0;ET;;;;;N;;;;; +A839;NORTH INDIC QUANTITY MARK;So;0;ET;;;;;N;;;;; +A840;PHAGS-PA LETTER KA;Lo;0;L;;;;;N;;;;; +A841;PHAGS-PA LETTER KHA;Lo;0;L;;;;;N;;;;; +A842;PHAGS-PA LETTER GA;Lo;0;L;;;;;N;;;;; +A843;PHAGS-PA LETTER NGA;Lo;0;L;;;;;N;;;;; +A844;PHAGS-PA LETTER CA;Lo;0;L;;;;;N;;;;; +A845;PHAGS-PA LETTER CHA;Lo;0;L;;;;;N;;;;; +A846;PHAGS-PA LETTER JA;Lo;0;L;;;;;N;;;;; +A847;PHAGS-PA LETTER NYA;Lo;0;L;;;;;N;;;;; +A848;PHAGS-PA LETTER TA;Lo;0;L;;;;;N;;;;; +A849;PHAGS-PA LETTER THA;Lo;0;L;;;;;N;;;;; +A84A;PHAGS-PA LETTER DA;Lo;0;L;;;;;N;;;;; +A84B;PHAGS-PA LETTER NA;Lo;0;L;;;;;N;;;;; +A84C;PHAGS-PA LETTER PA;Lo;0;L;;;;;N;;;;; +A84D;PHAGS-PA LETTER PHA;Lo;0;L;;;;;N;;;;; +A84E;PHAGS-PA LETTER BA;Lo;0;L;;;;;N;;;;; +A84F;PHAGS-PA LETTER MA;Lo;0;L;;;;;N;;;;; +A850;PHAGS-PA LETTER TSA;Lo;0;L;;;;;N;;;;; +A851;PHAGS-PA LETTER TSHA;Lo;0;L;;;;;N;;;;; +A852;PHAGS-PA LETTER DZA;Lo;0;L;;;;;N;;;;; +A853;PHAGS-PA LETTER WA;Lo;0;L;;;;;N;;;;; +A854;PHAGS-PA LETTER ZHA;Lo;0;L;;;;;N;;;;; +A855;PHAGS-PA LETTER ZA;Lo;0;L;;;;;N;;;;; +A856;PHAGS-PA LETTER SMALL A;Lo;0;L;;;;;N;;;;; +A857;PHAGS-PA LETTER YA;Lo;0;L;;;;;N;;;;; +A858;PHAGS-PA LETTER RA;Lo;0;L;;;;;N;;;;; +A859;PHAGS-PA LETTER LA;Lo;0;L;;;;;N;;;;; +A85A;PHAGS-PA LETTER SHA;Lo;0;L;;;;;N;;;;; +A85B;PHAGS-PA LETTER SA;Lo;0;L;;;;;N;;;;; +A85C;PHAGS-PA LETTER HA;Lo;0;L;;;;;N;;;;; +A85D;PHAGS-PA LETTER A;Lo;0;L;;;;;N;;;;; +A85E;PHAGS-PA LETTER I;Lo;0;L;;;;;N;;;;; +A85F;PHAGS-PA LETTER U;Lo;0;L;;;;;N;;;;; +A860;PHAGS-PA LETTER E;Lo;0;L;;;;;N;;;;; +A861;PHAGS-PA LETTER O;Lo;0;L;;;;;N;;;;; +A862;PHAGS-PA LETTER QA;Lo;0;L;;;;;N;;;;; +A863;PHAGS-PA LETTER XA;Lo;0;L;;;;;N;;;;; +A864;PHAGS-PA LETTER FA;Lo;0;L;;;;;N;;;;; +A865;PHAGS-PA LETTER GGA;Lo;0;L;;;;;N;;;;; +A866;PHAGS-PA LETTER EE;Lo;0;L;;;;;N;;;;; +A867;PHAGS-PA SUBJOINED LETTER WA;Lo;0;L;;;;;N;;;;; +A868;PHAGS-PA SUBJOINED LETTER YA;Lo;0;L;;;;;N;;;;; +A869;PHAGS-PA LETTER TTA;Lo;0;L;;;;;N;;;;; +A86A;PHAGS-PA LETTER TTHA;Lo;0;L;;;;;N;;;;; +A86B;PHAGS-PA LETTER DDA;Lo;0;L;;;;;N;;;;; +A86C;PHAGS-PA LETTER NNA;Lo;0;L;;;;;N;;;;; +A86D;PHAGS-PA LETTER ALTERNATE YA;Lo;0;L;;;;;N;;;;; +A86E;PHAGS-PA LETTER VOICELESS SHA;Lo;0;L;;;;;N;;;;; +A86F;PHAGS-PA LETTER VOICED HA;Lo;0;L;;;;;N;;;;; +A870;PHAGS-PA LETTER ASPIRATED FA;Lo;0;L;;;;;N;;;;; +A871;PHAGS-PA SUBJOINED LETTER RA;Lo;0;L;;;;;N;;;;; +A872;PHAGS-PA SUPERFIXED LETTER RA;Lo;0;L;;;;;N;;;;; +A873;PHAGS-PA LETTER CANDRABINDU;Lo;0;L;;;;;N;;;;; +A874;PHAGS-PA SINGLE HEAD MARK;Po;0;ON;;;;;N;;;;; +A875;PHAGS-PA DOUBLE HEAD MARK;Po;0;ON;;;;;N;;;;; +A876;PHAGS-PA MARK SHAD;Po;0;ON;;;;;N;;;;; +A877;PHAGS-PA MARK DOUBLE SHAD;Po;0;ON;;;;;N;;;;; +A880;SAURASHTRA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; +A881;SAURASHTRA SIGN VISARGA;Mc;0;L;;;;;N;;;;; +A882;SAURASHTRA LETTER A;Lo;0;L;;;;;N;;;;; +A883;SAURASHTRA LETTER AA;Lo;0;L;;;;;N;;;;; +A884;SAURASHTRA LETTER I;Lo;0;L;;;;;N;;;;; +A885;SAURASHTRA LETTER II;Lo;0;L;;;;;N;;;;; +A886;SAURASHTRA LETTER U;Lo;0;L;;;;;N;;;;; +A887;SAURASHTRA LETTER UU;Lo;0;L;;;;;N;;;;; +A888;SAURASHTRA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +A889;SAURASHTRA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +A88A;SAURASHTRA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +A88B;SAURASHTRA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +A88C;SAURASHTRA LETTER E;Lo;0;L;;;;;N;;;;; +A88D;SAURASHTRA LETTER EE;Lo;0;L;;;;;N;;;;; +A88E;SAURASHTRA LETTER AI;Lo;0;L;;;;;N;;;;; +A88F;SAURASHTRA LETTER O;Lo;0;L;;;;;N;;;;; +A890;SAURASHTRA LETTER OO;Lo;0;L;;;;;N;;;;; +A891;SAURASHTRA LETTER AU;Lo;0;L;;;;;N;;;;; +A892;SAURASHTRA LETTER KA;Lo;0;L;;;;;N;;;;; +A893;SAURASHTRA LETTER KHA;Lo;0;L;;;;;N;;;;; +A894;SAURASHTRA LETTER GA;Lo;0;L;;;;;N;;;;; +A895;SAURASHTRA LETTER GHA;Lo;0;L;;;;;N;;;;; +A896;SAURASHTRA LETTER NGA;Lo;0;L;;;;;N;;;;; +A897;SAURASHTRA LETTER CA;Lo;0;L;;;;;N;;;;; +A898;SAURASHTRA LETTER CHA;Lo;0;L;;;;;N;;;;; +A899;SAURASHTRA LETTER JA;Lo;0;L;;;;;N;;;;; +A89A;SAURASHTRA LETTER JHA;Lo;0;L;;;;;N;;;;; +A89B;SAURASHTRA LETTER NYA;Lo;0;L;;;;;N;;;;; +A89C;SAURASHTRA LETTER TTA;Lo;0;L;;;;;N;;;;; +A89D;SAURASHTRA LETTER TTHA;Lo;0;L;;;;;N;;;;; +A89E;SAURASHTRA LETTER DDA;Lo;0;L;;;;;N;;;;; +A89F;SAURASHTRA LETTER DDHA;Lo;0;L;;;;;N;;;;; +A8A0;SAURASHTRA LETTER NNA;Lo;0;L;;;;;N;;;;; +A8A1;SAURASHTRA LETTER TA;Lo;0;L;;;;;N;;;;; +A8A2;SAURASHTRA LETTER THA;Lo;0;L;;;;;N;;;;; +A8A3;SAURASHTRA LETTER DA;Lo;0;L;;;;;N;;;;; +A8A4;SAURASHTRA LETTER DHA;Lo;0;L;;;;;N;;;;; +A8A5;SAURASHTRA LETTER NA;Lo;0;L;;;;;N;;;;; +A8A6;SAURASHTRA LETTER PA;Lo;0;L;;;;;N;;;;; +A8A7;SAURASHTRA LETTER PHA;Lo;0;L;;;;;N;;;;; +A8A8;SAURASHTRA LETTER BA;Lo;0;L;;;;;N;;;;; +A8A9;SAURASHTRA LETTER BHA;Lo;0;L;;;;;N;;;;; +A8AA;SAURASHTRA LETTER MA;Lo;0;L;;;;;N;;;;; +A8AB;SAURASHTRA LETTER YA;Lo;0;L;;;;;N;;;;; +A8AC;SAURASHTRA LETTER RA;Lo;0;L;;;;;N;;;;; +A8AD;SAURASHTRA LETTER LA;Lo;0;L;;;;;N;;;;; +A8AE;SAURASHTRA LETTER VA;Lo;0;L;;;;;N;;;;; +A8AF;SAURASHTRA LETTER SHA;Lo;0;L;;;;;N;;;;; +A8B0;SAURASHTRA LETTER SSA;Lo;0;L;;;;;N;;;;; +A8B1;SAURASHTRA LETTER SA;Lo;0;L;;;;;N;;;;; +A8B2;SAURASHTRA LETTER HA;Lo;0;L;;;;;N;;;;; +A8B3;SAURASHTRA LETTER LLA;Lo;0;L;;;;;N;;;;; +A8B4;SAURASHTRA CONSONANT SIGN HAARU;Mc;0;L;;;;;N;;;;; +A8B5;SAURASHTRA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +A8B6;SAURASHTRA VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +A8B7;SAURASHTRA VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +A8B8;SAURASHTRA VOWEL SIGN U;Mc;0;L;;;;;N;;;;; +A8B9;SAURASHTRA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; +A8BA;SAURASHTRA VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;; +A8BB;SAURASHTRA VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;; +A8BC;SAURASHTRA VOWEL SIGN VOCALIC L;Mc;0;L;;;;;N;;;;; +A8BD;SAURASHTRA VOWEL SIGN VOCALIC LL;Mc;0;L;;;;;N;;;;; +A8BE;SAURASHTRA VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +A8BF;SAURASHTRA VOWEL SIGN EE;Mc;0;L;;;;;N;;;;; +A8C0;SAURASHTRA VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +A8C1;SAURASHTRA VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +A8C2;SAURASHTRA VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; +A8C3;SAURASHTRA VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +A8C4;SAURASHTRA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +A8C5;SAURASHTRA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +A8CE;SAURASHTRA DANDA;Po;0;L;;;;;N;;;;; +A8CF;SAURASHTRA DOUBLE DANDA;Po;0;L;;;;;N;;;;; +A8D0;SAURASHTRA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +A8D1;SAURASHTRA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +A8D2;SAURASHTRA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +A8D3;SAURASHTRA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +A8D4;SAURASHTRA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +A8D5;SAURASHTRA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +A8D6;SAURASHTRA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +A8D7;SAURASHTRA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +A8D8;SAURASHTRA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +A8D9;SAURASHTRA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +A8E0;COMBINING DEVANAGARI DIGIT ZERO;Mn;230;NSM;;;;;N;;;;; +A8E1;COMBINING DEVANAGARI DIGIT ONE;Mn;230;NSM;;;;;N;;;;; +A8E2;COMBINING DEVANAGARI DIGIT TWO;Mn;230;NSM;;;;;N;;;;; +A8E3;COMBINING DEVANAGARI DIGIT THREE;Mn;230;NSM;;;;;N;;;;; +A8E4;COMBINING DEVANAGARI DIGIT FOUR;Mn;230;NSM;;;;;N;;;;; +A8E5;COMBINING DEVANAGARI DIGIT FIVE;Mn;230;NSM;;;;;N;;;;; +A8E6;COMBINING DEVANAGARI DIGIT SIX;Mn;230;NSM;;;;;N;;;;; +A8E7;COMBINING DEVANAGARI DIGIT SEVEN;Mn;230;NSM;;;;;N;;;;; +A8E8;COMBINING DEVANAGARI DIGIT EIGHT;Mn;230;NSM;;;;;N;;;;; +A8E9;COMBINING DEVANAGARI DIGIT NINE;Mn;230;NSM;;;;;N;;;;; +A8EA;COMBINING DEVANAGARI LETTER A;Mn;230;NSM;;;;;N;;;;; +A8EB;COMBINING DEVANAGARI LETTER U;Mn;230;NSM;;;;;N;;;;; +A8EC;COMBINING DEVANAGARI LETTER KA;Mn;230;NSM;;;;;N;;;;; +A8ED;COMBINING DEVANAGARI LETTER NA;Mn;230;NSM;;;;;N;;;;; +A8EE;COMBINING DEVANAGARI LETTER PA;Mn;230;NSM;;;;;N;;;;; +A8EF;COMBINING DEVANAGARI LETTER RA;Mn;230;NSM;;;;;N;;;;; +A8F0;COMBINING DEVANAGARI LETTER VI;Mn;230;NSM;;;;;N;;;;; +A8F1;COMBINING DEVANAGARI SIGN AVAGRAHA;Mn;230;NSM;;;;;N;;;;; +A8F2;DEVANAGARI SIGN SPACING CANDRABINDU;Lo;0;L;;;;;N;;;;; +A8F3;DEVANAGARI SIGN CANDRABINDU VIRAMA;Lo;0;L;;;;;N;;;;; +A8F4;DEVANAGARI SIGN DOUBLE CANDRABINDU VIRAMA;Lo;0;L;;;;;N;;;;; +A8F5;DEVANAGARI SIGN CANDRABINDU TWO;Lo;0;L;;;;;N;;;;; +A8F6;DEVANAGARI SIGN CANDRABINDU THREE;Lo;0;L;;;;;N;;;;; +A8F7;DEVANAGARI SIGN CANDRABINDU AVAGRAHA;Lo;0;L;;;;;N;;;;; +A8F8;DEVANAGARI SIGN PUSHPIKA;Po;0;L;;;;;N;;;;; +A8F9;DEVANAGARI GAP FILLER;Po;0;L;;;;;N;;;;; +A8FA;DEVANAGARI CARET;Po;0;L;;;;;N;;;;; +A8FB;DEVANAGARI HEADSTROKE;Lo;0;L;;;;;N;;;;; +A8FC;DEVANAGARI SIGN SIDDHAM;Po;0;L;;;;;N;;;;; +A8FD;DEVANAGARI JAIN OM;Lo;0;L;;;;;N;;;;; +A900;KAYAH LI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +A901;KAYAH LI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +A902;KAYAH LI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +A903;KAYAH LI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +A904;KAYAH LI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +A905;KAYAH LI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +A906;KAYAH LI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +A907;KAYAH LI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +A908;KAYAH LI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +A909;KAYAH LI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +A90A;KAYAH LI LETTER KA;Lo;0;L;;;;;N;;;;; +A90B;KAYAH LI LETTER KHA;Lo;0;L;;;;;N;;;;; +A90C;KAYAH LI LETTER GA;Lo;0;L;;;;;N;;;;; +A90D;KAYAH LI LETTER NGA;Lo;0;L;;;;;N;;;;; +A90E;KAYAH LI LETTER SA;Lo;0;L;;;;;N;;;;; +A90F;KAYAH LI LETTER SHA;Lo;0;L;;;;;N;;;;; +A910;KAYAH LI LETTER ZA;Lo;0;L;;;;;N;;;;; +A911;KAYAH LI LETTER NYA;Lo;0;L;;;;;N;;;;; +A912;KAYAH LI LETTER TA;Lo;0;L;;;;;N;;;;; +A913;KAYAH LI LETTER HTA;Lo;0;L;;;;;N;;;;; +A914;KAYAH LI LETTER NA;Lo;0;L;;;;;N;;;;; +A915;KAYAH LI LETTER PA;Lo;0;L;;;;;N;;;;; +A916;KAYAH LI LETTER PHA;Lo;0;L;;;;;N;;;;; +A917;KAYAH LI LETTER MA;Lo;0;L;;;;;N;;;;; +A918;KAYAH LI LETTER DA;Lo;0;L;;;;;N;;;;; +A919;KAYAH LI LETTER BA;Lo;0;L;;;;;N;;;;; +A91A;KAYAH LI LETTER RA;Lo;0;L;;;;;N;;;;; +A91B;KAYAH LI LETTER YA;Lo;0;L;;;;;N;;;;; +A91C;KAYAH LI LETTER LA;Lo;0;L;;;;;N;;;;; +A91D;KAYAH LI LETTER WA;Lo;0;L;;;;;N;;;;; +A91E;KAYAH LI LETTER THA;Lo;0;L;;;;;N;;;;; +A91F;KAYAH LI LETTER HA;Lo;0;L;;;;;N;;;;; +A920;KAYAH LI LETTER VA;Lo;0;L;;;;;N;;;;; +A921;KAYAH LI LETTER CA;Lo;0;L;;;;;N;;;;; +A922;KAYAH LI LETTER A;Lo;0;L;;;;;N;;;;; +A923;KAYAH LI LETTER OE;Lo;0;L;;;;;N;;;;; +A924;KAYAH LI LETTER I;Lo;0;L;;;;;N;;;;; +A925;KAYAH LI LETTER OO;Lo;0;L;;;;;N;;;;; +A926;KAYAH LI VOWEL UE;Mn;0;NSM;;;;;N;;;;; +A927;KAYAH LI VOWEL E;Mn;0;NSM;;;;;N;;;;; +A928;KAYAH LI VOWEL U;Mn;0;NSM;;;;;N;;;;; +A929;KAYAH LI VOWEL EE;Mn;0;NSM;;;;;N;;;;; +A92A;KAYAH LI VOWEL O;Mn;0;NSM;;;;;N;;;;; +A92B;KAYAH LI TONE PLOPHU;Mn;220;NSM;;;;;N;;;;; +A92C;KAYAH LI TONE CALYA;Mn;220;NSM;;;;;N;;;;; +A92D;KAYAH LI TONE CALYA PLOPHU;Mn;220;NSM;;;;;N;;;;; +A92E;KAYAH LI SIGN CWI;Po;0;L;;;;;N;;;;; +A92F;KAYAH LI SIGN SHYA;Po;0;L;;;;;N;;;;; +A930;REJANG LETTER KA;Lo;0;L;;;;;N;;;;; +A931;REJANG LETTER GA;Lo;0;L;;;;;N;;;;; +A932;REJANG LETTER NGA;Lo;0;L;;;;;N;;;;; +A933;REJANG LETTER TA;Lo;0;L;;;;;N;;;;; +A934;REJANG LETTER DA;Lo;0;L;;;;;N;;;;; +A935;REJANG LETTER NA;Lo;0;L;;;;;N;;;;; +A936;REJANG LETTER PA;Lo;0;L;;;;;N;;;;; +A937;REJANG LETTER BA;Lo;0;L;;;;;N;;;;; +A938;REJANG LETTER MA;Lo;0;L;;;;;N;;;;; +A939;REJANG LETTER CA;Lo;0;L;;;;;N;;;;; +A93A;REJANG LETTER JA;Lo;0;L;;;;;N;;;;; +A93B;REJANG LETTER NYA;Lo;0;L;;;;;N;;;;; +A93C;REJANG LETTER SA;Lo;0;L;;;;;N;;;;; +A93D;REJANG LETTER RA;Lo;0;L;;;;;N;;;;; +A93E;REJANG LETTER LA;Lo;0;L;;;;;N;;;;; +A93F;REJANG LETTER YA;Lo;0;L;;;;;N;;;;; +A940;REJANG LETTER WA;Lo;0;L;;;;;N;;;;; +A941;REJANG LETTER HA;Lo;0;L;;;;;N;;;;; +A942;REJANG LETTER MBA;Lo;0;L;;;;;N;;;;; +A943;REJANG LETTER NGGA;Lo;0;L;;;;;N;;;;; +A944;REJANG LETTER NDA;Lo;0;L;;;;;N;;;;; +A945;REJANG LETTER NYJA;Lo;0;L;;;;;N;;;;; +A946;REJANG LETTER A;Lo;0;L;;;;;N;;;;; +A947;REJANG VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +A948;REJANG VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +A949;REJANG VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +A94A;REJANG VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +A94B;REJANG VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; +A94C;REJANG VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; +A94D;REJANG VOWEL SIGN EU;Mn;0;NSM;;;;;N;;;;; +A94E;REJANG VOWEL SIGN EA;Mn;0;NSM;;;;;N;;;;; +A94F;REJANG CONSONANT SIGN NG;Mn;0;NSM;;;;;N;;;;; +A950;REJANG CONSONANT SIGN N;Mn;0;NSM;;;;;N;;;;; +A951;REJANG CONSONANT SIGN R;Mn;0;NSM;;;;;N;;;;; +A952;REJANG CONSONANT SIGN H;Mc;0;L;;;;;N;;;;; +A953;REJANG VIRAMA;Mc;9;L;;;;;N;;;;; +A95F;REJANG SECTION MARK;Po;0;L;;;;;N;;;;; +A960;HANGUL CHOSEONG TIKEUT-MIEUM;Lo;0;L;;;;;N;;;;; +A961;HANGUL CHOSEONG TIKEUT-PIEUP;Lo;0;L;;;;;N;;;;; +A962;HANGUL CHOSEONG TIKEUT-SIOS;Lo;0;L;;;;;N;;;;; +A963;HANGUL CHOSEONG TIKEUT-CIEUC;Lo;0;L;;;;;N;;;;; +A964;HANGUL CHOSEONG RIEUL-KIYEOK;Lo;0;L;;;;;N;;;;; +A965;HANGUL CHOSEONG RIEUL-SSANGKIYEOK;Lo;0;L;;;;;N;;;;; +A966;HANGUL CHOSEONG RIEUL-TIKEUT;Lo;0;L;;;;;N;;;;; +A967;HANGUL CHOSEONG RIEUL-SSANGTIKEUT;Lo;0;L;;;;;N;;;;; +A968;HANGUL CHOSEONG RIEUL-MIEUM;Lo;0;L;;;;;N;;;;; +A969;HANGUL CHOSEONG RIEUL-PIEUP;Lo;0;L;;;;;N;;;;; +A96A;HANGUL CHOSEONG RIEUL-SSANGPIEUP;Lo;0;L;;;;;N;;;;; +A96B;HANGUL CHOSEONG RIEUL-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; +A96C;HANGUL CHOSEONG RIEUL-SIOS;Lo;0;L;;;;;N;;;;; +A96D;HANGUL CHOSEONG RIEUL-CIEUC;Lo;0;L;;;;;N;;;;; +A96E;HANGUL CHOSEONG RIEUL-KHIEUKH;Lo;0;L;;;;;N;;;;; +A96F;HANGUL CHOSEONG MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;; +A970;HANGUL CHOSEONG MIEUM-TIKEUT;Lo;0;L;;;;;N;;;;; +A971;HANGUL CHOSEONG MIEUM-SIOS;Lo;0;L;;;;;N;;;;; +A972;HANGUL CHOSEONG PIEUP-SIOS-THIEUTH;Lo;0;L;;;;;N;;;;; +A973;HANGUL CHOSEONG PIEUP-KHIEUKH;Lo;0;L;;;;;N;;;;; +A974;HANGUL CHOSEONG PIEUP-HIEUH;Lo;0;L;;;;;N;;;;; +A975;HANGUL CHOSEONG SSANGSIOS-PIEUP;Lo;0;L;;;;;N;;;;; +A976;HANGUL CHOSEONG IEUNG-RIEUL;Lo;0;L;;;;;N;;;;; +A977;HANGUL CHOSEONG IEUNG-HIEUH;Lo;0;L;;;;;N;;;;; +A978;HANGUL CHOSEONG SSANGCIEUC-HIEUH;Lo;0;L;;;;;N;;;;; +A979;HANGUL CHOSEONG SSANGTHIEUTH;Lo;0;L;;;;;N;;;;; +A97A;HANGUL CHOSEONG PHIEUPH-HIEUH;Lo;0;L;;;;;N;;;;; +A97B;HANGUL CHOSEONG HIEUH-SIOS;Lo;0;L;;;;;N;;;;; +A97C;HANGUL CHOSEONG SSANGYEORINHIEUH;Lo;0;L;;;;;N;;;;; +A980;JAVANESE SIGN PANYANGGA;Mn;0;NSM;;;;;N;;;;; +A981;JAVANESE SIGN CECAK;Mn;0;NSM;;;;;N;;;;; +A982;JAVANESE SIGN LAYAR;Mn;0;NSM;;;;;N;;;;; +A983;JAVANESE SIGN WIGNYAN;Mc;0;L;;;;;N;;;;; +A984;JAVANESE LETTER A;Lo;0;L;;;;;N;;;;; +A985;JAVANESE LETTER I KAWI;Lo;0;L;;;;;N;;;;; +A986;JAVANESE LETTER I;Lo;0;L;;;;;N;;;;; +A987;JAVANESE LETTER II;Lo;0;L;;;;;N;;;;; +A988;JAVANESE LETTER U;Lo;0;L;;;;;N;;;;; +A989;JAVANESE LETTER PA CEREK;Lo;0;L;;;;;N;;;;; +A98A;JAVANESE LETTER NGA LELET;Lo;0;L;;;;;N;;;;; +A98B;JAVANESE LETTER NGA LELET RASWADI;Lo;0;L;;;;;N;;;;; +A98C;JAVANESE LETTER E;Lo;0;L;;;;;N;;;;; +A98D;JAVANESE LETTER AI;Lo;0;L;;;;;N;;;;; +A98E;JAVANESE LETTER O;Lo;0;L;;;;;N;;;;; +A98F;JAVANESE LETTER KA;Lo;0;L;;;;;N;;;;; +A990;JAVANESE LETTER KA SASAK;Lo;0;L;;;;;N;;;;; +A991;JAVANESE LETTER KA MURDA;Lo;0;L;;;;;N;;;;; +A992;JAVANESE LETTER GA;Lo;0;L;;;;;N;;;;; +A993;JAVANESE LETTER GA MURDA;Lo;0;L;;;;;N;;;;; +A994;JAVANESE LETTER NGA;Lo;0;L;;;;;N;;;;; +A995;JAVANESE LETTER CA;Lo;0;L;;;;;N;;;;; +A996;JAVANESE LETTER CA MURDA;Lo;0;L;;;;;N;;;;; +A997;JAVANESE LETTER JA;Lo;0;L;;;;;N;;;;; +A998;JAVANESE LETTER NYA MURDA;Lo;0;L;;;;;N;;;;; +A999;JAVANESE LETTER JA MAHAPRANA;Lo;0;L;;;;;N;;;;; +A99A;JAVANESE LETTER NYA;Lo;0;L;;;;;N;;;;; +A99B;JAVANESE LETTER TTA;Lo;0;L;;;;;N;;;;; +A99C;JAVANESE LETTER TTA MAHAPRANA;Lo;0;L;;;;;N;;;;; +A99D;JAVANESE LETTER DDA;Lo;0;L;;;;;N;;;;; +A99E;JAVANESE LETTER DDA MAHAPRANA;Lo;0;L;;;;;N;;;;; +A99F;JAVANESE LETTER NA MURDA;Lo;0;L;;;;;N;;;;; +A9A0;JAVANESE LETTER TA;Lo;0;L;;;;;N;;;;; +A9A1;JAVANESE LETTER TA MURDA;Lo;0;L;;;;;N;;;;; +A9A2;JAVANESE LETTER DA;Lo;0;L;;;;;N;;;;; +A9A3;JAVANESE LETTER DA MAHAPRANA;Lo;0;L;;;;;N;;;;; +A9A4;JAVANESE LETTER NA;Lo;0;L;;;;;N;;;;; +A9A5;JAVANESE LETTER PA;Lo;0;L;;;;;N;;;;; +A9A6;JAVANESE LETTER PA MURDA;Lo;0;L;;;;;N;;;;; +A9A7;JAVANESE LETTER BA;Lo;0;L;;;;;N;;;;; +A9A8;JAVANESE LETTER BA MURDA;Lo;0;L;;;;;N;;;;; +A9A9;JAVANESE LETTER MA;Lo;0;L;;;;;N;;;;; +A9AA;JAVANESE LETTER YA;Lo;0;L;;;;;N;;;;; +A9AB;JAVANESE LETTER RA;Lo;0;L;;;;;N;;;;; +A9AC;JAVANESE LETTER RA AGUNG;Lo;0;L;;;;;N;;;;; +A9AD;JAVANESE LETTER LA;Lo;0;L;;;;;N;;;;; +A9AE;JAVANESE LETTER WA;Lo;0;L;;;;;N;;;;; +A9AF;JAVANESE LETTER SA MURDA;Lo;0;L;;;;;N;;;;; +A9B0;JAVANESE LETTER SA MAHAPRANA;Lo;0;L;;;;;N;;;;; +A9B1;JAVANESE LETTER SA;Lo;0;L;;;;;N;;;;; +A9B2;JAVANESE LETTER HA;Lo;0;L;;;;;N;;;;; +A9B3;JAVANESE SIGN CECAK TELU;Mn;7;NSM;;;;;N;;;;; +A9B4;JAVANESE VOWEL SIGN TARUNG;Mc;0;L;;;;;N;;;;; +A9B5;JAVANESE VOWEL SIGN TOLONG;Mc;0;L;;;;;N;;;;; +A9B6;JAVANESE VOWEL SIGN WULU;Mn;0;NSM;;;;;N;;;;; +A9B7;JAVANESE VOWEL SIGN WULU MELIK;Mn;0;NSM;;;;;N;;;;; +A9B8;JAVANESE VOWEL SIGN SUKU;Mn;0;NSM;;;;;N;;;;; +A9B9;JAVANESE VOWEL SIGN SUKU MENDUT;Mn;0;NSM;;;;;N;;;;; +A9BA;JAVANESE VOWEL SIGN TALING;Mc;0;L;;;;;N;;;;; +A9BB;JAVANESE VOWEL SIGN DIRGA MURE;Mc;0;L;;;;;N;;;;; +A9BC;JAVANESE VOWEL SIGN PEPET;Mn;0;NSM;;;;;N;;;;; +A9BD;JAVANESE CONSONANT SIGN KERET;Mc;0;L;;;;;N;;;;; +A9BE;JAVANESE CONSONANT SIGN PENGKAL;Mc;0;L;;;;;N;;;;; +A9BF;JAVANESE CONSONANT SIGN CAKRA;Mc;0;L;;;;;N;;;;; +A9C0;JAVANESE PANGKON;Mc;9;L;;;;;N;;;;; +A9C1;JAVANESE LEFT RERENGGAN;Po;0;L;;;;;N;;;;; +A9C2;JAVANESE RIGHT RERENGGAN;Po;0;L;;;;;N;;;;; +A9C3;JAVANESE PADA ANDAP;Po;0;L;;;;;N;;;;; +A9C4;JAVANESE PADA MADYA;Po;0;L;;;;;N;;;;; +A9C5;JAVANESE PADA LUHUR;Po;0;L;;;;;N;;;;; +A9C6;JAVANESE PADA WINDU;Po;0;L;;;;;N;;;;; +A9C7;JAVANESE PADA PANGKAT;Po;0;L;;;;;N;;;;; +A9C8;JAVANESE PADA LINGSA;Po;0;L;;;;;N;;;;; +A9C9;JAVANESE PADA LUNGSI;Po;0;L;;;;;N;;;;; +A9CA;JAVANESE PADA ADEG;Po;0;L;;;;;N;;;;; +A9CB;JAVANESE PADA ADEG ADEG;Po;0;L;;;;;N;;;;; +A9CC;JAVANESE PADA PISELEH;Po;0;L;;;;;N;;;;; +A9CD;JAVANESE TURNED PADA PISELEH;Po;0;L;;;;;N;;;;; +A9CF;JAVANESE PANGRANGKEP;Lm;0;L;;;;;N;;;;; +A9D0;JAVANESE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +A9D1;JAVANESE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +A9D2;JAVANESE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +A9D3;JAVANESE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +A9D4;JAVANESE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +A9D5;JAVANESE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +A9D6;JAVANESE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +A9D7;JAVANESE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +A9D8;JAVANESE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +A9D9;JAVANESE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +A9DE;JAVANESE PADA TIRTA TUMETES;Po;0;L;;;;;N;;;;; +A9DF;JAVANESE PADA ISEN-ISEN;Po;0;L;;;;;N;;;;; +A9E0;MYANMAR LETTER SHAN GHA;Lo;0;L;;;;;N;;;;; +A9E1;MYANMAR LETTER SHAN CHA;Lo;0;L;;;;;N;;;;; +A9E2;MYANMAR LETTER SHAN JHA;Lo;0;L;;;;;N;;;;; +A9E3;MYANMAR LETTER SHAN NNA;Lo;0;L;;;;;N;;;;; +A9E4;MYANMAR LETTER SHAN BHA;Lo;0;L;;;;;N;;;;; +A9E5;MYANMAR SIGN SHAN SAW;Mn;0;NSM;;;;;N;;;;; +A9E6;MYANMAR MODIFIER LETTER SHAN REDUPLICATION;Lm;0;L;;;;;N;;;;; +A9E7;MYANMAR LETTER TAI LAING NYA;Lo;0;L;;;;;N;;;;; +A9E8;MYANMAR LETTER TAI LAING FA;Lo;0;L;;;;;N;;;;; +A9E9;MYANMAR LETTER TAI LAING GA;Lo;0;L;;;;;N;;;;; +A9EA;MYANMAR LETTER TAI LAING GHA;Lo;0;L;;;;;N;;;;; +A9EB;MYANMAR LETTER TAI LAING JA;Lo;0;L;;;;;N;;;;; +A9EC;MYANMAR LETTER TAI LAING JHA;Lo;0;L;;;;;N;;;;; +A9ED;MYANMAR LETTER TAI LAING DDA;Lo;0;L;;;;;N;;;;; +A9EE;MYANMAR LETTER TAI LAING DDHA;Lo;0;L;;;;;N;;;;; +A9EF;MYANMAR LETTER TAI LAING NNA;Lo;0;L;;;;;N;;;;; +A9F0;MYANMAR TAI LAING DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +A9F1;MYANMAR TAI LAING DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +A9F2;MYANMAR TAI LAING DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +A9F3;MYANMAR TAI LAING DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +A9F4;MYANMAR TAI LAING DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +A9F5;MYANMAR TAI LAING DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +A9F6;MYANMAR TAI LAING DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +A9F7;MYANMAR TAI LAING DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +A9F8;MYANMAR TAI LAING DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +A9F9;MYANMAR TAI LAING DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +A9FA;MYANMAR LETTER TAI LAING LLA;Lo;0;L;;;;;N;;;;; +A9FB;MYANMAR LETTER TAI LAING DA;Lo;0;L;;;;;N;;;;; +A9FC;MYANMAR LETTER TAI LAING DHA;Lo;0;L;;;;;N;;;;; +A9FD;MYANMAR LETTER TAI LAING BA;Lo;0;L;;;;;N;;;;; +A9FE;MYANMAR LETTER TAI LAING BHA;Lo;0;L;;;;;N;;;;; +AA00;CHAM LETTER A;Lo;0;L;;;;;N;;;;; +AA01;CHAM LETTER I;Lo;0;L;;;;;N;;;;; +AA02;CHAM LETTER U;Lo;0;L;;;;;N;;;;; +AA03;CHAM LETTER E;Lo;0;L;;;;;N;;;;; +AA04;CHAM LETTER AI;Lo;0;L;;;;;N;;;;; +AA05;CHAM LETTER O;Lo;0;L;;;;;N;;;;; +AA06;CHAM LETTER KA;Lo;0;L;;;;;N;;;;; +AA07;CHAM LETTER KHA;Lo;0;L;;;;;N;;;;; +AA08;CHAM LETTER GA;Lo;0;L;;;;;N;;;;; +AA09;CHAM LETTER GHA;Lo;0;L;;;;;N;;;;; +AA0A;CHAM LETTER NGUE;Lo;0;L;;;;;N;;;;; +AA0B;CHAM LETTER NGA;Lo;0;L;;;;;N;;;;; +AA0C;CHAM LETTER CHA;Lo;0;L;;;;;N;;;;; +AA0D;CHAM LETTER CHHA;Lo;0;L;;;;;N;;;;; +AA0E;CHAM LETTER JA;Lo;0;L;;;;;N;;;;; +AA0F;CHAM LETTER JHA;Lo;0;L;;;;;N;;;;; +AA10;CHAM LETTER NHUE;Lo;0;L;;;;;N;;;;; +AA11;CHAM LETTER NHA;Lo;0;L;;;;;N;;;;; +AA12;CHAM LETTER NHJA;Lo;0;L;;;;;N;;;;; +AA13;CHAM LETTER TA;Lo;0;L;;;;;N;;;;; +AA14;CHAM LETTER THA;Lo;0;L;;;;;N;;;;; +AA15;CHAM LETTER DA;Lo;0;L;;;;;N;;;;; +AA16;CHAM LETTER DHA;Lo;0;L;;;;;N;;;;; +AA17;CHAM LETTER NUE;Lo;0;L;;;;;N;;;;; +AA18;CHAM LETTER NA;Lo;0;L;;;;;N;;;;; +AA19;CHAM LETTER DDA;Lo;0;L;;;;;N;;;;; +AA1A;CHAM LETTER PA;Lo;0;L;;;;;N;;;;; +AA1B;CHAM LETTER PPA;Lo;0;L;;;;;N;;;;; +AA1C;CHAM LETTER PHA;Lo;0;L;;;;;N;;;;; +AA1D;CHAM LETTER BA;Lo;0;L;;;;;N;;;;; +AA1E;CHAM LETTER BHA;Lo;0;L;;;;;N;;;;; +AA1F;CHAM LETTER MUE;Lo;0;L;;;;;N;;;;; +AA20;CHAM LETTER MA;Lo;0;L;;;;;N;;;;; +AA21;CHAM LETTER BBA;Lo;0;L;;;;;N;;;;; +AA22;CHAM LETTER YA;Lo;0;L;;;;;N;;;;; +AA23;CHAM LETTER RA;Lo;0;L;;;;;N;;;;; +AA24;CHAM LETTER LA;Lo;0;L;;;;;N;;;;; +AA25;CHAM LETTER VA;Lo;0;L;;;;;N;;;;; +AA26;CHAM LETTER SSA;Lo;0;L;;;;;N;;;;; +AA27;CHAM LETTER SA;Lo;0;L;;;;;N;;;;; +AA28;CHAM LETTER HA;Lo;0;L;;;;;N;;;;; +AA29;CHAM VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;; +AA2A;CHAM VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +AA2B;CHAM VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +AA2C;CHAM VOWEL SIGN EI;Mn;0;NSM;;;;;N;;;;; +AA2D;CHAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +AA2E;CHAM VOWEL SIGN OE;Mn;0;NSM;;;;;N;;;;; +AA2F;CHAM VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +AA30;CHAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +AA31;CHAM VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; +AA32;CHAM VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;; +AA33;CHAM CONSONANT SIGN YA;Mc;0;L;;;;;N;;;;; +AA34;CHAM CONSONANT SIGN RA;Mc;0;L;;;;;N;;;;; +AA35;CHAM CONSONANT SIGN LA;Mn;0;NSM;;;;;N;;;;; +AA36;CHAM CONSONANT SIGN WA;Mn;0;NSM;;;;;N;;;;; +AA40;CHAM LETTER FINAL K;Lo;0;L;;;;;N;;;;; +AA41;CHAM LETTER FINAL G;Lo;0;L;;;;;N;;;;; +AA42;CHAM LETTER FINAL NG;Lo;0;L;;;;;N;;;;; +AA43;CHAM CONSONANT SIGN FINAL NG;Mn;0;NSM;;;;;N;;;;; +AA44;CHAM LETTER FINAL CH;Lo;0;L;;;;;N;;;;; +AA45;CHAM LETTER FINAL T;Lo;0;L;;;;;N;;;;; +AA46;CHAM LETTER FINAL N;Lo;0;L;;;;;N;;;;; +AA47;CHAM LETTER FINAL P;Lo;0;L;;;;;N;;;;; +AA48;CHAM LETTER FINAL Y;Lo;0;L;;;;;N;;;;; +AA49;CHAM LETTER FINAL R;Lo;0;L;;;;;N;;;;; +AA4A;CHAM LETTER FINAL L;Lo;0;L;;;;;N;;;;; +AA4B;CHAM LETTER FINAL SS;Lo;0;L;;;;;N;;;;; +AA4C;CHAM CONSONANT SIGN FINAL M;Mn;0;NSM;;;;;N;;;;; +AA4D;CHAM CONSONANT SIGN FINAL H;Mc;0;L;;;;;N;;;;; +AA50;CHAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +AA51;CHAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +AA52;CHAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +AA53;CHAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +AA54;CHAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +AA55;CHAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +AA56;CHAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +AA57;CHAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +AA58;CHAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +AA59;CHAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +AA5C;CHAM PUNCTUATION SPIRAL;Po;0;L;;;;;N;;;;; +AA5D;CHAM PUNCTUATION DANDA;Po;0;L;;;;;N;;;;; +AA5E;CHAM PUNCTUATION DOUBLE DANDA;Po;0;L;;;;;N;;;;; +AA5F;CHAM PUNCTUATION TRIPLE DANDA;Po;0;L;;;;;N;;;;; +AA60;MYANMAR LETTER KHAMTI GA;Lo;0;L;;;;;N;;;;; +AA61;MYANMAR LETTER KHAMTI CA;Lo;0;L;;;;;N;;;;; +AA62;MYANMAR LETTER KHAMTI CHA;Lo;0;L;;;;;N;;;;; +AA63;MYANMAR LETTER KHAMTI JA;Lo;0;L;;;;;N;;;;; +AA64;MYANMAR LETTER KHAMTI JHA;Lo;0;L;;;;;N;;;;; +AA65;MYANMAR LETTER KHAMTI NYA;Lo;0;L;;;;;N;;;;; +AA66;MYANMAR LETTER KHAMTI TTA;Lo;0;L;;;;;N;;;;; +AA67;MYANMAR LETTER KHAMTI TTHA;Lo;0;L;;;;;N;;;;; +AA68;MYANMAR LETTER KHAMTI DDA;Lo;0;L;;;;;N;;;;; +AA69;MYANMAR LETTER KHAMTI DDHA;Lo;0;L;;;;;N;;;;; +AA6A;MYANMAR LETTER KHAMTI DHA;Lo;0;L;;;;;N;;;;; +AA6B;MYANMAR LETTER KHAMTI NA;Lo;0;L;;;;;N;;;;; +AA6C;MYANMAR LETTER KHAMTI SA;Lo;0;L;;;;;N;;;;; +AA6D;MYANMAR LETTER KHAMTI HA;Lo;0;L;;;;;N;;;;; +AA6E;MYANMAR LETTER KHAMTI HHA;Lo;0;L;;;;;N;;;;; +AA6F;MYANMAR LETTER KHAMTI FA;Lo;0;L;;;;;N;;;;; +AA70;MYANMAR MODIFIER LETTER KHAMTI REDUPLICATION;Lm;0;L;;;;;N;;;;; +AA71;MYANMAR LETTER KHAMTI XA;Lo;0;L;;;;;N;;;;; +AA72;MYANMAR LETTER KHAMTI ZA;Lo;0;L;;;;;N;;;;; +AA73;MYANMAR LETTER KHAMTI RA;Lo;0;L;;;;;N;;;;; +AA74;MYANMAR LOGOGRAM KHAMTI OAY;Lo;0;L;;;;;N;;;;; +AA75;MYANMAR LOGOGRAM KHAMTI QN;Lo;0;L;;;;;N;;;;; +AA76;MYANMAR LOGOGRAM KHAMTI HM;Lo;0;L;;;;;N;;;;; +AA77;MYANMAR SYMBOL AITON EXCLAMATION;So;0;L;;;;;N;;;;; +AA78;MYANMAR SYMBOL AITON ONE;So;0;L;;;;;N;;;;; +AA79;MYANMAR SYMBOL AITON TWO;So;0;L;;;;;N;;;;; +AA7A;MYANMAR LETTER AITON RA;Lo;0;L;;;;;N;;;;; +AA7B;MYANMAR SIGN PAO KAREN TONE;Mc;0;L;;;;;N;;;;; +AA7C;MYANMAR SIGN TAI LAING TONE-2;Mn;0;NSM;;;;;N;;;;; +AA7D;MYANMAR SIGN TAI LAING TONE-5;Mc;0;L;;;;;N;;;;; +AA7E;MYANMAR LETTER SHWE PALAUNG CHA;Lo;0;L;;;;;N;;;;; +AA7F;MYANMAR LETTER SHWE PALAUNG SHA;Lo;0;L;;;;;N;;;;; +AA80;TAI VIET LETTER LOW KO;Lo;0;L;;;;;N;;;;; +AA81;TAI VIET LETTER HIGH KO;Lo;0;L;;;;;N;;;;; +AA82;TAI VIET LETTER LOW KHO;Lo;0;L;;;;;N;;;;; +AA83;TAI VIET LETTER HIGH KHO;Lo;0;L;;;;;N;;;;; +AA84;TAI VIET LETTER LOW KHHO;Lo;0;L;;;;;N;;;;; +AA85;TAI VIET LETTER HIGH KHHO;Lo;0;L;;;;;N;;;;; +AA86;TAI VIET LETTER LOW GO;Lo;0;L;;;;;N;;;;; +AA87;TAI VIET LETTER HIGH GO;Lo;0;L;;;;;N;;;;; +AA88;TAI VIET LETTER LOW NGO;Lo;0;L;;;;;N;;;;; +AA89;TAI VIET LETTER HIGH NGO;Lo;0;L;;;;;N;;;;; +AA8A;TAI VIET LETTER LOW CO;Lo;0;L;;;;;N;;;;; +AA8B;TAI VIET LETTER HIGH CO;Lo;0;L;;;;;N;;;;; +AA8C;TAI VIET LETTER LOW CHO;Lo;0;L;;;;;N;;;;; +AA8D;TAI VIET LETTER HIGH CHO;Lo;0;L;;;;;N;;;;; +AA8E;TAI VIET LETTER LOW SO;Lo;0;L;;;;;N;;;;; +AA8F;TAI VIET LETTER HIGH SO;Lo;0;L;;;;;N;;;;; +AA90;TAI VIET LETTER LOW NYO;Lo;0;L;;;;;N;;;;; +AA91;TAI VIET LETTER HIGH NYO;Lo;0;L;;;;;N;;;;; +AA92;TAI VIET LETTER LOW DO;Lo;0;L;;;;;N;;;;; +AA93;TAI VIET LETTER HIGH DO;Lo;0;L;;;;;N;;;;; +AA94;TAI VIET LETTER LOW TO;Lo;0;L;;;;;N;;;;; +AA95;TAI VIET LETTER HIGH TO;Lo;0;L;;;;;N;;;;; +AA96;TAI VIET LETTER LOW THO;Lo;0;L;;;;;N;;;;; +AA97;TAI VIET LETTER HIGH THO;Lo;0;L;;;;;N;;;;; +AA98;TAI VIET LETTER LOW NO;Lo;0;L;;;;;N;;;;; +AA99;TAI VIET LETTER HIGH NO;Lo;0;L;;;;;N;;;;; +AA9A;TAI VIET LETTER LOW BO;Lo;0;L;;;;;N;;;;; +AA9B;TAI VIET LETTER HIGH BO;Lo;0;L;;;;;N;;;;; +AA9C;TAI VIET LETTER LOW PO;Lo;0;L;;;;;N;;;;; +AA9D;TAI VIET LETTER HIGH PO;Lo;0;L;;;;;N;;;;; +AA9E;TAI VIET LETTER LOW PHO;Lo;0;L;;;;;N;;;;; +AA9F;TAI VIET LETTER HIGH PHO;Lo;0;L;;;;;N;;;;; +AAA0;TAI VIET LETTER LOW FO;Lo;0;L;;;;;N;;;;; +AAA1;TAI VIET LETTER HIGH FO;Lo;0;L;;;;;N;;;;; +AAA2;TAI VIET LETTER LOW MO;Lo;0;L;;;;;N;;;;; +AAA3;TAI VIET LETTER HIGH MO;Lo;0;L;;;;;N;;;;; +AAA4;TAI VIET LETTER LOW YO;Lo;0;L;;;;;N;;;;; +AAA5;TAI VIET LETTER HIGH YO;Lo;0;L;;;;;N;;;;; +AAA6;TAI VIET LETTER LOW RO;Lo;0;L;;;;;N;;;;; +AAA7;TAI VIET LETTER HIGH RO;Lo;0;L;;;;;N;;;;; +AAA8;TAI VIET LETTER LOW LO;Lo;0;L;;;;;N;;;;; +AAA9;TAI VIET LETTER HIGH LO;Lo;0;L;;;;;N;;;;; +AAAA;TAI VIET LETTER LOW VO;Lo;0;L;;;;;N;;;;; +AAAB;TAI VIET LETTER HIGH VO;Lo;0;L;;;;;N;;;;; +AAAC;TAI VIET LETTER LOW HO;Lo;0;L;;;;;N;;;;; +AAAD;TAI VIET LETTER HIGH HO;Lo;0;L;;;;;N;;;;; +AAAE;TAI VIET LETTER LOW O;Lo;0;L;;;;;N;;;;; +AAAF;TAI VIET LETTER HIGH O;Lo;0;L;;;;;N;;;;; +AAB0;TAI VIET MAI KANG;Mn;230;NSM;;;;;N;;;;; +AAB1;TAI VIET VOWEL AA;Lo;0;L;;;;;N;;;;; +AAB2;TAI VIET VOWEL I;Mn;230;NSM;;;;;N;;;;; +AAB3;TAI VIET VOWEL UE;Mn;230;NSM;;;;;N;;;;; +AAB4;TAI VIET VOWEL U;Mn;220;NSM;;;;;N;;;;; +AAB5;TAI VIET VOWEL E;Lo;0;L;;;;;N;;;;; +AAB6;TAI VIET VOWEL O;Lo;0;L;;;;;N;;;;; +AAB7;TAI VIET MAI KHIT;Mn;230;NSM;;;;;N;;;;; +AAB8;TAI VIET VOWEL IA;Mn;230;NSM;;;;;N;;;;; +AAB9;TAI VIET VOWEL UEA;Lo;0;L;;;;;N;;;;; +AABA;TAI VIET VOWEL UA;Lo;0;L;;;;;N;;;;; +AABB;TAI VIET VOWEL AUE;Lo;0;L;;;;;N;;;;; +AABC;TAI VIET VOWEL AY;Lo;0;L;;;;;N;;;;; +AABD;TAI VIET VOWEL AN;Lo;0;L;;;;;N;;;;; +AABE;TAI VIET VOWEL AM;Mn;230;NSM;;;;;N;;;;; +AABF;TAI VIET TONE MAI EK;Mn;230;NSM;;;;;N;;;;; +AAC0;TAI VIET TONE MAI NUENG;Lo;0;L;;;;;N;;;;; +AAC1;TAI VIET TONE MAI THO;Mn;230;NSM;;;;;N;;;;; +AAC2;TAI VIET TONE MAI SONG;Lo;0;L;;;;;N;;;;; +AADB;TAI VIET SYMBOL KON;Lo;0;L;;;;;N;;;;; +AADC;TAI VIET SYMBOL NUENG;Lo;0;L;;;;;N;;;;; +AADD;TAI VIET SYMBOL SAM;Lm;0;L;;;;;N;;;;; +AADE;TAI VIET SYMBOL HO HOI;Po;0;L;;;;;N;;;;; +AADF;TAI VIET SYMBOL KOI KOI;Po;0;L;;;;;N;;;;; +AAE0;MEETEI MAYEK LETTER E;Lo;0;L;;;;;N;;;;; +AAE1;MEETEI MAYEK LETTER O;Lo;0;L;;;;;N;;;;; +AAE2;MEETEI MAYEK LETTER CHA;Lo;0;L;;;;;N;;;;; +AAE3;MEETEI MAYEK LETTER NYA;Lo;0;L;;;;;N;;;;; +AAE4;MEETEI MAYEK LETTER TTA;Lo;0;L;;;;;N;;;;; +AAE5;MEETEI MAYEK LETTER TTHA;Lo;0;L;;;;;N;;;;; +AAE6;MEETEI MAYEK LETTER DDA;Lo;0;L;;;;;N;;;;; +AAE7;MEETEI MAYEK LETTER DDHA;Lo;0;L;;;;;N;;;;; +AAE8;MEETEI MAYEK LETTER NNA;Lo;0;L;;;;;N;;;;; +AAE9;MEETEI MAYEK LETTER SHA;Lo;0;L;;;;;N;;;;; +AAEA;MEETEI MAYEK LETTER SSA;Lo;0;L;;;;;N;;;;; +AAEB;MEETEI MAYEK VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +AAEC;MEETEI MAYEK VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +AAED;MEETEI MAYEK VOWEL SIGN AAI;Mn;0;NSM;;;;;N;;;;; +AAEE;MEETEI MAYEK VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +AAEF;MEETEI MAYEK VOWEL SIGN AAU;Mc;0;L;;;;;N;;;;; +AAF0;MEETEI MAYEK CHEIKHAN;Po;0;L;;;;;N;;;;; +AAF1;MEETEI MAYEK AHANG KHUDAM;Po;0;L;;;;;N;;;;; +AAF2;MEETEI MAYEK ANJI;Lo;0;L;;;;;N;;;;; +AAF3;MEETEI MAYEK SYLLABLE REPETITION MARK;Lm;0;L;;;;;N;;;;; +AAF4;MEETEI MAYEK WORD REPETITION MARK;Lm;0;L;;;;;N;;;;; +AAF5;MEETEI MAYEK VOWEL SIGN VISARGA;Mc;0;L;;;;;N;;;;; +AAF6;MEETEI MAYEK VIRAMA;Mn;9;NSM;;;;;N;;;;; +AB01;ETHIOPIC SYLLABLE TTHU;Lo;0;L;;;;;N;;;;; +AB02;ETHIOPIC SYLLABLE TTHI;Lo;0;L;;;;;N;;;;; +AB03;ETHIOPIC SYLLABLE TTHAA;Lo;0;L;;;;;N;;;;; +AB04;ETHIOPIC SYLLABLE TTHEE;Lo;0;L;;;;;N;;;;; +AB05;ETHIOPIC SYLLABLE TTHE;Lo;0;L;;;;;N;;;;; +AB06;ETHIOPIC SYLLABLE TTHO;Lo;0;L;;;;;N;;;;; +AB09;ETHIOPIC SYLLABLE DDHU;Lo;0;L;;;;;N;;;;; +AB0A;ETHIOPIC SYLLABLE DDHI;Lo;0;L;;;;;N;;;;; +AB0B;ETHIOPIC SYLLABLE DDHAA;Lo;0;L;;;;;N;;;;; +AB0C;ETHIOPIC SYLLABLE DDHEE;Lo;0;L;;;;;N;;;;; +AB0D;ETHIOPIC SYLLABLE DDHE;Lo;0;L;;;;;N;;;;; +AB0E;ETHIOPIC SYLLABLE DDHO;Lo;0;L;;;;;N;;;;; +AB11;ETHIOPIC SYLLABLE DZU;Lo;0;L;;;;;N;;;;; +AB12;ETHIOPIC SYLLABLE DZI;Lo;0;L;;;;;N;;;;; +AB13;ETHIOPIC SYLLABLE DZAA;Lo;0;L;;;;;N;;;;; +AB14;ETHIOPIC SYLLABLE DZEE;Lo;0;L;;;;;N;;;;; +AB15;ETHIOPIC SYLLABLE DZE;Lo;0;L;;;;;N;;;;; +AB16;ETHIOPIC SYLLABLE DZO;Lo;0;L;;;;;N;;;;; +AB20;ETHIOPIC SYLLABLE CCHHA;Lo;0;L;;;;;N;;;;; +AB21;ETHIOPIC SYLLABLE CCHHU;Lo;0;L;;;;;N;;;;; +AB22;ETHIOPIC SYLLABLE CCHHI;Lo;0;L;;;;;N;;;;; +AB23;ETHIOPIC SYLLABLE CCHHAA;Lo;0;L;;;;;N;;;;; +AB24;ETHIOPIC SYLLABLE CCHHEE;Lo;0;L;;;;;N;;;;; +AB25;ETHIOPIC SYLLABLE CCHHE;Lo;0;L;;;;;N;;;;; +AB26;ETHIOPIC SYLLABLE CCHHO;Lo;0;L;;;;;N;;;;; +AB28;ETHIOPIC SYLLABLE BBA;Lo;0;L;;;;;N;;;;; +AB29;ETHIOPIC SYLLABLE BBU;Lo;0;L;;;;;N;;;;; +AB2A;ETHIOPIC SYLLABLE BBI;Lo;0;L;;;;;N;;;;; +AB2B;ETHIOPIC SYLLABLE BBAA;Lo;0;L;;;;;N;;;;; +AB2C;ETHIOPIC SYLLABLE BBEE;Lo;0;L;;;;;N;;;;; +AB2D;ETHIOPIC SYLLABLE BBE;Lo;0;L;;;;;N;;;;; +AB2E;ETHIOPIC SYLLABLE BBO;Lo;0;L;;;;;N;;;;; +AB30;LATIN SMALL LETTER BARRED ALPHA;Ll;0;L;;;;;N;;;;; +AB31;LATIN SMALL LETTER A REVERSED-SCHWA;Ll;0;L;;;;;N;;;;; +AB32;LATIN SMALL LETTER BLACKLETTER E;Ll;0;L;;;;;N;;;;; +AB33;LATIN SMALL LETTER BARRED E;Ll;0;L;;;;;N;;;;; +AB34;LATIN SMALL LETTER E WITH FLOURISH;Ll;0;L;;;;;N;;;;; +AB35;LATIN SMALL LETTER LENIS F;Ll;0;L;;;;;N;;;;; +AB36;LATIN SMALL LETTER SCRIPT G WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;; +AB37;LATIN SMALL LETTER L WITH INVERTED LAZY S;Ll;0;L;;;;;N;;;;; +AB38;LATIN SMALL LETTER L WITH DOUBLE MIDDLE TILDE;Ll;0;L;;;;;N;;;;; +AB39;LATIN SMALL LETTER L WITH MIDDLE RING;Ll;0;L;;;;;N;;;;; +AB3A;LATIN SMALL LETTER M WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;; +AB3B;LATIN SMALL LETTER N WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;; +AB3C;LATIN SMALL LETTER ENG WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;; +AB3D;LATIN SMALL LETTER BLACKLETTER O;Ll;0;L;;;;;N;;;;; +AB3E;LATIN SMALL LETTER BLACKLETTER O WITH STROKE;Ll;0;L;;;;;N;;;;; +AB3F;LATIN SMALL LETTER OPEN O WITH STROKE;Ll;0;L;;;;;N;;;;; +AB40;LATIN SMALL LETTER INVERTED OE;Ll;0;L;;;;;N;;;;; +AB41;LATIN SMALL LETTER TURNED OE WITH STROKE;Ll;0;L;;;;;N;;;;; +AB42;LATIN SMALL LETTER TURNED OE WITH HORIZONTAL STROKE;Ll;0;L;;;;;N;;;;; +AB43;LATIN SMALL LETTER TURNED O OPEN-O;Ll;0;L;;;;;N;;;;; +AB44;LATIN SMALL LETTER TURNED O OPEN-O WITH STROKE;Ll;0;L;;;;;N;;;;; +AB45;LATIN SMALL LETTER STIRRUP R;Ll;0;L;;;;;N;;;;; +AB46;LATIN LETTER SMALL CAPITAL R WITH RIGHT LEG;Ll;0;L;;;;;N;;;;; +AB47;LATIN SMALL LETTER R WITHOUT HANDLE;Ll;0;L;;;;;N;;;;; +AB48;LATIN SMALL LETTER DOUBLE R;Ll;0;L;;;;;N;;;;; +AB49;LATIN SMALL LETTER R WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;; +AB4A;LATIN SMALL LETTER DOUBLE R WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;; +AB4B;LATIN SMALL LETTER SCRIPT R;Ll;0;L;;;;;N;;;;; +AB4C;LATIN SMALL LETTER SCRIPT R WITH RING;Ll;0;L;;;;;N;;;;; +AB4D;LATIN SMALL LETTER BASELINE ESH;Ll;0;L;;;;;N;;;;; +AB4E;LATIN SMALL LETTER U WITH SHORT RIGHT LEG;Ll;0;L;;;;;N;;;;; +AB4F;LATIN SMALL LETTER U BAR WITH SHORT RIGHT LEG;Ll;0;L;;;;;N;;;;; +AB50;LATIN SMALL LETTER UI;Ll;0;L;;;;;N;;;;; +AB51;LATIN SMALL LETTER TURNED UI;Ll;0;L;;;;;N;;;;; +AB52;LATIN SMALL LETTER U WITH LEFT HOOK;Ll;0;L;;;;;N;;;;; +AB53;LATIN SMALL LETTER CHI;Ll;0;L;;;;;N;;;A7B3;;A7B3 +AB54;LATIN SMALL LETTER CHI WITH LOW RIGHT RING;Ll;0;L;;;;;N;;;;; +AB55;LATIN SMALL LETTER CHI WITH LOW LEFT SERIF;Ll;0;L;;;;;N;;;;; +AB56;LATIN SMALL LETTER X WITH LOW RIGHT RING;Ll;0;L;;;;;N;;;;; +AB57;LATIN SMALL LETTER X WITH LONG LEFT LEG;Ll;0;L;;;;;N;;;;; +AB58;LATIN SMALL LETTER X WITH LONG LEFT LEG AND LOW RIGHT RING;Ll;0;L;;;;;N;;;;; +AB59;LATIN SMALL LETTER X WITH LONG LEFT LEG WITH SERIF;Ll;0;L;;;;;N;;;;; +AB5A;LATIN SMALL LETTER Y WITH SHORT RIGHT LEG;Ll;0;L;;;;;N;;;;; +AB5B;MODIFIER BREVE WITH INVERTED BREVE;Sk;0;L;;;;;N;;;;; +AB5C;MODIFIER LETTER SMALL HENG;Lm;0;L;<super> A727;;;;N;;;;; +AB5D;MODIFIER LETTER SMALL L WITH INVERTED LAZY S;Lm;0;L;<super> AB37;;;;N;;;;; +AB5E;MODIFIER LETTER SMALL L WITH MIDDLE TILDE;Lm;0;L;<super> 026B;;;;N;;;;; +AB5F;MODIFIER LETTER SMALL U WITH LEFT HOOK;Lm;0;L;<super> AB52;;;;N;;;;; +AB60;LATIN SMALL LETTER SAKHA YAT;Ll;0;L;;;;;N;;;;; +AB61;LATIN SMALL LETTER IOTIFIED E;Ll;0;L;;;;;N;;;;; +AB62;LATIN SMALL LETTER OPEN OE;Ll;0;L;;;;;N;;;;; +AB63;LATIN SMALL LETTER UO;Ll;0;L;;;;;N;;;;; +AB64;LATIN SMALL LETTER INVERTED ALPHA;Ll;0;L;;;;;N;;;;; +AB65;GREEK LETTER SMALL CAPITAL OMEGA;Ll;0;L;;;;;N;;;;; +AB70;CHEROKEE SMALL LETTER A;Ll;0;L;;;;;N;;;13A0;;13A0 +AB71;CHEROKEE SMALL LETTER E;Ll;0;L;;;;;N;;;13A1;;13A1 +AB72;CHEROKEE SMALL LETTER I;Ll;0;L;;;;;N;;;13A2;;13A2 +AB73;CHEROKEE SMALL LETTER O;Ll;0;L;;;;;N;;;13A3;;13A3 +AB74;CHEROKEE SMALL LETTER U;Ll;0;L;;;;;N;;;13A4;;13A4 +AB75;CHEROKEE SMALL LETTER V;Ll;0;L;;;;;N;;;13A5;;13A5 +AB76;CHEROKEE SMALL LETTER GA;Ll;0;L;;;;;N;;;13A6;;13A6 +AB77;CHEROKEE SMALL LETTER KA;Ll;0;L;;;;;N;;;13A7;;13A7 +AB78;CHEROKEE SMALL LETTER GE;Ll;0;L;;;;;N;;;13A8;;13A8 +AB79;CHEROKEE SMALL LETTER GI;Ll;0;L;;;;;N;;;13A9;;13A9 +AB7A;CHEROKEE SMALL LETTER GO;Ll;0;L;;;;;N;;;13AA;;13AA +AB7B;CHEROKEE SMALL LETTER GU;Ll;0;L;;;;;N;;;13AB;;13AB +AB7C;CHEROKEE SMALL LETTER GV;Ll;0;L;;;;;N;;;13AC;;13AC +AB7D;CHEROKEE SMALL LETTER HA;Ll;0;L;;;;;N;;;13AD;;13AD +AB7E;CHEROKEE SMALL LETTER HE;Ll;0;L;;;;;N;;;13AE;;13AE +AB7F;CHEROKEE SMALL LETTER HI;Ll;0;L;;;;;N;;;13AF;;13AF +AB80;CHEROKEE SMALL LETTER HO;Ll;0;L;;;;;N;;;13B0;;13B0 +AB81;CHEROKEE SMALL LETTER HU;Ll;0;L;;;;;N;;;13B1;;13B1 +AB82;CHEROKEE SMALL LETTER HV;Ll;0;L;;;;;N;;;13B2;;13B2 +AB83;CHEROKEE SMALL LETTER LA;Ll;0;L;;;;;N;;;13B3;;13B3 +AB84;CHEROKEE SMALL LETTER LE;Ll;0;L;;;;;N;;;13B4;;13B4 +AB85;CHEROKEE SMALL LETTER LI;Ll;0;L;;;;;N;;;13B5;;13B5 +AB86;CHEROKEE SMALL LETTER LO;Ll;0;L;;;;;N;;;13B6;;13B6 +AB87;CHEROKEE SMALL LETTER LU;Ll;0;L;;;;;N;;;13B7;;13B7 +AB88;CHEROKEE SMALL LETTER LV;Ll;0;L;;;;;N;;;13B8;;13B8 +AB89;CHEROKEE SMALL LETTER MA;Ll;0;L;;;;;N;;;13B9;;13B9 +AB8A;CHEROKEE SMALL LETTER ME;Ll;0;L;;;;;N;;;13BA;;13BA +AB8B;CHEROKEE SMALL LETTER MI;Ll;0;L;;;;;N;;;13BB;;13BB +AB8C;CHEROKEE SMALL LETTER MO;Ll;0;L;;;;;N;;;13BC;;13BC +AB8D;CHEROKEE SMALL LETTER MU;Ll;0;L;;;;;N;;;13BD;;13BD +AB8E;CHEROKEE SMALL LETTER NA;Ll;0;L;;;;;N;;;13BE;;13BE +AB8F;CHEROKEE SMALL LETTER HNA;Ll;0;L;;;;;N;;;13BF;;13BF +AB90;CHEROKEE SMALL LETTER NAH;Ll;0;L;;;;;N;;;13C0;;13C0 +AB91;CHEROKEE SMALL LETTER NE;Ll;0;L;;;;;N;;;13C1;;13C1 +AB92;CHEROKEE SMALL LETTER NI;Ll;0;L;;;;;N;;;13C2;;13C2 +AB93;CHEROKEE SMALL LETTER NO;Ll;0;L;;;;;N;;;13C3;;13C3 +AB94;CHEROKEE SMALL LETTER NU;Ll;0;L;;;;;N;;;13C4;;13C4 +AB95;CHEROKEE SMALL LETTER NV;Ll;0;L;;;;;N;;;13C5;;13C5 +AB96;CHEROKEE SMALL LETTER QUA;Ll;0;L;;;;;N;;;13C6;;13C6 +AB97;CHEROKEE SMALL LETTER QUE;Ll;0;L;;;;;N;;;13C7;;13C7 +AB98;CHEROKEE SMALL LETTER QUI;Ll;0;L;;;;;N;;;13C8;;13C8 +AB99;CHEROKEE SMALL LETTER QUO;Ll;0;L;;;;;N;;;13C9;;13C9 +AB9A;CHEROKEE SMALL LETTER QUU;Ll;0;L;;;;;N;;;13CA;;13CA +AB9B;CHEROKEE SMALL LETTER QUV;Ll;0;L;;;;;N;;;13CB;;13CB +AB9C;CHEROKEE SMALL LETTER SA;Ll;0;L;;;;;N;;;13CC;;13CC +AB9D;CHEROKEE SMALL LETTER S;Ll;0;L;;;;;N;;;13CD;;13CD +AB9E;CHEROKEE SMALL LETTER SE;Ll;0;L;;;;;N;;;13CE;;13CE +AB9F;CHEROKEE SMALL LETTER SI;Ll;0;L;;;;;N;;;13CF;;13CF +ABA0;CHEROKEE SMALL LETTER SO;Ll;0;L;;;;;N;;;13D0;;13D0 +ABA1;CHEROKEE SMALL LETTER SU;Ll;0;L;;;;;N;;;13D1;;13D1 +ABA2;CHEROKEE SMALL LETTER SV;Ll;0;L;;;;;N;;;13D2;;13D2 +ABA3;CHEROKEE SMALL LETTER DA;Ll;0;L;;;;;N;;;13D3;;13D3 +ABA4;CHEROKEE SMALL LETTER TA;Ll;0;L;;;;;N;;;13D4;;13D4 +ABA5;CHEROKEE SMALL LETTER DE;Ll;0;L;;;;;N;;;13D5;;13D5 +ABA6;CHEROKEE SMALL LETTER TE;Ll;0;L;;;;;N;;;13D6;;13D6 +ABA7;CHEROKEE SMALL LETTER DI;Ll;0;L;;;;;N;;;13D7;;13D7 +ABA8;CHEROKEE SMALL LETTER TI;Ll;0;L;;;;;N;;;13D8;;13D8 +ABA9;CHEROKEE SMALL LETTER DO;Ll;0;L;;;;;N;;;13D9;;13D9 +ABAA;CHEROKEE SMALL LETTER DU;Ll;0;L;;;;;N;;;13DA;;13DA +ABAB;CHEROKEE SMALL LETTER DV;Ll;0;L;;;;;N;;;13DB;;13DB +ABAC;CHEROKEE SMALL LETTER DLA;Ll;0;L;;;;;N;;;13DC;;13DC +ABAD;CHEROKEE SMALL LETTER TLA;Ll;0;L;;;;;N;;;13DD;;13DD +ABAE;CHEROKEE SMALL LETTER TLE;Ll;0;L;;;;;N;;;13DE;;13DE +ABAF;CHEROKEE SMALL LETTER TLI;Ll;0;L;;;;;N;;;13DF;;13DF +ABB0;CHEROKEE SMALL LETTER TLO;Ll;0;L;;;;;N;;;13E0;;13E0 +ABB1;CHEROKEE SMALL LETTER TLU;Ll;0;L;;;;;N;;;13E1;;13E1 +ABB2;CHEROKEE SMALL LETTER TLV;Ll;0;L;;;;;N;;;13E2;;13E2 +ABB3;CHEROKEE SMALL LETTER TSA;Ll;0;L;;;;;N;;;13E3;;13E3 +ABB4;CHEROKEE SMALL LETTER TSE;Ll;0;L;;;;;N;;;13E4;;13E4 +ABB5;CHEROKEE SMALL LETTER TSI;Ll;0;L;;;;;N;;;13E5;;13E5 +ABB6;CHEROKEE SMALL LETTER TSO;Ll;0;L;;;;;N;;;13E6;;13E6 +ABB7;CHEROKEE SMALL LETTER TSU;Ll;0;L;;;;;N;;;13E7;;13E7 +ABB8;CHEROKEE SMALL LETTER TSV;Ll;0;L;;;;;N;;;13E8;;13E8 +ABB9;CHEROKEE SMALL LETTER WA;Ll;0;L;;;;;N;;;13E9;;13E9 +ABBA;CHEROKEE SMALL LETTER WE;Ll;0;L;;;;;N;;;13EA;;13EA +ABBB;CHEROKEE SMALL LETTER WI;Ll;0;L;;;;;N;;;13EB;;13EB +ABBC;CHEROKEE SMALL LETTER WO;Ll;0;L;;;;;N;;;13EC;;13EC +ABBD;CHEROKEE SMALL LETTER WU;Ll;0;L;;;;;N;;;13ED;;13ED +ABBE;CHEROKEE SMALL LETTER WV;Ll;0;L;;;;;N;;;13EE;;13EE +ABBF;CHEROKEE SMALL LETTER YA;Ll;0;L;;;;;N;;;13EF;;13EF +ABC0;MEETEI MAYEK LETTER KOK;Lo;0;L;;;;;N;;;;; +ABC1;MEETEI MAYEK LETTER SAM;Lo;0;L;;;;;N;;;;; +ABC2;MEETEI MAYEK LETTER LAI;Lo;0;L;;;;;N;;;;; +ABC3;MEETEI MAYEK LETTER MIT;Lo;0;L;;;;;N;;;;; +ABC4;MEETEI MAYEK LETTER PA;Lo;0;L;;;;;N;;;;; +ABC5;MEETEI MAYEK LETTER NA;Lo;0;L;;;;;N;;;;; +ABC6;MEETEI MAYEK LETTER CHIL;Lo;0;L;;;;;N;;;;; +ABC7;MEETEI MAYEK LETTER TIL;Lo;0;L;;;;;N;;;;; +ABC8;MEETEI MAYEK LETTER KHOU;Lo;0;L;;;;;N;;;;; +ABC9;MEETEI MAYEK LETTER NGOU;Lo;0;L;;;;;N;;;;; +ABCA;MEETEI MAYEK LETTER THOU;Lo;0;L;;;;;N;;;;; +ABCB;MEETEI MAYEK LETTER WAI;Lo;0;L;;;;;N;;;;; +ABCC;MEETEI MAYEK LETTER YANG;Lo;0;L;;;;;N;;;;; +ABCD;MEETEI MAYEK LETTER HUK;Lo;0;L;;;;;N;;;;; +ABCE;MEETEI MAYEK LETTER UN;Lo;0;L;;;;;N;;;;; +ABCF;MEETEI MAYEK LETTER I;Lo;0;L;;;;;N;;;;; +ABD0;MEETEI MAYEK LETTER PHAM;Lo;0;L;;;;;N;;;;; +ABD1;MEETEI MAYEK LETTER ATIYA;Lo;0;L;;;;;N;;;;; +ABD2;MEETEI MAYEK LETTER GOK;Lo;0;L;;;;;N;;;;; +ABD3;MEETEI MAYEK LETTER JHAM;Lo;0;L;;;;;N;;;;; +ABD4;MEETEI MAYEK LETTER RAI;Lo;0;L;;;;;N;;;;; +ABD5;MEETEI MAYEK LETTER BA;Lo;0;L;;;;;N;;;;; +ABD6;MEETEI MAYEK LETTER JIL;Lo;0;L;;;;;N;;;;; +ABD7;MEETEI MAYEK LETTER DIL;Lo;0;L;;;;;N;;;;; +ABD8;MEETEI MAYEK LETTER GHOU;Lo;0;L;;;;;N;;;;; +ABD9;MEETEI MAYEK LETTER DHOU;Lo;0;L;;;;;N;;;;; +ABDA;MEETEI MAYEK LETTER BHAM;Lo;0;L;;;;;N;;;;; +ABDB;MEETEI MAYEK LETTER KOK LONSUM;Lo;0;L;;;;;N;;;;; +ABDC;MEETEI MAYEK LETTER LAI LONSUM;Lo;0;L;;;;;N;;;;; +ABDD;MEETEI MAYEK LETTER MIT LONSUM;Lo;0;L;;;;;N;;;;; +ABDE;MEETEI MAYEK LETTER PA LONSUM;Lo;0;L;;;;;N;;;;; +ABDF;MEETEI MAYEK LETTER NA LONSUM;Lo;0;L;;;;;N;;;;; +ABE0;MEETEI MAYEK LETTER TIL LONSUM;Lo;0;L;;;;;N;;;;; +ABE1;MEETEI MAYEK LETTER NGOU LONSUM;Lo;0;L;;;;;N;;;;; +ABE2;MEETEI MAYEK LETTER I LONSUM;Lo;0;L;;;;;N;;;;; +ABE3;MEETEI MAYEK VOWEL SIGN ONAP;Mc;0;L;;;;;N;;;;; +ABE4;MEETEI MAYEK VOWEL SIGN INAP;Mc;0;L;;;;;N;;;;; +ABE5;MEETEI MAYEK VOWEL SIGN ANAP;Mn;0;NSM;;;;;N;;;;; +ABE6;MEETEI MAYEK VOWEL SIGN YENAP;Mc;0;L;;;;;N;;;;; +ABE7;MEETEI MAYEK VOWEL SIGN SOUNAP;Mc;0;L;;;;;N;;;;; +ABE8;MEETEI MAYEK VOWEL SIGN UNAP;Mn;0;NSM;;;;;N;;;;; +ABE9;MEETEI MAYEK VOWEL SIGN CHEINAP;Mc;0;L;;;;;N;;;;; +ABEA;MEETEI MAYEK VOWEL SIGN NUNG;Mc;0;L;;;;;N;;;;; +ABEB;MEETEI MAYEK CHEIKHEI;Po;0;L;;;;;N;;;;; +ABEC;MEETEI MAYEK LUM IYEK;Mc;0;L;;;;;N;;;;; +ABED;MEETEI MAYEK APUN IYEK;Mn;9;NSM;;;;;N;;;;; +ABF0;MEETEI MAYEK DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +ABF1;MEETEI MAYEK DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +ABF2;MEETEI MAYEK DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +ABF3;MEETEI MAYEK DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +ABF4;MEETEI MAYEK DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +ABF5;MEETEI MAYEK DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +ABF6;MEETEI MAYEK DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +ABF7;MEETEI MAYEK DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +ABF8;MEETEI MAYEK DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +ABF9;MEETEI MAYEK DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +AC00;<Hangul Syllable, First>;Lo;0;L;;;;;N;;;;; +D7A3;<Hangul Syllable, Last>;Lo;0;L;;;;;N;;;;; +D7B0;HANGUL JUNGSEONG O-YEO;Lo;0;L;;;;;N;;;;; +D7B1;HANGUL JUNGSEONG O-O-I;Lo;0;L;;;;;N;;;;; +D7B2;HANGUL JUNGSEONG YO-A;Lo;0;L;;;;;N;;;;; +D7B3;HANGUL JUNGSEONG YO-AE;Lo;0;L;;;;;N;;;;; +D7B4;HANGUL JUNGSEONG YO-EO;Lo;0;L;;;;;N;;;;; +D7B5;HANGUL JUNGSEONG U-YEO;Lo;0;L;;;;;N;;;;; +D7B6;HANGUL JUNGSEONG U-I-I;Lo;0;L;;;;;N;;;;; +D7B7;HANGUL JUNGSEONG YU-AE;Lo;0;L;;;;;N;;;;; +D7B8;HANGUL JUNGSEONG YU-O;Lo;0;L;;;;;N;;;;; +D7B9;HANGUL JUNGSEONG EU-A;Lo;0;L;;;;;N;;;;; +D7BA;HANGUL JUNGSEONG EU-EO;Lo;0;L;;;;;N;;;;; +D7BB;HANGUL JUNGSEONG EU-E;Lo;0;L;;;;;N;;;;; +D7BC;HANGUL JUNGSEONG EU-O;Lo;0;L;;;;;N;;;;; +D7BD;HANGUL JUNGSEONG I-YA-O;Lo;0;L;;;;;N;;;;; +D7BE;HANGUL JUNGSEONG I-YAE;Lo;0;L;;;;;N;;;;; +D7BF;HANGUL JUNGSEONG I-YEO;Lo;0;L;;;;;N;;;;; +D7C0;HANGUL JUNGSEONG I-YE;Lo;0;L;;;;;N;;;;; +D7C1;HANGUL JUNGSEONG I-O-I;Lo;0;L;;;;;N;;;;; +D7C2;HANGUL JUNGSEONG I-YO;Lo;0;L;;;;;N;;;;; +D7C3;HANGUL JUNGSEONG I-YU;Lo;0;L;;;;;N;;;;; +D7C4;HANGUL JUNGSEONG I-I;Lo;0;L;;;;;N;;;;; +D7C5;HANGUL JUNGSEONG ARAEA-A;Lo;0;L;;;;;N;;;;; +D7C6;HANGUL JUNGSEONG ARAEA-E;Lo;0;L;;;;;N;;;;; +D7CB;HANGUL JONGSEONG NIEUN-RIEUL;Lo;0;L;;;;;N;;;;; +D7CC;HANGUL JONGSEONG NIEUN-CHIEUCH;Lo;0;L;;;;;N;;;;; +D7CD;HANGUL JONGSEONG SSANGTIKEUT;Lo;0;L;;;;;N;;;;; +D7CE;HANGUL JONGSEONG SSANGTIKEUT-PIEUP;Lo;0;L;;;;;N;;;;; +D7CF;HANGUL JONGSEONG TIKEUT-PIEUP;Lo;0;L;;;;;N;;;;; +D7D0;HANGUL JONGSEONG TIKEUT-SIOS;Lo;0;L;;;;;N;;;;; +D7D1;HANGUL JONGSEONG TIKEUT-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;; +D7D2;HANGUL JONGSEONG TIKEUT-CIEUC;Lo;0;L;;;;;N;;;;; +D7D3;HANGUL JONGSEONG TIKEUT-CHIEUCH;Lo;0;L;;;;;N;;;;; +D7D4;HANGUL JONGSEONG TIKEUT-THIEUTH;Lo;0;L;;;;;N;;;;; +D7D5;HANGUL JONGSEONG RIEUL-SSANGKIYEOK;Lo;0;L;;;;;N;;;;; +D7D6;HANGUL JONGSEONG RIEUL-KIYEOK-HIEUH;Lo;0;L;;;;;N;;;;; +D7D7;HANGUL JONGSEONG SSANGRIEUL-KHIEUKH;Lo;0;L;;;;;N;;;;; +D7D8;HANGUL JONGSEONG RIEUL-MIEUM-HIEUH;Lo;0;L;;;;;N;;;;; +D7D9;HANGUL JONGSEONG RIEUL-PIEUP-TIKEUT;Lo;0;L;;;;;N;;;;; +D7DA;HANGUL JONGSEONG RIEUL-PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;; +D7DB;HANGUL JONGSEONG RIEUL-YESIEUNG;Lo;0;L;;;;;N;;;;; +D7DC;HANGUL JONGSEONG RIEUL-YEORINHIEUH-HIEUH;Lo;0;L;;;;;N;;;;; +D7DD;HANGUL JONGSEONG KAPYEOUNRIEUL;Lo;0;L;;;;;N;;;;; +D7DE;HANGUL JONGSEONG MIEUM-NIEUN;Lo;0;L;;;;;N;;;;; +D7DF;HANGUL JONGSEONG MIEUM-SSANGNIEUN;Lo;0;L;;;;;N;;;;; +D7E0;HANGUL JONGSEONG SSANGMIEUM;Lo;0;L;;;;;N;;;;; +D7E1;HANGUL JONGSEONG MIEUM-PIEUP-SIOS;Lo;0;L;;;;;N;;;;; +D7E2;HANGUL JONGSEONG MIEUM-CIEUC;Lo;0;L;;;;;N;;;;; +D7E3;HANGUL JONGSEONG PIEUP-TIKEUT;Lo;0;L;;;;;N;;;;; +D7E4;HANGUL JONGSEONG PIEUP-RIEUL-PHIEUPH;Lo;0;L;;;;;N;;;;; +D7E5;HANGUL JONGSEONG PIEUP-MIEUM;Lo;0;L;;;;;N;;;;; +D7E6;HANGUL JONGSEONG SSANGPIEUP;Lo;0;L;;;;;N;;;;; +D7E7;HANGUL JONGSEONG PIEUP-SIOS-TIKEUT;Lo;0;L;;;;;N;;;;; +D7E8;HANGUL JONGSEONG PIEUP-CIEUC;Lo;0;L;;;;;N;;;;; +D7E9;HANGUL JONGSEONG PIEUP-CHIEUCH;Lo;0;L;;;;;N;;;;; +D7EA;HANGUL JONGSEONG SIOS-MIEUM;Lo;0;L;;;;;N;;;;; +D7EB;HANGUL JONGSEONG SIOS-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; +D7EC;HANGUL JONGSEONG SSANGSIOS-KIYEOK;Lo;0;L;;;;;N;;;;; +D7ED;HANGUL JONGSEONG SSANGSIOS-TIKEUT;Lo;0;L;;;;;N;;;;; +D7EE;HANGUL JONGSEONG SIOS-PANSIOS;Lo;0;L;;;;;N;;;;; +D7EF;HANGUL JONGSEONG SIOS-CIEUC;Lo;0;L;;;;;N;;;;; +D7F0;HANGUL JONGSEONG SIOS-CHIEUCH;Lo;0;L;;;;;N;;;;; +D7F1;HANGUL JONGSEONG SIOS-THIEUTH;Lo;0;L;;;;;N;;;;; +D7F2;HANGUL JONGSEONG SIOS-HIEUH;Lo;0;L;;;;;N;;;;; +D7F3;HANGUL JONGSEONG PANSIOS-PIEUP;Lo;0;L;;;;;N;;;;; +D7F4;HANGUL JONGSEONG PANSIOS-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; +D7F5;HANGUL JONGSEONG YESIEUNG-MIEUM;Lo;0;L;;;;;N;;;;; +D7F6;HANGUL JONGSEONG YESIEUNG-HIEUH;Lo;0;L;;;;;N;;;;; +D7F7;HANGUL JONGSEONG CIEUC-PIEUP;Lo;0;L;;;;;N;;;;; +D7F8;HANGUL JONGSEONG CIEUC-SSANGPIEUP;Lo;0;L;;;;;N;;;;; +D7F9;HANGUL JONGSEONG SSANGCIEUC;Lo;0;L;;;;;N;;;;; +D7FA;HANGUL JONGSEONG PHIEUPH-SIOS;Lo;0;L;;;;;N;;;;; +D7FB;HANGUL JONGSEONG PHIEUPH-THIEUTH;Lo;0;L;;;;;N;;;;; +D800;<Non Private Use High Surrogate, First>;Cs;0;L;;;;;N;;;;; +DB7F;<Non Private Use High Surrogate, Last>;Cs;0;L;;;;;N;;;;; +DB80;<Private Use High Surrogate, First>;Cs;0;L;;;;;N;;;;; +DBFF;<Private Use High Surrogate, Last>;Cs;0;L;;;;;N;;;;; +DC00;<Low Surrogate, First>;Cs;0;L;;;;;N;;;;; +DFFF;<Low Surrogate, Last>;Cs;0;L;;;;;N;;;;; +E000;<Private Use, First>;Co;0;L;;;;;N;;;;; +F8FF;<Private Use, Last>;Co;0;L;;;;;N;;;;; +F900;CJK COMPATIBILITY IDEOGRAPH-F900;Lo;0;L;8C48;;;;N;;;;; +F901;CJK COMPATIBILITY IDEOGRAPH-F901;Lo;0;L;66F4;;;;N;;;;; +F902;CJK COMPATIBILITY IDEOGRAPH-F902;Lo;0;L;8ECA;;;;N;;;;; +F903;CJK COMPATIBILITY IDEOGRAPH-F903;Lo;0;L;8CC8;;;;N;;;;; +F904;CJK COMPATIBILITY IDEOGRAPH-F904;Lo;0;L;6ED1;;;;N;;;;; +F905;CJK COMPATIBILITY IDEOGRAPH-F905;Lo;0;L;4E32;;;;N;;;;; +F906;CJK COMPATIBILITY IDEOGRAPH-F906;Lo;0;L;53E5;;;;N;;;;; +F907;CJK COMPATIBILITY IDEOGRAPH-F907;Lo;0;L;9F9C;;;;N;;;;; +F908;CJK COMPATIBILITY IDEOGRAPH-F908;Lo;0;L;9F9C;;;;N;;;;; +F909;CJK COMPATIBILITY IDEOGRAPH-F909;Lo;0;L;5951;;;;N;;;;; +F90A;CJK COMPATIBILITY IDEOGRAPH-F90A;Lo;0;L;91D1;;;;N;;;;; +F90B;CJK COMPATIBILITY IDEOGRAPH-F90B;Lo;0;L;5587;;;;N;;;;; +F90C;CJK COMPATIBILITY IDEOGRAPH-F90C;Lo;0;L;5948;;;;N;;;;; +F90D;CJK COMPATIBILITY IDEOGRAPH-F90D;Lo;0;L;61F6;;;;N;;;;; +F90E;CJK COMPATIBILITY IDEOGRAPH-F90E;Lo;0;L;7669;;;;N;;;;; +F90F;CJK COMPATIBILITY IDEOGRAPH-F90F;Lo;0;L;7F85;;;;N;;;;; +F910;CJK COMPATIBILITY IDEOGRAPH-F910;Lo;0;L;863F;;;;N;;;;; +F911;CJK COMPATIBILITY IDEOGRAPH-F911;Lo;0;L;87BA;;;;N;;;;; +F912;CJK COMPATIBILITY IDEOGRAPH-F912;Lo;0;L;88F8;;;;N;;;;; +F913;CJK COMPATIBILITY IDEOGRAPH-F913;Lo;0;L;908F;;;;N;;;;; +F914;CJK COMPATIBILITY IDEOGRAPH-F914;Lo;0;L;6A02;;;;N;;;;; +F915;CJK COMPATIBILITY IDEOGRAPH-F915;Lo;0;L;6D1B;;;;N;;;;; +F916;CJK COMPATIBILITY IDEOGRAPH-F916;Lo;0;L;70D9;;;;N;;;;; +F917;CJK COMPATIBILITY IDEOGRAPH-F917;Lo;0;L;73DE;;;;N;;;;; +F918;CJK COMPATIBILITY IDEOGRAPH-F918;Lo;0;L;843D;;;;N;;;;; +F919;CJK COMPATIBILITY IDEOGRAPH-F919;Lo;0;L;916A;;;;N;;;;; +F91A;CJK COMPATIBILITY IDEOGRAPH-F91A;Lo;0;L;99F1;;;;N;;;;; +F91B;CJK COMPATIBILITY IDEOGRAPH-F91B;Lo;0;L;4E82;;;;N;;;;; +F91C;CJK COMPATIBILITY IDEOGRAPH-F91C;Lo;0;L;5375;;;;N;;;;; +F91D;CJK COMPATIBILITY IDEOGRAPH-F91D;Lo;0;L;6B04;;;;N;;;;; +F91E;CJK COMPATIBILITY IDEOGRAPH-F91E;Lo;0;L;721B;;;;N;;;;; +F91F;CJK COMPATIBILITY IDEOGRAPH-F91F;Lo;0;L;862D;;;;N;;;;; +F920;CJK COMPATIBILITY IDEOGRAPH-F920;Lo;0;L;9E1E;;;;N;;;;; +F921;CJK COMPATIBILITY IDEOGRAPH-F921;Lo;0;L;5D50;;;;N;;;;; +F922;CJK COMPATIBILITY IDEOGRAPH-F922;Lo;0;L;6FEB;;;;N;;;;; +F923;CJK COMPATIBILITY IDEOGRAPH-F923;Lo;0;L;85CD;;;;N;;;;; +F924;CJK COMPATIBILITY IDEOGRAPH-F924;Lo;0;L;8964;;;;N;;;;; +F925;CJK COMPATIBILITY IDEOGRAPH-F925;Lo;0;L;62C9;;;;N;;;;; +F926;CJK COMPATIBILITY IDEOGRAPH-F926;Lo;0;L;81D8;;;;N;;;;; +F927;CJK COMPATIBILITY IDEOGRAPH-F927;Lo;0;L;881F;;;;N;;;;; +F928;CJK COMPATIBILITY IDEOGRAPH-F928;Lo;0;L;5ECA;;;;N;;;;; +F929;CJK COMPATIBILITY IDEOGRAPH-F929;Lo;0;L;6717;;;;N;;;;; +F92A;CJK COMPATIBILITY IDEOGRAPH-F92A;Lo;0;L;6D6A;;;;N;;;;; +F92B;CJK COMPATIBILITY IDEOGRAPH-F92B;Lo;0;L;72FC;;;;N;;;;; +F92C;CJK COMPATIBILITY IDEOGRAPH-F92C;Lo;0;L;90CE;;;;N;;;;; +F92D;CJK COMPATIBILITY IDEOGRAPH-F92D;Lo;0;L;4F86;;;;N;;;;; +F92E;CJK COMPATIBILITY IDEOGRAPH-F92E;Lo;0;L;51B7;;;;N;;;;; +F92F;CJK COMPATIBILITY IDEOGRAPH-F92F;Lo;0;L;52DE;;;;N;;;;; +F930;CJK COMPATIBILITY IDEOGRAPH-F930;Lo;0;L;64C4;;;;N;;;;; +F931;CJK COMPATIBILITY IDEOGRAPH-F931;Lo;0;L;6AD3;;;;N;;;;; +F932;CJK COMPATIBILITY IDEOGRAPH-F932;Lo;0;L;7210;;;;N;;;;; +F933;CJK COMPATIBILITY IDEOGRAPH-F933;Lo;0;L;76E7;;;;N;;;;; +F934;CJK COMPATIBILITY IDEOGRAPH-F934;Lo;0;L;8001;;;;N;;;;; +F935;CJK COMPATIBILITY IDEOGRAPH-F935;Lo;0;L;8606;;;;N;;;;; +F936;CJK COMPATIBILITY IDEOGRAPH-F936;Lo;0;L;865C;;;;N;;;;; +F937;CJK COMPATIBILITY IDEOGRAPH-F937;Lo;0;L;8DEF;;;;N;;;;; +F938;CJK COMPATIBILITY IDEOGRAPH-F938;Lo;0;L;9732;;;;N;;;;; +F939;CJK COMPATIBILITY IDEOGRAPH-F939;Lo;0;L;9B6F;;;;N;;;;; +F93A;CJK COMPATIBILITY IDEOGRAPH-F93A;Lo;0;L;9DFA;;;;N;;;;; +F93B;CJK COMPATIBILITY IDEOGRAPH-F93B;Lo;0;L;788C;;;;N;;;;; +F93C;CJK COMPATIBILITY IDEOGRAPH-F93C;Lo;0;L;797F;;;;N;;;;; +F93D;CJK COMPATIBILITY IDEOGRAPH-F93D;Lo;0;L;7DA0;;;;N;;;;; +F93E;CJK COMPATIBILITY IDEOGRAPH-F93E;Lo;0;L;83C9;;;;N;;;;; +F93F;CJK COMPATIBILITY IDEOGRAPH-F93F;Lo;0;L;9304;;;;N;;;;; +F940;CJK COMPATIBILITY IDEOGRAPH-F940;Lo;0;L;9E7F;;;;N;;;;; +F941;CJK COMPATIBILITY IDEOGRAPH-F941;Lo;0;L;8AD6;;;;N;;;;; +F942;CJK COMPATIBILITY IDEOGRAPH-F942;Lo;0;L;58DF;;;;N;;;;; +F943;CJK COMPATIBILITY IDEOGRAPH-F943;Lo;0;L;5F04;;;;N;;;;; +F944;CJK COMPATIBILITY IDEOGRAPH-F944;Lo;0;L;7C60;;;;N;;;;; +F945;CJK COMPATIBILITY IDEOGRAPH-F945;Lo;0;L;807E;;;;N;;;;; +F946;CJK COMPATIBILITY IDEOGRAPH-F946;Lo;0;L;7262;;;;N;;;;; +F947;CJK COMPATIBILITY IDEOGRAPH-F947;Lo;0;L;78CA;;;;N;;;;; +F948;CJK COMPATIBILITY IDEOGRAPH-F948;Lo;0;L;8CC2;;;;N;;;;; +F949;CJK COMPATIBILITY IDEOGRAPH-F949;Lo;0;L;96F7;;;;N;;;;; +F94A;CJK COMPATIBILITY IDEOGRAPH-F94A;Lo;0;L;58D8;;;;N;;;;; +F94B;CJK COMPATIBILITY IDEOGRAPH-F94B;Lo;0;L;5C62;;;;N;;;;; +F94C;CJK COMPATIBILITY IDEOGRAPH-F94C;Lo;0;L;6A13;;;;N;;;;; +F94D;CJK COMPATIBILITY IDEOGRAPH-F94D;Lo;0;L;6DDA;;;;N;;;;; +F94E;CJK COMPATIBILITY IDEOGRAPH-F94E;Lo;0;L;6F0F;;;;N;;;;; +F94F;CJK COMPATIBILITY IDEOGRAPH-F94F;Lo;0;L;7D2F;;;;N;;;;; +F950;CJK COMPATIBILITY IDEOGRAPH-F950;Lo;0;L;7E37;;;;N;;;;; +F951;CJK COMPATIBILITY IDEOGRAPH-F951;Lo;0;L;964B;;;;N;;;;; +F952;CJK COMPATIBILITY IDEOGRAPH-F952;Lo;0;L;52D2;;;;N;;;;; +F953;CJK COMPATIBILITY IDEOGRAPH-F953;Lo;0;L;808B;;;;N;;;;; +F954;CJK COMPATIBILITY IDEOGRAPH-F954;Lo;0;L;51DC;;;;N;;;;; +F955;CJK COMPATIBILITY IDEOGRAPH-F955;Lo;0;L;51CC;;;;N;;;;; +F956;CJK COMPATIBILITY IDEOGRAPH-F956;Lo;0;L;7A1C;;;;N;;;;; +F957;CJK COMPATIBILITY IDEOGRAPH-F957;Lo;0;L;7DBE;;;;N;;;;; +F958;CJK COMPATIBILITY IDEOGRAPH-F958;Lo;0;L;83F1;;;;N;;;;; +F959;CJK COMPATIBILITY IDEOGRAPH-F959;Lo;0;L;9675;;;;N;;;;; +F95A;CJK COMPATIBILITY IDEOGRAPH-F95A;Lo;0;L;8B80;;;;N;;;;; +F95B;CJK COMPATIBILITY IDEOGRAPH-F95B;Lo;0;L;62CF;;;;N;;;;; +F95C;CJK COMPATIBILITY IDEOGRAPH-F95C;Lo;0;L;6A02;;;;N;;;;; +F95D;CJK COMPATIBILITY IDEOGRAPH-F95D;Lo;0;L;8AFE;;;;N;;;;; +F95E;CJK COMPATIBILITY IDEOGRAPH-F95E;Lo;0;L;4E39;;;;N;;;;; +F95F;CJK COMPATIBILITY IDEOGRAPH-F95F;Lo;0;L;5BE7;;;;N;;;;; +F960;CJK COMPATIBILITY IDEOGRAPH-F960;Lo;0;L;6012;;;;N;;;;; +F961;CJK COMPATIBILITY IDEOGRAPH-F961;Lo;0;L;7387;;;;N;;;;; +F962;CJK COMPATIBILITY IDEOGRAPH-F962;Lo;0;L;7570;;;;N;;;;; +F963;CJK COMPATIBILITY IDEOGRAPH-F963;Lo;0;L;5317;;;;N;;;;; +F964;CJK COMPATIBILITY IDEOGRAPH-F964;Lo;0;L;78FB;;;;N;;;;; +F965;CJK COMPATIBILITY IDEOGRAPH-F965;Lo;0;L;4FBF;;;;N;;;;; +F966;CJK COMPATIBILITY IDEOGRAPH-F966;Lo;0;L;5FA9;;;;N;;;;; +F967;CJK COMPATIBILITY IDEOGRAPH-F967;Lo;0;L;4E0D;;;;N;;;;; +F968;CJK COMPATIBILITY IDEOGRAPH-F968;Lo;0;L;6CCC;;;;N;;;;; +F969;CJK COMPATIBILITY IDEOGRAPH-F969;Lo;0;L;6578;;;;N;;;;; +F96A;CJK COMPATIBILITY IDEOGRAPH-F96A;Lo;0;L;7D22;;;;N;;;;; +F96B;CJK COMPATIBILITY IDEOGRAPH-F96B;Lo;0;L;53C3;;;3;N;;;;; +F96C;CJK COMPATIBILITY IDEOGRAPH-F96C;Lo;0;L;585E;;;;N;;;;; +F96D;CJK COMPATIBILITY IDEOGRAPH-F96D;Lo;0;L;7701;;;;N;;;;; +F96E;CJK COMPATIBILITY IDEOGRAPH-F96E;Lo;0;L;8449;;;;N;;;;; +F96F;CJK COMPATIBILITY IDEOGRAPH-F96F;Lo;0;L;8AAA;;;;N;;;;; +F970;CJK COMPATIBILITY IDEOGRAPH-F970;Lo;0;L;6BBA;;;;N;;;;; +F971;CJK COMPATIBILITY IDEOGRAPH-F971;Lo;0;L;8FB0;;;;N;;;;; +F972;CJK COMPATIBILITY IDEOGRAPH-F972;Lo;0;L;6C88;;;;N;;;;; +F973;CJK COMPATIBILITY IDEOGRAPH-F973;Lo;0;L;62FE;;;10;N;;;;; +F974;CJK COMPATIBILITY IDEOGRAPH-F974;Lo;0;L;82E5;;;;N;;;;; +F975;CJK COMPATIBILITY IDEOGRAPH-F975;Lo;0;L;63A0;;;;N;;;;; +F976;CJK COMPATIBILITY IDEOGRAPH-F976;Lo;0;L;7565;;;;N;;;;; +F977;CJK COMPATIBILITY IDEOGRAPH-F977;Lo;0;L;4EAE;;;;N;;;;; +F978;CJK COMPATIBILITY IDEOGRAPH-F978;Lo;0;L;5169;;;2;N;;;;; +F979;CJK COMPATIBILITY IDEOGRAPH-F979;Lo;0;L;51C9;;;;N;;;;; +F97A;CJK COMPATIBILITY IDEOGRAPH-F97A;Lo;0;L;6881;;;;N;;;;; +F97B;CJK COMPATIBILITY IDEOGRAPH-F97B;Lo;0;L;7CE7;;;;N;;;;; +F97C;CJK COMPATIBILITY IDEOGRAPH-F97C;Lo;0;L;826F;;;;N;;;;; +F97D;CJK COMPATIBILITY IDEOGRAPH-F97D;Lo;0;L;8AD2;;;;N;;;;; +F97E;CJK COMPATIBILITY IDEOGRAPH-F97E;Lo;0;L;91CF;;;;N;;;;; +F97F;CJK COMPATIBILITY IDEOGRAPH-F97F;Lo;0;L;52F5;;;;N;;;;; +F980;CJK COMPATIBILITY IDEOGRAPH-F980;Lo;0;L;5442;;;;N;;;;; +F981;CJK COMPATIBILITY IDEOGRAPH-F981;Lo;0;L;5973;;;;N;;;;; +F982;CJK COMPATIBILITY IDEOGRAPH-F982;Lo;0;L;5EEC;;;;N;;;;; +F983;CJK COMPATIBILITY IDEOGRAPH-F983;Lo;0;L;65C5;;;;N;;;;; +F984;CJK COMPATIBILITY IDEOGRAPH-F984;Lo;0;L;6FFE;;;;N;;;;; +F985;CJK COMPATIBILITY IDEOGRAPH-F985;Lo;0;L;792A;;;;N;;;;; +F986;CJK COMPATIBILITY IDEOGRAPH-F986;Lo;0;L;95AD;;;;N;;;;; +F987;CJK COMPATIBILITY IDEOGRAPH-F987;Lo;0;L;9A6A;;;;N;;;;; +F988;CJK COMPATIBILITY IDEOGRAPH-F988;Lo;0;L;9E97;;;;N;;;;; +F989;CJK COMPATIBILITY IDEOGRAPH-F989;Lo;0;L;9ECE;;;;N;;;;; +F98A;CJK COMPATIBILITY IDEOGRAPH-F98A;Lo;0;L;529B;;;;N;;;;; +F98B;CJK COMPATIBILITY IDEOGRAPH-F98B;Lo;0;L;66C6;;;;N;;;;; +F98C;CJK COMPATIBILITY IDEOGRAPH-F98C;Lo;0;L;6B77;;;;N;;;;; +F98D;CJK COMPATIBILITY IDEOGRAPH-F98D;Lo;0;L;8F62;;;;N;;;;; +F98E;CJK COMPATIBILITY IDEOGRAPH-F98E;Lo;0;L;5E74;;;;N;;;;; +F98F;CJK COMPATIBILITY IDEOGRAPH-F98F;Lo;0;L;6190;;;;N;;;;; +F990;CJK COMPATIBILITY IDEOGRAPH-F990;Lo;0;L;6200;;;;N;;;;; +F991;CJK COMPATIBILITY IDEOGRAPH-F991;Lo;0;L;649A;;;;N;;;;; +F992;CJK COMPATIBILITY IDEOGRAPH-F992;Lo;0;L;6F23;;;;N;;;;; +F993;CJK COMPATIBILITY IDEOGRAPH-F993;Lo;0;L;7149;;;;N;;;;; +F994;CJK COMPATIBILITY IDEOGRAPH-F994;Lo;0;L;7489;;;;N;;;;; +F995;CJK COMPATIBILITY IDEOGRAPH-F995;Lo;0;L;79CA;;;;N;;;;; +F996;CJK COMPATIBILITY IDEOGRAPH-F996;Lo;0;L;7DF4;;;;N;;;;; +F997;CJK COMPATIBILITY IDEOGRAPH-F997;Lo;0;L;806F;;;;N;;;;; +F998;CJK COMPATIBILITY IDEOGRAPH-F998;Lo;0;L;8F26;;;;N;;;;; +F999;CJK COMPATIBILITY IDEOGRAPH-F999;Lo;0;L;84EE;;;;N;;;;; +F99A;CJK COMPATIBILITY IDEOGRAPH-F99A;Lo;0;L;9023;;;;N;;;;; +F99B;CJK COMPATIBILITY IDEOGRAPH-F99B;Lo;0;L;934A;;;;N;;;;; +F99C;CJK COMPATIBILITY IDEOGRAPH-F99C;Lo;0;L;5217;;;;N;;;;; +F99D;CJK COMPATIBILITY IDEOGRAPH-F99D;Lo;0;L;52A3;;;;N;;;;; +F99E;CJK COMPATIBILITY IDEOGRAPH-F99E;Lo;0;L;54BD;;;;N;;;;; +F99F;CJK COMPATIBILITY IDEOGRAPH-F99F;Lo;0;L;70C8;;;;N;;;;; +F9A0;CJK COMPATIBILITY IDEOGRAPH-F9A0;Lo;0;L;88C2;;;;N;;;;; +F9A1;CJK COMPATIBILITY IDEOGRAPH-F9A1;Lo;0;L;8AAA;;;;N;;;;; +F9A2;CJK COMPATIBILITY IDEOGRAPH-F9A2;Lo;0;L;5EC9;;;;N;;;;; +F9A3;CJK COMPATIBILITY IDEOGRAPH-F9A3;Lo;0;L;5FF5;;;;N;;;;; +F9A4;CJK COMPATIBILITY IDEOGRAPH-F9A4;Lo;0;L;637B;;;;N;;;;; +F9A5;CJK COMPATIBILITY IDEOGRAPH-F9A5;Lo;0;L;6BAE;;;;N;;;;; +F9A6;CJK COMPATIBILITY IDEOGRAPH-F9A6;Lo;0;L;7C3E;;;;N;;;;; +F9A7;CJK COMPATIBILITY IDEOGRAPH-F9A7;Lo;0;L;7375;;;;N;;;;; +F9A8;CJK COMPATIBILITY IDEOGRAPH-F9A8;Lo;0;L;4EE4;;;;N;;;;; +F9A9;CJK COMPATIBILITY IDEOGRAPH-F9A9;Lo;0;L;56F9;;;;N;;;;; +F9AA;CJK COMPATIBILITY IDEOGRAPH-F9AA;Lo;0;L;5BE7;;;;N;;;;; +F9AB;CJK COMPATIBILITY IDEOGRAPH-F9AB;Lo;0;L;5DBA;;;;N;;;;; +F9AC;CJK COMPATIBILITY IDEOGRAPH-F9AC;Lo;0;L;601C;;;;N;;;;; +F9AD;CJK COMPATIBILITY IDEOGRAPH-F9AD;Lo;0;L;73B2;;;;N;;;;; +F9AE;CJK COMPATIBILITY IDEOGRAPH-F9AE;Lo;0;L;7469;;;;N;;;;; +F9AF;CJK COMPATIBILITY IDEOGRAPH-F9AF;Lo;0;L;7F9A;;;;N;;;;; +F9B0;CJK COMPATIBILITY IDEOGRAPH-F9B0;Lo;0;L;8046;;;;N;;;;; +F9B1;CJK COMPATIBILITY IDEOGRAPH-F9B1;Lo;0;L;9234;;;;N;;;;; +F9B2;CJK COMPATIBILITY IDEOGRAPH-F9B2;Lo;0;L;96F6;;;0;N;;;;; +F9B3;CJK COMPATIBILITY IDEOGRAPH-F9B3;Lo;0;L;9748;;;;N;;;;; +F9B4;CJK COMPATIBILITY IDEOGRAPH-F9B4;Lo;0;L;9818;;;;N;;;;; +F9B5;CJK COMPATIBILITY IDEOGRAPH-F9B5;Lo;0;L;4F8B;;;;N;;;;; +F9B6;CJK COMPATIBILITY IDEOGRAPH-F9B6;Lo;0;L;79AE;;;;N;;;;; +F9B7;CJK COMPATIBILITY IDEOGRAPH-F9B7;Lo;0;L;91B4;;;;N;;;;; +F9B8;CJK COMPATIBILITY IDEOGRAPH-F9B8;Lo;0;L;96B8;;;;N;;;;; +F9B9;CJK COMPATIBILITY IDEOGRAPH-F9B9;Lo;0;L;60E1;;;;N;;;;; +F9BA;CJK COMPATIBILITY IDEOGRAPH-F9BA;Lo;0;L;4E86;;;;N;;;;; +F9BB;CJK COMPATIBILITY IDEOGRAPH-F9BB;Lo;0;L;50DA;;;;N;;;;; +F9BC;CJK COMPATIBILITY IDEOGRAPH-F9BC;Lo;0;L;5BEE;;;;N;;;;; +F9BD;CJK COMPATIBILITY IDEOGRAPH-F9BD;Lo;0;L;5C3F;;;;N;;;;; +F9BE;CJK COMPATIBILITY IDEOGRAPH-F9BE;Lo;0;L;6599;;;;N;;;;; +F9BF;CJK COMPATIBILITY IDEOGRAPH-F9BF;Lo;0;L;6A02;;;;N;;;;; +F9C0;CJK COMPATIBILITY IDEOGRAPH-F9C0;Lo;0;L;71CE;;;;N;;;;; +F9C1;CJK COMPATIBILITY IDEOGRAPH-F9C1;Lo;0;L;7642;;;;N;;;;; +F9C2;CJK COMPATIBILITY IDEOGRAPH-F9C2;Lo;0;L;84FC;;;;N;;;;; +F9C3;CJK COMPATIBILITY IDEOGRAPH-F9C3;Lo;0;L;907C;;;;N;;;;; +F9C4;CJK COMPATIBILITY IDEOGRAPH-F9C4;Lo;0;L;9F8D;;;;N;;;;; +F9C5;CJK COMPATIBILITY IDEOGRAPH-F9C5;Lo;0;L;6688;;;;N;;;;; +F9C6;CJK COMPATIBILITY IDEOGRAPH-F9C6;Lo;0;L;962E;;;;N;;;;; +F9C7;CJK COMPATIBILITY IDEOGRAPH-F9C7;Lo;0;L;5289;;;;N;;;;; +F9C8;CJK COMPATIBILITY IDEOGRAPH-F9C8;Lo;0;L;677B;;;;N;;;;; +F9C9;CJK COMPATIBILITY IDEOGRAPH-F9C9;Lo;0;L;67F3;;;;N;;;;; +F9CA;CJK COMPATIBILITY IDEOGRAPH-F9CA;Lo;0;L;6D41;;;;N;;;;; +F9CB;CJK COMPATIBILITY IDEOGRAPH-F9CB;Lo;0;L;6E9C;;;;N;;;;; +F9CC;CJK COMPATIBILITY IDEOGRAPH-F9CC;Lo;0;L;7409;;;;N;;;;; +F9CD;CJK COMPATIBILITY IDEOGRAPH-F9CD;Lo;0;L;7559;;;;N;;;;; +F9CE;CJK COMPATIBILITY IDEOGRAPH-F9CE;Lo;0;L;786B;;;;N;;;;; +F9CF;CJK COMPATIBILITY IDEOGRAPH-F9CF;Lo;0;L;7D10;;;;N;;;;; +F9D0;CJK COMPATIBILITY IDEOGRAPH-F9D0;Lo;0;L;985E;;;;N;;;;; +F9D1;CJK COMPATIBILITY IDEOGRAPH-F9D1;Lo;0;L;516D;;;6;N;;;;; +F9D2;CJK COMPATIBILITY IDEOGRAPH-F9D2;Lo;0;L;622E;;;;N;;;;; +F9D3;CJK COMPATIBILITY IDEOGRAPH-F9D3;Lo;0;L;9678;;;6;N;;;;; +F9D4;CJK COMPATIBILITY IDEOGRAPH-F9D4;Lo;0;L;502B;;;;N;;;;; +F9D5;CJK COMPATIBILITY IDEOGRAPH-F9D5;Lo;0;L;5D19;;;;N;;;;; +F9D6;CJK COMPATIBILITY IDEOGRAPH-F9D6;Lo;0;L;6DEA;;;;N;;;;; +F9D7;CJK COMPATIBILITY IDEOGRAPH-F9D7;Lo;0;L;8F2A;;;;N;;;;; +F9D8;CJK COMPATIBILITY IDEOGRAPH-F9D8;Lo;0;L;5F8B;;;;N;;;;; +F9D9;CJK COMPATIBILITY IDEOGRAPH-F9D9;Lo;0;L;6144;;;;N;;;;; +F9DA;CJK COMPATIBILITY IDEOGRAPH-F9DA;Lo;0;L;6817;;;;N;;;;; +F9DB;CJK COMPATIBILITY IDEOGRAPH-F9DB;Lo;0;L;7387;;;;N;;;;; +F9DC;CJK COMPATIBILITY IDEOGRAPH-F9DC;Lo;0;L;9686;;;;N;;;;; +F9DD;CJK COMPATIBILITY IDEOGRAPH-F9DD;Lo;0;L;5229;;;;N;;;;; +F9DE;CJK COMPATIBILITY IDEOGRAPH-F9DE;Lo;0;L;540F;;;;N;;;;; +F9DF;CJK COMPATIBILITY IDEOGRAPH-F9DF;Lo;0;L;5C65;;;;N;;;;; +F9E0;CJK COMPATIBILITY IDEOGRAPH-F9E0;Lo;0;L;6613;;;;N;;;;; +F9E1;CJK COMPATIBILITY IDEOGRAPH-F9E1;Lo;0;L;674E;;;;N;;;;; +F9E2;CJK COMPATIBILITY IDEOGRAPH-F9E2;Lo;0;L;68A8;;;;N;;;;; +F9E3;CJK COMPATIBILITY IDEOGRAPH-F9E3;Lo;0;L;6CE5;;;;N;;;;; +F9E4;CJK COMPATIBILITY IDEOGRAPH-F9E4;Lo;0;L;7406;;;;N;;;;; +F9E5;CJK COMPATIBILITY IDEOGRAPH-F9E5;Lo;0;L;75E2;;;;N;;;;; +F9E6;CJK COMPATIBILITY IDEOGRAPH-F9E6;Lo;0;L;7F79;;;;N;;;;; +F9E7;CJK COMPATIBILITY IDEOGRAPH-F9E7;Lo;0;L;88CF;;;;N;;;;; +F9E8;CJK COMPATIBILITY IDEOGRAPH-F9E8;Lo;0;L;88E1;;;;N;;;;; +F9E9;CJK COMPATIBILITY IDEOGRAPH-F9E9;Lo;0;L;91CC;;;;N;;;;; +F9EA;CJK COMPATIBILITY IDEOGRAPH-F9EA;Lo;0;L;96E2;;;;N;;;;; +F9EB;CJK COMPATIBILITY IDEOGRAPH-F9EB;Lo;0;L;533F;;;;N;;;;; +F9EC;CJK COMPATIBILITY IDEOGRAPH-F9EC;Lo;0;L;6EBA;;;;N;;;;; +F9ED;CJK COMPATIBILITY IDEOGRAPH-F9ED;Lo;0;L;541D;;;;N;;;;; +F9EE;CJK COMPATIBILITY IDEOGRAPH-F9EE;Lo;0;L;71D0;;;;N;;;;; +F9EF;CJK COMPATIBILITY IDEOGRAPH-F9EF;Lo;0;L;7498;;;;N;;;;; +F9F0;CJK COMPATIBILITY IDEOGRAPH-F9F0;Lo;0;L;85FA;;;;N;;;;; +F9F1;CJK COMPATIBILITY IDEOGRAPH-F9F1;Lo;0;L;96A3;;;;N;;;;; +F9F2;CJK COMPATIBILITY IDEOGRAPH-F9F2;Lo;0;L;9C57;;;;N;;;;; +F9F3;CJK COMPATIBILITY IDEOGRAPH-F9F3;Lo;0;L;9E9F;;;;N;;;;; +F9F4;CJK COMPATIBILITY IDEOGRAPH-F9F4;Lo;0;L;6797;;;;N;;;;; +F9F5;CJK COMPATIBILITY IDEOGRAPH-F9F5;Lo;0;L;6DCB;;;;N;;;;; +F9F6;CJK COMPATIBILITY IDEOGRAPH-F9F6;Lo;0;L;81E8;;;;N;;;;; +F9F7;CJK COMPATIBILITY IDEOGRAPH-F9F7;Lo;0;L;7ACB;;;;N;;;;; +F9F8;CJK COMPATIBILITY IDEOGRAPH-F9F8;Lo;0;L;7B20;;;;N;;;;; +F9F9;CJK COMPATIBILITY IDEOGRAPH-F9F9;Lo;0;L;7C92;;;;N;;;;; +F9FA;CJK COMPATIBILITY IDEOGRAPH-F9FA;Lo;0;L;72C0;;;;N;;;;; +F9FB;CJK COMPATIBILITY IDEOGRAPH-F9FB;Lo;0;L;7099;;;;N;;;;; +F9FC;CJK COMPATIBILITY IDEOGRAPH-F9FC;Lo;0;L;8B58;;;;N;;;;; +F9FD;CJK COMPATIBILITY IDEOGRAPH-F9FD;Lo;0;L;4EC0;;;10;N;;;;; +F9FE;CJK COMPATIBILITY IDEOGRAPH-F9FE;Lo;0;L;8336;;;;N;;;;; +F9FF;CJK COMPATIBILITY IDEOGRAPH-F9FF;Lo;0;L;523A;;;;N;;;;; +FA00;CJK COMPATIBILITY IDEOGRAPH-FA00;Lo;0;L;5207;;;;N;;;;; +FA01;CJK COMPATIBILITY IDEOGRAPH-FA01;Lo;0;L;5EA6;;;;N;;;;; +FA02;CJK COMPATIBILITY IDEOGRAPH-FA02;Lo;0;L;62D3;;;;N;;;;; +FA03;CJK COMPATIBILITY IDEOGRAPH-FA03;Lo;0;L;7CD6;;;;N;;;;; +FA04;CJK COMPATIBILITY IDEOGRAPH-FA04;Lo;0;L;5B85;;;;N;;;;; +FA05;CJK COMPATIBILITY IDEOGRAPH-FA05;Lo;0;L;6D1E;;;;N;;;;; +FA06;CJK COMPATIBILITY IDEOGRAPH-FA06;Lo;0;L;66B4;;;;N;;;;; +FA07;CJK COMPATIBILITY IDEOGRAPH-FA07;Lo;0;L;8F3B;;;;N;;;;; +FA08;CJK COMPATIBILITY IDEOGRAPH-FA08;Lo;0;L;884C;;;;N;;;;; +FA09;CJK COMPATIBILITY IDEOGRAPH-FA09;Lo;0;L;964D;;;;N;;;;; +FA0A;CJK COMPATIBILITY IDEOGRAPH-FA0A;Lo;0;L;898B;;;;N;;;;; +FA0B;CJK COMPATIBILITY IDEOGRAPH-FA0B;Lo;0;L;5ED3;;;;N;;;;; +FA0C;CJK COMPATIBILITY IDEOGRAPH-FA0C;Lo;0;L;5140;;;;N;;;;; +FA0D;CJK COMPATIBILITY IDEOGRAPH-FA0D;Lo;0;L;55C0;;;;N;;;;; +FA0E;CJK COMPATIBILITY IDEOGRAPH-FA0E;Lo;0;L;;;;;N;;;;; +FA0F;CJK COMPATIBILITY IDEOGRAPH-FA0F;Lo;0;L;;;;;N;;;;; +FA10;CJK COMPATIBILITY IDEOGRAPH-FA10;Lo;0;L;585A;;;;N;;;;; +FA11;CJK COMPATIBILITY IDEOGRAPH-FA11;Lo;0;L;;;;;N;;;;; +FA12;CJK COMPATIBILITY IDEOGRAPH-FA12;Lo;0;L;6674;;;;N;;;;; +FA13;CJK COMPATIBILITY IDEOGRAPH-FA13;Lo;0;L;;;;;N;;;;; +FA14;CJK COMPATIBILITY IDEOGRAPH-FA14;Lo;0;L;;;;;N;;;;; +FA15;CJK COMPATIBILITY IDEOGRAPH-FA15;Lo;0;L;51DE;;;;N;;;;; +FA16;CJK COMPATIBILITY IDEOGRAPH-FA16;Lo;0;L;732A;;;;N;;;;; +FA17;CJK COMPATIBILITY IDEOGRAPH-FA17;Lo;0;L;76CA;;;;N;;;;; +FA18;CJK COMPATIBILITY IDEOGRAPH-FA18;Lo;0;L;793C;;;;N;;;;; +FA19;CJK COMPATIBILITY IDEOGRAPH-FA19;Lo;0;L;795E;;;;N;;;;; +FA1A;CJK COMPATIBILITY IDEOGRAPH-FA1A;Lo;0;L;7965;;;;N;;;;; +FA1B;CJK COMPATIBILITY IDEOGRAPH-FA1B;Lo;0;L;798F;;;;N;;;;; +FA1C;CJK COMPATIBILITY IDEOGRAPH-FA1C;Lo;0;L;9756;;;;N;;;;; +FA1D;CJK COMPATIBILITY IDEOGRAPH-FA1D;Lo;0;L;7CBE;;;;N;;;;; +FA1E;CJK COMPATIBILITY IDEOGRAPH-FA1E;Lo;0;L;7FBD;;;;N;;;;; +FA1F;CJK COMPATIBILITY IDEOGRAPH-FA1F;Lo;0;L;;;;;N;;;;; +FA20;CJK COMPATIBILITY IDEOGRAPH-FA20;Lo;0;L;8612;;;;N;;;;; +FA21;CJK COMPATIBILITY IDEOGRAPH-FA21;Lo;0;L;;;;;N;;;;; +FA22;CJK COMPATIBILITY IDEOGRAPH-FA22;Lo;0;L;8AF8;;;;N;;;;; +FA23;CJK COMPATIBILITY IDEOGRAPH-FA23;Lo;0;L;;;;;N;;;;; +FA24;CJK COMPATIBILITY IDEOGRAPH-FA24;Lo;0;L;;;;;N;;;;; +FA25;CJK COMPATIBILITY IDEOGRAPH-FA25;Lo;0;L;9038;;;;N;;;;; +FA26;CJK COMPATIBILITY IDEOGRAPH-FA26;Lo;0;L;90FD;;;;N;;;;; +FA27;CJK COMPATIBILITY IDEOGRAPH-FA27;Lo;0;L;;;;;N;;;;; +FA28;CJK COMPATIBILITY IDEOGRAPH-FA28;Lo;0;L;;;;;N;;;;; +FA29;CJK COMPATIBILITY IDEOGRAPH-FA29;Lo;0;L;;;;;N;;;;; +FA2A;CJK COMPATIBILITY IDEOGRAPH-FA2A;Lo;0;L;98EF;;;;N;;;;; +FA2B;CJK COMPATIBILITY IDEOGRAPH-FA2B;Lo;0;L;98FC;;;;N;;;;; +FA2C;CJK COMPATIBILITY IDEOGRAPH-FA2C;Lo;0;L;9928;;;;N;;;;; +FA2D;CJK COMPATIBILITY IDEOGRAPH-FA2D;Lo;0;L;9DB4;;;;N;;;;; +FA2E;CJK COMPATIBILITY IDEOGRAPH-FA2E;Lo;0;L;90DE;;;;N;;;;; +FA2F;CJK COMPATIBILITY IDEOGRAPH-FA2F;Lo;0;L;96B7;;;;N;;;;; +FA30;CJK COMPATIBILITY IDEOGRAPH-FA30;Lo;0;L;4FAE;;;;N;;;;; +FA31;CJK COMPATIBILITY IDEOGRAPH-FA31;Lo;0;L;50E7;;;;N;;;;; +FA32;CJK COMPATIBILITY IDEOGRAPH-FA32;Lo;0;L;514D;;;;N;;;;; +FA33;CJK COMPATIBILITY IDEOGRAPH-FA33;Lo;0;L;52C9;;;;N;;;;; +FA34;CJK COMPATIBILITY IDEOGRAPH-FA34;Lo;0;L;52E4;;;;N;;;;; +FA35;CJK COMPATIBILITY IDEOGRAPH-FA35;Lo;0;L;5351;;;;N;;;;; +FA36;CJK COMPATIBILITY IDEOGRAPH-FA36;Lo;0;L;559D;;;;N;;;;; +FA37;CJK COMPATIBILITY IDEOGRAPH-FA37;Lo;0;L;5606;;;;N;;;;; +FA38;CJK COMPATIBILITY IDEOGRAPH-FA38;Lo;0;L;5668;;;;N;;;;; +FA39;CJK COMPATIBILITY IDEOGRAPH-FA39;Lo;0;L;5840;;;;N;;;;; +FA3A;CJK COMPATIBILITY IDEOGRAPH-FA3A;Lo;0;L;58A8;;;;N;;;;; +FA3B;CJK COMPATIBILITY IDEOGRAPH-FA3B;Lo;0;L;5C64;;;;N;;;;; +FA3C;CJK COMPATIBILITY IDEOGRAPH-FA3C;Lo;0;L;5C6E;;;;N;;;;; +FA3D;CJK COMPATIBILITY IDEOGRAPH-FA3D;Lo;0;L;6094;;;;N;;;;; +FA3E;CJK COMPATIBILITY IDEOGRAPH-FA3E;Lo;0;L;6168;;;;N;;;;; +FA3F;CJK COMPATIBILITY IDEOGRAPH-FA3F;Lo;0;L;618E;;;;N;;;;; +FA40;CJK COMPATIBILITY IDEOGRAPH-FA40;Lo;0;L;61F2;;;;N;;;;; +FA41;CJK COMPATIBILITY IDEOGRAPH-FA41;Lo;0;L;654F;;;;N;;;;; +FA42;CJK COMPATIBILITY IDEOGRAPH-FA42;Lo;0;L;65E2;;;;N;;;;; +FA43;CJK COMPATIBILITY IDEOGRAPH-FA43;Lo;0;L;6691;;;;N;;;;; +FA44;CJK COMPATIBILITY IDEOGRAPH-FA44;Lo;0;L;6885;;;;N;;;;; +FA45;CJK COMPATIBILITY IDEOGRAPH-FA45;Lo;0;L;6D77;;;;N;;;;; +FA46;CJK COMPATIBILITY IDEOGRAPH-FA46;Lo;0;L;6E1A;;;;N;;;;; +FA47;CJK COMPATIBILITY IDEOGRAPH-FA47;Lo;0;L;6F22;;;;N;;;;; +FA48;CJK COMPATIBILITY IDEOGRAPH-FA48;Lo;0;L;716E;;;;N;;;;; +FA49;CJK COMPATIBILITY IDEOGRAPH-FA49;Lo;0;L;722B;;;;N;;;;; +FA4A;CJK COMPATIBILITY IDEOGRAPH-FA4A;Lo;0;L;7422;;;;N;;;;; +FA4B;CJK COMPATIBILITY IDEOGRAPH-FA4B;Lo;0;L;7891;;;;N;;;;; +FA4C;CJK COMPATIBILITY IDEOGRAPH-FA4C;Lo;0;L;793E;;;;N;;;;; +FA4D;CJK COMPATIBILITY IDEOGRAPH-FA4D;Lo;0;L;7949;;;;N;;;;; +FA4E;CJK COMPATIBILITY IDEOGRAPH-FA4E;Lo;0;L;7948;;;;N;;;;; +FA4F;CJK COMPATIBILITY IDEOGRAPH-FA4F;Lo;0;L;7950;;;;N;;;;; +FA50;CJK COMPATIBILITY IDEOGRAPH-FA50;Lo;0;L;7956;;;;N;;;;; +FA51;CJK COMPATIBILITY IDEOGRAPH-FA51;Lo;0;L;795D;;;;N;;;;; +FA52;CJK COMPATIBILITY IDEOGRAPH-FA52;Lo;0;L;798D;;;;N;;;;; +FA53;CJK COMPATIBILITY IDEOGRAPH-FA53;Lo;0;L;798E;;;;N;;;;; +FA54;CJK COMPATIBILITY IDEOGRAPH-FA54;Lo;0;L;7A40;;;;N;;;;; +FA55;CJK COMPATIBILITY IDEOGRAPH-FA55;Lo;0;L;7A81;;;;N;;;;; +FA56;CJK COMPATIBILITY IDEOGRAPH-FA56;Lo;0;L;7BC0;;;;N;;;;; +FA57;CJK COMPATIBILITY IDEOGRAPH-FA57;Lo;0;L;7DF4;;;;N;;;;; +FA58;CJK COMPATIBILITY IDEOGRAPH-FA58;Lo;0;L;7E09;;;;N;;;;; +FA59;CJK COMPATIBILITY IDEOGRAPH-FA59;Lo;0;L;7E41;;;;N;;;;; +FA5A;CJK COMPATIBILITY IDEOGRAPH-FA5A;Lo;0;L;7F72;;;;N;;;;; +FA5B;CJK COMPATIBILITY IDEOGRAPH-FA5B;Lo;0;L;8005;;;;N;;;;; +FA5C;CJK COMPATIBILITY IDEOGRAPH-FA5C;Lo;0;L;81ED;;;;N;;;;; +FA5D;CJK COMPATIBILITY IDEOGRAPH-FA5D;Lo;0;L;8279;;;;N;;;;; +FA5E;CJK COMPATIBILITY IDEOGRAPH-FA5E;Lo;0;L;8279;;;;N;;;;; +FA5F;CJK COMPATIBILITY IDEOGRAPH-FA5F;Lo;0;L;8457;;;;N;;;;; +FA60;CJK COMPATIBILITY IDEOGRAPH-FA60;Lo;0;L;8910;;;;N;;;;; +FA61;CJK COMPATIBILITY IDEOGRAPH-FA61;Lo;0;L;8996;;;;N;;;;; +FA62;CJK COMPATIBILITY IDEOGRAPH-FA62;Lo;0;L;8B01;;;;N;;;;; +FA63;CJK COMPATIBILITY IDEOGRAPH-FA63;Lo;0;L;8B39;;;;N;;;;; +FA64;CJK COMPATIBILITY IDEOGRAPH-FA64;Lo;0;L;8CD3;;;;N;;;;; +FA65;CJK COMPATIBILITY IDEOGRAPH-FA65;Lo;0;L;8D08;;;;N;;;;; +FA66;CJK COMPATIBILITY IDEOGRAPH-FA66;Lo;0;L;8FB6;;;;N;;;;; +FA67;CJK COMPATIBILITY IDEOGRAPH-FA67;Lo;0;L;9038;;;;N;;;;; +FA68;CJK COMPATIBILITY IDEOGRAPH-FA68;Lo;0;L;96E3;;;;N;;;;; +FA69;CJK COMPATIBILITY IDEOGRAPH-FA69;Lo;0;L;97FF;;;;N;;;;; +FA6A;CJK COMPATIBILITY IDEOGRAPH-FA6A;Lo;0;L;983B;;;;N;;;;; +FA6B;CJK COMPATIBILITY IDEOGRAPH-FA6B;Lo;0;L;6075;;;;N;;;;; +FA6C;CJK COMPATIBILITY IDEOGRAPH-FA6C;Lo;0;L;242EE;;;;N;;;;; +FA6D;CJK COMPATIBILITY IDEOGRAPH-FA6D;Lo;0;L;8218;;;;N;;;;; +FA70;CJK COMPATIBILITY IDEOGRAPH-FA70;Lo;0;L;4E26;;;;N;;;;; +FA71;CJK COMPATIBILITY IDEOGRAPH-FA71;Lo;0;L;51B5;;;;N;;;;; +FA72;CJK COMPATIBILITY IDEOGRAPH-FA72;Lo;0;L;5168;;;;N;;;;; +FA73;CJK COMPATIBILITY IDEOGRAPH-FA73;Lo;0;L;4F80;;;;N;;;;; +FA74;CJK COMPATIBILITY IDEOGRAPH-FA74;Lo;0;L;5145;;;;N;;;;; +FA75;CJK COMPATIBILITY IDEOGRAPH-FA75;Lo;0;L;5180;;;;N;;;;; +FA76;CJK COMPATIBILITY IDEOGRAPH-FA76;Lo;0;L;52C7;;;;N;;;;; +FA77;CJK COMPATIBILITY IDEOGRAPH-FA77;Lo;0;L;52FA;;;;N;;;;; +FA78;CJK COMPATIBILITY IDEOGRAPH-FA78;Lo;0;L;559D;;;;N;;;;; +FA79;CJK COMPATIBILITY IDEOGRAPH-FA79;Lo;0;L;5555;;;;N;;;;; +FA7A;CJK COMPATIBILITY IDEOGRAPH-FA7A;Lo;0;L;5599;;;;N;;;;; +FA7B;CJK COMPATIBILITY IDEOGRAPH-FA7B;Lo;0;L;55E2;;;;N;;;;; +FA7C;CJK COMPATIBILITY IDEOGRAPH-FA7C;Lo;0;L;585A;;;;N;;;;; +FA7D;CJK COMPATIBILITY IDEOGRAPH-FA7D;Lo;0;L;58B3;;;;N;;;;; +FA7E;CJK COMPATIBILITY IDEOGRAPH-FA7E;Lo;0;L;5944;;;;N;;;;; +FA7F;CJK COMPATIBILITY IDEOGRAPH-FA7F;Lo;0;L;5954;;;;N;;;;; +FA80;CJK COMPATIBILITY IDEOGRAPH-FA80;Lo;0;L;5A62;;;;N;;;;; +FA81;CJK COMPATIBILITY IDEOGRAPH-FA81;Lo;0;L;5B28;;;;N;;;;; +FA82;CJK COMPATIBILITY IDEOGRAPH-FA82;Lo;0;L;5ED2;;;;N;;;;; +FA83;CJK COMPATIBILITY IDEOGRAPH-FA83;Lo;0;L;5ED9;;;;N;;;;; +FA84;CJK COMPATIBILITY IDEOGRAPH-FA84;Lo;0;L;5F69;;;;N;;;;; +FA85;CJK COMPATIBILITY IDEOGRAPH-FA85;Lo;0;L;5FAD;;;;N;;;;; +FA86;CJK COMPATIBILITY IDEOGRAPH-FA86;Lo;0;L;60D8;;;;N;;;;; +FA87;CJK COMPATIBILITY IDEOGRAPH-FA87;Lo;0;L;614E;;;;N;;;;; +FA88;CJK COMPATIBILITY IDEOGRAPH-FA88;Lo;0;L;6108;;;;N;;;;; +FA89;CJK COMPATIBILITY IDEOGRAPH-FA89;Lo;0;L;618E;;;;N;;;;; +FA8A;CJK COMPATIBILITY IDEOGRAPH-FA8A;Lo;0;L;6160;;;;N;;;;; +FA8B;CJK COMPATIBILITY IDEOGRAPH-FA8B;Lo;0;L;61F2;;;;N;;;;; +FA8C;CJK COMPATIBILITY IDEOGRAPH-FA8C;Lo;0;L;6234;;;;N;;;;; +FA8D;CJK COMPATIBILITY IDEOGRAPH-FA8D;Lo;0;L;63C4;;;;N;;;;; +FA8E;CJK COMPATIBILITY IDEOGRAPH-FA8E;Lo;0;L;641C;;;;N;;;;; +FA8F;CJK COMPATIBILITY IDEOGRAPH-FA8F;Lo;0;L;6452;;;;N;;;;; +FA90;CJK COMPATIBILITY IDEOGRAPH-FA90;Lo;0;L;6556;;;;N;;;;; +FA91;CJK COMPATIBILITY IDEOGRAPH-FA91;Lo;0;L;6674;;;;N;;;;; +FA92;CJK COMPATIBILITY IDEOGRAPH-FA92;Lo;0;L;6717;;;;N;;;;; +FA93;CJK COMPATIBILITY IDEOGRAPH-FA93;Lo;0;L;671B;;;;N;;;;; +FA94;CJK COMPATIBILITY IDEOGRAPH-FA94;Lo;0;L;6756;;;;N;;;;; +FA95;CJK COMPATIBILITY IDEOGRAPH-FA95;Lo;0;L;6B79;;;;N;;;;; +FA96;CJK COMPATIBILITY IDEOGRAPH-FA96;Lo;0;L;6BBA;;;;N;;;;; +FA97;CJK COMPATIBILITY IDEOGRAPH-FA97;Lo;0;L;6D41;;;;N;;;;; +FA98;CJK COMPATIBILITY IDEOGRAPH-FA98;Lo;0;L;6EDB;;;;N;;;;; +FA99;CJK COMPATIBILITY IDEOGRAPH-FA99;Lo;0;L;6ECB;;;;N;;;;; +FA9A;CJK COMPATIBILITY IDEOGRAPH-FA9A;Lo;0;L;6F22;;;;N;;;;; +FA9B;CJK COMPATIBILITY IDEOGRAPH-FA9B;Lo;0;L;701E;;;;N;;;;; +FA9C;CJK COMPATIBILITY IDEOGRAPH-FA9C;Lo;0;L;716E;;;;N;;;;; +FA9D;CJK COMPATIBILITY IDEOGRAPH-FA9D;Lo;0;L;77A7;;;;N;;;;; +FA9E;CJK COMPATIBILITY IDEOGRAPH-FA9E;Lo;0;L;7235;;;;N;;;;; +FA9F;CJK COMPATIBILITY IDEOGRAPH-FA9F;Lo;0;L;72AF;;;;N;;;;; +FAA0;CJK COMPATIBILITY IDEOGRAPH-FAA0;Lo;0;L;732A;;;;N;;;;; +FAA1;CJK COMPATIBILITY IDEOGRAPH-FAA1;Lo;0;L;7471;;;;N;;;;; +FAA2;CJK COMPATIBILITY IDEOGRAPH-FAA2;Lo;0;L;7506;;;;N;;;;; +FAA3;CJK COMPATIBILITY IDEOGRAPH-FAA3;Lo;0;L;753B;;;;N;;;;; +FAA4;CJK COMPATIBILITY IDEOGRAPH-FAA4;Lo;0;L;761D;;;;N;;;;; +FAA5;CJK COMPATIBILITY IDEOGRAPH-FAA5;Lo;0;L;761F;;;;N;;;;; +FAA6;CJK COMPATIBILITY IDEOGRAPH-FAA6;Lo;0;L;76CA;;;;N;;;;; +FAA7;CJK COMPATIBILITY IDEOGRAPH-FAA7;Lo;0;L;76DB;;;;N;;;;; +FAA8;CJK COMPATIBILITY IDEOGRAPH-FAA8;Lo;0;L;76F4;;;;N;;;;; +FAA9;CJK COMPATIBILITY IDEOGRAPH-FAA9;Lo;0;L;774A;;;;N;;;;; +FAAA;CJK COMPATIBILITY IDEOGRAPH-FAAA;Lo;0;L;7740;;;;N;;;;; +FAAB;CJK COMPATIBILITY IDEOGRAPH-FAAB;Lo;0;L;78CC;;;;N;;;;; +FAAC;CJK COMPATIBILITY IDEOGRAPH-FAAC;Lo;0;L;7AB1;;;;N;;;;; +FAAD;CJK COMPATIBILITY IDEOGRAPH-FAAD;Lo;0;L;7BC0;;;;N;;;;; +FAAE;CJK COMPATIBILITY IDEOGRAPH-FAAE;Lo;0;L;7C7B;;;;N;;;;; +FAAF;CJK COMPATIBILITY IDEOGRAPH-FAAF;Lo;0;L;7D5B;;;;N;;;;; +FAB0;CJK COMPATIBILITY IDEOGRAPH-FAB0;Lo;0;L;7DF4;;;;N;;;;; +FAB1;CJK COMPATIBILITY IDEOGRAPH-FAB1;Lo;0;L;7F3E;;;;N;;;;; +FAB2;CJK COMPATIBILITY IDEOGRAPH-FAB2;Lo;0;L;8005;;;;N;;;;; +FAB3;CJK COMPATIBILITY IDEOGRAPH-FAB3;Lo;0;L;8352;;;;N;;;;; +FAB4;CJK COMPATIBILITY IDEOGRAPH-FAB4;Lo;0;L;83EF;;;;N;;;;; +FAB5;CJK COMPATIBILITY IDEOGRAPH-FAB5;Lo;0;L;8779;;;;N;;;;; +FAB6;CJK COMPATIBILITY IDEOGRAPH-FAB6;Lo;0;L;8941;;;;N;;;;; +FAB7;CJK COMPATIBILITY IDEOGRAPH-FAB7;Lo;0;L;8986;;;;N;;;;; +FAB8;CJK COMPATIBILITY IDEOGRAPH-FAB8;Lo;0;L;8996;;;;N;;;;; +FAB9;CJK COMPATIBILITY IDEOGRAPH-FAB9;Lo;0;L;8ABF;;;;N;;;;; +FABA;CJK COMPATIBILITY IDEOGRAPH-FABA;Lo;0;L;8AF8;;;;N;;;;; +FABB;CJK COMPATIBILITY IDEOGRAPH-FABB;Lo;0;L;8ACB;;;;N;;;;; +FABC;CJK COMPATIBILITY IDEOGRAPH-FABC;Lo;0;L;8B01;;;;N;;;;; +FABD;CJK COMPATIBILITY IDEOGRAPH-FABD;Lo;0;L;8AFE;;;;N;;;;; +FABE;CJK COMPATIBILITY IDEOGRAPH-FABE;Lo;0;L;8AED;;;;N;;;;; +FABF;CJK COMPATIBILITY IDEOGRAPH-FABF;Lo;0;L;8B39;;;;N;;;;; +FAC0;CJK COMPATIBILITY IDEOGRAPH-FAC0;Lo;0;L;8B8A;;;;N;;;;; +FAC1;CJK COMPATIBILITY IDEOGRAPH-FAC1;Lo;0;L;8D08;;;;N;;;;; +FAC2;CJK COMPATIBILITY IDEOGRAPH-FAC2;Lo;0;L;8F38;;;;N;;;;; +FAC3;CJK COMPATIBILITY IDEOGRAPH-FAC3;Lo;0;L;9072;;;;N;;;;; +FAC4;CJK COMPATIBILITY IDEOGRAPH-FAC4;Lo;0;L;9199;;;;N;;;;; +FAC5;CJK COMPATIBILITY IDEOGRAPH-FAC5;Lo;0;L;9276;;;;N;;;;; +FAC6;CJK COMPATIBILITY IDEOGRAPH-FAC6;Lo;0;L;967C;;;;N;;;;; +FAC7;CJK COMPATIBILITY IDEOGRAPH-FAC7;Lo;0;L;96E3;;;;N;;;;; +FAC8;CJK COMPATIBILITY IDEOGRAPH-FAC8;Lo;0;L;9756;;;;N;;;;; +FAC9;CJK COMPATIBILITY IDEOGRAPH-FAC9;Lo;0;L;97DB;;;;N;;;;; +FACA;CJK COMPATIBILITY IDEOGRAPH-FACA;Lo;0;L;97FF;;;;N;;;;; +FACB;CJK COMPATIBILITY IDEOGRAPH-FACB;Lo;0;L;980B;;;;N;;;;; +FACC;CJK COMPATIBILITY IDEOGRAPH-FACC;Lo;0;L;983B;;;;N;;;;; +FACD;CJK COMPATIBILITY IDEOGRAPH-FACD;Lo;0;L;9B12;;;;N;;;;; +FACE;CJK COMPATIBILITY IDEOGRAPH-FACE;Lo;0;L;9F9C;;;;N;;;;; +FACF;CJK COMPATIBILITY IDEOGRAPH-FACF;Lo;0;L;2284A;;;;N;;;;; +FAD0;CJK COMPATIBILITY IDEOGRAPH-FAD0;Lo;0;L;22844;;;;N;;;;; +FAD1;CJK COMPATIBILITY IDEOGRAPH-FAD1;Lo;0;L;233D5;;;;N;;;;; +FAD2;CJK COMPATIBILITY IDEOGRAPH-FAD2;Lo;0;L;3B9D;;;;N;;;;; +FAD3;CJK COMPATIBILITY IDEOGRAPH-FAD3;Lo;0;L;4018;;;;N;;;;; +FAD4;CJK COMPATIBILITY IDEOGRAPH-FAD4;Lo;0;L;4039;;;;N;;;;; +FAD5;CJK COMPATIBILITY IDEOGRAPH-FAD5;Lo;0;L;25249;;;;N;;;;; +FAD6;CJK COMPATIBILITY IDEOGRAPH-FAD6;Lo;0;L;25CD0;;;;N;;;;; +FAD7;CJK COMPATIBILITY IDEOGRAPH-FAD7;Lo;0;L;27ED3;;;;N;;;;; +FAD8;CJK COMPATIBILITY IDEOGRAPH-FAD8;Lo;0;L;9F43;;;;N;;;;; +FAD9;CJK COMPATIBILITY IDEOGRAPH-FAD9;Lo;0;L;9F8E;;;;N;;;;; +FB00;LATIN SMALL LIGATURE FF;Ll;0;L;<compat> 0066 0066;;;;N;;;;; +FB01;LATIN SMALL LIGATURE FI;Ll;0;L;<compat> 0066 0069;;;;N;;;;; +FB02;LATIN SMALL LIGATURE FL;Ll;0;L;<compat> 0066 006C;;;;N;;;;; +FB03;LATIN SMALL LIGATURE FFI;Ll;0;L;<compat> 0066 0066 0069;;;;N;;;;; +FB04;LATIN SMALL LIGATURE FFL;Ll;0;L;<compat> 0066 0066 006C;;;;N;;;;; +FB05;LATIN SMALL LIGATURE LONG S T;Ll;0;L;<compat> 017F 0074;;;;N;;;;; +FB06;LATIN SMALL LIGATURE ST;Ll;0;L;<compat> 0073 0074;;;;N;;;;; +FB13;ARMENIAN SMALL LIGATURE MEN NOW;Ll;0;L;<compat> 0574 0576;;;;N;;;;; +FB14;ARMENIAN SMALL LIGATURE MEN ECH;Ll;0;L;<compat> 0574 0565;;;;N;;;;; +FB15;ARMENIAN SMALL LIGATURE MEN INI;Ll;0;L;<compat> 0574 056B;;;;N;;;;; +FB16;ARMENIAN SMALL LIGATURE VEW NOW;Ll;0;L;<compat> 057E 0576;;;;N;;;;; +FB17;ARMENIAN SMALL LIGATURE MEN XEH;Ll;0;L;<compat> 0574 056D;;;;N;;;;; +FB1D;HEBREW LETTER YOD WITH HIRIQ;Lo;0;R;05D9 05B4;;;;N;;;;; +FB1E;HEBREW POINT JUDEO-SPANISH VARIKA;Mn;26;NSM;;;;;N;HEBREW POINT VARIKA;;;; +FB1F;HEBREW LIGATURE YIDDISH YOD YOD PATAH;Lo;0;R;05F2 05B7;;;;N;;;;; +FB20;HEBREW LETTER ALTERNATIVE AYIN;Lo;0;R;<font> 05E2;;;;N;;;;; +FB21;HEBREW LETTER WIDE ALEF;Lo;0;R;<font> 05D0;;;;N;;;;; +FB22;HEBREW LETTER WIDE DALET;Lo;0;R;<font> 05D3;;;;N;;;;; +FB23;HEBREW LETTER WIDE HE;Lo;0;R;<font> 05D4;;;;N;;;;; +FB24;HEBREW LETTER WIDE KAF;Lo;0;R;<font> 05DB;;;;N;;;;; +FB25;HEBREW LETTER WIDE LAMED;Lo;0;R;<font> 05DC;;;;N;;;;; +FB26;HEBREW LETTER WIDE FINAL MEM;Lo;0;R;<font> 05DD;;;;N;;;;; +FB27;HEBREW LETTER WIDE RESH;Lo;0;R;<font> 05E8;;;;N;;;;; +FB28;HEBREW LETTER WIDE TAV;Lo;0;R;<font> 05EA;;;;N;;;;; +FB29;HEBREW LETTER ALTERNATIVE PLUS SIGN;Sm;0;ES;<font> 002B;;;;N;;;;; +FB2A;HEBREW LETTER SHIN WITH SHIN DOT;Lo;0;R;05E9 05C1;;;;N;;;;; +FB2B;HEBREW LETTER SHIN WITH SIN DOT;Lo;0;R;05E9 05C2;;;;N;;;;; +FB2C;HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT;Lo;0;R;FB49 05C1;;;;N;;;;; +FB2D;HEBREW LETTER SHIN WITH DAGESH AND SIN DOT;Lo;0;R;FB49 05C2;;;;N;;;;; +FB2E;HEBREW LETTER ALEF WITH PATAH;Lo;0;R;05D0 05B7;;;;N;;;;; +FB2F;HEBREW LETTER ALEF WITH QAMATS;Lo;0;R;05D0 05B8;;;;N;;;;; +FB30;HEBREW LETTER ALEF WITH MAPIQ;Lo;0;R;05D0 05BC;;;;N;;;;; +FB31;HEBREW LETTER BET WITH DAGESH;Lo;0;R;05D1 05BC;;;;N;;;;; +FB32;HEBREW LETTER GIMEL WITH DAGESH;Lo;0;R;05D2 05BC;;;;N;;;;; +FB33;HEBREW LETTER DALET WITH DAGESH;Lo;0;R;05D3 05BC;;;;N;;;;; +FB34;HEBREW LETTER HE WITH MAPIQ;Lo;0;R;05D4 05BC;;;;N;;;;; +FB35;HEBREW LETTER VAV WITH DAGESH;Lo;0;R;05D5 05BC;;;;N;;;;; +FB36;HEBREW LETTER ZAYIN WITH DAGESH;Lo;0;R;05D6 05BC;;;;N;;;;; +FB38;HEBREW LETTER TET WITH DAGESH;Lo;0;R;05D8 05BC;;;;N;;;;; +FB39;HEBREW LETTER YOD WITH DAGESH;Lo;0;R;05D9 05BC;;;;N;;;;; +FB3A;HEBREW LETTER FINAL KAF WITH DAGESH;Lo;0;R;05DA 05BC;;;;N;;;;; +FB3B;HEBREW LETTER KAF WITH DAGESH;Lo;0;R;05DB 05BC;;;;N;;;;; +FB3C;HEBREW LETTER LAMED WITH DAGESH;Lo;0;R;05DC 05BC;;;;N;;;;; +FB3E;HEBREW LETTER MEM WITH DAGESH;Lo;0;R;05DE 05BC;;;;N;;;;; +FB40;HEBREW LETTER NUN WITH DAGESH;Lo;0;R;05E0 05BC;;;;N;;;;; +FB41;HEBREW LETTER SAMEKH WITH DAGESH;Lo;0;R;05E1 05BC;;;;N;;;;; +FB43;HEBREW LETTER FINAL PE WITH DAGESH;Lo;0;R;05E3 05BC;;;;N;;;;; +FB44;HEBREW LETTER PE WITH DAGESH;Lo;0;R;05E4 05BC;;;;N;;;;; +FB46;HEBREW LETTER TSADI WITH DAGESH;Lo;0;R;05E6 05BC;;;;N;;;;; +FB47;HEBREW LETTER QOF WITH DAGESH;Lo;0;R;05E7 05BC;;;;N;;;;; +FB48;HEBREW LETTER RESH WITH DAGESH;Lo;0;R;05E8 05BC;;;;N;;;;; +FB49;HEBREW LETTER SHIN WITH DAGESH;Lo;0;R;05E9 05BC;;;;N;;;;; +FB4A;HEBREW LETTER TAV WITH DAGESH;Lo;0;R;05EA 05BC;;;;N;;;;; +FB4B;HEBREW LETTER VAV WITH HOLAM;Lo;0;R;05D5 05B9;;;;N;;;;; +FB4C;HEBREW LETTER BET WITH RAFE;Lo;0;R;05D1 05BF;;;;N;;;;; +FB4D;HEBREW LETTER KAF WITH RAFE;Lo;0;R;05DB 05BF;;;;N;;;;; +FB4E;HEBREW LETTER PE WITH RAFE;Lo;0;R;05E4 05BF;;;;N;;;;; +FB4F;HEBREW LIGATURE ALEF LAMED;Lo;0;R;<compat> 05D0 05DC;;;;N;;;;; +FB50;ARABIC LETTER ALEF WASLA ISOLATED FORM;Lo;0;AL;<isolated> 0671;;;;N;;;;; +FB51;ARABIC LETTER ALEF WASLA FINAL FORM;Lo;0;AL;<final> 0671;;;;N;;;;; +FB52;ARABIC LETTER BEEH ISOLATED FORM;Lo;0;AL;<isolated> 067B;;;;N;;;;; +FB53;ARABIC LETTER BEEH FINAL FORM;Lo;0;AL;<final> 067B;;;;N;;;;; +FB54;ARABIC LETTER BEEH INITIAL FORM;Lo;0;AL;<initial> 067B;;;;N;;;;; +FB55;ARABIC LETTER BEEH MEDIAL FORM;Lo;0;AL;<medial> 067B;;;;N;;;;; +FB56;ARABIC LETTER PEH ISOLATED FORM;Lo;0;AL;<isolated> 067E;;;;N;;;;; +FB57;ARABIC LETTER PEH FINAL FORM;Lo;0;AL;<final> 067E;;;;N;;;;; +FB58;ARABIC LETTER PEH INITIAL FORM;Lo;0;AL;<initial> 067E;;;;N;;;;; +FB59;ARABIC LETTER PEH MEDIAL FORM;Lo;0;AL;<medial> 067E;;;;N;;;;; +FB5A;ARABIC LETTER BEHEH ISOLATED FORM;Lo;0;AL;<isolated> 0680;;;;N;;;;; +FB5B;ARABIC LETTER BEHEH FINAL FORM;Lo;0;AL;<final> 0680;;;;N;;;;; +FB5C;ARABIC LETTER BEHEH INITIAL FORM;Lo;0;AL;<initial> 0680;;;;N;;;;; +FB5D;ARABIC LETTER BEHEH MEDIAL FORM;Lo;0;AL;<medial> 0680;;;;N;;;;; +FB5E;ARABIC LETTER TTEHEH ISOLATED FORM;Lo;0;AL;<isolated> 067A;;;;N;;;;; +FB5F;ARABIC LETTER TTEHEH FINAL FORM;Lo;0;AL;<final> 067A;;;;N;;;;; +FB60;ARABIC LETTER TTEHEH INITIAL FORM;Lo;0;AL;<initial> 067A;;;;N;;;;; +FB61;ARABIC LETTER TTEHEH MEDIAL FORM;Lo;0;AL;<medial> 067A;;;;N;;;;; +FB62;ARABIC LETTER TEHEH ISOLATED FORM;Lo;0;AL;<isolated> 067F;;;;N;;;;; +FB63;ARABIC LETTER TEHEH FINAL FORM;Lo;0;AL;<final> 067F;;;;N;;;;; +FB64;ARABIC LETTER TEHEH INITIAL FORM;Lo;0;AL;<initial> 067F;;;;N;;;;; +FB65;ARABIC LETTER TEHEH MEDIAL FORM;Lo;0;AL;<medial> 067F;;;;N;;;;; +FB66;ARABIC LETTER TTEH ISOLATED FORM;Lo;0;AL;<isolated> 0679;;;;N;;;;; +FB67;ARABIC LETTER TTEH FINAL FORM;Lo;0;AL;<final> 0679;;;;N;;;;; +FB68;ARABIC LETTER TTEH INITIAL FORM;Lo;0;AL;<initial> 0679;;;;N;;;;; +FB69;ARABIC LETTER TTEH MEDIAL FORM;Lo;0;AL;<medial> 0679;;;;N;;;;; +FB6A;ARABIC LETTER VEH ISOLATED FORM;Lo;0;AL;<isolated> 06A4;;;;N;;;;; +FB6B;ARABIC LETTER VEH FINAL FORM;Lo;0;AL;<final> 06A4;;;;N;;;;; +FB6C;ARABIC LETTER VEH INITIAL FORM;Lo;0;AL;<initial> 06A4;;;;N;;;;; +FB6D;ARABIC LETTER VEH MEDIAL FORM;Lo;0;AL;<medial> 06A4;;;;N;;;;; +FB6E;ARABIC LETTER PEHEH ISOLATED FORM;Lo;0;AL;<isolated> 06A6;;;;N;;;;; +FB6F;ARABIC LETTER PEHEH FINAL FORM;Lo;0;AL;<final> 06A6;;;;N;;;;; +FB70;ARABIC LETTER PEHEH INITIAL FORM;Lo;0;AL;<initial> 06A6;;;;N;;;;; +FB71;ARABIC LETTER PEHEH MEDIAL FORM;Lo;0;AL;<medial> 06A6;;;;N;;;;; +FB72;ARABIC LETTER DYEH ISOLATED FORM;Lo;0;AL;<isolated> 0684;;;;N;;;;; +FB73;ARABIC LETTER DYEH FINAL FORM;Lo;0;AL;<final> 0684;;;;N;;;;; +FB74;ARABIC LETTER DYEH INITIAL FORM;Lo;0;AL;<initial> 0684;;;;N;;;;; +FB75;ARABIC LETTER DYEH MEDIAL FORM;Lo;0;AL;<medial> 0684;;;;N;;;;; +FB76;ARABIC LETTER NYEH ISOLATED FORM;Lo;0;AL;<isolated> 0683;;;;N;;;;; +FB77;ARABIC LETTER NYEH FINAL FORM;Lo;0;AL;<final> 0683;;;;N;;;;; +FB78;ARABIC LETTER NYEH INITIAL FORM;Lo;0;AL;<initial> 0683;;;;N;;;;; +FB79;ARABIC LETTER NYEH MEDIAL FORM;Lo;0;AL;<medial> 0683;;;;N;;;;; +FB7A;ARABIC LETTER TCHEH ISOLATED FORM;Lo;0;AL;<isolated> 0686;;;;N;;;;; +FB7B;ARABIC LETTER TCHEH FINAL FORM;Lo;0;AL;<final> 0686;;;;N;;;;; +FB7C;ARABIC LETTER TCHEH INITIAL FORM;Lo;0;AL;<initial> 0686;;;;N;;;;; +FB7D;ARABIC LETTER TCHEH MEDIAL FORM;Lo;0;AL;<medial> 0686;;;;N;;;;; +FB7E;ARABIC LETTER TCHEHEH ISOLATED FORM;Lo;0;AL;<isolated> 0687;;;;N;;;;; +FB7F;ARABIC LETTER TCHEHEH FINAL FORM;Lo;0;AL;<final> 0687;;;;N;;;;; +FB80;ARABIC LETTER TCHEHEH INITIAL FORM;Lo;0;AL;<initial> 0687;;;;N;;;;; +FB81;ARABIC LETTER TCHEHEH MEDIAL FORM;Lo;0;AL;<medial> 0687;;;;N;;;;; +FB82;ARABIC LETTER DDAHAL ISOLATED FORM;Lo;0;AL;<isolated> 068D;;;;N;;;;; +FB83;ARABIC LETTER DDAHAL FINAL FORM;Lo;0;AL;<final> 068D;;;;N;;;;; +FB84;ARABIC LETTER DAHAL ISOLATED FORM;Lo;0;AL;<isolated> 068C;;;;N;;;;; +FB85;ARABIC LETTER DAHAL FINAL FORM;Lo;0;AL;<final> 068C;;;;N;;;;; +FB86;ARABIC LETTER DUL ISOLATED FORM;Lo;0;AL;<isolated> 068E;;;;N;;;;; +FB87;ARABIC LETTER DUL FINAL FORM;Lo;0;AL;<final> 068E;;;;N;;;;; +FB88;ARABIC LETTER DDAL ISOLATED FORM;Lo;0;AL;<isolated> 0688;;;;N;;;;; +FB89;ARABIC LETTER DDAL FINAL FORM;Lo;0;AL;<final> 0688;;;;N;;;;; +FB8A;ARABIC LETTER JEH ISOLATED FORM;Lo;0;AL;<isolated> 0698;;;;N;;;;; +FB8B;ARABIC LETTER JEH FINAL FORM;Lo;0;AL;<final> 0698;;;;N;;;;; +FB8C;ARABIC LETTER RREH ISOLATED FORM;Lo;0;AL;<isolated> 0691;;;;N;;;;; +FB8D;ARABIC LETTER RREH FINAL FORM;Lo;0;AL;<final> 0691;;;;N;;;;; +FB8E;ARABIC LETTER KEHEH ISOLATED FORM;Lo;0;AL;<isolated> 06A9;;;;N;;;;; +FB8F;ARABIC LETTER KEHEH FINAL FORM;Lo;0;AL;<final> 06A9;;;;N;;;;; +FB90;ARABIC LETTER KEHEH INITIAL FORM;Lo;0;AL;<initial> 06A9;;;;N;;;;; +FB91;ARABIC LETTER KEHEH MEDIAL FORM;Lo;0;AL;<medial> 06A9;;;;N;;;;; +FB92;ARABIC LETTER GAF ISOLATED FORM;Lo;0;AL;<isolated> 06AF;;;;N;;;;; +FB93;ARABIC LETTER GAF FINAL FORM;Lo;0;AL;<final> 06AF;;;;N;;;;; +FB94;ARABIC LETTER GAF INITIAL FORM;Lo;0;AL;<initial> 06AF;;;;N;;;;; +FB95;ARABIC LETTER GAF MEDIAL FORM;Lo;0;AL;<medial> 06AF;;;;N;;;;; +FB96;ARABIC LETTER GUEH ISOLATED FORM;Lo;0;AL;<isolated> 06B3;;;;N;;;;; +FB97;ARABIC LETTER GUEH FINAL FORM;Lo;0;AL;<final> 06B3;;;;N;;;;; +FB98;ARABIC LETTER GUEH INITIAL FORM;Lo;0;AL;<initial> 06B3;;;;N;;;;; +FB99;ARABIC LETTER GUEH MEDIAL FORM;Lo;0;AL;<medial> 06B3;;;;N;;;;; +FB9A;ARABIC LETTER NGOEH ISOLATED FORM;Lo;0;AL;<isolated> 06B1;;;;N;;;;; +FB9B;ARABIC LETTER NGOEH FINAL FORM;Lo;0;AL;<final> 06B1;;;;N;;;;; +FB9C;ARABIC LETTER NGOEH INITIAL FORM;Lo;0;AL;<initial> 06B1;;;;N;;;;; +FB9D;ARABIC LETTER NGOEH MEDIAL FORM;Lo;0;AL;<medial> 06B1;;;;N;;;;; +FB9E;ARABIC LETTER NOON GHUNNA ISOLATED FORM;Lo;0;AL;<isolated> 06BA;;;;N;;;;; +FB9F;ARABIC LETTER NOON GHUNNA FINAL FORM;Lo;0;AL;<final> 06BA;;;;N;;;;; +FBA0;ARABIC LETTER RNOON ISOLATED FORM;Lo;0;AL;<isolated> 06BB;;;;N;;;;; +FBA1;ARABIC LETTER RNOON FINAL FORM;Lo;0;AL;<final> 06BB;;;;N;;;;; +FBA2;ARABIC LETTER RNOON INITIAL FORM;Lo;0;AL;<initial> 06BB;;;;N;;;;; +FBA3;ARABIC LETTER RNOON MEDIAL FORM;Lo;0;AL;<medial> 06BB;;;;N;;;;; +FBA4;ARABIC LETTER HEH WITH YEH ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 06C0;;;;N;;;;; +FBA5;ARABIC LETTER HEH WITH YEH ABOVE FINAL FORM;Lo;0;AL;<final> 06C0;;;;N;;;;; +FBA6;ARABIC LETTER HEH GOAL ISOLATED FORM;Lo;0;AL;<isolated> 06C1;;;;N;;;;; +FBA7;ARABIC LETTER HEH GOAL FINAL FORM;Lo;0;AL;<final> 06C1;;;;N;;;;; +FBA8;ARABIC LETTER HEH GOAL INITIAL FORM;Lo;0;AL;<initial> 06C1;;;;N;;;;; +FBA9;ARABIC LETTER HEH GOAL MEDIAL FORM;Lo;0;AL;<medial> 06C1;;;;N;;;;; +FBAA;ARABIC LETTER HEH DOACHASHMEE ISOLATED FORM;Lo;0;AL;<isolated> 06BE;;;;N;;;;; +FBAB;ARABIC LETTER HEH DOACHASHMEE FINAL FORM;Lo;0;AL;<final> 06BE;;;;N;;;;; +FBAC;ARABIC LETTER HEH DOACHASHMEE INITIAL FORM;Lo;0;AL;<initial> 06BE;;;;N;;;;; +FBAD;ARABIC LETTER HEH DOACHASHMEE MEDIAL FORM;Lo;0;AL;<medial> 06BE;;;;N;;;;; +FBAE;ARABIC LETTER YEH BARREE ISOLATED FORM;Lo;0;AL;<isolated> 06D2;;;;N;;;;; +FBAF;ARABIC LETTER YEH BARREE FINAL FORM;Lo;0;AL;<final> 06D2;;;;N;;;;; +FBB0;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 06D3;;;;N;;;;; +FBB1;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 06D3;;;;N;;;;; +FBB2;ARABIC SYMBOL DOT ABOVE;Sk;0;AL;;;;;N;;;;; +FBB3;ARABIC SYMBOL DOT BELOW;Sk;0;AL;;;;;N;;;;; +FBB4;ARABIC SYMBOL TWO DOTS ABOVE;Sk;0;AL;;;;;N;;;;; +FBB5;ARABIC SYMBOL TWO DOTS BELOW;Sk;0;AL;;;;;N;;;;; +FBB6;ARABIC SYMBOL THREE DOTS ABOVE;Sk;0;AL;;;;;N;;;;; +FBB7;ARABIC SYMBOL THREE DOTS BELOW;Sk;0;AL;;;;;N;;;;; +FBB8;ARABIC SYMBOL THREE DOTS POINTING DOWNWARDS ABOVE;Sk;0;AL;;;;;N;;;;; +FBB9;ARABIC SYMBOL THREE DOTS POINTING DOWNWARDS BELOW;Sk;0;AL;;;;;N;;;;; +FBBA;ARABIC SYMBOL FOUR DOTS ABOVE;Sk;0;AL;;;;;N;;;;; +FBBB;ARABIC SYMBOL FOUR DOTS BELOW;Sk;0;AL;;;;;N;;;;; +FBBC;ARABIC SYMBOL DOUBLE VERTICAL BAR BELOW;Sk;0;AL;;;;;N;;;;; +FBBD;ARABIC SYMBOL TWO DOTS VERTICALLY ABOVE;Sk;0;AL;;;;;N;;;;; +FBBE;ARABIC SYMBOL TWO DOTS VERTICALLY BELOW;Sk;0;AL;;;;;N;;;;; +FBBF;ARABIC SYMBOL RING;Sk;0;AL;;;;;N;;;;; +FBC0;ARABIC SYMBOL SMALL TAH ABOVE;Sk;0;AL;;;;;N;;;;; +FBC1;ARABIC SYMBOL SMALL TAH BELOW;Sk;0;AL;;;;;N;;;;; +FBD3;ARABIC LETTER NG ISOLATED FORM;Lo;0;AL;<isolated> 06AD;;;;N;;;;; +FBD4;ARABIC LETTER NG FINAL FORM;Lo;0;AL;<final> 06AD;;;;N;;;;; +FBD5;ARABIC LETTER NG INITIAL FORM;Lo;0;AL;<initial> 06AD;;;;N;;;;; +FBD6;ARABIC LETTER NG MEDIAL FORM;Lo;0;AL;<medial> 06AD;;;;N;;;;; +FBD7;ARABIC LETTER U ISOLATED FORM;Lo;0;AL;<isolated> 06C7;;;;N;;;;; +FBD8;ARABIC LETTER U FINAL FORM;Lo;0;AL;<final> 06C7;;;;N;;;;; +FBD9;ARABIC LETTER OE ISOLATED FORM;Lo;0;AL;<isolated> 06C6;;;;N;;;;; +FBDA;ARABIC LETTER OE FINAL FORM;Lo;0;AL;<final> 06C6;;;;N;;;;; +FBDB;ARABIC LETTER YU ISOLATED FORM;Lo;0;AL;<isolated> 06C8;;;;N;;;;; +FBDC;ARABIC LETTER YU FINAL FORM;Lo;0;AL;<final> 06C8;;;;N;;;;; +FBDD;ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0677;;;;N;;;;; +FBDE;ARABIC LETTER VE ISOLATED FORM;Lo;0;AL;<isolated> 06CB;;;;N;;;;; +FBDF;ARABIC LETTER VE FINAL FORM;Lo;0;AL;<final> 06CB;;;;N;;;;; +FBE0;ARABIC LETTER KIRGHIZ OE ISOLATED FORM;Lo;0;AL;<isolated> 06C5;;;;N;;;;; +FBE1;ARABIC LETTER KIRGHIZ OE FINAL FORM;Lo;0;AL;<final> 06C5;;;;N;;;;; +FBE2;ARABIC LETTER KIRGHIZ YU ISOLATED FORM;Lo;0;AL;<isolated> 06C9;;;;N;;;;; +FBE3;ARABIC LETTER KIRGHIZ YU FINAL FORM;Lo;0;AL;<final> 06C9;;;;N;;;;; +FBE4;ARABIC LETTER E ISOLATED FORM;Lo;0;AL;<isolated> 06D0;;;;N;;;;; +FBE5;ARABIC LETTER E FINAL FORM;Lo;0;AL;<final> 06D0;;;;N;;;;; +FBE6;ARABIC LETTER E INITIAL FORM;Lo;0;AL;<initial> 06D0;;;;N;;;;; +FBE7;ARABIC LETTER E MEDIAL FORM;Lo;0;AL;<medial> 06D0;;;;N;;;;; +FBE8;ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA INITIAL FORM;Lo;0;AL;<initial> 0649;;;;N;;;;; +FBE9;ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA MEDIAL FORM;Lo;0;AL;<medial> 0649;;;;N;;;;; +FBEA;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0626 0627;;;;N;;;;; +FBEB;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF FINAL FORM;Lo;0;AL;<final> 0626 0627;;;;N;;;;; +FBEC;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE ISOLATED FORM;Lo;0;AL;<isolated> 0626 06D5;;;;N;;;;; +FBED;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE FINAL FORM;Lo;0;AL;<final> 0626 06D5;;;;N;;;;; +FBEE;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW ISOLATED FORM;Lo;0;AL;<isolated> 0626 0648;;;;N;;;;; +FBEF;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW FINAL FORM;Lo;0;AL;<final> 0626 0648;;;;N;;;;; +FBF0;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C7;;;;N;;;;; +FBF1;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U FINAL FORM;Lo;0;AL;<final> 0626 06C7;;;;N;;;;; +FBF2;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C6;;;;N;;;;; +FBF3;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE FINAL FORM;Lo;0;AL;<final> 0626 06C6;;;;N;;;;; +FBF4;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C8;;;;N;;;;; +FBF5;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU FINAL FORM;Lo;0;AL;<final> 0626 06C8;;;;N;;;;; +FBF6;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E ISOLATED FORM;Lo;0;AL;<isolated> 0626 06D0;;;;N;;;;; +FBF7;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E FINAL FORM;Lo;0;AL;<final> 0626 06D0;;;;N;;;;; +FBF8;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E INITIAL FORM;Lo;0;AL;<initial> 0626 06D0;;;;N;;;;; +FBF9;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0626 0649;;;;N;;;;; +FBFA;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0626 0649;;;;N;;;;; +FBFB;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM;Lo;0;AL;<initial> 0626 0649;;;;N;;;;; +FBFC;ARABIC LETTER FARSI YEH ISOLATED FORM;Lo;0;AL;<isolated> 06CC;;;;N;;;;; +FBFD;ARABIC LETTER FARSI YEH FINAL FORM;Lo;0;AL;<final> 06CC;;;;N;;;;; +FBFE;ARABIC LETTER FARSI YEH INITIAL FORM;Lo;0;AL;<initial> 06CC;;;;N;;;;; +FBFF;ARABIC LETTER FARSI YEH MEDIAL FORM;Lo;0;AL;<medial> 06CC;;;;N;;;;; +FC00;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0626 062C;;;;N;;;;; +FC01;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0626 062D;;;;N;;;;; +FC02;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0626 0645;;;;N;;;;; +FC03;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0626 0649;;;;N;;;;; +FC04;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0626 064A;;;;N;;;;; +FC05;ARABIC LIGATURE BEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0628 062C;;;;N;;;;; +FC06;ARABIC LIGATURE BEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0628 062D;;;;N;;;;; +FC07;ARABIC LIGATURE BEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0628 062E;;;;N;;;;; +FC08;ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0628 0645;;;;N;;;;; +FC09;ARABIC LIGATURE BEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0628 0649;;;;N;;;;; +FC0A;ARABIC LIGATURE BEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0628 064A;;;;N;;;;; +FC0B;ARABIC LIGATURE TEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062A 062C;;;;N;;;;; +FC0C;ARABIC LIGATURE TEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062A 062D;;;;N;;;;; +FC0D;ARABIC LIGATURE TEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 062A 062E;;;;N;;;;; +FC0E;ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062A 0645;;;;N;;;;; +FC0F;ARABIC LIGATURE TEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062A 0649;;;;N;;;;; +FC10;ARABIC LIGATURE TEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062A 064A;;;;N;;;;; +FC11;ARABIC LIGATURE THEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062B 062C;;;;N;;;;; +FC12;ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062B 0645;;;;N;;;;; +FC13;ARABIC LIGATURE THEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062B 0649;;;;N;;;;; +FC14;ARABIC LIGATURE THEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062B 064A;;;;N;;;;; +FC15;ARABIC LIGATURE JEEM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062C 062D;;;;N;;;;; +FC16;ARABIC LIGATURE JEEM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062C 0645;;;;N;;;;; +FC17;ARABIC LIGATURE HAH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062D 062C;;;;N;;;;; +FC18;ARABIC LIGATURE HAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062D 0645;;;;N;;;;; +FC19;ARABIC LIGATURE KHAH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062E 062C;;;;N;;;;; +FC1A;ARABIC LIGATURE KHAH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062E 062D;;;;N;;;;; +FC1B;ARABIC LIGATURE KHAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062E 0645;;;;N;;;;; +FC1C;ARABIC LIGATURE SEEN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0633 062C;;;;N;;;;; +FC1D;ARABIC LIGATURE SEEN WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0633 062D;;;;N;;;;; +FC1E;ARABIC LIGATURE SEEN WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0633 062E;;;;N;;;;; +FC1F;ARABIC LIGATURE SEEN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0633 0645;;;;N;;;;; +FC20;ARABIC LIGATURE SAD WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0635 062D;;;;N;;;;; +FC21;ARABIC LIGATURE SAD WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0635 0645;;;;N;;;;; +FC22;ARABIC LIGATURE DAD WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0636 062C;;;;N;;;;; +FC23;ARABIC LIGATURE DAD WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0636 062D;;;;N;;;;; +FC24;ARABIC LIGATURE DAD WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0636 062E;;;;N;;;;; +FC25;ARABIC LIGATURE DAD WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0636 0645;;;;N;;;;; +FC26;ARABIC LIGATURE TAH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0637 062D;;;;N;;;;; +FC27;ARABIC LIGATURE TAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0637 0645;;;;N;;;;; +FC28;ARABIC LIGATURE ZAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0638 0645;;;;N;;;;; +FC29;ARABIC LIGATURE AIN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0639 062C;;;;N;;;;; +FC2A;ARABIC LIGATURE AIN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0639 0645;;;;N;;;;; +FC2B;ARABIC LIGATURE GHAIN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 063A 062C;;;;N;;;;; +FC2C;ARABIC LIGATURE GHAIN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 063A 0645;;;;N;;;;; +FC2D;ARABIC LIGATURE FEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0641 062C;;;;N;;;;; +FC2E;ARABIC LIGATURE FEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0641 062D;;;;N;;;;; +FC2F;ARABIC LIGATURE FEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0641 062E;;;;N;;;;; +FC30;ARABIC LIGATURE FEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0641 0645;;;;N;;;;; +FC31;ARABIC LIGATURE FEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0641 0649;;;;N;;;;; +FC32;ARABIC LIGATURE FEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0641 064A;;;;N;;;;; +FC33;ARABIC LIGATURE QAF WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0642 062D;;;;N;;;;; +FC34;ARABIC LIGATURE QAF WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0642 0645;;;;N;;;;; +FC35;ARABIC LIGATURE QAF WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0642 0649;;;;N;;;;; +FC36;ARABIC LIGATURE QAF WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0642 064A;;;;N;;;;; +FC37;ARABIC LIGATURE KAF WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0643 0627;;;;N;;;;; +FC38;ARABIC LIGATURE KAF WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0643 062C;;;;N;;;;; +FC39;ARABIC LIGATURE KAF WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0643 062D;;;;N;;;;; +FC3A;ARABIC LIGATURE KAF WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0643 062E;;;;N;;;;; +FC3B;ARABIC LIGATURE KAF WITH LAM ISOLATED FORM;Lo;0;AL;<isolated> 0643 0644;;;;N;;;;; +FC3C;ARABIC LIGATURE KAF WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0643 0645;;;;N;;;;; +FC3D;ARABIC LIGATURE KAF WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0643 0649;;;;N;;;;; +FC3E;ARABIC LIGATURE KAF WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0643 064A;;;;N;;;;; +FC3F;ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0644 062C;;;;N;;;;; +FC40;ARABIC LIGATURE LAM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0644 062D;;;;N;;;;; +FC41;ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0644 062E;;;;N;;;;; +FC42;ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0644 0645;;;;N;;;;; +FC43;ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0644 0649;;;;N;;;;; +FC44;ARABIC LIGATURE LAM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0644 064A;;;;N;;;;; +FC45;ARABIC LIGATURE MEEM WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645 062C;;;;N;;;;; +FC46;ARABIC LIGATURE MEEM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0645 062D;;;;N;;;;; +FC47;ARABIC LIGATURE MEEM WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0645 062E;;;;N;;;;; +FC48;ARABIC LIGATURE MEEM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645 0645;;;;N;;;;; +FC49;ARABIC LIGATURE MEEM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0645 0649;;;;N;;;;; +FC4A;ARABIC LIGATURE MEEM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0645 064A;;;;N;;;;; +FC4B;ARABIC LIGATURE NOON WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0646 062C;;;;N;;;;; +FC4C;ARABIC LIGATURE NOON WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0646 062D;;;;N;;;;; +FC4D;ARABIC LIGATURE NOON WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0646 062E;;;;N;;;;; +FC4E;ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0646 0645;;;;N;;;;; +FC4F;ARABIC LIGATURE NOON WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0646 0649;;;;N;;;;; +FC50;ARABIC LIGATURE NOON WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0646 064A;;;;N;;;;; +FC51;ARABIC LIGATURE HEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0647 062C;;;;N;;;;; +FC52;ARABIC LIGATURE HEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0647 0645;;;;N;;;;; +FC53;ARABIC LIGATURE HEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0647 0649;;;;N;;;;; +FC54;ARABIC LIGATURE HEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0647 064A;;;;N;;;;; +FC55;ARABIC LIGATURE YEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 064A 062C;;;;N;;;;; +FC56;ARABIC LIGATURE YEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 064A 062D;;;;N;;;;; +FC57;ARABIC LIGATURE YEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 064A 062E;;;;N;;;;; +FC58;ARABIC LIGATURE YEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 064A 0645;;;;N;;;;; +FC59;ARABIC LIGATURE YEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 064A 0649;;;;N;;;;; +FC5A;ARABIC LIGATURE YEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 064A 064A;;;;N;;;;; +FC5B;ARABIC LIGATURE THAL WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0630 0670;;;;N;;;;; +FC5C;ARABIC LIGATURE REH WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0631 0670;;;;N;;;;; +FC5D;ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0649 0670;;;;N;;;;; +FC5E;ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064C 0651;;;;N;;;;; +FC5F;ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064D 0651;;;;N;;;;; +FC60;ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064E 0651;;;;N;;;;; +FC61;ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064F 0651;;;;N;;;;; +FC62;ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0650 0651;;;;N;;;;; +FC63;ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0020 0651 0670;;;;N;;;;; +FC64;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH REH FINAL FORM;Lo;0;AL;<final> 0626 0631;;;;N;;;;; +FC65;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0626 0632;;;;N;;;;; +FC66;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM FINAL FORM;Lo;0;AL;<final> 0626 0645;;;;N;;;;; +FC67;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH NOON FINAL FORM;Lo;0;AL;<final> 0626 0646;;;;N;;;;; +FC68;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0626 0649;;;;N;;;;; +FC69;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH FINAL FORM;Lo;0;AL;<final> 0626 064A;;;;N;;;;; +FC6A;ARABIC LIGATURE BEH WITH REH FINAL FORM;Lo;0;AL;<final> 0628 0631;;;;N;;;;; +FC6B;ARABIC LIGATURE BEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0628 0632;;;;N;;;;; +FC6C;ARABIC LIGATURE BEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0628 0645;;;;N;;;;; +FC6D;ARABIC LIGATURE BEH WITH NOON FINAL FORM;Lo;0;AL;<final> 0628 0646;;;;N;;;;; +FC6E;ARABIC LIGATURE BEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0628 0649;;;;N;;;;; +FC6F;ARABIC LIGATURE BEH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 064A;;;;N;;;;; +FC70;ARABIC LIGATURE TEH WITH REH FINAL FORM;Lo;0;AL;<final> 062A 0631;;;;N;;;;; +FC71;ARABIC LIGATURE TEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 062A 0632;;;;N;;;;; +FC72;ARABIC LIGATURE TEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 062A 0645;;;;N;;;;; +FC73;ARABIC LIGATURE TEH WITH NOON FINAL FORM;Lo;0;AL;<final> 062A 0646;;;;N;;;;; +FC74;ARABIC LIGATURE TEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 0649;;;;N;;;;; +FC75;ARABIC LIGATURE TEH WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 064A;;;;N;;;;; +FC76;ARABIC LIGATURE THEH WITH REH FINAL FORM;Lo;0;AL;<final> 062B 0631;;;;N;;;;; +FC77;ARABIC LIGATURE THEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 062B 0632;;;;N;;;;; +FC78;ARABIC LIGATURE THEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 062B 0645;;;;N;;;;; +FC79;ARABIC LIGATURE THEH WITH NOON FINAL FORM;Lo;0;AL;<final> 062B 0646;;;;N;;;;; +FC7A;ARABIC LIGATURE THEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062B 0649;;;;N;;;;; +FC7B;ARABIC LIGATURE THEH WITH YEH FINAL FORM;Lo;0;AL;<final> 062B 064A;;;;N;;;;; +FC7C;ARABIC LIGATURE FEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0641 0649;;;;N;;;;; +FC7D;ARABIC LIGATURE FEH WITH YEH FINAL FORM;Lo;0;AL;<final> 0641 064A;;;;N;;;;; +FC7E;ARABIC LIGATURE QAF WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0642 0649;;;;N;;;;; +FC7F;ARABIC LIGATURE QAF WITH YEH FINAL FORM;Lo;0;AL;<final> 0642 064A;;;;N;;;;; +FC80;ARABIC LIGATURE KAF WITH ALEF FINAL FORM;Lo;0;AL;<final> 0643 0627;;;;N;;;;; +FC81;ARABIC LIGATURE KAF WITH LAM FINAL FORM;Lo;0;AL;<final> 0643 0644;;;;N;;;;; +FC82;ARABIC LIGATURE KAF WITH MEEM FINAL FORM;Lo;0;AL;<final> 0643 0645;;;;N;;;;; +FC83;ARABIC LIGATURE KAF WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0643 0649;;;;N;;;;; +FC84;ARABIC LIGATURE KAF WITH YEH FINAL FORM;Lo;0;AL;<final> 0643 064A;;;;N;;;;; +FC85;ARABIC LIGATURE LAM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 0645;;;;N;;;;; +FC86;ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0644 0649;;;;N;;;;; +FC87;ARABIC LIGATURE LAM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 064A;;;;N;;;;; +FC88;ARABIC LIGATURE MEEM WITH ALEF FINAL FORM;Lo;0;AL;<final> 0645 0627;;;;N;;;;; +FC89;ARABIC LIGATURE MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0645 0645;;;;N;;;;; +FC8A;ARABIC LIGATURE NOON WITH REH FINAL FORM;Lo;0;AL;<final> 0646 0631;;;;N;;;;; +FC8B;ARABIC LIGATURE NOON WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0646 0632;;;;N;;;;; +FC8C;ARABIC LIGATURE NOON WITH MEEM FINAL FORM;Lo;0;AL;<final> 0646 0645;;;;N;;;;; +FC8D;ARABIC LIGATURE NOON WITH NOON FINAL FORM;Lo;0;AL;<final> 0646 0646;;;;N;;;;; +FC8E;ARABIC LIGATURE NOON WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 0649;;;;N;;;;; +FC8F;ARABIC LIGATURE NOON WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 064A;;;;N;;;;; +FC90;ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF FINAL FORM;Lo;0;AL;<final> 0649 0670;;;;N;;;;; +FC91;ARABIC LIGATURE YEH WITH REH FINAL FORM;Lo;0;AL;<final> 064A 0631;;;;N;;;;; +FC92;ARABIC LIGATURE YEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 064A 0632;;;;N;;;;; +FC93;ARABIC LIGATURE YEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 064A 0645;;;;N;;;;; +FC94;ARABIC LIGATURE YEH WITH NOON FINAL FORM;Lo;0;AL;<final> 064A 0646;;;;N;;;;; +FC95;ARABIC LIGATURE YEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 064A 0649;;;;N;;;;; +FC96;ARABIC LIGATURE YEH WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 064A;;;;N;;;;; +FC97;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0626 062C;;;;N;;;;; +FC98;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0626 062D;;;;N;;;;; +FC99;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0626 062E;;;;N;;;;; +FC9A;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0626 0645;;;;N;;;;; +FC9B;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0626 0647;;;;N;;;;; +FC9C;ARABIC LIGATURE BEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0628 062C;;;;N;;;;; +FC9D;ARABIC LIGATURE BEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0628 062D;;;;N;;;;; +FC9E;ARABIC LIGATURE BEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0628 062E;;;;N;;;;; +FC9F;ARABIC LIGATURE BEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0628 0645;;;;N;;;;; +FCA0;ARABIC LIGATURE BEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0628 0647;;;;N;;;;; +FCA1;ARABIC LIGATURE TEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062C;;;;N;;;;; +FCA2;ARABIC LIGATURE TEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062A 062D;;;;N;;;;; +FCA3;ARABIC LIGATURE TEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 062A 062E;;;;N;;;;; +FCA4;ARABIC LIGATURE TEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 0645;;;;N;;;;; +FCA5;ARABIC LIGATURE TEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 062A 0647;;;;N;;;;; +FCA6;ARABIC LIGATURE THEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062B 0645;;;;N;;;;; +FCA7;ARABIC LIGATURE JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062C 062D;;;;N;;;;; +FCA8;ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062C 0645;;;;N;;;;; +FCA9;ARABIC LIGATURE HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062D 062C;;;;N;;;;; +FCAA;ARABIC LIGATURE HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062D 0645;;;;N;;;;; +FCAB;ARABIC LIGATURE KHAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062E 062C;;;;N;;;;; +FCAC;ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062E 0645;;;;N;;;;; +FCAD;ARABIC LIGATURE SEEN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 062C;;;;N;;;;; +FCAE;ARABIC LIGATURE SEEN WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 062D;;;;N;;;;; +FCAF;ARABIC LIGATURE SEEN WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0633 062E;;;;N;;;;; +FCB0;ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645;;;;N;;;;; +FCB1;ARABIC LIGATURE SAD WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0635 062D;;;;N;;;;; +FCB2;ARABIC LIGATURE SAD WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0635 062E;;;;N;;;;; +FCB3;ARABIC LIGATURE SAD WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0635 0645;;;;N;;;;; +FCB4;ARABIC LIGATURE DAD WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0636 062C;;;;N;;;;; +FCB5;ARABIC LIGATURE DAD WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0636 062D;;;;N;;;;; +FCB6;ARABIC LIGATURE DAD WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0636 062E;;;;N;;;;; +FCB7;ARABIC LIGATURE DAD WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0636 0645;;;;N;;;;; +FCB8;ARABIC LIGATURE TAH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0637 062D;;;;N;;;;; +FCB9;ARABIC LIGATURE ZAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0638 0645;;;;N;;;;; +FCBA;ARABIC LIGATURE AIN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0639 062C;;;;N;;;;; +FCBB;ARABIC LIGATURE AIN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 0645;;;;N;;;;; +FCBC;ARABIC LIGATURE GHAIN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 063A 062C;;;;N;;;;; +FCBD;ARABIC LIGATURE GHAIN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 063A 0645;;;;N;;;;; +FCBE;ARABIC LIGATURE FEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0641 062C;;;;N;;;;; +FCBF;ARABIC LIGATURE FEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0641 062D;;;;N;;;;; +FCC0;ARABIC LIGATURE FEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0641 062E;;;;N;;;;; +FCC1;ARABIC LIGATURE FEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0641 0645;;;;N;;;;; +FCC2;ARABIC LIGATURE QAF WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0642 062D;;;;N;;;;; +FCC3;ARABIC LIGATURE QAF WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0642 0645;;;;N;;;;; +FCC4;ARABIC LIGATURE KAF WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0643 062C;;;;N;;;;; +FCC5;ARABIC LIGATURE KAF WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0643 062D;;;;N;;;;; +FCC6;ARABIC LIGATURE KAF WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0643 062E;;;;N;;;;; +FCC7;ARABIC LIGATURE KAF WITH LAM INITIAL FORM;Lo;0;AL;<initial> 0643 0644;;;;N;;;;; +FCC8;ARABIC LIGATURE KAF WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0643 0645;;;;N;;;;; +FCC9;ARABIC LIGATURE LAM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C;;;;N;;;;; +FCCA;ARABIC LIGATURE LAM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0644 062D;;;;N;;;;; +FCCB;ARABIC LIGATURE LAM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0644 062E;;;;N;;;;; +FCCC;ARABIC LIGATURE LAM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 0645;;;;N;;;;; +FCCD;ARABIC LIGATURE LAM WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0644 0647;;;;N;;;;; +FCCE;ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062C;;;;N;;;;; +FCCF;ARABIC LIGATURE MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0645 062D;;;;N;;;;; +FCD0;ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0645 062E;;;;N;;;;; +FCD1;ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 0645;;;;N;;;;; +FCD2;ARABIC LIGATURE NOON WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062C;;;;N;;;;; +FCD3;ARABIC LIGATURE NOON WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0646 062D;;;;N;;;;; +FCD4;ARABIC LIGATURE NOON WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0646 062E;;;;N;;;;; +FCD5;ARABIC LIGATURE NOON WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 0645;;;;N;;;;; +FCD6;ARABIC LIGATURE NOON WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0646 0647;;;;N;;;;; +FCD7;ARABIC LIGATURE HEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0647 062C;;;;N;;;;; +FCD8;ARABIC LIGATURE HEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645;;;;N;;;;; +FCD9;ARABIC LIGATURE HEH WITH SUPERSCRIPT ALEF INITIAL FORM;Lo;0;AL;<initial> 0647 0670;;;;N;;;;; +FCDA;ARABIC LIGATURE YEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 064A 062C;;;;N;;;;; +FCDB;ARABIC LIGATURE YEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 064A 062D;;;;N;;;;; +FCDC;ARABIC LIGATURE YEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 064A 062E;;;;N;;;;; +FCDD;ARABIC LIGATURE YEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 064A 0645;;;;N;;;;; +FCDE;ARABIC LIGATURE YEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 064A 0647;;;;N;;;;; +FCDF;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0626 0645;;;;N;;;;; +FCE0;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0626 0647;;;;N;;;;; +FCE1;ARABIC LIGATURE BEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0628 0645;;;;N;;;;; +FCE2;ARABIC LIGATURE BEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0628 0647;;;;N;;;;; +FCE3;ARABIC LIGATURE TEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 062A 0645;;;;N;;;;; +FCE4;ARABIC LIGATURE TEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 062A 0647;;;;N;;;;; +FCE5;ARABIC LIGATURE THEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 062B 0645;;;;N;;;;; +FCE6;ARABIC LIGATURE THEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 062B 0647;;;;N;;;;; +FCE7;ARABIC LIGATURE SEEN WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0633 0645;;;;N;;;;; +FCE8;ARABIC LIGATURE SEEN WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0633 0647;;;;N;;;;; +FCE9;ARABIC LIGATURE SHEEN WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0634 0645;;;;N;;;;; +FCEA;ARABIC LIGATURE SHEEN WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0634 0647;;;;N;;;;; +FCEB;ARABIC LIGATURE KAF WITH LAM MEDIAL FORM;Lo;0;AL;<medial> 0643 0644;;;;N;;;;; +FCEC;ARABIC LIGATURE KAF WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0643 0645;;;;N;;;;; +FCED;ARABIC LIGATURE LAM WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0644 0645;;;;N;;;;; +FCEE;ARABIC LIGATURE NOON WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0646 0645;;;;N;;;;; +FCEF;ARABIC LIGATURE NOON WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0646 0647;;;;N;;;;; +FCF0;ARABIC LIGATURE YEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 064A 0645;;;;N;;;;; +FCF1;ARABIC LIGATURE YEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 064A 0647;;;;N;;;;; +FCF2;ARABIC LIGATURE SHADDA WITH FATHA MEDIAL FORM;Lo;0;AL;<medial> 0640 064E 0651;;;;N;;;;; +FCF3;ARABIC LIGATURE SHADDA WITH DAMMA MEDIAL FORM;Lo;0;AL;<medial> 0640 064F 0651;;;;N;;;;; +FCF4;ARABIC LIGATURE SHADDA WITH KASRA MEDIAL FORM;Lo;0;AL;<medial> 0640 0650 0651;;;;N;;;;; +FCF5;ARABIC LIGATURE TAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0637 0649;;;;N;;;;; +FCF6;ARABIC LIGATURE TAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0637 064A;;;;N;;;;; +FCF7;ARABIC LIGATURE AIN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0639 0649;;;;N;;;;; +FCF8;ARABIC LIGATURE AIN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0639 064A;;;;N;;;;; +FCF9;ARABIC LIGATURE GHAIN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 063A 0649;;;;N;;;;; +FCFA;ARABIC LIGATURE GHAIN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 063A 064A;;;;N;;;;; +FCFB;ARABIC LIGATURE SEEN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0633 0649;;;;N;;;;; +FCFC;ARABIC LIGATURE SEEN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0633 064A;;;;N;;;;; +FCFD;ARABIC LIGATURE SHEEN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0634 0649;;;;N;;;;; +FCFE;ARABIC LIGATURE SHEEN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0634 064A;;;;N;;;;; +FCFF;ARABIC LIGATURE HAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062D 0649;;;;N;;;;; +FD00;ARABIC LIGATURE HAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062D 064A;;;;N;;;;; +FD01;ARABIC LIGATURE JEEM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062C 0649;;;;N;;;;; +FD02;ARABIC LIGATURE JEEM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062C 064A;;;;N;;;;; +FD03;ARABIC LIGATURE KHAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062E 0649;;;;N;;;;; +FD04;ARABIC LIGATURE KHAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062E 064A;;;;N;;;;; +FD05;ARABIC LIGATURE SAD WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0635 0649;;;;N;;;;; +FD06;ARABIC LIGATURE SAD WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0635 064A;;;;N;;;;; +FD07;ARABIC LIGATURE DAD WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0636 0649;;;;N;;;;; +FD08;ARABIC LIGATURE DAD WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0636 064A;;;;N;;;;; +FD09;ARABIC LIGATURE SHEEN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0634 062C;;;;N;;;;; +FD0A;ARABIC LIGATURE SHEEN WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0634 062D;;;;N;;;;; +FD0B;ARABIC LIGATURE SHEEN WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0634 062E;;;;N;;;;; +FD0C;ARABIC LIGATURE SHEEN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0634 0645;;;;N;;;;; +FD0D;ARABIC LIGATURE SHEEN WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0634 0631;;;;N;;;;; +FD0E;ARABIC LIGATURE SEEN WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0633 0631;;;;N;;;;; +FD0F;ARABIC LIGATURE SAD WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0635 0631;;;;N;;;;; +FD10;ARABIC LIGATURE DAD WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0636 0631;;;;N;;;;; +FD11;ARABIC LIGATURE TAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0637 0649;;;;N;;;;; +FD12;ARABIC LIGATURE TAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0637 064A;;;;N;;;;; +FD13;ARABIC LIGATURE AIN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0639 0649;;;;N;;;;; +FD14;ARABIC LIGATURE AIN WITH YEH FINAL FORM;Lo;0;AL;<final> 0639 064A;;;;N;;;;; +FD15;ARABIC LIGATURE GHAIN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 063A 0649;;;;N;;;;; +FD16;ARABIC LIGATURE GHAIN WITH YEH FINAL FORM;Lo;0;AL;<final> 063A 064A;;;;N;;;;; +FD17;ARABIC LIGATURE SEEN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 0649;;;;N;;;;; +FD18;ARABIC LIGATURE SEEN WITH YEH FINAL FORM;Lo;0;AL;<final> 0633 064A;;;;N;;;;; +FD19;ARABIC LIGATURE SHEEN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0634 0649;;;;N;;;;; +FD1A;ARABIC LIGATURE SHEEN WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 064A;;;;N;;;;; +FD1B;ARABIC LIGATURE HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062D 0649;;;;N;;;;; +FD1C;ARABIC LIGATURE HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 064A;;;;N;;;;; +FD1D;ARABIC LIGATURE JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 0649;;;;N;;;;; +FD1E;ARABIC LIGATURE JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 064A;;;;N;;;;; +FD1F;ARABIC LIGATURE KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062E 0649;;;;N;;;;; +FD20;ARABIC LIGATURE KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062E 064A;;;;N;;;;; +FD21;ARABIC LIGATURE SAD WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0635 0649;;;;N;;;;; +FD22;ARABIC LIGATURE SAD WITH YEH FINAL FORM;Lo;0;AL;<final> 0635 064A;;;;N;;;;; +FD23;ARABIC LIGATURE DAD WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0636 0649;;;;N;;;;; +FD24;ARABIC LIGATURE DAD WITH YEH FINAL FORM;Lo;0;AL;<final> 0636 064A;;;;N;;;;; +FD25;ARABIC LIGATURE SHEEN WITH JEEM FINAL FORM;Lo;0;AL;<final> 0634 062C;;;;N;;;;; +FD26;ARABIC LIGATURE SHEEN WITH HAH FINAL FORM;Lo;0;AL;<final> 0634 062D;;;;N;;;;; +FD27;ARABIC LIGATURE SHEEN WITH KHAH FINAL FORM;Lo;0;AL;<final> 0634 062E;;;;N;;;;; +FD28;ARABIC LIGATURE SHEEN WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 0645;;;;N;;;;; +FD29;ARABIC LIGATURE SHEEN WITH REH FINAL FORM;Lo;0;AL;<final> 0634 0631;;;;N;;;;; +FD2A;ARABIC LIGATURE SEEN WITH REH FINAL FORM;Lo;0;AL;<final> 0633 0631;;;;N;;;;; +FD2B;ARABIC LIGATURE SAD WITH REH FINAL FORM;Lo;0;AL;<final> 0635 0631;;;;N;;;;; +FD2C;ARABIC LIGATURE DAD WITH REH FINAL FORM;Lo;0;AL;<final> 0636 0631;;;;N;;;;; +FD2D;ARABIC LIGATURE SHEEN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0634 062C;;;;N;;;;; +FD2E;ARABIC LIGATURE SHEEN WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0634 062D;;;;N;;;;; +FD2F;ARABIC LIGATURE SHEEN WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0634 062E;;;;N;;;;; +FD30;ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 0645;;;;N;;;;; +FD31;ARABIC LIGATURE SEEN WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0633 0647;;;;N;;;;; +FD32;ARABIC LIGATURE SHEEN WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0634 0647;;;;N;;;;; +FD33;ARABIC LIGATURE TAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0637 0645;;;;N;;;;; +FD34;ARABIC LIGATURE SEEN WITH JEEM MEDIAL FORM;Lo;0;AL;<medial> 0633 062C;;;;N;;;;; +FD35;ARABIC LIGATURE SEEN WITH HAH MEDIAL FORM;Lo;0;AL;<medial> 0633 062D;;;;N;;;;; +FD36;ARABIC LIGATURE SEEN WITH KHAH MEDIAL FORM;Lo;0;AL;<medial> 0633 062E;;;;N;;;;; +FD37;ARABIC LIGATURE SHEEN WITH JEEM MEDIAL FORM;Lo;0;AL;<medial> 0634 062C;;;;N;;;;; +FD38;ARABIC LIGATURE SHEEN WITH HAH MEDIAL FORM;Lo;0;AL;<medial> 0634 062D;;;;N;;;;; +FD39;ARABIC LIGATURE SHEEN WITH KHAH MEDIAL FORM;Lo;0;AL;<medial> 0634 062E;;;;N;;;;; +FD3A;ARABIC LIGATURE TAH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0637 0645;;;;N;;;;; +FD3B;ARABIC LIGATURE ZAH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0638 0645;;;;N;;;;; +FD3C;ARABIC LIGATURE ALEF WITH FATHATAN FINAL FORM;Lo;0;AL;<final> 0627 064B;;;;N;;;;; +FD3D;ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0627 064B;;;;N;;;;; +FD3E;ORNATE LEFT PARENTHESIS;Pe;0;ON;;;;;N;;;;; +FD3F;ORNATE RIGHT PARENTHESIS;Ps;0;ON;;;;;N;;;;; +FD50;ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062C 0645;;;;N;;;;; +FD51;ARABIC LIGATURE TEH WITH HAH WITH JEEM FINAL FORM;Lo;0;AL;<final> 062A 062D 062C;;;;N;;;;; +FD52;ARABIC LIGATURE TEH WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062D 062C;;;;N;;;;; +FD53;ARABIC LIGATURE TEH WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062D 0645;;;;N;;;;; +FD54;ARABIC LIGATURE TEH WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062E 0645;;;;N;;;;; +FD55;ARABIC LIGATURE TEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062C;;;;N;;;;; +FD56;ARABIC LIGATURE TEH WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062D;;;;N;;;;; +FD57;ARABIC LIGATURE TEH WITH MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062E;;;;N;;;;; +FD58;ARABIC LIGATURE JEEM WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 062C 0645 062D;;;;N;;;;; +FD59;ARABIC LIGATURE JEEM WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062C 0645 062D;;;;N;;;;; +FD5A;ARABIC LIGATURE HAH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 0645 064A;;;;N;;;;; +FD5B;ARABIC LIGATURE HAH WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062D 0645 0649;;;;N;;;;; +FD5C;ARABIC LIGATURE SEEN WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 062D 062C;;;;N;;;;; +FD5D;ARABIC LIGATURE SEEN WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 062C 062D;;;;N;;;;; +FD5E;ARABIC LIGATURE SEEN WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 062C 0649;;;;N;;;;; +FD5F;ARABIC LIGATURE SEEN WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0633 0645 062D;;;;N;;;;; +FD60;ARABIC LIGATURE SEEN WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 0645 062D;;;;N;;;;; +FD61;ARABIC LIGATURE SEEN WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645 062C;;;;N;;;;; +FD62;ARABIC LIGATURE SEEN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0633 0645 0645;;;;N;;;;; +FD63;ARABIC LIGATURE SEEN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645 0645;;;;N;;;;; +FD64;ARABIC LIGATURE SAD WITH HAH WITH HAH FINAL FORM;Lo;0;AL;<final> 0635 062D 062D;;;;N;;;;; +FD65;ARABIC LIGATURE SAD WITH HAH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0635 062D 062D;;;;N;;;;; +FD66;ARABIC LIGATURE SAD WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0635 0645 0645;;;;N;;;;; +FD67;ARABIC LIGATURE SHEEN WITH HAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 062D 0645;;;;N;;;;; +FD68;ARABIC LIGATURE SHEEN WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 062D 0645;;;;N;;;;; +FD69;ARABIC LIGATURE SHEEN WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 062C 064A;;;;N;;;;; +FD6A;ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH FINAL FORM;Lo;0;AL;<final> 0634 0645 062E;;;;N;;;;; +FD6B;ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0634 0645 062E;;;;N;;;;; +FD6C;ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 0645 0645;;;;N;;;;; +FD6D;ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 0645 0645;;;;N;;;;; +FD6E;ARABIC LIGATURE DAD WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0636 062D 0649;;;;N;;;;; +FD6F;ARABIC LIGATURE DAD WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0636 062E 0645;;;;N;;;;; +FD70;ARABIC LIGATURE DAD WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0636 062E 0645;;;;N;;;;; +FD71;ARABIC LIGATURE TAH WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0637 0645 062D;;;;N;;;;; +FD72;ARABIC LIGATURE TAH WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0637 0645 062D;;;;N;;;;; +FD73;ARABIC LIGATURE TAH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0637 0645 0645;;;;N;;;;; +FD74;ARABIC LIGATURE TAH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0637 0645 064A;;;;N;;;;; +FD75;ARABIC LIGATURE AIN WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0639 062C 0645;;;;N;;;;; +FD76;ARABIC LIGATURE AIN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0639 0645 0645;;;;N;;;;; +FD77;ARABIC LIGATURE AIN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 0645 0645;;;;N;;;;; +FD78;ARABIC LIGATURE AIN WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0639 0645 0649;;;;N;;;;; +FD79;ARABIC LIGATURE GHAIN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 063A 0645 0645;;;;N;;;;; +FD7A;ARABIC LIGATURE GHAIN WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 063A 0645 064A;;;;N;;;;; +FD7B;ARABIC LIGATURE GHAIN WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 063A 0645 0649;;;;N;;;;; +FD7C;ARABIC LIGATURE FEH WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0641 062E 0645;;;;N;;;;; +FD7D;ARABIC LIGATURE FEH WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0641 062E 0645;;;;N;;;;; +FD7E;ARABIC LIGATURE QAF WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0642 0645 062D;;;;N;;;;; +FD7F;ARABIC LIGATURE QAF WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0642 0645 0645;;;;N;;;;; +FD80;ARABIC LIGATURE LAM WITH HAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062D 0645;;;;N;;;;; +FD81;ARABIC LIGATURE LAM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 062D 064A;;;;N;;;;; +FD82;ARABIC LIGATURE LAM WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0644 062D 0649;;;;N;;;;; +FD83;ARABIC LIGATURE LAM WITH JEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C 062C;;;;N;;;;; +FD84;ARABIC LIGATURE LAM WITH JEEM WITH JEEM FINAL FORM;Lo;0;AL;<final> 0644 062C 062C;;;;N;;;;; +FD85;ARABIC LIGATURE LAM WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062E 0645;;;;N;;;;; +FD86;ARABIC LIGATURE LAM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062E 0645;;;;N;;;;; +FD87;ARABIC LIGATURE LAM WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0644 0645 062D;;;;N;;;;; +FD88;ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0644 0645 062D;;;;N;;;;; +FD89;ARABIC LIGATURE MEEM WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062D 062C;;;;N;;;;; +FD8A;ARABIC LIGATURE MEEM WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062D 0645;;;;N;;;;; +FD8B;ARABIC LIGATURE MEEM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062D 064A;;;;N;;;;; +FD8C;ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0645 062C 062D;;;;N;;;;; +FD8D;ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062C 0645;;;;N;;;;; +FD8E;ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062E 062C;;;;N;;;;; +FD8F;ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062E 0645;;;;N;;;;; +FD92;ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0645 062C 062E;;;;N;;;;; +FD93;ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645 062C;;;;N;;;;; +FD94;ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645 0645;;;;N;;;;; +FD95;ARABIC LIGATURE NOON WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062D 0645;;;;N;;;;; +FD96;ARABIC LIGATURE NOON WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 062D 0649;;;;N;;;;; +FD97;ARABIC LIGATURE NOON WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0646 062C 0645;;;;N;;;;; +FD98;ARABIC LIGATURE NOON WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062C 0645;;;;N;;;;; +FD99;ARABIC LIGATURE NOON WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 062C 0649;;;;N;;;;; +FD9A;ARABIC LIGATURE NOON WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 0645 064A;;;;N;;;;; +FD9B;ARABIC LIGATURE NOON WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 0645 0649;;;;N;;;;; +FD9C;ARABIC LIGATURE YEH WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 064A 0645 0645;;;;N;;;;; +FD9D;ARABIC LIGATURE YEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 064A 0645 0645;;;;N;;;;; +FD9E;ARABIC LIGATURE BEH WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 062E 064A;;;;N;;;;; +FD9F;ARABIC LIGATURE TEH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 062C 064A;;;;N;;;;; +FDA0;ARABIC LIGATURE TEH WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 062C 0649;;;;N;;;;; +FDA1;ARABIC LIGATURE TEH WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 062E 064A;;;;N;;;;; +FDA2;ARABIC LIGATURE TEH WITH KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 062E 0649;;;;N;;;;; +FDA3;ARABIC LIGATURE TEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 0645 064A;;;;N;;;;; +FDA4;ARABIC LIGATURE TEH WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 0645 0649;;;;N;;;;; +FDA5;ARABIC LIGATURE JEEM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 0645 064A;;;;N;;;;; +FDA6;ARABIC LIGATURE JEEM WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 062D 0649;;;;N;;;;; +FDA7;ARABIC LIGATURE JEEM WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 0645 0649;;;;N;;;;; +FDA8;ARABIC LIGATURE SEEN WITH KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 062E 0649;;;;N;;;;; +FDA9;ARABIC LIGATURE SAD WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0635 062D 064A;;;;N;;;;; +FDAA;ARABIC LIGATURE SHEEN WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 062D 064A;;;;N;;;;; +FDAB;ARABIC LIGATURE DAD WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0636 062D 064A;;;;N;;;;; +FDAC;ARABIC LIGATURE LAM WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 062C 064A;;;;N;;;;; +FDAD;ARABIC LIGATURE LAM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 0645 064A;;;;N;;;;; +FDAE;ARABIC LIGATURE YEH WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 062D 064A;;;;N;;;;; +FDAF;ARABIC LIGATURE YEH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 062C 064A;;;;N;;;;; +FDB0;ARABIC LIGATURE YEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 0645 064A;;;;N;;;;; +FDB1;ARABIC LIGATURE MEEM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 0645 064A;;;;N;;;;; +FDB2;ARABIC LIGATURE QAF WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0642 0645 064A;;;;N;;;;; +FDB3;ARABIC LIGATURE NOON WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 062D 064A;;;;N;;;;; +FDB4;ARABIC LIGATURE QAF WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0642 0645 062D;;;;N;;;;; +FDB5;ARABIC LIGATURE LAM WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062D 0645;;;;N;;;;; +FDB6;ARABIC LIGATURE AIN WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0639 0645 064A;;;;N;;;;; +FDB7;ARABIC LIGATURE KAF WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0643 0645 064A;;;;N;;;;; +FDB8;ARABIC LIGATURE NOON WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0646 062C 062D;;;;N;;;;; +FDB9;ARABIC LIGATURE MEEM WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062E 064A;;;;N;;;;; +FDBA;ARABIC LIGATURE LAM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C 0645;;;;N;;;;; +FDBB;ARABIC LIGATURE KAF WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0643 0645 0645;;;;N;;;;; +FDBC;ARABIC LIGATURE LAM WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062C 0645;;;;N;;;;; +FDBD;ARABIC LIGATURE NOON WITH JEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0646 062C 062D;;;;N;;;;; +FDBE;ARABIC LIGATURE JEEM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 062D 064A;;;;N;;;;; +FDBF;ARABIC LIGATURE HAH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 062C 064A;;;;N;;;;; +FDC0;ARABIC LIGATURE MEEM WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062C 064A;;;;N;;;;; +FDC1;ARABIC LIGATURE FEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0641 0645 064A;;;;N;;;;; +FDC2;ARABIC LIGATURE BEH WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 062D 064A;;;;N;;;;; +FDC3;ARABIC LIGATURE KAF WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0643 0645 0645;;;;N;;;;; +FDC4;ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 062C 0645;;;;N;;;;; +FDC5;ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0635 0645 0645;;;;N;;;;; +FDC6;ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0633 062E 064A;;;;N;;;;; +FDC7;ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 062C 064A;;;;N;;;;; +FDF0;ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 06D2;;;;N;;;;; +FDF1;ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0642 0644 06D2;;;;N;;;;; +FDF2;ARABIC LIGATURE ALLAH ISOLATED FORM;Lo;0;AL;<isolated> 0627 0644 0644 0647;;;;N;;;;; +FDF3;ARABIC LIGATURE AKBAR ISOLATED FORM;Lo;0;AL;<isolated> 0627 0643 0628 0631;;;;N;;;;; +FDF4;ARABIC LIGATURE MOHAMMAD ISOLATED FORM;Lo;0;AL;<isolated> 0645 062D 0645 062F;;;;N;;;;; +FDF5;ARABIC LIGATURE SALAM ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 0639 0645;;;;N;;;;; +FDF6;ARABIC LIGATURE RASOUL ISOLATED FORM;Lo;0;AL;<isolated> 0631 0633 0648 0644;;;;N;;;;; +FDF7;ARABIC LIGATURE ALAYHE ISOLATED FORM;Lo;0;AL;<isolated> 0639 0644 064A 0647;;;;N;;;;; +FDF8;ARABIC LIGATURE WASALLAM ISOLATED FORM;Lo;0;AL;<isolated> 0648 0633 0644 0645;;;;N;;;;; +FDF9;ARABIC LIGATURE SALLA ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 0649;;;;N;;;;; +FDFA;ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM;Lo;0;AL;<isolated> 0635 0644 0649 0020 0627 0644 0644 0647 0020 0639 0644 064A 0647 0020 0648 0633 0644 0645;;;;N;ARABIC LETTER SALLALLAHOU ALAYHE WASALLAM;;;; +FDFB;ARABIC LIGATURE JALLAJALALOUHOU;Lo;0;AL;<isolated> 062C 0644 0020 062C 0644 0627 0644 0647;;;;N;ARABIC LETTER JALLAJALALOUHOU;;;; +FDFC;RIAL SIGN;Sc;0;AL;<isolated> 0631 06CC 0627 0644;;;;N;;;;; +FDFD;ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM;So;0;ON;;;;;N;;;;; +FE00;VARIATION SELECTOR-1;Mn;0;NSM;;;;;N;;;;; +FE01;VARIATION SELECTOR-2;Mn;0;NSM;;;;;N;;;;; +FE02;VARIATION SELECTOR-3;Mn;0;NSM;;;;;N;;;;; +FE03;VARIATION SELECTOR-4;Mn;0;NSM;;;;;N;;;;; +FE04;VARIATION SELECTOR-5;Mn;0;NSM;;;;;N;;;;; +FE05;VARIATION SELECTOR-6;Mn;0;NSM;;;;;N;;;;; +FE06;VARIATION SELECTOR-7;Mn;0;NSM;;;;;N;;;;; +FE07;VARIATION SELECTOR-8;Mn;0;NSM;;;;;N;;;;; +FE08;VARIATION SELECTOR-9;Mn;0;NSM;;;;;N;;;;; +FE09;VARIATION SELECTOR-10;Mn;0;NSM;;;;;N;;;;; +FE0A;VARIATION SELECTOR-11;Mn;0;NSM;;;;;N;;;;; +FE0B;VARIATION SELECTOR-12;Mn;0;NSM;;;;;N;;;;; +FE0C;VARIATION SELECTOR-13;Mn;0;NSM;;;;;N;;;;; +FE0D;VARIATION SELECTOR-14;Mn;0;NSM;;;;;N;;;;; +FE0E;VARIATION SELECTOR-15;Mn;0;NSM;;;;;N;;;;; +FE0F;VARIATION SELECTOR-16;Mn;0;NSM;;;;;N;;;;; +FE10;PRESENTATION FORM FOR VERTICAL COMMA;Po;0;ON;<vertical> 002C;;;;N;;;;; +FE11;PRESENTATION FORM FOR VERTICAL IDEOGRAPHIC COMMA;Po;0;ON;<vertical> 3001;;;;N;;;;; +FE12;PRESENTATION FORM FOR VERTICAL IDEOGRAPHIC FULL STOP;Po;0;ON;<vertical> 3002;;;;N;;;;; +FE13;PRESENTATION FORM FOR VERTICAL COLON;Po;0;ON;<vertical> 003A;;;;N;;;;; +FE14;PRESENTATION FORM FOR VERTICAL SEMICOLON;Po;0;ON;<vertical> 003B;;;;N;;;;; +FE15;PRESENTATION FORM FOR VERTICAL EXCLAMATION MARK;Po;0;ON;<vertical> 0021;;;;N;;;;; +FE16;PRESENTATION FORM FOR VERTICAL QUESTION MARK;Po;0;ON;<vertical> 003F;;;;N;;;;; +FE17;PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET;Ps;0;ON;<vertical> 3016;;;;N;;;;; +FE18;PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET;Pe;0;ON;<vertical> 3017;;;;N;;;;; +FE19;PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS;Po;0;ON;<vertical> 2026;;;;N;;;;; +FE20;COMBINING LIGATURE LEFT HALF;Mn;230;NSM;;;;;N;;;;; +FE21;COMBINING LIGATURE RIGHT HALF;Mn;230;NSM;;;;;N;;;;; +FE22;COMBINING DOUBLE TILDE LEFT HALF;Mn;230;NSM;;;;;N;;;;; +FE23;COMBINING DOUBLE TILDE RIGHT HALF;Mn;230;NSM;;;;;N;;;;; +FE24;COMBINING MACRON LEFT HALF;Mn;230;NSM;;;;;N;;;;; +FE25;COMBINING MACRON RIGHT HALF;Mn;230;NSM;;;;;N;;;;; +FE26;COMBINING CONJOINING MACRON;Mn;230;NSM;;;;;N;;;;; +FE27;COMBINING LIGATURE LEFT HALF BELOW;Mn;220;NSM;;;;;N;;;;; +FE28;COMBINING LIGATURE RIGHT HALF BELOW;Mn;220;NSM;;;;;N;;;;; +FE29;COMBINING TILDE LEFT HALF BELOW;Mn;220;NSM;;;;;N;;;;; +FE2A;COMBINING TILDE RIGHT HALF BELOW;Mn;220;NSM;;;;;N;;;;; +FE2B;COMBINING MACRON LEFT HALF BELOW;Mn;220;NSM;;;;;N;;;;; +FE2C;COMBINING MACRON RIGHT HALF BELOW;Mn;220;NSM;;;;;N;;;;; +FE2D;COMBINING CONJOINING MACRON BELOW;Mn;220;NSM;;;;;N;;;;; +FE2E;COMBINING CYRILLIC TITLO LEFT HALF;Mn;230;NSM;;;;;N;;;;; +FE2F;COMBINING CYRILLIC TITLO RIGHT HALF;Mn;230;NSM;;;;;N;;;;; +FE30;PRESENTATION FORM FOR VERTICAL TWO DOT LEADER;Po;0;ON;<vertical> 2025;;;;N;GLYPH FOR VERTICAL TWO DOT LEADER;;;; +FE31;PRESENTATION FORM FOR VERTICAL EM DASH;Pd;0;ON;<vertical> 2014;;;;N;GLYPH FOR VERTICAL EM DASH;;;; +FE32;PRESENTATION FORM FOR VERTICAL EN DASH;Pd;0;ON;<vertical> 2013;;;;N;GLYPH FOR VERTICAL EN DASH;;;; +FE33;PRESENTATION FORM FOR VERTICAL LOW LINE;Pc;0;ON;<vertical> 005F;;;;N;GLYPH FOR VERTICAL SPACING UNDERSCORE;;;; +FE34;PRESENTATION FORM FOR VERTICAL WAVY LOW LINE;Pc;0;ON;<vertical> 005F;;;;N;GLYPH FOR VERTICAL SPACING WAVY UNDERSCORE;;;; +FE35;PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS;Ps;0;ON;<vertical> 0028;;;;N;GLYPH FOR VERTICAL OPENING PARENTHESIS;;;; +FE36;PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS;Pe;0;ON;<vertical> 0029;;;;N;GLYPH FOR VERTICAL CLOSING PARENTHESIS;;;; +FE37;PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET;Ps;0;ON;<vertical> 007B;;;;N;GLYPH FOR VERTICAL OPENING CURLY BRACKET;;;; +FE38;PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET;Pe;0;ON;<vertical> 007D;;;;N;GLYPH FOR VERTICAL CLOSING CURLY BRACKET;;;; +FE39;PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET;Ps;0;ON;<vertical> 3014;;;;N;GLYPH FOR VERTICAL OPENING TORTOISE SHELL BRACKET;;;; +FE3A;PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;<vertical> 3015;;;;N;GLYPH FOR VERTICAL CLOSING TORTOISE SHELL BRACKET;;;; +FE3B;PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET;Ps;0;ON;<vertical> 3010;;;;N;GLYPH FOR VERTICAL OPENING BLACK LENTICULAR BRACKET;;;; +FE3C;PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET;Pe;0;ON;<vertical> 3011;;;;N;GLYPH FOR VERTICAL CLOSING BLACK LENTICULAR BRACKET;;;; +FE3D;PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;<vertical> 300A;;;;N;GLYPH FOR VERTICAL OPENING DOUBLE ANGLE BRACKET;;;; +FE3E;PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;<vertical> 300B;;;;N;GLYPH FOR VERTICAL CLOSING DOUBLE ANGLE BRACKET;;;; +FE3F;PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET;Ps;0;ON;<vertical> 3008;;;;N;GLYPH FOR VERTICAL OPENING ANGLE BRACKET;;;; +FE40;PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET;Pe;0;ON;<vertical> 3009;;;;N;GLYPH FOR VERTICAL CLOSING ANGLE BRACKET;;;; +FE41;PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET;Ps;0;ON;<vertical> 300C;;;;N;GLYPH FOR VERTICAL OPENING CORNER BRACKET;;;; +FE42;PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET;Pe;0;ON;<vertical> 300D;;;;N;GLYPH FOR VERTICAL CLOSING CORNER BRACKET;;;; +FE43;PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET;Ps;0;ON;<vertical> 300E;;;;N;GLYPH FOR VERTICAL OPENING WHITE CORNER BRACKET;;;; +FE44;PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET;Pe;0;ON;<vertical> 300F;;;;N;GLYPH FOR VERTICAL CLOSING WHITE CORNER BRACKET;;;; +FE45;SESAME DOT;Po;0;ON;;;;;N;;;;; +FE46;WHITE SESAME DOT;Po;0;ON;;;;;N;;;;; +FE47;PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET;Ps;0;ON;<vertical> 005B;;;;N;;;;; +FE48;PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET;Pe;0;ON;<vertical> 005D;;;;N;;;;; +FE49;DASHED OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING DASHED OVERSCORE;;;; +FE4A;CENTRELINE OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING CENTERLINE OVERSCORE;;;; +FE4B;WAVY OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING WAVY OVERSCORE;;;; +FE4C;DOUBLE WAVY OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING DOUBLE WAVY OVERSCORE;;;; +FE4D;DASHED LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING DASHED UNDERSCORE;;;; +FE4E;CENTRELINE LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING CENTERLINE UNDERSCORE;;;; +FE4F;WAVY LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING WAVY UNDERSCORE;;;; +FE50;SMALL COMMA;Po;0;CS;<small> 002C;;;;N;;;;; +FE51;SMALL IDEOGRAPHIC COMMA;Po;0;ON;<small> 3001;;;;N;;;;; +FE52;SMALL FULL STOP;Po;0;CS;<small> 002E;;;;N;SMALL PERIOD;;;; +FE54;SMALL SEMICOLON;Po;0;ON;<small> 003B;;;;N;;;;; +FE55;SMALL COLON;Po;0;CS;<small> 003A;;;;N;;;;; +FE56;SMALL QUESTION MARK;Po;0;ON;<small> 003F;;;;N;;;;; +FE57;SMALL EXCLAMATION MARK;Po;0;ON;<small> 0021;;;;N;;;;; +FE58;SMALL EM DASH;Pd;0;ON;<small> 2014;;;;N;;;;; +FE59;SMALL LEFT PARENTHESIS;Ps;0;ON;<small> 0028;;;;Y;SMALL OPENING PARENTHESIS;;;; +FE5A;SMALL RIGHT PARENTHESIS;Pe;0;ON;<small> 0029;;;;Y;SMALL CLOSING PARENTHESIS;;;; +FE5B;SMALL LEFT CURLY BRACKET;Ps;0;ON;<small> 007B;;;;Y;SMALL OPENING CURLY BRACKET;;;; +FE5C;SMALL RIGHT CURLY BRACKET;Pe;0;ON;<small> 007D;;;;Y;SMALL CLOSING CURLY BRACKET;;;; +FE5D;SMALL LEFT TORTOISE SHELL BRACKET;Ps;0;ON;<small> 3014;;;;Y;SMALL OPENING TORTOISE SHELL BRACKET;;;; +FE5E;SMALL RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;<small> 3015;;;;Y;SMALL CLOSING TORTOISE SHELL BRACKET;;;; +FE5F;SMALL NUMBER SIGN;Po;0;ET;<small> 0023;;;;N;;;;; +FE60;SMALL AMPERSAND;Po;0;ON;<small> 0026;;;;N;;;;; +FE61;SMALL ASTERISK;Po;0;ON;<small> 002A;;;;N;;;;; +FE62;SMALL PLUS SIGN;Sm;0;ES;<small> 002B;;;;N;;;;; +FE63;SMALL HYPHEN-MINUS;Pd;0;ES;<small> 002D;;;;N;;;;; +FE64;SMALL LESS-THAN SIGN;Sm;0;ON;<small> 003C;;;;Y;;;;; +FE65;SMALL GREATER-THAN SIGN;Sm;0;ON;<small> 003E;;;;Y;;;;; +FE66;SMALL EQUALS SIGN;Sm;0;ON;<small> 003D;;;;N;;;;; +FE68;SMALL REVERSE SOLIDUS;Po;0;ON;<small> 005C;;;;N;SMALL BACKSLASH;;;; +FE69;SMALL DOLLAR SIGN;Sc;0;ET;<small> 0024;;;;N;;;;; +FE6A;SMALL PERCENT SIGN;Po;0;ET;<small> 0025;;;;N;;;;; +FE6B;SMALL COMMERCIAL AT;Po;0;ON;<small> 0040;;;;N;;;;; +FE70;ARABIC FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064B;;;;N;ARABIC SPACING FATHATAN;;;; +FE71;ARABIC TATWEEL WITH FATHATAN ABOVE;Lo;0;AL;<medial> 0640 064B;;;;N;ARABIC FATHATAN ON TATWEEL;;;; +FE72;ARABIC DAMMATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064C;;;;N;ARABIC SPACING DAMMATAN;;;; +FE73;ARABIC TAIL FRAGMENT;Lo;0;AL;;;;;N;;;;; +FE74;ARABIC KASRATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064D;;;;N;ARABIC SPACING KASRATAN;;;; +FE76;ARABIC FATHA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064E;;;;N;ARABIC SPACING FATHAH;;;; +FE77;ARABIC FATHA MEDIAL FORM;Lo;0;AL;<medial> 0640 064E;;;;N;ARABIC FATHAH ON TATWEEL;;;; +FE78;ARABIC DAMMA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064F;;;;N;ARABIC SPACING DAMMAH;;;; +FE79;ARABIC DAMMA MEDIAL FORM;Lo;0;AL;<medial> 0640 064F;;;;N;ARABIC DAMMAH ON TATWEEL;;;; +FE7A;ARABIC KASRA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0650;;;;N;ARABIC SPACING KASRAH;;;; +FE7B;ARABIC KASRA MEDIAL FORM;Lo;0;AL;<medial> 0640 0650;;;;N;ARABIC KASRAH ON TATWEEL;;;; +FE7C;ARABIC SHADDA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0651;;;;N;ARABIC SPACING SHADDAH;;;; +FE7D;ARABIC SHADDA MEDIAL FORM;Lo;0;AL;<medial> 0640 0651;;;;N;ARABIC SHADDAH ON TATWEEL;;;; +FE7E;ARABIC SUKUN ISOLATED FORM;Lo;0;AL;<isolated> 0020 0652;;;;N;ARABIC SPACING SUKUN;;;; +FE7F;ARABIC SUKUN MEDIAL FORM;Lo;0;AL;<medial> 0640 0652;;;;N;ARABIC SUKUN ON TATWEEL;;;; +FE80;ARABIC LETTER HAMZA ISOLATED FORM;Lo;0;AL;<isolated> 0621;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH;;;; +FE81;ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON ALEF;;;; +FE82;ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL;<final> 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON ALEF;;;; +FE83;ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON ALEF;;;; +FE84;ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON ALEF;;;; +FE85;ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0624;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON WAW;;;; +FE86;ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0624;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON WAW;;;; +FE87;ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM;Lo;0;AL;<isolated> 0625;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH UNDER ALEF;;;; +FE88;ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM;Lo;0;AL;<final> 0625;;;;N;GLYPH FOR FINAL ARABIC HAMZAH UNDER ALEF;;;; +FE89;ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0626;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON YA;;;; +FE8A;ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0626;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON YA;;;; +FE8B;ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM;Lo;0;AL;<initial> 0626;;;;N;GLYPH FOR INITIAL ARABIC HAMZAH ON YA;;;; +FE8C;ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM;Lo;0;AL;<medial> 0626;;;;N;GLYPH FOR MEDIAL ARABIC HAMZAH ON YA;;;; +FE8D;ARABIC LETTER ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0627;;;;N;GLYPH FOR ISOLATE ARABIC ALEF;;;; +FE8E;ARABIC LETTER ALEF FINAL FORM;Lo;0;AL;<final> 0627;;;;N;GLYPH FOR FINAL ARABIC ALEF;;;; +FE8F;ARABIC LETTER BEH ISOLATED FORM;Lo;0;AL;<isolated> 0628;;;;N;GLYPH FOR ISOLATE ARABIC BAA;;;; +FE90;ARABIC LETTER BEH FINAL FORM;Lo;0;AL;<final> 0628;;;;N;GLYPH FOR FINAL ARABIC BAA;;;; +FE91;ARABIC LETTER BEH INITIAL FORM;Lo;0;AL;<initial> 0628;;;;N;GLYPH FOR INITIAL ARABIC BAA;;;; +FE92;ARABIC LETTER BEH MEDIAL FORM;Lo;0;AL;<medial> 0628;;;;N;GLYPH FOR MEDIAL ARABIC BAA;;;; +FE93;ARABIC LETTER TEH MARBUTA ISOLATED FORM;Lo;0;AL;<isolated> 0629;;;;N;GLYPH FOR ISOLATE ARABIC TAA MARBUTAH;;;; +FE94;ARABIC LETTER TEH MARBUTA FINAL FORM;Lo;0;AL;<final> 0629;;;;N;GLYPH FOR FINAL ARABIC TAA MARBUTAH;;;; +FE95;ARABIC LETTER TEH ISOLATED FORM;Lo;0;AL;<isolated> 062A;;;;N;GLYPH FOR ISOLATE ARABIC TAA;;;; +FE96;ARABIC LETTER TEH FINAL FORM;Lo;0;AL;<final> 062A;;;;N;GLYPH FOR FINAL ARABIC TAA;;;; +FE97;ARABIC LETTER TEH INITIAL FORM;Lo;0;AL;<initial> 062A;;;;N;GLYPH FOR INITIAL ARABIC TAA;;;; +FE98;ARABIC LETTER TEH MEDIAL FORM;Lo;0;AL;<medial> 062A;;;;N;GLYPH FOR MEDIAL ARABIC TAA;;;; +FE99;ARABIC LETTER THEH ISOLATED FORM;Lo;0;AL;<isolated> 062B;;;;N;GLYPH FOR ISOLATE ARABIC THAA;;;; +FE9A;ARABIC LETTER THEH FINAL FORM;Lo;0;AL;<final> 062B;;;;N;GLYPH FOR FINAL ARABIC THAA;;;; +FE9B;ARABIC LETTER THEH INITIAL FORM;Lo;0;AL;<initial> 062B;;;;N;GLYPH FOR INITIAL ARABIC THAA;;;; +FE9C;ARABIC LETTER THEH MEDIAL FORM;Lo;0;AL;<medial> 062B;;;;N;GLYPH FOR MEDIAL ARABIC THAA;;;; +FE9D;ARABIC LETTER JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062C;;;;N;GLYPH FOR ISOLATE ARABIC JEEM;;;; +FE9E;ARABIC LETTER JEEM FINAL FORM;Lo;0;AL;<final> 062C;;;;N;GLYPH FOR FINAL ARABIC JEEM;;;; +FE9F;ARABIC LETTER JEEM INITIAL FORM;Lo;0;AL;<initial> 062C;;;;N;GLYPH FOR INITIAL ARABIC JEEM;;;; +FEA0;ARABIC LETTER JEEM MEDIAL FORM;Lo;0;AL;<medial> 062C;;;;N;GLYPH FOR MEDIAL ARABIC JEEM;;;; +FEA1;ARABIC LETTER HAH ISOLATED FORM;Lo;0;AL;<isolated> 062D;;;;N;GLYPH FOR ISOLATE ARABIC HAA;;;; +FEA2;ARABIC LETTER HAH FINAL FORM;Lo;0;AL;<final> 062D;;;;N;GLYPH FOR FINAL ARABIC HAA;;;; +FEA3;ARABIC LETTER HAH INITIAL FORM;Lo;0;AL;<initial> 062D;;;;N;GLYPH FOR INITIAL ARABIC HAA;;;; +FEA4;ARABIC LETTER HAH MEDIAL FORM;Lo;0;AL;<medial> 062D;;;;N;GLYPH FOR MEDIAL ARABIC HAA;;;; +FEA5;ARABIC LETTER KHAH ISOLATED FORM;Lo;0;AL;<isolated> 062E;;;;N;GLYPH FOR ISOLATE ARABIC KHAA;;;; +FEA6;ARABIC LETTER KHAH FINAL FORM;Lo;0;AL;<final> 062E;;;;N;GLYPH FOR FINAL ARABIC KHAA;;;; +FEA7;ARABIC LETTER KHAH INITIAL FORM;Lo;0;AL;<initial> 062E;;;;N;GLYPH FOR INITIAL ARABIC KHAA;;;; +FEA8;ARABIC LETTER KHAH MEDIAL FORM;Lo;0;AL;<medial> 062E;;;;N;GLYPH FOR MEDIAL ARABIC KHAA;;;; +FEA9;ARABIC LETTER DAL ISOLATED FORM;Lo;0;AL;<isolated> 062F;;;;N;GLYPH FOR ISOLATE ARABIC DAL;;;; +FEAA;ARABIC LETTER DAL FINAL FORM;Lo;0;AL;<final> 062F;;;;N;GLYPH FOR FINAL ARABIC DAL;;;; +FEAB;ARABIC LETTER THAL ISOLATED FORM;Lo;0;AL;<isolated> 0630;;;;N;GLYPH FOR ISOLATE ARABIC THAL;;;; +FEAC;ARABIC LETTER THAL FINAL FORM;Lo;0;AL;<final> 0630;;;;N;GLYPH FOR FINAL ARABIC THAL;;;; +FEAD;ARABIC LETTER REH ISOLATED FORM;Lo;0;AL;<isolated> 0631;;;;N;GLYPH FOR ISOLATE ARABIC RA;;;; +FEAE;ARABIC LETTER REH FINAL FORM;Lo;0;AL;<final> 0631;;;;N;GLYPH FOR FINAL ARABIC RA;;;; +FEAF;ARABIC LETTER ZAIN ISOLATED FORM;Lo;0;AL;<isolated> 0632;;;;N;GLYPH FOR ISOLATE ARABIC ZAIN;;;; +FEB0;ARABIC LETTER ZAIN FINAL FORM;Lo;0;AL;<final> 0632;;;;N;GLYPH FOR FINAL ARABIC ZAIN;;;; +FEB1;ARABIC LETTER SEEN ISOLATED FORM;Lo;0;AL;<isolated> 0633;;;;N;GLYPH FOR ISOLATE ARABIC SEEN;;;; +FEB2;ARABIC LETTER SEEN FINAL FORM;Lo;0;AL;<final> 0633;;;;N;GLYPH FOR FINAL ARABIC SEEN;;;; +FEB3;ARABIC LETTER SEEN INITIAL FORM;Lo;0;AL;<initial> 0633;;;;N;GLYPH FOR INITIAL ARABIC SEEN;;;; +FEB4;ARABIC LETTER SEEN MEDIAL FORM;Lo;0;AL;<medial> 0633;;;;N;GLYPH FOR MEDIAL ARABIC SEEN;;;; +FEB5;ARABIC LETTER SHEEN ISOLATED FORM;Lo;0;AL;<isolated> 0634;;;;N;GLYPH FOR ISOLATE ARABIC SHEEN;;;; +FEB6;ARABIC LETTER SHEEN FINAL FORM;Lo;0;AL;<final> 0634;;;;N;GLYPH FOR FINAL ARABIC SHEEN;;;; +FEB7;ARABIC LETTER SHEEN INITIAL FORM;Lo;0;AL;<initial> 0634;;;;N;GLYPH FOR INITIAL ARABIC SHEEN;;;; +FEB8;ARABIC LETTER SHEEN MEDIAL FORM;Lo;0;AL;<medial> 0634;;;;N;GLYPH FOR MEDIAL ARABIC SHEEN;;;; +FEB9;ARABIC LETTER SAD ISOLATED FORM;Lo;0;AL;<isolated> 0635;;;;N;GLYPH FOR ISOLATE ARABIC SAD;;;; +FEBA;ARABIC LETTER SAD FINAL FORM;Lo;0;AL;<final> 0635;;;;N;GLYPH FOR FINAL ARABIC SAD;;;; +FEBB;ARABIC LETTER SAD INITIAL FORM;Lo;0;AL;<initial> 0635;;;;N;GLYPH FOR INITIAL ARABIC SAD;;;; +FEBC;ARABIC LETTER SAD MEDIAL FORM;Lo;0;AL;<medial> 0635;;;;N;GLYPH FOR MEDIAL ARABIC SAD;;;; +FEBD;ARABIC LETTER DAD ISOLATED FORM;Lo;0;AL;<isolated> 0636;;;;N;GLYPH FOR ISOLATE ARABIC DAD;;;; +FEBE;ARABIC LETTER DAD FINAL FORM;Lo;0;AL;<final> 0636;;;;N;GLYPH FOR FINAL ARABIC DAD;;;; +FEBF;ARABIC LETTER DAD INITIAL FORM;Lo;0;AL;<initial> 0636;;;;N;GLYPH FOR INITIAL ARABIC DAD;;;; +FEC0;ARABIC LETTER DAD MEDIAL FORM;Lo;0;AL;<medial> 0636;;;;N;GLYPH FOR MEDIAL ARABIC DAD;;;; +FEC1;ARABIC LETTER TAH ISOLATED FORM;Lo;0;AL;<isolated> 0637;;;;N;GLYPH FOR ISOLATE ARABIC TAH;;;; +FEC2;ARABIC LETTER TAH FINAL FORM;Lo;0;AL;<final> 0637;;;;N;GLYPH FOR FINAL ARABIC TAH;;;; +FEC3;ARABIC LETTER TAH INITIAL FORM;Lo;0;AL;<initial> 0637;;;;N;GLYPH FOR INITIAL ARABIC TAH;;;; +FEC4;ARABIC LETTER TAH MEDIAL FORM;Lo;0;AL;<medial> 0637;;;;N;GLYPH FOR MEDIAL ARABIC TAH;;;; +FEC5;ARABIC LETTER ZAH ISOLATED FORM;Lo;0;AL;<isolated> 0638;;;;N;GLYPH FOR ISOLATE ARABIC DHAH;;;; +FEC6;ARABIC LETTER ZAH FINAL FORM;Lo;0;AL;<final> 0638;;;;N;GLYPH FOR FINAL ARABIC DHAH;;;; +FEC7;ARABIC LETTER ZAH INITIAL FORM;Lo;0;AL;<initial> 0638;;;;N;GLYPH FOR INITIAL ARABIC DHAH;;;; +FEC8;ARABIC LETTER ZAH MEDIAL FORM;Lo;0;AL;<medial> 0638;;;;N;GLYPH FOR MEDIAL ARABIC DHAH;;;; +FEC9;ARABIC LETTER AIN ISOLATED FORM;Lo;0;AL;<isolated> 0639;;;;N;GLYPH FOR ISOLATE ARABIC AIN;;;; +FECA;ARABIC LETTER AIN FINAL FORM;Lo;0;AL;<final> 0639;;;;N;GLYPH FOR FINAL ARABIC AIN;;;; +FECB;ARABIC LETTER AIN INITIAL FORM;Lo;0;AL;<initial> 0639;;;;N;GLYPH FOR INITIAL ARABIC AIN;;;; +FECC;ARABIC LETTER AIN MEDIAL FORM;Lo;0;AL;<medial> 0639;;;;N;GLYPH FOR MEDIAL ARABIC AIN;;;; +FECD;ARABIC LETTER GHAIN ISOLATED FORM;Lo;0;AL;<isolated> 063A;;;;N;GLYPH FOR ISOLATE ARABIC GHAIN;;;; +FECE;ARABIC LETTER GHAIN FINAL FORM;Lo;0;AL;<final> 063A;;;;N;GLYPH FOR FINAL ARABIC GHAIN;;;; +FECF;ARABIC LETTER GHAIN INITIAL FORM;Lo;0;AL;<initial> 063A;;;;N;GLYPH FOR INITIAL ARABIC GHAIN;;;; +FED0;ARABIC LETTER GHAIN MEDIAL FORM;Lo;0;AL;<medial> 063A;;;;N;GLYPH FOR MEDIAL ARABIC GHAIN;;;; +FED1;ARABIC LETTER FEH ISOLATED FORM;Lo;0;AL;<isolated> 0641;;;;N;GLYPH FOR ISOLATE ARABIC FA;;;; +FED2;ARABIC LETTER FEH FINAL FORM;Lo;0;AL;<final> 0641;;;;N;GLYPH FOR FINAL ARABIC FA;;;; +FED3;ARABIC LETTER FEH INITIAL FORM;Lo;0;AL;<initial> 0641;;;;N;GLYPH FOR INITIAL ARABIC FA;;;; +FED4;ARABIC LETTER FEH MEDIAL FORM;Lo;0;AL;<medial> 0641;;;;N;GLYPH FOR MEDIAL ARABIC FA;;;; +FED5;ARABIC LETTER QAF ISOLATED FORM;Lo;0;AL;<isolated> 0642;;;;N;GLYPH FOR ISOLATE ARABIC QAF;;;; +FED6;ARABIC LETTER QAF FINAL FORM;Lo;0;AL;<final> 0642;;;;N;GLYPH FOR FINAL ARABIC QAF;;;; +FED7;ARABIC LETTER QAF INITIAL FORM;Lo;0;AL;<initial> 0642;;;;N;GLYPH FOR INITIAL ARABIC QAF;;;; +FED8;ARABIC LETTER QAF MEDIAL FORM;Lo;0;AL;<medial> 0642;;;;N;GLYPH FOR MEDIAL ARABIC QAF;;;; +FED9;ARABIC LETTER KAF ISOLATED FORM;Lo;0;AL;<isolated> 0643;;;;N;GLYPH FOR ISOLATE ARABIC CAF;;;; +FEDA;ARABIC LETTER KAF FINAL FORM;Lo;0;AL;<final> 0643;;;;N;GLYPH FOR FINAL ARABIC CAF;;;; +FEDB;ARABIC LETTER KAF INITIAL FORM;Lo;0;AL;<initial> 0643;;;;N;GLYPH FOR INITIAL ARABIC CAF;;;; +FEDC;ARABIC LETTER KAF MEDIAL FORM;Lo;0;AL;<medial> 0643;;;;N;GLYPH FOR MEDIAL ARABIC CAF;;;; +FEDD;ARABIC LETTER LAM ISOLATED FORM;Lo;0;AL;<isolated> 0644;;;;N;GLYPH FOR ISOLATE ARABIC LAM;;;; +FEDE;ARABIC LETTER LAM FINAL FORM;Lo;0;AL;<final> 0644;;;;N;GLYPH FOR FINAL ARABIC LAM;;;; +FEDF;ARABIC LETTER LAM INITIAL FORM;Lo;0;AL;<initial> 0644;;;;N;GLYPH FOR INITIAL ARABIC LAM;;;; +FEE0;ARABIC LETTER LAM MEDIAL FORM;Lo;0;AL;<medial> 0644;;;;N;GLYPH FOR MEDIAL ARABIC LAM;;;; +FEE1;ARABIC LETTER MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645;;;;N;GLYPH FOR ISOLATE ARABIC MEEM;;;; +FEE2;ARABIC LETTER MEEM FINAL FORM;Lo;0;AL;<final> 0645;;;;N;GLYPH FOR FINAL ARABIC MEEM;;;; +FEE3;ARABIC LETTER MEEM INITIAL FORM;Lo;0;AL;<initial> 0645;;;;N;GLYPH FOR INITIAL ARABIC MEEM;;;; +FEE4;ARABIC LETTER MEEM MEDIAL FORM;Lo;0;AL;<medial> 0645;;;;N;GLYPH FOR MEDIAL ARABIC MEEM;;;; +FEE5;ARABIC LETTER NOON ISOLATED FORM;Lo;0;AL;<isolated> 0646;;;;N;GLYPH FOR ISOLATE ARABIC NOON;;;; +FEE6;ARABIC LETTER NOON FINAL FORM;Lo;0;AL;<final> 0646;;;;N;GLYPH FOR FINAL ARABIC NOON;;;; +FEE7;ARABIC LETTER NOON INITIAL FORM;Lo;0;AL;<initial> 0646;;;;N;GLYPH FOR INITIAL ARABIC NOON;;;; +FEE8;ARABIC LETTER NOON MEDIAL FORM;Lo;0;AL;<medial> 0646;;;;N;GLYPH FOR MEDIAL ARABIC NOON;;;; +FEE9;ARABIC LETTER HEH ISOLATED FORM;Lo;0;AL;<isolated> 0647;;;;N;GLYPH FOR ISOLATE ARABIC HA;;;; +FEEA;ARABIC LETTER HEH FINAL FORM;Lo;0;AL;<final> 0647;;;;N;GLYPH FOR FINAL ARABIC HA;;;; +FEEB;ARABIC LETTER HEH INITIAL FORM;Lo;0;AL;<initial> 0647;;;;N;GLYPH FOR INITIAL ARABIC HA;;;; +FEEC;ARABIC LETTER HEH MEDIAL FORM;Lo;0;AL;<medial> 0647;;;;N;GLYPH FOR MEDIAL ARABIC HA;;;; +FEED;ARABIC LETTER WAW ISOLATED FORM;Lo;0;AL;<isolated> 0648;;;;N;GLYPH FOR ISOLATE ARABIC WAW;;;; +FEEE;ARABIC LETTER WAW FINAL FORM;Lo;0;AL;<final> 0648;;;;N;GLYPH FOR FINAL ARABIC WAW;;;; +FEEF;ARABIC LETTER ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0649;;;;N;GLYPH FOR ISOLATE ARABIC ALEF MAQSURAH;;;; +FEF0;ARABIC LETTER ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0649;;;;N;GLYPH FOR FINAL ARABIC ALEF MAQSURAH;;;; +FEF1;ARABIC LETTER YEH ISOLATED FORM;Lo;0;AL;<isolated> 064A;;;;N;GLYPH FOR ISOLATE ARABIC YA;;;; +FEF2;ARABIC LETTER YEH FINAL FORM;Lo;0;AL;<final> 064A;;;;N;GLYPH FOR FINAL ARABIC YA;;;; +FEF3;ARABIC LETTER YEH INITIAL FORM;Lo;0;AL;<initial> 064A;;;;N;GLYPH FOR INITIAL ARABIC YA;;;; +FEF4;ARABIC LETTER YEH MEDIAL FORM;Lo;0;AL;<medial> 064A;;;;N;GLYPH FOR MEDIAL ARABIC YA;;;; +FEF5;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0644 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON LIGATURE LAM ALEF;;;; +FEF6;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL;<final> 0644 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON LIGATURE LAM ALEF;;;; +FEF7;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0644 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON LIGATURE LAM ALEF;;;; +FEF8;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0644 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON LIGATURE LAM ALEF;;;; +FEF9;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM;Lo;0;AL;<isolated> 0644 0625;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH UNDER LIGATURE LAM ALEF;;;; +FEFA;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM;Lo;0;AL;<final> 0644 0625;;;;N;GLYPH FOR FINAL ARABIC HAMZAH UNDER LIGATURE LAM ALEF;;;; +FEFB;ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0644 0627;;;;N;GLYPH FOR ISOLATE ARABIC LIGATURE LAM ALEF;;;; +FEFC;ARABIC LIGATURE LAM WITH ALEF FINAL FORM;Lo;0;AL;<final> 0644 0627;;;;N;GLYPH FOR FINAL ARABIC LIGATURE LAM ALEF;;;; +FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; +FF01;FULLWIDTH EXCLAMATION MARK;Po;0;ON;<wide> 0021;;;;N;;;;; +FF02;FULLWIDTH QUOTATION MARK;Po;0;ON;<wide> 0022;;;;N;;;;; +FF03;FULLWIDTH NUMBER SIGN;Po;0;ET;<wide> 0023;;;;N;;;;; +FF04;FULLWIDTH DOLLAR SIGN;Sc;0;ET;<wide> 0024;;;;N;;;;; +FF05;FULLWIDTH PERCENT SIGN;Po;0;ET;<wide> 0025;;;;N;;;;; +FF06;FULLWIDTH AMPERSAND;Po;0;ON;<wide> 0026;;;;N;;;;; +FF07;FULLWIDTH APOSTROPHE;Po;0;ON;<wide> 0027;;;;N;;;;; +FF08;FULLWIDTH LEFT PARENTHESIS;Ps;0;ON;<wide> 0028;;;;Y;FULLWIDTH OPENING PARENTHESIS;;;; +FF09;FULLWIDTH RIGHT PARENTHESIS;Pe;0;ON;<wide> 0029;;;;Y;FULLWIDTH CLOSING PARENTHESIS;;;; +FF0A;FULLWIDTH ASTERISK;Po;0;ON;<wide> 002A;;;;N;;;;; +FF0B;FULLWIDTH PLUS SIGN;Sm;0;ES;<wide> 002B;;;;N;;;;; +FF0C;FULLWIDTH COMMA;Po;0;CS;<wide> 002C;;;;N;;;;; +FF0D;FULLWIDTH HYPHEN-MINUS;Pd;0;ES;<wide> 002D;;;;N;;;;; +FF0E;FULLWIDTH FULL STOP;Po;0;CS;<wide> 002E;;;;N;FULLWIDTH PERIOD;;;; +FF0F;FULLWIDTH SOLIDUS;Po;0;CS;<wide> 002F;;;;N;FULLWIDTH SLASH;;;; +FF10;FULLWIDTH DIGIT ZERO;Nd;0;EN;<wide> 0030;0;0;0;N;;;;; +FF11;FULLWIDTH DIGIT ONE;Nd;0;EN;<wide> 0031;1;1;1;N;;;;; +FF12;FULLWIDTH DIGIT TWO;Nd;0;EN;<wide> 0032;2;2;2;N;;;;; +FF13;FULLWIDTH DIGIT THREE;Nd;0;EN;<wide> 0033;3;3;3;N;;;;; +FF14;FULLWIDTH DIGIT FOUR;Nd;0;EN;<wide> 0034;4;4;4;N;;;;; +FF15;FULLWIDTH DIGIT FIVE;Nd;0;EN;<wide> 0035;5;5;5;N;;;;; +FF16;FULLWIDTH DIGIT SIX;Nd;0;EN;<wide> 0036;6;6;6;N;;;;; +FF17;FULLWIDTH DIGIT SEVEN;Nd;0;EN;<wide> 0037;7;7;7;N;;;;; +FF18;FULLWIDTH DIGIT EIGHT;Nd;0;EN;<wide> 0038;8;8;8;N;;;;; +FF19;FULLWIDTH DIGIT NINE;Nd;0;EN;<wide> 0039;9;9;9;N;;;;; +FF1A;FULLWIDTH COLON;Po;0;CS;<wide> 003A;;;;N;;;;; +FF1B;FULLWIDTH SEMICOLON;Po;0;ON;<wide> 003B;;;;N;;;;; +FF1C;FULLWIDTH LESS-THAN SIGN;Sm;0;ON;<wide> 003C;;;;Y;;;;; +FF1D;FULLWIDTH EQUALS SIGN;Sm;0;ON;<wide> 003D;;;;N;;;;; +FF1E;FULLWIDTH GREATER-THAN SIGN;Sm;0;ON;<wide> 003E;;;;Y;;;;; +FF1F;FULLWIDTH QUESTION MARK;Po;0;ON;<wide> 003F;;;;N;;;;; +FF20;FULLWIDTH COMMERCIAL AT;Po;0;ON;<wide> 0040;;;;N;;;;; +FF21;FULLWIDTH LATIN CAPITAL LETTER A;Lu;0;L;<wide> 0041;;;;N;;;;FF41; +FF22;FULLWIDTH LATIN CAPITAL LETTER B;Lu;0;L;<wide> 0042;;;;N;;;;FF42; +FF23;FULLWIDTH LATIN CAPITAL LETTER C;Lu;0;L;<wide> 0043;;;;N;;;;FF43; +FF24;FULLWIDTH LATIN CAPITAL LETTER D;Lu;0;L;<wide> 0044;;;;N;;;;FF44; +FF25;FULLWIDTH LATIN CAPITAL LETTER E;Lu;0;L;<wide> 0045;;;;N;;;;FF45; +FF26;FULLWIDTH LATIN CAPITAL LETTER F;Lu;0;L;<wide> 0046;;;;N;;;;FF46; +FF27;FULLWIDTH LATIN CAPITAL LETTER G;Lu;0;L;<wide> 0047;;;;N;;;;FF47; +FF28;FULLWIDTH LATIN CAPITAL LETTER H;Lu;0;L;<wide> 0048;;;;N;;;;FF48; +FF29;FULLWIDTH LATIN CAPITAL LETTER I;Lu;0;L;<wide> 0049;;;;N;;;;FF49; +FF2A;FULLWIDTH LATIN CAPITAL LETTER J;Lu;0;L;<wide> 004A;;;;N;;;;FF4A; +FF2B;FULLWIDTH LATIN CAPITAL LETTER K;Lu;0;L;<wide> 004B;;;;N;;;;FF4B; +FF2C;FULLWIDTH LATIN CAPITAL LETTER L;Lu;0;L;<wide> 004C;;;;N;;;;FF4C; +FF2D;FULLWIDTH LATIN CAPITAL LETTER M;Lu;0;L;<wide> 004D;;;;N;;;;FF4D; +FF2E;FULLWIDTH LATIN CAPITAL LETTER N;Lu;0;L;<wide> 004E;;;;N;;;;FF4E; +FF2F;FULLWIDTH LATIN CAPITAL LETTER O;Lu;0;L;<wide> 004F;;;;N;;;;FF4F; +FF30;FULLWIDTH LATIN CAPITAL LETTER P;Lu;0;L;<wide> 0050;;;;N;;;;FF50; +FF31;FULLWIDTH LATIN CAPITAL LETTER Q;Lu;0;L;<wide> 0051;;;;N;;;;FF51; +FF32;FULLWIDTH LATIN CAPITAL LETTER R;Lu;0;L;<wide> 0052;;;;N;;;;FF52; +FF33;FULLWIDTH LATIN CAPITAL LETTER S;Lu;0;L;<wide> 0053;;;;N;;;;FF53; +FF34;FULLWIDTH LATIN CAPITAL LETTER T;Lu;0;L;<wide> 0054;;;;N;;;;FF54; +FF35;FULLWIDTH LATIN CAPITAL LETTER U;Lu;0;L;<wide> 0055;;;;N;;;;FF55; +FF36;FULLWIDTH LATIN CAPITAL LETTER V;Lu;0;L;<wide> 0056;;;;N;;;;FF56; +FF37;FULLWIDTH LATIN CAPITAL LETTER W;Lu;0;L;<wide> 0057;;;;N;;;;FF57; +FF38;FULLWIDTH LATIN CAPITAL LETTER X;Lu;0;L;<wide> 0058;;;;N;;;;FF58; +FF39;FULLWIDTH LATIN CAPITAL LETTER Y;Lu;0;L;<wide> 0059;;;;N;;;;FF59; +FF3A;FULLWIDTH LATIN CAPITAL LETTER Z;Lu;0;L;<wide> 005A;;;;N;;;;FF5A; +FF3B;FULLWIDTH LEFT SQUARE BRACKET;Ps;0;ON;<wide> 005B;;;;Y;FULLWIDTH OPENING SQUARE BRACKET;;;; +FF3C;FULLWIDTH REVERSE SOLIDUS;Po;0;ON;<wide> 005C;;;;N;FULLWIDTH BACKSLASH;;;; +FF3D;FULLWIDTH RIGHT SQUARE BRACKET;Pe;0;ON;<wide> 005D;;;;Y;FULLWIDTH CLOSING SQUARE BRACKET;;;; +FF3E;FULLWIDTH CIRCUMFLEX ACCENT;Sk;0;ON;<wide> 005E;;;;N;FULLWIDTH SPACING CIRCUMFLEX;;;; +FF3F;FULLWIDTH LOW LINE;Pc;0;ON;<wide> 005F;;;;N;FULLWIDTH SPACING UNDERSCORE;;;; +FF40;FULLWIDTH GRAVE ACCENT;Sk;0;ON;<wide> 0060;;;;N;FULLWIDTH SPACING GRAVE;;;; +FF41;FULLWIDTH LATIN SMALL LETTER A;Ll;0;L;<wide> 0061;;;;N;;;FF21;;FF21 +FF42;FULLWIDTH LATIN SMALL LETTER B;Ll;0;L;<wide> 0062;;;;N;;;FF22;;FF22 +FF43;FULLWIDTH LATIN SMALL LETTER C;Ll;0;L;<wide> 0063;;;;N;;;FF23;;FF23 +FF44;FULLWIDTH LATIN SMALL LETTER D;Ll;0;L;<wide> 0064;;;;N;;;FF24;;FF24 +FF45;FULLWIDTH LATIN SMALL LETTER E;Ll;0;L;<wide> 0065;;;;N;;;FF25;;FF25 +FF46;FULLWIDTH LATIN SMALL LETTER F;Ll;0;L;<wide> 0066;;;;N;;;FF26;;FF26 +FF47;FULLWIDTH LATIN SMALL LETTER G;Ll;0;L;<wide> 0067;;;;N;;;FF27;;FF27 +FF48;FULLWIDTH LATIN SMALL LETTER H;Ll;0;L;<wide> 0068;;;;N;;;FF28;;FF28 +FF49;FULLWIDTH LATIN SMALL LETTER I;Ll;0;L;<wide> 0069;;;;N;;;FF29;;FF29 +FF4A;FULLWIDTH LATIN SMALL LETTER J;Ll;0;L;<wide> 006A;;;;N;;;FF2A;;FF2A +FF4B;FULLWIDTH LATIN SMALL LETTER K;Ll;0;L;<wide> 006B;;;;N;;;FF2B;;FF2B +FF4C;FULLWIDTH LATIN SMALL LETTER L;Ll;0;L;<wide> 006C;;;;N;;;FF2C;;FF2C +FF4D;FULLWIDTH LATIN SMALL LETTER M;Ll;0;L;<wide> 006D;;;;N;;;FF2D;;FF2D +FF4E;FULLWIDTH LATIN SMALL LETTER N;Ll;0;L;<wide> 006E;;;;N;;;FF2E;;FF2E +FF4F;FULLWIDTH LATIN SMALL LETTER O;Ll;0;L;<wide> 006F;;;;N;;;FF2F;;FF2F +FF50;FULLWIDTH LATIN SMALL LETTER P;Ll;0;L;<wide> 0070;;;;N;;;FF30;;FF30 +FF51;FULLWIDTH LATIN SMALL LETTER Q;Ll;0;L;<wide> 0071;;;;N;;;FF31;;FF31 +FF52;FULLWIDTH LATIN SMALL LETTER R;Ll;0;L;<wide> 0072;;;;N;;;FF32;;FF32 +FF53;FULLWIDTH LATIN SMALL LETTER S;Ll;0;L;<wide> 0073;;;;N;;;FF33;;FF33 +FF54;FULLWIDTH LATIN SMALL LETTER T;Ll;0;L;<wide> 0074;;;;N;;;FF34;;FF34 +FF55;FULLWIDTH LATIN SMALL LETTER U;Ll;0;L;<wide> 0075;;;;N;;;FF35;;FF35 +FF56;FULLWIDTH LATIN SMALL LETTER V;Ll;0;L;<wide> 0076;;;;N;;;FF36;;FF36 +FF57;FULLWIDTH LATIN SMALL LETTER W;Ll;0;L;<wide> 0077;;;;N;;;FF37;;FF37 +FF58;FULLWIDTH LATIN SMALL LETTER X;Ll;0;L;<wide> 0078;;;;N;;;FF38;;FF38 +FF59;FULLWIDTH LATIN SMALL LETTER Y;Ll;0;L;<wide> 0079;;;;N;;;FF39;;FF39 +FF5A;FULLWIDTH LATIN SMALL LETTER Z;Ll;0;L;<wide> 007A;;;;N;;;FF3A;;FF3A +FF5B;FULLWIDTH LEFT CURLY BRACKET;Ps;0;ON;<wide> 007B;;;;Y;FULLWIDTH OPENING CURLY BRACKET;;;; +FF5C;FULLWIDTH VERTICAL LINE;Sm;0;ON;<wide> 007C;;;;N;FULLWIDTH VERTICAL BAR;;;; +FF5D;FULLWIDTH RIGHT CURLY BRACKET;Pe;0;ON;<wide> 007D;;;;Y;FULLWIDTH CLOSING CURLY BRACKET;;;; +FF5E;FULLWIDTH TILDE;Sm;0;ON;<wide> 007E;;;;N;FULLWIDTH SPACING TILDE;;;; +FF5F;FULLWIDTH LEFT WHITE PARENTHESIS;Ps;0;ON;<wide> 2985;;;;Y;;;;; +FF60;FULLWIDTH RIGHT WHITE PARENTHESIS;Pe;0;ON;<wide> 2986;;;;Y;;;;; +FF61;HALFWIDTH IDEOGRAPHIC FULL STOP;Po;0;ON;<narrow> 3002;;;;N;HALFWIDTH IDEOGRAPHIC PERIOD;;;; +FF62;HALFWIDTH LEFT CORNER BRACKET;Ps;0;ON;<narrow> 300C;;;;Y;HALFWIDTH OPENING CORNER BRACKET;;;; +FF63;HALFWIDTH RIGHT CORNER BRACKET;Pe;0;ON;<narrow> 300D;;;;Y;HALFWIDTH CLOSING CORNER BRACKET;;;; +FF64;HALFWIDTH IDEOGRAPHIC COMMA;Po;0;ON;<narrow> 3001;;;;N;;;;; +FF65;HALFWIDTH KATAKANA MIDDLE DOT;Po;0;ON;<narrow> 30FB;;;;N;;;;; +FF66;HALFWIDTH KATAKANA LETTER WO;Lo;0;L;<narrow> 30F2;;;;N;;;;; +FF67;HALFWIDTH KATAKANA LETTER SMALL A;Lo;0;L;<narrow> 30A1;;;;N;;;;; +FF68;HALFWIDTH KATAKANA LETTER SMALL I;Lo;0;L;<narrow> 30A3;;;;N;;;;; +FF69;HALFWIDTH KATAKANA LETTER SMALL U;Lo;0;L;<narrow> 30A5;;;;N;;;;; +FF6A;HALFWIDTH KATAKANA LETTER SMALL E;Lo;0;L;<narrow> 30A7;;;;N;;;;; +FF6B;HALFWIDTH KATAKANA LETTER SMALL O;Lo;0;L;<narrow> 30A9;;;;N;;;;; +FF6C;HALFWIDTH KATAKANA LETTER SMALL YA;Lo;0;L;<narrow> 30E3;;;;N;;;;; +FF6D;HALFWIDTH KATAKANA LETTER SMALL YU;Lo;0;L;<narrow> 30E5;;;;N;;;;; +FF6E;HALFWIDTH KATAKANA LETTER SMALL YO;Lo;0;L;<narrow> 30E7;;;;N;;;;; +FF6F;HALFWIDTH KATAKANA LETTER SMALL TU;Lo;0;L;<narrow> 30C3;;;;N;;;;; +FF70;HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L;<narrow> 30FC;;;;N;;;;; +FF71;HALFWIDTH KATAKANA LETTER A;Lo;0;L;<narrow> 30A2;;;;N;;;;; +FF72;HALFWIDTH KATAKANA LETTER I;Lo;0;L;<narrow> 30A4;;;;N;;;;; +FF73;HALFWIDTH KATAKANA LETTER U;Lo;0;L;<narrow> 30A6;;;;N;;;;; +FF74;HALFWIDTH KATAKANA LETTER E;Lo;0;L;<narrow> 30A8;;;;N;;;;; +FF75;HALFWIDTH KATAKANA LETTER O;Lo;0;L;<narrow> 30AA;;;;N;;;;; +FF76;HALFWIDTH KATAKANA LETTER KA;Lo;0;L;<narrow> 30AB;;;;N;;;;; +FF77;HALFWIDTH KATAKANA LETTER KI;Lo;0;L;<narrow> 30AD;;;;N;;;;; +FF78;HALFWIDTH KATAKANA LETTER KU;Lo;0;L;<narrow> 30AF;;;;N;;;;; +FF79;HALFWIDTH KATAKANA LETTER KE;Lo;0;L;<narrow> 30B1;;;;N;;;;; +FF7A;HALFWIDTH KATAKANA LETTER KO;Lo;0;L;<narrow> 30B3;;;;N;;;;; +FF7B;HALFWIDTH KATAKANA LETTER SA;Lo;0;L;<narrow> 30B5;;;;N;;;;; +FF7C;HALFWIDTH KATAKANA LETTER SI;Lo;0;L;<narrow> 30B7;;;;N;;;;; +FF7D;HALFWIDTH KATAKANA LETTER SU;Lo;0;L;<narrow> 30B9;;;;N;;;;; +FF7E;HALFWIDTH KATAKANA LETTER SE;Lo;0;L;<narrow> 30BB;;;;N;;;;; +FF7F;HALFWIDTH KATAKANA LETTER SO;Lo;0;L;<narrow> 30BD;;;;N;;;;; +FF80;HALFWIDTH KATAKANA LETTER TA;Lo;0;L;<narrow> 30BF;;;;N;;;;; +FF81;HALFWIDTH KATAKANA LETTER TI;Lo;0;L;<narrow> 30C1;;;;N;;;;; +FF82;HALFWIDTH KATAKANA LETTER TU;Lo;0;L;<narrow> 30C4;;;;N;;;;; +FF83;HALFWIDTH KATAKANA LETTER TE;Lo;0;L;<narrow> 30C6;;;;N;;;;; +FF84;HALFWIDTH KATAKANA LETTER TO;Lo;0;L;<narrow> 30C8;;;;N;;;;; +FF85;HALFWIDTH KATAKANA LETTER NA;Lo;0;L;<narrow> 30CA;;;;N;;;;; +FF86;HALFWIDTH KATAKANA LETTER NI;Lo;0;L;<narrow> 30CB;;;;N;;;;; +FF87;HALFWIDTH KATAKANA LETTER NU;Lo;0;L;<narrow> 30CC;;;;N;;;;; +FF88;HALFWIDTH KATAKANA LETTER NE;Lo;0;L;<narrow> 30CD;;;;N;;;;; +FF89;HALFWIDTH KATAKANA LETTER NO;Lo;0;L;<narrow> 30CE;;;;N;;;;; +FF8A;HALFWIDTH KATAKANA LETTER HA;Lo;0;L;<narrow> 30CF;;;;N;;;;; +FF8B;HALFWIDTH KATAKANA LETTER HI;Lo;0;L;<narrow> 30D2;;;;N;;;;; +FF8C;HALFWIDTH KATAKANA LETTER HU;Lo;0;L;<narrow> 30D5;;;;N;;;;; +FF8D;HALFWIDTH KATAKANA LETTER HE;Lo;0;L;<narrow> 30D8;;;;N;;;;; +FF8E;HALFWIDTH KATAKANA LETTER HO;Lo;0;L;<narrow> 30DB;;;;N;;;;; +FF8F;HALFWIDTH KATAKANA LETTER MA;Lo;0;L;<narrow> 30DE;;;;N;;;;; +FF90;HALFWIDTH KATAKANA LETTER MI;Lo;0;L;<narrow> 30DF;;;;N;;;;; +FF91;HALFWIDTH KATAKANA LETTER MU;Lo;0;L;<narrow> 30E0;;;;N;;;;; +FF92;HALFWIDTH KATAKANA LETTER ME;Lo;0;L;<narrow> 30E1;;;;N;;;;; +FF93;HALFWIDTH KATAKANA LETTER MO;Lo;0;L;<narrow> 30E2;;;;N;;;;; +FF94;HALFWIDTH KATAKANA LETTER YA;Lo;0;L;<narrow> 30E4;;;;N;;;;; +FF95;HALFWIDTH KATAKANA LETTER YU;Lo;0;L;<narrow> 30E6;;;;N;;;;; +FF96;HALFWIDTH KATAKANA LETTER YO;Lo;0;L;<narrow> 30E8;;;;N;;;;; +FF97;HALFWIDTH KATAKANA LETTER RA;Lo;0;L;<narrow> 30E9;;;;N;;;;; +FF98;HALFWIDTH KATAKANA LETTER RI;Lo;0;L;<narrow> 30EA;;;;N;;;;; +FF99;HALFWIDTH KATAKANA LETTER RU;Lo;0;L;<narrow> 30EB;;;;N;;;;; +FF9A;HALFWIDTH KATAKANA LETTER RE;Lo;0;L;<narrow> 30EC;;;;N;;;;; +FF9B;HALFWIDTH KATAKANA LETTER RO;Lo;0;L;<narrow> 30ED;;;;N;;;;; +FF9C;HALFWIDTH KATAKANA LETTER WA;Lo;0;L;<narrow> 30EF;;;;N;;;;; +FF9D;HALFWIDTH KATAKANA LETTER N;Lo;0;L;<narrow> 30F3;;;;N;;;;; +FF9E;HALFWIDTH KATAKANA VOICED SOUND MARK;Lm;0;L;<narrow> 3099;;;;N;;;;; +FF9F;HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK;Lm;0;L;<narrow> 309A;;;;N;;;;; +FFA0;HALFWIDTH HANGUL FILLER;Lo;0;L;<narrow> 3164;;;;N;HALFWIDTH HANGUL CAE OM;;;; +FFA1;HALFWIDTH HANGUL LETTER KIYEOK;Lo;0;L;<narrow> 3131;;;;N;HALFWIDTH HANGUL LETTER GIYEOG;;;; +FFA2;HALFWIDTH HANGUL LETTER SSANGKIYEOK;Lo;0;L;<narrow> 3132;;;;N;HALFWIDTH HANGUL LETTER SSANG GIYEOG;;;; +FFA3;HALFWIDTH HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<narrow> 3133;;;;N;HALFWIDTH HANGUL LETTER GIYEOG SIOS;;;; +FFA4;HALFWIDTH HANGUL LETTER NIEUN;Lo;0;L;<narrow> 3134;;;;N;;;;; +FFA5;HALFWIDTH HANGUL LETTER NIEUN-CIEUC;Lo;0;L;<narrow> 3135;;;;N;HALFWIDTH HANGUL LETTER NIEUN JIEUJ;;;; +FFA6;HALFWIDTH HANGUL LETTER NIEUN-HIEUH;Lo;0;L;<narrow> 3136;;;;N;HALFWIDTH HANGUL LETTER NIEUN HIEUH;;;; +FFA7;HALFWIDTH HANGUL LETTER TIKEUT;Lo;0;L;<narrow> 3137;;;;N;HALFWIDTH HANGUL LETTER DIGEUD;;;; +FFA8;HALFWIDTH HANGUL LETTER SSANGTIKEUT;Lo;0;L;<narrow> 3138;;;;N;HALFWIDTH HANGUL LETTER SSANG DIGEUD;;;; +FFA9;HALFWIDTH HANGUL LETTER RIEUL;Lo;0;L;<narrow> 3139;;;;N;HALFWIDTH HANGUL LETTER LIEUL;;;; +FFAA;HALFWIDTH HANGUL LETTER RIEUL-KIYEOK;Lo;0;L;<narrow> 313A;;;;N;HALFWIDTH HANGUL LETTER LIEUL GIYEOG;;;; +FFAB;HALFWIDTH HANGUL LETTER RIEUL-MIEUM;Lo;0;L;<narrow> 313B;;;;N;HALFWIDTH HANGUL LETTER LIEUL MIEUM;;;; +FFAC;HALFWIDTH HANGUL LETTER RIEUL-PIEUP;Lo;0;L;<narrow> 313C;;;;N;HALFWIDTH HANGUL LETTER LIEUL BIEUB;;;; +FFAD;HALFWIDTH HANGUL LETTER RIEUL-SIOS;Lo;0;L;<narrow> 313D;;;;N;HALFWIDTH HANGUL LETTER LIEUL SIOS;;;; +FFAE;HALFWIDTH HANGUL LETTER RIEUL-THIEUTH;Lo;0;L;<narrow> 313E;;;;N;HALFWIDTH HANGUL LETTER LIEUL TIEUT;;;; +FFAF;HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH;Lo;0;L;<narrow> 313F;;;;N;HALFWIDTH HANGUL LETTER LIEUL PIEUP;;;; +FFB0;HALFWIDTH HANGUL LETTER RIEUL-HIEUH;Lo;0;L;<narrow> 3140;;;;N;HALFWIDTH HANGUL LETTER LIEUL HIEUH;;;; +FFB1;HALFWIDTH HANGUL LETTER MIEUM;Lo;0;L;<narrow> 3141;;;;N;;;;; +FFB2;HALFWIDTH HANGUL LETTER PIEUP;Lo;0;L;<narrow> 3142;;;;N;HALFWIDTH HANGUL LETTER BIEUB;;;; +FFB3;HALFWIDTH HANGUL LETTER SSANGPIEUP;Lo;0;L;<narrow> 3143;;;;N;HALFWIDTH HANGUL LETTER SSANG BIEUB;;;; +FFB4;HALFWIDTH HANGUL LETTER PIEUP-SIOS;Lo;0;L;<narrow> 3144;;;;N;HALFWIDTH HANGUL LETTER BIEUB SIOS;;;; +FFB5;HALFWIDTH HANGUL LETTER SIOS;Lo;0;L;<narrow> 3145;;;;N;;;;; +FFB6;HALFWIDTH HANGUL LETTER SSANGSIOS;Lo;0;L;<narrow> 3146;;;;N;HALFWIDTH HANGUL LETTER SSANG SIOS;;;; +FFB7;HALFWIDTH HANGUL LETTER IEUNG;Lo;0;L;<narrow> 3147;;;;N;;;;; +FFB8;HALFWIDTH HANGUL LETTER CIEUC;Lo;0;L;<narrow> 3148;;;;N;HALFWIDTH HANGUL LETTER JIEUJ;;;; +FFB9;HALFWIDTH HANGUL LETTER SSANGCIEUC;Lo;0;L;<narrow> 3149;;;;N;HALFWIDTH HANGUL LETTER SSANG JIEUJ;;;; +FFBA;HALFWIDTH HANGUL LETTER CHIEUCH;Lo;0;L;<narrow> 314A;;;;N;HALFWIDTH HANGUL LETTER CIEUC;;;; +FFBB;HALFWIDTH HANGUL LETTER KHIEUKH;Lo;0;L;<narrow> 314B;;;;N;HALFWIDTH HANGUL LETTER KIYEOK;;;; +FFBC;HALFWIDTH HANGUL LETTER THIEUTH;Lo;0;L;<narrow> 314C;;;;N;HALFWIDTH HANGUL LETTER TIEUT;;;; +FFBD;HALFWIDTH HANGUL LETTER PHIEUPH;Lo;0;L;<narrow> 314D;;;;N;HALFWIDTH HANGUL LETTER PIEUP;;;; +FFBE;HALFWIDTH HANGUL LETTER HIEUH;Lo;0;L;<narrow> 314E;;;;N;;;;; +FFC2;HALFWIDTH HANGUL LETTER A;Lo;0;L;<narrow> 314F;;;;N;;;;; +FFC3;HALFWIDTH HANGUL LETTER AE;Lo;0;L;<narrow> 3150;;;;N;;;;; +FFC4;HALFWIDTH HANGUL LETTER YA;Lo;0;L;<narrow> 3151;;;;N;;;;; +FFC5;HALFWIDTH HANGUL LETTER YAE;Lo;0;L;<narrow> 3152;;;;N;;;;; +FFC6;HALFWIDTH HANGUL LETTER EO;Lo;0;L;<narrow> 3153;;;;N;;;;; +FFC7;HALFWIDTH HANGUL LETTER E;Lo;0;L;<narrow> 3154;;;;N;;;;; +FFCA;HALFWIDTH HANGUL LETTER YEO;Lo;0;L;<narrow> 3155;;;;N;;;;; +FFCB;HALFWIDTH HANGUL LETTER YE;Lo;0;L;<narrow> 3156;;;;N;;;;; +FFCC;HALFWIDTH HANGUL LETTER O;Lo;0;L;<narrow> 3157;;;;N;;;;; +FFCD;HALFWIDTH HANGUL LETTER WA;Lo;0;L;<narrow> 3158;;;;N;;;;; +FFCE;HALFWIDTH HANGUL LETTER WAE;Lo;0;L;<narrow> 3159;;;;N;;;;; +FFCF;HALFWIDTH HANGUL LETTER OE;Lo;0;L;<narrow> 315A;;;;N;;;;; +FFD2;HALFWIDTH HANGUL LETTER YO;Lo;0;L;<narrow> 315B;;;;N;;;;; +FFD3;HALFWIDTH HANGUL LETTER U;Lo;0;L;<narrow> 315C;;;;N;;;;; +FFD4;HALFWIDTH HANGUL LETTER WEO;Lo;0;L;<narrow> 315D;;;;N;;;;; +FFD5;HALFWIDTH HANGUL LETTER WE;Lo;0;L;<narrow> 315E;;;;N;;;;; +FFD6;HALFWIDTH HANGUL LETTER WI;Lo;0;L;<narrow> 315F;;;;N;;;;; +FFD7;HALFWIDTH HANGUL LETTER YU;Lo;0;L;<narrow> 3160;;;;N;;;;; +FFDA;HALFWIDTH HANGUL LETTER EU;Lo;0;L;<narrow> 3161;;;;N;;;;; +FFDB;HALFWIDTH HANGUL LETTER YI;Lo;0;L;<narrow> 3162;;;;N;;;;; +FFDC;HALFWIDTH HANGUL LETTER I;Lo;0;L;<narrow> 3163;;;;N;;;;; +FFE0;FULLWIDTH CENT SIGN;Sc;0;ET;<wide> 00A2;;;;N;;;;; +FFE1;FULLWIDTH POUND SIGN;Sc;0;ET;<wide> 00A3;;;;N;;;;; +FFE2;FULLWIDTH NOT SIGN;Sm;0;ON;<wide> 00AC;;;;N;;;;; +FFE3;FULLWIDTH MACRON;Sk;0;ON;<wide> 00AF;;;;N;FULLWIDTH SPACING MACRON;;;; +FFE4;FULLWIDTH BROKEN BAR;So;0;ON;<wide> 00A6;;;;N;FULLWIDTH BROKEN VERTICAL BAR;;;; +FFE5;FULLWIDTH YEN SIGN;Sc;0;ET;<wide> 00A5;;;;N;;;;; +FFE6;FULLWIDTH WON SIGN;Sc;0;ET;<wide> 20A9;;;;N;;;;; +FFE8;HALFWIDTH FORMS LIGHT VERTICAL;So;0;ON;<narrow> 2502;;;;N;;;;; +FFE9;HALFWIDTH LEFTWARDS ARROW;Sm;0;ON;<narrow> 2190;;;;N;;;;; +FFEA;HALFWIDTH UPWARDS ARROW;Sm;0;ON;<narrow> 2191;;;;N;;;;; +FFEB;HALFWIDTH RIGHTWARDS ARROW;Sm;0;ON;<narrow> 2192;;;;N;;;;; +FFEC;HALFWIDTH DOWNWARDS ARROW;Sm;0;ON;<narrow> 2193;;;;N;;;;; +FFED;HALFWIDTH BLACK SQUARE;So;0;ON;<narrow> 25A0;;;;N;;;;; +FFEE;HALFWIDTH WHITE CIRCLE;So;0;ON;<narrow> 25CB;;;;N;;;;; +FFF9;INTERLINEAR ANNOTATION ANCHOR;Cf;0;ON;;;;;N;;;;; +FFFA;INTERLINEAR ANNOTATION SEPARATOR;Cf;0;ON;;;;;N;;;;; +FFFB;INTERLINEAR ANNOTATION TERMINATOR;Cf;0;ON;;;;;N;;;;; +FFFC;OBJECT REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; +FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; +10000;LINEAR B SYLLABLE B008 A;Lo;0;L;;;;;N;;;;; +10001;LINEAR B SYLLABLE B038 E;Lo;0;L;;;;;N;;;;; +10002;LINEAR B SYLLABLE B028 I;Lo;0;L;;;;;N;;;;; +10003;LINEAR B SYLLABLE B061 O;Lo;0;L;;;;;N;;;;; +10004;LINEAR B SYLLABLE B010 U;Lo;0;L;;;;;N;;;;; +10005;LINEAR B SYLLABLE B001 DA;Lo;0;L;;;;;N;;;;; +10006;LINEAR B SYLLABLE B045 DE;Lo;0;L;;;;;N;;;;; +10007;LINEAR B SYLLABLE B007 DI;Lo;0;L;;;;;N;;;;; +10008;LINEAR B SYLLABLE B014 DO;Lo;0;L;;;;;N;;;;; +10009;LINEAR B SYLLABLE B051 DU;Lo;0;L;;;;;N;;;;; +1000A;LINEAR B SYLLABLE B057 JA;Lo;0;L;;;;;N;;;;; +1000B;LINEAR B SYLLABLE B046 JE;Lo;0;L;;;;;N;;;;; +1000D;LINEAR B SYLLABLE B036 JO;Lo;0;L;;;;;N;;;;; +1000E;LINEAR B SYLLABLE B065 JU;Lo;0;L;;;;;N;;;;; +1000F;LINEAR B SYLLABLE B077 KA;Lo;0;L;;;;;N;;;;; +10010;LINEAR B SYLLABLE B044 KE;Lo;0;L;;;;;N;;;;; +10011;LINEAR B SYLLABLE B067 KI;Lo;0;L;;;;;N;;;;; +10012;LINEAR B SYLLABLE B070 KO;Lo;0;L;;;;;N;;;;; +10013;LINEAR B SYLLABLE B081 KU;Lo;0;L;;;;;N;;;;; +10014;LINEAR B SYLLABLE B080 MA;Lo;0;L;;;;;N;;;;; +10015;LINEAR B SYLLABLE B013 ME;Lo;0;L;;;;;N;;;;; +10016;LINEAR B SYLLABLE B073 MI;Lo;0;L;;;;;N;;;;; +10017;LINEAR B SYLLABLE B015 MO;Lo;0;L;;;;;N;;;;; +10018;LINEAR B SYLLABLE B023 MU;Lo;0;L;;;;;N;;;;; +10019;LINEAR B SYLLABLE B006 NA;Lo;0;L;;;;;N;;;;; +1001A;LINEAR B SYLLABLE B024 NE;Lo;0;L;;;;;N;;;;; +1001B;LINEAR B SYLLABLE B030 NI;Lo;0;L;;;;;N;;;;; +1001C;LINEAR B SYLLABLE B052 NO;Lo;0;L;;;;;N;;;;; +1001D;LINEAR B SYLLABLE B055 NU;Lo;0;L;;;;;N;;;;; +1001E;LINEAR B SYLLABLE B003 PA;Lo;0;L;;;;;N;;;;; +1001F;LINEAR B SYLLABLE B072 PE;Lo;0;L;;;;;N;;;;; +10020;LINEAR B SYLLABLE B039 PI;Lo;0;L;;;;;N;;;;; +10021;LINEAR B SYLLABLE B011 PO;Lo;0;L;;;;;N;;;;; +10022;LINEAR B SYLLABLE B050 PU;Lo;0;L;;;;;N;;;;; +10023;LINEAR B SYLLABLE B016 QA;Lo;0;L;;;;;N;;;;; +10024;LINEAR B SYLLABLE B078 QE;Lo;0;L;;;;;N;;;;; +10025;LINEAR B SYLLABLE B021 QI;Lo;0;L;;;;;N;;;;; +10026;LINEAR B SYLLABLE B032 QO;Lo;0;L;;;;;N;;;;; +10028;LINEAR B SYLLABLE B060 RA;Lo;0;L;;;;;N;;;;; +10029;LINEAR B SYLLABLE B027 RE;Lo;0;L;;;;;N;;;;; +1002A;LINEAR B SYLLABLE B053 RI;Lo;0;L;;;;;N;;;;; +1002B;LINEAR B SYLLABLE B002 RO;Lo;0;L;;;;;N;;;;; +1002C;LINEAR B SYLLABLE B026 RU;Lo;0;L;;;;;N;;;;; +1002D;LINEAR B SYLLABLE B031 SA;Lo;0;L;;;;;N;;;;; +1002E;LINEAR B SYLLABLE B009 SE;Lo;0;L;;;;;N;;;;; +1002F;LINEAR B SYLLABLE B041 SI;Lo;0;L;;;;;N;;;;; +10030;LINEAR B SYLLABLE B012 SO;Lo;0;L;;;;;N;;;;; +10031;LINEAR B SYLLABLE B058 SU;Lo;0;L;;;;;N;;;;; +10032;LINEAR B SYLLABLE B059 TA;Lo;0;L;;;;;N;;;;; +10033;LINEAR B SYLLABLE B004 TE;Lo;0;L;;;;;N;;;;; +10034;LINEAR B SYLLABLE B037 TI;Lo;0;L;;;;;N;;;;; +10035;LINEAR B SYLLABLE B005 TO;Lo;0;L;;;;;N;;;;; +10036;LINEAR B SYLLABLE B069 TU;Lo;0;L;;;;;N;;;;; +10037;LINEAR B SYLLABLE B054 WA;Lo;0;L;;;;;N;;;;; +10038;LINEAR B SYLLABLE B075 WE;Lo;0;L;;;;;N;;;;; +10039;LINEAR B SYLLABLE B040 WI;Lo;0;L;;;;;N;;;;; +1003A;LINEAR B SYLLABLE B042 WO;Lo;0;L;;;;;N;;;;; +1003C;LINEAR B SYLLABLE B017 ZA;Lo;0;L;;;;;N;;;;; +1003D;LINEAR B SYLLABLE B074 ZE;Lo;0;L;;;;;N;;;;; +1003F;LINEAR B SYLLABLE B020 ZO;Lo;0;L;;;;;N;;;;; +10040;LINEAR B SYLLABLE B025 A2;Lo;0;L;;;;;N;;;;; +10041;LINEAR B SYLLABLE B043 A3;Lo;0;L;;;;;N;;;;; +10042;LINEAR B SYLLABLE B085 AU;Lo;0;L;;;;;N;;;;; +10043;LINEAR B SYLLABLE B071 DWE;Lo;0;L;;;;;N;;;;; +10044;LINEAR B SYLLABLE B090 DWO;Lo;0;L;;;;;N;;;;; +10045;LINEAR B SYLLABLE B048 NWA;Lo;0;L;;;;;N;;;;; +10046;LINEAR B SYLLABLE B029 PU2;Lo;0;L;;;;;N;;;;; +10047;LINEAR B SYLLABLE B062 PTE;Lo;0;L;;;;;N;;;;; +10048;LINEAR B SYLLABLE B076 RA2;Lo;0;L;;;;;N;;;;; +10049;LINEAR B SYLLABLE B033 RA3;Lo;0;L;;;;;N;;;;; +1004A;LINEAR B SYLLABLE B068 RO2;Lo;0;L;;;;;N;;;;; +1004B;LINEAR B SYLLABLE B066 TA2;Lo;0;L;;;;;N;;;;; +1004C;LINEAR B SYLLABLE B087 TWE;Lo;0;L;;;;;N;;;;; +1004D;LINEAR B SYLLABLE B091 TWO;Lo;0;L;;;;;N;;;;; +10050;LINEAR B SYMBOL B018;Lo;0;L;;;;;N;;;;; +10051;LINEAR B SYMBOL B019;Lo;0;L;;;;;N;;;;; +10052;LINEAR B SYMBOL B022;Lo;0;L;;;;;N;;;;; +10053;LINEAR B SYMBOL B034;Lo;0;L;;;;;N;;;;; +10054;LINEAR B SYMBOL B047;Lo;0;L;;;;;N;;;;; +10055;LINEAR B SYMBOL B049;Lo;0;L;;;;;N;;;;; +10056;LINEAR B SYMBOL B056;Lo;0;L;;;;;N;;;;; +10057;LINEAR B SYMBOL B063;Lo;0;L;;;;;N;;;;; +10058;LINEAR B SYMBOL B064;Lo;0;L;;;;;N;;;;; +10059;LINEAR B SYMBOL B079;Lo;0;L;;;;;N;;;;; +1005A;LINEAR B SYMBOL B082;Lo;0;L;;;;;N;;;;; +1005B;LINEAR B SYMBOL B083;Lo;0;L;;;;;N;;;;; +1005C;LINEAR B SYMBOL B086;Lo;0;L;;;;;N;;;;; +1005D;LINEAR B SYMBOL B089;Lo;0;L;;;;;N;;;;; +10080;LINEAR B IDEOGRAM B100 MAN;Lo;0;L;;;;;N;;;;; +10081;LINEAR B IDEOGRAM B102 WOMAN;Lo;0;L;;;;;N;;;;; +10082;LINEAR B IDEOGRAM B104 DEER;Lo;0;L;;;;;N;;;;; +10083;LINEAR B IDEOGRAM B105 EQUID;Lo;0;L;;;;;N;;;;; +10084;LINEAR B IDEOGRAM B105F MARE;Lo;0;L;;;;;N;;;;; +10085;LINEAR B IDEOGRAM B105M STALLION;Lo;0;L;;;;;N;;;;; +10086;LINEAR B IDEOGRAM B106F EWE;Lo;0;L;;;;;N;;;;; +10087;LINEAR B IDEOGRAM B106M RAM;Lo;0;L;;;;;N;;;;; +10088;LINEAR B IDEOGRAM B107F SHE-GOAT;Lo;0;L;;;;;N;;;;; +10089;LINEAR B IDEOGRAM B107M HE-GOAT;Lo;0;L;;;;;N;;;;; +1008A;LINEAR B IDEOGRAM B108F SOW;Lo;0;L;;;;;N;;;;; +1008B;LINEAR B IDEOGRAM B108M BOAR;Lo;0;L;;;;;N;;;;; +1008C;LINEAR B IDEOGRAM B109F COW;Lo;0;L;;;;;N;;;;; +1008D;LINEAR B IDEOGRAM B109M BULL;Lo;0;L;;;;;N;;;;; +1008E;LINEAR B IDEOGRAM B120 WHEAT;Lo;0;L;;;;;N;;;;; +1008F;LINEAR B IDEOGRAM B121 BARLEY;Lo;0;L;;;;;N;;;;; +10090;LINEAR B IDEOGRAM B122 OLIVE;Lo;0;L;;;;;N;;;;; +10091;LINEAR B IDEOGRAM B123 SPICE;Lo;0;L;;;;;N;;;;; +10092;LINEAR B IDEOGRAM B125 CYPERUS;Lo;0;L;;;;;N;;;;; +10093;LINEAR B MONOGRAM B127 KAPO;Lo;0;L;;;;;N;;;;; +10094;LINEAR B MONOGRAM B128 KANAKO;Lo;0;L;;;;;N;;;;; +10095;LINEAR B IDEOGRAM B130 OIL;Lo;0;L;;;;;N;;;;; +10096;LINEAR B IDEOGRAM B131 WINE;Lo;0;L;;;;;N;;;;; +10097;LINEAR B IDEOGRAM B132;Lo;0;L;;;;;N;;;;; +10098;LINEAR B MONOGRAM B133 AREPA;Lo;0;L;;;;;N;;;;; +10099;LINEAR B MONOGRAM B135 MERI;Lo;0;L;;;;;N;;;;; +1009A;LINEAR B IDEOGRAM B140 BRONZE;Lo;0;L;;;;;N;;;;; +1009B;LINEAR B IDEOGRAM B141 GOLD;Lo;0;L;;;;;N;;;;; +1009C;LINEAR B IDEOGRAM B142;Lo;0;L;;;;;N;;;;; +1009D;LINEAR B IDEOGRAM B145 WOOL;Lo;0;L;;;;;N;;;;; +1009E;LINEAR B IDEOGRAM B146;Lo;0;L;;;;;N;;;;; +1009F;LINEAR B IDEOGRAM B150;Lo;0;L;;;;;N;;;;; +100A0;LINEAR B IDEOGRAM B151 HORN;Lo;0;L;;;;;N;;;;; +100A1;LINEAR B IDEOGRAM B152;Lo;0;L;;;;;N;;;;; +100A2;LINEAR B IDEOGRAM B153;Lo;0;L;;;;;N;;;;; +100A3;LINEAR B IDEOGRAM B154;Lo;0;L;;;;;N;;;;; +100A4;LINEAR B MONOGRAM B156 TURO2;Lo;0;L;;;;;N;;;;; +100A5;LINEAR B IDEOGRAM B157;Lo;0;L;;;;;N;;;;; +100A6;LINEAR B IDEOGRAM B158;Lo;0;L;;;;;N;;;;; +100A7;LINEAR B IDEOGRAM B159 CLOTH;Lo;0;L;;;;;N;;;;; +100A8;LINEAR B IDEOGRAM B160;Lo;0;L;;;;;N;;;;; +100A9;LINEAR B IDEOGRAM B161;Lo;0;L;;;;;N;;;;; +100AA;LINEAR B IDEOGRAM B162 GARMENT;Lo;0;L;;;;;N;;;;; +100AB;LINEAR B IDEOGRAM B163 ARMOUR;Lo;0;L;;;;;N;;;;; +100AC;LINEAR B IDEOGRAM B164;Lo;0;L;;;;;N;;;;; +100AD;LINEAR B IDEOGRAM B165;Lo;0;L;;;;;N;;;;; +100AE;LINEAR B IDEOGRAM B166;Lo;0;L;;;;;N;;;;; +100AF;LINEAR B IDEOGRAM B167;Lo;0;L;;;;;N;;;;; +100B0;LINEAR B IDEOGRAM B168;Lo;0;L;;;;;N;;;;; +100B1;LINEAR B IDEOGRAM B169;Lo;0;L;;;;;N;;;;; +100B2;LINEAR B IDEOGRAM B170;Lo;0;L;;;;;N;;;;; +100B3;LINEAR B IDEOGRAM B171;Lo;0;L;;;;;N;;;;; +100B4;LINEAR B IDEOGRAM B172;Lo;0;L;;;;;N;;;;; +100B5;LINEAR B IDEOGRAM B173 MONTH;Lo;0;L;;;;;N;;;;; +100B6;LINEAR B IDEOGRAM B174;Lo;0;L;;;;;N;;;;; +100B7;LINEAR B IDEOGRAM B176 TREE;Lo;0;L;;;;;N;;;;; +100B8;LINEAR B IDEOGRAM B177;Lo;0;L;;;;;N;;;;; +100B9;LINEAR B IDEOGRAM B178;Lo;0;L;;;;;N;;;;; +100BA;LINEAR B IDEOGRAM B179;Lo;0;L;;;;;N;;;;; +100BB;LINEAR B IDEOGRAM B180;Lo;0;L;;;;;N;;;;; +100BC;LINEAR B IDEOGRAM B181;Lo;0;L;;;;;N;;;;; +100BD;LINEAR B IDEOGRAM B182;Lo;0;L;;;;;N;;;;; +100BE;LINEAR B IDEOGRAM B183;Lo;0;L;;;;;N;;;;; +100BF;LINEAR B IDEOGRAM B184;Lo;0;L;;;;;N;;;;; +100C0;LINEAR B IDEOGRAM B185;Lo;0;L;;;;;N;;;;; +100C1;LINEAR B IDEOGRAM B189;Lo;0;L;;;;;N;;;;; +100C2;LINEAR B IDEOGRAM B190;Lo;0;L;;;;;N;;;;; +100C3;LINEAR B IDEOGRAM B191 HELMET;Lo;0;L;;;;;N;;;;; +100C4;LINEAR B IDEOGRAM B220 FOOTSTOOL;Lo;0;L;;;;;N;;;;; +100C5;LINEAR B IDEOGRAM B225 BATHTUB;Lo;0;L;;;;;N;;;;; +100C6;LINEAR B IDEOGRAM B230 SPEAR;Lo;0;L;;;;;N;;;;; +100C7;LINEAR B IDEOGRAM B231 ARROW;Lo;0;L;;;;;N;;;;; +100C8;LINEAR B IDEOGRAM B232;Lo;0;L;;;;;N;;;;; +100C9;LINEAR B IDEOGRAM B233 SWORD;Lo;0;L;;;;;N;;;;; +100CA;LINEAR B IDEOGRAM B234;Lo;0;L;;;;;N;;;;; +100CB;LINEAR B IDEOGRAM B236;Lo;0;L;;;;;N;;;;; +100CC;LINEAR B IDEOGRAM B240 WHEELED CHARIOT;Lo;0;L;;;;;N;;;;; +100CD;LINEAR B IDEOGRAM B241 CHARIOT;Lo;0;L;;;;;N;;;;; +100CE;LINEAR B IDEOGRAM B242 CHARIOT FRAME;Lo;0;L;;;;;N;;;;; +100CF;LINEAR B IDEOGRAM B243 WHEEL;Lo;0;L;;;;;N;;;;; +100D0;LINEAR B IDEOGRAM B245;Lo;0;L;;;;;N;;;;; +100D1;LINEAR B IDEOGRAM B246;Lo;0;L;;;;;N;;;;; +100D2;LINEAR B MONOGRAM B247 DIPTE;Lo;0;L;;;;;N;;;;; +100D3;LINEAR B IDEOGRAM B248;Lo;0;L;;;;;N;;;;; +100D4;LINEAR B IDEOGRAM B249;Lo;0;L;;;;;N;;;;; +100D5;LINEAR B IDEOGRAM B251;Lo;0;L;;;;;N;;;;; +100D6;LINEAR B IDEOGRAM B252;Lo;0;L;;;;;N;;;;; +100D7;LINEAR B IDEOGRAM B253;Lo;0;L;;;;;N;;;;; +100D8;LINEAR B IDEOGRAM B254 DART;Lo;0;L;;;;;N;;;;; +100D9;LINEAR B IDEOGRAM B255;Lo;0;L;;;;;N;;;;; +100DA;LINEAR B IDEOGRAM B256;Lo;0;L;;;;;N;;;;; +100DB;LINEAR B IDEOGRAM B257;Lo;0;L;;;;;N;;;;; +100DC;LINEAR B IDEOGRAM B258;Lo;0;L;;;;;N;;;;; +100DD;LINEAR B IDEOGRAM B259;Lo;0;L;;;;;N;;;;; +100DE;LINEAR B IDEOGRAM VESSEL B155;Lo;0;L;;;;;N;;;;; +100DF;LINEAR B IDEOGRAM VESSEL B200;Lo;0;L;;;;;N;;;;; +100E0;LINEAR B IDEOGRAM VESSEL B201;Lo;0;L;;;;;N;;;;; +100E1;LINEAR B IDEOGRAM VESSEL B202;Lo;0;L;;;;;N;;;;; +100E2;LINEAR B IDEOGRAM VESSEL B203;Lo;0;L;;;;;N;;;;; +100E3;LINEAR B IDEOGRAM VESSEL B204;Lo;0;L;;;;;N;;;;; +100E4;LINEAR B IDEOGRAM VESSEL B205;Lo;0;L;;;;;N;;;;; +100E5;LINEAR B IDEOGRAM VESSEL B206;Lo;0;L;;;;;N;;;;; +100E6;LINEAR B IDEOGRAM VESSEL B207;Lo;0;L;;;;;N;;;;; +100E7;LINEAR B IDEOGRAM VESSEL B208;Lo;0;L;;;;;N;;;;; +100E8;LINEAR B IDEOGRAM VESSEL B209;Lo;0;L;;;;;N;;;;; +100E9;LINEAR B IDEOGRAM VESSEL B210;Lo;0;L;;;;;N;;;;; +100EA;LINEAR B IDEOGRAM VESSEL B211;Lo;0;L;;;;;N;;;;; +100EB;LINEAR B IDEOGRAM VESSEL B212;Lo;0;L;;;;;N;;;;; +100EC;LINEAR B IDEOGRAM VESSEL B213;Lo;0;L;;;;;N;;;;; +100ED;LINEAR B IDEOGRAM VESSEL B214;Lo;0;L;;;;;N;;;;; +100EE;LINEAR B IDEOGRAM VESSEL B215;Lo;0;L;;;;;N;;;;; +100EF;LINEAR B IDEOGRAM VESSEL B216;Lo;0;L;;;;;N;;;;; +100F0;LINEAR B IDEOGRAM VESSEL B217;Lo;0;L;;;;;N;;;;; +100F1;LINEAR B IDEOGRAM VESSEL B218;Lo;0;L;;;;;N;;;;; +100F2;LINEAR B IDEOGRAM VESSEL B219;Lo;0;L;;;;;N;;;;; +100F3;LINEAR B IDEOGRAM VESSEL B221;Lo;0;L;;;;;N;;;;; +100F4;LINEAR B IDEOGRAM VESSEL B222;Lo;0;L;;;;;N;;;;; +100F5;LINEAR B IDEOGRAM VESSEL B226;Lo;0;L;;;;;N;;;;; +100F6;LINEAR B IDEOGRAM VESSEL B227;Lo;0;L;;;;;N;;;;; +100F7;LINEAR B IDEOGRAM VESSEL B228;Lo;0;L;;;;;N;;;;; +100F8;LINEAR B IDEOGRAM VESSEL B229;Lo;0;L;;;;;N;;;;; +100F9;LINEAR B IDEOGRAM VESSEL B250;Lo;0;L;;;;;N;;;;; +100FA;LINEAR B IDEOGRAM VESSEL B305;Lo;0;L;;;;;N;;;;; +10100;AEGEAN WORD SEPARATOR LINE;Po;0;L;;;;;N;;;;; +10101;AEGEAN WORD SEPARATOR DOT;Po;0;ON;;;;;N;;;;; +10102;AEGEAN CHECK MARK;Po;0;L;;;;;N;;;;; +10107;AEGEAN NUMBER ONE;No;0;L;;;;1;N;;;;; +10108;AEGEAN NUMBER TWO;No;0;L;;;;2;N;;;;; +10109;AEGEAN NUMBER THREE;No;0;L;;;;3;N;;;;; +1010A;AEGEAN NUMBER FOUR;No;0;L;;;;4;N;;;;; +1010B;AEGEAN NUMBER FIVE;No;0;L;;;;5;N;;;;; +1010C;AEGEAN NUMBER SIX;No;0;L;;;;6;N;;;;; +1010D;AEGEAN NUMBER SEVEN;No;0;L;;;;7;N;;;;; +1010E;AEGEAN NUMBER EIGHT;No;0;L;;;;8;N;;;;; +1010F;AEGEAN NUMBER NINE;No;0;L;;;;9;N;;;;; +10110;AEGEAN NUMBER TEN;No;0;L;;;;10;N;;;;; +10111;AEGEAN NUMBER TWENTY;No;0;L;;;;20;N;;;;; +10112;AEGEAN NUMBER THIRTY;No;0;L;;;;30;N;;;;; +10113;AEGEAN NUMBER FORTY;No;0;L;;;;40;N;;;;; +10114;AEGEAN NUMBER FIFTY;No;0;L;;;;50;N;;;;; +10115;AEGEAN NUMBER SIXTY;No;0;L;;;;60;N;;;;; +10116;AEGEAN NUMBER SEVENTY;No;0;L;;;;70;N;;;;; +10117;AEGEAN NUMBER EIGHTY;No;0;L;;;;80;N;;;;; +10118;AEGEAN NUMBER NINETY;No;0;L;;;;90;N;;;;; +10119;AEGEAN NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;; +1011A;AEGEAN NUMBER TWO HUNDRED;No;0;L;;;;200;N;;;;; +1011B;AEGEAN NUMBER THREE HUNDRED;No;0;L;;;;300;N;;;;; +1011C;AEGEAN NUMBER FOUR HUNDRED;No;0;L;;;;400;N;;;;; +1011D;AEGEAN NUMBER FIVE HUNDRED;No;0;L;;;;500;N;;;;; +1011E;AEGEAN NUMBER SIX HUNDRED;No;0;L;;;;600;N;;;;; +1011F;AEGEAN NUMBER SEVEN HUNDRED;No;0;L;;;;700;N;;;;; +10120;AEGEAN NUMBER EIGHT HUNDRED;No;0;L;;;;800;N;;;;; +10121;AEGEAN NUMBER NINE HUNDRED;No;0;L;;;;900;N;;;;; +10122;AEGEAN NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;; +10123;AEGEAN NUMBER TWO THOUSAND;No;0;L;;;;2000;N;;;;; +10124;AEGEAN NUMBER THREE THOUSAND;No;0;L;;;;3000;N;;;;; +10125;AEGEAN NUMBER FOUR THOUSAND;No;0;L;;;;4000;N;;;;; +10126;AEGEAN NUMBER FIVE THOUSAND;No;0;L;;;;5000;N;;;;; +10127;AEGEAN NUMBER SIX THOUSAND;No;0;L;;;;6000;N;;;;; +10128;AEGEAN NUMBER SEVEN THOUSAND;No;0;L;;;;7000;N;;;;; +10129;AEGEAN NUMBER EIGHT THOUSAND;No;0;L;;;;8000;N;;;;; +1012A;AEGEAN NUMBER NINE THOUSAND;No;0;L;;;;9000;N;;;;; +1012B;AEGEAN NUMBER TEN THOUSAND;No;0;L;;;;10000;N;;;;; +1012C;AEGEAN NUMBER TWENTY THOUSAND;No;0;L;;;;20000;N;;;;; +1012D;AEGEAN NUMBER THIRTY THOUSAND;No;0;L;;;;30000;N;;;;; +1012E;AEGEAN NUMBER FORTY THOUSAND;No;0;L;;;;40000;N;;;;; +1012F;AEGEAN NUMBER FIFTY THOUSAND;No;0;L;;;;50000;N;;;;; +10130;AEGEAN NUMBER SIXTY THOUSAND;No;0;L;;;;60000;N;;;;; +10131;AEGEAN NUMBER SEVENTY THOUSAND;No;0;L;;;;70000;N;;;;; +10132;AEGEAN NUMBER EIGHTY THOUSAND;No;0;L;;;;80000;N;;;;; +10133;AEGEAN NUMBER NINETY THOUSAND;No;0;L;;;;90000;N;;;;; +10137;AEGEAN WEIGHT BASE UNIT;So;0;L;;;;;N;;;;; +10138;AEGEAN WEIGHT FIRST SUBUNIT;So;0;L;;;;;N;;;;; +10139;AEGEAN WEIGHT SECOND SUBUNIT;So;0;L;;;;;N;;;;; +1013A;AEGEAN WEIGHT THIRD SUBUNIT;So;0;L;;;;;N;;;;; +1013B;AEGEAN WEIGHT FOURTH SUBUNIT;So;0;L;;;;;N;;;;; +1013C;AEGEAN DRY MEASURE FIRST SUBUNIT;So;0;L;;;;;N;;;;; +1013D;AEGEAN LIQUID MEASURE FIRST SUBUNIT;So;0;L;;;;;N;;;;; +1013E;AEGEAN MEASURE SECOND SUBUNIT;So;0;L;;;;;N;;;;; +1013F;AEGEAN MEASURE THIRD SUBUNIT;So;0;L;;;;;N;;;;; +10140;GREEK ACROPHONIC ATTIC ONE QUARTER;Nl;0;ON;;;;1/4;N;;;;; +10141;GREEK ACROPHONIC ATTIC ONE HALF;Nl;0;ON;;;;1/2;N;;;;; +10142;GREEK ACROPHONIC ATTIC ONE DRACHMA;Nl;0;ON;;;;1;N;;;;; +10143;GREEK ACROPHONIC ATTIC FIVE;Nl;0;ON;;;;5;N;;;;; +10144;GREEK ACROPHONIC ATTIC FIFTY;Nl;0;ON;;;;50;N;;;;; +10145;GREEK ACROPHONIC ATTIC FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;; +10146;GREEK ACROPHONIC ATTIC FIVE THOUSAND;Nl;0;ON;;;;5000;N;;;;; +10147;GREEK ACROPHONIC ATTIC FIFTY THOUSAND;Nl;0;ON;;;;50000;N;;;;; +10148;GREEK ACROPHONIC ATTIC FIVE TALENTS;Nl;0;ON;;;;5;N;;;;; +10149;GREEK ACROPHONIC ATTIC TEN TALENTS;Nl;0;ON;;;;10;N;;;;; +1014A;GREEK ACROPHONIC ATTIC FIFTY TALENTS;Nl;0;ON;;;;50;N;;;;; +1014B;GREEK ACROPHONIC ATTIC ONE HUNDRED TALENTS;Nl;0;ON;;;;100;N;;;;; +1014C;GREEK ACROPHONIC ATTIC FIVE HUNDRED TALENTS;Nl;0;ON;;;;500;N;;;;; +1014D;GREEK ACROPHONIC ATTIC ONE THOUSAND TALENTS;Nl;0;ON;;;;1000;N;;;;; +1014E;GREEK ACROPHONIC ATTIC FIVE THOUSAND TALENTS;Nl;0;ON;;;;5000;N;;;;; +1014F;GREEK ACROPHONIC ATTIC FIVE STATERS;Nl;0;ON;;;;5;N;;;;; +10150;GREEK ACROPHONIC ATTIC TEN STATERS;Nl;0;ON;;;;10;N;;;;; +10151;GREEK ACROPHONIC ATTIC FIFTY STATERS;Nl;0;ON;;;;50;N;;;;; +10152;GREEK ACROPHONIC ATTIC ONE HUNDRED STATERS;Nl;0;ON;;;;100;N;;;;; +10153;GREEK ACROPHONIC ATTIC FIVE HUNDRED STATERS;Nl;0;ON;;;;500;N;;;;; +10154;GREEK ACROPHONIC ATTIC ONE THOUSAND STATERS;Nl;0;ON;;;;1000;N;;;;; +10155;GREEK ACROPHONIC ATTIC TEN THOUSAND STATERS;Nl;0;ON;;;;10000;N;;;;; +10156;GREEK ACROPHONIC ATTIC FIFTY THOUSAND STATERS;Nl;0;ON;;;;50000;N;;;;; +10157;GREEK ACROPHONIC ATTIC TEN MNAS;Nl;0;ON;;;;10;N;;;;; +10158;GREEK ACROPHONIC HERAEUM ONE PLETHRON;Nl;0;ON;;;;1;N;;;;; +10159;GREEK ACROPHONIC THESPIAN ONE;Nl;0;ON;;;;1;N;;;;; +1015A;GREEK ACROPHONIC HERMIONIAN ONE;Nl;0;ON;;;;1;N;;;;; +1015B;GREEK ACROPHONIC EPIDAUREAN TWO;Nl;0;ON;;;;2;N;;;;; +1015C;GREEK ACROPHONIC THESPIAN TWO;Nl;0;ON;;;;2;N;;;;; +1015D;GREEK ACROPHONIC CYRENAIC TWO DRACHMAS;Nl;0;ON;;;;2;N;;;;; +1015E;GREEK ACROPHONIC EPIDAUREAN TWO DRACHMAS;Nl;0;ON;;;;2;N;;;;; +1015F;GREEK ACROPHONIC TROEZENIAN FIVE;Nl;0;ON;;;;5;N;;;;; +10160;GREEK ACROPHONIC TROEZENIAN TEN;Nl;0;ON;;;;10;N;;;;; +10161;GREEK ACROPHONIC TROEZENIAN TEN ALTERNATE FORM;Nl;0;ON;;;;10;N;;;;; +10162;GREEK ACROPHONIC HERMIONIAN TEN;Nl;0;ON;;;;10;N;;;;; +10163;GREEK ACROPHONIC MESSENIAN TEN;Nl;0;ON;;;;10;N;;;;; +10164;GREEK ACROPHONIC THESPIAN TEN;Nl;0;ON;;;;10;N;;;;; +10165;GREEK ACROPHONIC THESPIAN THIRTY;Nl;0;ON;;;;30;N;;;;; +10166;GREEK ACROPHONIC TROEZENIAN FIFTY;Nl;0;ON;;;;50;N;;;;; +10167;GREEK ACROPHONIC TROEZENIAN FIFTY ALTERNATE FORM;Nl;0;ON;;;;50;N;;;;; +10168;GREEK ACROPHONIC HERMIONIAN FIFTY;Nl;0;ON;;;;50;N;;;;; +10169;GREEK ACROPHONIC THESPIAN FIFTY;Nl;0;ON;;;;50;N;;;;; +1016A;GREEK ACROPHONIC THESPIAN ONE HUNDRED;Nl;0;ON;;;;100;N;;;;; +1016B;GREEK ACROPHONIC THESPIAN THREE HUNDRED;Nl;0;ON;;;;300;N;;;;; +1016C;GREEK ACROPHONIC EPIDAUREAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;; +1016D;GREEK ACROPHONIC TROEZENIAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;; +1016E;GREEK ACROPHONIC THESPIAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;; +1016F;GREEK ACROPHONIC CARYSTIAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;; +10170;GREEK ACROPHONIC NAXIAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;; +10171;GREEK ACROPHONIC THESPIAN ONE THOUSAND;Nl;0;ON;;;;1000;N;;;;; +10172;GREEK ACROPHONIC THESPIAN FIVE THOUSAND;Nl;0;ON;;;;5000;N;;;;; +10173;GREEK ACROPHONIC DELPHIC FIVE MNAS;Nl;0;ON;;;;5;N;;;;; +10174;GREEK ACROPHONIC STRATIAN FIFTY MNAS;Nl;0;ON;;;;50;N;;;;; +10175;GREEK ONE HALF SIGN;No;0;ON;;;;1/2;N;;;;; +10176;GREEK ONE HALF SIGN ALTERNATE FORM;No;0;ON;;;;1/2;N;;;;; +10177;GREEK TWO THIRDS SIGN;No;0;ON;;;;2/3;N;;;;; +10178;GREEK THREE QUARTERS SIGN;No;0;ON;;;;3/4;N;;;;; +10179;GREEK YEAR SIGN;So;0;ON;;;;;N;;;;; +1017A;GREEK TALENT SIGN;So;0;ON;;;;;N;;;;; +1017B;GREEK DRACHMA SIGN;So;0;ON;;;;;N;;;;; +1017C;GREEK OBOL SIGN;So;0;ON;;;;;N;;;;; +1017D;GREEK TWO OBOLS SIGN;So;0;ON;;;;;N;;;;; +1017E;GREEK THREE OBOLS SIGN;So;0;ON;;;;;N;;;;; +1017F;GREEK FOUR OBOLS SIGN;So;0;ON;;;;;N;;;;; +10180;GREEK FIVE OBOLS SIGN;So;0;ON;;;;;N;;;;; +10181;GREEK METRETES SIGN;So;0;ON;;;;;N;;;;; +10182;GREEK KYATHOS BASE SIGN;So;0;ON;;;;;N;;;;; +10183;GREEK LITRA SIGN;So;0;ON;;;;;N;;;;; +10184;GREEK OUNKIA SIGN;So;0;ON;;;;;N;;;;; +10185;GREEK XESTES SIGN;So;0;ON;;;;;N;;;;; +10186;GREEK ARTABE SIGN;So;0;ON;;;;;N;;;;; +10187;GREEK AROURA SIGN;So;0;ON;;;;;N;;;;; +10188;GREEK GRAMMA SIGN;So;0;ON;;;;;N;;;;; +10189;GREEK TRYBLION BASE SIGN;So;0;ON;;;;;N;;;;; +1018A;GREEK ZERO SIGN;No;0;ON;;;;0;N;;;;; +1018B;GREEK ONE QUARTER SIGN;No;0;ON;;;;1/4;N;;;;; +1018C;GREEK SINUSOID SIGN;So;0;ON;;;;;N;;;;; +1018D;GREEK INDICTION SIGN;So;0;L;;;;;N;;;;; +1018E;NOMISMA SIGN;So;0;L;;;;;N;;;;; +10190;ROMAN SEXTANS SIGN;So;0;ON;;;;;N;;;;; +10191;ROMAN UNCIA SIGN;So;0;ON;;;;;N;;;;; +10192;ROMAN SEMUNCIA SIGN;So;0;ON;;;;;N;;;;; +10193;ROMAN SEXTULA SIGN;So;0;ON;;;;;N;;;;; +10194;ROMAN DIMIDIA SEXTULA SIGN;So;0;ON;;;;;N;;;;; +10195;ROMAN SILIQUA SIGN;So;0;ON;;;;;N;;;;; +10196;ROMAN DENARIUS SIGN;So;0;ON;;;;;N;;;;; +10197;ROMAN QUINARIUS SIGN;So;0;ON;;;;;N;;;;; +10198;ROMAN SESTERTIUS SIGN;So;0;ON;;;;;N;;;;; +10199;ROMAN DUPONDIUS SIGN;So;0;ON;;;;;N;;;;; +1019A;ROMAN AS SIGN;So;0;ON;;;;;N;;;;; +1019B;ROMAN CENTURIAL SIGN;So;0;ON;;;;;N;;;;; +101A0;GREEK SYMBOL TAU RHO;So;0;ON;;;;;N;;;;; +101D0;PHAISTOS DISC SIGN PEDESTRIAN;So;0;L;;;;;N;;;;; +101D1;PHAISTOS DISC SIGN PLUMED HEAD;So;0;L;;;;;N;;;;; +101D2;PHAISTOS DISC SIGN TATTOOED HEAD;So;0;L;;;;;N;;;;; +101D3;PHAISTOS DISC SIGN CAPTIVE;So;0;L;;;;;N;;;;; +101D4;PHAISTOS DISC SIGN CHILD;So;0;L;;;;;N;;;;; +101D5;PHAISTOS DISC SIGN WOMAN;So;0;L;;;;;N;;;;; +101D6;PHAISTOS DISC SIGN HELMET;So;0;L;;;;;N;;;;; +101D7;PHAISTOS DISC SIGN GAUNTLET;So;0;L;;;;;N;;;;; +101D8;PHAISTOS DISC SIGN TIARA;So;0;L;;;;;N;;;;; +101D9;PHAISTOS DISC SIGN ARROW;So;0;L;;;;;N;;;;; +101DA;PHAISTOS DISC SIGN BOW;So;0;L;;;;;N;;;;; +101DB;PHAISTOS DISC SIGN SHIELD;So;0;L;;;;;N;;;;; +101DC;PHAISTOS DISC SIGN CLUB;So;0;L;;;;;N;;;;; +101DD;PHAISTOS DISC SIGN MANACLES;So;0;L;;;;;N;;;;; +101DE;PHAISTOS DISC SIGN MATTOCK;So;0;L;;;;;N;;;;; +101DF;PHAISTOS DISC SIGN SAW;So;0;L;;;;;N;;;;; +101E0;PHAISTOS DISC SIGN LID;So;0;L;;;;;N;;;;; +101E1;PHAISTOS DISC SIGN BOOMERANG;So;0;L;;;;;N;;;;; +101E2;PHAISTOS DISC SIGN CARPENTRY PLANE;So;0;L;;;;;N;;;;; +101E3;PHAISTOS DISC SIGN DOLIUM;So;0;L;;;;;N;;;;; +101E4;PHAISTOS DISC SIGN COMB;So;0;L;;;;;N;;;;; +101E5;PHAISTOS DISC SIGN SLING;So;0;L;;;;;N;;;;; +101E6;PHAISTOS DISC SIGN COLUMN;So;0;L;;;;;N;;;;; +101E7;PHAISTOS DISC SIGN BEEHIVE;So;0;L;;;;;N;;;;; +101E8;PHAISTOS DISC SIGN SHIP;So;0;L;;;;;N;;;;; +101E9;PHAISTOS DISC SIGN HORN;So;0;L;;;;;N;;;;; +101EA;PHAISTOS DISC SIGN HIDE;So;0;L;;;;;N;;;;; +101EB;PHAISTOS DISC SIGN BULLS LEG;So;0;L;;;;;N;;;;; +101EC;PHAISTOS DISC SIGN CAT;So;0;L;;;;;N;;;;; +101ED;PHAISTOS DISC SIGN RAM;So;0;L;;;;;N;;;;; +101EE;PHAISTOS DISC SIGN EAGLE;So;0;L;;;;;N;;;;; +101EF;PHAISTOS DISC SIGN DOVE;So;0;L;;;;;N;;;;; +101F0;PHAISTOS DISC SIGN TUNNY;So;0;L;;;;;N;;;;; +101F1;PHAISTOS DISC SIGN BEE;So;0;L;;;;;N;;;;; +101F2;PHAISTOS DISC SIGN PLANE TREE;So;0;L;;;;;N;;;;; +101F3;PHAISTOS DISC SIGN VINE;So;0;L;;;;;N;;;;; +101F4;PHAISTOS DISC SIGN PAPYRUS;So;0;L;;;;;N;;;;; +101F5;PHAISTOS DISC SIGN ROSETTE;So;0;L;;;;;N;;;;; +101F6;PHAISTOS DISC SIGN LILY;So;0;L;;;;;N;;;;; +101F7;PHAISTOS DISC SIGN OX BACK;So;0;L;;;;;N;;;;; +101F8;PHAISTOS DISC SIGN FLUTE;So;0;L;;;;;N;;;;; +101F9;PHAISTOS DISC SIGN GRATER;So;0;L;;;;;N;;;;; +101FA;PHAISTOS DISC SIGN STRAINER;So;0;L;;;;;N;;;;; +101FB;PHAISTOS DISC SIGN SMALL AXE;So;0;L;;;;;N;;;;; +101FC;PHAISTOS DISC SIGN WAVY BAND;So;0;L;;;;;N;;;;; +101FD;PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE;Mn;220;NSM;;;;;N;;;;; +10280;LYCIAN LETTER A;Lo;0;L;;;;;N;;;;; +10281;LYCIAN LETTER E;Lo;0;L;;;;;N;;;;; +10282;LYCIAN LETTER B;Lo;0;L;;;;;N;;;;; +10283;LYCIAN LETTER BH;Lo;0;L;;;;;N;;;;; +10284;LYCIAN LETTER G;Lo;0;L;;;;;N;;;;; +10285;LYCIAN LETTER D;Lo;0;L;;;;;N;;;;; +10286;LYCIAN LETTER I;Lo;0;L;;;;;N;;;;; +10287;LYCIAN LETTER W;Lo;0;L;;;;;N;;;;; +10288;LYCIAN LETTER Z;Lo;0;L;;;;;N;;;;; +10289;LYCIAN LETTER TH;Lo;0;L;;;;;N;;;;; +1028A;LYCIAN LETTER J;Lo;0;L;;;;;N;;;;; +1028B;LYCIAN LETTER K;Lo;0;L;;;;;N;;;;; +1028C;LYCIAN LETTER Q;Lo;0;L;;;;;N;;;;; +1028D;LYCIAN LETTER L;Lo;0;L;;;;;N;;;;; +1028E;LYCIAN LETTER M;Lo;0;L;;;;;N;;;;; +1028F;LYCIAN LETTER N;Lo;0;L;;;;;N;;;;; +10290;LYCIAN LETTER MM;Lo;0;L;;;;;N;;;;; +10291;LYCIAN LETTER NN;Lo;0;L;;;;;N;;;;; +10292;LYCIAN LETTER U;Lo;0;L;;;;;N;;;;; +10293;LYCIAN LETTER P;Lo;0;L;;;;;N;;;;; +10294;LYCIAN LETTER KK;Lo;0;L;;;;;N;;;;; +10295;LYCIAN LETTER R;Lo;0;L;;;;;N;;;;; +10296;LYCIAN LETTER S;Lo;0;L;;;;;N;;;;; +10297;LYCIAN LETTER T;Lo;0;L;;;;;N;;;;; +10298;LYCIAN LETTER TT;Lo;0;L;;;;;N;;;;; +10299;LYCIAN LETTER AN;Lo;0;L;;;;;N;;;;; +1029A;LYCIAN LETTER EN;Lo;0;L;;;;;N;;;;; +1029B;LYCIAN LETTER H;Lo;0;L;;;;;N;;;;; +1029C;LYCIAN LETTER X;Lo;0;L;;;;;N;;;;; +102A0;CARIAN LETTER A;Lo;0;L;;;;;N;;;;; +102A1;CARIAN LETTER P2;Lo;0;L;;;;;N;;;;; +102A2;CARIAN LETTER D;Lo;0;L;;;;;N;;;;; +102A3;CARIAN LETTER L;Lo;0;L;;;;;N;;;;; +102A4;CARIAN LETTER UUU;Lo;0;L;;;;;N;;;;; +102A5;CARIAN LETTER R;Lo;0;L;;;;;N;;;;; +102A6;CARIAN LETTER LD;Lo;0;L;;;;;N;;;;; +102A7;CARIAN LETTER A2;Lo;0;L;;;;;N;;;;; +102A8;CARIAN LETTER Q;Lo;0;L;;;;;N;;;;; +102A9;CARIAN LETTER B;Lo;0;L;;;;;N;;;;; +102AA;CARIAN LETTER M;Lo;0;L;;;;;N;;;;; +102AB;CARIAN LETTER O;Lo;0;L;;;;;N;;;;; +102AC;CARIAN LETTER D2;Lo;0;L;;;;;N;;;;; +102AD;CARIAN LETTER T;Lo;0;L;;;;;N;;;;; +102AE;CARIAN LETTER SH;Lo;0;L;;;;;N;;;;; +102AF;CARIAN LETTER SH2;Lo;0;L;;;;;N;;;;; +102B0;CARIAN LETTER S;Lo;0;L;;;;;N;;;;; +102B1;CARIAN LETTER C-18;Lo;0;L;;;;;N;;;;; +102B2;CARIAN LETTER U;Lo;0;L;;;;;N;;;;; +102B3;CARIAN LETTER NN;Lo;0;L;;;;;N;;;;; +102B4;CARIAN LETTER X;Lo;0;L;;;;;N;;;;; +102B5;CARIAN LETTER N;Lo;0;L;;;;;N;;;;; +102B6;CARIAN LETTER TT2;Lo;0;L;;;;;N;;;;; +102B7;CARIAN LETTER P;Lo;0;L;;;;;N;;;;; +102B8;CARIAN LETTER SS;Lo;0;L;;;;;N;;;;; +102B9;CARIAN LETTER I;Lo;0;L;;;;;N;;;;; +102BA;CARIAN LETTER E;Lo;0;L;;;;;N;;;;; +102BB;CARIAN LETTER UUUU;Lo;0;L;;;;;N;;;;; +102BC;CARIAN LETTER K;Lo;0;L;;;;;N;;;;; +102BD;CARIAN LETTER K2;Lo;0;L;;;;;N;;;;; +102BE;CARIAN LETTER ND;Lo;0;L;;;;;N;;;;; +102BF;CARIAN LETTER UU;Lo;0;L;;;;;N;;;;; +102C0;CARIAN LETTER G;Lo;0;L;;;;;N;;;;; +102C1;CARIAN LETTER G2;Lo;0;L;;;;;N;;;;; +102C2;CARIAN LETTER ST;Lo;0;L;;;;;N;;;;; +102C3;CARIAN LETTER ST2;Lo;0;L;;;;;N;;;;; +102C4;CARIAN LETTER NG;Lo;0;L;;;;;N;;;;; +102C5;CARIAN LETTER II;Lo;0;L;;;;;N;;;;; +102C6;CARIAN LETTER C-39;Lo;0;L;;;;;N;;;;; +102C7;CARIAN LETTER TT;Lo;0;L;;;;;N;;;;; +102C8;CARIAN LETTER UUU2;Lo;0;L;;;;;N;;;;; +102C9;CARIAN LETTER RR;Lo;0;L;;;;;N;;;;; +102CA;CARIAN LETTER MB;Lo;0;L;;;;;N;;;;; +102CB;CARIAN LETTER MB2;Lo;0;L;;;;;N;;;;; +102CC;CARIAN LETTER MB3;Lo;0;L;;;;;N;;;;; +102CD;CARIAN LETTER MB4;Lo;0;L;;;;;N;;;;; +102CE;CARIAN LETTER LD2;Lo;0;L;;;;;N;;;;; +102CF;CARIAN LETTER E2;Lo;0;L;;;;;N;;;;; +102D0;CARIAN LETTER UUU3;Lo;0;L;;;;;N;;;;; +102E0;COPTIC EPACT THOUSANDS MARK;Mn;220;NSM;;;;;N;;;;; +102E1;COPTIC EPACT DIGIT ONE;No;0;EN;;;;1;N;;;;; +102E2;COPTIC EPACT DIGIT TWO;No;0;EN;;;;2;N;;;;; +102E3;COPTIC EPACT DIGIT THREE;No;0;EN;;;;3;N;;;;; +102E4;COPTIC EPACT DIGIT FOUR;No;0;EN;;;;4;N;;;;; +102E5;COPTIC EPACT DIGIT FIVE;No;0;EN;;;;5;N;;;;; +102E6;COPTIC EPACT DIGIT SIX;No;0;EN;;;;6;N;;;;; +102E7;COPTIC EPACT DIGIT SEVEN;No;0;EN;;;;7;N;;;;; +102E8;COPTIC EPACT DIGIT EIGHT;No;0;EN;;;;8;N;;;;; +102E9;COPTIC EPACT DIGIT NINE;No;0;EN;;;;9;N;;;;; +102EA;COPTIC EPACT NUMBER TEN;No;0;EN;;;;10;N;;;;; +102EB;COPTIC EPACT NUMBER TWENTY;No;0;EN;;;;20;N;;;;; +102EC;COPTIC EPACT NUMBER THIRTY;No;0;EN;;;;30;N;;;;; +102ED;COPTIC EPACT NUMBER FORTY;No;0;EN;;;;40;N;;;;; +102EE;COPTIC EPACT NUMBER FIFTY;No;0;EN;;;;50;N;;;;; +102EF;COPTIC EPACT NUMBER SIXTY;No;0;EN;;;;60;N;;;;; +102F0;COPTIC EPACT NUMBER SEVENTY;No;0;EN;;;;70;N;;;;; +102F1;COPTIC EPACT NUMBER EIGHTY;No;0;EN;;;;80;N;;;;; +102F2;COPTIC EPACT NUMBER NINETY;No;0;EN;;;;90;N;;;;; +102F3;COPTIC EPACT NUMBER ONE HUNDRED;No;0;EN;;;;100;N;;;;; +102F4;COPTIC EPACT NUMBER TWO HUNDRED;No;0;EN;;;;200;N;;;;; +102F5;COPTIC EPACT NUMBER THREE HUNDRED;No;0;EN;;;;300;N;;;;; +102F6;COPTIC EPACT NUMBER FOUR HUNDRED;No;0;EN;;;;400;N;;;;; +102F7;COPTIC EPACT NUMBER FIVE HUNDRED;No;0;EN;;;;500;N;;;;; +102F8;COPTIC EPACT NUMBER SIX HUNDRED;No;0;EN;;;;600;N;;;;; +102F9;COPTIC EPACT NUMBER SEVEN HUNDRED;No;0;EN;;;;700;N;;;;; +102FA;COPTIC EPACT NUMBER EIGHT HUNDRED;No;0;EN;;;;800;N;;;;; +102FB;COPTIC EPACT NUMBER NINE HUNDRED;No;0;EN;;;;900;N;;;;; +10300;OLD ITALIC LETTER A;Lo;0;L;;;;;N;;;;; +10301;OLD ITALIC LETTER BE;Lo;0;L;;;;;N;;;;; +10302;OLD ITALIC LETTER KE;Lo;0;L;;;;;N;;;;; +10303;OLD ITALIC LETTER DE;Lo;0;L;;;;;N;;;;; +10304;OLD ITALIC LETTER E;Lo;0;L;;;;;N;;;;; +10305;OLD ITALIC LETTER VE;Lo;0;L;;;;;N;;;;; +10306;OLD ITALIC LETTER ZE;Lo;0;L;;;;;N;;;;; +10307;OLD ITALIC LETTER HE;Lo;0;L;;;;;N;;;;; +10308;OLD ITALIC LETTER THE;Lo;0;L;;;;;N;;;;; +10309;OLD ITALIC LETTER I;Lo;0;L;;;;;N;;;;; +1030A;OLD ITALIC LETTER KA;Lo;0;L;;;;;N;;;;; +1030B;OLD ITALIC LETTER EL;Lo;0;L;;;;;N;;;;; +1030C;OLD ITALIC LETTER EM;Lo;0;L;;;;;N;;;;; +1030D;OLD ITALIC LETTER EN;Lo;0;L;;;;;N;;;;; +1030E;OLD ITALIC LETTER ESH;Lo;0;L;;;;;N;;;;; +1030F;OLD ITALIC LETTER O;Lo;0;L;;;;;N;;;;; +10310;OLD ITALIC LETTER PE;Lo;0;L;;;;;N;;;;; +10311;OLD ITALIC LETTER SHE;Lo;0;L;;;;;N;;;;; +10312;OLD ITALIC LETTER KU;Lo;0;L;;;;;N;;;;; +10313;OLD ITALIC LETTER ER;Lo;0;L;;;;;N;;;;; +10314;OLD ITALIC LETTER ES;Lo;0;L;;;;;N;;;;; +10315;OLD ITALIC LETTER TE;Lo;0;L;;;;;N;;;;; +10316;OLD ITALIC LETTER U;Lo;0;L;;;;;N;;;;; +10317;OLD ITALIC LETTER EKS;Lo;0;L;;;;;N;;;;; +10318;OLD ITALIC LETTER PHE;Lo;0;L;;;;;N;;;;; +10319;OLD ITALIC LETTER KHE;Lo;0;L;;;;;N;;;;; +1031A;OLD ITALIC LETTER EF;Lo;0;L;;;;;N;;;;; +1031B;OLD ITALIC LETTER ERS;Lo;0;L;;;;;N;;;;; +1031C;OLD ITALIC LETTER CHE;Lo;0;L;;;;;N;;;;; +1031D;OLD ITALIC LETTER II;Lo;0;L;;;;;N;;;;; +1031E;OLD ITALIC LETTER UU;Lo;0;L;;;;;N;;;;; +1031F;OLD ITALIC LETTER ESS;Lo;0;L;;;;;N;;;;; +10320;OLD ITALIC NUMERAL ONE;No;0;L;;;;1;N;;;;; +10321;OLD ITALIC NUMERAL FIVE;No;0;L;;;;5;N;;;;; +10322;OLD ITALIC NUMERAL TEN;No;0;L;;;;10;N;;;;; +10323;OLD ITALIC NUMERAL FIFTY;No;0;L;;;;50;N;;;;; +10330;GOTHIC LETTER AHSA;Lo;0;L;;;;;N;;;;; +10331;GOTHIC LETTER BAIRKAN;Lo;0;L;;;;;N;;;;; +10332;GOTHIC LETTER GIBA;Lo;0;L;;;;;N;;;;; +10333;GOTHIC LETTER DAGS;Lo;0;L;;;;;N;;;;; +10334;GOTHIC LETTER AIHVUS;Lo;0;L;;;;;N;;;;; +10335;GOTHIC LETTER QAIRTHRA;Lo;0;L;;;;;N;;;;; +10336;GOTHIC LETTER IUJA;Lo;0;L;;;;;N;;;;; +10337;GOTHIC LETTER HAGL;Lo;0;L;;;;;N;;;;; +10338;GOTHIC LETTER THIUTH;Lo;0;L;;;;;N;;;;; +10339;GOTHIC LETTER EIS;Lo;0;L;;;;;N;;;;; +1033A;GOTHIC LETTER KUSMA;Lo;0;L;;;;;N;;;;; +1033B;GOTHIC LETTER LAGUS;Lo;0;L;;;;;N;;;;; +1033C;GOTHIC LETTER MANNA;Lo;0;L;;;;;N;;;;; +1033D;GOTHIC LETTER NAUTHS;Lo;0;L;;;;;N;;;;; +1033E;GOTHIC LETTER JER;Lo;0;L;;;;;N;;;;; +1033F;GOTHIC LETTER URUS;Lo;0;L;;;;;N;;;;; +10340;GOTHIC LETTER PAIRTHRA;Lo;0;L;;;;;N;;;;; +10341;GOTHIC LETTER NINETY;Nl;0;L;;;;90;N;;;;; +10342;GOTHIC LETTER RAIDA;Lo;0;L;;;;;N;;;;; +10343;GOTHIC LETTER SAUIL;Lo;0;L;;;;;N;;;;; +10344;GOTHIC LETTER TEIWS;Lo;0;L;;;;;N;;;;; +10345;GOTHIC LETTER WINJA;Lo;0;L;;;;;N;;;;; +10346;GOTHIC LETTER FAIHU;Lo;0;L;;;;;N;;;;; +10347;GOTHIC LETTER IGGWS;Lo;0;L;;;;;N;;;;; +10348;GOTHIC LETTER HWAIR;Lo;0;L;;;;;N;;;;; +10349;GOTHIC LETTER OTHAL;Lo;0;L;;;;;N;;;;; +1034A;GOTHIC LETTER NINE HUNDRED;Nl;0;L;;;;900;N;;;;; +10350;OLD PERMIC LETTER AN;Lo;0;L;;;;;N;;;;; +10351;OLD PERMIC LETTER BUR;Lo;0;L;;;;;N;;;;; +10352;OLD PERMIC LETTER GAI;Lo;0;L;;;;;N;;;;; +10353;OLD PERMIC LETTER DOI;Lo;0;L;;;;;N;;;;; +10354;OLD PERMIC LETTER E;Lo;0;L;;;;;N;;;;; +10355;OLD PERMIC LETTER ZHOI;Lo;0;L;;;;;N;;;;; +10356;OLD PERMIC LETTER DZHOI;Lo;0;L;;;;;N;;;;; +10357;OLD PERMIC LETTER ZATA;Lo;0;L;;;;;N;;;;; +10358;OLD PERMIC LETTER DZITA;Lo;0;L;;;;;N;;;;; +10359;OLD PERMIC LETTER I;Lo;0;L;;;;;N;;;;; +1035A;OLD PERMIC LETTER KOKE;Lo;0;L;;;;;N;;;;; +1035B;OLD PERMIC LETTER LEI;Lo;0;L;;;;;N;;;;; +1035C;OLD PERMIC LETTER MENOE;Lo;0;L;;;;;N;;;;; +1035D;OLD PERMIC LETTER NENOE;Lo;0;L;;;;;N;;;;; +1035E;OLD PERMIC LETTER VOOI;Lo;0;L;;;;;N;;;;; +1035F;OLD PERMIC LETTER PEEI;Lo;0;L;;;;;N;;;;; +10360;OLD PERMIC LETTER REI;Lo;0;L;;;;;N;;;;; +10361;OLD PERMIC LETTER SII;Lo;0;L;;;;;N;;;;; +10362;OLD PERMIC LETTER TAI;Lo;0;L;;;;;N;;;;; +10363;OLD PERMIC LETTER U;Lo;0;L;;;;;N;;;;; +10364;OLD PERMIC LETTER CHERY;Lo;0;L;;;;;N;;;;; +10365;OLD PERMIC LETTER SHOOI;Lo;0;L;;;;;N;;;;; +10366;OLD PERMIC LETTER SHCHOOI;Lo;0;L;;;;;N;;;;; +10367;OLD PERMIC LETTER YRY;Lo;0;L;;;;;N;;;;; +10368;OLD PERMIC LETTER YERU;Lo;0;L;;;;;N;;;;; +10369;OLD PERMIC LETTER O;Lo;0;L;;;;;N;;;;; +1036A;OLD PERMIC LETTER OO;Lo;0;L;;;;;N;;;;; +1036B;OLD PERMIC LETTER EF;Lo;0;L;;;;;N;;;;; +1036C;OLD PERMIC LETTER HA;Lo;0;L;;;;;N;;;;; +1036D;OLD PERMIC LETTER TSIU;Lo;0;L;;;;;N;;;;; +1036E;OLD PERMIC LETTER VER;Lo;0;L;;;;;N;;;;; +1036F;OLD PERMIC LETTER YER;Lo;0;L;;;;;N;;;;; +10370;OLD PERMIC LETTER YERI;Lo;0;L;;;;;N;;;;; +10371;OLD PERMIC LETTER YAT;Lo;0;L;;;;;N;;;;; +10372;OLD PERMIC LETTER IE;Lo;0;L;;;;;N;;;;; +10373;OLD PERMIC LETTER YU;Lo;0;L;;;;;N;;;;; +10374;OLD PERMIC LETTER YA;Lo;0;L;;;;;N;;;;; +10375;OLD PERMIC LETTER IA;Lo;0;L;;;;;N;;;;; +10376;COMBINING OLD PERMIC LETTER AN;Mn;230;NSM;;;;;N;;;;; +10377;COMBINING OLD PERMIC LETTER DOI;Mn;230;NSM;;;;;N;;;;; +10378;COMBINING OLD PERMIC LETTER ZATA;Mn;230;NSM;;;;;N;;;;; +10379;COMBINING OLD PERMIC LETTER NENOE;Mn;230;NSM;;;;;N;;;;; +1037A;COMBINING OLD PERMIC LETTER SII;Mn;230;NSM;;;;;N;;;;; +10380;UGARITIC LETTER ALPA;Lo;0;L;;;;;N;;;;; +10381;UGARITIC LETTER BETA;Lo;0;L;;;;;N;;;;; +10382;UGARITIC LETTER GAMLA;Lo;0;L;;;;;N;;;;; +10383;UGARITIC LETTER KHA;Lo;0;L;;;;;N;;;;; +10384;UGARITIC LETTER DELTA;Lo;0;L;;;;;N;;;;; +10385;UGARITIC LETTER HO;Lo;0;L;;;;;N;;;;; +10386;UGARITIC LETTER WO;Lo;0;L;;;;;N;;;;; +10387;UGARITIC LETTER ZETA;Lo;0;L;;;;;N;;;;; +10388;UGARITIC LETTER HOTA;Lo;0;L;;;;;N;;;;; +10389;UGARITIC LETTER TET;Lo;0;L;;;;;N;;;;; +1038A;UGARITIC LETTER YOD;Lo;0;L;;;;;N;;;;; +1038B;UGARITIC LETTER KAF;Lo;0;L;;;;;N;;;;; +1038C;UGARITIC LETTER SHIN;Lo;0;L;;;;;N;;;;; +1038D;UGARITIC LETTER LAMDA;Lo;0;L;;;;;N;;;;; +1038E;UGARITIC LETTER MEM;Lo;0;L;;;;;N;;;;; +1038F;UGARITIC LETTER DHAL;Lo;0;L;;;;;N;;;;; +10390;UGARITIC LETTER NUN;Lo;0;L;;;;;N;;;;; +10391;UGARITIC LETTER ZU;Lo;0;L;;;;;N;;;;; +10392;UGARITIC LETTER SAMKA;Lo;0;L;;;;;N;;;;; +10393;UGARITIC LETTER AIN;Lo;0;L;;;;;N;;;;; +10394;UGARITIC LETTER PU;Lo;0;L;;;;;N;;;;; +10395;UGARITIC LETTER SADE;Lo;0;L;;;;;N;;;;; +10396;UGARITIC LETTER QOPA;Lo;0;L;;;;;N;;;;; +10397;UGARITIC LETTER RASHA;Lo;0;L;;;;;N;;;;; +10398;UGARITIC LETTER THANNA;Lo;0;L;;;;;N;;;;; +10399;UGARITIC LETTER GHAIN;Lo;0;L;;;;;N;;;;; +1039A;UGARITIC LETTER TO;Lo;0;L;;;;;N;;;;; +1039B;UGARITIC LETTER I;Lo;0;L;;;;;N;;;;; +1039C;UGARITIC LETTER U;Lo;0;L;;;;;N;;;;; +1039D;UGARITIC LETTER SSU;Lo;0;L;;;;;N;;;;; +1039F;UGARITIC WORD DIVIDER;Po;0;L;;;;;N;;;;; +103A0;OLD PERSIAN SIGN A;Lo;0;L;;;;;N;;;;; +103A1;OLD PERSIAN SIGN I;Lo;0;L;;;;;N;;;;; +103A2;OLD PERSIAN SIGN U;Lo;0;L;;;;;N;;;;; +103A3;OLD PERSIAN SIGN KA;Lo;0;L;;;;;N;;;;; +103A4;OLD PERSIAN SIGN KU;Lo;0;L;;;;;N;;;;; +103A5;OLD PERSIAN SIGN GA;Lo;0;L;;;;;N;;;;; +103A6;OLD PERSIAN SIGN GU;Lo;0;L;;;;;N;;;;; +103A7;OLD PERSIAN SIGN XA;Lo;0;L;;;;;N;;;;; +103A8;OLD PERSIAN SIGN CA;Lo;0;L;;;;;N;;;;; +103A9;OLD PERSIAN SIGN JA;Lo;0;L;;;;;N;;;;; +103AA;OLD PERSIAN SIGN JI;Lo;0;L;;;;;N;;;;; +103AB;OLD PERSIAN SIGN TA;Lo;0;L;;;;;N;;;;; +103AC;OLD PERSIAN SIGN TU;Lo;0;L;;;;;N;;;;; +103AD;OLD PERSIAN SIGN DA;Lo;0;L;;;;;N;;;;; +103AE;OLD PERSIAN SIGN DI;Lo;0;L;;;;;N;;;;; +103AF;OLD PERSIAN SIGN DU;Lo;0;L;;;;;N;;;;; +103B0;OLD PERSIAN SIGN THA;Lo;0;L;;;;;N;;;;; +103B1;OLD PERSIAN SIGN PA;Lo;0;L;;;;;N;;;;; +103B2;OLD PERSIAN SIGN BA;Lo;0;L;;;;;N;;;;; +103B3;OLD PERSIAN SIGN FA;Lo;0;L;;;;;N;;;;; +103B4;OLD PERSIAN SIGN NA;Lo;0;L;;;;;N;;;;; +103B5;OLD PERSIAN SIGN NU;Lo;0;L;;;;;N;;;;; +103B6;OLD PERSIAN SIGN MA;Lo;0;L;;;;;N;;;;; +103B7;OLD PERSIAN SIGN MI;Lo;0;L;;;;;N;;;;; +103B8;OLD PERSIAN SIGN MU;Lo;0;L;;;;;N;;;;; +103B9;OLD PERSIAN SIGN YA;Lo;0;L;;;;;N;;;;; +103BA;OLD PERSIAN SIGN VA;Lo;0;L;;;;;N;;;;; +103BB;OLD PERSIAN SIGN VI;Lo;0;L;;;;;N;;;;; +103BC;OLD PERSIAN SIGN RA;Lo;0;L;;;;;N;;;;; +103BD;OLD PERSIAN SIGN RU;Lo;0;L;;;;;N;;;;; +103BE;OLD PERSIAN SIGN LA;Lo;0;L;;;;;N;;;;; +103BF;OLD PERSIAN SIGN SA;Lo;0;L;;;;;N;;;;; +103C0;OLD PERSIAN SIGN ZA;Lo;0;L;;;;;N;;;;; +103C1;OLD PERSIAN SIGN SHA;Lo;0;L;;;;;N;;;;; +103C2;OLD PERSIAN SIGN SSA;Lo;0;L;;;;;N;;;;; +103C3;OLD PERSIAN SIGN HA;Lo;0;L;;;;;N;;;;; +103C8;OLD PERSIAN SIGN AURAMAZDAA;Lo;0;L;;;;;N;;;;; +103C9;OLD PERSIAN SIGN AURAMAZDAA-2;Lo;0;L;;;;;N;;;;; +103CA;OLD PERSIAN SIGN AURAMAZDAAHA;Lo;0;L;;;;;N;;;;; +103CB;OLD PERSIAN SIGN XSHAAYATHIYA;Lo;0;L;;;;;N;;;;; +103CC;OLD PERSIAN SIGN DAHYAAUSH;Lo;0;L;;;;;N;;;;; +103CD;OLD PERSIAN SIGN DAHYAAUSH-2;Lo;0;L;;;;;N;;;;; +103CE;OLD PERSIAN SIGN BAGA;Lo;0;L;;;;;N;;;;; +103CF;OLD PERSIAN SIGN BUUMISH;Lo;0;L;;;;;N;;;;; +103D0;OLD PERSIAN WORD DIVIDER;Po;0;L;;;;;N;;;;; +103D1;OLD PERSIAN NUMBER ONE;Nl;0;L;;;;1;N;;;;; +103D2;OLD PERSIAN NUMBER TWO;Nl;0;L;;;;2;N;;;;; +103D3;OLD PERSIAN NUMBER TEN;Nl;0;L;;;;10;N;;;;; +103D4;OLD PERSIAN NUMBER TWENTY;Nl;0;L;;;;20;N;;;;; +103D5;OLD PERSIAN NUMBER HUNDRED;Nl;0;L;;;;100;N;;;;; +10400;DESERET CAPITAL LETTER LONG I;Lu;0;L;;;;;N;;;;10428; +10401;DESERET CAPITAL LETTER LONG E;Lu;0;L;;;;;N;;;;10429; +10402;DESERET CAPITAL LETTER LONG A;Lu;0;L;;;;;N;;;;1042A; +10403;DESERET CAPITAL LETTER LONG AH;Lu;0;L;;;;;N;;;;1042B; +10404;DESERET CAPITAL LETTER LONG O;Lu;0;L;;;;;N;;;;1042C; +10405;DESERET CAPITAL LETTER LONG OO;Lu;0;L;;;;;N;;;;1042D; +10406;DESERET CAPITAL LETTER SHORT I;Lu;0;L;;;;;N;;;;1042E; +10407;DESERET CAPITAL LETTER SHORT E;Lu;0;L;;;;;N;;;;1042F; +10408;DESERET CAPITAL LETTER SHORT A;Lu;0;L;;;;;N;;;;10430; +10409;DESERET CAPITAL LETTER SHORT AH;Lu;0;L;;;;;N;;;;10431; +1040A;DESERET CAPITAL LETTER SHORT O;Lu;0;L;;;;;N;;;;10432; +1040B;DESERET CAPITAL LETTER SHORT OO;Lu;0;L;;;;;N;;;;10433; +1040C;DESERET CAPITAL LETTER AY;Lu;0;L;;;;;N;;;;10434; +1040D;DESERET CAPITAL LETTER OW;Lu;0;L;;;;;N;;;;10435; +1040E;DESERET CAPITAL LETTER WU;Lu;0;L;;;;;N;;;;10436; +1040F;DESERET CAPITAL LETTER YEE;Lu;0;L;;;;;N;;;;10437; +10410;DESERET CAPITAL LETTER H;Lu;0;L;;;;;N;;;;10438; +10411;DESERET CAPITAL LETTER PEE;Lu;0;L;;;;;N;;;;10439; +10412;DESERET CAPITAL LETTER BEE;Lu;0;L;;;;;N;;;;1043A; +10413;DESERET CAPITAL LETTER TEE;Lu;0;L;;;;;N;;;;1043B; +10414;DESERET CAPITAL LETTER DEE;Lu;0;L;;;;;N;;;;1043C; +10415;DESERET CAPITAL LETTER CHEE;Lu;0;L;;;;;N;;;;1043D; +10416;DESERET CAPITAL LETTER JEE;Lu;0;L;;;;;N;;;;1043E; +10417;DESERET CAPITAL LETTER KAY;Lu;0;L;;;;;N;;;;1043F; +10418;DESERET CAPITAL LETTER GAY;Lu;0;L;;;;;N;;;;10440; +10419;DESERET CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;10441; +1041A;DESERET CAPITAL LETTER VEE;Lu;0;L;;;;;N;;;;10442; +1041B;DESERET CAPITAL LETTER ETH;Lu;0;L;;;;;N;;;;10443; +1041C;DESERET CAPITAL LETTER THEE;Lu;0;L;;;;;N;;;;10444; +1041D;DESERET CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;10445; +1041E;DESERET CAPITAL LETTER ZEE;Lu;0;L;;;;;N;;;;10446; +1041F;DESERET CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;10447; +10420;DESERET CAPITAL LETTER ZHEE;Lu;0;L;;;;;N;;;;10448; +10421;DESERET CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;10449; +10422;DESERET CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;1044A; +10423;DESERET CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;1044B; +10424;DESERET CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;1044C; +10425;DESERET CAPITAL LETTER ENG;Lu;0;L;;;;;N;;;;1044D; +10426;DESERET CAPITAL LETTER OI;Lu;0;L;;;;;N;;;;1044E; +10427;DESERET CAPITAL LETTER EW;Lu;0;L;;;;;N;;;;1044F; +10428;DESERET SMALL LETTER LONG I;Ll;0;L;;;;;N;;;10400;;10400 +10429;DESERET SMALL LETTER LONG E;Ll;0;L;;;;;N;;;10401;;10401 +1042A;DESERET SMALL LETTER LONG A;Ll;0;L;;;;;N;;;10402;;10402 +1042B;DESERET SMALL LETTER LONG AH;Ll;0;L;;;;;N;;;10403;;10403 +1042C;DESERET SMALL LETTER LONG O;Ll;0;L;;;;;N;;;10404;;10404 +1042D;DESERET SMALL LETTER LONG OO;Ll;0;L;;;;;N;;;10405;;10405 +1042E;DESERET SMALL LETTER SHORT I;Ll;0;L;;;;;N;;;10406;;10406 +1042F;DESERET SMALL LETTER SHORT E;Ll;0;L;;;;;N;;;10407;;10407 +10430;DESERET SMALL LETTER SHORT A;Ll;0;L;;;;;N;;;10408;;10408 +10431;DESERET SMALL LETTER SHORT AH;Ll;0;L;;;;;N;;;10409;;10409 +10432;DESERET SMALL LETTER SHORT O;Ll;0;L;;;;;N;;;1040A;;1040A +10433;DESERET SMALL LETTER SHORT OO;Ll;0;L;;;;;N;;;1040B;;1040B +10434;DESERET SMALL LETTER AY;Ll;0;L;;;;;N;;;1040C;;1040C +10435;DESERET SMALL LETTER OW;Ll;0;L;;;;;N;;;1040D;;1040D +10436;DESERET SMALL LETTER WU;Ll;0;L;;;;;N;;;1040E;;1040E +10437;DESERET SMALL LETTER YEE;Ll;0;L;;;;;N;;;1040F;;1040F +10438;DESERET SMALL LETTER H;Ll;0;L;;;;;N;;;10410;;10410 +10439;DESERET SMALL LETTER PEE;Ll;0;L;;;;;N;;;10411;;10411 +1043A;DESERET SMALL LETTER BEE;Ll;0;L;;;;;N;;;10412;;10412 +1043B;DESERET SMALL LETTER TEE;Ll;0;L;;;;;N;;;10413;;10413 +1043C;DESERET SMALL LETTER DEE;Ll;0;L;;;;;N;;;10414;;10414 +1043D;DESERET SMALL LETTER CHEE;Ll;0;L;;;;;N;;;10415;;10415 +1043E;DESERET SMALL LETTER JEE;Ll;0;L;;;;;N;;;10416;;10416 +1043F;DESERET SMALL LETTER KAY;Ll;0;L;;;;;N;;;10417;;10417 +10440;DESERET SMALL LETTER GAY;Ll;0;L;;;;;N;;;10418;;10418 +10441;DESERET SMALL LETTER EF;Ll;0;L;;;;;N;;;10419;;10419 +10442;DESERET SMALL LETTER VEE;Ll;0;L;;;;;N;;;1041A;;1041A +10443;DESERET SMALL LETTER ETH;Ll;0;L;;;;;N;;;1041B;;1041B +10444;DESERET SMALL LETTER THEE;Ll;0;L;;;;;N;;;1041C;;1041C +10445;DESERET SMALL LETTER ES;Ll;0;L;;;;;N;;;1041D;;1041D +10446;DESERET SMALL LETTER ZEE;Ll;0;L;;;;;N;;;1041E;;1041E +10447;DESERET SMALL LETTER ESH;Ll;0;L;;;;;N;;;1041F;;1041F +10448;DESERET SMALL LETTER ZHEE;Ll;0;L;;;;;N;;;10420;;10420 +10449;DESERET SMALL LETTER ER;Ll;0;L;;;;;N;;;10421;;10421 +1044A;DESERET SMALL LETTER EL;Ll;0;L;;;;;N;;;10422;;10422 +1044B;DESERET SMALL LETTER EM;Ll;0;L;;;;;N;;;10423;;10423 +1044C;DESERET SMALL LETTER EN;Ll;0;L;;;;;N;;;10424;;10424 +1044D;DESERET SMALL LETTER ENG;Ll;0;L;;;;;N;;;10425;;10425 +1044E;DESERET SMALL LETTER OI;Ll;0;L;;;;;N;;;10426;;10426 +1044F;DESERET SMALL LETTER EW;Ll;0;L;;;;;N;;;10427;;10427 +10450;SHAVIAN LETTER PEEP;Lo;0;L;;;;;N;;;;; +10451;SHAVIAN LETTER TOT;Lo;0;L;;;;;N;;;;; +10452;SHAVIAN LETTER KICK;Lo;0;L;;;;;N;;;;; +10453;SHAVIAN LETTER FEE;Lo;0;L;;;;;N;;;;; +10454;SHAVIAN LETTER THIGH;Lo;0;L;;;;;N;;;;; +10455;SHAVIAN LETTER SO;Lo;0;L;;;;;N;;;;; +10456;SHAVIAN LETTER SURE;Lo;0;L;;;;;N;;;;; +10457;SHAVIAN LETTER CHURCH;Lo;0;L;;;;;N;;;;; +10458;SHAVIAN LETTER YEA;Lo;0;L;;;;;N;;;;; +10459;SHAVIAN LETTER HUNG;Lo;0;L;;;;;N;;;;; +1045A;SHAVIAN LETTER BIB;Lo;0;L;;;;;N;;;;; +1045B;SHAVIAN LETTER DEAD;Lo;0;L;;;;;N;;;;; +1045C;SHAVIAN LETTER GAG;Lo;0;L;;;;;N;;;;; +1045D;SHAVIAN LETTER VOW;Lo;0;L;;;;;N;;;;; +1045E;SHAVIAN LETTER THEY;Lo;0;L;;;;;N;;;;; +1045F;SHAVIAN LETTER ZOO;Lo;0;L;;;;;N;;;;; +10460;SHAVIAN LETTER MEASURE;Lo;0;L;;;;;N;;;;; +10461;SHAVIAN LETTER JUDGE;Lo;0;L;;;;;N;;;;; +10462;SHAVIAN LETTER WOE;Lo;0;L;;;;;N;;;;; +10463;SHAVIAN LETTER HA-HA;Lo;0;L;;;;;N;;;;; +10464;SHAVIAN LETTER LOLL;Lo;0;L;;;;;N;;;;; +10465;SHAVIAN LETTER MIME;Lo;0;L;;;;;N;;;;; +10466;SHAVIAN LETTER IF;Lo;0;L;;;;;N;;;;; +10467;SHAVIAN LETTER EGG;Lo;0;L;;;;;N;;;;; +10468;SHAVIAN LETTER ASH;Lo;0;L;;;;;N;;;;; +10469;SHAVIAN LETTER ADO;Lo;0;L;;;;;N;;;;; +1046A;SHAVIAN LETTER ON;Lo;0;L;;;;;N;;;;; +1046B;SHAVIAN LETTER WOOL;Lo;0;L;;;;;N;;;;; +1046C;SHAVIAN LETTER OUT;Lo;0;L;;;;;N;;;;; +1046D;SHAVIAN LETTER AH;Lo;0;L;;;;;N;;;;; +1046E;SHAVIAN LETTER ROAR;Lo;0;L;;;;;N;;;;; +1046F;SHAVIAN LETTER NUN;Lo;0;L;;;;;N;;;;; +10470;SHAVIAN LETTER EAT;Lo;0;L;;;;;N;;;;; +10471;SHAVIAN LETTER AGE;Lo;0;L;;;;;N;;;;; +10472;SHAVIAN LETTER ICE;Lo;0;L;;;;;N;;;;; +10473;SHAVIAN LETTER UP;Lo;0;L;;;;;N;;;;; +10474;SHAVIAN LETTER OAK;Lo;0;L;;;;;N;;;;; +10475;SHAVIAN LETTER OOZE;Lo;0;L;;;;;N;;;;; +10476;SHAVIAN LETTER OIL;Lo;0;L;;;;;N;;;;; +10477;SHAVIAN LETTER AWE;Lo;0;L;;;;;N;;;;; +10478;SHAVIAN LETTER ARE;Lo;0;L;;;;;N;;;;; +10479;SHAVIAN LETTER OR;Lo;0;L;;;;;N;;;;; +1047A;SHAVIAN LETTER AIR;Lo;0;L;;;;;N;;;;; +1047B;SHAVIAN LETTER ERR;Lo;0;L;;;;;N;;;;; +1047C;SHAVIAN LETTER ARRAY;Lo;0;L;;;;;N;;;;; +1047D;SHAVIAN LETTER EAR;Lo;0;L;;;;;N;;;;; +1047E;SHAVIAN LETTER IAN;Lo;0;L;;;;;N;;;;; +1047F;SHAVIAN LETTER YEW;Lo;0;L;;;;;N;;;;; +10480;OSMANYA LETTER ALEF;Lo;0;L;;;;;N;;;;; +10481;OSMANYA LETTER BA;Lo;0;L;;;;;N;;;;; +10482;OSMANYA LETTER TA;Lo;0;L;;;;;N;;;;; +10483;OSMANYA LETTER JA;Lo;0;L;;;;;N;;;;; +10484;OSMANYA LETTER XA;Lo;0;L;;;;;N;;;;; +10485;OSMANYA LETTER KHA;Lo;0;L;;;;;N;;;;; +10486;OSMANYA LETTER DEEL;Lo;0;L;;;;;N;;;;; +10487;OSMANYA LETTER RA;Lo;0;L;;;;;N;;;;; +10488;OSMANYA LETTER SA;Lo;0;L;;;;;N;;;;; +10489;OSMANYA LETTER SHIIN;Lo;0;L;;;;;N;;;;; +1048A;OSMANYA LETTER DHA;Lo;0;L;;;;;N;;;;; +1048B;OSMANYA LETTER CAYN;Lo;0;L;;;;;N;;;;; +1048C;OSMANYA LETTER GA;Lo;0;L;;;;;N;;;;; +1048D;OSMANYA LETTER FA;Lo;0;L;;;;;N;;;;; +1048E;OSMANYA LETTER QAAF;Lo;0;L;;;;;N;;;;; +1048F;OSMANYA LETTER KAAF;Lo;0;L;;;;;N;;;;; +10490;OSMANYA LETTER LAAN;Lo;0;L;;;;;N;;;;; +10491;OSMANYA LETTER MIIN;Lo;0;L;;;;;N;;;;; +10492;OSMANYA LETTER NUUN;Lo;0;L;;;;;N;;;;; +10493;OSMANYA LETTER WAW;Lo;0;L;;;;;N;;;;; +10494;OSMANYA LETTER HA;Lo;0;L;;;;;N;;;;; +10495;OSMANYA LETTER YA;Lo;0;L;;;;;N;;;;; +10496;OSMANYA LETTER A;Lo;0;L;;;;;N;;;;; +10497;OSMANYA LETTER E;Lo;0;L;;;;;N;;;;; +10498;OSMANYA LETTER I;Lo;0;L;;;;;N;;;;; +10499;OSMANYA LETTER O;Lo;0;L;;;;;N;;;;; +1049A;OSMANYA LETTER U;Lo;0;L;;;;;N;;;;; +1049B;OSMANYA LETTER AA;Lo;0;L;;;;;N;;;;; +1049C;OSMANYA LETTER EE;Lo;0;L;;;;;N;;;;; +1049D;OSMANYA LETTER OO;Lo;0;L;;;;;N;;;;; +104A0;OSMANYA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +104A1;OSMANYA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +104A2;OSMANYA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +104A3;OSMANYA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +104A4;OSMANYA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +104A5;OSMANYA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +104A6;OSMANYA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +104A7;OSMANYA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +104A8;OSMANYA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +104A9;OSMANYA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +104B0;OSAGE CAPITAL LETTER A;Lu;0;L;;;;;N;;;;104D8; +104B1;OSAGE CAPITAL LETTER AI;Lu;0;L;;;;;N;;;;104D9; +104B2;OSAGE CAPITAL LETTER AIN;Lu;0;L;;;;;N;;;;104DA; +104B3;OSAGE CAPITAL LETTER AH;Lu;0;L;;;;;N;;;;104DB; +104B4;OSAGE CAPITAL LETTER BRA;Lu;0;L;;;;;N;;;;104DC; +104B5;OSAGE CAPITAL LETTER CHA;Lu;0;L;;;;;N;;;;104DD; +104B6;OSAGE CAPITAL LETTER EHCHA;Lu;0;L;;;;;N;;;;104DE; +104B7;OSAGE CAPITAL LETTER E;Lu;0;L;;;;;N;;;;104DF; +104B8;OSAGE CAPITAL LETTER EIN;Lu;0;L;;;;;N;;;;104E0; +104B9;OSAGE CAPITAL LETTER HA;Lu;0;L;;;;;N;;;;104E1; +104BA;OSAGE CAPITAL LETTER HYA;Lu;0;L;;;;;N;;;;104E2; +104BB;OSAGE CAPITAL LETTER I;Lu;0;L;;;;;N;;;;104E3; +104BC;OSAGE CAPITAL LETTER KA;Lu;0;L;;;;;N;;;;104E4; +104BD;OSAGE CAPITAL LETTER EHKA;Lu;0;L;;;;;N;;;;104E5; +104BE;OSAGE CAPITAL LETTER KYA;Lu;0;L;;;;;N;;;;104E6; +104BF;OSAGE CAPITAL LETTER LA;Lu;0;L;;;;;N;;;;104E7; +104C0;OSAGE CAPITAL LETTER MA;Lu;0;L;;;;;N;;;;104E8; +104C1;OSAGE CAPITAL LETTER NA;Lu;0;L;;;;;N;;;;104E9; +104C2;OSAGE CAPITAL LETTER O;Lu;0;L;;;;;N;;;;104EA; +104C3;OSAGE CAPITAL LETTER OIN;Lu;0;L;;;;;N;;;;104EB; +104C4;OSAGE CAPITAL LETTER PA;Lu;0;L;;;;;N;;;;104EC; +104C5;OSAGE CAPITAL LETTER EHPA;Lu;0;L;;;;;N;;;;104ED; +104C6;OSAGE CAPITAL LETTER SA;Lu;0;L;;;;;N;;;;104EE; +104C7;OSAGE CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;104EF; +104C8;OSAGE CAPITAL LETTER TA;Lu;0;L;;;;;N;;;;104F0; +104C9;OSAGE CAPITAL LETTER EHTA;Lu;0;L;;;;;N;;;;104F1; +104CA;OSAGE CAPITAL LETTER TSA;Lu;0;L;;;;;N;;;;104F2; +104CB;OSAGE CAPITAL LETTER EHTSA;Lu;0;L;;;;;N;;;;104F3; +104CC;OSAGE CAPITAL LETTER TSHA;Lu;0;L;;;;;N;;;;104F4; +104CD;OSAGE CAPITAL LETTER DHA;Lu;0;L;;;;;N;;;;104F5; +104CE;OSAGE CAPITAL LETTER U;Lu;0;L;;;;;N;;;;104F6; +104CF;OSAGE CAPITAL LETTER WA;Lu;0;L;;;;;N;;;;104F7; +104D0;OSAGE CAPITAL LETTER KHA;Lu;0;L;;;;;N;;;;104F8; +104D1;OSAGE CAPITAL LETTER GHA;Lu;0;L;;;;;N;;;;104F9; +104D2;OSAGE CAPITAL LETTER ZA;Lu;0;L;;;;;N;;;;104FA; +104D3;OSAGE CAPITAL LETTER ZHA;Lu;0;L;;;;;N;;;;104FB; +104D8;OSAGE SMALL LETTER A;Ll;0;L;;;;;N;;;104B0;;104B0 +104D9;OSAGE SMALL LETTER AI;Ll;0;L;;;;;N;;;104B1;;104B1 +104DA;OSAGE SMALL LETTER AIN;Ll;0;L;;;;;N;;;104B2;;104B2 +104DB;OSAGE SMALL LETTER AH;Ll;0;L;;;;;N;;;104B3;;104B3 +104DC;OSAGE SMALL LETTER BRA;Ll;0;L;;;;;N;;;104B4;;104B4 +104DD;OSAGE SMALL LETTER CHA;Ll;0;L;;;;;N;;;104B5;;104B5 +104DE;OSAGE SMALL LETTER EHCHA;Ll;0;L;;;;;N;;;104B6;;104B6 +104DF;OSAGE SMALL LETTER E;Ll;0;L;;;;;N;;;104B7;;104B7 +104E0;OSAGE SMALL LETTER EIN;Ll;0;L;;;;;N;;;104B8;;104B8 +104E1;OSAGE SMALL LETTER HA;Ll;0;L;;;;;N;;;104B9;;104B9 +104E2;OSAGE SMALL LETTER HYA;Ll;0;L;;;;;N;;;104BA;;104BA +104E3;OSAGE SMALL LETTER I;Ll;0;L;;;;;N;;;104BB;;104BB +104E4;OSAGE SMALL LETTER KA;Ll;0;L;;;;;N;;;104BC;;104BC +104E5;OSAGE SMALL LETTER EHKA;Ll;0;L;;;;;N;;;104BD;;104BD +104E6;OSAGE SMALL LETTER KYA;Ll;0;L;;;;;N;;;104BE;;104BE +104E7;OSAGE SMALL LETTER LA;Ll;0;L;;;;;N;;;104BF;;104BF +104E8;OSAGE SMALL LETTER MA;Ll;0;L;;;;;N;;;104C0;;104C0 +104E9;OSAGE SMALL LETTER NA;Ll;0;L;;;;;N;;;104C1;;104C1 +104EA;OSAGE SMALL LETTER O;Ll;0;L;;;;;N;;;104C2;;104C2 +104EB;OSAGE SMALL LETTER OIN;Ll;0;L;;;;;N;;;104C3;;104C3 +104EC;OSAGE SMALL LETTER PA;Ll;0;L;;;;;N;;;104C4;;104C4 +104ED;OSAGE SMALL LETTER EHPA;Ll;0;L;;;;;N;;;104C5;;104C5 +104EE;OSAGE SMALL LETTER SA;Ll;0;L;;;;;N;;;104C6;;104C6 +104EF;OSAGE SMALL LETTER SHA;Ll;0;L;;;;;N;;;104C7;;104C7 +104F0;OSAGE SMALL LETTER TA;Ll;0;L;;;;;N;;;104C8;;104C8 +104F1;OSAGE SMALL LETTER EHTA;Ll;0;L;;;;;N;;;104C9;;104C9 +104F2;OSAGE SMALL LETTER TSA;Ll;0;L;;;;;N;;;104CA;;104CA +104F3;OSAGE SMALL LETTER EHTSA;Ll;0;L;;;;;N;;;104CB;;104CB +104F4;OSAGE SMALL LETTER TSHA;Ll;0;L;;;;;N;;;104CC;;104CC +104F5;OSAGE SMALL LETTER DHA;Ll;0;L;;;;;N;;;104CD;;104CD +104F6;OSAGE SMALL LETTER U;Ll;0;L;;;;;N;;;104CE;;104CE +104F7;OSAGE SMALL LETTER WA;Ll;0;L;;;;;N;;;104CF;;104CF +104F8;OSAGE SMALL LETTER KHA;Ll;0;L;;;;;N;;;104D0;;104D0 +104F9;OSAGE SMALL LETTER GHA;Ll;0;L;;;;;N;;;104D1;;104D1 +104FA;OSAGE SMALL LETTER ZA;Ll;0;L;;;;;N;;;104D2;;104D2 +104FB;OSAGE SMALL LETTER ZHA;Ll;0;L;;;;;N;;;104D3;;104D3 +10500;ELBASAN LETTER A;Lo;0;L;;;;;N;;;;; +10501;ELBASAN LETTER BE;Lo;0;L;;;;;N;;;;; +10502;ELBASAN LETTER CE;Lo;0;L;;;;;N;;;;; +10503;ELBASAN LETTER CHE;Lo;0;L;;;;;N;;;;; +10504;ELBASAN LETTER DE;Lo;0;L;;;;;N;;;;; +10505;ELBASAN LETTER NDE;Lo;0;L;;;;;N;;;;; +10506;ELBASAN LETTER DHE;Lo;0;L;;;;;N;;;;; +10507;ELBASAN LETTER EI;Lo;0;L;;;;;N;;;;; +10508;ELBASAN LETTER E;Lo;0;L;;;;;N;;;;; +10509;ELBASAN LETTER FE;Lo;0;L;;;;;N;;;;; +1050A;ELBASAN LETTER GE;Lo;0;L;;;;;N;;;;; +1050B;ELBASAN LETTER GJE;Lo;0;L;;;;;N;;;;; +1050C;ELBASAN LETTER HE;Lo;0;L;;;;;N;;;;; +1050D;ELBASAN LETTER I;Lo;0;L;;;;;N;;;;; +1050E;ELBASAN LETTER JE;Lo;0;L;;;;;N;;;;; +1050F;ELBASAN LETTER KE;Lo;0;L;;;;;N;;;;; +10510;ELBASAN LETTER LE;Lo;0;L;;;;;N;;;;; +10511;ELBASAN LETTER LLE;Lo;0;L;;;;;N;;;;; +10512;ELBASAN LETTER ME;Lo;0;L;;;;;N;;;;; +10513;ELBASAN LETTER NE;Lo;0;L;;;;;N;;;;; +10514;ELBASAN LETTER NA;Lo;0;L;;;;;N;;;;; +10515;ELBASAN LETTER NJE;Lo;0;L;;;;;N;;;;; +10516;ELBASAN LETTER O;Lo;0;L;;;;;N;;;;; +10517;ELBASAN LETTER PE;Lo;0;L;;;;;N;;;;; +10518;ELBASAN LETTER QE;Lo;0;L;;;;;N;;;;; +10519;ELBASAN LETTER RE;Lo;0;L;;;;;N;;;;; +1051A;ELBASAN LETTER RRE;Lo;0;L;;;;;N;;;;; +1051B;ELBASAN LETTER SE;Lo;0;L;;;;;N;;;;; +1051C;ELBASAN LETTER SHE;Lo;0;L;;;;;N;;;;; +1051D;ELBASAN LETTER TE;Lo;0;L;;;;;N;;;;; +1051E;ELBASAN LETTER THE;Lo;0;L;;;;;N;;;;; +1051F;ELBASAN LETTER U;Lo;0;L;;;;;N;;;;; +10520;ELBASAN LETTER VE;Lo;0;L;;;;;N;;;;; +10521;ELBASAN LETTER XE;Lo;0;L;;;;;N;;;;; +10522;ELBASAN LETTER Y;Lo;0;L;;;;;N;;;;; +10523;ELBASAN LETTER ZE;Lo;0;L;;;;;N;;;;; +10524;ELBASAN LETTER ZHE;Lo;0;L;;;;;N;;;;; +10525;ELBASAN LETTER GHE;Lo;0;L;;;;;N;;;;; +10526;ELBASAN LETTER GHAMMA;Lo;0;L;;;;;N;;;;; +10527;ELBASAN LETTER KHE;Lo;0;L;;;;;N;;;;; +10530;CAUCASIAN ALBANIAN LETTER ALT;Lo;0;L;;;;;N;;;;; +10531;CAUCASIAN ALBANIAN LETTER BET;Lo;0;L;;;;;N;;;;; +10532;CAUCASIAN ALBANIAN LETTER GIM;Lo;0;L;;;;;N;;;;; +10533;CAUCASIAN ALBANIAN LETTER DAT;Lo;0;L;;;;;N;;;;; +10534;CAUCASIAN ALBANIAN LETTER EB;Lo;0;L;;;;;N;;;;; +10535;CAUCASIAN ALBANIAN LETTER ZARL;Lo;0;L;;;;;N;;;;; +10536;CAUCASIAN ALBANIAN LETTER EYN;Lo;0;L;;;;;N;;;;; +10537;CAUCASIAN ALBANIAN LETTER ZHIL;Lo;0;L;;;;;N;;;;; +10538;CAUCASIAN ALBANIAN LETTER TAS;Lo;0;L;;;;;N;;;;; +10539;CAUCASIAN ALBANIAN LETTER CHA;Lo;0;L;;;;;N;;;;; +1053A;CAUCASIAN ALBANIAN LETTER YOWD;Lo;0;L;;;;;N;;;;; +1053B;CAUCASIAN ALBANIAN LETTER ZHA;Lo;0;L;;;;;N;;;;; +1053C;CAUCASIAN ALBANIAN LETTER IRB;Lo;0;L;;;;;N;;;;; +1053D;CAUCASIAN ALBANIAN LETTER SHA;Lo;0;L;;;;;N;;;;; +1053E;CAUCASIAN ALBANIAN LETTER LAN;Lo;0;L;;;;;N;;;;; +1053F;CAUCASIAN ALBANIAN LETTER INYA;Lo;0;L;;;;;N;;;;; +10540;CAUCASIAN ALBANIAN LETTER XEYN;Lo;0;L;;;;;N;;;;; +10541;CAUCASIAN ALBANIAN LETTER DYAN;Lo;0;L;;;;;N;;;;; +10542;CAUCASIAN ALBANIAN LETTER CAR;Lo;0;L;;;;;N;;;;; +10543;CAUCASIAN ALBANIAN LETTER JHOX;Lo;0;L;;;;;N;;;;; +10544;CAUCASIAN ALBANIAN LETTER KAR;Lo;0;L;;;;;N;;;;; +10545;CAUCASIAN ALBANIAN LETTER LYIT;Lo;0;L;;;;;N;;;;; +10546;CAUCASIAN ALBANIAN LETTER HEYT;Lo;0;L;;;;;N;;;;; +10547;CAUCASIAN ALBANIAN LETTER QAY;Lo;0;L;;;;;N;;;;; +10548;CAUCASIAN ALBANIAN LETTER AOR;Lo;0;L;;;;;N;;;;; +10549;CAUCASIAN ALBANIAN LETTER CHOY;Lo;0;L;;;;;N;;;;; +1054A;CAUCASIAN ALBANIAN LETTER CHI;Lo;0;L;;;;;N;;;;; +1054B;CAUCASIAN ALBANIAN LETTER CYAY;Lo;0;L;;;;;N;;;;; +1054C;CAUCASIAN ALBANIAN LETTER MAQ;Lo;0;L;;;;;N;;;;; +1054D;CAUCASIAN ALBANIAN LETTER QAR;Lo;0;L;;;;;N;;;;; +1054E;CAUCASIAN ALBANIAN LETTER NOWC;Lo;0;L;;;;;N;;;;; +1054F;CAUCASIAN ALBANIAN LETTER DZYAY;Lo;0;L;;;;;N;;;;; +10550;CAUCASIAN ALBANIAN LETTER SHAK;Lo;0;L;;;;;N;;;;; +10551;CAUCASIAN ALBANIAN LETTER JAYN;Lo;0;L;;;;;N;;;;; +10552;CAUCASIAN ALBANIAN LETTER ON;Lo;0;L;;;;;N;;;;; +10553;CAUCASIAN ALBANIAN LETTER TYAY;Lo;0;L;;;;;N;;;;; +10554;CAUCASIAN ALBANIAN LETTER FAM;Lo;0;L;;;;;N;;;;; +10555;CAUCASIAN ALBANIAN LETTER DZAY;Lo;0;L;;;;;N;;;;; +10556;CAUCASIAN ALBANIAN LETTER CHAT;Lo;0;L;;;;;N;;;;; +10557;CAUCASIAN ALBANIAN LETTER PEN;Lo;0;L;;;;;N;;;;; +10558;CAUCASIAN ALBANIAN LETTER GHEYS;Lo;0;L;;;;;N;;;;; +10559;CAUCASIAN ALBANIAN LETTER RAT;Lo;0;L;;;;;N;;;;; +1055A;CAUCASIAN ALBANIAN LETTER SEYK;Lo;0;L;;;;;N;;;;; +1055B;CAUCASIAN ALBANIAN LETTER VEYZ;Lo;0;L;;;;;N;;;;; +1055C;CAUCASIAN ALBANIAN LETTER TIWR;Lo;0;L;;;;;N;;;;; +1055D;CAUCASIAN ALBANIAN LETTER SHOY;Lo;0;L;;;;;N;;;;; +1055E;CAUCASIAN ALBANIAN LETTER IWN;Lo;0;L;;;;;N;;;;; +1055F;CAUCASIAN ALBANIAN LETTER CYAW;Lo;0;L;;;;;N;;;;; +10560;CAUCASIAN ALBANIAN LETTER CAYN;Lo;0;L;;;;;N;;;;; +10561;CAUCASIAN ALBANIAN LETTER YAYD;Lo;0;L;;;;;N;;;;; +10562;CAUCASIAN ALBANIAN LETTER PIWR;Lo;0;L;;;;;N;;;;; +10563;CAUCASIAN ALBANIAN LETTER KIW;Lo;0;L;;;;;N;;;;; +1056F;CAUCASIAN ALBANIAN CITATION MARK;Po;0;L;;;;;N;;;;; +10600;LINEAR A SIGN AB001;Lo;0;L;;;;;N;;;;; +10601;LINEAR A SIGN AB002;Lo;0;L;;;;;N;;;;; +10602;LINEAR A SIGN AB003;Lo;0;L;;;;;N;;;;; +10603;LINEAR A SIGN AB004;Lo;0;L;;;;;N;;;;; +10604;LINEAR A SIGN AB005;Lo;0;L;;;;;N;;;;; +10605;LINEAR A SIGN AB006;Lo;0;L;;;;;N;;;;; +10606;LINEAR A SIGN AB007;Lo;0;L;;;;;N;;;;; +10607;LINEAR A SIGN AB008;Lo;0;L;;;;;N;;;;; +10608;LINEAR A SIGN AB009;Lo;0;L;;;;;N;;;;; +10609;LINEAR A SIGN AB010;Lo;0;L;;;;;N;;;;; +1060A;LINEAR A SIGN AB011;Lo;0;L;;;;;N;;;;; +1060B;LINEAR A SIGN AB013;Lo;0;L;;;;;N;;;;; +1060C;LINEAR A SIGN AB016;Lo;0;L;;;;;N;;;;; +1060D;LINEAR A SIGN AB017;Lo;0;L;;;;;N;;;;; +1060E;LINEAR A SIGN AB020;Lo;0;L;;;;;N;;;;; +1060F;LINEAR A SIGN AB021;Lo;0;L;;;;;N;;;;; +10610;LINEAR A SIGN AB021F;Lo;0;L;;;;;N;;;;; +10611;LINEAR A SIGN AB021M;Lo;0;L;;;;;N;;;;; +10612;LINEAR A SIGN AB022;Lo;0;L;;;;;N;;;;; +10613;LINEAR A SIGN AB022F;Lo;0;L;;;;;N;;;;; +10614;LINEAR A SIGN AB022M;Lo;0;L;;;;;N;;;;; +10615;LINEAR A SIGN AB023;Lo;0;L;;;;;N;;;;; +10616;LINEAR A SIGN AB023M;Lo;0;L;;;;;N;;;;; +10617;LINEAR A SIGN AB024;Lo;0;L;;;;;N;;;;; +10618;LINEAR A SIGN AB026;Lo;0;L;;;;;N;;;;; +10619;LINEAR A SIGN AB027;Lo;0;L;;;;;N;;;;; +1061A;LINEAR A SIGN AB028;Lo;0;L;;;;;N;;;;; +1061B;LINEAR A SIGN A028B;Lo;0;L;;;;;N;;;;; +1061C;LINEAR A SIGN AB029;Lo;0;L;;;;;N;;;;; +1061D;LINEAR A SIGN AB030;Lo;0;L;;;;;N;;;;; +1061E;LINEAR A SIGN AB031;Lo;0;L;;;;;N;;;;; +1061F;LINEAR A SIGN AB034;Lo;0;L;;;;;N;;;;; +10620;LINEAR A SIGN AB037;Lo;0;L;;;;;N;;;;; +10621;LINEAR A SIGN AB038;Lo;0;L;;;;;N;;;;; +10622;LINEAR A SIGN AB039;Lo;0;L;;;;;N;;;;; +10623;LINEAR A SIGN AB040;Lo;0;L;;;;;N;;;;; +10624;LINEAR A SIGN AB041;Lo;0;L;;;;;N;;;;; +10625;LINEAR A SIGN AB044;Lo;0;L;;;;;N;;;;; +10626;LINEAR A SIGN AB045;Lo;0;L;;;;;N;;;;; +10627;LINEAR A SIGN AB046;Lo;0;L;;;;;N;;;;; +10628;LINEAR A SIGN AB047;Lo;0;L;;;;;N;;;;; +10629;LINEAR A SIGN AB048;Lo;0;L;;;;;N;;;;; +1062A;LINEAR A SIGN AB049;Lo;0;L;;;;;N;;;;; +1062B;LINEAR A SIGN AB050;Lo;0;L;;;;;N;;;;; +1062C;LINEAR A SIGN AB051;Lo;0;L;;;;;N;;;;; +1062D;LINEAR A SIGN AB053;Lo;0;L;;;;;N;;;;; +1062E;LINEAR A SIGN AB054;Lo;0;L;;;;;N;;;;; +1062F;LINEAR A SIGN AB055;Lo;0;L;;;;;N;;;;; +10630;LINEAR A SIGN AB056;Lo;0;L;;;;;N;;;;; +10631;LINEAR A SIGN AB057;Lo;0;L;;;;;N;;;;; +10632;LINEAR A SIGN AB058;Lo;0;L;;;;;N;;;;; +10633;LINEAR A SIGN AB059;Lo;0;L;;;;;N;;;;; +10634;LINEAR A SIGN AB060;Lo;0;L;;;;;N;;;;; +10635;LINEAR A SIGN AB061;Lo;0;L;;;;;N;;;;; +10636;LINEAR A SIGN AB065;Lo;0;L;;;;;N;;;;; +10637;LINEAR A SIGN AB066;Lo;0;L;;;;;N;;;;; +10638;LINEAR A SIGN AB067;Lo;0;L;;;;;N;;;;; +10639;LINEAR A SIGN AB069;Lo;0;L;;;;;N;;;;; +1063A;LINEAR A SIGN AB070;Lo;0;L;;;;;N;;;;; +1063B;LINEAR A SIGN AB073;Lo;0;L;;;;;N;;;;; +1063C;LINEAR A SIGN AB074;Lo;0;L;;;;;N;;;;; +1063D;LINEAR A SIGN AB076;Lo;0;L;;;;;N;;;;; +1063E;LINEAR A SIGN AB077;Lo;0;L;;;;;N;;;;; +1063F;LINEAR A SIGN AB078;Lo;0;L;;;;;N;;;;; +10640;LINEAR A SIGN AB079;Lo;0;L;;;;;N;;;;; +10641;LINEAR A SIGN AB080;Lo;0;L;;;;;N;;;;; +10642;LINEAR A SIGN AB081;Lo;0;L;;;;;N;;;;; +10643;LINEAR A SIGN AB082;Lo;0;L;;;;;N;;;;; +10644;LINEAR A SIGN AB085;Lo;0;L;;;;;N;;;;; +10645;LINEAR A SIGN AB086;Lo;0;L;;;;;N;;;;; +10646;LINEAR A SIGN AB087;Lo;0;L;;;;;N;;;;; +10647;LINEAR A SIGN A100-102;Lo;0;L;;;;;N;;;;; +10648;LINEAR A SIGN AB118;Lo;0;L;;;;;N;;;;; +10649;LINEAR A SIGN AB120;Lo;0;L;;;;;N;;;;; +1064A;LINEAR A SIGN A120B;Lo;0;L;;;;;N;;;;; +1064B;LINEAR A SIGN AB122;Lo;0;L;;;;;N;;;;; +1064C;LINEAR A SIGN AB123;Lo;0;L;;;;;N;;;;; +1064D;LINEAR A SIGN AB131A;Lo;0;L;;;;;N;;;;; +1064E;LINEAR A SIGN AB131B;Lo;0;L;;;;;N;;;;; +1064F;LINEAR A SIGN A131C;Lo;0;L;;;;;N;;;;; +10650;LINEAR A SIGN AB164;Lo;0;L;;;;;N;;;;; +10651;LINEAR A SIGN AB171;Lo;0;L;;;;;N;;;;; +10652;LINEAR A SIGN AB180;Lo;0;L;;;;;N;;;;; +10653;LINEAR A SIGN AB188;Lo;0;L;;;;;N;;;;; +10654;LINEAR A SIGN AB191;Lo;0;L;;;;;N;;;;; +10655;LINEAR A SIGN A301;Lo;0;L;;;;;N;;;;; +10656;LINEAR A SIGN A302;Lo;0;L;;;;;N;;;;; +10657;LINEAR A SIGN A303;Lo;0;L;;;;;N;;;;; +10658;LINEAR A SIGN A304;Lo;0;L;;;;;N;;;;; +10659;LINEAR A SIGN A305;Lo;0;L;;;;;N;;;;; +1065A;LINEAR A SIGN A306;Lo;0;L;;;;;N;;;;; +1065B;LINEAR A SIGN A307;Lo;0;L;;;;;N;;;;; +1065C;LINEAR A SIGN A308;Lo;0;L;;;;;N;;;;; +1065D;LINEAR A SIGN A309A;Lo;0;L;;;;;N;;;;; +1065E;LINEAR A SIGN A309B;Lo;0;L;;;;;N;;;;; +1065F;LINEAR A SIGN A309C;Lo;0;L;;;;;N;;;;; +10660;LINEAR A SIGN A310;Lo;0;L;;;;;N;;;;; +10661;LINEAR A SIGN A311;Lo;0;L;;;;;N;;;;; +10662;LINEAR A SIGN A312;Lo;0;L;;;;;N;;;;; +10663;LINEAR A SIGN A313A;Lo;0;L;;;;;N;;;;; +10664;LINEAR A SIGN A313B;Lo;0;L;;;;;N;;;;; +10665;LINEAR A SIGN A313C;Lo;0;L;;;;;N;;;;; +10666;LINEAR A SIGN A314;Lo;0;L;;;;;N;;;;; +10667;LINEAR A SIGN A315;Lo;0;L;;;;;N;;;;; +10668;LINEAR A SIGN A316;Lo;0;L;;;;;N;;;;; +10669;LINEAR A SIGN A317;Lo;0;L;;;;;N;;;;; +1066A;LINEAR A SIGN A318;Lo;0;L;;;;;N;;;;; +1066B;LINEAR A SIGN A319;Lo;0;L;;;;;N;;;;; +1066C;LINEAR A SIGN A320;Lo;0;L;;;;;N;;;;; +1066D;LINEAR A SIGN A321;Lo;0;L;;;;;N;;;;; +1066E;LINEAR A SIGN A322;Lo;0;L;;;;;N;;;;; +1066F;LINEAR A SIGN A323;Lo;0;L;;;;;N;;;;; +10670;LINEAR A SIGN A324;Lo;0;L;;;;;N;;;;; +10671;LINEAR A SIGN A325;Lo;0;L;;;;;N;;;;; +10672;LINEAR A SIGN A326;Lo;0;L;;;;;N;;;;; +10673;LINEAR A SIGN A327;Lo;0;L;;;;;N;;;;; +10674;LINEAR A SIGN A328;Lo;0;L;;;;;N;;;;; +10675;LINEAR A SIGN A329;Lo;0;L;;;;;N;;;;; +10676;LINEAR A SIGN A330;Lo;0;L;;;;;N;;;;; +10677;LINEAR A SIGN A331;Lo;0;L;;;;;N;;;;; +10678;LINEAR A SIGN A332;Lo;0;L;;;;;N;;;;; +10679;LINEAR A SIGN A333;Lo;0;L;;;;;N;;;;; +1067A;LINEAR A SIGN A334;Lo;0;L;;;;;N;;;;; +1067B;LINEAR A SIGN A335;Lo;0;L;;;;;N;;;;; +1067C;LINEAR A SIGN A336;Lo;0;L;;;;;N;;;;; +1067D;LINEAR A SIGN A337;Lo;0;L;;;;;N;;;;; +1067E;LINEAR A SIGN A338;Lo;0;L;;;;;N;;;;; +1067F;LINEAR A SIGN A339;Lo;0;L;;;;;N;;;;; +10680;LINEAR A SIGN A340;Lo;0;L;;;;;N;;;;; +10681;LINEAR A SIGN A341;Lo;0;L;;;;;N;;;;; +10682;LINEAR A SIGN A342;Lo;0;L;;;;;N;;;;; +10683;LINEAR A SIGN A343;Lo;0;L;;;;;N;;;;; +10684;LINEAR A SIGN A344;Lo;0;L;;;;;N;;;;; +10685;LINEAR A SIGN A345;Lo;0;L;;;;;N;;;;; +10686;LINEAR A SIGN A346;Lo;0;L;;;;;N;;;;; +10687;LINEAR A SIGN A347;Lo;0;L;;;;;N;;;;; +10688;LINEAR A SIGN A348;Lo;0;L;;;;;N;;;;; +10689;LINEAR A SIGN A349;Lo;0;L;;;;;N;;;;; +1068A;LINEAR A SIGN A350;Lo;0;L;;;;;N;;;;; +1068B;LINEAR A SIGN A351;Lo;0;L;;;;;N;;;;; +1068C;LINEAR A SIGN A352;Lo;0;L;;;;;N;;;;; +1068D;LINEAR A SIGN A353;Lo;0;L;;;;;N;;;;; +1068E;LINEAR A SIGN A354;Lo;0;L;;;;;N;;;;; +1068F;LINEAR A SIGN A355;Lo;0;L;;;;;N;;;;; +10690;LINEAR A SIGN A356;Lo;0;L;;;;;N;;;;; +10691;LINEAR A SIGN A357;Lo;0;L;;;;;N;;;;; +10692;LINEAR A SIGN A358;Lo;0;L;;;;;N;;;;; +10693;LINEAR A SIGN A359;Lo;0;L;;;;;N;;;;; +10694;LINEAR A SIGN A360;Lo;0;L;;;;;N;;;;; +10695;LINEAR A SIGN A361;Lo;0;L;;;;;N;;;;; +10696;LINEAR A SIGN A362;Lo;0;L;;;;;N;;;;; +10697;LINEAR A SIGN A363;Lo;0;L;;;;;N;;;;; +10698;LINEAR A SIGN A364;Lo;0;L;;;;;N;;;;; +10699;LINEAR A SIGN A365;Lo;0;L;;;;;N;;;;; +1069A;LINEAR A SIGN A366;Lo;0;L;;;;;N;;;;; +1069B;LINEAR A SIGN A367;Lo;0;L;;;;;N;;;;; +1069C;LINEAR A SIGN A368;Lo;0;L;;;;;N;;;;; +1069D;LINEAR A SIGN A369;Lo;0;L;;;;;N;;;;; +1069E;LINEAR A SIGN A370;Lo;0;L;;;;;N;;;;; +1069F;LINEAR A SIGN A371;Lo;0;L;;;;;N;;;;; +106A0;LINEAR A SIGN A400-VAS;Lo;0;L;;;;;N;;;;; +106A1;LINEAR A SIGN A401-VAS;Lo;0;L;;;;;N;;;;; +106A2;LINEAR A SIGN A402-VAS;Lo;0;L;;;;;N;;;;; +106A3;LINEAR A SIGN A403-VAS;Lo;0;L;;;;;N;;;;; +106A4;LINEAR A SIGN A404-VAS;Lo;0;L;;;;;N;;;;; +106A5;LINEAR A SIGN A405-VAS;Lo;0;L;;;;;N;;;;; +106A6;LINEAR A SIGN A406-VAS;Lo;0;L;;;;;N;;;;; +106A7;LINEAR A SIGN A407-VAS;Lo;0;L;;;;;N;;;;; +106A8;LINEAR A SIGN A408-VAS;Lo;0;L;;;;;N;;;;; +106A9;LINEAR A SIGN A409-VAS;Lo;0;L;;;;;N;;;;; +106AA;LINEAR A SIGN A410-VAS;Lo;0;L;;;;;N;;;;; +106AB;LINEAR A SIGN A411-VAS;Lo;0;L;;;;;N;;;;; +106AC;LINEAR A SIGN A412-VAS;Lo;0;L;;;;;N;;;;; +106AD;LINEAR A SIGN A413-VAS;Lo;0;L;;;;;N;;;;; +106AE;LINEAR A SIGN A414-VAS;Lo;0;L;;;;;N;;;;; +106AF;LINEAR A SIGN A415-VAS;Lo;0;L;;;;;N;;;;; +106B0;LINEAR A SIGN A416-VAS;Lo;0;L;;;;;N;;;;; +106B1;LINEAR A SIGN A417-VAS;Lo;0;L;;;;;N;;;;; +106B2;LINEAR A SIGN A418-VAS;Lo;0;L;;;;;N;;;;; +106B3;LINEAR A SIGN A501;Lo;0;L;;;;;N;;;;; +106B4;LINEAR A SIGN A502;Lo;0;L;;;;;N;;;;; +106B5;LINEAR A SIGN A503;Lo;0;L;;;;;N;;;;; +106B6;LINEAR A SIGN A504;Lo;0;L;;;;;N;;;;; +106B7;LINEAR A SIGN A505;Lo;0;L;;;;;N;;;;; +106B8;LINEAR A SIGN A506;Lo;0;L;;;;;N;;;;; +106B9;LINEAR A SIGN A508;Lo;0;L;;;;;N;;;;; +106BA;LINEAR A SIGN A509;Lo;0;L;;;;;N;;;;; +106BB;LINEAR A SIGN A510;Lo;0;L;;;;;N;;;;; +106BC;LINEAR A SIGN A511;Lo;0;L;;;;;N;;;;; +106BD;LINEAR A SIGN A512;Lo;0;L;;;;;N;;;;; +106BE;LINEAR A SIGN A513;Lo;0;L;;;;;N;;;;; +106BF;LINEAR A SIGN A515;Lo;0;L;;;;;N;;;;; +106C0;LINEAR A SIGN A516;Lo;0;L;;;;;N;;;;; +106C1;LINEAR A SIGN A520;Lo;0;L;;;;;N;;;;; +106C2;LINEAR A SIGN A521;Lo;0;L;;;;;N;;;;; +106C3;LINEAR A SIGN A523;Lo;0;L;;;;;N;;;;; +106C4;LINEAR A SIGN A524;Lo;0;L;;;;;N;;;;; +106C5;LINEAR A SIGN A525;Lo;0;L;;;;;N;;;;; +106C6;LINEAR A SIGN A526;Lo;0;L;;;;;N;;;;; +106C7;LINEAR A SIGN A527;Lo;0;L;;;;;N;;;;; +106C8;LINEAR A SIGN A528;Lo;0;L;;;;;N;;;;; +106C9;LINEAR A SIGN A529;Lo;0;L;;;;;N;;;;; +106CA;LINEAR A SIGN A530;Lo;0;L;;;;;N;;;;; +106CB;LINEAR A SIGN A531;Lo;0;L;;;;;N;;;;; +106CC;LINEAR A SIGN A532;Lo;0;L;;;;;N;;;;; +106CD;LINEAR A SIGN A534;Lo;0;L;;;;;N;;;;; +106CE;LINEAR A SIGN A535;Lo;0;L;;;;;N;;;;; +106CF;LINEAR A SIGN A536;Lo;0;L;;;;;N;;;;; +106D0;LINEAR A SIGN A537;Lo;0;L;;;;;N;;;;; +106D1;LINEAR A SIGN A538;Lo;0;L;;;;;N;;;;; +106D2;LINEAR A SIGN A539;Lo;0;L;;;;;N;;;;; +106D3;LINEAR A SIGN A540;Lo;0;L;;;;;N;;;;; +106D4;LINEAR A SIGN A541;Lo;0;L;;;;;N;;;;; +106D5;LINEAR A SIGN A542;Lo;0;L;;;;;N;;;;; +106D6;LINEAR A SIGN A545;Lo;0;L;;;;;N;;;;; +106D7;LINEAR A SIGN A547;Lo;0;L;;;;;N;;;;; +106D8;LINEAR A SIGN A548;Lo;0;L;;;;;N;;;;; +106D9;LINEAR A SIGN A549;Lo;0;L;;;;;N;;;;; +106DA;LINEAR A SIGN A550;Lo;0;L;;;;;N;;;;; +106DB;LINEAR A SIGN A551;Lo;0;L;;;;;N;;;;; +106DC;LINEAR A SIGN A552;Lo;0;L;;;;;N;;;;; +106DD;LINEAR A SIGN A553;Lo;0;L;;;;;N;;;;; +106DE;LINEAR A SIGN A554;Lo;0;L;;;;;N;;;;; +106DF;LINEAR A SIGN A555;Lo;0;L;;;;;N;;;;; +106E0;LINEAR A SIGN A556;Lo;0;L;;;;;N;;;;; +106E1;LINEAR A SIGN A557;Lo;0;L;;;;;N;;;;; +106E2;LINEAR A SIGN A559;Lo;0;L;;;;;N;;;;; +106E3;LINEAR A SIGN A563;Lo;0;L;;;;;N;;;;; +106E4;LINEAR A SIGN A564;Lo;0;L;;;;;N;;;;; +106E5;LINEAR A SIGN A565;Lo;0;L;;;;;N;;;;; +106E6;LINEAR A SIGN A566;Lo;0;L;;;;;N;;;;; +106E7;LINEAR A SIGN A568;Lo;0;L;;;;;N;;;;; +106E8;LINEAR A SIGN A569;Lo;0;L;;;;;N;;;;; +106E9;LINEAR A SIGN A570;Lo;0;L;;;;;N;;;;; +106EA;LINEAR A SIGN A571;Lo;0;L;;;;;N;;;;; +106EB;LINEAR A SIGN A572;Lo;0;L;;;;;N;;;;; +106EC;LINEAR A SIGN A573;Lo;0;L;;;;;N;;;;; +106ED;LINEAR A SIGN A574;Lo;0;L;;;;;N;;;;; +106EE;LINEAR A SIGN A575;Lo;0;L;;;;;N;;;;; +106EF;LINEAR A SIGN A576;Lo;0;L;;;;;N;;;;; +106F0;LINEAR A SIGN A577;Lo;0;L;;;;;N;;;;; +106F1;LINEAR A SIGN A578;Lo;0;L;;;;;N;;;;; +106F2;LINEAR A SIGN A579;Lo;0;L;;;;;N;;;;; +106F3;LINEAR A SIGN A580;Lo;0;L;;;;;N;;;;; +106F4;LINEAR A SIGN A581;Lo;0;L;;;;;N;;;;; +106F5;LINEAR A SIGN A582;Lo;0;L;;;;;N;;;;; +106F6;LINEAR A SIGN A583;Lo;0;L;;;;;N;;;;; +106F7;LINEAR A SIGN A584;Lo;0;L;;;;;N;;;;; +106F8;LINEAR A SIGN A585;Lo;0;L;;;;;N;;;;; +106F9;LINEAR A SIGN A586;Lo;0;L;;;;;N;;;;; +106FA;LINEAR A SIGN A587;Lo;0;L;;;;;N;;;;; +106FB;LINEAR A SIGN A588;Lo;0;L;;;;;N;;;;; +106FC;LINEAR A SIGN A589;Lo;0;L;;;;;N;;;;; +106FD;LINEAR A SIGN A591;Lo;0;L;;;;;N;;;;; +106FE;LINEAR A SIGN A592;Lo;0;L;;;;;N;;;;; +106FF;LINEAR A SIGN A594;Lo;0;L;;;;;N;;;;; +10700;LINEAR A SIGN A595;Lo;0;L;;;;;N;;;;; +10701;LINEAR A SIGN A596;Lo;0;L;;;;;N;;;;; +10702;LINEAR A SIGN A598;Lo;0;L;;;;;N;;;;; +10703;LINEAR A SIGN A600;Lo;0;L;;;;;N;;;;; +10704;LINEAR A SIGN A601;Lo;0;L;;;;;N;;;;; +10705;LINEAR A SIGN A602;Lo;0;L;;;;;N;;;;; +10706;LINEAR A SIGN A603;Lo;0;L;;;;;N;;;;; +10707;LINEAR A SIGN A604;Lo;0;L;;;;;N;;;;; +10708;LINEAR A SIGN A606;Lo;0;L;;;;;N;;;;; +10709;LINEAR A SIGN A608;Lo;0;L;;;;;N;;;;; +1070A;LINEAR A SIGN A609;Lo;0;L;;;;;N;;;;; +1070B;LINEAR A SIGN A610;Lo;0;L;;;;;N;;;;; +1070C;LINEAR A SIGN A611;Lo;0;L;;;;;N;;;;; +1070D;LINEAR A SIGN A612;Lo;0;L;;;;;N;;;;; +1070E;LINEAR A SIGN A613;Lo;0;L;;;;;N;;;;; +1070F;LINEAR A SIGN A614;Lo;0;L;;;;;N;;;;; +10710;LINEAR A SIGN A615;Lo;0;L;;;;;N;;;;; +10711;LINEAR A SIGN A616;Lo;0;L;;;;;N;;;;; +10712;LINEAR A SIGN A617;Lo;0;L;;;;;N;;;;; +10713;LINEAR A SIGN A618;Lo;0;L;;;;;N;;;;; +10714;LINEAR A SIGN A619;Lo;0;L;;;;;N;;;;; +10715;LINEAR A SIGN A620;Lo;0;L;;;;;N;;;;; +10716;LINEAR A SIGN A621;Lo;0;L;;;;;N;;;;; +10717;LINEAR A SIGN A622;Lo;0;L;;;;;N;;;;; +10718;LINEAR A SIGN A623;Lo;0;L;;;;;N;;;;; +10719;LINEAR A SIGN A624;Lo;0;L;;;;;N;;;;; +1071A;LINEAR A SIGN A626;Lo;0;L;;;;;N;;;;; +1071B;LINEAR A SIGN A627;Lo;0;L;;;;;N;;;;; +1071C;LINEAR A SIGN A628;Lo;0;L;;;;;N;;;;; +1071D;LINEAR A SIGN A629;Lo;0;L;;;;;N;;;;; +1071E;LINEAR A SIGN A634;Lo;0;L;;;;;N;;;;; +1071F;LINEAR A SIGN A637;Lo;0;L;;;;;N;;;;; +10720;LINEAR A SIGN A638;Lo;0;L;;;;;N;;;;; +10721;LINEAR A SIGN A640;Lo;0;L;;;;;N;;;;; +10722;LINEAR A SIGN A642;Lo;0;L;;;;;N;;;;; +10723;LINEAR A SIGN A643;Lo;0;L;;;;;N;;;;; +10724;LINEAR A SIGN A644;Lo;0;L;;;;;N;;;;; +10725;LINEAR A SIGN A645;Lo;0;L;;;;;N;;;;; +10726;LINEAR A SIGN A646;Lo;0;L;;;;;N;;;;; +10727;LINEAR A SIGN A648;Lo;0;L;;;;;N;;;;; +10728;LINEAR A SIGN A649;Lo;0;L;;;;;N;;;;; +10729;LINEAR A SIGN A651;Lo;0;L;;;;;N;;;;; +1072A;LINEAR A SIGN A652;Lo;0;L;;;;;N;;;;; +1072B;LINEAR A SIGN A653;Lo;0;L;;;;;N;;;;; +1072C;LINEAR A SIGN A654;Lo;0;L;;;;;N;;;;; +1072D;LINEAR A SIGN A655;Lo;0;L;;;;;N;;;;; +1072E;LINEAR A SIGN A656;Lo;0;L;;;;;N;;;;; +1072F;LINEAR A SIGN A657;Lo;0;L;;;;;N;;;;; +10730;LINEAR A SIGN A658;Lo;0;L;;;;;N;;;;; +10731;LINEAR A SIGN A659;Lo;0;L;;;;;N;;;;; +10732;LINEAR A SIGN A660;Lo;0;L;;;;;N;;;;; +10733;LINEAR A SIGN A661;Lo;0;L;;;;;N;;;;; +10734;LINEAR A SIGN A662;Lo;0;L;;;;;N;;;;; +10735;LINEAR A SIGN A663;Lo;0;L;;;;;N;;;;; +10736;LINEAR A SIGN A664;Lo;0;L;;;;;N;;;;; +10740;LINEAR A SIGN A701 A;Lo;0;L;;;;;N;;;;; +10741;LINEAR A SIGN A702 B;Lo;0;L;;;;;N;;;;; +10742;LINEAR A SIGN A703 D;Lo;0;L;;;;;N;;;;; +10743;LINEAR A SIGN A704 E;Lo;0;L;;;;;N;;;;; +10744;LINEAR A SIGN A705 F;Lo;0;L;;;;;N;;;;; +10745;LINEAR A SIGN A706 H;Lo;0;L;;;;;N;;;;; +10746;LINEAR A SIGN A707 J;Lo;0;L;;;;;N;;;;; +10747;LINEAR A SIGN A708 K;Lo;0;L;;;;;N;;;;; +10748;LINEAR A SIGN A709 L;Lo;0;L;;;;;N;;;;; +10749;LINEAR A SIGN A709-2 L2;Lo;0;L;;;;;N;;;;; +1074A;LINEAR A SIGN A709-3 L3;Lo;0;L;;;;;N;;;;; +1074B;LINEAR A SIGN A709-4 L4;Lo;0;L;;;;;N;;;;; +1074C;LINEAR A SIGN A709-6 L6;Lo;0;L;;;;;N;;;;; +1074D;LINEAR A SIGN A710 W;Lo;0;L;;;;;N;;;;; +1074E;LINEAR A SIGN A711 X;Lo;0;L;;;;;N;;;;; +1074F;LINEAR A SIGN A712 Y;Lo;0;L;;;;;N;;;;; +10750;LINEAR A SIGN A713 OMEGA;Lo;0;L;;;;;N;;;;; +10751;LINEAR A SIGN A714 ABB;Lo;0;L;;;;;N;;;;; +10752;LINEAR A SIGN A715 BB;Lo;0;L;;;;;N;;;;; +10753;LINEAR A SIGN A717 DD;Lo;0;L;;;;;N;;;;; +10754;LINEAR A SIGN A726 EYYY;Lo;0;L;;;;;N;;;;; +10755;LINEAR A SIGN A732 JE;Lo;0;L;;;;;N;;;;; +10760;LINEAR A SIGN A800;Lo;0;L;;;;;N;;;;; +10761;LINEAR A SIGN A801;Lo;0;L;;;;;N;;;;; +10762;LINEAR A SIGN A802;Lo;0;L;;;;;N;;;;; +10763;LINEAR A SIGN A803;Lo;0;L;;;;;N;;;;; +10764;LINEAR A SIGN A804;Lo;0;L;;;;;N;;;;; +10765;LINEAR A SIGN A805;Lo;0;L;;;;;N;;;;; +10766;LINEAR A SIGN A806;Lo;0;L;;;;;N;;;;; +10767;LINEAR A SIGN A807;Lo;0;L;;;;;N;;;;; +10800;CYPRIOT SYLLABLE A;Lo;0;R;;;;;N;;;;; +10801;CYPRIOT SYLLABLE E;Lo;0;R;;;;;N;;;;; +10802;CYPRIOT SYLLABLE I;Lo;0;R;;;;;N;;;;; +10803;CYPRIOT SYLLABLE O;Lo;0;R;;;;;N;;;;; +10804;CYPRIOT SYLLABLE U;Lo;0;R;;;;;N;;;;; +10805;CYPRIOT SYLLABLE JA;Lo;0;R;;;;;N;;;;; +10808;CYPRIOT SYLLABLE JO;Lo;0;R;;;;;N;;;;; +1080A;CYPRIOT SYLLABLE KA;Lo;0;R;;;;;N;;;;; +1080B;CYPRIOT SYLLABLE KE;Lo;0;R;;;;;N;;;;; +1080C;CYPRIOT SYLLABLE KI;Lo;0;R;;;;;N;;;;; +1080D;CYPRIOT SYLLABLE KO;Lo;0;R;;;;;N;;;;; +1080E;CYPRIOT SYLLABLE KU;Lo;0;R;;;;;N;;;;; +1080F;CYPRIOT SYLLABLE LA;Lo;0;R;;;;;N;;;;; +10810;CYPRIOT SYLLABLE LE;Lo;0;R;;;;;N;;;;; +10811;CYPRIOT SYLLABLE LI;Lo;0;R;;;;;N;;;;; +10812;CYPRIOT SYLLABLE LO;Lo;0;R;;;;;N;;;;; +10813;CYPRIOT SYLLABLE LU;Lo;0;R;;;;;N;;;;; +10814;CYPRIOT SYLLABLE MA;Lo;0;R;;;;;N;;;;; +10815;CYPRIOT SYLLABLE ME;Lo;0;R;;;;;N;;;;; +10816;CYPRIOT SYLLABLE MI;Lo;0;R;;;;;N;;;;; +10817;CYPRIOT SYLLABLE MO;Lo;0;R;;;;;N;;;;; +10818;CYPRIOT SYLLABLE MU;Lo;0;R;;;;;N;;;;; +10819;CYPRIOT SYLLABLE NA;Lo;0;R;;;;;N;;;;; +1081A;CYPRIOT SYLLABLE NE;Lo;0;R;;;;;N;;;;; +1081B;CYPRIOT SYLLABLE NI;Lo;0;R;;;;;N;;;;; +1081C;CYPRIOT SYLLABLE NO;Lo;0;R;;;;;N;;;;; +1081D;CYPRIOT SYLLABLE NU;Lo;0;R;;;;;N;;;;; +1081E;CYPRIOT SYLLABLE PA;Lo;0;R;;;;;N;;;;; +1081F;CYPRIOT SYLLABLE PE;Lo;0;R;;;;;N;;;;; +10820;CYPRIOT SYLLABLE PI;Lo;0;R;;;;;N;;;;; +10821;CYPRIOT SYLLABLE PO;Lo;0;R;;;;;N;;;;; +10822;CYPRIOT SYLLABLE PU;Lo;0;R;;;;;N;;;;; +10823;CYPRIOT SYLLABLE RA;Lo;0;R;;;;;N;;;;; +10824;CYPRIOT SYLLABLE RE;Lo;0;R;;;;;N;;;;; +10825;CYPRIOT SYLLABLE RI;Lo;0;R;;;;;N;;;;; +10826;CYPRIOT SYLLABLE RO;Lo;0;R;;;;;N;;;;; +10827;CYPRIOT SYLLABLE RU;Lo;0;R;;;;;N;;;;; +10828;CYPRIOT SYLLABLE SA;Lo;0;R;;;;;N;;;;; +10829;CYPRIOT SYLLABLE SE;Lo;0;R;;;;;N;;;;; +1082A;CYPRIOT SYLLABLE SI;Lo;0;R;;;;;N;;;;; +1082B;CYPRIOT SYLLABLE SO;Lo;0;R;;;;;N;;;;; +1082C;CYPRIOT SYLLABLE SU;Lo;0;R;;;;;N;;;;; +1082D;CYPRIOT SYLLABLE TA;Lo;0;R;;;;;N;;;;; +1082E;CYPRIOT SYLLABLE TE;Lo;0;R;;;;;N;;;;; +1082F;CYPRIOT SYLLABLE TI;Lo;0;R;;;;;N;;;;; +10830;CYPRIOT SYLLABLE TO;Lo;0;R;;;;;N;;;;; +10831;CYPRIOT SYLLABLE TU;Lo;0;R;;;;;N;;;;; +10832;CYPRIOT SYLLABLE WA;Lo;0;R;;;;;N;;;;; +10833;CYPRIOT SYLLABLE WE;Lo;0;R;;;;;N;;;;; +10834;CYPRIOT SYLLABLE WI;Lo;0;R;;;;;N;;;;; +10835;CYPRIOT SYLLABLE WO;Lo;0;R;;;;;N;;;;; +10837;CYPRIOT SYLLABLE XA;Lo;0;R;;;;;N;;;;; +10838;CYPRIOT SYLLABLE XE;Lo;0;R;;;;;N;;;;; +1083C;CYPRIOT SYLLABLE ZA;Lo;0;R;;;;;N;;;;; +1083F;CYPRIOT SYLLABLE ZO;Lo;0;R;;;;;N;;;;; +10840;IMPERIAL ARAMAIC LETTER ALEPH;Lo;0;R;;;;;N;;;;; +10841;IMPERIAL ARAMAIC LETTER BETH;Lo;0;R;;;;;N;;;;; +10842;IMPERIAL ARAMAIC LETTER GIMEL;Lo;0;R;;;;;N;;;;; +10843;IMPERIAL ARAMAIC LETTER DALETH;Lo;0;R;;;;;N;;;;; +10844;IMPERIAL ARAMAIC LETTER HE;Lo;0;R;;;;;N;;;;; +10845;IMPERIAL ARAMAIC LETTER WAW;Lo;0;R;;;;;N;;;;; +10846;IMPERIAL ARAMAIC LETTER ZAYIN;Lo;0;R;;;;;N;;;;; +10847;IMPERIAL ARAMAIC LETTER HETH;Lo;0;R;;;;;N;;;;; +10848;IMPERIAL ARAMAIC LETTER TETH;Lo;0;R;;;;;N;;;;; +10849;IMPERIAL ARAMAIC LETTER YODH;Lo;0;R;;;;;N;;;;; +1084A;IMPERIAL ARAMAIC LETTER KAPH;Lo;0;R;;;;;N;;;;; +1084B;IMPERIAL ARAMAIC LETTER LAMEDH;Lo;0;R;;;;;N;;;;; +1084C;IMPERIAL ARAMAIC LETTER MEM;Lo;0;R;;;;;N;;;;; +1084D;IMPERIAL ARAMAIC LETTER NUN;Lo;0;R;;;;;N;;;;; +1084E;IMPERIAL ARAMAIC LETTER SAMEKH;Lo;0;R;;;;;N;;;;; +1084F;IMPERIAL ARAMAIC LETTER AYIN;Lo;0;R;;;;;N;;;;; +10850;IMPERIAL ARAMAIC LETTER PE;Lo;0;R;;;;;N;;;;; +10851;IMPERIAL ARAMAIC LETTER SADHE;Lo;0;R;;;;;N;;;;; +10852;IMPERIAL ARAMAIC LETTER QOPH;Lo;0;R;;;;;N;;;;; +10853;IMPERIAL ARAMAIC LETTER RESH;Lo;0;R;;;;;N;;;;; +10854;IMPERIAL ARAMAIC LETTER SHIN;Lo;0;R;;;;;N;;;;; +10855;IMPERIAL ARAMAIC LETTER TAW;Lo;0;R;;;;;N;;;;; +10857;IMPERIAL ARAMAIC SECTION SIGN;Po;0;R;;;;;N;;;;; +10858;IMPERIAL ARAMAIC NUMBER ONE;No;0;R;;;;1;N;;;;; +10859;IMPERIAL ARAMAIC NUMBER TWO;No;0;R;;;;2;N;;;;; +1085A;IMPERIAL ARAMAIC NUMBER THREE;No;0;R;;;;3;N;;;;; +1085B;IMPERIAL ARAMAIC NUMBER TEN;No;0;R;;;;10;N;;;;; +1085C;IMPERIAL ARAMAIC NUMBER TWENTY;No;0;R;;;;20;N;;;;; +1085D;IMPERIAL ARAMAIC NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; +1085E;IMPERIAL ARAMAIC NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;; +1085F;IMPERIAL ARAMAIC NUMBER TEN THOUSAND;No;0;R;;;;10000;N;;;;; +10860;PALMYRENE LETTER ALEPH;Lo;0;R;;;;;N;;;;; +10861;PALMYRENE LETTER BETH;Lo;0;R;;;;;N;;;;; +10862;PALMYRENE LETTER GIMEL;Lo;0;R;;;;;N;;;;; +10863;PALMYRENE LETTER DALETH;Lo;0;R;;;;;N;;;;; +10864;PALMYRENE LETTER HE;Lo;0;R;;;;;N;;;;; +10865;PALMYRENE LETTER WAW;Lo;0;R;;;;;N;;;;; +10866;PALMYRENE LETTER ZAYIN;Lo;0;R;;;;;N;;;;; +10867;PALMYRENE LETTER HETH;Lo;0;R;;;;;N;;;;; +10868;PALMYRENE LETTER TETH;Lo;0;R;;;;;N;;;;; +10869;PALMYRENE LETTER YODH;Lo;0;R;;;;;N;;;;; +1086A;PALMYRENE LETTER KAPH;Lo;0;R;;;;;N;;;;; +1086B;PALMYRENE LETTER LAMEDH;Lo;0;R;;;;;N;;;;; +1086C;PALMYRENE LETTER MEM;Lo;0;R;;;;;N;;;;; +1086D;PALMYRENE LETTER FINAL NUN;Lo;0;R;;;;;N;;;;; +1086E;PALMYRENE LETTER NUN;Lo;0;R;;;;;N;;;;; +1086F;PALMYRENE LETTER SAMEKH;Lo;0;R;;;;;N;;;;; +10870;PALMYRENE LETTER AYIN;Lo;0;R;;;;;N;;;;; +10871;PALMYRENE LETTER PE;Lo;0;R;;;;;N;;;;; +10872;PALMYRENE LETTER SADHE;Lo;0;R;;;;;N;;;;; +10873;PALMYRENE LETTER QOPH;Lo;0;R;;;;;N;;;;; +10874;PALMYRENE LETTER RESH;Lo;0;R;;;;;N;;;;; +10875;PALMYRENE LETTER SHIN;Lo;0;R;;;;;N;;;;; +10876;PALMYRENE LETTER TAW;Lo;0;R;;;;;N;;;;; +10877;PALMYRENE LEFT-POINTING FLEURON;So;0;R;;;;;N;;;;; +10878;PALMYRENE RIGHT-POINTING FLEURON;So;0;R;;;;;N;;;;; +10879;PALMYRENE NUMBER ONE;No;0;R;;;;1;N;;;;; +1087A;PALMYRENE NUMBER TWO;No;0;R;;;;2;N;;;;; +1087B;PALMYRENE NUMBER THREE;No;0;R;;;;3;N;;;;; +1087C;PALMYRENE NUMBER FOUR;No;0;R;;;;4;N;;;;; +1087D;PALMYRENE NUMBER FIVE;No;0;R;;;;5;N;;;;; +1087E;PALMYRENE NUMBER TEN;No;0;R;;;;10;N;;;;; +1087F;PALMYRENE NUMBER TWENTY;No;0;R;;;;20;N;;;;; +10880;NABATAEAN LETTER FINAL ALEPH;Lo;0;R;;;;;N;;;;; +10881;NABATAEAN LETTER ALEPH;Lo;0;R;;;;;N;;;;; +10882;NABATAEAN LETTER FINAL BETH;Lo;0;R;;;;;N;;;;; +10883;NABATAEAN LETTER BETH;Lo;0;R;;;;;N;;;;; +10884;NABATAEAN LETTER GIMEL;Lo;0;R;;;;;N;;;;; +10885;NABATAEAN LETTER DALETH;Lo;0;R;;;;;N;;;;; +10886;NABATAEAN LETTER FINAL HE;Lo;0;R;;;;;N;;;;; +10887;NABATAEAN LETTER HE;Lo;0;R;;;;;N;;;;; +10888;NABATAEAN LETTER WAW;Lo;0;R;;;;;N;;;;; +10889;NABATAEAN LETTER ZAYIN;Lo;0;R;;;;;N;;;;; +1088A;NABATAEAN LETTER HETH;Lo;0;R;;;;;N;;;;; +1088B;NABATAEAN LETTER TETH;Lo;0;R;;;;;N;;;;; +1088C;NABATAEAN LETTER FINAL YODH;Lo;0;R;;;;;N;;;;; +1088D;NABATAEAN LETTER YODH;Lo;0;R;;;;;N;;;;; +1088E;NABATAEAN LETTER FINAL KAPH;Lo;0;R;;;;;N;;;;; +1088F;NABATAEAN LETTER KAPH;Lo;0;R;;;;;N;;;;; +10890;NABATAEAN LETTER FINAL LAMEDH;Lo;0;R;;;;;N;;;;; +10891;NABATAEAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;; +10892;NABATAEAN LETTER FINAL MEM;Lo;0;R;;;;;N;;;;; +10893;NABATAEAN LETTER MEM;Lo;0;R;;;;;N;;;;; +10894;NABATAEAN LETTER FINAL NUN;Lo;0;R;;;;;N;;;;; +10895;NABATAEAN LETTER NUN;Lo;0;R;;;;;N;;;;; +10896;NABATAEAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;; +10897;NABATAEAN LETTER AYIN;Lo;0;R;;;;;N;;;;; +10898;NABATAEAN LETTER PE;Lo;0;R;;;;;N;;;;; +10899;NABATAEAN LETTER SADHE;Lo;0;R;;;;;N;;;;; +1089A;NABATAEAN LETTER QOPH;Lo;0;R;;;;;N;;;;; +1089B;NABATAEAN LETTER RESH;Lo;0;R;;;;;N;;;;; +1089C;NABATAEAN LETTER FINAL SHIN;Lo;0;R;;;;;N;;;;; +1089D;NABATAEAN LETTER SHIN;Lo;0;R;;;;;N;;;;; +1089E;NABATAEAN LETTER TAW;Lo;0;R;;;;;N;;;;; +108A7;NABATAEAN NUMBER ONE;No;0;R;;;;1;N;;;;; +108A8;NABATAEAN NUMBER TWO;No;0;R;;;;2;N;;;;; +108A9;NABATAEAN NUMBER THREE;No;0;R;;;;3;N;;;;; +108AA;NABATAEAN NUMBER FOUR;No;0;R;;;;4;N;;;;; +108AB;NABATAEAN CRUCIFORM NUMBER FOUR;No;0;R;;;;4;N;;;;; +108AC;NABATAEAN NUMBER FIVE;No;0;R;;;;5;N;;;;; +108AD;NABATAEAN NUMBER TEN;No;0;R;;;;10;N;;;;; +108AE;NABATAEAN NUMBER TWENTY;No;0;R;;;;20;N;;;;; +108AF;NABATAEAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; +108E0;HATRAN LETTER ALEPH;Lo;0;R;;;;;N;;;;; +108E1;HATRAN LETTER BETH;Lo;0;R;;;;;N;;;;; +108E2;HATRAN LETTER GIMEL;Lo;0;R;;;;;N;;;;; +108E3;HATRAN LETTER DALETH-RESH;Lo;0;R;;;;;N;;;;; +108E4;HATRAN LETTER HE;Lo;0;R;;;;;N;;;;; +108E5;HATRAN LETTER WAW;Lo;0;R;;;;;N;;;;; +108E6;HATRAN LETTER ZAYN;Lo;0;R;;;;;N;;;;; +108E7;HATRAN LETTER HETH;Lo;0;R;;;;;N;;;;; +108E8;HATRAN LETTER TETH;Lo;0;R;;;;;N;;;;; +108E9;HATRAN LETTER YODH;Lo;0;R;;;;;N;;;;; +108EA;HATRAN LETTER KAPH;Lo;0;R;;;;;N;;;;; +108EB;HATRAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;; +108EC;HATRAN LETTER MEM;Lo;0;R;;;;;N;;;;; +108ED;HATRAN LETTER NUN;Lo;0;R;;;;;N;;;;; +108EE;HATRAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;; +108EF;HATRAN LETTER AYN;Lo;0;R;;;;;N;;;;; +108F0;HATRAN LETTER PE;Lo;0;R;;;;;N;;;;; +108F1;HATRAN LETTER SADHE;Lo;0;R;;;;;N;;;;; +108F2;HATRAN LETTER QOPH;Lo;0;R;;;;;N;;;;; +108F4;HATRAN LETTER SHIN;Lo;0;R;;;;;N;;;;; +108F5;HATRAN LETTER TAW;Lo;0;R;;;;;N;;;;; +108FB;HATRAN NUMBER ONE;No;0;R;;;;1;N;;;;; +108FC;HATRAN NUMBER FIVE;No;0;R;;;;5;N;;;;; +108FD;HATRAN NUMBER TEN;No;0;R;;;;10;N;;;;; +108FE;HATRAN NUMBER TWENTY;No;0;R;;;;20;N;;;;; +108FF;HATRAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; +10900;PHOENICIAN LETTER ALF;Lo;0;R;;;;;N;;;;; +10901;PHOENICIAN LETTER BET;Lo;0;R;;;;;N;;;;; +10902;PHOENICIAN LETTER GAML;Lo;0;R;;;;;N;;;;; +10903;PHOENICIAN LETTER DELT;Lo;0;R;;;;;N;;;;; +10904;PHOENICIAN LETTER HE;Lo;0;R;;;;;N;;;;; +10905;PHOENICIAN LETTER WAU;Lo;0;R;;;;;N;;;;; +10906;PHOENICIAN LETTER ZAI;Lo;0;R;;;;;N;;;;; +10907;PHOENICIAN LETTER HET;Lo;0;R;;;;;N;;;;; +10908;PHOENICIAN LETTER TET;Lo;0;R;;;;;N;;;;; +10909;PHOENICIAN LETTER YOD;Lo;0;R;;;;;N;;;;; +1090A;PHOENICIAN LETTER KAF;Lo;0;R;;;;;N;;;;; +1090B;PHOENICIAN LETTER LAMD;Lo;0;R;;;;;N;;;;; +1090C;PHOENICIAN LETTER MEM;Lo;0;R;;;;;N;;;;; +1090D;PHOENICIAN LETTER NUN;Lo;0;R;;;;;N;;;;; +1090E;PHOENICIAN LETTER SEMK;Lo;0;R;;;;;N;;;;; +1090F;PHOENICIAN LETTER AIN;Lo;0;R;;;;;N;;;;; +10910;PHOENICIAN LETTER PE;Lo;0;R;;;;;N;;;;; +10911;PHOENICIAN LETTER SADE;Lo;0;R;;;;;N;;;;; +10912;PHOENICIAN LETTER QOF;Lo;0;R;;;;;N;;;;; +10913;PHOENICIAN LETTER ROSH;Lo;0;R;;;;;N;;;;; +10914;PHOENICIAN LETTER SHIN;Lo;0;R;;;;;N;;;;; +10915;PHOENICIAN LETTER TAU;Lo;0;R;;;;;N;;;;; +10916;PHOENICIAN NUMBER ONE;No;0;R;;;;1;N;;;;; +10917;PHOENICIAN NUMBER TEN;No;0;R;;;;10;N;;;;; +10918;PHOENICIAN NUMBER TWENTY;No;0;R;;;;20;N;;;;; +10919;PHOENICIAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; +1091A;PHOENICIAN NUMBER TWO;No;0;R;;;;2;N;;;;; +1091B;PHOENICIAN NUMBER THREE;No;0;R;;;;3;N;;;;; +1091F;PHOENICIAN WORD SEPARATOR;Po;0;ON;;;;;N;;;;; +10920;LYDIAN LETTER A;Lo;0;R;;;;;N;;;;; +10921;LYDIAN LETTER B;Lo;0;R;;;;;N;;;;; +10922;LYDIAN LETTER G;Lo;0;R;;;;;N;;;;; +10923;LYDIAN LETTER D;Lo;0;R;;;;;N;;;;; +10924;LYDIAN LETTER E;Lo;0;R;;;;;N;;;;; +10925;LYDIAN LETTER V;Lo;0;R;;;;;N;;;;; +10926;LYDIAN LETTER I;Lo;0;R;;;;;N;;;;; +10927;LYDIAN LETTER Y;Lo;0;R;;;;;N;;;;; +10928;LYDIAN LETTER K;Lo;0;R;;;;;N;;;;; +10929;LYDIAN LETTER L;Lo;0;R;;;;;N;;;;; +1092A;LYDIAN LETTER M;Lo;0;R;;;;;N;;;;; +1092B;LYDIAN LETTER N;Lo;0;R;;;;;N;;;;; +1092C;LYDIAN LETTER O;Lo;0;R;;;;;N;;;;; +1092D;LYDIAN LETTER R;Lo;0;R;;;;;N;;;;; +1092E;LYDIAN LETTER SS;Lo;0;R;;;;;N;;;;; +1092F;LYDIAN LETTER T;Lo;0;R;;;;;N;;;;; +10930;LYDIAN LETTER U;Lo;0;R;;;;;N;;;;; +10931;LYDIAN LETTER F;Lo;0;R;;;;;N;;;;; +10932;LYDIAN LETTER Q;Lo;0;R;;;;;N;;;;; +10933;LYDIAN LETTER S;Lo;0;R;;;;;N;;;;; +10934;LYDIAN LETTER TT;Lo;0;R;;;;;N;;;;; +10935;LYDIAN LETTER AN;Lo;0;R;;;;;N;;;;; +10936;LYDIAN LETTER EN;Lo;0;R;;;;;N;;;;; +10937;LYDIAN LETTER LY;Lo;0;R;;;;;N;;;;; +10938;LYDIAN LETTER NN;Lo;0;R;;;;;N;;;;; +10939;LYDIAN LETTER C;Lo;0;R;;;;;N;;;;; +1093F;LYDIAN TRIANGULAR MARK;Po;0;R;;;;;N;;;;; +10980;MEROITIC HIEROGLYPHIC LETTER A;Lo;0;R;;;;;N;;;;; +10981;MEROITIC HIEROGLYPHIC LETTER E;Lo;0;R;;;;;N;;;;; +10982;MEROITIC HIEROGLYPHIC LETTER I;Lo;0;R;;;;;N;;;;; +10983;MEROITIC HIEROGLYPHIC LETTER O;Lo;0;R;;;;;N;;;;; +10984;MEROITIC HIEROGLYPHIC LETTER YA;Lo;0;R;;;;;N;;;;; +10985;MEROITIC HIEROGLYPHIC LETTER WA;Lo;0;R;;;;;N;;;;; +10986;MEROITIC HIEROGLYPHIC LETTER BA;Lo;0;R;;;;;N;;;;; +10987;MEROITIC HIEROGLYPHIC LETTER BA-2;Lo;0;R;;;;;N;;;;; +10988;MEROITIC HIEROGLYPHIC LETTER PA;Lo;0;R;;;;;N;;;;; +10989;MEROITIC HIEROGLYPHIC LETTER MA;Lo;0;R;;;;;N;;;;; +1098A;MEROITIC HIEROGLYPHIC LETTER NA;Lo;0;R;;;;;N;;;;; +1098B;MEROITIC HIEROGLYPHIC LETTER NA-2;Lo;0;R;;;;;N;;;;; +1098C;MEROITIC HIEROGLYPHIC LETTER NE;Lo;0;R;;;;;N;;;;; +1098D;MEROITIC HIEROGLYPHIC LETTER NE-2;Lo;0;R;;;;;N;;;;; +1098E;MEROITIC HIEROGLYPHIC LETTER RA;Lo;0;R;;;;;N;;;;; +1098F;MEROITIC HIEROGLYPHIC LETTER RA-2;Lo;0;R;;;;;N;;;;; +10990;MEROITIC HIEROGLYPHIC LETTER LA;Lo;0;R;;;;;N;;;;; +10991;MEROITIC HIEROGLYPHIC LETTER KHA;Lo;0;R;;;;;N;;;;; +10992;MEROITIC HIEROGLYPHIC LETTER HHA;Lo;0;R;;;;;N;;;;; +10993;MEROITIC HIEROGLYPHIC LETTER SA;Lo;0;R;;;;;N;;;;; +10994;MEROITIC HIEROGLYPHIC LETTER SA-2;Lo;0;R;;;;;N;;;;; +10995;MEROITIC HIEROGLYPHIC LETTER SE;Lo;0;R;;;;;N;;;;; +10996;MEROITIC HIEROGLYPHIC LETTER KA;Lo;0;R;;;;;N;;;;; +10997;MEROITIC HIEROGLYPHIC LETTER QA;Lo;0;R;;;;;N;;;;; +10998;MEROITIC HIEROGLYPHIC LETTER TA;Lo;0;R;;;;;N;;;;; +10999;MEROITIC HIEROGLYPHIC LETTER TA-2;Lo;0;R;;;;;N;;;;; +1099A;MEROITIC HIEROGLYPHIC LETTER TE;Lo;0;R;;;;;N;;;;; +1099B;MEROITIC HIEROGLYPHIC LETTER TE-2;Lo;0;R;;;;;N;;;;; +1099C;MEROITIC HIEROGLYPHIC LETTER TO;Lo;0;R;;;;;N;;;;; +1099D;MEROITIC HIEROGLYPHIC LETTER DA;Lo;0;R;;;;;N;;;;; +1099E;MEROITIC HIEROGLYPHIC SYMBOL VIDJ;Lo;0;R;;;;;N;;;;; +1099F;MEROITIC HIEROGLYPHIC SYMBOL VIDJ-2;Lo;0;R;;;;;N;;;;; +109A0;MEROITIC CURSIVE LETTER A;Lo;0;R;;;;;N;;;;; +109A1;MEROITIC CURSIVE LETTER E;Lo;0;R;;;;;N;;;;; +109A2;MEROITIC CURSIVE LETTER I;Lo;0;R;;;;;N;;;;; +109A3;MEROITIC CURSIVE LETTER O;Lo;0;R;;;;;N;;;;; +109A4;MEROITIC CURSIVE LETTER YA;Lo;0;R;;;;;N;;;;; +109A5;MEROITIC CURSIVE LETTER WA;Lo;0;R;;;;;N;;;;; +109A6;MEROITIC CURSIVE LETTER BA;Lo;0;R;;;;;N;;;;; +109A7;MEROITIC CURSIVE LETTER PA;Lo;0;R;;;;;N;;;;; +109A8;MEROITIC CURSIVE LETTER MA;Lo;0;R;;;;;N;;;;; +109A9;MEROITIC CURSIVE LETTER NA;Lo;0;R;;;;;N;;;;; +109AA;MEROITIC CURSIVE LETTER NE;Lo;0;R;;;;;N;;;;; +109AB;MEROITIC CURSIVE LETTER RA;Lo;0;R;;;;;N;;;;; +109AC;MEROITIC CURSIVE LETTER LA;Lo;0;R;;;;;N;;;;; +109AD;MEROITIC CURSIVE LETTER KHA;Lo;0;R;;;;;N;;;;; +109AE;MEROITIC CURSIVE LETTER HHA;Lo;0;R;;;;;N;;;;; +109AF;MEROITIC CURSIVE LETTER SA;Lo;0;R;;;;;N;;;;; +109B0;MEROITIC CURSIVE LETTER ARCHAIC SA;Lo;0;R;;;;;N;;;;; +109B1;MEROITIC CURSIVE LETTER SE;Lo;0;R;;;;;N;;;;; +109B2;MEROITIC CURSIVE LETTER KA;Lo;0;R;;;;;N;;;;; +109B3;MEROITIC CURSIVE LETTER QA;Lo;0;R;;;;;N;;;;; +109B4;MEROITIC CURSIVE LETTER TA;Lo;0;R;;;;;N;;;;; +109B5;MEROITIC CURSIVE LETTER TE;Lo;0;R;;;;;N;;;;; +109B6;MEROITIC CURSIVE LETTER TO;Lo;0;R;;;;;N;;;;; +109B7;MEROITIC CURSIVE LETTER DA;Lo;0;R;;;;;N;;;;; +109BC;MEROITIC CURSIVE FRACTION ELEVEN TWELFTHS;No;0;R;;;;11/12;N;;;;; +109BD;MEROITIC CURSIVE FRACTION ONE HALF;No;0;R;;;;1/2;N;;;;; +109BE;MEROITIC CURSIVE LOGOGRAM RMT;Lo;0;R;;;;;N;;;;; +109BF;MEROITIC CURSIVE LOGOGRAM IMN;Lo;0;R;;;;;N;;;;; +109C0;MEROITIC CURSIVE NUMBER ONE;No;0;R;;;;1;N;;;;; +109C1;MEROITIC CURSIVE NUMBER TWO;No;0;R;;;;2;N;;;;; +109C2;MEROITIC CURSIVE NUMBER THREE;No;0;R;;;;3;N;;;;; +109C3;MEROITIC CURSIVE NUMBER FOUR;No;0;R;;;;4;N;;;;; +109C4;MEROITIC CURSIVE NUMBER FIVE;No;0;R;;;;5;N;;;;; +109C5;MEROITIC CURSIVE NUMBER SIX;No;0;R;;;;6;N;;;;; +109C6;MEROITIC CURSIVE NUMBER SEVEN;No;0;R;;;;7;N;;;;; +109C7;MEROITIC CURSIVE NUMBER EIGHT;No;0;R;;;;8;N;;;;; +109C8;MEROITIC CURSIVE NUMBER NINE;No;0;R;;;;9;N;;;;; +109C9;MEROITIC CURSIVE NUMBER TEN;No;0;R;;;;10;N;;;;; +109CA;MEROITIC CURSIVE NUMBER TWENTY;No;0;R;;;;20;N;;;;; +109CB;MEROITIC CURSIVE NUMBER THIRTY;No;0;R;;;;30;N;;;;; +109CC;MEROITIC CURSIVE NUMBER FORTY;No;0;R;;;;40;N;;;;; +109CD;MEROITIC CURSIVE NUMBER FIFTY;No;0;R;;;;50;N;;;;; +109CE;MEROITIC CURSIVE NUMBER SIXTY;No;0;R;;;;60;N;;;;; +109CF;MEROITIC CURSIVE NUMBER SEVENTY;No;0;R;;;;70;N;;;;; +109D2;MEROITIC CURSIVE NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; +109D3;MEROITIC CURSIVE NUMBER TWO HUNDRED;No;0;R;;;;200;N;;;;; +109D4;MEROITIC CURSIVE NUMBER THREE HUNDRED;No;0;R;;;;300;N;;;;; +109D5;MEROITIC CURSIVE NUMBER FOUR HUNDRED;No;0;R;;;;400;N;;;;; +109D6;MEROITIC CURSIVE NUMBER FIVE HUNDRED;No;0;R;;;;500;N;;;;; +109D7;MEROITIC CURSIVE NUMBER SIX HUNDRED;No;0;R;;;;600;N;;;;; +109D8;MEROITIC CURSIVE NUMBER SEVEN HUNDRED;No;0;R;;;;700;N;;;;; +109D9;MEROITIC CURSIVE NUMBER EIGHT HUNDRED;No;0;R;;;;800;N;;;;; +109DA;MEROITIC CURSIVE NUMBER NINE HUNDRED;No;0;R;;;;900;N;;;;; +109DB;MEROITIC CURSIVE NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;; +109DC;MEROITIC CURSIVE NUMBER TWO THOUSAND;No;0;R;;;;2000;N;;;;; +109DD;MEROITIC CURSIVE NUMBER THREE THOUSAND;No;0;R;;;;3000;N;;;;; +109DE;MEROITIC CURSIVE NUMBER FOUR THOUSAND;No;0;R;;;;4000;N;;;;; +109DF;MEROITIC CURSIVE NUMBER FIVE THOUSAND;No;0;R;;;;5000;N;;;;; +109E0;MEROITIC CURSIVE NUMBER SIX THOUSAND;No;0;R;;;;6000;N;;;;; +109E1;MEROITIC CURSIVE NUMBER SEVEN THOUSAND;No;0;R;;;;7000;N;;;;; +109E2;MEROITIC CURSIVE NUMBER EIGHT THOUSAND;No;0;R;;;;8000;N;;;;; +109E3;MEROITIC CURSIVE NUMBER NINE THOUSAND;No;0;R;;;;9000;N;;;;; +109E4;MEROITIC CURSIVE NUMBER TEN THOUSAND;No;0;R;;;;10000;N;;;;; +109E5;MEROITIC CURSIVE NUMBER TWENTY THOUSAND;No;0;R;;;;20000;N;;;;; +109E6;MEROITIC CURSIVE NUMBER THIRTY THOUSAND;No;0;R;;;;30000;N;;;;; +109E7;MEROITIC CURSIVE NUMBER FORTY THOUSAND;No;0;R;;;;40000;N;;;;; +109E8;MEROITIC CURSIVE NUMBER FIFTY THOUSAND;No;0;R;;;;50000;N;;;;; +109E9;MEROITIC CURSIVE NUMBER SIXTY THOUSAND;No;0;R;;;;60000;N;;;;; +109EA;MEROITIC CURSIVE NUMBER SEVENTY THOUSAND;No;0;R;;;;70000;N;;;;; +109EB;MEROITIC CURSIVE NUMBER EIGHTY THOUSAND;No;0;R;;;;80000;N;;;;; +109EC;MEROITIC CURSIVE NUMBER NINETY THOUSAND;No;0;R;;;;90000;N;;;;; +109ED;MEROITIC CURSIVE NUMBER ONE HUNDRED THOUSAND;No;0;R;;;;100000;N;;;;; +109EE;MEROITIC CURSIVE NUMBER TWO HUNDRED THOUSAND;No;0;R;;;;200000;N;;;;; +109EF;MEROITIC CURSIVE NUMBER THREE HUNDRED THOUSAND;No;0;R;;;;300000;N;;;;; +109F0;MEROITIC CURSIVE NUMBER FOUR HUNDRED THOUSAND;No;0;R;;;;400000;N;;;;; +109F1;MEROITIC CURSIVE NUMBER FIVE HUNDRED THOUSAND;No;0;R;;;;500000;N;;;;; +109F2;MEROITIC CURSIVE NUMBER SIX HUNDRED THOUSAND;No;0;R;;;;600000;N;;;;; +109F3;MEROITIC CURSIVE NUMBER SEVEN HUNDRED THOUSAND;No;0;R;;;;700000;N;;;;; +109F4;MEROITIC CURSIVE NUMBER EIGHT HUNDRED THOUSAND;No;0;R;;;;800000;N;;;;; +109F5;MEROITIC CURSIVE NUMBER NINE HUNDRED THOUSAND;No;0;R;;;;900000;N;;;;; +109F6;MEROITIC CURSIVE FRACTION ONE TWELFTH;No;0;R;;;;1/12;N;;;;; +109F7;MEROITIC CURSIVE FRACTION TWO TWELFTHS;No;0;R;;;;2/12;N;;;;; +109F8;MEROITIC CURSIVE FRACTION THREE TWELFTHS;No;0;R;;;;3/12;N;;;;; +109F9;MEROITIC CURSIVE FRACTION FOUR TWELFTHS;No;0;R;;;;4/12;N;;;;; +109FA;MEROITIC CURSIVE FRACTION FIVE TWELFTHS;No;0;R;;;;5/12;N;;;;; +109FB;MEROITIC CURSIVE FRACTION SIX TWELFTHS;No;0;R;;;;6/12;N;;;;; +109FC;MEROITIC CURSIVE FRACTION SEVEN TWELFTHS;No;0;R;;;;7/12;N;;;;; +109FD;MEROITIC CURSIVE FRACTION EIGHT TWELFTHS;No;0;R;;;;8/12;N;;;;; +109FE;MEROITIC CURSIVE FRACTION NINE TWELFTHS;No;0;R;;;;9/12;N;;;;; +109FF;MEROITIC CURSIVE FRACTION TEN TWELFTHS;No;0;R;;;;10/12;N;;;;; +10A00;KHAROSHTHI LETTER A;Lo;0;R;;;;;N;;;;; +10A01;KHAROSHTHI VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +10A02;KHAROSHTHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +10A03;KHAROSHTHI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +10A05;KHAROSHTHI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +10A06;KHAROSHTHI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; +10A0C;KHAROSHTHI VOWEL LENGTH MARK;Mn;0;NSM;;;;;N;;;;; +10A0D;KHAROSHTHI SIGN DOUBLE RING BELOW;Mn;220;NSM;;;;;N;;;;; +10A0E;KHAROSHTHI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +10A0F;KHAROSHTHI SIGN VISARGA;Mn;230;NSM;;;;;N;;;;; +10A10;KHAROSHTHI LETTER KA;Lo;0;R;;;;;N;;;;; +10A11;KHAROSHTHI LETTER KHA;Lo;0;R;;;;;N;;;;; +10A12;KHAROSHTHI LETTER GA;Lo;0;R;;;;;N;;;;; +10A13;KHAROSHTHI LETTER GHA;Lo;0;R;;;;;N;;;;; +10A15;KHAROSHTHI LETTER CA;Lo;0;R;;;;;N;;;;; +10A16;KHAROSHTHI LETTER CHA;Lo;0;R;;;;;N;;;;; +10A17;KHAROSHTHI LETTER JA;Lo;0;R;;;;;N;;;;; +10A19;KHAROSHTHI LETTER NYA;Lo;0;R;;;;;N;;;;; +10A1A;KHAROSHTHI LETTER TTA;Lo;0;R;;;;;N;;;;; +10A1B;KHAROSHTHI LETTER TTHA;Lo;0;R;;;;;N;;;;; +10A1C;KHAROSHTHI LETTER DDA;Lo;0;R;;;;;N;;;;; +10A1D;KHAROSHTHI LETTER DDHA;Lo;0;R;;;;;N;;;;; +10A1E;KHAROSHTHI LETTER NNA;Lo;0;R;;;;;N;;;;; +10A1F;KHAROSHTHI LETTER TA;Lo;0;R;;;;;N;;;;; +10A20;KHAROSHTHI LETTER THA;Lo;0;R;;;;;N;;;;; +10A21;KHAROSHTHI LETTER DA;Lo;0;R;;;;;N;;;;; +10A22;KHAROSHTHI LETTER DHA;Lo;0;R;;;;;N;;;;; +10A23;KHAROSHTHI LETTER NA;Lo;0;R;;;;;N;;;;; +10A24;KHAROSHTHI LETTER PA;Lo;0;R;;;;;N;;;;; +10A25;KHAROSHTHI LETTER PHA;Lo;0;R;;;;;N;;;;; +10A26;KHAROSHTHI LETTER BA;Lo;0;R;;;;;N;;;;; +10A27;KHAROSHTHI LETTER BHA;Lo;0;R;;;;;N;;;;; +10A28;KHAROSHTHI LETTER MA;Lo;0;R;;;;;N;;;;; +10A29;KHAROSHTHI LETTER YA;Lo;0;R;;;;;N;;;;; +10A2A;KHAROSHTHI LETTER RA;Lo;0;R;;;;;N;;;;; +10A2B;KHAROSHTHI LETTER LA;Lo;0;R;;;;;N;;;;; +10A2C;KHAROSHTHI LETTER VA;Lo;0;R;;;;;N;;;;; +10A2D;KHAROSHTHI LETTER SHA;Lo;0;R;;;;;N;;;;; +10A2E;KHAROSHTHI LETTER SSA;Lo;0;R;;;;;N;;;;; +10A2F;KHAROSHTHI LETTER SA;Lo;0;R;;;;;N;;;;; +10A30;KHAROSHTHI LETTER ZA;Lo;0;R;;;;;N;;;;; +10A31;KHAROSHTHI LETTER HA;Lo;0;R;;;;;N;;;;; +10A32;KHAROSHTHI LETTER KKA;Lo;0;R;;;;;N;;;;; +10A33;KHAROSHTHI LETTER TTTHA;Lo;0;R;;;;;N;;;;; +10A38;KHAROSHTHI SIGN BAR ABOVE;Mn;230;NSM;;;;;N;;;;; +10A39;KHAROSHTHI SIGN CAUDA;Mn;1;NSM;;;;;N;;;;; +10A3A;KHAROSHTHI SIGN DOT BELOW;Mn;220;NSM;;;;;N;;;;; +10A3F;KHAROSHTHI VIRAMA;Mn;9;NSM;;;;;N;;;;; +10A40;KHAROSHTHI DIGIT ONE;No;0;R;;;1;1;N;;;;; +10A41;KHAROSHTHI DIGIT TWO;No;0;R;;;2;2;N;;;;; +10A42;KHAROSHTHI DIGIT THREE;No;0;R;;;3;3;N;;;;; +10A43;KHAROSHTHI DIGIT FOUR;No;0;R;;;4;4;N;;;;; +10A44;KHAROSHTHI NUMBER TEN;No;0;R;;;;10;N;;;;; +10A45;KHAROSHTHI NUMBER TWENTY;No;0;R;;;;20;N;;;;; +10A46;KHAROSHTHI NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; +10A47;KHAROSHTHI NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;; +10A50;KHAROSHTHI PUNCTUATION DOT;Po;0;R;;;;;N;;;;; +10A51;KHAROSHTHI PUNCTUATION SMALL CIRCLE;Po;0;R;;;;;N;;;;; +10A52;KHAROSHTHI PUNCTUATION CIRCLE;Po;0;R;;;;;N;;;;; +10A53;KHAROSHTHI PUNCTUATION CRESCENT BAR;Po;0;R;;;;;N;;;;; +10A54;KHAROSHTHI PUNCTUATION MANGALAM;Po;0;R;;;;;N;;;;; +10A55;KHAROSHTHI PUNCTUATION LOTUS;Po;0;R;;;;;N;;;;; +10A56;KHAROSHTHI PUNCTUATION DANDA;Po;0;R;;;;;N;;;;; +10A57;KHAROSHTHI PUNCTUATION DOUBLE DANDA;Po;0;R;;;;;N;;;;; +10A58;KHAROSHTHI PUNCTUATION LINES;Po;0;R;;;;;N;;;;; +10A60;OLD SOUTH ARABIAN LETTER HE;Lo;0;R;;;;;N;;;;; +10A61;OLD SOUTH ARABIAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;; +10A62;OLD SOUTH ARABIAN LETTER HETH;Lo;0;R;;;;;N;;;;; +10A63;OLD SOUTH ARABIAN LETTER MEM;Lo;0;R;;;;;N;;;;; +10A64;OLD SOUTH ARABIAN LETTER QOPH;Lo;0;R;;;;;N;;;;; +10A65;OLD SOUTH ARABIAN LETTER WAW;Lo;0;R;;;;;N;;;;; +10A66;OLD SOUTH ARABIAN LETTER SHIN;Lo;0;R;;;;;N;;;;; +10A67;OLD SOUTH ARABIAN LETTER RESH;Lo;0;R;;;;;N;;;;; +10A68;OLD SOUTH ARABIAN LETTER BETH;Lo;0;R;;;;;N;;;;; +10A69;OLD SOUTH ARABIAN LETTER TAW;Lo;0;R;;;;;N;;;;; +10A6A;OLD SOUTH ARABIAN LETTER SAT;Lo;0;R;;;;;N;;;;; +10A6B;OLD SOUTH ARABIAN LETTER KAPH;Lo;0;R;;;;;N;;;;; +10A6C;OLD SOUTH ARABIAN LETTER NUN;Lo;0;R;;;;;N;;;;; +10A6D;OLD SOUTH ARABIAN LETTER KHETH;Lo;0;R;;;;;N;;;;; +10A6E;OLD SOUTH ARABIAN LETTER SADHE;Lo;0;R;;;;;N;;;;; +10A6F;OLD SOUTH ARABIAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;; +10A70;OLD SOUTH ARABIAN LETTER FE;Lo;0;R;;;;;N;;;;; +10A71;OLD SOUTH ARABIAN LETTER ALEF;Lo;0;R;;;;;N;;;;; +10A72;OLD SOUTH ARABIAN LETTER AYN;Lo;0;R;;;;;N;;;;; +10A73;OLD SOUTH ARABIAN LETTER DHADHE;Lo;0;R;;;;;N;;;;; +10A74;OLD SOUTH ARABIAN LETTER GIMEL;Lo;0;R;;;;;N;;;;; +10A75;OLD SOUTH ARABIAN LETTER DALETH;Lo;0;R;;;;;N;;;;; +10A76;OLD SOUTH ARABIAN LETTER GHAYN;Lo;0;R;;;;;N;;;;; +10A77;OLD SOUTH ARABIAN LETTER TETH;Lo;0;R;;;;;N;;;;; +10A78;OLD SOUTH ARABIAN LETTER ZAYN;Lo;0;R;;;;;N;;;;; +10A79;OLD SOUTH ARABIAN LETTER DHALETH;Lo;0;R;;;;;N;;;;; +10A7A;OLD SOUTH ARABIAN LETTER YODH;Lo;0;R;;;;;N;;;;; +10A7B;OLD SOUTH ARABIAN LETTER THAW;Lo;0;R;;;;;N;;;;; +10A7C;OLD SOUTH ARABIAN LETTER THETH;Lo;0;R;;;;;N;;;;; +10A7D;OLD SOUTH ARABIAN NUMBER ONE;No;0;R;;;;1;N;;;;; +10A7E;OLD SOUTH ARABIAN NUMBER FIFTY;No;0;R;;;;50;N;;;;; +10A7F;OLD SOUTH ARABIAN NUMERIC INDICATOR;Po;0;R;;;;;N;;;;; +10A80;OLD NORTH ARABIAN LETTER HEH;Lo;0;R;;;;;N;;;;; +10A81;OLD NORTH ARABIAN LETTER LAM;Lo;0;R;;;;;N;;;;; +10A82;OLD NORTH ARABIAN LETTER HAH;Lo;0;R;;;;;N;;;;; +10A83;OLD NORTH ARABIAN LETTER MEEM;Lo;0;R;;;;;N;;;;; +10A84;OLD NORTH ARABIAN LETTER QAF;Lo;0;R;;;;;N;;;;; +10A85;OLD NORTH ARABIAN LETTER WAW;Lo;0;R;;;;;N;;;;; +10A86;OLD NORTH ARABIAN LETTER ES-2;Lo;0;R;;;;;N;;;;; +10A87;OLD NORTH ARABIAN LETTER REH;Lo;0;R;;;;;N;;;;; +10A88;OLD NORTH ARABIAN LETTER BEH;Lo;0;R;;;;;N;;;;; +10A89;OLD NORTH ARABIAN LETTER TEH;Lo;0;R;;;;;N;;;;; +10A8A;OLD NORTH ARABIAN LETTER ES-1;Lo;0;R;;;;;N;;;;; +10A8B;OLD NORTH ARABIAN LETTER KAF;Lo;0;R;;;;;N;;;;; +10A8C;OLD NORTH ARABIAN LETTER NOON;Lo;0;R;;;;;N;;;;; +10A8D;OLD NORTH ARABIAN LETTER KHAH;Lo;0;R;;;;;N;;;;; +10A8E;OLD NORTH ARABIAN LETTER SAD;Lo;0;R;;;;;N;;;;; +10A8F;OLD NORTH ARABIAN LETTER ES-3;Lo;0;R;;;;;N;;;;; +10A90;OLD NORTH ARABIAN LETTER FEH;Lo;0;R;;;;;N;;;;; +10A91;OLD NORTH ARABIAN LETTER ALEF;Lo;0;R;;;;;N;;;;; +10A92;OLD NORTH ARABIAN LETTER AIN;Lo;0;R;;;;;N;;;;; +10A93;OLD NORTH ARABIAN LETTER DAD;Lo;0;R;;;;;N;;;;; +10A94;OLD NORTH ARABIAN LETTER GEEM;Lo;0;R;;;;;N;;;;; +10A95;OLD NORTH ARABIAN LETTER DAL;Lo;0;R;;;;;N;;;;; +10A96;OLD NORTH ARABIAN LETTER GHAIN;Lo;0;R;;;;;N;;;;; +10A97;OLD NORTH ARABIAN LETTER TAH;Lo;0;R;;;;;N;;;;; +10A98;OLD NORTH ARABIAN LETTER ZAIN;Lo;0;R;;;;;N;;;;; +10A99;OLD NORTH ARABIAN LETTER THAL;Lo;0;R;;;;;N;;;;; +10A9A;OLD NORTH ARABIAN LETTER YEH;Lo;0;R;;;;;N;;;;; +10A9B;OLD NORTH ARABIAN LETTER THEH;Lo;0;R;;;;;N;;;;; +10A9C;OLD NORTH ARABIAN LETTER ZAH;Lo;0;R;;;;;N;;;;; +10A9D;OLD NORTH ARABIAN NUMBER ONE;No;0;R;;;;1;N;;;;; +10A9E;OLD NORTH ARABIAN NUMBER TEN;No;0;R;;;;10;N;;;;; +10A9F;OLD NORTH ARABIAN NUMBER TWENTY;No;0;R;;;;20;N;;;;; +10AC0;MANICHAEAN LETTER ALEPH;Lo;0;R;;;;;N;;;;; +10AC1;MANICHAEAN LETTER BETH;Lo;0;R;;;;;N;;;;; +10AC2;MANICHAEAN LETTER BHETH;Lo;0;R;;;;;N;;;;; +10AC3;MANICHAEAN LETTER GIMEL;Lo;0;R;;;;;N;;;;; +10AC4;MANICHAEAN LETTER GHIMEL;Lo;0;R;;;;;N;;;;; +10AC5;MANICHAEAN LETTER DALETH;Lo;0;R;;;;;N;;;;; +10AC6;MANICHAEAN LETTER HE;Lo;0;R;;;;;N;;;;; +10AC7;MANICHAEAN LETTER WAW;Lo;0;R;;;;;N;;;;; +10AC8;MANICHAEAN SIGN UD;So;0;R;;;;;N;;;;; +10AC9;MANICHAEAN LETTER ZAYIN;Lo;0;R;;;;;N;;;;; +10ACA;MANICHAEAN LETTER ZHAYIN;Lo;0;R;;;;;N;;;;; +10ACB;MANICHAEAN LETTER JAYIN;Lo;0;R;;;;;N;;;;; +10ACC;MANICHAEAN LETTER JHAYIN;Lo;0;R;;;;;N;;;;; +10ACD;MANICHAEAN LETTER HETH;Lo;0;R;;;;;N;;;;; +10ACE;MANICHAEAN LETTER TETH;Lo;0;R;;;;;N;;;;; +10ACF;MANICHAEAN LETTER YODH;Lo;0;R;;;;;N;;;;; +10AD0;MANICHAEAN LETTER KAPH;Lo;0;R;;;;;N;;;;; +10AD1;MANICHAEAN LETTER XAPH;Lo;0;R;;;;;N;;;;; +10AD2;MANICHAEAN LETTER KHAPH;Lo;0;R;;;;;N;;;;; +10AD3;MANICHAEAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;; +10AD4;MANICHAEAN LETTER DHAMEDH;Lo;0;R;;;;;N;;;;; +10AD5;MANICHAEAN LETTER THAMEDH;Lo;0;R;;;;;N;;;;; +10AD6;MANICHAEAN LETTER MEM;Lo;0;R;;;;;N;;;;; +10AD7;MANICHAEAN LETTER NUN;Lo;0;R;;;;;N;;;;; +10AD8;MANICHAEAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;; +10AD9;MANICHAEAN LETTER AYIN;Lo;0;R;;;;;N;;;;; +10ADA;MANICHAEAN LETTER AAYIN;Lo;0;R;;;;;N;;;;; +10ADB;MANICHAEAN LETTER PE;Lo;0;R;;;;;N;;;;; +10ADC;MANICHAEAN LETTER FE;Lo;0;R;;;;;N;;;;; +10ADD;MANICHAEAN LETTER SADHE;Lo;0;R;;;;;N;;;;; +10ADE;MANICHAEAN LETTER QOPH;Lo;0;R;;;;;N;;;;; +10ADF;MANICHAEAN LETTER XOPH;Lo;0;R;;;;;N;;;;; +10AE0;MANICHAEAN LETTER QHOPH;Lo;0;R;;;;;N;;;;; +10AE1;MANICHAEAN LETTER RESH;Lo;0;R;;;;;N;;;;; +10AE2;MANICHAEAN LETTER SHIN;Lo;0;R;;;;;N;;;;; +10AE3;MANICHAEAN LETTER SSHIN;Lo;0;R;;;;;N;;;;; +10AE4;MANICHAEAN LETTER TAW;Lo;0;R;;;;;N;;;;; +10AE5;MANICHAEAN ABBREVIATION MARK ABOVE;Mn;230;NSM;;;;;N;;;;; +10AE6;MANICHAEAN ABBREVIATION MARK BELOW;Mn;220;NSM;;;;;N;;;;; +10AEB;MANICHAEAN NUMBER ONE;No;0;R;;;;1;N;;;;; +10AEC;MANICHAEAN NUMBER FIVE;No;0;R;;;;5;N;;;;; +10AED;MANICHAEAN NUMBER TEN;No;0;R;;;;10;N;;;;; +10AEE;MANICHAEAN NUMBER TWENTY;No;0;R;;;;20;N;;;;; +10AEF;MANICHAEAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; +10AF0;MANICHAEAN PUNCTUATION STAR;Po;0;R;;;;;N;;;;; +10AF1;MANICHAEAN PUNCTUATION FLEURON;Po;0;R;;;;;N;;;;; +10AF2;MANICHAEAN PUNCTUATION DOUBLE DOT WITHIN DOT;Po;0;R;;;;;N;;;;; +10AF3;MANICHAEAN PUNCTUATION DOT WITHIN DOT;Po;0;R;;;;;N;;;;; +10AF4;MANICHAEAN PUNCTUATION DOT;Po;0;R;;;;;N;;;;; +10AF5;MANICHAEAN PUNCTUATION TWO DOTS;Po;0;R;;;;;N;;;;; +10AF6;MANICHAEAN PUNCTUATION LINE FILLER;Po;0;R;;;;;N;;;;; +10B00;AVESTAN LETTER A;Lo;0;R;;;;;N;;;;; +10B01;AVESTAN LETTER AA;Lo;0;R;;;;;N;;;;; +10B02;AVESTAN LETTER AO;Lo;0;R;;;;;N;;;;; +10B03;AVESTAN LETTER AAO;Lo;0;R;;;;;N;;;;; +10B04;AVESTAN LETTER AN;Lo;0;R;;;;;N;;;;; +10B05;AVESTAN LETTER AAN;Lo;0;R;;;;;N;;;;; +10B06;AVESTAN LETTER AE;Lo;0;R;;;;;N;;;;; +10B07;AVESTAN LETTER AEE;Lo;0;R;;;;;N;;;;; +10B08;AVESTAN LETTER E;Lo;0;R;;;;;N;;;;; +10B09;AVESTAN LETTER EE;Lo;0;R;;;;;N;;;;; +10B0A;AVESTAN LETTER O;Lo;0;R;;;;;N;;;;; +10B0B;AVESTAN LETTER OO;Lo;0;R;;;;;N;;;;; +10B0C;AVESTAN LETTER I;Lo;0;R;;;;;N;;;;; +10B0D;AVESTAN LETTER II;Lo;0;R;;;;;N;;;;; +10B0E;AVESTAN LETTER U;Lo;0;R;;;;;N;;;;; +10B0F;AVESTAN LETTER UU;Lo;0;R;;;;;N;;;;; +10B10;AVESTAN LETTER KE;Lo;0;R;;;;;N;;;;; +10B11;AVESTAN LETTER XE;Lo;0;R;;;;;N;;;;; +10B12;AVESTAN LETTER XYE;Lo;0;R;;;;;N;;;;; +10B13;AVESTAN LETTER XVE;Lo;0;R;;;;;N;;;;; +10B14;AVESTAN LETTER GE;Lo;0;R;;;;;N;;;;; +10B15;AVESTAN LETTER GGE;Lo;0;R;;;;;N;;;;; +10B16;AVESTAN LETTER GHE;Lo;0;R;;;;;N;;;;; +10B17;AVESTAN LETTER CE;Lo;0;R;;;;;N;;;;; +10B18;AVESTAN LETTER JE;Lo;0;R;;;;;N;;;;; +10B19;AVESTAN LETTER TE;Lo;0;R;;;;;N;;;;; +10B1A;AVESTAN LETTER THE;Lo;0;R;;;;;N;;;;; +10B1B;AVESTAN LETTER DE;Lo;0;R;;;;;N;;;;; +10B1C;AVESTAN LETTER DHE;Lo;0;R;;;;;N;;;;; +10B1D;AVESTAN LETTER TTE;Lo;0;R;;;;;N;;;;; +10B1E;AVESTAN LETTER PE;Lo;0;R;;;;;N;;;;; +10B1F;AVESTAN LETTER FE;Lo;0;R;;;;;N;;;;; +10B20;AVESTAN LETTER BE;Lo;0;R;;;;;N;;;;; +10B21;AVESTAN LETTER BHE;Lo;0;R;;;;;N;;;;; +10B22;AVESTAN LETTER NGE;Lo;0;R;;;;;N;;;;; +10B23;AVESTAN LETTER NGYE;Lo;0;R;;;;;N;;;;; +10B24;AVESTAN LETTER NGVE;Lo;0;R;;;;;N;;;;; +10B25;AVESTAN LETTER NE;Lo;0;R;;;;;N;;;;; +10B26;AVESTAN LETTER NYE;Lo;0;R;;;;;N;;;;; +10B27;AVESTAN LETTER NNE;Lo;0;R;;;;;N;;;;; +10B28;AVESTAN LETTER ME;Lo;0;R;;;;;N;;;;; +10B29;AVESTAN LETTER HME;Lo;0;R;;;;;N;;;;; +10B2A;AVESTAN LETTER YYE;Lo;0;R;;;;;N;;;;; +10B2B;AVESTAN LETTER YE;Lo;0;R;;;;;N;;;;; +10B2C;AVESTAN LETTER VE;Lo;0;R;;;;;N;;;;; +10B2D;AVESTAN LETTER RE;Lo;0;R;;;;;N;;;;; +10B2E;AVESTAN LETTER LE;Lo;0;R;;;;;N;;;;; +10B2F;AVESTAN LETTER SE;Lo;0;R;;;;;N;;;;; +10B30;AVESTAN LETTER ZE;Lo;0;R;;;;;N;;;;; +10B31;AVESTAN LETTER SHE;Lo;0;R;;;;;N;;;;; +10B32;AVESTAN LETTER ZHE;Lo;0;R;;;;;N;;;;; +10B33;AVESTAN LETTER SHYE;Lo;0;R;;;;;N;;;;; +10B34;AVESTAN LETTER SSHE;Lo;0;R;;;;;N;;;;; +10B35;AVESTAN LETTER HE;Lo;0;R;;;;;N;;;;; +10B39;AVESTAN ABBREVIATION MARK;Po;0;ON;;;;;N;;;;; +10B3A;TINY TWO DOTS OVER ONE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; +10B3B;SMALL TWO DOTS OVER ONE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; +10B3C;LARGE TWO DOTS OVER ONE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;; +10B3D;LARGE ONE DOT OVER TWO DOTS PUNCTUATION;Po;0;ON;;;;;N;;;;; +10B3E;LARGE TWO RINGS OVER ONE RING PUNCTUATION;Po;0;ON;;;;;N;;;;; +10B3F;LARGE ONE RING OVER TWO RINGS PUNCTUATION;Po;0;ON;;;;;N;;;;; +10B40;INSCRIPTIONAL PARTHIAN LETTER ALEPH;Lo;0;R;;;;;N;;;;; +10B41;INSCRIPTIONAL PARTHIAN LETTER BETH;Lo;0;R;;;;;N;;;;; +10B42;INSCRIPTIONAL PARTHIAN LETTER GIMEL;Lo;0;R;;;;;N;;;;; +10B43;INSCRIPTIONAL PARTHIAN LETTER DALETH;Lo;0;R;;;;;N;;;;; +10B44;INSCRIPTIONAL PARTHIAN LETTER HE;Lo;0;R;;;;;N;;;;; +10B45;INSCRIPTIONAL PARTHIAN LETTER WAW;Lo;0;R;;;;;N;;;;; +10B46;INSCRIPTIONAL PARTHIAN LETTER ZAYIN;Lo;0;R;;;;;N;;;;; +10B47;INSCRIPTIONAL PARTHIAN LETTER HETH;Lo;0;R;;;;;N;;;;; +10B48;INSCRIPTIONAL PARTHIAN LETTER TETH;Lo;0;R;;;;;N;;;;; +10B49;INSCRIPTIONAL PARTHIAN LETTER YODH;Lo;0;R;;;;;N;;;;; +10B4A;INSCRIPTIONAL PARTHIAN LETTER KAPH;Lo;0;R;;;;;N;;;;; +10B4B;INSCRIPTIONAL PARTHIAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;; +10B4C;INSCRIPTIONAL PARTHIAN LETTER MEM;Lo;0;R;;;;;N;;;;; +10B4D;INSCRIPTIONAL PARTHIAN LETTER NUN;Lo;0;R;;;;;N;;;;; +10B4E;INSCRIPTIONAL PARTHIAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;; +10B4F;INSCRIPTIONAL PARTHIAN LETTER AYIN;Lo;0;R;;;;;N;;;;; +10B50;INSCRIPTIONAL PARTHIAN LETTER PE;Lo;0;R;;;;;N;;;;; +10B51;INSCRIPTIONAL PARTHIAN LETTER SADHE;Lo;0;R;;;;;N;;;;; +10B52;INSCRIPTIONAL PARTHIAN LETTER QOPH;Lo;0;R;;;;;N;;;;; +10B53;INSCRIPTIONAL PARTHIAN LETTER RESH;Lo;0;R;;;;;N;;;;; +10B54;INSCRIPTIONAL PARTHIAN LETTER SHIN;Lo;0;R;;;;;N;;;;; +10B55;INSCRIPTIONAL PARTHIAN LETTER TAW;Lo;0;R;;;;;N;;;;; +10B58;INSCRIPTIONAL PARTHIAN NUMBER ONE;No;0;R;;;;1;N;;;;; +10B59;INSCRIPTIONAL PARTHIAN NUMBER TWO;No;0;R;;;;2;N;;;;; +10B5A;INSCRIPTIONAL PARTHIAN NUMBER THREE;No;0;R;;;;3;N;;;;; +10B5B;INSCRIPTIONAL PARTHIAN NUMBER FOUR;No;0;R;;;;4;N;;;;; +10B5C;INSCRIPTIONAL PARTHIAN NUMBER TEN;No;0;R;;;;10;N;;;;; +10B5D;INSCRIPTIONAL PARTHIAN NUMBER TWENTY;No;0;R;;;;20;N;;;;; +10B5E;INSCRIPTIONAL PARTHIAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; +10B5F;INSCRIPTIONAL PARTHIAN NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;; +10B60;INSCRIPTIONAL PAHLAVI LETTER ALEPH;Lo;0;R;;;;;N;;;;; +10B61;INSCRIPTIONAL PAHLAVI LETTER BETH;Lo;0;R;;;;;N;;;;; +10B62;INSCRIPTIONAL PAHLAVI LETTER GIMEL;Lo;0;R;;;;;N;;;;; +10B63;INSCRIPTIONAL PAHLAVI LETTER DALETH;Lo;0;R;;;;;N;;;;; +10B64;INSCRIPTIONAL PAHLAVI LETTER HE;Lo;0;R;;;;;N;;;;; +10B65;INSCRIPTIONAL PAHLAVI LETTER WAW-AYIN-RESH;Lo;0;R;;;;;N;;;;; +10B66;INSCRIPTIONAL PAHLAVI LETTER ZAYIN;Lo;0;R;;;;;N;;;;; +10B67;INSCRIPTIONAL PAHLAVI LETTER HETH;Lo;0;R;;;;;N;;;;; +10B68;INSCRIPTIONAL PAHLAVI LETTER TETH;Lo;0;R;;;;;N;;;;; +10B69;INSCRIPTIONAL PAHLAVI LETTER YODH;Lo;0;R;;;;;N;;;;; +10B6A;INSCRIPTIONAL PAHLAVI LETTER KAPH;Lo;0;R;;;;;N;;;;; +10B6B;INSCRIPTIONAL PAHLAVI LETTER LAMEDH;Lo;0;R;;;;;N;;;;; +10B6C;INSCRIPTIONAL PAHLAVI LETTER MEM-QOPH;Lo;0;R;;;;;N;;;;; +10B6D;INSCRIPTIONAL PAHLAVI LETTER NUN;Lo;0;R;;;;;N;;;;; +10B6E;INSCRIPTIONAL PAHLAVI LETTER SAMEKH;Lo;0;R;;;;;N;;;;; +10B6F;INSCRIPTIONAL PAHLAVI LETTER PE;Lo;0;R;;;;;N;;;;; +10B70;INSCRIPTIONAL PAHLAVI LETTER SADHE;Lo;0;R;;;;;N;;;;; +10B71;INSCRIPTIONAL PAHLAVI LETTER SHIN;Lo;0;R;;;;;N;;;;; +10B72;INSCRIPTIONAL PAHLAVI LETTER TAW;Lo;0;R;;;;;N;;;;; +10B78;INSCRIPTIONAL PAHLAVI NUMBER ONE;No;0;R;;;;1;N;;;;; +10B79;INSCRIPTIONAL PAHLAVI NUMBER TWO;No;0;R;;;;2;N;;;;; +10B7A;INSCRIPTIONAL PAHLAVI NUMBER THREE;No;0;R;;;;3;N;;;;; +10B7B;INSCRIPTIONAL PAHLAVI NUMBER FOUR;No;0;R;;;;4;N;;;;; +10B7C;INSCRIPTIONAL PAHLAVI NUMBER TEN;No;0;R;;;;10;N;;;;; +10B7D;INSCRIPTIONAL PAHLAVI NUMBER TWENTY;No;0;R;;;;20;N;;;;; +10B7E;INSCRIPTIONAL PAHLAVI NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; +10B7F;INSCRIPTIONAL PAHLAVI NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;; +10B80;PSALTER PAHLAVI LETTER ALEPH;Lo;0;R;;;;;N;;;;; +10B81;PSALTER PAHLAVI LETTER BETH;Lo;0;R;;;;;N;;;;; +10B82;PSALTER PAHLAVI LETTER GIMEL;Lo;0;R;;;;;N;;;;; +10B83;PSALTER PAHLAVI LETTER DALETH;Lo;0;R;;;;;N;;;;; +10B84;PSALTER PAHLAVI LETTER HE;Lo;0;R;;;;;N;;;;; +10B85;PSALTER PAHLAVI LETTER WAW-AYIN-RESH;Lo;0;R;;;;;N;;;;; +10B86;PSALTER PAHLAVI LETTER ZAYIN;Lo;0;R;;;;;N;;;;; +10B87;PSALTER PAHLAVI LETTER HETH;Lo;0;R;;;;;N;;;;; +10B88;PSALTER PAHLAVI LETTER YODH;Lo;0;R;;;;;N;;;;; +10B89;PSALTER PAHLAVI LETTER KAPH;Lo;0;R;;;;;N;;;;; +10B8A;PSALTER PAHLAVI LETTER LAMEDH;Lo;0;R;;;;;N;;;;; +10B8B;PSALTER PAHLAVI LETTER MEM-QOPH;Lo;0;R;;;;;N;;;;; +10B8C;PSALTER PAHLAVI LETTER NUN;Lo;0;R;;;;;N;;;;; +10B8D;PSALTER PAHLAVI LETTER SAMEKH;Lo;0;R;;;;;N;;;;; +10B8E;PSALTER PAHLAVI LETTER PE;Lo;0;R;;;;;N;;;;; +10B8F;PSALTER PAHLAVI LETTER SADHE;Lo;0;R;;;;;N;;;;; +10B90;PSALTER PAHLAVI LETTER SHIN;Lo;0;R;;;;;N;;;;; +10B91;PSALTER PAHLAVI LETTER TAW;Lo;0;R;;;;;N;;;;; +10B99;PSALTER PAHLAVI SECTION MARK;Po;0;R;;;;;N;;;;; +10B9A;PSALTER PAHLAVI TURNED SECTION MARK;Po;0;R;;;;;N;;;;; +10B9B;PSALTER PAHLAVI FOUR DOTS WITH CROSS;Po;0;R;;;;;N;;;;; +10B9C;PSALTER PAHLAVI FOUR DOTS WITH DOT;Po;0;R;;;;;N;;;;; +10BA9;PSALTER PAHLAVI NUMBER ONE;No;0;R;;;;1;N;;;;; +10BAA;PSALTER PAHLAVI NUMBER TWO;No;0;R;;;;2;N;;;;; +10BAB;PSALTER PAHLAVI NUMBER THREE;No;0;R;;;;3;N;;;;; +10BAC;PSALTER PAHLAVI NUMBER FOUR;No;0;R;;;;4;N;;;;; +10BAD;PSALTER PAHLAVI NUMBER TEN;No;0;R;;;;10;N;;;;; +10BAE;PSALTER PAHLAVI NUMBER TWENTY;No;0;R;;;;20;N;;;;; +10BAF;PSALTER PAHLAVI NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; +10C00;OLD TURKIC LETTER ORKHON A;Lo;0;R;;;;;N;;;;; +10C01;OLD TURKIC LETTER YENISEI A;Lo;0;R;;;;;N;;;;; +10C02;OLD TURKIC LETTER YENISEI AE;Lo;0;R;;;;;N;;;;; +10C03;OLD TURKIC LETTER ORKHON I;Lo;0;R;;;;;N;;;;; +10C04;OLD TURKIC LETTER YENISEI I;Lo;0;R;;;;;N;;;;; +10C05;OLD TURKIC LETTER YENISEI E;Lo;0;R;;;;;N;;;;; +10C06;OLD TURKIC LETTER ORKHON O;Lo;0;R;;;;;N;;;;; +10C07;OLD TURKIC LETTER ORKHON OE;Lo;0;R;;;;;N;;;;; +10C08;OLD TURKIC LETTER YENISEI OE;Lo;0;R;;;;;N;;;;; +10C09;OLD TURKIC LETTER ORKHON AB;Lo;0;R;;;;;N;;;;; +10C0A;OLD TURKIC LETTER YENISEI AB;Lo;0;R;;;;;N;;;;; +10C0B;OLD TURKIC LETTER ORKHON AEB;Lo;0;R;;;;;N;;;;; +10C0C;OLD TURKIC LETTER YENISEI AEB;Lo;0;R;;;;;N;;;;; +10C0D;OLD TURKIC LETTER ORKHON AG;Lo;0;R;;;;;N;;;;; +10C0E;OLD TURKIC LETTER YENISEI AG;Lo;0;R;;;;;N;;;;; +10C0F;OLD TURKIC LETTER ORKHON AEG;Lo;0;R;;;;;N;;;;; +10C10;OLD TURKIC LETTER YENISEI AEG;Lo;0;R;;;;;N;;;;; +10C11;OLD TURKIC LETTER ORKHON AD;Lo;0;R;;;;;N;;;;; +10C12;OLD TURKIC LETTER YENISEI AD;Lo;0;R;;;;;N;;;;; +10C13;OLD TURKIC LETTER ORKHON AED;Lo;0;R;;;;;N;;;;; +10C14;OLD TURKIC LETTER ORKHON EZ;Lo;0;R;;;;;N;;;;; +10C15;OLD TURKIC LETTER YENISEI EZ;Lo;0;R;;;;;N;;;;; +10C16;OLD TURKIC LETTER ORKHON AY;Lo;0;R;;;;;N;;;;; +10C17;OLD TURKIC LETTER YENISEI AY;Lo;0;R;;;;;N;;;;; +10C18;OLD TURKIC LETTER ORKHON AEY;Lo;0;R;;;;;N;;;;; +10C19;OLD TURKIC LETTER YENISEI AEY;Lo;0;R;;;;;N;;;;; +10C1A;OLD TURKIC LETTER ORKHON AEK;Lo;0;R;;;;;N;;;;; +10C1B;OLD TURKIC LETTER YENISEI AEK;Lo;0;R;;;;;N;;;;; +10C1C;OLD TURKIC LETTER ORKHON OEK;Lo;0;R;;;;;N;;;;; +10C1D;OLD TURKIC LETTER YENISEI OEK;Lo;0;R;;;;;N;;;;; +10C1E;OLD TURKIC LETTER ORKHON AL;Lo;0;R;;;;;N;;;;; +10C1F;OLD TURKIC LETTER YENISEI AL;Lo;0;R;;;;;N;;;;; +10C20;OLD TURKIC LETTER ORKHON AEL;Lo;0;R;;;;;N;;;;; +10C21;OLD TURKIC LETTER ORKHON ELT;Lo;0;R;;;;;N;;;;; +10C22;OLD TURKIC LETTER ORKHON EM;Lo;0;R;;;;;N;;;;; +10C23;OLD TURKIC LETTER ORKHON AN;Lo;0;R;;;;;N;;;;; +10C24;OLD TURKIC LETTER ORKHON AEN;Lo;0;R;;;;;N;;;;; +10C25;OLD TURKIC LETTER YENISEI AEN;Lo;0;R;;;;;N;;;;; +10C26;OLD TURKIC LETTER ORKHON ENT;Lo;0;R;;;;;N;;;;; +10C27;OLD TURKIC LETTER YENISEI ENT;Lo;0;R;;;;;N;;;;; +10C28;OLD TURKIC LETTER ORKHON ENC;Lo;0;R;;;;;N;;;;; +10C29;OLD TURKIC LETTER YENISEI ENC;Lo;0;R;;;;;N;;;;; +10C2A;OLD TURKIC LETTER ORKHON ENY;Lo;0;R;;;;;N;;;;; +10C2B;OLD TURKIC LETTER YENISEI ENY;Lo;0;R;;;;;N;;;;; +10C2C;OLD TURKIC LETTER YENISEI ANG;Lo;0;R;;;;;N;;;;; +10C2D;OLD TURKIC LETTER ORKHON ENG;Lo;0;R;;;;;N;;;;; +10C2E;OLD TURKIC LETTER YENISEI AENG;Lo;0;R;;;;;N;;;;; +10C2F;OLD TURKIC LETTER ORKHON EP;Lo;0;R;;;;;N;;;;; +10C30;OLD TURKIC LETTER ORKHON OP;Lo;0;R;;;;;N;;;;; +10C31;OLD TURKIC LETTER ORKHON IC;Lo;0;R;;;;;N;;;;; +10C32;OLD TURKIC LETTER ORKHON EC;Lo;0;R;;;;;N;;;;; +10C33;OLD TURKIC LETTER YENISEI EC;Lo;0;R;;;;;N;;;;; +10C34;OLD TURKIC LETTER ORKHON AQ;Lo;0;R;;;;;N;;;;; +10C35;OLD TURKIC LETTER YENISEI AQ;Lo;0;R;;;;;N;;;;; +10C36;OLD TURKIC LETTER ORKHON IQ;Lo;0;R;;;;;N;;;;; +10C37;OLD TURKIC LETTER YENISEI IQ;Lo;0;R;;;;;N;;;;; +10C38;OLD TURKIC LETTER ORKHON OQ;Lo;0;R;;;;;N;;;;; +10C39;OLD TURKIC LETTER YENISEI OQ;Lo;0;R;;;;;N;;;;; +10C3A;OLD TURKIC LETTER ORKHON AR;Lo;0;R;;;;;N;;;;; +10C3B;OLD TURKIC LETTER YENISEI AR;Lo;0;R;;;;;N;;;;; +10C3C;OLD TURKIC LETTER ORKHON AER;Lo;0;R;;;;;N;;;;; +10C3D;OLD TURKIC LETTER ORKHON AS;Lo;0;R;;;;;N;;;;; +10C3E;OLD TURKIC LETTER ORKHON AES;Lo;0;R;;;;;N;;;;; +10C3F;OLD TURKIC LETTER ORKHON ASH;Lo;0;R;;;;;N;;;;; +10C40;OLD TURKIC LETTER YENISEI ASH;Lo;0;R;;;;;N;;;;; +10C41;OLD TURKIC LETTER ORKHON ESH;Lo;0;R;;;;;N;;;;; +10C42;OLD TURKIC LETTER YENISEI ESH;Lo;0;R;;;;;N;;;;; +10C43;OLD TURKIC LETTER ORKHON AT;Lo;0;R;;;;;N;;;;; +10C44;OLD TURKIC LETTER YENISEI AT;Lo;0;R;;;;;N;;;;; +10C45;OLD TURKIC LETTER ORKHON AET;Lo;0;R;;;;;N;;;;; +10C46;OLD TURKIC LETTER YENISEI AET;Lo;0;R;;;;;N;;;;; +10C47;OLD TURKIC LETTER ORKHON OT;Lo;0;R;;;;;N;;;;; +10C48;OLD TURKIC LETTER ORKHON BASH;Lo;0;R;;;;;N;;;;; +10C80;OLD HUNGARIAN CAPITAL LETTER A;Lu;0;R;;;;;N;;;;10CC0; +10C81;OLD HUNGARIAN CAPITAL LETTER AA;Lu;0;R;;;;;N;;;;10CC1; +10C82;OLD HUNGARIAN CAPITAL LETTER EB;Lu;0;R;;;;;N;;;;10CC2; +10C83;OLD HUNGARIAN CAPITAL LETTER AMB;Lu;0;R;;;;;N;;;;10CC3; +10C84;OLD HUNGARIAN CAPITAL LETTER EC;Lu;0;R;;;;;N;;;;10CC4; +10C85;OLD HUNGARIAN CAPITAL LETTER ENC;Lu;0;R;;;;;N;;;;10CC5; +10C86;OLD HUNGARIAN CAPITAL LETTER ECS;Lu;0;R;;;;;N;;;;10CC6; +10C87;OLD HUNGARIAN CAPITAL LETTER ED;Lu;0;R;;;;;N;;;;10CC7; +10C88;OLD HUNGARIAN CAPITAL LETTER AND;Lu;0;R;;;;;N;;;;10CC8; +10C89;OLD HUNGARIAN CAPITAL LETTER E;Lu;0;R;;;;;N;;;;10CC9; +10C8A;OLD HUNGARIAN CAPITAL LETTER CLOSE E;Lu;0;R;;;;;N;;;;10CCA; +10C8B;OLD HUNGARIAN CAPITAL LETTER EE;Lu;0;R;;;;;N;;;;10CCB; +10C8C;OLD HUNGARIAN CAPITAL LETTER EF;Lu;0;R;;;;;N;;;;10CCC; +10C8D;OLD HUNGARIAN CAPITAL LETTER EG;Lu;0;R;;;;;N;;;;10CCD; +10C8E;OLD HUNGARIAN CAPITAL LETTER EGY;Lu;0;R;;;;;N;;;;10CCE; +10C8F;OLD HUNGARIAN CAPITAL LETTER EH;Lu;0;R;;;;;N;;;;10CCF; +10C90;OLD HUNGARIAN CAPITAL LETTER I;Lu;0;R;;;;;N;;;;10CD0; +10C91;OLD HUNGARIAN CAPITAL LETTER II;Lu;0;R;;;;;N;;;;10CD1; +10C92;OLD HUNGARIAN CAPITAL LETTER EJ;Lu;0;R;;;;;N;;;;10CD2; +10C93;OLD HUNGARIAN CAPITAL LETTER EK;Lu;0;R;;;;;N;;;;10CD3; +10C94;OLD HUNGARIAN CAPITAL LETTER AK;Lu;0;R;;;;;N;;;;10CD4; +10C95;OLD HUNGARIAN CAPITAL LETTER UNK;Lu;0;R;;;;;N;;;;10CD5; +10C96;OLD HUNGARIAN CAPITAL LETTER EL;Lu;0;R;;;;;N;;;;10CD6; +10C97;OLD HUNGARIAN CAPITAL LETTER ELY;Lu;0;R;;;;;N;;;;10CD7; +10C98;OLD HUNGARIAN CAPITAL LETTER EM;Lu;0;R;;;;;N;;;;10CD8; +10C99;OLD HUNGARIAN CAPITAL LETTER EN;Lu;0;R;;;;;N;;;;10CD9; +10C9A;OLD HUNGARIAN CAPITAL LETTER ENY;Lu;0;R;;;;;N;;;;10CDA; +10C9B;OLD HUNGARIAN CAPITAL LETTER O;Lu;0;R;;;;;N;;;;10CDB; +10C9C;OLD HUNGARIAN CAPITAL LETTER OO;Lu;0;R;;;;;N;;;;10CDC; +10C9D;OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG OE;Lu;0;R;;;;;N;;;;10CDD; +10C9E;OLD HUNGARIAN CAPITAL LETTER RUDIMENTA OE;Lu;0;R;;;;;N;;;;10CDE; +10C9F;OLD HUNGARIAN CAPITAL LETTER OEE;Lu;0;R;;;;;N;;;;10CDF; +10CA0;OLD HUNGARIAN CAPITAL LETTER EP;Lu;0;R;;;;;N;;;;10CE0; +10CA1;OLD HUNGARIAN CAPITAL LETTER EMP;Lu;0;R;;;;;N;;;;10CE1; +10CA2;OLD HUNGARIAN CAPITAL LETTER ER;Lu;0;R;;;;;N;;;;10CE2; +10CA3;OLD HUNGARIAN CAPITAL LETTER SHORT ER;Lu;0;R;;;;;N;;;;10CE3; +10CA4;OLD HUNGARIAN CAPITAL LETTER ES;Lu;0;R;;;;;N;;;;10CE4; +10CA5;OLD HUNGARIAN CAPITAL LETTER ESZ;Lu;0;R;;;;;N;;;;10CE5; +10CA6;OLD HUNGARIAN CAPITAL LETTER ET;Lu;0;R;;;;;N;;;;10CE6; +10CA7;OLD HUNGARIAN CAPITAL LETTER ENT;Lu;0;R;;;;;N;;;;10CE7; +10CA8;OLD HUNGARIAN CAPITAL LETTER ETY;Lu;0;R;;;;;N;;;;10CE8; +10CA9;OLD HUNGARIAN CAPITAL LETTER ECH;Lu;0;R;;;;;N;;;;10CE9; +10CAA;OLD HUNGARIAN CAPITAL LETTER U;Lu;0;R;;;;;N;;;;10CEA; +10CAB;OLD HUNGARIAN CAPITAL LETTER UU;Lu;0;R;;;;;N;;;;10CEB; +10CAC;OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG UE;Lu;0;R;;;;;N;;;;10CEC; +10CAD;OLD HUNGARIAN CAPITAL LETTER RUDIMENTA UE;Lu;0;R;;;;;N;;;;10CED; +10CAE;OLD HUNGARIAN CAPITAL LETTER EV;Lu;0;R;;;;;N;;;;10CEE; +10CAF;OLD HUNGARIAN CAPITAL LETTER EZ;Lu;0;R;;;;;N;;;;10CEF; +10CB0;OLD HUNGARIAN CAPITAL LETTER EZS;Lu;0;R;;;;;N;;;;10CF0; +10CB1;OLD HUNGARIAN CAPITAL LETTER ENT-SHAPED SIGN;Lu;0;R;;;;;N;;;;10CF1; +10CB2;OLD HUNGARIAN CAPITAL LETTER US;Lu;0;R;;;;;N;;;;10CF2; +10CC0;OLD HUNGARIAN SMALL LETTER A;Ll;0;R;;;;;N;;;10C80;;10C80 +10CC1;OLD HUNGARIAN SMALL LETTER AA;Ll;0;R;;;;;N;;;10C81;;10C81 +10CC2;OLD HUNGARIAN SMALL LETTER EB;Ll;0;R;;;;;N;;;10C82;;10C82 +10CC3;OLD HUNGARIAN SMALL LETTER AMB;Ll;0;R;;;;;N;;;10C83;;10C83 +10CC4;OLD HUNGARIAN SMALL LETTER EC;Ll;0;R;;;;;N;;;10C84;;10C84 +10CC5;OLD HUNGARIAN SMALL LETTER ENC;Ll;0;R;;;;;N;;;10C85;;10C85 +10CC6;OLD HUNGARIAN SMALL LETTER ECS;Ll;0;R;;;;;N;;;10C86;;10C86 +10CC7;OLD HUNGARIAN SMALL LETTER ED;Ll;0;R;;;;;N;;;10C87;;10C87 +10CC8;OLD HUNGARIAN SMALL LETTER AND;Ll;0;R;;;;;N;;;10C88;;10C88 +10CC9;OLD HUNGARIAN SMALL LETTER E;Ll;0;R;;;;;N;;;10C89;;10C89 +10CCA;OLD HUNGARIAN SMALL LETTER CLOSE E;Ll;0;R;;;;;N;;;10C8A;;10C8A +10CCB;OLD HUNGARIAN SMALL LETTER EE;Ll;0;R;;;;;N;;;10C8B;;10C8B +10CCC;OLD HUNGARIAN SMALL LETTER EF;Ll;0;R;;;;;N;;;10C8C;;10C8C +10CCD;OLD HUNGARIAN SMALL LETTER EG;Ll;0;R;;;;;N;;;10C8D;;10C8D +10CCE;OLD HUNGARIAN SMALL LETTER EGY;Ll;0;R;;;;;N;;;10C8E;;10C8E +10CCF;OLD HUNGARIAN SMALL LETTER EH;Ll;0;R;;;;;N;;;10C8F;;10C8F +10CD0;OLD HUNGARIAN SMALL LETTER I;Ll;0;R;;;;;N;;;10C90;;10C90 +10CD1;OLD HUNGARIAN SMALL LETTER II;Ll;0;R;;;;;N;;;10C91;;10C91 +10CD2;OLD HUNGARIAN SMALL LETTER EJ;Ll;0;R;;;;;N;;;10C92;;10C92 +10CD3;OLD HUNGARIAN SMALL LETTER EK;Ll;0;R;;;;;N;;;10C93;;10C93 +10CD4;OLD HUNGARIAN SMALL LETTER AK;Ll;0;R;;;;;N;;;10C94;;10C94 +10CD5;OLD HUNGARIAN SMALL LETTER UNK;Ll;0;R;;;;;N;;;10C95;;10C95 +10CD6;OLD HUNGARIAN SMALL LETTER EL;Ll;0;R;;;;;N;;;10C96;;10C96 +10CD7;OLD HUNGARIAN SMALL LETTER ELY;Ll;0;R;;;;;N;;;10C97;;10C97 +10CD8;OLD HUNGARIAN SMALL LETTER EM;Ll;0;R;;;;;N;;;10C98;;10C98 +10CD9;OLD HUNGARIAN SMALL LETTER EN;Ll;0;R;;;;;N;;;10C99;;10C99 +10CDA;OLD HUNGARIAN SMALL LETTER ENY;Ll;0;R;;;;;N;;;10C9A;;10C9A +10CDB;OLD HUNGARIAN SMALL LETTER O;Ll;0;R;;;;;N;;;10C9B;;10C9B +10CDC;OLD HUNGARIAN SMALL LETTER OO;Ll;0;R;;;;;N;;;10C9C;;10C9C +10CDD;OLD HUNGARIAN SMALL LETTER NIKOLSBURG OE;Ll;0;R;;;;;N;;;10C9D;;10C9D +10CDE;OLD HUNGARIAN SMALL LETTER RUDIMENTA OE;Ll;0;R;;;;;N;;;10C9E;;10C9E +10CDF;OLD HUNGARIAN SMALL LETTER OEE;Ll;0;R;;;;;N;;;10C9F;;10C9F +10CE0;OLD HUNGARIAN SMALL LETTER EP;Ll;0;R;;;;;N;;;10CA0;;10CA0 +10CE1;OLD HUNGARIAN SMALL LETTER EMP;Ll;0;R;;;;;N;;;10CA1;;10CA1 +10CE2;OLD HUNGARIAN SMALL LETTER ER;Ll;0;R;;;;;N;;;10CA2;;10CA2 +10CE3;OLD HUNGARIAN SMALL LETTER SHORT ER;Ll;0;R;;;;;N;;;10CA3;;10CA3 +10CE4;OLD HUNGARIAN SMALL LETTER ES;Ll;0;R;;;;;N;;;10CA4;;10CA4 +10CE5;OLD HUNGARIAN SMALL LETTER ESZ;Ll;0;R;;;;;N;;;10CA5;;10CA5 +10CE6;OLD HUNGARIAN SMALL LETTER ET;Ll;0;R;;;;;N;;;10CA6;;10CA6 +10CE7;OLD HUNGARIAN SMALL LETTER ENT;Ll;0;R;;;;;N;;;10CA7;;10CA7 +10CE8;OLD HUNGARIAN SMALL LETTER ETY;Ll;0;R;;;;;N;;;10CA8;;10CA8 +10CE9;OLD HUNGARIAN SMALL LETTER ECH;Ll;0;R;;;;;N;;;10CA9;;10CA9 +10CEA;OLD HUNGARIAN SMALL LETTER U;Ll;0;R;;;;;N;;;10CAA;;10CAA +10CEB;OLD HUNGARIAN SMALL LETTER UU;Ll;0;R;;;;;N;;;10CAB;;10CAB +10CEC;OLD HUNGARIAN SMALL LETTER NIKOLSBURG UE;Ll;0;R;;;;;N;;;10CAC;;10CAC +10CED;OLD HUNGARIAN SMALL LETTER RUDIMENTA UE;Ll;0;R;;;;;N;;;10CAD;;10CAD +10CEE;OLD HUNGARIAN SMALL LETTER EV;Ll;0;R;;;;;N;;;10CAE;;10CAE +10CEF;OLD HUNGARIAN SMALL LETTER EZ;Ll;0;R;;;;;N;;;10CAF;;10CAF +10CF0;OLD HUNGARIAN SMALL LETTER EZS;Ll;0;R;;;;;N;;;10CB0;;10CB0 +10CF1;OLD HUNGARIAN SMALL LETTER ENT-SHAPED SIGN;Ll;0;R;;;;;N;;;10CB1;;10CB1 +10CF2;OLD HUNGARIAN SMALL LETTER US;Ll;0;R;;;;;N;;;10CB2;;10CB2 +10CFA;OLD HUNGARIAN NUMBER ONE;No;0;R;;;;1;N;;;;; +10CFB;OLD HUNGARIAN NUMBER FIVE;No;0;R;;;;5;N;;;;; +10CFC;OLD HUNGARIAN NUMBER TEN;No;0;R;;;;10;N;;;;; +10CFD;OLD HUNGARIAN NUMBER FIFTY;No;0;R;;;;50;N;;;;; +10CFE;OLD HUNGARIAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;; +10CFF;OLD HUNGARIAN NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;; +10E60;RUMI DIGIT ONE;No;0;AN;;;1;1;N;;;;; +10E61;RUMI DIGIT TWO;No;0;AN;;;2;2;N;;;;; +10E62;RUMI DIGIT THREE;No;0;AN;;;3;3;N;;;;; +10E63;RUMI DIGIT FOUR;No;0;AN;;;4;4;N;;;;; +10E64;RUMI DIGIT FIVE;No;0;AN;;;5;5;N;;;;; +10E65;RUMI DIGIT SIX;No;0;AN;;;6;6;N;;;;; +10E66;RUMI DIGIT SEVEN;No;0;AN;;;7;7;N;;;;; +10E67;RUMI DIGIT EIGHT;No;0;AN;;;8;8;N;;;;; +10E68;RUMI DIGIT NINE;No;0;AN;;;9;9;N;;;;; +10E69;RUMI NUMBER TEN;No;0;AN;;;;10;N;;;;; +10E6A;RUMI NUMBER TWENTY;No;0;AN;;;;20;N;;;;; +10E6B;RUMI NUMBER THIRTY;No;0;AN;;;;30;N;;;;; +10E6C;RUMI NUMBER FORTY;No;0;AN;;;;40;N;;;;; +10E6D;RUMI NUMBER FIFTY;No;0;AN;;;;50;N;;;;; +10E6E;RUMI NUMBER SIXTY;No;0;AN;;;;60;N;;;;; +10E6F;RUMI NUMBER SEVENTY;No;0;AN;;;;70;N;;;;; +10E70;RUMI NUMBER EIGHTY;No;0;AN;;;;80;N;;;;; +10E71;RUMI NUMBER NINETY;No;0;AN;;;;90;N;;;;; +10E72;RUMI NUMBER ONE HUNDRED;No;0;AN;;;;100;N;;;;; +10E73;RUMI NUMBER TWO HUNDRED;No;0;AN;;;;200;N;;;;; +10E74;RUMI NUMBER THREE HUNDRED;No;0;AN;;;;300;N;;;;; +10E75;RUMI NUMBER FOUR HUNDRED;No;0;AN;;;;400;N;;;;; +10E76;RUMI NUMBER FIVE HUNDRED;No;0;AN;;;;500;N;;;;; +10E77;RUMI NUMBER SIX HUNDRED;No;0;AN;;;;600;N;;;;; +10E78;RUMI NUMBER SEVEN HUNDRED;No;0;AN;;;;700;N;;;;; +10E79;RUMI NUMBER EIGHT HUNDRED;No;0;AN;;;;800;N;;;;; +10E7A;RUMI NUMBER NINE HUNDRED;No;0;AN;;;;900;N;;;;; +10E7B;RUMI FRACTION ONE HALF;No;0;AN;;;;1/2;N;;;;; +10E7C;RUMI FRACTION ONE QUARTER;No;0;AN;;;;1/4;N;;;;; +10E7D;RUMI FRACTION ONE THIRD;No;0;AN;;;;1/3;N;;;;; +10E7E;RUMI FRACTION TWO THIRDS;No;0;AN;;;;2/3;N;;;;; +11000;BRAHMI SIGN CANDRABINDU;Mc;0;L;;;;;N;;;;; +11001;BRAHMI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +11002;BRAHMI SIGN VISARGA;Mc;0;L;;;;;N;;;;; +11003;BRAHMI SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;; +11004;BRAHMI SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;; +11005;BRAHMI LETTER A;Lo;0;L;;;;;N;;;;; +11006;BRAHMI LETTER AA;Lo;0;L;;;;;N;;;;; +11007;BRAHMI LETTER I;Lo;0;L;;;;;N;;;;; +11008;BRAHMI LETTER II;Lo;0;L;;;;;N;;;;; +11009;BRAHMI LETTER U;Lo;0;L;;;;;N;;;;; +1100A;BRAHMI LETTER UU;Lo;0;L;;;;;N;;;;; +1100B;BRAHMI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +1100C;BRAHMI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +1100D;BRAHMI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +1100E;BRAHMI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +1100F;BRAHMI LETTER E;Lo;0;L;;;;;N;;;;; +11010;BRAHMI LETTER AI;Lo;0;L;;;;;N;;;;; +11011;BRAHMI LETTER O;Lo;0;L;;;;;N;;;;; +11012;BRAHMI LETTER AU;Lo;0;L;;;;;N;;;;; +11013;BRAHMI LETTER KA;Lo;0;L;;;;;N;;;;; +11014;BRAHMI LETTER KHA;Lo;0;L;;;;;N;;;;; +11015;BRAHMI LETTER GA;Lo;0;L;;;;;N;;;;; +11016;BRAHMI LETTER GHA;Lo;0;L;;;;;N;;;;; +11017;BRAHMI LETTER NGA;Lo;0;L;;;;;N;;;;; +11018;BRAHMI LETTER CA;Lo;0;L;;;;;N;;;;; +11019;BRAHMI LETTER CHA;Lo;0;L;;;;;N;;;;; +1101A;BRAHMI LETTER JA;Lo;0;L;;;;;N;;;;; +1101B;BRAHMI LETTER JHA;Lo;0;L;;;;;N;;;;; +1101C;BRAHMI LETTER NYA;Lo;0;L;;;;;N;;;;; +1101D;BRAHMI LETTER TTA;Lo;0;L;;;;;N;;;;; +1101E;BRAHMI LETTER TTHA;Lo;0;L;;;;;N;;;;; +1101F;BRAHMI LETTER DDA;Lo;0;L;;;;;N;;;;; +11020;BRAHMI LETTER DDHA;Lo;0;L;;;;;N;;;;; +11021;BRAHMI LETTER NNA;Lo;0;L;;;;;N;;;;; +11022;BRAHMI LETTER TA;Lo;0;L;;;;;N;;;;; +11023;BRAHMI LETTER THA;Lo;0;L;;;;;N;;;;; +11024;BRAHMI LETTER DA;Lo;0;L;;;;;N;;;;; +11025;BRAHMI LETTER DHA;Lo;0;L;;;;;N;;;;; +11026;BRAHMI LETTER NA;Lo;0;L;;;;;N;;;;; +11027;BRAHMI LETTER PA;Lo;0;L;;;;;N;;;;; +11028;BRAHMI LETTER PHA;Lo;0;L;;;;;N;;;;; +11029;BRAHMI LETTER BA;Lo;0;L;;;;;N;;;;; +1102A;BRAHMI LETTER BHA;Lo;0;L;;;;;N;;;;; +1102B;BRAHMI LETTER MA;Lo;0;L;;;;;N;;;;; +1102C;BRAHMI LETTER YA;Lo;0;L;;;;;N;;;;; +1102D;BRAHMI LETTER RA;Lo;0;L;;;;;N;;;;; +1102E;BRAHMI LETTER LA;Lo;0;L;;;;;N;;;;; +1102F;BRAHMI LETTER VA;Lo;0;L;;;;;N;;;;; +11030;BRAHMI LETTER SHA;Lo;0;L;;;;;N;;;;; +11031;BRAHMI LETTER SSA;Lo;0;L;;;;;N;;;;; +11032;BRAHMI LETTER SA;Lo;0;L;;;;;N;;;;; +11033;BRAHMI LETTER HA;Lo;0;L;;;;;N;;;;; +11034;BRAHMI LETTER LLA;Lo;0;L;;;;;N;;;;; +11035;BRAHMI LETTER OLD TAMIL LLLA;Lo;0;L;;;;;N;;;;; +11036;BRAHMI LETTER OLD TAMIL RRA;Lo;0;L;;;;;N;;;;; +11037;BRAHMI LETTER OLD TAMIL NNNA;Lo;0;L;;;;;N;;;;; +11038;BRAHMI VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;; +11039;BRAHMI VOWEL SIGN BHATTIPROLU AA;Mn;0;NSM;;;;;N;;;;; +1103A;BRAHMI VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +1103B;BRAHMI VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +1103C;BRAHMI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1103D;BRAHMI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +1103E;BRAHMI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +1103F;BRAHMI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +11040;BRAHMI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +11041;BRAHMI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +11042;BRAHMI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +11043;BRAHMI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +11044;BRAHMI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; +11045;BRAHMI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; +11046;BRAHMI VIRAMA;Mn;9;NSM;;;;;N;;;;; +11047;BRAHMI DANDA;Po;0;L;;;;;N;;;;; +11048;BRAHMI DOUBLE DANDA;Po;0;L;;;;;N;;;;; +11049;BRAHMI PUNCTUATION DOT;Po;0;L;;;;;N;;;;; +1104A;BRAHMI PUNCTUATION DOUBLE DOT;Po;0;L;;;;;N;;;;; +1104B;BRAHMI PUNCTUATION LINE;Po;0;L;;;;;N;;;;; +1104C;BRAHMI PUNCTUATION CRESCENT BAR;Po;0;L;;;;;N;;;;; +1104D;BRAHMI PUNCTUATION LOTUS;Po;0;L;;;;;N;;;;; +11052;BRAHMI NUMBER ONE;No;0;ON;;;1;1;N;;;;; +11053;BRAHMI NUMBER TWO;No;0;ON;;;2;2;N;;;;; +11054;BRAHMI NUMBER THREE;No;0;ON;;;3;3;N;;;;; +11055;BRAHMI NUMBER FOUR;No;0;ON;;;4;4;N;;;;; +11056;BRAHMI NUMBER FIVE;No;0;ON;;;5;5;N;;;;; +11057;BRAHMI NUMBER SIX;No;0;ON;;;6;6;N;;;;; +11058;BRAHMI NUMBER SEVEN;No;0;ON;;;7;7;N;;;;; +11059;BRAHMI NUMBER EIGHT;No;0;ON;;;8;8;N;;;;; +1105A;BRAHMI NUMBER NINE;No;0;ON;;;9;9;N;;;;; +1105B;BRAHMI NUMBER TEN;No;0;ON;;;;10;N;;;;; +1105C;BRAHMI NUMBER TWENTY;No;0;ON;;;;20;N;;;;; +1105D;BRAHMI NUMBER THIRTY;No;0;ON;;;;30;N;;;;; +1105E;BRAHMI NUMBER FORTY;No;0;ON;;;;40;N;;;;; +1105F;BRAHMI NUMBER FIFTY;No;0;ON;;;;50;N;;;;; +11060;BRAHMI NUMBER SIXTY;No;0;ON;;;;60;N;;;;; +11061;BRAHMI NUMBER SEVENTY;No;0;ON;;;;70;N;;;;; +11062;BRAHMI NUMBER EIGHTY;No;0;ON;;;;80;N;;;;; +11063;BRAHMI NUMBER NINETY;No;0;ON;;;;90;N;;;;; +11064;BRAHMI NUMBER ONE HUNDRED;No;0;ON;;;;100;N;;;;; +11065;BRAHMI NUMBER ONE THOUSAND;No;0;ON;;;;1000;N;;;;; +11066;BRAHMI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +11067;BRAHMI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +11068;BRAHMI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +11069;BRAHMI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +1106A;BRAHMI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +1106B;BRAHMI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +1106C;BRAHMI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +1106D;BRAHMI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1106E;BRAHMI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1106F;BRAHMI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1107F;BRAHMI NUMBER JOINER;Mn;9;NSM;;;;;N;;;;; +11080;KAITHI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +11081;KAITHI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +11082;KAITHI SIGN VISARGA;Mc;0;L;;;;;N;;;;; +11083;KAITHI LETTER A;Lo;0;L;;;;;N;;;;; +11084;KAITHI LETTER AA;Lo;0;L;;;;;N;;;;; +11085;KAITHI LETTER I;Lo;0;L;;;;;N;;;;; +11086;KAITHI LETTER II;Lo;0;L;;;;;N;;;;; +11087;KAITHI LETTER U;Lo;0;L;;;;;N;;;;; +11088;KAITHI LETTER UU;Lo;0;L;;;;;N;;;;; +11089;KAITHI LETTER E;Lo;0;L;;;;;N;;;;; +1108A;KAITHI LETTER AI;Lo;0;L;;;;;N;;;;; +1108B;KAITHI LETTER O;Lo;0;L;;;;;N;;;;; +1108C;KAITHI LETTER AU;Lo;0;L;;;;;N;;;;; +1108D;KAITHI LETTER KA;Lo;0;L;;;;;N;;;;; +1108E;KAITHI LETTER KHA;Lo;0;L;;;;;N;;;;; +1108F;KAITHI LETTER GA;Lo;0;L;;;;;N;;;;; +11090;KAITHI LETTER GHA;Lo;0;L;;;;;N;;;;; +11091;KAITHI LETTER NGA;Lo;0;L;;;;;N;;;;; +11092;KAITHI LETTER CA;Lo;0;L;;;;;N;;;;; +11093;KAITHI LETTER CHA;Lo;0;L;;;;;N;;;;; +11094;KAITHI LETTER JA;Lo;0;L;;;;;N;;;;; +11095;KAITHI LETTER JHA;Lo;0;L;;;;;N;;;;; +11096;KAITHI LETTER NYA;Lo;0;L;;;;;N;;;;; +11097;KAITHI LETTER TTA;Lo;0;L;;;;;N;;;;; +11098;KAITHI LETTER TTHA;Lo;0;L;;;;;N;;;;; +11099;KAITHI LETTER DDA;Lo;0;L;;;;;N;;;;; +1109A;KAITHI LETTER DDDHA;Lo;0;L;11099 110BA;;;;N;;;;; +1109B;KAITHI LETTER DDHA;Lo;0;L;;;;;N;;;;; +1109C;KAITHI LETTER RHA;Lo;0;L;1109B 110BA;;;;N;;;;; +1109D;KAITHI LETTER NNA;Lo;0;L;;;;;N;;;;; +1109E;KAITHI LETTER TA;Lo;0;L;;;;;N;;;;; +1109F;KAITHI LETTER THA;Lo;0;L;;;;;N;;;;; +110A0;KAITHI LETTER DA;Lo;0;L;;;;;N;;;;; +110A1;KAITHI LETTER DHA;Lo;0;L;;;;;N;;;;; +110A2;KAITHI LETTER NA;Lo;0;L;;;;;N;;;;; +110A3;KAITHI LETTER PA;Lo;0;L;;;;;N;;;;; +110A4;KAITHI LETTER PHA;Lo;0;L;;;;;N;;;;; +110A5;KAITHI LETTER BA;Lo;0;L;;;;;N;;;;; +110A6;KAITHI LETTER BHA;Lo;0;L;;;;;N;;;;; +110A7;KAITHI LETTER MA;Lo;0;L;;;;;N;;;;; +110A8;KAITHI LETTER YA;Lo;0;L;;;;;N;;;;; +110A9;KAITHI LETTER RA;Lo;0;L;;;;;N;;;;; +110AA;KAITHI LETTER LA;Lo;0;L;;;;;N;;;;; +110AB;KAITHI LETTER VA;Lo;0;L;110A5 110BA;;;;N;;;;; +110AC;KAITHI LETTER SHA;Lo;0;L;;;;;N;;;;; +110AD;KAITHI LETTER SSA;Lo;0;L;;;;;N;;;;; +110AE;KAITHI LETTER SA;Lo;0;L;;;;;N;;;;; +110AF;KAITHI LETTER HA;Lo;0;L;;;;;N;;;;; +110B0;KAITHI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +110B1;KAITHI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +110B2;KAITHI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +110B3;KAITHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +110B4;KAITHI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +110B5;KAITHI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +110B6;KAITHI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +110B7;KAITHI VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +110B8;KAITHI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +110B9;KAITHI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +110BA;KAITHI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +110BB;KAITHI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; +110BC;KAITHI ENUMERATION SIGN;Po;0;L;;;;;N;;;;; +110BD;KAITHI NUMBER SIGN;Cf;0;L;;;;;N;;;;; +110BE;KAITHI SECTION MARK;Po;0;L;;;;;N;;;;; +110BF;KAITHI DOUBLE SECTION MARK;Po;0;L;;;;;N;;;;; +110C0;KAITHI DANDA;Po;0;L;;;;;N;;;;; +110C1;KAITHI DOUBLE DANDA;Po;0;L;;;;;N;;;;; +110D0;SORA SOMPENG LETTER SAH;Lo;0;L;;;;;N;;;;; +110D1;SORA SOMPENG LETTER TAH;Lo;0;L;;;;;N;;;;; +110D2;SORA SOMPENG LETTER BAH;Lo;0;L;;;;;N;;;;; +110D3;SORA SOMPENG LETTER CAH;Lo;0;L;;;;;N;;;;; +110D4;SORA SOMPENG LETTER DAH;Lo;0;L;;;;;N;;;;; +110D5;SORA SOMPENG LETTER GAH;Lo;0;L;;;;;N;;;;; +110D6;SORA SOMPENG LETTER MAH;Lo;0;L;;;;;N;;;;; +110D7;SORA SOMPENG LETTER NGAH;Lo;0;L;;;;;N;;;;; +110D8;SORA SOMPENG LETTER LAH;Lo;0;L;;;;;N;;;;; +110D9;SORA SOMPENG LETTER NAH;Lo;0;L;;;;;N;;;;; +110DA;SORA SOMPENG LETTER VAH;Lo;0;L;;;;;N;;;;; +110DB;SORA SOMPENG LETTER PAH;Lo;0;L;;;;;N;;;;; +110DC;SORA SOMPENG LETTER YAH;Lo;0;L;;;;;N;;;;; +110DD;SORA SOMPENG LETTER RAH;Lo;0;L;;;;;N;;;;; +110DE;SORA SOMPENG LETTER HAH;Lo;0;L;;;;;N;;;;; +110DF;SORA SOMPENG LETTER KAH;Lo;0;L;;;;;N;;;;; +110E0;SORA SOMPENG LETTER JAH;Lo;0;L;;;;;N;;;;; +110E1;SORA SOMPENG LETTER NYAH;Lo;0;L;;;;;N;;;;; +110E2;SORA SOMPENG LETTER AH;Lo;0;L;;;;;N;;;;; +110E3;SORA SOMPENG LETTER EEH;Lo;0;L;;;;;N;;;;; +110E4;SORA SOMPENG LETTER IH;Lo;0;L;;;;;N;;;;; +110E5;SORA SOMPENG LETTER UH;Lo;0;L;;;;;N;;;;; +110E6;SORA SOMPENG LETTER OH;Lo;0;L;;;;;N;;;;; +110E7;SORA SOMPENG LETTER EH;Lo;0;L;;;;;N;;;;; +110E8;SORA SOMPENG LETTER MAE;Lo;0;L;;;;;N;;;;; +110F0;SORA SOMPENG DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +110F1;SORA SOMPENG DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +110F2;SORA SOMPENG DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +110F3;SORA SOMPENG DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +110F4;SORA SOMPENG DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +110F5;SORA SOMPENG DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +110F6;SORA SOMPENG DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +110F7;SORA SOMPENG DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +110F8;SORA SOMPENG DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +110F9;SORA SOMPENG DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +11100;CHAKMA SIGN CANDRABINDU;Mn;230;NSM;;;;;N;;;;; +11101;CHAKMA SIGN ANUSVARA;Mn;230;NSM;;;;;N;;;;; +11102;CHAKMA SIGN VISARGA;Mn;230;NSM;;;;;N;;;;; +11103;CHAKMA LETTER AA;Lo;0;L;;;;;N;;;;; +11104;CHAKMA LETTER I;Lo;0;L;;;;;N;;;;; +11105;CHAKMA LETTER U;Lo;0;L;;;;;N;;;;; +11106;CHAKMA LETTER E;Lo;0;L;;;;;N;;;;; +11107;CHAKMA LETTER KAA;Lo;0;L;;;;;N;;;;; +11108;CHAKMA LETTER KHAA;Lo;0;L;;;;;N;;;;; +11109;CHAKMA LETTER GAA;Lo;0;L;;;;;N;;;;; +1110A;CHAKMA LETTER GHAA;Lo;0;L;;;;;N;;;;; +1110B;CHAKMA LETTER NGAA;Lo;0;L;;;;;N;;;;; +1110C;CHAKMA LETTER CAA;Lo;0;L;;;;;N;;;;; +1110D;CHAKMA LETTER CHAA;Lo;0;L;;;;;N;;;;; +1110E;CHAKMA LETTER JAA;Lo;0;L;;;;;N;;;;; +1110F;CHAKMA LETTER JHAA;Lo;0;L;;;;;N;;;;; +11110;CHAKMA LETTER NYAA;Lo;0;L;;;;;N;;;;; +11111;CHAKMA LETTER TTAA;Lo;0;L;;;;;N;;;;; +11112;CHAKMA LETTER TTHAA;Lo;0;L;;;;;N;;;;; +11113;CHAKMA LETTER DDAA;Lo;0;L;;;;;N;;;;; +11114;CHAKMA LETTER DDHAA;Lo;0;L;;;;;N;;;;; +11115;CHAKMA LETTER NNAA;Lo;0;L;;;;;N;;;;; +11116;CHAKMA LETTER TAA;Lo;0;L;;;;;N;;;;; +11117;CHAKMA LETTER THAA;Lo;0;L;;;;;N;;;;; +11118;CHAKMA LETTER DAA;Lo;0;L;;;;;N;;;;; +11119;CHAKMA LETTER DHAA;Lo;0;L;;;;;N;;;;; +1111A;CHAKMA LETTER NAA;Lo;0;L;;;;;N;;;;; +1111B;CHAKMA LETTER PAA;Lo;0;L;;;;;N;;;;; +1111C;CHAKMA LETTER PHAA;Lo;0;L;;;;;N;;;;; +1111D;CHAKMA LETTER BAA;Lo;0;L;;;;;N;;;;; +1111E;CHAKMA LETTER BHAA;Lo;0;L;;;;;N;;;;; +1111F;CHAKMA LETTER MAA;Lo;0;L;;;;;N;;;;; +11120;CHAKMA LETTER YYAA;Lo;0;L;;;;;N;;;;; +11121;CHAKMA LETTER YAA;Lo;0;L;;;;;N;;;;; +11122;CHAKMA LETTER RAA;Lo;0;L;;;;;N;;;;; +11123;CHAKMA LETTER LAA;Lo;0;L;;;;;N;;;;; +11124;CHAKMA LETTER WAA;Lo;0;L;;;;;N;;;;; +11125;CHAKMA LETTER SAA;Lo;0;L;;;;;N;;;;; +11126;CHAKMA LETTER HAA;Lo;0;L;;;;;N;;;;; +11127;CHAKMA VOWEL SIGN A;Mn;0;NSM;;;;;N;;;;; +11128;CHAKMA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +11129;CHAKMA VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +1112A;CHAKMA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1112B;CHAKMA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +1112C;CHAKMA VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +1112D;CHAKMA VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +1112E;CHAKMA VOWEL SIGN O;Mn;0;NSM;11131 11127;;;;N;;;;; +1112F;CHAKMA VOWEL SIGN AU;Mn;0;NSM;11132 11127;;;;N;;;;; +11130;CHAKMA VOWEL SIGN OI;Mn;0;NSM;;;;;N;;;;; +11131;CHAKMA O MARK;Mn;0;NSM;;;;;N;;;;; +11132;CHAKMA AU MARK;Mn;0;NSM;;;;;N;;;;; +11133;CHAKMA VIRAMA;Mn;9;NSM;;;;;N;;;;; +11134;CHAKMA MAAYYAA;Mn;9;NSM;;;;;N;;;;; +11136;CHAKMA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +11137;CHAKMA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +11138;CHAKMA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +11139;CHAKMA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +1113A;CHAKMA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +1113B;CHAKMA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +1113C;CHAKMA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +1113D;CHAKMA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1113E;CHAKMA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1113F;CHAKMA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +11140;CHAKMA SECTION MARK;Po;0;L;;;;;N;;;;; +11141;CHAKMA DANDA;Po;0;L;;;;;N;;;;; +11142;CHAKMA DOUBLE DANDA;Po;0;L;;;;;N;;;;; +11143;CHAKMA QUESTION MARK;Po;0;L;;;;;N;;;;; +11150;MAHAJANI LETTER A;Lo;0;L;;;;;N;;;;; +11151;MAHAJANI LETTER I;Lo;0;L;;;;;N;;;;; +11152;MAHAJANI LETTER U;Lo;0;L;;;;;N;;;;; +11153;MAHAJANI LETTER E;Lo;0;L;;;;;N;;;;; +11154;MAHAJANI LETTER O;Lo;0;L;;;;;N;;;;; +11155;MAHAJANI LETTER KA;Lo;0;L;;;;;N;;;;; +11156;MAHAJANI LETTER KHA;Lo;0;L;;;;;N;;;;; +11157;MAHAJANI LETTER GA;Lo;0;L;;;;;N;;;;; +11158;MAHAJANI LETTER GHA;Lo;0;L;;;;;N;;;;; +11159;MAHAJANI LETTER CA;Lo;0;L;;;;;N;;;;; +1115A;MAHAJANI LETTER CHA;Lo;0;L;;;;;N;;;;; +1115B;MAHAJANI LETTER JA;Lo;0;L;;;;;N;;;;; +1115C;MAHAJANI LETTER JHA;Lo;0;L;;;;;N;;;;; +1115D;MAHAJANI LETTER NYA;Lo;0;L;;;;;N;;;;; +1115E;MAHAJANI LETTER TTA;Lo;0;L;;;;;N;;;;; +1115F;MAHAJANI LETTER TTHA;Lo;0;L;;;;;N;;;;; +11160;MAHAJANI LETTER DDA;Lo;0;L;;;;;N;;;;; +11161;MAHAJANI LETTER DDHA;Lo;0;L;;;;;N;;;;; +11162;MAHAJANI LETTER NNA;Lo;0;L;;;;;N;;;;; +11163;MAHAJANI LETTER TA;Lo;0;L;;;;;N;;;;; +11164;MAHAJANI LETTER THA;Lo;0;L;;;;;N;;;;; +11165;MAHAJANI LETTER DA;Lo;0;L;;;;;N;;;;; +11166;MAHAJANI LETTER DHA;Lo;0;L;;;;;N;;;;; +11167;MAHAJANI LETTER NA;Lo;0;L;;;;;N;;;;; +11168;MAHAJANI LETTER PA;Lo;0;L;;;;;N;;;;; +11169;MAHAJANI LETTER PHA;Lo;0;L;;;;;N;;;;; +1116A;MAHAJANI LETTER BA;Lo;0;L;;;;;N;;;;; +1116B;MAHAJANI LETTER BHA;Lo;0;L;;;;;N;;;;; +1116C;MAHAJANI LETTER MA;Lo;0;L;;;;;N;;;;; +1116D;MAHAJANI LETTER RA;Lo;0;L;;;;;N;;;;; +1116E;MAHAJANI LETTER LA;Lo;0;L;;;;;N;;;;; +1116F;MAHAJANI LETTER VA;Lo;0;L;;;;;N;;;;; +11170;MAHAJANI LETTER SA;Lo;0;L;;;;;N;;;;; +11171;MAHAJANI LETTER HA;Lo;0;L;;;;;N;;;;; +11172;MAHAJANI LETTER RRA;Lo;0;L;;;;;N;;;;; +11173;MAHAJANI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +11174;MAHAJANI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; +11175;MAHAJANI SECTION MARK;Po;0;L;;;;;N;;;;; +11176;MAHAJANI LIGATURE SHRI;Lo;0;L;;;;;N;;;;; +11180;SHARADA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +11181;SHARADA SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +11182;SHARADA SIGN VISARGA;Mc;0;L;;;;;N;;;;; +11183;SHARADA LETTER A;Lo;0;L;;;;;N;;;;; +11184;SHARADA LETTER AA;Lo;0;L;;;;;N;;;;; +11185;SHARADA LETTER I;Lo;0;L;;;;;N;;;;; +11186;SHARADA LETTER II;Lo;0;L;;;;;N;;;;; +11187;SHARADA LETTER U;Lo;0;L;;;;;N;;;;; +11188;SHARADA LETTER UU;Lo;0;L;;;;;N;;;;; +11189;SHARADA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +1118A;SHARADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +1118B;SHARADA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +1118C;SHARADA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +1118D;SHARADA LETTER E;Lo;0;L;;;;;N;;;;; +1118E;SHARADA LETTER AI;Lo;0;L;;;;;N;;;;; +1118F;SHARADA LETTER O;Lo;0;L;;;;;N;;;;; +11190;SHARADA LETTER AU;Lo;0;L;;;;;N;;;;; +11191;SHARADA LETTER KA;Lo;0;L;;;;;N;;;;; +11192;SHARADA LETTER KHA;Lo;0;L;;;;;N;;;;; +11193;SHARADA LETTER GA;Lo;0;L;;;;;N;;;;; +11194;SHARADA LETTER GHA;Lo;0;L;;;;;N;;;;; +11195;SHARADA LETTER NGA;Lo;0;L;;;;;N;;;;; +11196;SHARADA LETTER CA;Lo;0;L;;;;;N;;;;; +11197;SHARADA LETTER CHA;Lo;0;L;;;;;N;;;;; +11198;SHARADA LETTER JA;Lo;0;L;;;;;N;;;;; +11199;SHARADA LETTER JHA;Lo;0;L;;;;;N;;;;; +1119A;SHARADA LETTER NYA;Lo;0;L;;;;;N;;;;; +1119B;SHARADA LETTER TTA;Lo;0;L;;;;;N;;;;; +1119C;SHARADA LETTER TTHA;Lo;0;L;;;;;N;;;;; +1119D;SHARADA LETTER DDA;Lo;0;L;;;;;N;;;;; +1119E;SHARADA LETTER DDHA;Lo;0;L;;;;;N;;;;; +1119F;SHARADA LETTER NNA;Lo;0;L;;;;;N;;;;; +111A0;SHARADA LETTER TA;Lo;0;L;;;;;N;;;;; +111A1;SHARADA LETTER THA;Lo;0;L;;;;;N;;;;; +111A2;SHARADA LETTER DA;Lo;0;L;;;;;N;;;;; +111A3;SHARADA LETTER DHA;Lo;0;L;;;;;N;;;;; +111A4;SHARADA LETTER NA;Lo;0;L;;;;;N;;;;; +111A5;SHARADA LETTER PA;Lo;0;L;;;;;N;;;;; +111A6;SHARADA LETTER PHA;Lo;0;L;;;;;N;;;;; +111A7;SHARADA LETTER BA;Lo;0;L;;;;;N;;;;; +111A8;SHARADA LETTER BHA;Lo;0;L;;;;;N;;;;; +111A9;SHARADA LETTER MA;Lo;0;L;;;;;N;;;;; +111AA;SHARADA LETTER YA;Lo;0;L;;;;;N;;;;; +111AB;SHARADA LETTER RA;Lo;0;L;;;;;N;;;;; +111AC;SHARADA LETTER LA;Lo;0;L;;;;;N;;;;; +111AD;SHARADA LETTER LLA;Lo;0;L;;;;;N;;;;; +111AE;SHARADA LETTER VA;Lo;0;L;;;;;N;;;;; +111AF;SHARADA LETTER SHA;Lo;0;L;;;;;N;;;;; +111B0;SHARADA LETTER SSA;Lo;0;L;;;;;N;;;;; +111B1;SHARADA LETTER SA;Lo;0;L;;;;;N;;;;; +111B2;SHARADA LETTER HA;Lo;0;L;;;;;N;;;;; +111B3;SHARADA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +111B4;SHARADA VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +111B5;SHARADA VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +111B6;SHARADA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +111B7;SHARADA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +111B8;SHARADA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +111B9;SHARADA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +111BA;SHARADA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +111BB;SHARADA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +111BC;SHARADA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +111BD;SHARADA VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +111BE;SHARADA VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; +111BF;SHARADA VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +111C0;SHARADA SIGN VIRAMA;Mc;9;L;;;;;N;;;;; +111C1;SHARADA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +111C2;SHARADA SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;; +111C3;SHARADA SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;; +111C4;SHARADA OM;Lo;0;L;;;;;N;;;;; +111C5;SHARADA DANDA;Po;0;L;;;;;N;;;;; +111C6;SHARADA DOUBLE DANDA;Po;0;L;;;;;N;;;;; +111C7;SHARADA ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; +111C8;SHARADA SEPARATOR;Po;0;L;;;;;N;;;;; +111C9;SHARADA SANDHI MARK;Po;0;L;;;;;N;;;;; +111CA;SHARADA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +111CB;SHARADA VOWEL MODIFIER MARK;Mn;0;NSM;;;;;N;;;;; +111CC;SHARADA EXTRA SHORT VOWEL MARK;Mn;0;NSM;;;;;N;;;;; +111CD;SHARADA SUTRA MARK;Po;0;L;;;;;N;;;;; +111D0;SHARADA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +111D1;SHARADA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +111D2;SHARADA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +111D3;SHARADA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +111D4;SHARADA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +111D5;SHARADA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +111D6;SHARADA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +111D7;SHARADA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +111D8;SHARADA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +111D9;SHARADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +111DA;SHARADA EKAM;Lo;0;L;;;;;N;;;;; +111DB;SHARADA SIGN SIDDHAM;Po;0;L;;;;;N;;;;; +111DC;SHARADA HEADSTROKE;Lo;0;L;;;;;N;;;;; +111DD;SHARADA CONTINUATION SIGN;Po;0;L;;;;;N;;;;; +111DE;SHARADA SECTION MARK-1;Po;0;L;;;;;N;;;;; +111DF;SHARADA SECTION MARK-2;Po;0;L;;;;;N;;;;; +111E1;SINHALA ARCHAIC DIGIT ONE;No;0;L;;;;1;N;;;;; +111E2;SINHALA ARCHAIC DIGIT TWO;No;0;L;;;;2;N;;;;; +111E3;SINHALA ARCHAIC DIGIT THREE;No;0;L;;;;3;N;;;;; +111E4;SINHALA ARCHAIC DIGIT FOUR;No;0;L;;;;4;N;;;;; +111E5;SINHALA ARCHAIC DIGIT FIVE;No;0;L;;;;5;N;;;;; +111E6;SINHALA ARCHAIC DIGIT SIX;No;0;L;;;;6;N;;;;; +111E7;SINHALA ARCHAIC DIGIT SEVEN;No;0;L;;;;7;N;;;;; +111E8;SINHALA ARCHAIC DIGIT EIGHT;No;0;L;;;;8;N;;;;; +111E9;SINHALA ARCHAIC DIGIT NINE;No;0;L;;;;9;N;;;;; +111EA;SINHALA ARCHAIC NUMBER TEN;No;0;L;;;;10;N;;;;; +111EB;SINHALA ARCHAIC NUMBER TWENTY;No;0;L;;;;20;N;;;;; +111EC;SINHALA ARCHAIC NUMBER THIRTY;No;0;L;;;;30;N;;;;; +111ED;SINHALA ARCHAIC NUMBER FORTY;No;0;L;;;;40;N;;;;; +111EE;SINHALA ARCHAIC NUMBER FIFTY;No;0;L;;;;50;N;;;;; +111EF;SINHALA ARCHAIC NUMBER SIXTY;No;0;L;;;;60;N;;;;; +111F0;SINHALA ARCHAIC NUMBER SEVENTY;No;0;L;;;;70;N;;;;; +111F1;SINHALA ARCHAIC NUMBER EIGHTY;No;0;L;;;;80;N;;;;; +111F2;SINHALA ARCHAIC NUMBER NINETY;No;0;L;;;;90;N;;;;; +111F3;SINHALA ARCHAIC NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;; +111F4;SINHALA ARCHAIC NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;; +11200;KHOJKI LETTER A;Lo;0;L;;;;;N;;;;; +11201;KHOJKI LETTER AA;Lo;0;L;;;;;N;;;;; +11202;KHOJKI LETTER I;Lo;0;L;;;;;N;;;;; +11203;KHOJKI LETTER U;Lo;0;L;;;;;N;;;;; +11204;KHOJKI LETTER E;Lo;0;L;;;;;N;;;;; +11205;KHOJKI LETTER AI;Lo;0;L;;;;;N;;;;; +11206;KHOJKI LETTER O;Lo;0;L;;;;;N;;;;; +11207;KHOJKI LETTER AU;Lo;0;L;;;;;N;;;;; +11208;KHOJKI LETTER KA;Lo;0;L;;;;;N;;;;; +11209;KHOJKI LETTER KHA;Lo;0;L;;;;;N;;;;; +1120A;KHOJKI LETTER GA;Lo;0;L;;;;;N;;;;; +1120B;KHOJKI LETTER GGA;Lo;0;L;;;;;N;;;;; +1120C;KHOJKI LETTER GHA;Lo;0;L;;;;;N;;;;; +1120D;KHOJKI LETTER NGA;Lo;0;L;;;;;N;;;;; +1120E;KHOJKI LETTER CA;Lo;0;L;;;;;N;;;;; +1120F;KHOJKI LETTER CHA;Lo;0;L;;;;;N;;;;; +11210;KHOJKI LETTER JA;Lo;0;L;;;;;N;;;;; +11211;KHOJKI LETTER JJA;Lo;0;L;;;;;N;;;;; +11213;KHOJKI LETTER NYA;Lo;0;L;;;;;N;;;;; +11214;KHOJKI LETTER TTA;Lo;0;L;;;;;N;;;;; +11215;KHOJKI LETTER TTHA;Lo;0;L;;;;;N;;;;; +11216;KHOJKI LETTER DDA;Lo;0;L;;;;;N;;;;; +11217;KHOJKI LETTER DDHA;Lo;0;L;;;;;N;;;;; +11218;KHOJKI LETTER NNA;Lo;0;L;;;;;N;;;;; +11219;KHOJKI LETTER TA;Lo;0;L;;;;;N;;;;; +1121A;KHOJKI LETTER THA;Lo;0;L;;;;;N;;;;; +1121B;KHOJKI LETTER DA;Lo;0;L;;;;;N;;;;; +1121C;KHOJKI LETTER DDDA;Lo;0;L;;;;;N;;;;; +1121D;KHOJKI LETTER DHA;Lo;0;L;;;;;N;;;;; +1121E;KHOJKI LETTER NA;Lo;0;L;;;;;N;;;;; +1121F;KHOJKI LETTER PA;Lo;0;L;;;;;N;;;;; +11220;KHOJKI LETTER PHA;Lo;0;L;;;;;N;;;;; +11221;KHOJKI LETTER BA;Lo;0;L;;;;;N;;;;; +11222;KHOJKI LETTER BBA;Lo;0;L;;;;;N;;;;; +11223;KHOJKI LETTER BHA;Lo;0;L;;;;;N;;;;; +11224;KHOJKI LETTER MA;Lo;0;L;;;;;N;;;;; +11225;KHOJKI LETTER YA;Lo;0;L;;;;;N;;;;; +11226;KHOJKI LETTER RA;Lo;0;L;;;;;N;;;;; +11227;KHOJKI LETTER LA;Lo;0;L;;;;;N;;;;; +11228;KHOJKI LETTER VA;Lo;0;L;;;;;N;;;;; +11229;KHOJKI LETTER SA;Lo;0;L;;;;;N;;;;; +1122A;KHOJKI LETTER HA;Lo;0;L;;;;;N;;;;; +1122B;KHOJKI LETTER LLA;Lo;0;L;;;;;N;;;;; +1122C;KHOJKI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +1122D;KHOJKI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +1122E;KHOJKI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +1122F;KHOJKI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +11230;KHOJKI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +11231;KHOJKI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +11232;KHOJKI VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +11233;KHOJKI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +11234;KHOJKI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +11235;KHOJKI SIGN VIRAMA;Mc;9;L;;;;;N;;;;; +11236;KHOJKI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +11237;KHOJKI SIGN SHADDA;Mn;0;NSM;;;;;N;;;;; +11238;KHOJKI DANDA;Po;0;L;;;;;N;;;;; +11239;KHOJKI DOUBLE DANDA;Po;0;L;;;;;N;;;;; +1123A;KHOJKI WORD SEPARATOR;Po;0;L;;;;;N;;;;; +1123B;KHOJKI SECTION MARK;Po;0;L;;;;;N;;;;; +1123C;KHOJKI DOUBLE SECTION MARK;Po;0;L;;;;;N;;;;; +1123D;KHOJKI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; +1123E;KHOJKI SIGN SUKUN;Mn;0;NSM;;;;;N;;;;; +11280;MULTANI LETTER A;Lo;0;L;;;;;N;;;;; +11281;MULTANI LETTER I;Lo;0;L;;;;;N;;;;; +11282;MULTANI LETTER U;Lo;0;L;;;;;N;;;;; +11283;MULTANI LETTER E;Lo;0;L;;;;;N;;;;; +11284;MULTANI LETTER KA;Lo;0;L;;;;;N;;;;; +11285;MULTANI LETTER KHA;Lo;0;L;;;;;N;;;;; +11286;MULTANI LETTER GA;Lo;0;L;;;;;N;;;;; +11288;MULTANI LETTER GHA;Lo;0;L;;;;;N;;;;; +1128A;MULTANI LETTER CA;Lo;0;L;;;;;N;;;;; +1128B;MULTANI LETTER CHA;Lo;0;L;;;;;N;;;;; +1128C;MULTANI LETTER JA;Lo;0;L;;;;;N;;;;; +1128D;MULTANI LETTER JJA;Lo;0;L;;;;;N;;;;; +1128F;MULTANI LETTER NYA;Lo;0;L;;;;;N;;;;; +11290;MULTANI LETTER TTA;Lo;0;L;;;;;N;;;;; +11291;MULTANI LETTER TTHA;Lo;0;L;;;;;N;;;;; +11292;MULTANI LETTER DDA;Lo;0;L;;;;;N;;;;; +11293;MULTANI LETTER DDDA;Lo;0;L;;;;;N;;;;; +11294;MULTANI LETTER DDHA;Lo;0;L;;;;;N;;;;; +11295;MULTANI LETTER NNA;Lo;0;L;;;;;N;;;;; +11296;MULTANI LETTER TA;Lo;0;L;;;;;N;;;;; +11297;MULTANI LETTER THA;Lo;0;L;;;;;N;;;;; +11298;MULTANI LETTER DA;Lo;0;L;;;;;N;;;;; +11299;MULTANI LETTER DHA;Lo;0;L;;;;;N;;;;; +1129A;MULTANI LETTER NA;Lo;0;L;;;;;N;;;;; +1129B;MULTANI LETTER PA;Lo;0;L;;;;;N;;;;; +1129C;MULTANI LETTER PHA;Lo;0;L;;;;;N;;;;; +1129D;MULTANI LETTER BA;Lo;0;L;;;;;N;;;;; +1129F;MULTANI LETTER BHA;Lo;0;L;;;;;N;;;;; +112A0;MULTANI LETTER MA;Lo;0;L;;;;;N;;;;; +112A1;MULTANI LETTER YA;Lo;0;L;;;;;N;;;;; +112A2;MULTANI LETTER RA;Lo;0;L;;;;;N;;;;; +112A3;MULTANI LETTER LA;Lo;0;L;;;;;N;;;;; +112A4;MULTANI LETTER VA;Lo;0;L;;;;;N;;;;; +112A5;MULTANI LETTER SA;Lo;0;L;;;;;N;;;;; +112A6;MULTANI LETTER HA;Lo;0;L;;;;;N;;;;; +112A7;MULTANI LETTER RRA;Lo;0;L;;;;;N;;;;; +112A8;MULTANI LETTER RHA;Lo;0;L;;;;;N;;;;; +112A9;MULTANI SECTION MARK;Po;0;L;;;;;N;;;;; +112B0;KHUDAWADI LETTER A;Lo;0;L;;;;;N;;;;; +112B1;KHUDAWADI LETTER AA;Lo;0;L;;;;;N;;;;; +112B2;KHUDAWADI LETTER I;Lo;0;L;;;;;N;;;;; +112B3;KHUDAWADI LETTER II;Lo;0;L;;;;;N;;;;; +112B4;KHUDAWADI LETTER U;Lo;0;L;;;;;N;;;;; +112B5;KHUDAWADI LETTER UU;Lo;0;L;;;;;N;;;;; +112B6;KHUDAWADI LETTER E;Lo;0;L;;;;;N;;;;; +112B7;KHUDAWADI LETTER AI;Lo;0;L;;;;;N;;;;; +112B8;KHUDAWADI LETTER O;Lo;0;L;;;;;N;;;;; +112B9;KHUDAWADI LETTER AU;Lo;0;L;;;;;N;;;;; +112BA;KHUDAWADI LETTER KA;Lo;0;L;;;;;N;;;;; +112BB;KHUDAWADI LETTER KHA;Lo;0;L;;;;;N;;;;; +112BC;KHUDAWADI LETTER GA;Lo;0;L;;;;;N;;;;; +112BD;KHUDAWADI LETTER GGA;Lo;0;L;;;;;N;;;;; +112BE;KHUDAWADI LETTER GHA;Lo;0;L;;;;;N;;;;; +112BF;KHUDAWADI LETTER NGA;Lo;0;L;;;;;N;;;;; +112C0;KHUDAWADI LETTER CA;Lo;0;L;;;;;N;;;;; +112C1;KHUDAWADI LETTER CHA;Lo;0;L;;;;;N;;;;; +112C2;KHUDAWADI LETTER JA;Lo;0;L;;;;;N;;;;; +112C3;KHUDAWADI LETTER JJA;Lo;0;L;;;;;N;;;;; +112C4;KHUDAWADI LETTER JHA;Lo;0;L;;;;;N;;;;; +112C5;KHUDAWADI LETTER NYA;Lo;0;L;;;;;N;;;;; +112C6;KHUDAWADI LETTER TTA;Lo;0;L;;;;;N;;;;; +112C7;KHUDAWADI LETTER TTHA;Lo;0;L;;;;;N;;;;; +112C8;KHUDAWADI LETTER DDA;Lo;0;L;;;;;N;;;;; +112C9;KHUDAWADI LETTER DDDA;Lo;0;L;;;;;N;;;;; +112CA;KHUDAWADI LETTER RRA;Lo;0;L;;;;;N;;;;; +112CB;KHUDAWADI LETTER DDHA;Lo;0;L;;;;;N;;;;; +112CC;KHUDAWADI LETTER NNA;Lo;0;L;;;;;N;;;;; +112CD;KHUDAWADI LETTER TA;Lo;0;L;;;;;N;;;;; +112CE;KHUDAWADI LETTER THA;Lo;0;L;;;;;N;;;;; +112CF;KHUDAWADI LETTER DA;Lo;0;L;;;;;N;;;;; +112D0;KHUDAWADI LETTER DHA;Lo;0;L;;;;;N;;;;; +112D1;KHUDAWADI LETTER NA;Lo;0;L;;;;;N;;;;; +112D2;KHUDAWADI LETTER PA;Lo;0;L;;;;;N;;;;; +112D3;KHUDAWADI LETTER PHA;Lo;0;L;;;;;N;;;;; +112D4;KHUDAWADI LETTER BA;Lo;0;L;;;;;N;;;;; +112D5;KHUDAWADI LETTER BBA;Lo;0;L;;;;;N;;;;; +112D6;KHUDAWADI LETTER BHA;Lo;0;L;;;;;N;;;;; +112D7;KHUDAWADI LETTER MA;Lo;0;L;;;;;N;;;;; +112D8;KHUDAWADI LETTER YA;Lo;0;L;;;;;N;;;;; +112D9;KHUDAWADI LETTER RA;Lo;0;L;;;;;N;;;;; +112DA;KHUDAWADI LETTER LA;Lo;0;L;;;;;N;;;;; +112DB;KHUDAWADI LETTER VA;Lo;0;L;;;;;N;;;;; +112DC;KHUDAWADI LETTER SHA;Lo;0;L;;;;;N;;;;; +112DD;KHUDAWADI LETTER SA;Lo;0;L;;;;;N;;;;; +112DE;KHUDAWADI LETTER HA;Lo;0;L;;;;;N;;;;; +112DF;KHUDAWADI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +112E0;KHUDAWADI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +112E1;KHUDAWADI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +112E2;KHUDAWADI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +112E3;KHUDAWADI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +112E4;KHUDAWADI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +112E5;KHUDAWADI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +112E6;KHUDAWADI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +112E7;KHUDAWADI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; +112E8;KHUDAWADI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; +112E9;KHUDAWADI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +112EA;KHUDAWADI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +112F0;KHUDAWADI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +112F1;KHUDAWADI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +112F2;KHUDAWADI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +112F3;KHUDAWADI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +112F4;KHUDAWADI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +112F5;KHUDAWADI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +112F6;KHUDAWADI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +112F7;KHUDAWADI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +112F8;KHUDAWADI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +112F9;KHUDAWADI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +11300;GRANTHA SIGN COMBINING ANUSVARA ABOVE;Mn;0;NSM;;;;;N;;;;; +11301;GRANTHA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +11302;GRANTHA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; +11303;GRANTHA SIGN VISARGA;Mc;0;L;;;;;N;;;;; +11305;GRANTHA LETTER A;Lo;0;L;;;;;N;;;;; +11306;GRANTHA LETTER AA;Lo;0;L;;;;;N;;;;; +11307;GRANTHA LETTER I;Lo;0;L;;;;;N;;;;; +11308;GRANTHA LETTER II;Lo;0;L;;;;;N;;;;; +11309;GRANTHA LETTER U;Lo;0;L;;;;;N;;;;; +1130A;GRANTHA LETTER UU;Lo;0;L;;;;;N;;;;; +1130B;GRANTHA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +1130C;GRANTHA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +1130F;GRANTHA LETTER EE;Lo;0;L;;;;;N;;;;; +11310;GRANTHA LETTER AI;Lo;0;L;;;;;N;;;;; +11313;GRANTHA LETTER OO;Lo;0;L;;;;;N;;;;; +11314;GRANTHA LETTER AU;Lo;0;L;;;;;N;;;;; +11315;GRANTHA LETTER KA;Lo;0;L;;;;;N;;;;; +11316;GRANTHA LETTER KHA;Lo;0;L;;;;;N;;;;; +11317;GRANTHA LETTER GA;Lo;0;L;;;;;N;;;;; +11318;GRANTHA LETTER GHA;Lo;0;L;;;;;N;;;;; +11319;GRANTHA LETTER NGA;Lo;0;L;;;;;N;;;;; +1131A;GRANTHA LETTER CA;Lo;0;L;;;;;N;;;;; +1131B;GRANTHA LETTER CHA;Lo;0;L;;;;;N;;;;; +1131C;GRANTHA LETTER JA;Lo;0;L;;;;;N;;;;; +1131D;GRANTHA LETTER JHA;Lo;0;L;;;;;N;;;;; +1131E;GRANTHA LETTER NYA;Lo;0;L;;;;;N;;;;; +1131F;GRANTHA LETTER TTA;Lo;0;L;;;;;N;;;;; +11320;GRANTHA LETTER TTHA;Lo;0;L;;;;;N;;;;; +11321;GRANTHA LETTER DDA;Lo;0;L;;;;;N;;;;; +11322;GRANTHA LETTER DDHA;Lo;0;L;;;;;N;;;;; +11323;GRANTHA LETTER NNA;Lo;0;L;;;;;N;;;;; +11324;GRANTHA LETTER TA;Lo;0;L;;;;;N;;;;; +11325;GRANTHA LETTER THA;Lo;0;L;;;;;N;;;;; +11326;GRANTHA LETTER DA;Lo;0;L;;;;;N;;;;; +11327;GRANTHA LETTER DHA;Lo;0;L;;;;;N;;;;; +11328;GRANTHA LETTER NA;Lo;0;L;;;;;N;;;;; +1132A;GRANTHA LETTER PA;Lo;0;L;;;;;N;;;;; +1132B;GRANTHA LETTER PHA;Lo;0;L;;;;;N;;;;; +1132C;GRANTHA LETTER BA;Lo;0;L;;;;;N;;;;; +1132D;GRANTHA LETTER BHA;Lo;0;L;;;;;N;;;;; +1132E;GRANTHA LETTER MA;Lo;0;L;;;;;N;;;;; +1132F;GRANTHA LETTER YA;Lo;0;L;;;;;N;;;;; +11330;GRANTHA LETTER RA;Lo;0;L;;;;;N;;;;; +11332;GRANTHA LETTER LA;Lo;0;L;;;;;N;;;;; +11333;GRANTHA LETTER LLA;Lo;0;L;;;;;N;;;;; +11335;GRANTHA LETTER VA;Lo;0;L;;;;;N;;;;; +11336;GRANTHA LETTER SHA;Lo;0;L;;;;;N;;;;; +11337;GRANTHA LETTER SSA;Lo;0;L;;;;;N;;;;; +11338;GRANTHA LETTER SA;Lo;0;L;;;;;N;;;;; +11339;GRANTHA LETTER HA;Lo;0;L;;;;;N;;;;; +1133C;GRANTHA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +1133D;GRANTHA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +1133E;GRANTHA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +1133F;GRANTHA VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +11340;GRANTHA VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +11341;GRANTHA VOWEL SIGN U;Mc;0;L;;;;;N;;;;; +11342;GRANTHA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; +11343;GRANTHA VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;; +11344;GRANTHA VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;; +11347;GRANTHA VOWEL SIGN EE;Mc;0;L;;;;;N;;;;; +11348;GRANTHA VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +1134B;GRANTHA VOWEL SIGN OO;Mc;0;L;11347 1133E;;;;N;;;;; +1134C;GRANTHA VOWEL SIGN AU;Mc;0;L;11347 11357;;;;N;;;;; +1134D;GRANTHA SIGN VIRAMA;Mc;9;L;;;;;N;;;;; +11350;GRANTHA OM;Lo;0;L;;;;;N;;;;; +11357;GRANTHA AU LENGTH MARK;Mc;0;L;;;;;N;;;;; +1135D;GRANTHA SIGN PLUTA;Lo;0;L;;;;;N;;;;; +1135E;GRANTHA LETTER VEDIC ANUSVARA;Lo;0;L;;;;;N;;;;; +1135F;GRANTHA LETTER VEDIC DOUBLE ANUSVARA;Lo;0;L;;;;;N;;;;; +11360;GRANTHA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +11361;GRANTHA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +11362;GRANTHA VOWEL SIGN VOCALIC L;Mc;0;L;;;;;N;;;;; +11363;GRANTHA VOWEL SIGN VOCALIC LL;Mc;0;L;;;;;N;;;;; +11366;COMBINING GRANTHA DIGIT ZERO;Mn;230;NSM;;;;;N;;;;; +11367;COMBINING GRANTHA DIGIT ONE;Mn;230;NSM;;;;;N;;;;; +11368;COMBINING GRANTHA DIGIT TWO;Mn;230;NSM;;;;;N;;;;; +11369;COMBINING GRANTHA DIGIT THREE;Mn;230;NSM;;;;;N;;;;; +1136A;COMBINING GRANTHA DIGIT FOUR;Mn;230;NSM;;;;;N;;;;; +1136B;COMBINING GRANTHA DIGIT FIVE;Mn;230;NSM;;;;;N;;;;; +1136C;COMBINING GRANTHA DIGIT SIX;Mn;230;NSM;;;;;N;;;;; +11370;COMBINING GRANTHA LETTER A;Mn;230;NSM;;;;;N;;;;; +11371;COMBINING GRANTHA LETTER KA;Mn;230;NSM;;;;;N;;;;; +11372;COMBINING GRANTHA LETTER NA;Mn;230;NSM;;;;;N;;;;; +11373;COMBINING GRANTHA LETTER VI;Mn;230;NSM;;;;;N;;;;; +11374;COMBINING GRANTHA LETTER PA;Mn;230;NSM;;;;;N;;;;; +11400;NEWA LETTER A;Lo;0;L;;;;;N;;;;; +11401;NEWA LETTER AA;Lo;0;L;;;;;N;;;;; +11402;NEWA LETTER I;Lo;0;L;;;;;N;;;;; +11403;NEWA LETTER II;Lo;0;L;;;;;N;;;;; +11404;NEWA LETTER U;Lo;0;L;;;;;N;;;;; +11405;NEWA LETTER UU;Lo;0;L;;;;;N;;;;; +11406;NEWA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +11407;NEWA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +11408;NEWA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +11409;NEWA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +1140A;NEWA LETTER E;Lo;0;L;;;;;N;;;;; +1140B;NEWA LETTER AI;Lo;0;L;;;;;N;;;;; +1140C;NEWA LETTER O;Lo;0;L;;;;;N;;;;; +1140D;NEWA LETTER AU;Lo;0;L;;;;;N;;;;; +1140E;NEWA LETTER KA;Lo;0;L;;;;;N;;;;; +1140F;NEWA LETTER KHA;Lo;0;L;;;;;N;;;;; +11410;NEWA LETTER GA;Lo;0;L;;;;;N;;;;; +11411;NEWA LETTER GHA;Lo;0;L;;;;;N;;;;; +11412;NEWA LETTER NGA;Lo;0;L;;;;;N;;;;; +11413;NEWA LETTER NGHA;Lo;0;L;;;;;N;;;;; +11414;NEWA LETTER CA;Lo;0;L;;;;;N;;;;; +11415;NEWA LETTER CHA;Lo;0;L;;;;;N;;;;; +11416;NEWA LETTER JA;Lo;0;L;;;;;N;;;;; +11417;NEWA LETTER JHA;Lo;0;L;;;;;N;;;;; +11418;NEWA LETTER NYA;Lo;0;L;;;;;N;;;;; +11419;NEWA LETTER NYHA;Lo;0;L;;;;;N;;;;; +1141A;NEWA LETTER TTA;Lo;0;L;;;;;N;;;;; +1141B;NEWA LETTER TTHA;Lo;0;L;;;;;N;;;;; +1141C;NEWA LETTER DDA;Lo;0;L;;;;;N;;;;; +1141D;NEWA LETTER DDHA;Lo;0;L;;;;;N;;;;; +1141E;NEWA LETTER NNA;Lo;0;L;;;;;N;;;;; +1141F;NEWA LETTER TA;Lo;0;L;;;;;N;;;;; +11420;NEWA LETTER THA;Lo;0;L;;;;;N;;;;; +11421;NEWA LETTER DA;Lo;0;L;;;;;N;;;;; +11422;NEWA LETTER DHA;Lo;0;L;;;;;N;;;;; +11423;NEWA LETTER NA;Lo;0;L;;;;;N;;;;; +11424;NEWA LETTER NHA;Lo;0;L;;;;;N;;;;; +11425;NEWA LETTER PA;Lo;0;L;;;;;N;;;;; +11426;NEWA LETTER PHA;Lo;0;L;;;;;N;;;;; +11427;NEWA LETTER BA;Lo;0;L;;;;;N;;;;; +11428;NEWA LETTER BHA;Lo;0;L;;;;;N;;;;; +11429;NEWA LETTER MA;Lo;0;L;;;;;N;;;;; +1142A;NEWA LETTER MHA;Lo;0;L;;;;;N;;;;; +1142B;NEWA LETTER YA;Lo;0;L;;;;;N;;;;; +1142C;NEWA LETTER RA;Lo;0;L;;;;;N;;;;; +1142D;NEWA LETTER RHA;Lo;0;L;;;;;N;;;;; +1142E;NEWA LETTER LA;Lo;0;L;;;;;N;;;;; +1142F;NEWA LETTER LHA;Lo;0;L;;;;;N;;;;; +11430;NEWA LETTER WA;Lo;0;L;;;;;N;;;;; +11431;NEWA LETTER SHA;Lo;0;L;;;;;N;;;;; +11432;NEWA LETTER SSA;Lo;0;L;;;;;N;;;;; +11433;NEWA LETTER SA;Lo;0;L;;;;;N;;;;; +11434;NEWA LETTER HA;Lo;0;L;;;;;N;;;;; +11435;NEWA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +11436;NEWA VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +11437;NEWA VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +11438;NEWA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +11439;NEWA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +1143A;NEWA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +1143B;NEWA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +1143C;NEWA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +1143D;NEWA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +1143E;NEWA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +1143F;NEWA VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +11440;NEWA VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +11441;NEWA VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +11442;NEWA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +11443;NEWA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +11444;NEWA SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +11445;NEWA SIGN VISARGA;Mc;0;L;;;;;N;;;;; +11446;NEWA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +11447;NEWA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +11448;NEWA SIGN FINAL ANUSVARA;Lo;0;L;;;;;N;;;;; +11449;NEWA OM;Lo;0;L;;;;;N;;;;; +1144A;NEWA SIDDHI;Lo;0;L;;;;;N;;;;; +1144B;NEWA DANDA;Po;0;L;;;;;N;;;;; +1144C;NEWA DOUBLE DANDA;Po;0;L;;;;;N;;;;; +1144D;NEWA COMMA;Po;0;L;;;;;N;;;;; +1144E;NEWA GAP FILLER;Po;0;L;;;;;N;;;;; +1144F;NEWA ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; +11450;NEWA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +11451;NEWA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +11452;NEWA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +11453;NEWA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +11454;NEWA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +11455;NEWA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +11456;NEWA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +11457;NEWA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +11458;NEWA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +11459;NEWA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1145B;NEWA PLACEHOLDER MARK;Po;0;L;;;;;N;;;;; +1145D;NEWA INSERTION SIGN;Po;0;L;;;;;N;;;;; +11480;TIRHUTA ANJI;Lo;0;L;;;;;N;;;;; +11481;TIRHUTA LETTER A;Lo;0;L;;;;;N;;;;; +11482;TIRHUTA LETTER AA;Lo;0;L;;;;;N;;;;; +11483;TIRHUTA LETTER I;Lo;0;L;;;;;N;;;;; +11484;TIRHUTA LETTER II;Lo;0;L;;;;;N;;;;; +11485;TIRHUTA LETTER U;Lo;0;L;;;;;N;;;;; +11486;TIRHUTA LETTER UU;Lo;0;L;;;;;N;;;;; +11487;TIRHUTA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +11488;TIRHUTA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +11489;TIRHUTA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +1148A;TIRHUTA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +1148B;TIRHUTA LETTER E;Lo;0;L;;;;;N;;;;; +1148C;TIRHUTA LETTER AI;Lo;0;L;;;;;N;;;;; +1148D;TIRHUTA LETTER O;Lo;0;L;;;;;N;;;;; +1148E;TIRHUTA LETTER AU;Lo;0;L;;;;;N;;;;; +1148F;TIRHUTA LETTER KA;Lo;0;L;;;;;N;;;;; +11490;TIRHUTA LETTER KHA;Lo;0;L;;;;;N;;;;; +11491;TIRHUTA LETTER GA;Lo;0;L;;;;;N;;;;; +11492;TIRHUTA LETTER GHA;Lo;0;L;;;;;N;;;;; +11493;TIRHUTA LETTER NGA;Lo;0;L;;;;;N;;;;; +11494;TIRHUTA LETTER CA;Lo;0;L;;;;;N;;;;; +11495;TIRHUTA LETTER CHA;Lo;0;L;;;;;N;;;;; +11496;TIRHUTA LETTER JA;Lo;0;L;;;;;N;;;;; +11497;TIRHUTA LETTER JHA;Lo;0;L;;;;;N;;;;; +11498;TIRHUTA LETTER NYA;Lo;0;L;;;;;N;;;;; +11499;TIRHUTA LETTER TTA;Lo;0;L;;;;;N;;;;; +1149A;TIRHUTA LETTER TTHA;Lo;0;L;;;;;N;;;;; +1149B;TIRHUTA LETTER DDA;Lo;0;L;;;;;N;;;;; +1149C;TIRHUTA LETTER DDHA;Lo;0;L;;;;;N;;;;; +1149D;TIRHUTA LETTER NNA;Lo;0;L;;;;;N;;;;; +1149E;TIRHUTA LETTER TA;Lo;0;L;;;;;N;;;;; +1149F;TIRHUTA LETTER THA;Lo;0;L;;;;;N;;;;; +114A0;TIRHUTA LETTER DA;Lo;0;L;;;;;N;;;;; +114A1;TIRHUTA LETTER DHA;Lo;0;L;;;;;N;;;;; +114A2;TIRHUTA LETTER NA;Lo;0;L;;;;;N;;;;; +114A3;TIRHUTA LETTER PA;Lo;0;L;;;;;N;;;;; +114A4;TIRHUTA LETTER PHA;Lo;0;L;;;;;N;;;;; +114A5;TIRHUTA LETTER BA;Lo;0;L;;;;;N;;;;; +114A6;TIRHUTA LETTER BHA;Lo;0;L;;;;;N;;;;; +114A7;TIRHUTA LETTER MA;Lo;0;L;;;;;N;;;;; +114A8;TIRHUTA LETTER YA;Lo;0;L;;;;;N;;;;; +114A9;TIRHUTA LETTER RA;Lo;0;L;;;;;N;;;;; +114AA;TIRHUTA LETTER LA;Lo;0;L;;;;;N;;;;; +114AB;TIRHUTA LETTER VA;Lo;0;L;;;;;N;;;;; +114AC;TIRHUTA LETTER SHA;Lo;0;L;;;;;N;;;;; +114AD;TIRHUTA LETTER SSA;Lo;0;L;;;;;N;;;;; +114AE;TIRHUTA LETTER SA;Lo;0;L;;;;;N;;;;; +114AF;TIRHUTA LETTER HA;Lo;0;L;;;;;N;;;;; +114B0;TIRHUTA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +114B1;TIRHUTA VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +114B2;TIRHUTA VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +114B3;TIRHUTA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +114B4;TIRHUTA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +114B5;TIRHUTA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +114B6;TIRHUTA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +114B7;TIRHUTA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +114B8;TIRHUTA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +114B9;TIRHUTA VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +114BA;TIRHUTA VOWEL SIGN SHORT E;Mn;0;NSM;;;;;N;;;;; +114BB;TIRHUTA VOWEL SIGN AI;Mc;0;L;114B9 114BA;;;;N;;;;; +114BC;TIRHUTA VOWEL SIGN O;Mc;0;L;114B9 114B0;;;;N;;;;; +114BD;TIRHUTA VOWEL SIGN SHORT O;Mc;0;L;;;;;N;;;;; +114BE;TIRHUTA VOWEL SIGN AU;Mc;0;L;114B9 114BD;;;;N;;;;; +114BF;TIRHUTA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +114C0;TIRHUTA SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +114C1;TIRHUTA SIGN VISARGA;Mc;0;L;;;;;N;;;;; +114C2;TIRHUTA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +114C3;TIRHUTA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +114C4;TIRHUTA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +114C5;TIRHUTA GVANG;Lo;0;L;;;;;N;;;;; +114C6;TIRHUTA ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; +114C7;TIRHUTA OM;Lo;0;L;;;;;N;;;;; +114D0;TIRHUTA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +114D1;TIRHUTA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +114D2;TIRHUTA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +114D3;TIRHUTA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +114D4;TIRHUTA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +114D5;TIRHUTA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +114D6;TIRHUTA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +114D7;TIRHUTA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +114D8;TIRHUTA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +114D9;TIRHUTA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +11580;SIDDHAM LETTER A;Lo;0;L;;;;;N;;;;; +11581;SIDDHAM LETTER AA;Lo;0;L;;;;;N;;;;; +11582;SIDDHAM LETTER I;Lo;0;L;;;;;N;;;;; +11583;SIDDHAM LETTER II;Lo;0;L;;;;;N;;;;; +11584;SIDDHAM LETTER U;Lo;0;L;;;;;N;;;;; +11585;SIDDHAM LETTER UU;Lo;0;L;;;;;N;;;;; +11586;SIDDHAM LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +11587;SIDDHAM LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +11588;SIDDHAM LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +11589;SIDDHAM LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +1158A;SIDDHAM LETTER E;Lo;0;L;;;;;N;;;;; +1158B;SIDDHAM LETTER AI;Lo;0;L;;;;;N;;;;; +1158C;SIDDHAM LETTER O;Lo;0;L;;;;;N;;;;; +1158D;SIDDHAM LETTER AU;Lo;0;L;;;;;N;;;;; +1158E;SIDDHAM LETTER KA;Lo;0;L;;;;;N;;;;; +1158F;SIDDHAM LETTER KHA;Lo;0;L;;;;;N;;;;; +11590;SIDDHAM LETTER GA;Lo;0;L;;;;;N;;;;; +11591;SIDDHAM LETTER GHA;Lo;0;L;;;;;N;;;;; +11592;SIDDHAM LETTER NGA;Lo;0;L;;;;;N;;;;; +11593;SIDDHAM LETTER CA;Lo;0;L;;;;;N;;;;; +11594;SIDDHAM LETTER CHA;Lo;0;L;;;;;N;;;;; +11595;SIDDHAM LETTER JA;Lo;0;L;;;;;N;;;;; +11596;SIDDHAM LETTER JHA;Lo;0;L;;;;;N;;;;; +11597;SIDDHAM LETTER NYA;Lo;0;L;;;;;N;;;;; +11598;SIDDHAM LETTER TTA;Lo;0;L;;;;;N;;;;; +11599;SIDDHAM LETTER TTHA;Lo;0;L;;;;;N;;;;; +1159A;SIDDHAM LETTER DDA;Lo;0;L;;;;;N;;;;; +1159B;SIDDHAM LETTER DDHA;Lo;0;L;;;;;N;;;;; +1159C;SIDDHAM LETTER NNA;Lo;0;L;;;;;N;;;;; +1159D;SIDDHAM LETTER TA;Lo;0;L;;;;;N;;;;; +1159E;SIDDHAM LETTER THA;Lo;0;L;;;;;N;;;;; +1159F;SIDDHAM LETTER DA;Lo;0;L;;;;;N;;;;; +115A0;SIDDHAM LETTER DHA;Lo;0;L;;;;;N;;;;; +115A1;SIDDHAM LETTER NA;Lo;0;L;;;;;N;;;;; +115A2;SIDDHAM LETTER PA;Lo;0;L;;;;;N;;;;; +115A3;SIDDHAM LETTER PHA;Lo;0;L;;;;;N;;;;; +115A4;SIDDHAM LETTER BA;Lo;0;L;;;;;N;;;;; +115A5;SIDDHAM LETTER BHA;Lo;0;L;;;;;N;;;;; +115A6;SIDDHAM LETTER MA;Lo;0;L;;;;;N;;;;; +115A7;SIDDHAM LETTER YA;Lo;0;L;;;;;N;;;;; +115A8;SIDDHAM LETTER RA;Lo;0;L;;;;;N;;;;; +115A9;SIDDHAM LETTER LA;Lo;0;L;;;;;N;;;;; +115AA;SIDDHAM LETTER VA;Lo;0;L;;;;;N;;;;; +115AB;SIDDHAM LETTER SHA;Lo;0;L;;;;;N;;;;; +115AC;SIDDHAM LETTER SSA;Lo;0;L;;;;;N;;;;; +115AD;SIDDHAM LETTER SA;Lo;0;L;;;;;N;;;;; +115AE;SIDDHAM LETTER HA;Lo;0;L;;;;;N;;;;; +115AF;SIDDHAM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +115B0;SIDDHAM VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +115B1;SIDDHAM VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +115B2;SIDDHAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +115B3;SIDDHAM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +115B4;SIDDHAM VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +115B5;SIDDHAM VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +115B8;SIDDHAM VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +115B9;SIDDHAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +115BA;SIDDHAM VOWEL SIGN O;Mc;0;L;115B8 115AF;;;;N;;;;; +115BB;SIDDHAM VOWEL SIGN AU;Mc;0;L;115B9 115AF;;;;N;;;;; +115BC;SIDDHAM SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +115BD;SIDDHAM SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +115BE;SIDDHAM SIGN VISARGA;Mc;0;L;;;;;N;;;;; +115BF;SIDDHAM SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +115C0;SIDDHAM SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +115C1;SIDDHAM SIGN SIDDHAM;Po;0;L;;;;;N;;;;; +115C2;SIDDHAM DANDA;Po;0;L;;;;;N;;;;; +115C3;SIDDHAM DOUBLE DANDA;Po;0;L;;;;;N;;;;; +115C4;SIDDHAM SEPARATOR DOT;Po;0;L;;;;;N;;;;; +115C5;SIDDHAM SEPARATOR BAR;Po;0;L;;;;;N;;;;; +115C6;SIDDHAM REPETITION MARK-1;Po;0;L;;;;;N;;;;; +115C7;SIDDHAM REPETITION MARK-2;Po;0;L;;;;;N;;;;; +115C8;SIDDHAM REPETITION MARK-3;Po;0;L;;;;;N;;;;; +115C9;SIDDHAM END OF TEXT MARK;Po;0;L;;;;;N;;;;; +115CA;SIDDHAM SECTION MARK WITH TRIDENT AND U-SHAPED ORNAMENTS;Po;0;L;;;;;N;;;;; +115CB;SIDDHAM SECTION MARK WITH TRIDENT AND DOTTED CRESCENTS;Po;0;L;;;;;N;;;;; +115CC;SIDDHAM SECTION MARK WITH RAYS AND DOTTED CRESCENTS;Po;0;L;;;;;N;;;;; +115CD;SIDDHAM SECTION MARK WITH RAYS AND DOTTED DOUBLE CRESCENTS;Po;0;L;;;;;N;;;;; +115CE;SIDDHAM SECTION MARK WITH RAYS AND DOTTED TRIPLE CRESCENTS;Po;0;L;;;;;N;;;;; +115CF;SIDDHAM SECTION MARK DOUBLE RING;Po;0;L;;;;;N;;;;; +115D0;SIDDHAM SECTION MARK DOUBLE RING WITH RAYS;Po;0;L;;;;;N;;;;; +115D1;SIDDHAM SECTION MARK WITH DOUBLE CRESCENTS;Po;0;L;;;;;N;;;;; +115D2;SIDDHAM SECTION MARK WITH TRIPLE CRESCENTS;Po;0;L;;;;;N;;;;; +115D3;SIDDHAM SECTION MARK WITH QUADRUPLE CRESCENTS;Po;0;L;;;;;N;;;;; +115D4;SIDDHAM SECTION MARK WITH SEPTUPLE CRESCENTS;Po;0;L;;;;;N;;;;; +115D5;SIDDHAM SECTION MARK WITH CIRCLES AND RAYS;Po;0;L;;;;;N;;;;; +115D6;SIDDHAM SECTION MARK WITH CIRCLES AND TWO ENCLOSURES;Po;0;L;;;;;N;;;;; +115D7;SIDDHAM SECTION MARK WITH CIRCLES AND FOUR ENCLOSURES;Po;0;L;;;;;N;;;;; +115D8;SIDDHAM LETTER THREE-CIRCLE ALTERNATE I;Lo;0;L;;;;;N;;;;; +115D9;SIDDHAM LETTER TWO-CIRCLE ALTERNATE I;Lo;0;L;;;;;N;;;;; +115DA;SIDDHAM LETTER TWO-CIRCLE ALTERNATE II;Lo;0;L;;;;;N;;;;; +115DB;SIDDHAM LETTER ALTERNATE U;Lo;0;L;;;;;N;;;;; +115DC;SIDDHAM VOWEL SIGN ALTERNATE U;Mn;0;NSM;;;;;N;;;;; +115DD;SIDDHAM VOWEL SIGN ALTERNATE UU;Mn;0;NSM;;;;;N;;;;; +11600;MODI LETTER A;Lo;0;L;;;;;N;;;;; +11601;MODI LETTER AA;Lo;0;L;;;;;N;;;;; +11602;MODI LETTER I;Lo;0;L;;;;;N;;;;; +11603;MODI LETTER II;Lo;0;L;;;;;N;;;;; +11604;MODI LETTER U;Lo;0;L;;;;;N;;;;; +11605;MODI LETTER UU;Lo;0;L;;;;;N;;;;; +11606;MODI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +11607;MODI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +11608;MODI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +11609;MODI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +1160A;MODI LETTER E;Lo;0;L;;;;;N;;;;; +1160B;MODI LETTER AI;Lo;0;L;;;;;N;;;;; +1160C;MODI LETTER O;Lo;0;L;;;;;N;;;;; +1160D;MODI LETTER AU;Lo;0;L;;;;;N;;;;; +1160E;MODI LETTER KA;Lo;0;L;;;;;N;;;;; +1160F;MODI LETTER KHA;Lo;0;L;;;;;N;;;;; +11610;MODI LETTER GA;Lo;0;L;;;;;N;;;;; +11611;MODI LETTER GHA;Lo;0;L;;;;;N;;;;; +11612;MODI LETTER NGA;Lo;0;L;;;;;N;;;;; +11613;MODI LETTER CA;Lo;0;L;;;;;N;;;;; +11614;MODI LETTER CHA;Lo;0;L;;;;;N;;;;; +11615;MODI LETTER JA;Lo;0;L;;;;;N;;;;; +11616;MODI LETTER JHA;Lo;0;L;;;;;N;;;;; +11617;MODI LETTER NYA;Lo;0;L;;;;;N;;;;; +11618;MODI LETTER TTA;Lo;0;L;;;;;N;;;;; +11619;MODI LETTER TTHA;Lo;0;L;;;;;N;;;;; +1161A;MODI LETTER DDA;Lo;0;L;;;;;N;;;;; +1161B;MODI LETTER DDHA;Lo;0;L;;;;;N;;;;; +1161C;MODI LETTER NNA;Lo;0;L;;;;;N;;;;; +1161D;MODI LETTER TA;Lo;0;L;;;;;N;;;;; +1161E;MODI LETTER THA;Lo;0;L;;;;;N;;;;; +1161F;MODI LETTER DA;Lo;0;L;;;;;N;;;;; +11620;MODI LETTER DHA;Lo;0;L;;;;;N;;;;; +11621;MODI LETTER NA;Lo;0;L;;;;;N;;;;; +11622;MODI LETTER PA;Lo;0;L;;;;;N;;;;; +11623;MODI LETTER PHA;Lo;0;L;;;;;N;;;;; +11624;MODI LETTER BA;Lo;0;L;;;;;N;;;;; +11625;MODI LETTER BHA;Lo;0;L;;;;;N;;;;; +11626;MODI LETTER MA;Lo;0;L;;;;;N;;;;; +11627;MODI LETTER YA;Lo;0;L;;;;;N;;;;; +11628;MODI LETTER RA;Lo;0;L;;;;;N;;;;; +11629;MODI LETTER LA;Lo;0;L;;;;;N;;;;; +1162A;MODI LETTER VA;Lo;0;L;;;;;N;;;;; +1162B;MODI LETTER SHA;Lo;0;L;;;;;N;;;;; +1162C;MODI LETTER SSA;Lo;0;L;;;;;N;;;;; +1162D;MODI LETTER SA;Lo;0;L;;;;;N;;;;; +1162E;MODI LETTER HA;Lo;0;L;;;;;N;;;;; +1162F;MODI LETTER LLA;Lo;0;L;;;;;N;;;;; +11630;MODI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +11631;MODI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +11632;MODI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +11633;MODI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +11634;MODI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +11635;MODI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +11636;MODI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +11637;MODI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +11638;MODI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +11639;MODI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +1163A;MODI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +1163B;MODI VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +1163C;MODI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +1163D;MODI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +1163E;MODI SIGN VISARGA;Mc;0;L;;;;;N;;;;; +1163F;MODI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +11640;MODI SIGN ARDHACANDRA;Mn;0;NSM;;;;;N;;;;; +11641;MODI DANDA;Po;0;L;;;;;N;;;;; +11642;MODI DOUBLE DANDA;Po;0;L;;;;;N;;;;; +11643;MODI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; +11644;MODI SIGN HUVA;Lo;0;L;;;;;N;;;;; +11650;MODI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +11651;MODI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +11652;MODI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +11653;MODI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +11654;MODI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +11655;MODI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +11656;MODI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +11657;MODI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +11658;MODI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +11659;MODI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +11660;MONGOLIAN BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;; +11661;MONGOLIAN ROTATED BIRGA;Po;0;ON;;;;;N;;;;; +11662;MONGOLIAN DOUBLE BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;; +11663;MONGOLIAN TRIPLE BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;; +11664;MONGOLIAN BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;; +11665;MONGOLIAN ROTATED BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;; +11666;MONGOLIAN ROTATED BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;; +11667;MONGOLIAN INVERTED BIRGA;Po;0;ON;;;;;N;;;;; +11668;MONGOLIAN INVERTED BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;; +11669;MONGOLIAN SWIRL BIRGA;Po;0;ON;;;;;N;;;;; +1166A;MONGOLIAN SWIRL BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;; +1166B;MONGOLIAN SWIRL BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;; +1166C;MONGOLIAN TURNED SWIRL BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;; +11680;TAKRI LETTER A;Lo;0;L;;;;;N;;;;; +11681;TAKRI LETTER AA;Lo;0;L;;;;;N;;;;; +11682;TAKRI LETTER I;Lo;0;L;;;;;N;;;;; +11683;TAKRI LETTER II;Lo;0;L;;;;;N;;;;; +11684;TAKRI LETTER U;Lo;0;L;;;;;N;;;;; +11685;TAKRI LETTER UU;Lo;0;L;;;;;N;;;;; +11686;TAKRI LETTER E;Lo;0;L;;;;;N;;;;; +11687;TAKRI LETTER AI;Lo;0;L;;;;;N;;;;; +11688;TAKRI LETTER O;Lo;0;L;;;;;N;;;;; +11689;TAKRI LETTER AU;Lo;0;L;;;;;N;;;;; +1168A;TAKRI LETTER KA;Lo;0;L;;;;;N;;;;; +1168B;TAKRI LETTER KHA;Lo;0;L;;;;;N;;;;; +1168C;TAKRI LETTER GA;Lo;0;L;;;;;N;;;;; +1168D;TAKRI LETTER GHA;Lo;0;L;;;;;N;;;;; +1168E;TAKRI LETTER NGA;Lo;0;L;;;;;N;;;;; +1168F;TAKRI LETTER CA;Lo;0;L;;;;;N;;;;; +11690;TAKRI LETTER CHA;Lo;0;L;;;;;N;;;;; +11691;TAKRI LETTER JA;Lo;0;L;;;;;N;;;;; +11692;TAKRI LETTER JHA;Lo;0;L;;;;;N;;;;; +11693;TAKRI LETTER NYA;Lo;0;L;;;;;N;;;;; +11694;TAKRI LETTER TTA;Lo;0;L;;;;;N;;;;; +11695;TAKRI LETTER TTHA;Lo;0;L;;;;;N;;;;; +11696;TAKRI LETTER DDA;Lo;0;L;;;;;N;;;;; +11697;TAKRI LETTER DDHA;Lo;0;L;;;;;N;;;;; +11698;TAKRI LETTER NNA;Lo;0;L;;;;;N;;;;; +11699;TAKRI LETTER TA;Lo;0;L;;;;;N;;;;; +1169A;TAKRI LETTER THA;Lo;0;L;;;;;N;;;;; +1169B;TAKRI LETTER DA;Lo;0;L;;;;;N;;;;; +1169C;TAKRI LETTER DHA;Lo;0;L;;;;;N;;;;; +1169D;TAKRI LETTER NA;Lo;0;L;;;;;N;;;;; +1169E;TAKRI LETTER PA;Lo;0;L;;;;;N;;;;; +1169F;TAKRI LETTER PHA;Lo;0;L;;;;;N;;;;; +116A0;TAKRI LETTER BA;Lo;0;L;;;;;N;;;;; +116A1;TAKRI LETTER BHA;Lo;0;L;;;;;N;;;;; +116A2;TAKRI LETTER MA;Lo;0;L;;;;;N;;;;; +116A3;TAKRI LETTER YA;Lo;0;L;;;;;N;;;;; +116A4;TAKRI LETTER RA;Lo;0;L;;;;;N;;;;; +116A5;TAKRI LETTER LA;Lo;0;L;;;;;N;;;;; +116A6;TAKRI LETTER VA;Lo;0;L;;;;;N;;;;; +116A7;TAKRI LETTER SHA;Lo;0;L;;;;;N;;;;; +116A8;TAKRI LETTER SA;Lo;0;L;;;;;N;;;;; +116A9;TAKRI LETTER HA;Lo;0;L;;;;;N;;;;; +116AA;TAKRI LETTER RRA;Lo;0;L;;;;;N;;;;; +116AB;TAKRI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +116AC;TAKRI SIGN VISARGA;Mc;0;L;;;;;N;;;;; +116AD;TAKRI VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;; +116AE;TAKRI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +116AF;TAKRI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +116B0;TAKRI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +116B1;TAKRI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +116B2;TAKRI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +116B3;TAKRI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +116B4;TAKRI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; +116B5;TAKRI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; +116B6;TAKRI SIGN VIRAMA;Mc;9;L;;;;;N;;;;; +116B7;TAKRI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +116C0;TAKRI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +116C1;TAKRI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +116C2;TAKRI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +116C3;TAKRI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +116C4;TAKRI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +116C5;TAKRI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +116C6;TAKRI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +116C7;TAKRI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +116C8;TAKRI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +116C9;TAKRI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +11700;AHOM LETTER KA;Lo;0;L;;;;;N;;;;; +11701;AHOM LETTER KHA;Lo;0;L;;;;;N;;;;; +11702;AHOM LETTER NGA;Lo;0;L;;;;;N;;;;; +11703;AHOM LETTER NA;Lo;0;L;;;;;N;;;;; +11704;AHOM LETTER TA;Lo;0;L;;;;;N;;;;; +11705;AHOM LETTER ALTERNATE TA;Lo;0;L;;;;;N;;;;; +11706;AHOM LETTER PA;Lo;0;L;;;;;N;;;;; +11707;AHOM LETTER PHA;Lo;0;L;;;;;N;;;;; +11708;AHOM LETTER BA;Lo;0;L;;;;;N;;;;; +11709;AHOM LETTER MA;Lo;0;L;;;;;N;;;;; +1170A;AHOM LETTER JA;Lo;0;L;;;;;N;;;;; +1170B;AHOM LETTER CHA;Lo;0;L;;;;;N;;;;; +1170C;AHOM LETTER THA;Lo;0;L;;;;;N;;;;; +1170D;AHOM LETTER RA;Lo;0;L;;;;;N;;;;; +1170E;AHOM LETTER LA;Lo;0;L;;;;;N;;;;; +1170F;AHOM LETTER SA;Lo;0;L;;;;;N;;;;; +11710;AHOM LETTER NYA;Lo;0;L;;;;;N;;;;; +11711;AHOM LETTER HA;Lo;0;L;;;;;N;;;;; +11712;AHOM LETTER A;Lo;0;L;;;;;N;;;;; +11713;AHOM LETTER DA;Lo;0;L;;;;;N;;;;; +11714;AHOM LETTER DHA;Lo;0;L;;;;;N;;;;; +11715;AHOM LETTER GA;Lo;0;L;;;;;N;;;;; +11716;AHOM LETTER ALTERNATE GA;Lo;0;L;;;;;N;;;;; +11717;AHOM LETTER GHA;Lo;0;L;;;;;N;;;;; +11718;AHOM LETTER BHA;Lo;0;L;;;;;N;;;;; +11719;AHOM LETTER JHA;Lo;0;L;;;;;N;;;;; +1171D;AHOM CONSONANT SIGN MEDIAL LA;Mn;0;NSM;;;;;N;;;;; +1171E;AHOM CONSONANT SIGN MEDIAL RA;Mn;0;NSM;;;;;N;;;;; +1171F;AHOM CONSONANT SIGN MEDIAL LIGATING RA;Mn;0;NSM;;;;;N;;;;; +11720;AHOM VOWEL SIGN A;Mc;0;L;;;;;N;;;;; +11721;AHOM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +11722;AHOM VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +11723;AHOM VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +11724;AHOM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +11725;AHOM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +11726;AHOM VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +11727;AHOM VOWEL SIGN AW;Mn;0;NSM;;;;;N;;;;; +11728;AHOM VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; +11729;AHOM VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +1172A;AHOM VOWEL SIGN AM;Mn;0;NSM;;;;;N;;;;; +1172B;AHOM SIGN KILLER;Mn;9;NSM;;;;;N;;;;; +11730;AHOM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +11731;AHOM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +11732;AHOM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +11733;AHOM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +11734;AHOM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +11735;AHOM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +11736;AHOM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +11737;AHOM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +11738;AHOM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +11739;AHOM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1173A;AHOM NUMBER TEN;No;0;L;;;;10;N;;;;; +1173B;AHOM NUMBER TWENTY;No;0;L;;;;20;N;;;;; +1173C;AHOM SIGN SMALL SECTION;Po;0;L;;;;;N;;;;; +1173D;AHOM SIGN SECTION;Po;0;L;;;;;N;;;;; +1173E;AHOM SIGN RULAI;Po;0;L;;;;;N;;;;; +1173F;AHOM SYMBOL VI;So;0;L;;;;;N;;;;; +118A0;WARANG CITI CAPITAL LETTER NGAA;Lu;0;L;;;;;N;;;;118C0; +118A1;WARANG CITI CAPITAL LETTER A;Lu;0;L;;;;;N;;;;118C1; +118A2;WARANG CITI CAPITAL LETTER WI;Lu;0;L;;;;;N;;;;118C2; +118A3;WARANG CITI CAPITAL LETTER YU;Lu;0;L;;;;;N;;;;118C3; +118A4;WARANG CITI CAPITAL LETTER YA;Lu;0;L;;;;;N;;;;118C4; +118A5;WARANG CITI CAPITAL LETTER YO;Lu;0;L;;;;;N;;;;118C5; +118A6;WARANG CITI CAPITAL LETTER II;Lu;0;L;;;;;N;;;;118C6; +118A7;WARANG CITI CAPITAL LETTER UU;Lu;0;L;;;;;N;;;;118C7; +118A8;WARANG CITI CAPITAL LETTER E;Lu;0;L;;;;;N;;;;118C8; +118A9;WARANG CITI CAPITAL LETTER O;Lu;0;L;;;;;N;;;;118C9; +118AA;WARANG CITI CAPITAL LETTER ANG;Lu;0;L;;;;;N;;;;118CA; +118AB;WARANG CITI CAPITAL LETTER GA;Lu;0;L;;;;;N;;;;118CB; +118AC;WARANG CITI CAPITAL LETTER KO;Lu;0;L;;;;;N;;;;118CC; +118AD;WARANG CITI CAPITAL LETTER ENY;Lu;0;L;;;;;N;;;;118CD; +118AE;WARANG CITI CAPITAL LETTER YUJ;Lu;0;L;;;;;N;;;;118CE; +118AF;WARANG CITI CAPITAL LETTER UC;Lu;0;L;;;;;N;;;;118CF; +118B0;WARANG CITI CAPITAL LETTER ENN;Lu;0;L;;;;;N;;;;118D0; +118B1;WARANG CITI CAPITAL LETTER ODD;Lu;0;L;;;;;N;;;;118D1; +118B2;WARANG CITI CAPITAL LETTER TTE;Lu;0;L;;;;;N;;;;118D2; +118B3;WARANG CITI CAPITAL LETTER NUNG;Lu;0;L;;;;;N;;;;118D3; +118B4;WARANG CITI CAPITAL LETTER DA;Lu;0;L;;;;;N;;;;118D4; +118B5;WARANG CITI CAPITAL LETTER AT;Lu;0;L;;;;;N;;;;118D5; +118B6;WARANG CITI CAPITAL LETTER AM;Lu;0;L;;;;;N;;;;118D6; +118B7;WARANG CITI CAPITAL LETTER BU;Lu;0;L;;;;;N;;;;118D7; +118B8;WARANG CITI CAPITAL LETTER PU;Lu;0;L;;;;;N;;;;118D8; +118B9;WARANG CITI CAPITAL LETTER HIYO;Lu;0;L;;;;;N;;;;118D9; +118BA;WARANG CITI CAPITAL LETTER HOLO;Lu;0;L;;;;;N;;;;118DA; +118BB;WARANG CITI CAPITAL LETTER HORR;Lu;0;L;;;;;N;;;;118DB; +118BC;WARANG CITI CAPITAL LETTER HAR;Lu;0;L;;;;;N;;;;118DC; +118BD;WARANG CITI CAPITAL LETTER SSUU;Lu;0;L;;;;;N;;;;118DD; +118BE;WARANG CITI CAPITAL LETTER SII;Lu;0;L;;;;;N;;;;118DE; +118BF;WARANG CITI CAPITAL LETTER VIYO;Lu;0;L;;;;;N;;;;118DF; +118C0;WARANG CITI SMALL LETTER NGAA;Ll;0;L;;;;;N;;;118A0;;118A0 +118C1;WARANG CITI SMALL LETTER A;Ll;0;L;;;;;N;;;118A1;;118A1 +118C2;WARANG CITI SMALL LETTER WI;Ll;0;L;;;;;N;;;118A2;;118A2 +118C3;WARANG CITI SMALL LETTER YU;Ll;0;L;;;;;N;;;118A3;;118A3 +118C4;WARANG CITI SMALL LETTER YA;Ll;0;L;;;;;N;;;118A4;;118A4 +118C5;WARANG CITI SMALL LETTER YO;Ll;0;L;;;;;N;;;118A5;;118A5 +118C6;WARANG CITI SMALL LETTER II;Ll;0;L;;;;;N;;;118A6;;118A6 +118C7;WARANG CITI SMALL LETTER UU;Ll;0;L;;;;;N;;;118A7;;118A7 +118C8;WARANG CITI SMALL LETTER E;Ll;0;L;;;;;N;;;118A8;;118A8 +118C9;WARANG CITI SMALL LETTER O;Ll;0;L;;;;;N;;;118A9;;118A9 +118CA;WARANG CITI SMALL LETTER ANG;Ll;0;L;;;;;N;;;118AA;;118AA +118CB;WARANG CITI SMALL LETTER GA;Ll;0;L;;;;;N;;;118AB;;118AB +118CC;WARANG CITI SMALL LETTER KO;Ll;0;L;;;;;N;;;118AC;;118AC +118CD;WARANG CITI SMALL LETTER ENY;Ll;0;L;;;;;N;;;118AD;;118AD +118CE;WARANG CITI SMALL LETTER YUJ;Ll;0;L;;;;;N;;;118AE;;118AE +118CF;WARANG CITI SMALL LETTER UC;Ll;0;L;;;;;N;;;118AF;;118AF +118D0;WARANG CITI SMALL LETTER ENN;Ll;0;L;;;;;N;;;118B0;;118B0 +118D1;WARANG CITI SMALL LETTER ODD;Ll;0;L;;;;;N;;;118B1;;118B1 +118D2;WARANG CITI SMALL LETTER TTE;Ll;0;L;;;;;N;;;118B2;;118B2 +118D3;WARANG CITI SMALL LETTER NUNG;Ll;0;L;;;;;N;;;118B3;;118B3 +118D4;WARANG CITI SMALL LETTER DA;Ll;0;L;;;;;N;;;118B4;;118B4 +118D5;WARANG CITI SMALL LETTER AT;Ll;0;L;;;;;N;;;118B5;;118B5 +118D6;WARANG CITI SMALL LETTER AM;Ll;0;L;;;;;N;;;118B6;;118B6 +118D7;WARANG CITI SMALL LETTER BU;Ll;0;L;;;;;N;;;118B7;;118B7 +118D8;WARANG CITI SMALL LETTER PU;Ll;0;L;;;;;N;;;118B8;;118B8 +118D9;WARANG CITI SMALL LETTER HIYO;Ll;0;L;;;;;N;;;118B9;;118B9 +118DA;WARANG CITI SMALL LETTER HOLO;Ll;0;L;;;;;N;;;118BA;;118BA +118DB;WARANG CITI SMALL LETTER HORR;Ll;0;L;;;;;N;;;118BB;;118BB +118DC;WARANG CITI SMALL LETTER HAR;Ll;0;L;;;;;N;;;118BC;;118BC +118DD;WARANG CITI SMALL LETTER SSUU;Ll;0;L;;;;;N;;;118BD;;118BD +118DE;WARANG CITI SMALL LETTER SII;Ll;0;L;;;;;N;;;118BE;;118BE +118DF;WARANG CITI SMALL LETTER VIYO;Ll;0;L;;;;;N;;;118BF;;118BF +118E0;WARANG CITI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +118E1;WARANG CITI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +118E2;WARANG CITI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +118E3;WARANG CITI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +118E4;WARANG CITI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +118E5;WARANG CITI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +118E6;WARANG CITI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +118E7;WARANG CITI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +118E8;WARANG CITI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +118E9;WARANG CITI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +118EA;WARANG CITI NUMBER TEN;No;0;L;;;;10;N;;;;; +118EB;WARANG CITI NUMBER TWENTY;No;0;L;;;;20;N;;;;; +118EC;WARANG CITI NUMBER THIRTY;No;0;L;;;;30;N;;;;; +118ED;WARANG CITI NUMBER FORTY;No;0;L;;;;40;N;;;;; +118EE;WARANG CITI NUMBER FIFTY;No;0;L;;;;50;N;;;;; +118EF;WARANG CITI NUMBER SIXTY;No;0;L;;;;60;N;;;;; +118F0;WARANG CITI NUMBER SEVENTY;No;0;L;;;;70;N;;;;; +118F1;WARANG CITI NUMBER EIGHTY;No;0;L;;;;80;N;;;;; +118F2;WARANG CITI NUMBER NINETY;No;0;L;;;;90;N;;;;; +118FF;WARANG CITI OM;Lo;0;L;;;;;N;;;;; +11AC0;PAU CIN HAU LETTER PA;Lo;0;L;;;;;N;;;;; +11AC1;PAU CIN HAU LETTER KA;Lo;0;L;;;;;N;;;;; +11AC2;PAU CIN HAU LETTER LA;Lo;0;L;;;;;N;;;;; +11AC3;PAU CIN HAU LETTER MA;Lo;0;L;;;;;N;;;;; +11AC4;PAU CIN HAU LETTER DA;Lo;0;L;;;;;N;;;;; +11AC5;PAU CIN HAU LETTER ZA;Lo;0;L;;;;;N;;;;; +11AC6;PAU CIN HAU LETTER VA;Lo;0;L;;;;;N;;;;; +11AC7;PAU CIN HAU LETTER NGA;Lo;0;L;;;;;N;;;;; +11AC8;PAU CIN HAU LETTER HA;Lo;0;L;;;;;N;;;;; +11AC9;PAU CIN HAU LETTER GA;Lo;0;L;;;;;N;;;;; +11ACA;PAU CIN HAU LETTER KHA;Lo;0;L;;;;;N;;;;; +11ACB;PAU CIN HAU LETTER SA;Lo;0;L;;;;;N;;;;; +11ACC;PAU CIN HAU LETTER BA;Lo;0;L;;;;;N;;;;; +11ACD;PAU CIN HAU LETTER CA;Lo;0;L;;;;;N;;;;; +11ACE;PAU CIN HAU LETTER TA;Lo;0;L;;;;;N;;;;; +11ACF;PAU CIN HAU LETTER THA;Lo;0;L;;;;;N;;;;; +11AD0;PAU CIN HAU LETTER NA;Lo;0;L;;;;;N;;;;; +11AD1;PAU CIN HAU LETTER PHA;Lo;0;L;;;;;N;;;;; +11AD2;PAU CIN HAU LETTER RA;Lo;0;L;;;;;N;;;;; +11AD3;PAU CIN HAU LETTER FA;Lo;0;L;;;;;N;;;;; +11AD4;PAU CIN HAU LETTER CHA;Lo;0;L;;;;;N;;;;; +11AD5;PAU CIN HAU LETTER A;Lo;0;L;;;;;N;;;;; +11AD6;PAU CIN HAU LETTER E;Lo;0;L;;;;;N;;;;; +11AD7;PAU CIN HAU LETTER I;Lo;0;L;;;;;N;;;;; +11AD8;PAU CIN HAU LETTER O;Lo;0;L;;;;;N;;;;; +11AD9;PAU CIN HAU LETTER U;Lo;0;L;;;;;N;;;;; +11ADA;PAU CIN HAU LETTER UA;Lo;0;L;;;;;N;;;;; +11ADB;PAU CIN HAU LETTER IA;Lo;0;L;;;;;N;;;;; +11ADC;PAU CIN HAU LETTER FINAL P;Lo;0;L;;;;;N;;;;; +11ADD;PAU CIN HAU LETTER FINAL K;Lo;0;L;;;;;N;;;;; +11ADE;PAU CIN HAU LETTER FINAL T;Lo;0;L;;;;;N;;;;; +11ADF;PAU CIN HAU LETTER FINAL M;Lo;0;L;;;;;N;;;;; +11AE0;PAU CIN HAU LETTER FINAL N;Lo;0;L;;;;;N;;;;; +11AE1;PAU CIN HAU LETTER FINAL L;Lo;0;L;;;;;N;;;;; +11AE2;PAU CIN HAU LETTER FINAL W;Lo;0;L;;;;;N;;;;; +11AE3;PAU CIN HAU LETTER FINAL NG;Lo;0;L;;;;;N;;;;; +11AE4;PAU CIN HAU LETTER FINAL Y;Lo;0;L;;;;;N;;;;; +11AE5;PAU CIN HAU RISING TONE LONG;Lo;0;L;;;;;N;;;;; +11AE6;PAU CIN HAU RISING TONE;Lo;0;L;;;;;N;;;;; +11AE7;PAU CIN HAU SANDHI GLOTTAL STOP;Lo;0;L;;;;;N;;;;; +11AE8;PAU CIN HAU RISING TONE LONG FINAL;Lo;0;L;;;;;N;;;;; +11AE9;PAU CIN HAU RISING TONE FINAL;Lo;0;L;;;;;N;;;;; +11AEA;PAU CIN HAU SANDHI GLOTTAL STOP FINAL;Lo;0;L;;;;;N;;;;; +11AEB;PAU CIN HAU SANDHI TONE LONG;Lo;0;L;;;;;N;;;;; +11AEC;PAU CIN HAU SANDHI TONE;Lo;0;L;;;;;N;;;;; +11AED;PAU CIN HAU SANDHI TONE LONG FINAL;Lo;0;L;;;;;N;;;;; +11AEE;PAU CIN HAU SANDHI TONE FINAL;Lo;0;L;;;;;N;;;;; +11AEF;PAU CIN HAU MID-LEVEL TONE;Lo;0;L;;;;;N;;;;; +11AF0;PAU CIN HAU GLOTTAL STOP VARIANT;Lo;0;L;;;;;N;;;;; +11AF1;PAU CIN HAU MID-LEVEL TONE LONG FINAL;Lo;0;L;;;;;N;;;;; +11AF2;PAU CIN HAU MID-LEVEL TONE FINAL;Lo;0;L;;;;;N;;;;; +11AF3;PAU CIN HAU LOW-FALLING TONE LONG;Lo;0;L;;;;;N;;;;; +11AF4;PAU CIN HAU LOW-FALLING TONE;Lo;0;L;;;;;N;;;;; +11AF5;PAU CIN HAU GLOTTAL STOP;Lo;0;L;;;;;N;;;;; +11AF6;PAU CIN HAU LOW-FALLING TONE LONG FINAL;Lo;0;L;;;;;N;;;;; +11AF7;PAU CIN HAU LOW-FALLING TONE FINAL;Lo;0;L;;;;;N;;;;; +11AF8;PAU CIN HAU GLOTTAL STOP FINAL;Lo;0;L;;;;;N;;;;; +11C00;BHAIKSUKI LETTER A;Lo;0;L;;;;;N;;;;; +11C01;BHAIKSUKI LETTER AA;Lo;0;L;;;;;N;;;;; +11C02;BHAIKSUKI LETTER I;Lo;0;L;;;;;N;;;;; +11C03;BHAIKSUKI LETTER II;Lo;0;L;;;;;N;;;;; +11C04;BHAIKSUKI LETTER U;Lo;0;L;;;;;N;;;;; +11C05;BHAIKSUKI LETTER UU;Lo;0;L;;;;;N;;;;; +11C06;BHAIKSUKI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +11C07;BHAIKSUKI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +11C08;BHAIKSUKI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +11C0A;BHAIKSUKI LETTER E;Lo;0;L;;;;;N;;;;; +11C0B;BHAIKSUKI LETTER AI;Lo;0;L;;;;;N;;;;; +11C0C;BHAIKSUKI LETTER O;Lo;0;L;;;;;N;;;;; +11C0D;BHAIKSUKI LETTER AU;Lo;0;L;;;;;N;;;;; +11C0E;BHAIKSUKI LETTER KA;Lo;0;L;;;;;N;;;;; +11C0F;BHAIKSUKI LETTER KHA;Lo;0;L;;;;;N;;;;; +11C10;BHAIKSUKI LETTER GA;Lo;0;L;;;;;N;;;;; +11C11;BHAIKSUKI LETTER GHA;Lo;0;L;;;;;N;;;;; +11C12;BHAIKSUKI LETTER NGA;Lo;0;L;;;;;N;;;;; +11C13;BHAIKSUKI LETTER CA;Lo;0;L;;;;;N;;;;; +11C14;BHAIKSUKI LETTER CHA;Lo;0;L;;;;;N;;;;; +11C15;BHAIKSUKI LETTER JA;Lo;0;L;;;;;N;;;;; +11C16;BHAIKSUKI LETTER JHA;Lo;0;L;;;;;N;;;;; +11C17;BHAIKSUKI LETTER NYA;Lo;0;L;;;;;N;;;;; +11C18;BHAIKSUKI LETTER TTA;Lo;0;L;;;;;N;;;;; +11C19;BHAIKSUKI LETTER TTHA;Lo;0;L;;;;;N;;;;; +11C1A;BHAIKSUKI LETTER DDA;Lo;0;L;;;;;N;;;;; +11C1B;BHAIKSUKI LETTER DDHA;Lo;0;L;;;;;N;;;;; +11C1C;BHAIKSUKI LETTER NNA;Lo;0;L;;;;;N;;;;; +11C1D;BHAIKSUKI LETTER TA;Lo;0;L;;;;;N;;;;; +11C1E;BHAIKSUKI LETTER THA;Lo;0;L;;;;;N;;;;; +11C1F;BHAIKSUKI LETTER DA;Lo;0;L;;;;;N;;;;; +11C20;BHAIKSUKI LETTER DHA;Lo;0;L;;;;;N;;;;; +11C21;BHAIKSUKI LETTER NA;Lo;0;L;;;;;N;;;;; +11C22;BHAIKSUKI LETTER PA;Lo;0;L;;;;;N;;;;; +11C23;BHAIKSUKI LETTER PHA;Lo;0;L;;;;;N;;;;; +11C24;BHAIKSUKI LETTER BA;Lo;0;L;;;;;N;;;;; +11C25;BHAIKSUKI LETTER BHA;Lo;0;L;;;;;N;;;;; +11C26;BHAIKSUKI LETTER MA;Lo;0;L;;;;;N;;;;; +11C27;BHAIKSUKI LETTER YA;Lo;0;L;;;;;N;;;;; +11C28;BHAIKSUKI LETTER RA;Lo;0;L;;;;;N;;;;; +11C29;BHAIKSUKI LETTER LA;Lo;0;L;;;;;N;;;;; +11C2A;BHAIKSUKI LETTER VA;Lo;0;L;;;;;N;;;;; +11C2B;BHAIKSUKI LETTER SHA;Lo;0;L;;;;;N;;;;; +11C2C;BHAIKSUKI LETTER SSA;Lo;0;L;;;;;N;;;;; +11C2D;BHAIKSUKI LETTER SA;Lo;0;L;;;;;N;;;;; +11C2E;BHAIKSUKI LETTER HA;Lo;0;L;;;;;N;;;;; +11C2F;BHAIKSUKI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +11C30;BHAIKSUKI VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +11C31;BHAIKSUKI VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +11C32;BHAIKSUKI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +11C33;BHAIKSUKI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +11C34;BHAIKSUKI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +11C35;BHAIKSUKI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +11C36;BHAIKSUKI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +11C38;BHAIKSUKI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +11C39;BHAIKSUKI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +11C3A;BHAIKSUKI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; +11C3B;BHAIKSUKI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; +11C3C;BHAIKSUKI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +11C3D;BHAIKSUKI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +11C3E;BHAIKSUKI SIGN VISARGA;Mc;0;L;;;;;N;;;;; +11C3F;BHAIKSUKI SIGN VIRAMA;Mn;9;L;;;;;N;;;;; +11C40;BHAIKSUKI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +11C41;BHAIKSUKI DANDA;Po;0;L;;;;;N;;;;; +11C42;BHAIKSUKI DOUBLE DANDA;Po;0;L;;;;;N;;;;; +11C43;BHAIKSUKI WORD SEPARATOR;Po;0;L;;;;;N;;;;; +11C44;BHAIKSUKI GAP FILLER-1;Po;0;L;;;;;N;;;;; +11C45;BHAIKSUKI GAP FILLER-2;Po;0;L;;;;;N;;;;; +11C50;BHAIKSUKI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +11C51;BHAIKSUKI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +11C52;BHAIKSUKI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +11C53;BHAIKSUKI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +11C54;BHAIKSUKI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +11C55;BHAIKSUKI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +11C56;BHAIKSUKI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +11C57;BHAIKSUKI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +11C58;BHAIKSUKI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +11C59;BHAIKSUKI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +11C5A;BHAIKSUKI NUMBER ONE;No;0;L;;;;1;N;;;;; +11C5B;BHAIKSUKI NUMBER TWO;No;0;L;;;;2;N;;;;; +11C5C;BHAIKSUKI NUMBER THREE;No;0;L;;;;3;N;;;;; +11C5D;BHAIKSUKI NUMBER FOUR;No;0;L;;;;4;N;;;;; +11C5E;BHAIKSUKI NUMBER FIVE;No;0;L;;;;5;N;;;;; +11C5F;BHAIKSUKI NUMBER SIX;No;0;L;;;;6;N;;;;; +11C60;BHAIKSUKI NUMBER SEVEN;No;0;L;;;;7;N;;;;; +11C61;BHAIKSUKI NUMBER EIGHT;No;0;L;;;;8;N;;;;; +11C62;BHAIKSUKI NUMBER NINE;No;0;L;;;;9;N;;;;; +11C63;BHAIKSUKI NUMBER TEN;No;0;L;;;;10;N;;;;; +11C64;BHAIKSUKI NUMBER TWENTY;No;0;L;;;;20;N;;;;; +11C65;BHAIKSUKI NUMBER THIRTY;No;0;L;;;;30;N;;;;; +11C66;BHAIKSUKI NUMBER FORTY;No;0;L;;;;40;N;;;;; +11C67;BHAIKSUKI NUMBER FIFTY;No;0;L;;;;50;N;;;;; +11C68;BHAIKSUKI NUMBER SIXTY;No;0;L;;;;60;N;;;;; +11C69;BHAIKSUKI NUMBER SEVENTY;No;0;L;;;;70;N;;;;; +11C6A;BHAIKSUKI NUMBER EIGHTY;No;0;L;;;;80;N;;;;; +11C6B;BHAIKSUKI NUMBER NINETY;No;0;L;;;;90;N;;;;; +11C6C;BHAIKSUKI HUNDREDS UNIT MARK;No;0;L;;;;100;N;;;;; +11C70;MARCHEN HEAD MARK;Po;0;L;;;;;N;;;;; +11C71;MARCHEN MARK SHAD;Po;0;L;;;;;N;;;;; +11C72;MARCHEN LETTER KA;Lo;0;L;;;;;N;;;;; +11C73;MARCHEN LETTER KHA;Lo;0;L;;;;;N;;;;; +11C74;MARCHEN LETTER GA;Lo;0;L;;;;;N;;;;; +11C75;MARCHEN LETTER NGA;Lo;0;L;;;;;N;;;;; +11C76;MARCHEN LETTER CA;Lo;0;L;;;;;N;;;;; +11C77;MARCHEN LETTER CHA;Lo;0;L;;;;;N;;;;; +11C78;MARCHEN LETTER JA;Lo;0;L;;;;;N;;;;; +11C79;MARCHEN LETTER NYA;Lo;0;L;;;;;N;;;;; +11C7A;MARCHEN LETTER TA;Lo;0;L;;;;;N;;;;; +11C7B;MARCHEN LETTER THA;Lo;0;L;;;;;N;;;;; +11C7C;MARCHEN LETTER DA;Lo;0;L;;;;;N;;;;; +11C7D;MARCHEN LETTER NA;Lo;0;L;;;;;N;;;;; +11C7E;MARCHEN LETTER PA;Lo;0;L;;;;;N;;;;; +11C7F;MARCHEN LETTER PHA;Lo;0;L;;;;;N;;;;; +11C80;MARCHEN LETTER BA;Lo;0;L;;;;;N;;;;; +11C81;MARCHEN LETTER MA;Lo;0;L;;;;;N;;;;; +11C82;MARCHEN LETTER TSA;Lo;0;L;;;;;N;;;;; +11C83;MARCHEN LETTER TSHA;Lo;0;L;;;;;N;;;;; +11C84;MARCHEN LETTER DZA;Lo;0;L;;;;;N;;;;; +11C85;MARCHEN LETTER WA;Lo;0;L;;;;;N;;;;; +11C86;MARCHEN LETTER ZHA;Lo;0;L;;;;;N;;;;; +11C87;MARCHEN LETTER ZA;Lo;0;L;;;;;N;;;;; +11C88;MARCHEN LETTER -A;Lo;0;L;;;;;N;;;;; +11C89;MARCHEN LETTER YA;Lo;0;L;;;;;N;;;;; +11C8A;MARCHEN LETTER RA;Lo;0;L;;;;;N;;;;; +11C8B;MARCHEN LETTER LA;Lo;0;L;;;;;N;;;;; +11C8C;MARCHEN LETTER SHA;Lo;0;L;;;;;N;;;;; +11C8D;MARCHEN LETTER SA;Lo;0;L;;;;;N;;;;; +11C8E;MARCHEN LETTER HA;Lo;0;L;;;;;N;;;;; +11C8F;MARCHEN LETTER A;Lo;0;L;;;;;N;;;;; +11C92;MARCHEN SUBJOINED LETTER KA;Mn;0;NSM;;;;;N;;;;; +11C93;MARCHEN SUBJOINED LETTER KHA;Mn;0;NSM;;;;;N;;;;; +11C94;MARCHEN SUBJOINED LETTER GA;Mn;0;NSM;;;;;N;;;;; +11C95;MARCHEN SUBJOINED LETTER NGA;Mn;0;NSM;;;;;N;;;;; +11C96;MARCHEN SUBJOINED LETTER CA;Mn;0;NSM;;;;;N;;;;; +11C97;MARCHEN SUBJOINED LETTER CHA;Mn;0;NSM;;;;;N;;;;; +11C98;MARCHEN SUBJOINED LETTER JA;Mn;0;NSM;;;;;N;;;;; +11C99;MARCHEN SUBJOINED LETTER NYA;Mn;0;NSM;;;;;N;;;;; +11C9A;MARCHEN SUBJOINED LETTER TA;Mn;0;NSM;;;;;N;;;;; +11C9B;MARCHEN SUBJOINED LETTER THA;Mn;0;NSM;;;;;N;;;;; +11C9C;MARCHEN SUBJOINED LETTER DA;Mn;0;NSM;;;;;N;;;;; +11C9D;MARCHEN SUBJOINED LETTER NA;Mn;0;NSM;;;;;N;;;;; +11C9E;MARCHEN SUBJOINED LETTER PA;Mn;0;NSM;;;;;N;;;;; +11C9F;MARCHEN SUBJOINED LETTER PHA;Mn;0;NSM;;;;;N;;;;; +11CA0;MARCHEN SUBJOINED LETTER BA;Mn;0;NSM;;;;;N;;;;; +11CA1;MARCHEN SUBJOINED LETTER MA;Mn;0;NSM;;;;;N;;;;; +11CA2;MARCHEN SUBJOINED LETTER TSA;Mn;0;NSM;;;;;N;;;;; +11CA3;MARCHEN SUBJOINED LETTER TSHA;Mn;0;NSM;;;;;N;;;;; +11CA4;MARCHEN SUBJOINED LETTER DZA;Mn;0;NSM;;;;;N;;;;; +11CA5;MARCHEN SUBJOINED LETTER WA;Mn;0;NSM;;;;;N;;;;; +11CA6;MARCHEN SUBJOINED LETTER ZHA;Mn;0;NSM;;;;;N;;;;; +11CA7;MARCHEN SUBJOINED LETTER ZA;Mn;0;NSM;;;;;N;;;;; +11CA9;MARCHEN SUBJOINED LETTER YA;Mc;0;L;;;;;N;;;;; +11CAA;MARCHEN SUBJOINED LETTER RA;Mn;0;NSM;;;;;N;;;;; +11CAB;MARCHEN SUBJOINED LETTER LA;Mn;0;NSM;;;;;N;;;;; +11CAC;MARCHEN SUBJOINED LETTER SHA;Mn;0;NSM;;;;;N;;;;; +11CAD;MARCHEN SUBJOINED LETTER SA;Mn;0;NSM;;;;;N;;;;; +11CAE;MARCHEN SUBJOINED LETTER HA;Mn;0;NSM;;;;;N;;;;; +11CAF;MARCHEN SUBJOINED LETTER A;Mn;0;NSM;;;;;N;;;;; +11CB0;MARCHEN VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;; +11CB1;MARCHEN VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +11CB2;MARCHEN VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +11CB3;MARCHEN VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +11CB4;MARCHEN VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +11CB5;MARCHEN SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +11CB6;MARCHEN SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +12000;CUNEIFORM SIGN A;Lo;0;L;;;;;N;;;;; +12001;CUNEIFORM SIGN A TIMES A;Lo;0;L;;;;;N;;;;; +12002;CUNEIFORM SIGN A TIMES BAD;Lo;0;L;;;;;N;;;;; +12003;CUNEIFORM SIGN A TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; +12004;CUNEIFORM SIGN A TIMES HA;Lo;0;L;;;;;N;;;;; +12005;CUNEIFORM SIGN A TIMES IGI;Lo;0;L;;;;;N;;;;; +12006;CUNEIFORM SIGN A TIMES LAGAR GUNU;Lo;0;L;;;;;N;;;;; +12007;CUNEIFORM SIGN A TIMES MUSH;Lo;0;L;;;;;N;;;;; +12008;CUNEIFORM SIGN A TIMES SAG;Lo;0;L;;;;;N;;;;; +12009;CUNEIFORM SIGN A2;Lo;0;L;;;;;N;;;;; +1200A;CUNEIFORM SIGN AB;Lo;0;L;;;;;N;;;;; +1200B;CUNEIFORM SIGN AB TIMES ASH2;Lo;0;L;;;;;N;;;;; +1200C;CUNEIFORM SIGN AB TIMES DUN3 GUNU;Lo;0;L;;;;;N;;;;; +1200D;CUNEIFORM SIGN AB TIMES GAL;Lo;0;L;;;;;N;;;;; +1200E;CUNEIFORM SIGN AB TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; +1200F;CUNEIFORM SIGN AB TIMES HA;Lo;0;L;;;;;N;;;;; +12010;CUNEIFORM SIGN AB TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +12011;CUNEIFORM SIGN AB TIMES IMIN;Lo;0;L;;;;;N;;;;; +12012;CUNEIFORM SIGN AB TIMES LAGAB;Lo;0;L;;;;;N;;;;; +12013;CUNEIFORM SIGN AB TIMES SHESH;Lo;0;L;;;;;N;;;;; +12014;CUNEIFORM SIGN AB TIMES U PLUS U PLUS U;Lo;0;L;;;;;N;;;;; +12015;CUNEIFORM SIGN AB GUNU;Lo;0;L;;;;;N;;;;; +12016;CUNEIFORM SIGN AB2;Lo;0;L;;;;;N;;;;; +12017;CUNEIFORM SIGN AB2 TIMES BALAG;Lo;0;L;;;;;N;;;;; +12018;CUNEIFORM SIGN AB2 TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; +12019;CUNEIFORM SIGN AB2 TIMES ME PLUS EN;Lo;0;L;;;;;N;;;;; +1201A;CUNEIFORM SIGN AB2 TIMES SHA3;Lo;0;L;;;;;N;;;;; +1201B;CUNEIFORM SIGN AB2 TIMES TAK4;Lo;0;L;;;;;N;;;;; +1201C;CUNEIFORM SIGN AD;Lo;0;L;;;;;N;;;;; +1201D;CUNEIFORM SIGN AK;Lo;0;L;;;;;N;;;;; +1201E;CUNEIFORM SIGN AK TIMES ERIN2;Lo;0;L;;;;;N;;;;; +1201F;CUNEIFORM SIGN AK TIMES SHITA PLUS GISH;Lo;0;L;;;;;N;;;;; +12020;CUNEIFORM SIGN AL;Lo;0;L;;;;;N;;;;; +12021;CUNEIFORM SIGN AL TIMES AL;Lo;0;L;;;;;N;;;;; +12022;CUNEIFORM SIGN AL TIMES DIM2;Lo;0;L;;;;;N;;;;; +12023;CUNEIFORM SIGN AL TIMES GISH;Lo;0;L;;;;;N;;;;; +12024;CUNEIFORM SIGN AL TIMES HA;Lo;0;L;;;;;N;;;;; +12025;CUNEIFORM SIGN AL TIMES KAD3;Lo;0;L;;;;;N;;;;; +12026;CUNEIFORM SIGN AL TIMES KI;Lo;0;L;;;;;N;;;;; +12027;CUNEIFORM SIGN AL TIMES SHE;Lo;0;L;;;;;N;;;;; +12028;CUNEIFORM SIGN AL TIMES USH;Lo;0;L;;;;;N;;;;; +12029;CUNEIFORM SIGN ALAN;Lo;0;L;;;;;N;;;;; +1202A;CUNEIFORM SIGN ALEPH;Lo;0;L;;;;;N;;;;; +1202B;CUNEIFORM SIGN AMAR;Lo;0;L;;;;;N;;;;; +1202C;CUNEIFORM SIGN AMAR TIMES SHE;Lo;0;L;;;;;N;;;;; +1202D;CUNEIFORM SIGN AN;Lo;0;L;;;;;N;;;;; +1202E;CUNEIFORM SIGN AN OVER AN;Lo;0;L;;;;;N;;;;; +1202F;CUNEIFORM SIGN AN THREE TIMES;Lo;0;L;;;;;N;;;;; +12030;CUNEIFORM SIGN AN PLUS NAGA OPPOSING AN PLUS NAGA;Lo;0;L;;;;;N;;;;; +12031;CUNEIFORM SIGN AN PLUS NAGA SQUARED;Lo;0;L;;;;;N;;;;; +12032;CUNEIFORM SIGN ANSHE;Lo;0;L;;;;;N;;;;; +12033;CUNEIFORM SIGN APIN;Lo;0;L;;;;;N;;;;; +12034;CUNEIFORM SIGN ARAD;Lo;0;L;;;;;N;;;;; +12035;CUNEIFORM SIGN ARAD TIMES KUR;Lo;0;L;;;;;N;;;;; +12036;CUNEIFORM SIGN ARKAB;Lo;0;L;;;;;N;;;;; +12037;CUNEIFORM SIGN ASAL2;Lo;0;L;;;;;N;;;;; +12038;CUNEIFORM SIGN ASH;Lo;0;L;;;;;N;;;;; +12039;CUNEIFORM SIGN ASH ZIDA TENU;Lo;0;L;;;;;N;;;;; +1203A;CUNEIFORM SIGN ASH KABA TENU;Lo;0;L;;;;;N;;;;; +1203B;CUNEIFORM SIGN ASH OVER ASH TUG2 OVER TUG2 TUG2 OVER TUG2 PAP;Lo;0;L;;;;;N;;;;; +1203C;CUNEIFORM SIGN ASH OVER ASH OVER ASH;Lo;0;L;;;;;N;;;;; +1203D;CUNEIFORM SIGN ASH OVER ASH OVER ASH CROSSING ASH OVER ASH OVER ASH;Lo;0;L;;;;;N;;;;; +1203E;CUNEIFORM SIGN ASH2;Lo;0;L;;;;;N;;;;; +1203F;CUNEIFORM SIGN ASHGAB;Lo;0;L;;;;;N;;;;; +12040;CUNEIFORM SIGN BA;Lo;0;L;;;;;N;;;;; +12041;CUNEIFORM SIGN BAD;Lo;0;L;;;;;N;;;;; +12042;CUNEIFORM SIGN BAG3;Lo;0;L;;;;;N;;;;; +12043;CUNEIFORM SIGN BAHAR2;Lo;0;L;;;;;N;;;;; +12044;CUNEIFORM SIGN BAL;Lo;0;L;;;;;N;;;;; +12045;CUNEIFORM SIGN BAL OVER BAL;Lo;0;L;;;;;N;;;;; +12046;CUNEIFORM SIGN BALAG;Lo;0;L;;;;;N;;;;; +12047;CUNEIFORM SIGN BAR;Lo;0;L;;;;;N;;;;; +12048;CUNEIFORM SIGN BARA2;Lo;0;L;;;;;N;;;;; +12049;CUNEIFORM SIGN BI;Lo;0;L;;;;;N;;;;; +1204A;CUNEIFORM SIGN BI TIMES A;Lo;0;L;;;;;N;;;;; +1204B;CUNEIFORM SIGN BI TIMES GAR;Lo;0;L;;;;;N;;;;; +1204C;CUNEIFORM SIGN BI TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +1204D;CUNEIFORM SIGN BU;Lo;0;L;;;;;N;;;;; +1204E;CUNEIFORM SIGN BU OVER BU AB;Lo;0;L;;;;;N;;;;; +1204F;CUNEIFORM SIGN BU OVER BU UN;Lo;0;L;;;;;N;;;;; +12050;CUNEIFORM SIGN BU CROSSING BU;Lo;0;L;;;;;N;;;;; +12051;CUNEIFORM SIGN BULUG;Lo;0;L;;;;;N;;;;; +12052;CUNEIFORM SIGN BULUG OVER BULUG;Lo;0;L;;;;;N;;;;; +12053;CUNEIFORM SIGN BUR;Lo;0;L;;;;;N;;;;; +12054;CUNEIFORM SIGN BUR2;Lo;0;L;;;;;N;;;;; +12055;CUNEIFORM SIGN DA;Lo;0;L;;;;;N;;;;; +12056;CUNEIFORM SIGN DAG;Lo;0;L;;;;;N;;;;; +12057;CUNEIFORM SIGN DAG KISIM5 TIMES A PLUS MASH;Lo;0;L;;;;;N;;;;; +12058;CUNEIFORM SIGN DAG KISIM5 TIMES AMAR;Lo;0;L;;;;;N;;;;; +12059;CUNEIFORM SIGN DAG KISIM5 TIMES BALAG;Lo;0;L;;;;;N;;;;; +1205A;CUNEIFORM SIGN DAG KISIM5 TIMES BI;Lo;0;L;;;;;N;;;;; +1205B;CUNEIFORM SIGN DAG KISIM5 TIMES GA;Lo;0;L;;;;;N;;;;; +1205C;CUNEIFORM SIGN DAG KISIM5 TIMES GA PLUS MASH;Lo;0;L;;;;;N;;;;; +1205D;CUNEIFORM SIGN DAG KISIM5 TIMES GI;Lo;0;L;;;;;N;;;;; +1205E;CUNEIFORM SIGN DAG KISIM5 TIMES GIR2;Lo;0;L;;;;;N;;;;; +1205F;CUNEIFORM SIGN DAG KISIM5 TIMES GUD;Lo;0;L;;;;;N;;;;; +12060;CUNEIFORM SIGN DAG KISIM5 TIMES HA;Lo;0;L;;;;;N;;;;; +12061;CUNEIFORM SIGN DAG KISIM5 TIMES IR;Lo;0;L;;;;;N;;;;; +12062;CUNEIFORM SIGN DAG KISIM5 TIMES IR PLUS LU;Lo;0;L;;;;;N;;;;; +12063;CUNEIFORM SIGN DAG KISIM5 TIMES KAK;Lo;0;L;;;;;N;;;;; +12064;CUNEIFORM SIGN DAG KISIM5 TIMES LA;Lo;0;L;;;;;N;;;;; +12065;CUNEIFORM SIGN DAG KISIM5 TIMES LU;Lo;0;L;;;;;N;;;;; +12066;CUNEIFORM SIGN DAG KISIM5 TIMES LU PLUS MASH2;Lo;0;L;;;;;N;;;;; +12067;CUNEIFORM SIGN DAG KISIM5 TIMES LUM;Lo;0;L;;;;;N;;;;; +12068;CUNEIFORM SIGN DAG KISIM5 TIMES NE;Lo;0;L;;;;;N;;;;; +12069;CUNEIFORM SIGN DAG KISIM5 TIMES PAP PLUS PAP;Lo;0;L;;;;;N;;;;; +1206A;CUNEIFORM SIGN DAG KISIM5 TIMES SI;Lo;0;L;;;;;N;;;;; +1206B;CUNEIFORM SIGN DAG KISIM5 TIMES TAK4;Lo;0;L;;;;;N;;;;; +1206C;CUNEIFORM SIGN DAG KISIM5 TIMES U2 PLUS GIR2;Lo;0;L;;;;;N;;;;; +1206D;CUNEIFORM SIGN DAG KISIM5 TIMES USH;Lo;0;L;;;;;N;;;;; +1206E;CUNEIFORM SIGN DAM;Lo;0;L;;;;;N;;;;; +1206F;CUNEIFORM SIGN DAR;Lo;0;L;;;;;N;;;;; +12070;CUNEIFORM SIGN DARA3;Lo;0;L;;;;;N;;;;; +12071;CUNEIFORM SIGN DARA4;Lo;0;L;;;;;N;;;;; +12072;CUNEIFORM SIGN DI;Lo;0;L;;;;;N;;;;; +12073;CUNEIFORM SIGN DIB;Lo;0;L;;;;;N;;;;; +12074;CUNEIFORM SIGN DIM;Lo;0;L;;;;;N;;;;; +12075;CUNEIFORM SIGN DIM TIMES SHE;Lo;0;L;;;;;N;;;;; +12076;CUNEIFORM SIGN DIM2;Lo;0;L;;;;;N;;;;; +12077;CUNEIFORM SIGN DIN;Lo;0;L;;;;;N;;;;; +12078;CUNEIFORM SIGN DIN KASKAL U GUNU DISH;Lo;0;L;;;;;N;;;;; +12079;CUNEIFORM SIGN DISH;Lo;0;L;;;;;N;;;;; +1207A;CUNEIFORM SIGN DU;Lo;0;L;;;;;N;;;;; +1207B;CUNEIFORM SIGN DU OVER DU;Lo;0;L;;;;;N;;;;; +1207C;CUNEIFORM SIGN DU GUNU;Lo;0;L;;;;;N;;;;; +1207D;CUNEIFORM SIGN DU SHESHIG;Lo;0;L;;;;;N;;;;; +1207E;CUNEIFORM SIGN DUB;Lo;0;L;;;;;N;;;;; +1207F;CUNEIFORM SIGN DUB TIMES ESH2;Lo;0;L;;;;;N;;;;; +12080;CUNEIFORM SIGN DUB2;Lo;0;L;;;;;N;;;;; +12081;CUNEIFORM SIGN DUG;Lo;0;L;;;;;N;;;;; +12082;CUNEIFORM SIGN DUGUD;Lo;0;L;;;;;N;;;;; +12083;CUNEIFORM SIGN DUH;Lo;0;L;;;;;N;;;;; +12084;CUNEIFORM SIGN DUN;Lo;0;L;;;;;N;;;;; +12085;CUNEIFORM SIGN DUN3;Lo;0;L;;;;;N;;;;; +12086;CUNEIFORM SIGN DUN3 GUNU;Lo;0;L;;;;;N;;;;; +12087;CUNEIFORM SIGN DUN3 GUNU GUNU;Lo;0;L;;;;;N;;;;; +12088;CUNEIFORM SIGN DUN4;Lo;0;L;;;;;N;;;;; +12089;CUNEIFORM SIGN DUR2;Lo;0;L;;;;;N;;;;; +1208A;CUNEIFORM SIGN E;Lo;0;L;;;;;N;;;;; +1208B;CUNEIFORM SIGN E TIMES PAP;Lo;0;L;;;;;N;;;;; +1208C;CUNEIFORM SIGN E OVER E NUN OVER NUN;Lo;0;L;;;;;N;;;;; +1208D;CUNEIFORM SIGN E2;Lo;0;L;;;;;N;;;;; +1208E;CUNEIFORM SIGN E2 TIMES A PLUS HA PLUS DA;Lo;0;L;;;;;N;;;;; +1208F;CUNEIFORM SIGN E2 TIMES GAR;Lo;0;L;;;;;N;;;;; +12090;CUNEIFORM SIGN E2 TIMES MI;Lo;0;L;;;;;N;;;;; +12091;CUNEIFORM SIGN E2 TIMES SAL;Lo;0;L;;;;;N;;;;; +12092;CUNEIFORM SIGN E2 TIMES SHE;Lo;0;L;;;;;N;;;;; +12093;CUNEIFORM SIGN E2 TIMES U;Lo;0;L;;;;;N;;;;; +12094;CUNEIFORM SIGN EDIN;Lo;0;L;;;;;N;;;;; +12095;CUNEIFORM SIGN EGIR;Lo;0;L;;;;;N;;;;; +12096;CUNEIFORM SIGN EL;Lo;0;L;;;;;N;;;;; +12097;CUNEIFORM SIGN EN;Lo;0;L;;;;;N;;;;; +12098;CUNEIFORM SIGN EN TIMES GAN2;Lo;0;L;;;;;N;;;;; +12099;CUNEIFORM SIGN EN TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; +1209A;CUNEIFORM SIGN EN TIMES ME;Lo;0;L;;;;;N;;;;; +1209B;CUNEIFORM SIGN EN CROSSING EN;Lo;0;L;;;;;N;;;;; +1209C;CUNEIFORM SIGN EN OPPOSING EN;Lo;0;L;;;;;N;;;;; +1209D;CUNEIFORM SIGN EN SQUARED;Lo;0;L;;;;;N;;;;; +1209E;CUNEIFORM SIGN EREN;Lo;0;L;;;;;N;;;;; +1209F;CUNEIFORM SIGN ERIN2;Lo;0;L;;;;;N;;;;; +120A0;CUNEIFORM SIGN ESH2;Lo;0;L;;;;;N;;;;; +120A1;CUNEIFORM SIGN EZEN;Lo;0;L;;;;;N;;;;; +120A2;CUNEIFORM SIGN EZEN TIMES A;Lo;0;L;;;;;N;;;;; +120A3;CUNEIFORM SIGN EZEN TIMES A PLUS LAL;Lo;0;L;;;;;N;;;;; +120A4;CUNEIFORM SIGN EZEN TIMES A PLUS LAL TIMES LAL;Lo;0;L;;;;;N;;;;; +120A5;CUNEIFORM SIGN EZEN TIMES AN;Lo;0;L;;;;;N;;;;; +120A6;CUNEIFORM SIGN EZEN TIMES BAD;Lo;0;L;;;;;N;;;;; +120A7;CUNEIFORM SIGN EZEN TIMES DUN3 GUNU;Lo;0;L;;;;;N;;;;; +120A8;CUNEIFORM SIGN EZEN TIMES DUN3 GUNU GUNU;Lo;0;L;;;;;N;;;;; +120A9;CUNEIFORM SIGN EZEN TIMES HA;Lo;0;L;;;;;N;;;;; +120AA;CUNEIFORM SIGN EZEN TIMES HA GUNU;Lo;0;L;;;;;N;;;;; +120AB;CUNEIFORM SIGN EZEN TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +120AC;CUNEIFORM SIGN EZEN TIMES KASKAL;Lo;0;L;;;;;N;;;;; +120AD;CUNEIFORM SIGN EZEN TIMES KASKAL SQUARED;Lo;0;L;;;;;N;;;;; +120AE;CUNEIFORM SIGN EZEN TIMES KU3;Lo;0;L;;;;;N;;;;; +120AF;CUNEIFORM SIGN EZEN TIMES LA;Lo;0;L;;;;;N;;;;; +120B0;CUNEIFORM SIGN EZEN TIMES LAL TIMES LAL;Lo;0;L;;;;;N;;;;; +120B1;CUNEIFORM SIGN EZEN TIMES LI;Lo;0;L;;;;;N;;;;; +120B2;CUNEIFORM SIGN EZEN TIMES LU;Lo;0;L;;;;;N;;;;; +120B3;CUNEIFORM SIGN EZEN TIMES U2;Lo;0;L;;;;;N;;;;; +120B4;CUNEIFORM SIGN EZEN TIMES UD;Lo;0;L;;;;;N;;;;; +120B5;CUNEIFORM SIGN GA;Lo;0;L;;;;;N;;;;; +120B6;CUNEIFORM SIGN GA GUNU;Lo;0;L;;;;;N;;;;; +120B7;CUNEIFORM SIGN GA2;Lo;0;L;;;;;N;;;;; +120B8;CUNEIFORM SIGN GA2 TIMES A PLUS DA PLUS HA;Lo;0;L;;;;;N;;;;; +120B9;CUNEIFORM SIGN GA2 TIMES A PLUS HA;Lo;0;L;;;;;N;;;;; +120BA;CUNEIFORM SIGN GA2 TIMES A PLUS IGI;Lo;0;L;;;;;N;;;;; +120BB;CUNEIFORM SIGN GA2 TIMES AB2 TENU PLUS TAB;Lo;0;L;;;;;N;;;;; +120BC;CUNEIFORM SIGN GA2 TIMES AN;Lo;0;L;;;;;N;;;;; +120BD;CUNEIFORM SIGN GA2 TIMES ASH;Lo;0;L;;;;;N;;;;; +120BE;CUNEIFORM SIGN GA2 TIMES ASH2 PLUS GAL;Lo;0;L;;;;;N;;;;; +120BF;CUNEIFORM SIGN GA2 TIMES BAD;Lo;0;L;;;;;N;;;;; +120C0;CUNEIFORM SIGN GA2 TIMES BAR PLUS RA;Lo;0;L;;;;;N;;;;; +120C1;CUNEIFORM SIGN GA2 TIMES BUR;Lo;0;L;;;;;N;;;;; +120C2;CUNEIFORM SIGN GA2 TIMES BUR PLUS RA;Lo;0;L;;;;;N;;;;; +120C3;CUNEIFORM SIGN GA2 TIMES DA;Lo;0;L;;;;;N;;;;; +120C4;CUNEIFORM SIGN GA2 TIMES DI;Lo;0;L;;;;;N;;;;; +120C5;CUNEIFORM SIGN GA2 TIMES DIM TIMES SHE;Lo;0;L;;;;;N;;;;; +120C6;CUNEIFORM SIGN GA2 TIMES DUB;Lo;0;L;;;;;N;;;;; +120C7;CUNEIFORM SIGN GA2 TIMES EL;Lo;0;L;;;;;N;;;;; +120C8;CUNEIFORM SIGN GA2 TIMES EL PLUS LA;Lo;0;L;;;;;N;;;;; +120C9;CUNEIFORM SIGN GA2 TIMES EN;Lo;0;L;;;;;N;;;;; +120CA;CUNEIFORM SIGN GA2 TIMES EN TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; +120CB;CUNEIFORM SIGN GA2 TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; +120CC;CUNEIFORM SIGN GA2 TIMES GAR;Lo;0;L;;;;;N;;;;; +120CD;CUNEIFORM SIGN GA2 TIMES GI;Lo;0;L;;;;;N;;;;; +120CE;CUNEIFORM SIGN GA2 TIMES GI4;Lo;0;L;;;;;N;;;;; +120CF;CUNEIFORM SIGN GA2 TIMES GI4 PLUS A;Lo;0;L;;;;;N;;;;; +120D0;CUNEIFORM SIGN GA2 TIMES GIR2 PLUS SU;Lo;0;L;;;;;N;;;;; +120D1;CUNEIFORM SIGN GA2 TIMES HA PLUS LU PLUS ESH2;Lo;0;L;;;;;N;;;;; +120D2;CUNEIFORM SIGN GA2 TIMES HAL;Lo;0;L;;;;;N;;;;; +120D3;CUNEIFORM SIGN GA2 TIMES HAL PLUS LA;Lo;0;L;;;;;N;;;;; +120D4;CUNEIFORM SIGN GA2 TIMES HI PLUS LI;Lo;0;L;;;;;N;;;;; +120D5;CUNEIFORM SIGN GA2 TIMES HUB2;Lo;0;L;;;;;N;;;;; +120D6;CUNEIFORM SIGN GA2 TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +120D7;CUNEIFORM SIGN GA2 TIMES ISH PLUS HU PLUS ASH;Lo;0;L;;;;;N;;;;; +120D8;CUNEIFORM SIGN GA2 TIMES KAK;Lo;0;L;;;;;N;;;;; +120D9;CUNEIFORM SIGN GA2 TIMES KASKAL;Lo;0;L;;;;;N;;;;; +120DA;CUNEIFORM SIGN GA2 TIMES KID;Lo;0;L;;;;;N;;;;; +120DB;CUNEIFORM SIGN GA2 TIMES KID PLUS LAL;Lo;0;L;;;;;N;;;;; +120DC;CUNEIFORM SIGN GA2 TIMES KU3 PLUS AN;Lo;0;L;;;;;N;;;;; +120DD;CUNEIFORM SIGN GA2 TIMES LA;Lo;0;L;;;;;N;;;;; +120DE;CUNEIFORM SIGN GA2 TIMES ME PLUS EN;Lo;0;L;;;;;N;;;;; +120DF;CUNEIFORM SIGN GA2 TIMES MI;Lo;0;L;;;;;N;;;;; +120E0;CUNEIFORM SIGN GA2 TIMES NUN;Lo;0;L;;;;;N;;;;; +120E1;CUNEIFORM SIGN GA2 TIMES NUN OVER NUN;Lo;0;L;;;;;N;;;;; +120E2;CUNEIFORM SIGN GA2 TIMES PA;Lo;0;L;;;;;N;;;;; +120E3;CUNEIFORM SIGN GA2 TIMES SAL;Lo;0;L;;;;;N;;;;; +120E4;CUNEIFORM SIGN GA2 TIMES SAR;Lo;0;L;;;;;N;;;;; +120E5;CUNEIFORM SIGN GA2 TIMES SHE;Lo;0;L;;;;;N;;;;; +120E6;CUNEIFORM SIGN GA2 TIMES SHE PLUS TUR;Lo;0;L;;;;;N;;;;; +120E7;CUNEIFORM SIGN GA2 TIMES SHID;Lo;0;L;;;;;N;;;;; +120E8;CUNEIFORM SIGN GA2 TIMES SUM;Lo;0;L;;;;;N;;;;; +120E9;CUNEIFORM SIGN GA2 TIMES TAK4;Lo;0;L;;;;;N;;;;; +120EA;CUNEIFORM SIGN GA2 TIMES U;Lo;0;L;;;;;N;;;;; +120EB;CUNEIFORM SIGN GA2 TIMES UD;Lo;0;L;;;;;N;;;;; +120EC;CUNEIFORM SIGN GA2 TIMES UD PLUS DU;Lo;0;L;;;;;N;;;;; +120ED;CUNEIFORM SIGN GA2 OVER GA2;Lo;0;L;;;;;N;;;;; +120EE;CUNEIFORM SIGN GABA;Lo;0;L;;;;;N;;;;; +120EF;CUNEIFORM SIGN GABA CROSSING GABA;Lo;0;L;;;;;N;;;;; +120F0;CUNEIFORM SIGN GAD;Lo;0;L;;;;;N;;;;; +120F1;CUNEIFORM SIGN GAD OVER GAD GAR OVER GAR;Lo;0;L;;;;;N;;;;; +120F2;CUNEIFORM SIGN GAL;Lo;0;L;;;;;N;;;;; +120F3;CUNEIFORM SIGN GAL GAD OVER GAD GAR OVER GAR;Lo;0;L;;;;;N;;;;; +120F4;CUNEIFORM SIGN GALAM;Lo;0;L;;;;;N;;;;; +120F5;CUNEIFORM SIGN GAM;Lo;0;L;;;;;N;;;;; +120F6;CUNEIFORM SIGN GAN;Lo;0;L;;;;;N;;;;; +120F7;CUNEIFORM SIGN GAN2;Lo;0;L;;;;;N;;;;; +120F8;CUNEIFORM SIGN GAN2 TENU;Lo;0;L;;;;;N;;;;; +120F9;CUNEIFORM SIGN GAN2 OVER GAN2;Lo;0;L;;;;;N;;;;; +120FA;CUNEIFORM SIGN GAN2 CROSSING GAN2;Lo;0;L;;;;;N;;;;; +120FB;CUNEIFORM SIGN GAR;Lo;0;L;;;;;N;;;;; +120FC;CUNEIFORM SIGN GAR3;Lo;0;L;;;;;N;;;;; +120FD;CUNEIFORM SIGN GASHAN;Lo;0;L;;;;;N;;;;; +120FE;CUNEIFORM SIGN GESHTIN;Lo;0;L;;;;;N;;;;; +120FF;CUNEIFORM SIGN GESHTIN TIMES KUR;Lo;0;L;;;;;N;;;;; +12100;CUNEIFORM SIGN GI;Lo;0;L;;;;;N;;;;; +12101;CUNEIFORM SIGN GI TIMES E;Lo;0;L;;;;;N;;;;; +12102;CUNEIFORM SIGN GI TIMES U;Lo;0;L;;;;;N;;;;; +12103;CUNEIFORM SIGN GI CROSSING GI;Lo;0;L;;;;;N;;;;; +12104;CUNEIFORM SIGN GI4;Lo;0;L;;;;;N;;;;; +12105;CUNEIFORM SIGN GI4 OVER GI4;Lo;0;L;;;;;N;;;;; +12106;CUNEIFORM SIGN GI4 CROSSING GI4;Lo;0;L;;;;;N;;;;; +12107;CUNEIFORM SIGN GIDIM;Lo;0;L;;;;;N;;;;; +12108;CUNEIFORM SIGN GIR2;Lo;0;L;;;;;N;;;;; +12109;CUNEIFORM SIGN GIR2 GUNU;Lo;0;L;;;;;N;;;;; +1210A;CUNEIFORM SIGN GIR3;Lo;0;L;;;;;N;;;;; +1210B;CUNEIFORM SIGN GIR3 TIMES A PLUS IGI;Lo;0;L;;;;;N;;;;; +1210C;CUNEIFORM SIGN GIR3 TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; +1210D;CUNEIFORM SIGN GIR3 TIMES IGI;Lo;0;L;;;;;N;;;;; +1210E;CUNEIFORM SIGN GIR3 TIMES LU PLUS IGI;Lo;0;L;;;;;N;;;;; +1210F;CUNEIFORM SIGN GIR3 TIMES PA;Lo;0;L;;;;;N;;;;; +12110;CUNEIFORM SIGN GISAL;Lo;0;L;;;;;N;;;;; +12111;CUNEIFORM SIGN GISH;Lo;0;L;;;;;N;;;;; +12112;CUNEIFORM SIGN GISH CROSSING GISH;Lo;0;L;;;;;N;;;;; +12113;CUNEIFORM SIGN GISH TIMES BAD;Lo;0;L;;;;;N;;;;; +12114;CUNEIFORM SIGN GISH TIMES TAK4;Lo;0;L;;;;;N;;;;; +12115;CUNEIFORM SIGN GISH TENU;Lo;0;L;;;;;N;;;;; +12116;CUNEIFORM SIGN GU;Lo;0;L;;;;;N;;;;; +12117;CUNEIFORM SIGN GU CROSSING GU;Lo;0;L;;;;;N;;;;; +12118;CUNEIFORM SIGN GU2;Lo;0;L;;;;;N;;;;; +12119;CUNEIFORM SIGN GU2 TIMES KAK;Lo;0;L;;;;;N;;;;; +1211A;CUNEIFORM SIGN GU2 TIMES KAK TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +1211B;CUNEIFORM SIGN GU2 TIMES NUN;Lo;0;L;;;;;N;;;;; +1211C;CUNEIFORM SIGN GU2 TIMES SAL PLUS TUG2;Lo;0;L;;;;;N;;;;; +1211D;CUNEIFORM SIGN GU2 GUNU;Lo;0;L;;;;;N;;;;; +1211E;CUNEIFORM SIGN GUD;Lo;0;L;;;;;N;;;;; +1211F;CUNEIFORM SIGN GUD TIMES A PLUS KUR;Lo;0;L;;;;;N;;;;; +12120;CUNEIFORM SIGN GUD TIMES KUR;Lo;0;L;;;;;N;;;;; +12121;CUNEIFORM SIGN GUD OVER GUD LUGAL;Lo;0;L;;;;;N;;;;; +12122;CUNEIFORM SIGN GUL;Lo;0;L;;;;;N;;;;; +12123;CUNEIFORM SIGN GUM;Lo;0;L;;;;;N;;;;; +12124;CUNEIFORM SIGN GUM TIMES SHE;Lo;0;L;;;;;N;;;;; +12125;CUNEIFORM SIGN GUR;Lo;0;L;;;;;N;;;;; +12126;CUNEIFORM SIGN GUR7;Lo;0;L;;;;;N;;;;; +12127;CUNEIFORM SIGN GURUN;Lo;0;L;;;;;N;;;;; +12128;CUNEIFORM SIGN GURUSH;Lo;0;L;;;;;N;;;;; +12129;CUNEIFORM SIGN HA;Lo;0;L;;;;;N;;;;; +1212A;CUNEIFORM SIGN HA TENU;Lo;0;L;;;;;N;;;;; +1212B;CUNEIFORM SIGN HA GUNU;Lo;0;L;;;;;N;;;;; +1212C;CUNEIFORM SIGN HAL;Lo;0;L;;;;;N;;;;; +1212D;CUNEIFORM SIGN HI;Lo;0;L;;;;;N;;;;; +1212E;CUNEIFORM SIGN HI TIMES ASH;Lo;0;L;;;;;N;;;;; +1212F;CUNEIFORM SIGN HI TIMES ASH2;Lo;0;L;;;;;N;;;;; +12130;CUNEIFORM SIGN HI TIMES BAD;Lo;0;L;;;;;N;;;;; +12131;CUNEIFORM SIGN HI TIMES DISH;Lo;0;L;;;;;N;;;;; +12132;CUNEIFORM SIGN HI TIMES GAD;Lo;0;L;;;;;N;;;;; +12133;CUNEIFORM SIGN HI TIMES KIN;Lo;0;L;;;;;N;;;;; +12134;CUNEIFORM SIGN HI TIMES NUN;Lo;0;L;;;;;N;;;;; +12135;CUNEIFORM SIGN HI TIMES SHE;Lo;0;L;;;;;N;;;;; +12136;CUNEIFORM SIGN HI TIMES U;Lo;0;L;;;;;N;;;;; +12137;CUNEIFORM SIGN HU;Lo;0;L;;;;;N;;;;; +12138;CUNEIFORM SIGN HUB2;Lo;0;L;;;;;N;;;;; +12139;CUNEIFORM SIGN HUB2 TIMES AN;Lo;0;L;;;;;N;;;;; +1213A;CUNEIFORM SIGN HUB2 TIMES HAL;Lo;0;L;;;;;N;;;;; +1213B;CUNEIFORM SIGN HUB2 TIMES KASKAL;Lo;0;L;;;;;N;;;;; +1213C;CUNEIFORM SIGN HUB2 TIMES LISH;Lo;0;L;;;;;N;;;;; +1213D;CUNEIFORM SIGN HUB2 TIMES UD;Lo;0;L;;;;;N;;;;; +1213E;CUNEIFORM SIGN HUL2;Lo;0;L;;;;;N;;;;; +1213F;CUNEIFORM SIGN I;Lo;0;L;;;;;N;;;;; +12140;CUNEIFORM SIGN I A;Lo;0;L;;;;;N;;;;; +12141;CUNEIFORM SIGN IB;Lo;0;L;;;;;N;;;;; +12142;CUNEIFORM SIGN IDIM;Lo;0;L;;;;;N;;;;; +12143;CUNEIFORM SIGN IDIM OVER IDIM BUR;Lo;0;L;;;;;N;;;;; +12144;CUNEIFORM SIGN IDIM OVER IDIM SQUARED;Lo;0;L;;;;;N;;;;; +12145;CUNEIFORM SIGN IG;Lo;0;L;;;;;N;;;;; +12146;CUNEIFORM SIGN IGI;Lo;0;L;;;;;N;;;;; +12147;CUNEIFORM SIGN IGI DIB;Lo;0;L;;;;;N;;;;; +12148;CUNEIFORM SIGN IGI RI;Lo;0;L;;;;;N;;;;; +12149;CUNEIFORM SIGN IGI OVER IGI SHIR OVER SHIR UD OVER UD;Lo;0;L;;;;;N;;;;; +1214A;CUNEIFORM SIGN IGI GUNU;Lo;0;L;;;;;N;;;;; +1214B;CUNEIFORM SIGN IL;Lo;0;L;;;;;N;;;;; +1214C;CUNEIFORM SIGN IL TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; +1214D;CUNEIFORM SIGN IL2;Lo;0;L;;;;;N;;;;; +1214E;CUNEIFORM SIGN IM;Lo;0;L;;;;;N;;;;; +1214F;CUNEIFORM SIGN IM TIMES TAK4;Lo;0;L;;;;;N;;;;; +12150;CUNEIFORM SIGN IM CROSSING IM;Lo;0;L;;;;;N;;;;; +12151;CUNEIFORM SIGN IM OPPOSING IM;Lo;0;L;;;;;N;;;;; +12152;CUNEIFORM SIGN IM SQUARED;Lo;0;L;;;;;N;;;;; +12153;CUNEIFORM SIGN IMIN;Lo;0;L;;;;;N;;;;; +12154;CUNEIFORM SIGN IN;Lo;0;L;;;;;N;;;;; +12155;CUNEIFORM SIGN IR;Lo;0;L;;;;;N;;;;; +12156;CUNEIFORM SIGN ISH;Lo;0;L;;;;;N;;;;; +12157;CUNEIFORM SIGN KA;Lo;0;L;;;;;N;;;;; +12158;CUNEIFORM SIGN KA TIMES A;Lo;0;L;;;;;N;;;;; +12159;CUNEIFORM SIGN KA TIMES AD;Lo;0;L;;;;;N;;;;; +1215A;CUNEIFORM SIGN KA TIMES AD PLUS KU3;Lo;0;L;;;;;N;;;;; +1215B;CUNEIFORM SIGN KA TIMES ASH2;Lo;0;L;;;;;N;;;;; +1215C;CUNEIFORM SIGN KA TIMES BAD;Lo;0;L;;;;;N;;;;; +1215D;CUNEIFORM SIGN KA TIMES BALAG;Lo;0;L;;;;;N;;;;; +1215E;CUNEIFORM SIGN KA TIMES BAR;Lo;0;L;;;;;N;;;;; +1215F;CUNEIFORM SIGN KA TIMES BI;Lo;0;L;;;;;N;;;;; +12160;CUNEIFORM SIGN KA TIMES ERIN2;Lo;0;L;;;;;N;;;;; +12161;CUNEIFORM SIGN KA TIMES ESH2;Lo;0;L;;;;;N;;;;; +12162;CUNEIFORM SIGN KA TIMES GA;Lo;0;L;;;;;N;;;;; +12163;CUNEIFORM SIGN KA TIMES GAL;Lo;0;L;;;;;N;;;;; +12164;CUNEIFORM SIGN KA TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; +12165;CUNEIFORM SIGN KA TIMES GAR;Lo;0;L;;;;;N;;;;; +12166;CUNEIFORM SIGN KA TIMES GAR PLUS SHA3 PLUS A;Lo;0;L;;;;;N;;;;; +12167;CUNEIFORM SIGN KA TIMES GI;Lo;0;L;;;;;N;;;;; +12168;CUNEIFORM SIGN KA TIMES GIR2;Lo;0;L;;;;;N;;;;; +12169;CUNEIFORM SIGN KA TIMES GISH PLUS SAR;Lo;0;L;;;;;N;;;;; +1216A;CUNEIFORM SIGN KA TIMES GISH CROSSING GISH;Lo;0;L;;;;;N;;;;; +1216B;CUNEIFORM SIGN KA TIMES GU;Lo;0;L;;;;;N;;;;; +1216C;CUNEIFORM SIGN KA TIMES GUR7;Lo;0;L;;;;;N;;;;; +1216D;CUNEIFORM SIGN KA TIMES IGI;Lo;0;L;;;;;N;;;;; +1216E;CUNEIFORM SIGN KA TIMES IM;Lo;0;L;;;;;N;;;;; +1216F;CUNEIFORM SIGN KA TIMES KAK;Lo;0;L;;;;;N;;;;; +12170;CUNEIFORM SIGN KA TIMES KI;Lo;0;L;;;;;N;;;;; +12171;CUNEIFORM SIGN KA TIMES KID;Lo;0;L;;;;;N;;;;; +12172;CUNEIFORM SIGN KA TIMES LI;Lo;0;L;;;;;N;;;;; +12173;CUNEIFORM SIGN KA TIMES LU;Lo;0;L;;;;;N;;;;; +12174;CUNEIFORM SIGN KA TIMES ME;Lo;0;L;;;;;N;;;;; +12175;CUNEIFORM SIGN KA TIMES ME PLUS DU;Lo;0;L;;;;;N;;;;; +12176;CUNEIFORM SIGN KA TIMES ME PLUS GI;Lo;0;L;;;;;N;;;;; +12177;CUNEIFORM SIGN KA TIMES ME PLUS TE;Lo;0;L;;;;;N;;;;; +12178;CUNEIFORM SIGN KA TIMES MI;Lo;0;L;;;;;N;;;;; +12179;CUNEIFORM SIGN KA TIMES MI PLUS NUNUZ;Lo;0;L;;;;;N;;;;; +1217A;CUNEIFORM SIGN KA TIMES NE;Lo;0;L;;;;;N;;;;; +1217B;CUNEIFORM SIGN KA TIMES NUN;Lo;0;L;;;;;N;;;;; +1217C;CUNEIFORM SIGN KA TIMES PI;Lo;0;L;;;;;N;;;;; +1217D;CUNEIFORM SIGN KA TIMES RU;Lo;0;L;;;;;N;;;;; +1217E;CUNEIFORM SIGN KA TIMES SA;Lo;0;L;;;;;N;;;;; +1217F;CUNEIFORM SIGN KA TIMES SAR;Lo;0;L;;;;;N;;;;; +12180;CUNEIFORM SIGN KA TIMES SHA;Lo;0;L;;;;;N;;;;; +12181;CUNEIFORM SIGN KA TIMES SHE;Lo;0;L;;;;;N;;;;; +12182;CUNEIFORM SIGN KA TIMES SHID;Lo;0;L;;;;;N;;;;; +12183;CUNEIFORM SIGN KA TIMES SHU;Lo;0;L;;;;;N;;;;; +12184;CUNEIFORM SIGN KA TIMES SIG;Lo;0;L;;;;;N;;;;; +12185;CUNEIFORM SIGN KA TIMES SUHUR;Lo;0;L;;;;;N;;;;; +12186;CUNEIFORM SIGN KA TIMES TAR;Lo;0;L;;;;;N;;;;; +12187;CUNEIFORM SIGN KA TIMES U;Lo;0;L;;;;;N;;;;; +12188;CUNEIFORM SIGN KA TIMES U2;Lo;0;L;;;;;N;;;;; +12189;CUNEIFORM SIGN KA TIMES UD;Lo;0;L;;;;;N;;;;; +1218A;CUNEIFORM SIGN KA TIMES UMUM TIMES PA;Lo;0;L;;;;;N;;;;; +1218B;CUNEIFORM SIGN KA TIMES USH;Lo;0;L;;;;;N;;;;; +1218C;CUNEIFORM SIGN KA TIMES ZI;Lo;0;L;;;;;N;;;;; +1218D;CUNEIFORM SIGN KA2;Lo;0;L;;;;;N;;;;; +1218E;CUNEIFORM SIGN KA2 CROSSING KA2;Lo;0;L;;;;;N;;;;; +1218F;CUNEIFORM SIGN KAB;Lo;0;L;;;;;N;;;;; +12190;CUNEIFORM SIGN KAD2;Lo;0;L;;;;;N;;;;; +12191;CUNEIFORM SIGN KAD3;Lo;0;L;;;;;N;;;;; +12192;CUNEIFORM SIGN KAD4;Lo;0;L;;;;;N;;;;; +12193;CUNEIFORM SIGN KAD5;Lo;0;L;;;;;N;;;;; +12194;CUNEIFORM SIGN KAD5 OVER KAD5;Lo;0;L;;;;;N;;;;; +12195;CUNEIFORM SIGN KAK;Lo;0;L;;;;;N;;;;; +12196;CUNEIFORM SIGN KAK TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +12197;CUNEIFORM SIGN KAL;Lo;0;L;;;;;N;;;;; +12198;CUNEIFORM SIGN KAL TIMES BAD;Lo;0;L;;;;;N;;;;; +12199;CUNEIFORM SIGN KAL CROSSING KAL;Lo;0;L;;;;;N;;;;; +1219A;CUNEIFORM SIGN KAM2;Lo;0;L;;;;;N;;;;; +1219B;CUNEIFORM SIGN KAM4;Lo;0;L;;;;;N;;;;; +1219C;CUNEIFORM SIGN KASKAL;Lo;0;L;;;;;N;;;;; +1219D;CUNEIFORM SIGN KASKAL LAGAB TIMES U OVER LAGAB TIMES U;Lo;0;L;;;;;N;;;;; +1219E;CUNEIFORM SIGN KASKAL OVER KASKAL LAGAB TIMES U OVER LAGAB TIMES U;Lo;0;L;;;;;N;;;;; +1219F;CUNEIFORM SIGN KESH2;Lo;0;L;;;;;N;;;;; +121A0;CUNEIFORM SIGN KI;Lo;0;L;;;;;N;;;;; +121A1;CUNEIFORM SIGN KI TIMES BAD;Lo;0;L;;;;;N;;;;; +121A2;CUNEIFORM SIGN KI TIMES U;Lo;0;L;;;;;N;;;;; +121A3;CUNEIFORM SIGN KI TIMES UD;Lo;0;L;;;;;N;;;;; +121A4;CUNEIFORM SIGN KID;Lo;0;L;;;;;N;;;;; +121A5;CUNEIFORM SIGN KIN;Lo;0;L;;;;;N;;;;; +121A6;CUNEIFORM SIGN KISAL;Lo;0;L;;;;;N;;;;; +121A7;CUNEIFORM SIGN KISH;Lo;0;L;;;;;N;;;;; +121A8;CUNEIFORM SIGN KISIM5;Lo;0;L;;;;;N;;;;; +121A9;CUNEIFORM SIGN KISIM5 OVER KISIM5;Lo;0;L;;;;;N;;;;; +121AA;CUNEIFORM SIGN KU;Lo;0;L;;;;;N;;;;; +121AB;CUNEIFORM SIGN KU OVER HI TIMES ASH2 KU OVER HI TIMES ASH2;Lo;0;L;;;;;N;;;;; +121AC;CUNEIFORM SIGN KU3;Lo;0;L;;;;;N;;;;; +121AD;CUNEIFORM SIGN KU4;Lo;0;L;;;;;N;;;;; +121AE;CUNEIFORM SIGN KU4 VARIANT FORM;Lo;0;L;;;;;N;;;;; +121AF;CUNEIFORM SIGN KU7;Lo;0;L;;;;;N;;;;; +121B0;CUNEIFORM SIGN KUL;Lo;0;L;;;;;N;;;;; +121B1;CUNEIFORM SIGN KUL GUNU;Lo;0;L;;;;;N;;;;; +121B2;CUNEIFORM SIGN KUN;Lo;0;L;;;;;N;;;;; +121B3;CUNEIFORM SIGN KUR;Lo;0;L;;;;;N;;;;; +121B4;CUNEIFORM SIGN KUR OPPOSING KUR;Lo;0;L;;;;;N;;;;; +121B5;CUNEIFORM SIGN KUSHU2;Lo;0;L;;;;;N;;;;; +121B6;CUNEIFORM SIGN KWU318;Lo;0;L;;;;;N;;;;; +121B7;CUNEIFORM SIGN LA;Lo;0;L;;;;;N;;;;; +121B8;CUNEIFORM SIGN LAGAB;Lo;0;L;;;;;N;;;;; +121B9;CUNEIFORM SIGN LAGAB TIMES A;Lo;0;L;;;;;N;;;;; +121BA;CUNEIFORM SIGN LAGAB TIMES A PLUS DA PLUS HA;Lo;0;L;;;;;N;;;;; +121BB;CUNEIFORM SIGN LAGAB TIMES A PLUS GAR;Lo;0;L;;;;;N;;;;; +121BC;CUNEIFORM SIGN LAGAB TIMES A PLUS LAL;Lo;0;L;;;;;N;;;;; +121BD;CUNEIFORM SIGN LAGAB TIMES AL;Lo;0;L;;;;;N;;;;; +121BE;CUNEIFORM SIGN LAGAB TIMES AN;Lo;0;L;;;;;N;;;;; +121BF;CUNEIFORM SIGN LAGAB TIMES ASH ZIDA TENU;Lo;0;L;;;;;N;;;;; +121C0;CUNEIFORM SIGN LAGAB TIMES BAD;Lo;0;L;;;;;N;;;;; +121C1;CUNEIFORM SIGN LAGAB TIMES BI;Lo;0;L;;;;;N;;;;; +121C2;CUNEIFORM SIGN LAGAB TIMES DAR;Lo;0;L;;;;;N;;;;; +121C3;CUNEIFORM SIGN LAGAB TIMES EN;Lo;0;L;;;;;N;;;;; +121C4;CUNEIFORM SIGN LAGAB TIMES GA;Lo;0;L;;;;;N;;;;; +121C5;CUNEIFORM SIGN LAGAB TIMES GAR;Lo;0;L;;;;;N;;;;; +121C6;CUNEIFORM SIGN LAGAB TIMES GUD;Lo;0;L;;;;;N;;;;; +121C7;CUNEIFORM SIGN LAGAB TIMES GUD PLUS GUD;Lo;0;L;;;;;N;;;;; +121C8;CUNEIFORM SIGN LAGAB TIMES HA;Lo;0;L;;;;;N;;;;; +121C9;CUNEIFORM SIGN LAGAB TIMES HAL;Lo;0;L;;;;;N;;;;; +121CA;CUNEIFORM SIGN LAGAB TIMES HI TIMES NUN;Lo;0;L;;;;;N;;;;; +121CB;CUNEIFORM SIGN LAGAB TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +121CC;CUNEIFORM SIGN LAGAB TIMES IM;Lo;0;L;;;;;N;;;;; +121CD;CUNEIFORM SIGN LAGAB TIMES IM PLUS HA;Lo;0;L;;;;;N;;;;; +121CE;CUNEIFORM SIGN LAGAB TIMES IM PLUS LU;Lo;0;L;;;;;N;;;;; +121CF;CUNEIFORM SIGN LAGAB TIMES KI;Lo;0;L;;;;;N;;;;; +121D0;CUNEIFORM SIGN LAGAB TIMES KIN;Lo;0;L;;;;;N;;;;; +121D1;CUNEIFORM SIGN LAGAB TIMES KU3;Lo;0;L;;;;;N;;;;; +121D2;CUNEIFORM SIGN LAGAB TIMES KUL;Lo;0;L;;;;;N;;;;; +121D3;CUNEIFORM SIGN LAGAB TIMES KUL PLUS HI PLUS A;Lo;0;L;;;;;N;;;;; +121D4;CUNEIFORM SIGN LAGAB TIMES LAGAB;Lo;0;L;;;;;N;;;;; +121D5;CUNEIFORM SIGN LAGAB TIMES LISH;Lo;0;L;;;;;N;;;;; +121D6;CUNEIFORM SIGN LAGAB TIMES LU;Lo;0;L;;;;;N;;;;; +121D7;CUNEIFORM SIGN LAGAB TIMES LUL;Lo;0;L;;;;;N;;;;; +121D8;CUNEIFORM SIGN LAGAB TIMES ME;Lo;0;L;;;;;N;;;;; +121D9;CUNEIFORM SIGN LAGAB TIMES ME PLUS EN;Lo;0;L;;;;;N;;;;; +121DA;CUNEIFORM SIGN LAGAB TIMES MUSH;Lo;0;L;;;;;N;;;;; +121DB;CUNEIFORM SIGN LAGAB TIMES NE;Lo;0;L;;;;;N;;;;; +121DC;CUNEIFORM SIGN LAGAB TIMES SHE PLUS SUM;Lo;0;L;;;;;N;;;;; +121DD;CUNEIFORM SIGN LAGAB TIMES SHITA PLUS GISH PLUS ERIN2;Lo;0;L;;;;;N;;;;; +121DE;CUNEIFORM SIGN LAGAB TIMES SHITA PLUS GISH TENU;Lo;0;L;;;;;N;;;;; +121DF;CUNEIFORM SIGN LAGAB TIMES SHU2;Lo;0;L;;;;;N;;;;; +121E0;CUNEIFORM SIGN LAGAB TIMES SHU2 PLUS SHU2;Lo;0;L;;;;;N;;;;; +121E1;CUNEIFORM SIGN LAGAB TIMES SUM;Lo;0;L;;;;;N;;;;; +121E2;CUNEIFORM SIGN LAGAB TIMES TAG;Lo;0;L;;;;;N;;;;; +121E3;CUNEIFORM SIGN LAGAB TIMES TAK4;Lo;0;L;;;;;N;;;;; +121E4;CUNEIFORM SIGN LAGAB TIMES TE PLUS A PLUS SU PLUS NA;Lo;0;L;;;;;N;;;;; +121E5;CUNEIFORM SIGN LAGAB TIMES U;Lo;0;L;;;;;N;;;;; +121E6;CUNEIFORM SIGN LAGAB TIMES U PLUS A;Lo;0;L;;;;;N;;;;; +121E7;CUNEIFORM SIGN LAGAB TIMES U PLUS U PLUS U;Lo;0;L;;;;;N;;;;; +121E8;CUNEIFORM SIGN LAGAB TIMES U2 PLUS ASH;Lo;0;L;;;;;N;;;;; +121E9;CUNEIFORM SIGN LAGAB TIMES UD;Lo;0;L;;;;;N;;;;; +121EA;CUNEIFORM SIGN LAGAB TIMES USH;Lo;0;L;;;;;N;;;;; +121EB;CUNEIFORM SIGN LAGAB SQUARED;Lo;0;L;;;;;N;;;;; +121EC;CUNEIFORM SIGN LAGAR;Lo;0;L;;;;;N;;;;; +121ED;CUNEIFORM SIGN LAGAR TIMES SHE;Lo;0;L;;;;;N;;;;; +121EE;CUNEIFORM SIGN LAGAR TIMES SHE PLUS SUM;Lo;0;L;;;;;N;;;;; +121EF;CUNEIFORM SIGN LAGAR GUNU;Lo;0;L;;;;;N;;;;; +121F0;CUNEIFORM SIGN LAGAR GUNU OVER LAGAR GUNU SHE;Lo;0;L;;;;;N;;;;; +121F1;CUNEIFORM SIGN LAHSHU;Lo;0;L;;;;;N;;;;; +121F2;CUNEIFORM SIGN LAL;Lo;0;L;;;;;N;;;;; +121F3;CUNEIFORM SIGN LAL TIMES LAL;Lo;0;L;;;;;N;;;;; +121F4;CUNEIFORM SIGN LAM;Lo;0;L;;;;;N;;;;; +121F5;CUNEIFORM SIGN LAM TIMES KUR;Lo;0;L;;;;;N;;;;; +121F6;CUNEIFORM SIGN LAM TIMES KUR PLUS RU;Lo;0;L;;;;;N;;;;; +121F7;CUNEIFORM SIGN LI;Lo;0;L;;;;;N;;;;; +121F8;CUNEIFORM SIGN LIL;Lo;0;L;;;;;N;;;;; +121F9;CUNEIFORM SIGN LIMMU2;Lo;0;L;;;;;N;;;;; +121FA;CUNEIFORM SIGN LISH;Lo;0;L;;;;;N;;;;; +121FB;CUNEIFORM SIGN LU;Lo;0;L;;;;;N;;;;; +121FC;CUNEIFORM SIGN LU TIMES BAD;Lo;0;L;;;;;N;;;;; +121FD;CUNEIFORM SIGN LU2;Lo;0;L;;;;;N;;;;; +121FE;CUNEIFORM SIGN LU2 TIMES AL;Lo;0;L;;;;;N;;;;; +121FF;CUNEIFORM SIGN LU2 TIMES BAD;Lo;0;L;;;;;N;;;;; +12200;CUNEIFORM SIGN LU2 TIMES ESH2;Lo;0;L;;;;;N;;;;; +12201;CUNEIFORM SIGN LU2 TIMES ESH2 TENU;Lo;0;L;;;;;N;;;;; +12202;CUNEIFORM SIGN LU2 TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; +12203;CUNEIFORM SIGN LU2 TIMES HI TIMES BAD;Lo;0;L;;;;;N;;;;; +12204;CUNEIFORM SIGN LU2 TIMES IM;Lo;0;L;;;;;N;;;;; +12205;CUNEIFORM SIGN LU2 TIMES KAD2;Lo;0;L;;;;;N;;;;; +12206;CUNEIFORM SIGN LU2 TIMES KAD3;Lo;0;L;;;;;N;;;;; +12207;CUNEIFORM SIGN LU2 TIMES KAD3 PLUS ASH;Lo;0;L;;;;;N;;;;; +12208;CUNEIFORM SIGN LU2 TIMES KI;Lo;0;L;;;;;N;;;;; +12209;CUNEIFORM SIGN LU2 TIMES LA PLUS ASH;Lo;0;L;;;;;N;;;;; +1220A;CUNEIFORM SIGN LU2 TIMES LAGAB;Lo;0;L;;;;;N;;;;; +1220B;CUNEIFORM SIGN LU2 TIMES ME PLUS EN;Lo;0;L;;;;;N;;;;; +1220C;CUNEIFORM SIGN LU2 TIMES NE;Lo;0;L;;;;;N;;;;; +1220D;CUNEIFORM SIGN LU2 TIMES NU;Lo;0;L;;;;;N;;;;; +1220E;CUNEIFORM SIGN LU2 TIMES SI PLUS ASH;Lo;0;L;;;;;N;;;;; +1220F;CUNEIFORM SIGN LU2 TIMES SIK2 PLUS BU;Lo;0;L;;;;;N;;;;; +12210;CUNEIFORM SIGN LU2 TIMES TUG2;Lo;0;L;;;;;N;;;;; +12211;CUNEIFORM SIGN LU2 TENU;Lo;0;L;;;;;N;;;;; +12212;CUNEIFORM SIGN LU2 CROSSING LU2;Lo;0;L;;;;;N;;;;; +12213;CUNEIFORM SIGN LU2 OPPOSING LU2;Lo;0;L;;;;;N;;;;; +12214;CUNEIFORM SIGN LU2 SQUARED;Lo;0;L;;;;;N;;;;; +12215;CUNEIFORM SIGN LU2 SHESHIG;Lo;0;L;;;;;N;;;;; +12216;CUNEIFORM SIGN LU3;Lo;0;L;;;;;N;;;;; +12217;CUNEIFORM SIGN LUGAL;Lo;0;L;;;;;N;;;;; +12218;CUNEIFORM SIGN LUGAL OVER LUGAL;Lo;0;L;;;;;N;;;;; +12219;CUNEIFORM SIGN LUGAL OPPOSING LUGAL;Lo;0;L;;;;;N;;;;; +1221A;CUNEIFORM SIGN LUGAL SHESHIG;Lo;0;L;;;;;N;;;;; +1221B;CUNEIFORM SIGN LUH;Lo;0;L;;;;;N;;;;; +1221C;CUNEIFORM SIGN LUL;Lo;0;L;;;;;N;;;;; +1221D;CUNEIFORM SIGN LUM;Lo;0;L;;;;;N;;;;; +1221E;CUNEIFORM SIGN LUM OVER LUM;Lo;0;L;;;;;N;;;;; +1221F;CUNEIFORM SIGN LUM OVER LUM GAR OVER GAR;Lo;0;L;;;;;N;;;;; +12220;CUNEIFORM SIGN MA;Lo;0;L;;;;;N;;;;; +12221;CUNEIFORM SIGN MA TIMES TAK4;Lo;0;L;;;;;N;;;;; +12222;CUNEIFORM SIGN MA GUNU;Lo;0;L;;;;;N;;;;; +12223;CUNEIFORM SIGN MA2;Lo;0;L;;;;;N;;;;; +12224;CUNEIFORM SIGN MAH;Lo;0;L;;;;;N;;;;; +12225;CUNEIFORM SIGN MAR;Lo;0;L;;;;;N;;;;; +12226;CUNEIFORM SIGN MASH;Lo;0;L;;;;;N;;;;; +12227;CUNEIFORM SIGN MASH2;Lo;0;L;;;;;N;;;;; +12228;CUNEIFORM SIGN ME;Lo;0;L;;;;;N;;;;; +12229;CUNEIFORM SIGN MES;Lo;0;L;;;;;N;;;;; +1222A;CUNEIFORM SIGN MI;Lo;0;L;;;;;N;;;;; +1222B;CUNEIFORM SIGN MIN;Lo;0;L;;;;;N;;;;; +1222C;CUNEIFORM SIGN MU;Lo;0;L;;;;;N;;;;; +1222D;CUNEIFORM SIGN MU OVER MU;Lo;0;L;;;;;N;;;;; +1222E;CUNEIFORM SIGN MUG;Lo;0;L;;;;;N;;;;; +1222F;CUNEIFORM SIGN MUG GUNU;Lo;0;L;;;;;N;;;;; +12230;CUNEIFORM SIGN MUNSUB;Lo;0;L;;;;;N;;;;; +12231;CUNEIFORM SIGN MURGU2;Lo;0;L;;;;;N;;;;; +12232;CUNEIFORM SIGN MUSH;Lo;0;L;;;;;N;;;;; +12233;CUNEIFORM SIGN MUSH TIMES A;Lo;0;L;;;;;N;;;;; +12234;CUNEIFORM SIGN MUSH TIMES KUR;Lo;0;L;;;;;N;;;;; +12235;CUNEIFORM SIGN MUSH TIMES ZA;Lo;0;L;;;;;N;;;;; +12236;CUNEIFORM SIGN MUSH OVER MUSH;Lo;0;L;;;;;N;;;;; +12237;CUNEIFORM SIGN MUSH OVER MUSH TIMES A PLUS NA;Lo;0;L;;;;;N;;;;; +12238;CUNEIFORM SIGN MUSH CROSSING MUSH;Lo;0;L;;;;;N;;;;; +12239;CUNEIFORM SIGN MUSH3;Lo;0;L;;;;;N;;;;; +1223A;CUNEIFORM SIGN MUSH3 TIMES A;Lo;0;L;;;;;N;;;;; +1223B;CUNEIFORM SIGN MUSH3 TIMES A PLUS DI;Lo;0;L;;;;;N;;;;; +1223C;CUNEIFORM SIGN MUSH3 TIMES DI;Lo;0;L;;;;;N;;;;; +1223D;CUNEIFORM SIGN MUSH3 GUNU;Lo;0;L;;;;;N;;;;; +1223E;CUNEIFORM SIGN NA;Lo;0;L;;;;;N;;;;; +1223F;CUNEIFORM SIGN NA2;Lo;0;L;;;;;N;;;;; +12240;CUNEIFORM SIGN NAGA;Lo;0;L;;;;;N;;;;; +12241;CUNEIFORM SIGN NAGA INVERTED;Lo;0;L;;;;;N;;;;; +12242;CUNEIFORM SIGN NAGA TIMES SHU TENU;Lo;0;L;;;;;N;;;;; +12243;CUNEIFORM SIGN NAGA OPPOSING NAGA;Lo;0;L;;;;;N;;;;; +12244;CUNEIFORM SIGN NAGAR;Lo;0;L;;;;;N;;;;; +12245;CUNEIFORM SIGN NAM NUTILLU;Lo;0;L;;;;;N;;;;; +12246;CUNEIFORM SIGN NAM;Lo;0;L;;;;;N;;;;; +12247;CUNEIFORM SIGN NAM2;Lo;0;L;;;;;N;;;;; +12248;CUNEIFORM SIGN NE;Lo;0;L;;;;;N;;;;; +12249;CUNEIFORM SIGN NE TIMES A;Lo;0;L;;;;;N;;;;; +1224A;CUNEIFORM SIGN NE TIMES UD;Lo;0;L;;;;;N;;;;; +1224B;CUNEIFORM SIGN NE SHESHIG;Lo;0;L;;;;;N;;;;; +1224C;CUNEIFORM SIGN NI;Lo;0;L;;;;;N;;;;; +1224D;CUNEIFORM SIGN NI TIMES E;Lo;0;L;;;;;N;;;;; +1224E;CUNEIFORM SIGN NI2;Lo;0;L;;;;;N;;;;; +1224F;CUNEIFORM SIGN NIM;Lo;0;L;;;;;N;;;;; +12250;CUNEIFORM SIGN NIM TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; +12251;CUNEIFORM SIGN NIM TIMES GAR PLUS GAN2 TENU;Lo;0;L;;;;;N;;;;; +12252;CUNEIFORM SIGN NINDA2;Lo;0;L;;;;;N;;;;; +12253;CUNEIFORM SIGN NINDA2 TIMES AN;Lo;0;L;;;;;N;;;;; +12254;CUNEIFORM SIGN NINDA2 TIMES ASH;Lo;0;L;;;;;N;;;;; +12255;CUNEIFORM SIGN NINDA2 TIMES ASH PLUS ASH;Lo;0;L;;;;;N;;;;; +12256;CUNEIFORM SIGN NINDA2 TIMES GUD;Lo;0;L;;;;;N;;;;; +12257;CUNEIFORM SIGN NINDA2 TIMES ME PLUS GAN2 TENU;Lo;0;L;;;;;N;;;;; +12258;CUNEIFORM SIGN NINDA2 TIMES NE;Lo;0;L;;;;;N;;;;; +12259;CUNEIFORM SIGN NINDA2 TIMES NUN;Lo;0;L;;;;;N;;;;; +1225A;CUNEIFORM SIGN NINDA2 TIMES SHE;Lo;0;L;;;;;N;;;;; +1225B;CUNEIFORM SIGN NINDA2 TIMES SHE PLUS A AN;Lo;0;L;;;;;N;;;;; +1225C;CUNEIFORM SIGN NINDA2 TIMES SHE PLUS ASH;Lo;0;L;;;;;N;;;;; +1225D;CUNEIFORM SIGN NINDA2 TIMES SHE PLUS ASH PLUS ASH;Lo;0;L;;;;;N;;;;; +1225E;CUNEIFORM SIGN NINDA2 TIMES U2 PLUS ASH;Lo;0;L;;;;;N;;;;; +1225F;CUNEIFORM SIGN NINDA2 TIMES USH;Lo;0;L;;;;;N;;;;; +12260;CUNEIFORM SIGN NISAG;Lo;0;L;;;;;N;;;;; +12261;CUNEIFORM SIGN NU;Lo;0;L;;;;;N;;;;; +12262;CUNEIFORM SIGN NU11;Lo;0;L;;;;;N;;;;; +12263;CUNEIFORM SIGN NUN;Lo;0;L;;;;;N;;;;; +12264;CUNEIFORM SIGN NUN LAGAR TIMES GAR;Lo;0;L;;;;;N;;;;; +12265;CUNEIFORM SIGN NUN LAGAR TIMES MASH;Lo;0;L;;;;;N;;;;; +12266;CUNEIFORM SIGN NUN LAGAR TIMES SAL;Lo;0;L;;;;;N;;;;; +12267;CUNEIFORM SIGN NUN LAGAR TIMES SAL OVER NUN LAGAR TIMES SAL;Lo;0;L;;;;;N;;;;; +12268;CUNEIFORM SIGN NUN LAGAR TIMES USH;Lo;0;L;;;;;N;;;;; +12269;CUNEIFORM SIGN NUN TENU;Lo;0;L;;;;;N;;;;; +1226A;CUNEIFORM SIGN NUN OVER NUN;Lo;0;L;;;;;N;;;;; +1226B;CUNEIFORM SIGN NUN CROSSING NUN;Lo;0;L;;;;;N;;;;; +1226C;CUNEIFORM SIGN NUN CROSSING NUN LAGAR OVER LAGAR;Lo;0;L;;;;;N;;;;; +1226D;CUNEIFORM SIGN NUNUZ;Lo;0;L;;;;;N;;;;; +1226E;CUNEIFORM SIGN NUNUZ AB2 TIMES ASHGAB;Lo;0;L;;;;;N;;;;; +1226F;CUNEIFORM SIGN NUNUZ AB2 TIMES BI;Lo;0;L;;;;;N;;;;; +12270;CUNEIFORM SIGN NUNUZ AB2 TIMES DUG;Lo;0;L;;;;;N;;;;; +12271;CUNEIFORM SIGN NUNUZ AB2 TIMES GUD;Lo;0;L;;;;;N;;;;; +12272;CUNEIFORM SIGN NUNUZ AB2 TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +12273;CUNEIFORM SIGN NUNUZ AB2 TIMES KAD3;Lo;0;L;;;;;N;;;;; +12274;CUNEIFORM SIGN NUNUZ AB2 TIMES LA;Lo;0;L;;;;;N;;;;; +12275;CUNEIFORM SIGN NUNUZ AB2 TIMES NE;Lo;0;L;;;;;N;;;;; +12276;CUNEIFORM SIGN NUNUZ AB2 TIMES SILA3;Lo;0;L;;;;;N;;;;; +12277;CUNEIFORM SIGN NUNUZ AB2 TIMES U2;Lo;0;L;;;;;N;;;;; +12278;CUNEIFORM SIGN NUNUZ KISIM5 TIMES BI;Lo;0;L;;;;;N;;;;; +12279;CUNEIFORM SIGN NUNUZ KISIM5 TIMES BI U;Lo;0;L;;;;;N;;;;; +1227A;CUNEIFORM SIGN PA;Lo;0;L;;;;;N;;;;; +1227B;CUNEIFORM SIGN PAD;Lo;0;L;;;;;N;;;;; +1227C;CUNEIFORM SIGN PAN;Lo;0;L;;;;;N;;;;; +1227D;CUNEIFORM SIGN PAP;Lo;0;L;;;;;N;;;;; +1227E;CUNEIFORM SIGN PESH2;Lo;0;L;;;;;N;;;;; +1227F;CUNEIFORM SIGN PI;Lo;0;L;;;;;N;;;;; +12280;CUNEIFORM SIGN PI TIMES A;Lo;0;L;;;;;N;;;;; +12281;CUNEIFORM SIGN PI TIMES AB;Lo;0;L;;;;;N;;;;; +12282;CUNEIFORM SIGN PI TIMES BI;Lo;0;L;;;;;N;;;;; +12283;CUNEIFORM SIGN PI TIMES BU;Lo;0;L;;;;;N;;;;; +12284;CUNEIFORM SIGN PI TIMES E;Lo;0;L;;;;;N;;;;; +12285;CUNEIFORM SIGN PI TIMES I;Lo;0;L;;;;;N;;;;; +12286;CUNEIFORM SIGN PI TIMES IB;Lo;0;L;;;;;N;;;;; +12287;CUNEIFORM SIGN PI TIMES U;Lo;0;L;;;;;N;;;;; +12288;CUNEIFORM SIGN PI TIMES U2;Lo;0;L;;;;;N;;;;; +12289;CUNEIFORM SIGN PI CROSSING PI;Lo;0;L;;;;;N;;;;; +1228A;CUNEIFORM SIGN PIRIG;Lo;0;L;;;;;N;;;;; +1228B;CUNEIFORM SIGN PIRIG TIMES KAL;Lo;0;L;;;;;N;;;;; +1228C;CUNEIFORM SIGN PIRIG TIMES UD;Lo;0;L;;;;;N;;;;; +1228D;CUNEIFORM SIGN PIRIG TIMES ZA;Lo;0;L;;;;;N;;;;; +1228E;CUNEIFORM SIGN PIRIG OPPOSING PIRIG;Lo;0;L;;;;;N;;;;; +1228F;CUNEIFORM SIGN RA;Lo;0;L;;;;;N;;;;; +12290;CUNEIFORM SIGN RAB;Lo;0;L;;;;;N;;;;; +12291;CUNEIFORM SIGN RI;Lo;0;L;;;;;N;;;;; +12292;CUNEIFORM SIGN RU;Lo;0;L;;;;;N;;;;; +12293;CUNEIFORM SIGN SA;Lo;0;L;;;;;N;;;;; +12294;CUNEIFORM SIGN SAG NUTILLU;Lo;0;L;;;;;N;;;;; +12295;CUNEIFORM SIGN SAG;Lo;0;L;;;;;N;;;;; +12296;CUNEIFORM SIGN SAG TIMES A;Lo;0;L;;;;;N;;;;; +12297;CUNEIFORM SIGN SAG TIMES DU;Lo;0;L;;;;;N;;;;; +12298;CUNEIFORM SIGN SAG TIMES DUB;Lo;0;L;;;;;N;;;;; +12299;CUNEIFORM SIGN SAG TIMES HA;Lo;0;L;;;;;N;;;;; +1229A;CUNEIFORM SIGN SAG TIMES KAK;Lo;0;L;;;;;N;;;;; +1229B;CUNEIFORM SIGN SAG TIMES KUR;Lo;0;L;;;;;N;;;;; +1229C;CUNEIFORM SIGN SAG TIMES LUM;Lo;0;L;;;;;N;;;;; +1229D;CUNEIFORM SIGN SAG TIMES MI;Lo;0;L;;;;;N;;;;; +1229E;CUNEIFORM SIGN SAG TIMES NUN;Lo;0;L;;;;;N;;;;; +1229F;CUNEIFORM SIGN SAG TIMES SAL;Lo;0;L;;;;;N;;;;; +122A0;CUNEIFORM SIGN SAG TIMES SHID;Lo;0;L;;;;;N;;;;; +122A1;CUNEIFORM SIGN SAG TIMES TAB;Lo;0;L;;;;;N;;;;; +122A2;CUNEIFORM SIGN SAG TIMES U2;Lo;0;L;;;;;N;;;;; +122A3;CUNEIFORM SIGN SAG TIMES UB;Lo;0;L;;;;;N;;;;; +122A4;CUNEIFORM SIGN SAG TIMES UM;Lo;0;L;;;;;N;;;;; +122A5;CUNEIFORM SIGN SAG TIMES UR;Lo;0;L;;;;;N;;;;; +122A6;CUNEIFORM SIGN SAG TIMES USH;Lo;0;L;;;;;N;;;;; +122A7;CUNEIFORM SIGN SAG OVER SAG;Lo;0;L;;;;;N;;;;; +122A8;CUNEIFORM SIGN SAG GUNU;Lo;0;L;;;;;N;;;;; +122A9;CUNEIFORM SIGN SAL;Lo;0;L;;;;;N;;;;; +122AA;CUNEIFORM SIGN SAL LAGAB TIMES ASH2;Lo;0;L;;;;;N;;;;; +122AB;CUNEIFORM SIGN SANGA2;Lo;0;L;;;;;N;;;;; +122AC;CUNEIFORM SIGN SAR;Lo;0;L;;;;;N;;;;; +122AD;CUNEIFORM SIGN SHA;Lo;0;L;;;;;N;;;;; +122AE;CUNEIFORM SIGN SHA3;Lo;0;L;;;;;N;;;;; +122AF;CUNEIFORM SIGN SHA3 TIMES A;Lo;0;L;;;;;N;;;;; +122B0;CUNEIFORM SIGN SHA3 TIMES BAD;Lo;0;L;;;;;N;;;;; +122B1;CUNEIFORM SIGN SHA3 TIMES GISH;Lo;0;L;;;;;N;;;;; +122B2;CUNEIFORM SIGN SHA3 TIMES NE;Lo;0;L;;;;;N;;;;; +122B3;CUNEIFORM SIGN SHA3 TIMES SHU2;Lo;0;L;;;;;N;;;;; +122B4;CUNEIFORM SIGN SHA3 TIMES TUR;Lo;0;L;;;;;N;;;;; +122B5;CUNEIFORM SIGN SHA3 TIMES U;Lo;0;L;;;;;N;;;;; +122B6;CUNEIFORM SIGN SHA3 TIMES U PLUS A;Lo;0;L;;;;;N;;;;; +122B7;CUNEIFORM SIGN SHA6;Lo;0;L;;;;;N;;;;; +122B8;CUNEIFORM SIGN SHAB6;Lo;0;L;;;;;N;;;;; +122B9;CUNEIFORM SIGN SHAR2;Lo;0;L;;;;;N;;;;; +122BA;CUNEIFORM SIGN SHE;Lo;0;L;;;;;N;;;;; +122BB;CUNEIFORM SIGN SHE HU;Lo;0;L;;;;;N;;;;; +122BC;CUNEIFORM SIGN SHE OVER SHE GAD OVER GAD GAR OVER GAR;Lo;0;L;;;;;N;;;;; +122BD;CUNEIFORM SIGN SHE OVER SHE TAB OVER TAB GAR OVER GAR;Lo;0;L;;;;;N;;;;; +122BE;CUNEIFORM SIGN SHEG9;Lo;0;L;;;;;N;;;;; +122BF;CUNEIFORM SIGN SHEN;Lo;0;L;;;;;N;;;;; +122C0;CUNEIFORM SIGN SHESH;Lo;0;L;;;;;N;;;;; +122C1;CUNEIFORM SIGN SHESH2;Lo;0;L;;;;;N;;;;; +122C2;CUNEIFORM SIGN SHESHLAM;Lo;0;L;;;;;N;;;;; +122C3;CUNEIFORM SIGN SHID;Lo;0;L;;;;;N;;;;; +122C4;CUNEIFORM SIGN SHID TIMES A;Lo;0;L;;;;;N;;;;; +122C5;CUNEIFORM SIGN SHID TIMES IM;Lo;0;L;;;;;N;;;;; +122C6;CUNEIFORM SIGN SHIM;Lo;0;L;;;;;N;;;;; +122C7;CUNEIFORM SIGN SHIM TIMES A;Lo;0;L;;;;;N;;;;; +122C8;CUNEIFORM SIGN SHIM TIMES BAL;Lo;0;L;;;;;N;;;;; +122C9;CUNEIFORM SIGN SHIM TIMES BULUG;Lo;0;L;;;;;N;;;;; +122CA;CUNEIFORM SIGN SHIM TIMES DIN;Lo;0;L;;;;;N;;;;; +122CB;CUNEIFORM SIGN SHIM TIMES GAR;Lo;0;L;;;;;N;;;;; +122CC;CUNEIFORM SIGN SHIM TIMES IGI;Lo;0;L;;;;;N;;;;; +122CD;CUNEIFORM SIGN SHIM TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +122CE;CUNEIFORM SIGN SHIM TIMES KUSHU2;Lo;0;L;;;;;N;;;;; +122CF;CUNEIFORM SIGN SHIM TIMES LUL;Lo;0;L;;;;;N;;;;; +122D0;CUNEIFORM SIGN SHIM TIMES MUG;Lo;0;L;;;;;N;;;;; +122D1;CUNEIFORM SIGN SHIM TIMES SAL;Lo;0;L;;;;;N;;;;; +122D2;CUNEIFORM SIGN SHINIG;Lo;0;L;;;;;N;;;;; +122D3;CUNEIFORM SIGN SHIR;Lo;0;L;;;;;N;;;;; +122D4;CUNEIFORM SIGN SHIR TENU;Lo;0;L;;;;;N;;;;; +122D5;CUNEIFORM SIGN SHIR OVER SHIR BUR OVER BUR;Lo;0;L;;;;;N;;;;; +122D6;CUNEIFORM SIGN SHITA;Lo;0;L;;;;;N;;;;; +122D7;CUNEIFORM SIGN SHU;Lo;0;L;;;;;N;;;;; +122D8;CUNEIFORM SIGN SHU OVER INVERTED SHU;Lo;0;L;;;;;N;;;;; +122D9;CUNEIFORM SIGN SHU2;Lo;0;L;;;;;N;;;;; +122DA;CUNEIFORM SIGN SHUBUR;Lo;0;L;;;;;N;;;;; +122DB;CUNEIFORM SIGN SI;Lo;0;L;;;;;N;;;;; +122DC;CUNEIFORM SIGN SI GUNU;Lo;0;L;;;;;N;;;;; +122DD;CUNEIFORM SIGN SIG;Lo;0;L;;;;;N;;;;; +122DE;CUNEIFORM SIGN SIG4;Lo;0;L;;;;;N;;;;; +122DF;CUNEIFORM SIGN SIG4 OVER SIG4 SHU2;Lo;0;L;;;;;N;;;;; +122E0;CUNEIFORM SIGN SIK2;Lo;0;L;;;;;N;;;;; +122E1;CUNEIFORM SIGN SILA3;Lo;0;L;;;;;N;;;;; +122E2;CUNEIFORM SIGN SU;Lo;0;L;;;;;N;;;;; +122E3;CUNEIFORM SIGN SU OVER SU;Lo;0;L;;;;;N;;;;; +122E4;CUNEIFORM SIGN SUD;Lo;0;L;;;;;N;;;;; +122E5;CUNEIFORM SIGN SUD2;Lo;0;L;;;;;N;;;;; +122E6;CUNEIFORM SIGN SUHUR;Lo;0;L;;;;;N;;;;; +122E7;CUNEIFORM SIGN SUM;Lo;0;L;;;;;N;;;;; +122E8;CUNEIFORM SIGN SUMASH;Lo;0;L;;;;;N;;;;; +122E9;CUNEIFORM SIGN SUR;Lo;0;L;;;;;N;;;;; +122EA;CUNEIFORM SIGN SUR9;Lo;0;L;;;;;N;;;;; +122EB;CUNEIFORM SIGN TA;Lo;0;L;;;;;N;;;;; +122EC;CUNEIFORM SIGN TA ASTERISK;Lo;0;L;;;;;N;;;;; +122ED;CUNEIFORM SIGN TA TIMES HI;Lo;0;L;;;;;N;;;;; +122EE;CUNEIFORM SIGN TA TIMES MI;Lo;0;L;;;;;N;;;;; +122EF;CUNEIFORM SIGN TA GUNU;Lo;0;L;;;;;N;;;;; +122F0;CUNEIFORM SIGN TAB;Lo;0;L;;;;;N;;;;; +122F1;CUNEIFORM SIGN TAB OVER TAB NI OVER NI DISH OVER DISH;Lo;0;L;;;;;N;;;;; +122F2;CUNEIFORM SIGN TAB SQUARED;Lo;0;L;;;;;N;;;;; +122F3;CUNEIFORM SIGN TAG;Lo;0;L;;;;;N;;;;; +122F4;CUNEIFORM SIGN TAG TIMES BI;Lo;0;L;;;;;N;;;;; +122F5;CUNEIFORM SIGN TAG TIMES GUD;Lo;0;L;;;;;N;;;;; +122F6;CUNEIFORM SIGN TAG TIMES SHE;Lo;0;L;;;;;N;;;;; +122F7;CUNEIFORM SIGN TAG TIMES SHU;Lo;0;L;;;;;N;;;;; +122F8;CUNEIFORM SIGN TAG TIMES TUG2;Lo;0;L;;;;;N;;;;; +122F9;CUNEIFORM SIGN TAG TIMES UD;Lo;0;L;;;;;N;;;;; +122FA;CUNEIFORM SIGN TAK4;Lo;0;L;;;;;N;;;;; +122FB;CUNEIFORM SIGN TAR;Lo;0;L;;;;;N;;;;; +122FC;CUNEIFORM SIGN TE;Lo;0;L;;;;;N;;;;; +122FD;CUNEIFORM SIGN TE GUNU;Lo;0;L;;;;;N;;;;; +122FE;CUNEIFORM SIGN TI;Lo;0;L;;;;;N;;;;; +122FF;CUNEIFORM SIGN TI TENU;Lo;0;L;;;;;N;;;;; +12300;CUNEIFORM SIGN TIL;Lo;0;L;;;;;N;;;;; +12301;CUNEIFORM SIGN TIR;Lo;0;L;;;;;N;;;;; +12302;CUNEIFORM SIGN TIR TIMES TAK4;Lo;0;L;;;;;N;;;;; +12303;CUNEIFORM SIGN TIR OVER TIR;Lo;0;L;;;;;N;;;;; +12304;CUNEIFORM SIGN TIR OVER TIR GAD OVER GAD GAR OVER GAR;Lo;0;L;;;;;N;;;;; +12305;CUNEIFORM SIGN TU;Lo;0;L;;;;;N;;;;; +12306;CUNEIFORM SIGN TUG2;Lo;0;L;;;;;N;;;;; +12307;CUNEIFORM SIGN TUK;Lo;0;L;;;;;N;;;;; +12308;CUNEIFORM SIGN TUM;Lo;0;L;;;;;N;;;;; +12309;CUNEIFORM SIGN TUR;Lo;0;L;;;;;N;;;;; +1230A;CUNEIFORM SIGN TUR OVER TUR ZA OVER ZA;Lo;0;L;;;;;N;;;;; +1230B;CUNEIFORM SIGN U;Lo;0;L;;;;;N;;;;; +1230C;CUNEIFORM SIGN U GUD;Lo;0;L;;;;;N;;;;; +1230D;CUNEIFORM SIGN U U U;Lo;0;L;;;;;N;;;;; +1230E;CUNEIFORM SIGN U OVER U PA OVER PA GAR OVER GAR;Lo;0;L;;;;;N;;;;; +1230F;CUNEIFORM SIGN U OVER U SUR OVER SUR;Lo;0;L;;;;;N;;;;; +12310;CUNEIFORM SIGN U OVER U U REVERSED OVER U REVERSED;Lo;0;L;;;;;N;;;;; +12311;CUNEIFORM SIGN U2;Lo;0;L;;;;;N;;;;; +12312;CUNEIFORM SIGN UB;Lo;0;L;;;;;N;;;;; +12313;CUNEIFORM SIGN UD;Lo;0;L;;;;;N;;;;; +12314;CUNEIFORM SIGN UD KUSHU2;Lo;0;L;;;;;N;;;;; +12315;CUNEIFORM SIGN UD TIMES BAD;Lo;0;L;;;;;N;;;;; +12316;CUNEIFORM SIGN UD TIMES MI;Lo;0;L;;;;;N;;;;; +12317;CUNEIFORM SIGN UD TIMES U PLUS U PLUS U;Lo;0;L;;;;;N;;;;; +12318;CUNEIFORM SIGN UD TIMES U PLUS U PLUS U GUNU;Lo;0;L;;;;;N;;;;; +12319;CUNEIFORM SIGN UD GUNU;Lo;0;L;;;;;N;;;;; +1231A;CUNEIFORM SIGN UD SHESHIG;Lo;0;L;;;;;N;;;;; +1231B;CUNEIFORM SIGN UD SHESHIG TIMES BAD;Lo;0;L;;;;;N;;;;; +1231C;CUNEIFORM SIGN UDUG;Lo;0;L;;;;;N;;;;; +1231D;CUNEIFORM SIGN UM;Lo;0;L;;;;;N;;;;; +1231E;CUNEIFORM SIGN UM TIMES LAGAB;Lo;0;L;;;;;N;;;;; +1231F;CUNEIFORM SIGN UM TIMES ME PLUS DA;Lo;0;L;;;;;N;;;;; +12320;CUNEIFORM SIGN UM TIMES SHA3;Lo;0;L;;;;;N;;;;; +12321;CUNEIFORM SIGN UM TIMES U;Lo;0;L;;;;;N;;;;; +12322;CUNEIFORM SIGN UMBIN;Lo;0;L;;;;;N;;;;; +12323;CUNEIFORM SIGN UMUM;Lo;0;L;;;;;N;;;;; +12324;CUNEIFORM SIGN UMUM TIMES KASKAL;Lo;0;L;;;;;N;;;;; +12325;CUNEIFORM SIGN UMUM TIMES PA;Lo;0;L;;;;;N;;;;; +12326;CUNEIFORM SIGN UN;Lo;0;L;;;;;N;;;;; +12327;CUNEIFORM SIGN UN GUNU;Lo;0;L;;;;;N;;;;; +12328;CUNEIFORM SIGN UR;Lo;0;L;;;;;N;;;;; +12329;CUNEIFORM SIGN UR CROSSING UR;Lo;0;L;;;;;N;;;;; +1232A;CUNEIFORM SIGN UR SHESHIG;Lo;0;L;;;;;N;;;;; +1232B;CUNEIFORM SIGN UR2;Lo;0;L;;;;;N;;;;; +1232C;CUNEIFORM SIGN UR2 TIMES A PLUS HA;Lo;0;L;;;;;N;;;;; +1232D;CUNEIFORM SIGN UR2 TIMES A PLUS NA;Lo;0;L;;;;;N;;;;; +1232E;CUNEIFORM SIGN UR2 TIMES AL;Lo;0;L;;;;;N;;;;; +1232F;CUNEIFORM SIGN UR2 TIMES HA;Lo;0;L;;;;;N;;;;; +12330;CUNEIFORM SIGN UR2 TIMES NUN;Lo;0;L;;;;;N;;;;; +12331;CUNEIFORM SIGN UR2 TIMES U2;Lo;0;L;;;;;N;;;;; +12332;CUNEIFORM SIGN UR2 TIMES U2 PLUS ASH;Lo;0;L;;;;;N;;;;; +12333;CUNEIFORM SIGN UR2 TIMES U2 PLUS BI;Lo;0;L;;;;;N;;;;; +12334;CUNEIFORM SIGN UR4;Lo;0;L;;;;;N;;;;; +12335;CUNEIFORM SIGN URI;Lo;0;L;;;;;N;;;;; +12336;CUNEIFORM SIGN URI3;Lo;0;L;;;;;N;;;;; +12337;CUNEIFORM SIGN URU;Lo;0;L;;;;;N;;;;; +12338;CUNEIFORM SIGN URU TIMES A;Lo;0;L;;;;;N;;;;; +12339;CUNEIFORM SIGN URU TIMES ASHGAB;Lo;0;L;;;;;N;;;;; +1233A;CUNEIFORM SIGN URU TIMES BAR;Lo;0;L;;;;;N;;;;; +1233B;CUNEIFORM SIGN URU TIMES DUN;Lo;0;L;;;;;N;;;;; +1233C;CUNEIFORM SIGN URU TIMES GA;Lo;0;L;;;;;N;;;;; +1233D;CUNEIFORM SIGN URU TIMES GAL;Lo;0;L;;;;;N;;;;; +1233E;CUNEIFORM SIGN URU TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; +1233F;CUNEIFORM SIGN URU TIMES GAR;Lo;0;L;;;;;N;;;;; +12340;CUNEIFORM SIGN URU TIMES GU;Lo;0;L;;;;;N;;;;; +12341;CUNEIFORM SIGN URU TIMES HA;Lo;0;L;;;;;N;;;;; +12342;CUNEIFORM SIGN URU TIMES IGI;Lo;0;L;;;;;N;;;;; +12343;CUNEIFORM SIGN URU TIMES IM;Lo;0;L;;;;;N;;;;; +12344;CUNEIFORM SIGN URU TIMES ISH;Lo;0;L;;;;;N;;;;; +12345;CUNEIFORM SIGN URU TIMES KI;Lo;0;L;;;;;N;;;;; +12346;CUNEIFORM SIGN URU TIMES LUM;Lo;0;L;;;;;N;;;;; +12347;CUNEIFORM SIGN URU TIMES MIN;Lo;0;L;;;;;N;;;;; +12348;CUNEIFORM SIGN URU TIMES PA;Lo;0;L;;;;;N;;;;; +12349;CUNEIFORM SIGN URU TIMES SHE;Lo;0;L;;;;;N;;;;; +1234A;CUNEIFORM SIGN URU TIMES SIG4;Lo;0;L;;;;;N;;;;; +1234B;CUNEIFORM SIGN URU TIMES TU;Lo;0;L;;;;;N;;;;; +1234C;CUNEIFORM SIGN URU TIMES U PLUS GUD;Lo;0;L;;;;;N;;;;; +1234D;CUNEIFORM SIGN URU TIMES UD;Lo;0;L;;;;;N;;;;; +1234E;CUNEIFORM SIGN URU TIMES URUDA;Lo;0;L;;;;;N;;;;; +1234F;CUNEIFORM SIGN URUDA;Lo;0;L;;;;;N;;;;; +12350;CUNEIFORM SIGN URUDA TIMES U;Lo;0;L;;;;;N;;;;; +12351;CUNEIFORM SIGN USH;Lo;0;L;;;;;N;;;;; +12352;CUNEIFORM SIGN USH TIMES A;Lo;0;L;;;;;N;;;;; +12353;CUNEIFORM SIGN USH TIMES KU;Lo;0;L;;;;;N;;;;; +12354;CUNEIFORM SIGN USH TIMES KUR;Lo;0;L;;;;;N;;;;; +12355;CUNEIFORM SIGN USH TIMES TAK4;Lo;0;L;;;;;N;;;;; +12356;CUNEIFORM SIGN USHX;Lo;0;L;;;;;N;;;;; +12357;CUNEIFORM SIGN USH2;Lo;0;L;;;;;N;;;;; +12358;CUNEIFORM SIGN USHUMX;Lo;0;L;;;;;N;;;;; +12359;CUNEIFORM SIGN UTUKI;Lo;0;L;;;;;N;;;;; +1235A;CUNEIFORM SIGN UZ3;Lo;0;L;;;;;N;;;;; +1235B;CUNEIFORM SIGN UZ3 TIMES KASKAL;Lo;0;L;;;;;N;;;;; +1235C;CUNEIFORM SIGN UZU;Lo;0;L;;;;;N;;;;; +1235D;CUNEIFORM SIGN ZA;Lo;0;L;;;;;N;;;;; +1235E;CUNEIFORM SIGN ZA TENU;Lo;0;L;;;;;N;;;;; +1235F;CUNEIFORM SIGN ZA SQUARED TIMES KUR;Lo;0;L;;;;;N;;;;; +12360;CUNEIFORM SIGN ZAG;Lo;0;L;;;;;N;;;;; +12361;CUNEIFORM SIGN ZAMX;Lo;0;L;;;;;N;;;;; +12362;CUNEIFORM SIGN ZE2;Lo;0;L;;;;;N;;;;; +12363;CUNEIFORM SIGN ZI;Lo;0;L;;;;;N;;;;; +12364;CUNEIFORM SIGN ZI OVER ZI;Lo;0;L;;;;;N;;;;; +12365;CUNEIFORM SIGN ZI3;Lo;0;L;;;;;N;;;;; +12366;CUNEIFORM SIGN ZIB;Lo;0;L;;;;;N;;;;; +12367;CUNEIFORM SIGN ZIB KABA TENU;Lo;0;L;;;;;N;;;;; +12368;CUNEIFORM SIGN ZIG;Lo;0;L;;;;;N;;;;; +12369;CUNEIFORM SIGN ZIZ2;Lo;0;L;;;;;N;;;;; +1236A;CUNEIFORM SIGN ZU;Lo;0;L;;;;;N;;;;; +1236B;CUNEIFORM SIGN ZU5;Lo;0;L;;;;;N;;;;; +1236C;CUNEIFORM SIGN ZU5 TIMES A;Lo;0;L;;;;;N;;;;; +1236D;CUNEIFORM SIGN ZUBUR;Lo;0;L;;;;;N;;;;; +1236E;CUNEIFORM SIGN ZUM;Lo;0;L;;;;;N;;;;; +1236F;CUNEIFORM SIGN KAP ELAMITE;Lo;0;L;;;;;N;;;;; +12370;CUNEIFORM SIGN AB TIMES NUN;Lo;0;L;;;;;N;;;;; +12371;CUNEIFORM SIGN AB2 TIMES A;Lo;0;L;;;;;N;;;;; +12372;CUNEIFORM SIGN AMAR TIMES KUG;Lo;0;L;;;;;N;;;;; +12373;CUNEIFORM SIGN DAG KISIM5 TIMES U2 PLUS MASH;Lo;0;L;;;;;N;;;;; +12374;CUNEIFORM SIGN DAG3;Lo;0;L;;;;;N;;;;; +12375;CUNEIFORM SIGN DISH PLUS SHU;Lo;0;L;;;;;N;;;;; +12376;CUNEIFORM SIGN DUB TIMES SHE;Lo;0;L;;;;;N;;;;; +12377;CUNEIFORM SIGN EZEN TIMES GUD;Lo;0;L;;;;;N;;;;; +12378;CUNEIFORM SIGN EZEN TIMES SHE;Lo;0;L;;;;;N;;;;; +12379;CUNEIFORM SIGN GA2 TIMES AN PLUS KAK PLUS A;Lo;0;L;;;;;N;;;;; +1237A;CUNEIFORM SIGN GA2 TIMES ASH2;Lo;0;L;;;;;N;;;;; +1237B;CUNEIFORM SIGN GE22;Lo;0;L;;;;;N;;;;; +1237C;CUNEIFORM SIGN GIG;Lo;0;L;;;;;N;;;;; +1237D;CUNEIFORM SIGN HUSH;Lo;0;L;;;;;N;;;;; +1237E;CUNEIFORM SIGN KA TIMES ANSHE;Lo;0;L;;;;;N;;;;; +1237F;CUNEIFORM SIGN KA TIMES ASH3;Lo;0;L;;;;;N;;;;; +12380;CUNEIFORM SIGN KA TIMES GISH;Lo;0;L;;;;;N;;;;; +12381;CUNEIFORM SIGN KA TIMES GUD;Lo;0;L;;;;;N;;;;; +12382;CUNEIFORM SIGN KA TIMES HI TIMES ASH2;Lo;0;L;;;;;N;;;;; +12383;CUNEIFORM SIGN KA TIMES LUM;Lo;0;L;;;;;N;;;;; +12384;CUNEIFORM SIGN KA TIMES PA;Lo;0;L;;;;;N;;;;; +12385;CUNEIFORM SIGN KA TIMES SHUL;Lo;0;L;;;;;N;;;;; +12386;CUNEIFORM SIGN KA TIMES TU;Lo;0;L;;;;;N;;;;; +12387;CUNEIFORM SIGN KA TIMES UR2;Lo;0;L;;;;;N;;;;; +12388;CUNEIFORM SIGN LAGAB TIMES GI;Lo;0;L;;;;;N;;;;; +12389;CUNEIFORM SIGN LU2 SHESHIG TIMES BAD;Lo;0;L;;;;;N;;;;; +1238A;CUNEIFORM SIGN LU2 TIMES ESH2 PLUS LAL;Lo;0;L;;;;;N;;;;; +1238B;CUNEIFORM SIGN LU2 TIMES SHU;Lo;0;L;;;;;N;;;;; +1238C;CUNEIFORM SIGN MESH;Lo;0;L;;;;;N;;;;; +1238D;CUNEIFORM SIGN MUSH3 TIMES ZA;Lo;0;L;;;;;N;;;;; +1238E;CUNEIFORM SIGN NA4;Lo;0;L;;;;;N;;;;; +1238F;CUNEIFORM SIGN NIN;Lo;0;L;;;;;N;;;;; +12390;CUNEIFORM SIGN NIN9;Lo;0;L;;;;;N;;;;; +12391;CUNEIFORM SIGN NINDA2 TIMES BAL;Lo;0;L;;;;;N;;;;; +12392;CUNEIFORM SIGN NINDA2 TIMES GI;Lo;0;L;;;;;N;;;;; +12393;CUNEIFORM SIGN NU11 ROTATED NINETY DEGREES;Lo;0;L;;;;;N;;;;; +12394;CUNEIFORM SIGN PESH2 ASTERISK;Lo;0;L;;;;;N;;;;; +12395;CUNEIFORM SIGN PIR2;Lo;0;L;;;;;N;;;;; +12396;CUNEIFORM SIGN SAG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +12397;CUNEIFORM SIGN TI2;Lo;0;L;;;;;N;;;;; +12398;CUNEIFORM SIGN UM TIMES ME;Lo;0;L;;;;;N;;;;; +12399;CUNEIFORM SIGN U U;Lo;0;L;;;;;N;;;;; +12400;CUNEIFORM NUMERIC SIGN TWO ASH;Nl;0;L;;;;2;N;;;;; +12401;CUNEIFORM NUMERIC SIGN THREE ASH;Nl;0;L;;;;3;N;;;;; +12402;CUNEIFORM NUMERIC SIGN FOUR ASH;Nl;0;L;;;;4;N;;;;; +12403;CUNEIFORM NUMERIC SIGN FIVE ASH;Nl;0;L;;;;5;N;;;;; +12404;CUNEIFORM NUMERIC SIGN SIX ASH;Nl;0;L;;;;6;N;;;;; +12405;CUNEIFORM NUMERIC SIGN SEVEN ASH;Nl;0;L;;;;7;N;;;;; +12406;CUNEIFORM NUMERIC SIGN EIGHT ASH;Nl;0;L;;;;8;N;;;;; +12407;CUNEIFORM NUMERIC SIGN NINE ASH;Nl;0;L;;;;9;N;;;;; +12408;CUNEIFORM NUMERIC SIGN THREE DISH;Nl;0;L;;;;3;N;;;;; +12409;CUNEIFORM NUMERIC SIGN FOUR DISH;Nl;0;L;;;;4;N;;;;; +1240A;CUNEIFORM NUMERIC SIGN FIVE DISH;Nl;0;L;;;;5;N;;;;; +1240B;CUNEIFORM NUMERIC SIGN SIX DISH;Nl;0;L;;;;6;N;;;;; +1240C;CUNEIFORM NUMERIC SIGN SEVEN DISH;Nl;0;L;;;;7;N;;;;; +1240D;CUNEIFORM NUMERIC SIGN EIGHT DISH;Nl;0;L;;;;8;N;;;;; +1240E;CUNEIFORM NUMERIC SIGN NINE DISH;Nl;0;L;;;;9;N;;;;; +1240F;CUNEIFORM NUMERIC SIGN FOUR U;Nl;0;L;;;;4;N;;;;; +12410;CUNEIFORM NUMERIC SIGN FIVE U;Nl;0;L;;;;5;N;;;;; +12411;CUNEIFORM NUMERIC SIGN SIX U;Nl;0;L;;;;6;N;;;;; +12412;CUNEIFORM NUMERIC SIGN SEVEN U;Nl;0;L;;;;7;N;;;;; +12413;CUNEIFORM NUMERIC SIGN EIGHT U;Nl;0;L;;;;8;N;;;;; +12414;CUNEIFORM NUMERIC SIGN NINE U;Nl;0;L;;;;9;N;;;;; +12415;CUNEIFORM NUMERIC SIGN ONE GESH2;Nl;0;L;;;;1;N;;;;; +12416;CUNEIFORM NUMERIC SIGN TWO GESH2;Nl;0;L;;;;2;N;;;;; +12417;CUNEIFORM NUMERIC SIGN THREE GESH2;Nl;0;L;;;;3;N;;;;; +12418;CUNEIFORM NUMERIC SIGN FOUR GESH2;Nl;0;L;;;;4;N;;;;; +12419;CUNEIFORM NUMERIC SIGN FIVE GESH2;Nl;0;L;;;;5;N;;;;; +1241A;CUNEIFORM NUMERIC SIGN SIX GESH2;Nl;0;L;;;;6;N;;;;; +1241B;CUNEIFORM NUMERIC SIGN SEVEN GESH2;Nl;0;L;;;;7;N;;;;; +1241C;CUNEIFORM NUMERIC SIGN EIGHT GESH2;Nl;0;L;;;;8;N;;;;; +1241D;CUNEIFORM NUMERIC SIGN NINE GESH2;Nl;0;L;;;;9;N;;;;; +1241E;CUNEIFORM NUMERIC SIGN ONE GESHU;Nl;0;L;;;;1;N;;;;; +1241F;CUNEIFORM NUMERIC SIGN TWO GESHU;Nl;0;L;;;;2;N;;;;; +12420;CUNEIFORM NUMERIC SIGN THREE GESHU;Nl;0;L;;;;3;N;;;;; +12421;CUNEIFORM NUMERIC SIGN FOUR GESHU;Nl;0;L;;;;4;N;;;;; +12422;CUNEIFORM NUMERIC SIGN FIVE GESHU;Nl;0;L;;;;5;N;;;;; +12423;CUNEIFORM NUMERIC SIGN TWO SHAR2;Nl;0;L;;;;2;N;;;;; +12424;CUNEIFORM NUMERIC SIGN THREE SHAR2;Nl;0;L;;;;3;N;;;;; +12425;CUNEIFORM NUMERIC SIGN THREE SHAR2 VARIANT FORM;Nl;0;L;;;;3;N;;;;; +12426;CUNEIFORM NUMERIC SIGN FOUR SHAR2;Nl;0;L;;;;4;N;;;;; +12427;CUNEIFORM NUMERIC SIGN FIVE SHAR2;Nl;0;L;;;;5;N;;;;; +12428;CUNEIFORM NUMERIC SIGN SIX SHAR2;Nl;0;L;;;;6;N;;;;; +12429;CUNEIFORM NUMERIC SIGN SEVEN SHAR2;Nl;0;L;;;;7;N;;;;; +1242A;CUNEIFORM NUMERIC SIGN EIGHT SHAR2;Nl;0;L;;;;8;N;;;;; +1242B;CUNEIFORM NUMERIC SIGN NINE SHAR2;Nl;0;L;;;;9;N;;;;; +1242C;CUNEIFORM NUMERIC SIGN ONE SHARU;Nl;0;L;;;;1;N;;;;; +1242D;CUNEIFORM NUMERIC SIGN TWO SHARU;Nl;0;L;;;;2;N;;;;; +1242E;CUNEIFORM NUMERIC SIGN THREE SHARU;Nl;0;L;;;;3;N;;;;; +1242F;CUNEIFORM NUMERIC SIGN THREE SHARU VARIANT FORM;Nl;0;L;;;;3;N;;;;; +12430;CUNEIFORM NUMERIC SIGN FOUR SHARU;Nl;0;L;;;;4;N;;;;; +12431;CUNEIFORM NUMERIC SIGN FIVE SHARU;Nl;0;L;;;;5;N;;;;; +12432;CUNEIFORM NUMERIC SIGN SHAR2 TIMES GAL PLUS DISH;Nl;0;L;;;;216000;N;;;;; +12433;CUNEIFORM NUMERIC SIGN SHAR2 TIMES GAL PLUS MIN;Nl;0;L;;;;432000;N;;;;; +12434;CUNEIFORM NUMERIC SIGN ONE BURU;Nl;0;L;;;;1;N;;;;; +12435;CUNEIFORM NUMERIC SIGN TWO BURU;Nl;0;L;;;;2;N;;;;; +12436;CUNEIFORM NUMERIC SIGN THREE BURU;Nl;0;L;;;;3;N;;;;; +12437;CUNEIFORM NUMERIC SIGN THREE BURU VARIANT FORM;Nl;0;L;;;;3;N;;;;; +12438;CUNEIFORM NUMERIC SIGN FOUR BURU;Nl;0;L;;;;4;N;;;;; +12439;CUNEIFORM NUMERIC SIGN FIVE BURU;Nl;0;L;;;;5;N;;;;; +1243A;CUNEIFORM NUMERIC SIGN THREE VARIANT FORM ESH16;Nl;0;L;;;;3;N;;;;; +1243B;CUNEIFORM NUMERIC SIGN THREE VARIANT FORM ESH21;Nl;0;L;;;;3;N;;;;; +1243C;CUNEIFORM NUMERIC SIGN FOUR VARIANT FORM LIMMU;Nl;0;L;;;;4;N;;;;; +1243D;CUNEIFORM NUMERIC SIGN FOUR VARIANT FORM LIMMU4;Nl;0;L;;;;4;N;;;;; +1243E;CUNEIFORM NUMERIC SIGN FOUR VARIANT FORM LIMMU A;Nl;0;L;;;;4;N;;;;; +1243F;CUNEIFORM NUMERIC SIGN FOUR VARIANT FORM LIMMU B;Nl;0;L;;;;4;N;;;;; +12440;CUNEIFORM NUMERIC SIGN SIX VARIANT FORM ASH9;Nl;0;L;;;;6;N;;;;; +12441;CUNEIFORM NUMERIC SIGN SEVEN VARIANT FORM IMIN3;Nl;0;L;;;;7;N;;;;; +12442;CUNEIFORM NUMERIC SIGN SEVEN VARIANT FORM IMIN A;Nl;0;L;;;;7;N;;;;; +12443;CUNEIFORM NUMERIC SIGN SEVEN VARIANT FORM IMIN B;Nl;0;L;;;;7;N;;;;; +12444;CUNEIFORM NUMERIC SIGN EIGHT VARIANT FORM USSU;Nl;0;L;;;;8;N;;;;; +12445;CUNEIFORM NUMERIC SIGN EIGHT VARIANT FORM USSU3;Nl;0;L;;;;8;N;;;;; +12446;CUNEIFORM NUMERIC SIGN NINE VARIANT FORM ILIMMU;Nl;0;L;;;;9;N;;;;; +12447;CUNEIFORM NUMERIC SIGN NINE VARIANT FORM ILIMMU3;Nl;0;L;;;;9;N;;;;; +12448;CUNEIFORM NUMERIC SIGN NINE VARIANT FORM ILIMMU4;Nl;0;L;;;;9;N;;;;; +12449;CUNEIFORM NUMERIC SIGN NINE VARIANT FORM ILIMMU A;Nl;0;L;;;;9;N;;;;; +1244A;CUNEIFORM NUMERIC SIGN TWO ASH TENU;Nl;0;L;;;;2;N;;;;; +1244B;CUNEIFORM NUMERIC SIGN THREE ASH TENU;Nl;0;L;;;;3;N;;;;; +1244C;CUNEIFORM NUMERIC SIGN FOUR ASH TENU;Nl;0;L;;;;4;N;;;;; +1244D;CUNEIFORM NUMERIC SIGN FIVE ASH TENU;Nl;0;L;;;;5;N;;;;; +1244E;CUNEIFORM NUMERIC SIGN SIX ASH TENU;Nl;0;L;;;;6;N;;;;; +1244F;CUNEIFORM NUMERIC SIGN ONE BAN2;Nl;0;L;;;;1;N;;;;; +12450;CUNEIFORM NUMERIC SIGN TWO BAN2;Nl;0;L;;;;2;N;;;;; +12451;CUNEIFORM NUMERIC SIGN THREE BAN2;Nl;0;L;;;;3;N;;;;; +12452;CUNEIFORM NUMERIC SIGN FOUR BAN2;Nl;0;L;;;;4;N;;;;; +12453;CUNEIFORM NUMERIC SIGN FOUR BAN2 VARIANT FORM;Nl;0;L;;;;4;N;;;;; +12454;CUNEIFORM NUMERIC SIGN FIVE BAN2;Nl;0;L;;;;5;N;;;;; +12455;CUNEIFORM NUMERIC SIGN FIVE BAN2 VARIANT FORM;Nl;0;L;;;;5;N;;;;; +12456;CUNEIFORM NUMERIC SIGN NIGIDAMIN;Nl;0;L;;;;2;N;;;;; +12457;CUNEIFORM NUMERIC SIGN NIGIDAESH;Nl;0;L;;;;3;N;;;;; +12458;CUNEIFORM NUMERIC SIGN ONE ESHE3;Nl;0;L;;;;1;N;;;;; +12459;CUNEIFORM NUMERIC SIGN TWO ESHE3;Nl;0;L;;;;2;N;;;;; +1245A;CUNEIFORM NUMERIC SIGN ONE THIRD DISH;Nl;0;L;;;;1/3;N;;;;; +1245B;CUNEIFORM NUMERIC SIGN TWO THIRDS DISH;Nl;0;L;;;;2/3;N;;;;; +1245C;CUNEIFORM NUMERIC SIGN FIVE SIXTHS DISH;Nl;0;L;;;;5/6;N;;;;; +1245D;CUNEIFORM NUMERIC SIGN ONE THIRD VARIANT FORM A;Nl;0;L;;;;1/3;N;;;;; +1245E;CUNEIFORM NUMERIC SIGN TWO THIRDS VARIANT FORM A;Nl;0;L;;;;2/3;N;;;;; +1245F;CUNEIFORM NUMERIC SIGN ONE EIGHTH ASH;Nl;0;L;;;;1/8;N;;;;; +12460;CUNEIFORM NUMERIC SIGN ONE QUARTER ASH;Nl;0;L;;;;1/4;N;;;;; +12461;CUNEIFORM NUMERIC SIGN OLD ASSYRIAN ONE SIXTH;Nl;0;L;;;;1/6;N;;;;; +12462;CUNEIFORM NUMERIC SIGN OLD ASSYRIAN ONE QUARTER;Nl;0;L;;;;1/4;N;;;;; +12463;CUNEIFORM NUMERIC SIGN ONE QUARTER GUR;Nl;0;L;;;;1/4;N;;;;; +12464;CUNEIFORM NUMERIC SIGN ONE HALF GUR;Nl;0;L;;;;1/2;N;;;;; +12465;CUNEIFORM NUMERIC SIGN ELAMITE ONE THIRD;Nl;0;L;;;;1/3;N;;;;; +12466;CUNEIFORM NUMERIC SIGN ELAMITE TWO THIRDS;Nl;0;L;;;;2/3;N;;;;; +12467;CUNEIFORM NUMERIC SIGN ELAMITE FORTY;Nl;0;L;;;;40;N;;;;; +12468;CUNEIFORM NUMERIC SIGN ELAMITE FIFTY;Nl;0;L;;;;50;N;;;;; +12469;CUNEIFORM NUMERIC SIGN FOUR U VARIANT FORM;Nl;0;L;;;;4;N;;;;; +1246A;CUNEIFORM NUMERIC SIGN FIVE U VARIANT FORM;Nl;0;L;;;;5;N;;;;; +1246B;CUNEIFORM NUMERIC SIGN SIX U VARIANT FORM;Nl;0;L;;;;6;N;;;;; +1246C;CUNEIFORM NUMERIC SIGN SEVEN U VARIANT FORM;Nl;0;L;;;;7;N;;;;; +1246D;CUNEIFORM NUMERIC SIGN EIGHT U VARIANT FORM;Nl;0;L;;;;8;N;;;;; +1246E;CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM;Nl;0;L;;;;9;N;;;;; +12470;CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER;Po;0;L;;;;;N;;;;; +12471;CUNEIFORM PUNCTUATION SIGN VERTICAL COLON;Po;0;L;;;;;N;;;;; +12472;CUNEIFORM PUNCTUATION SIGN DIAGONAL COLON;Po;0;L;;;;;N;;;;; +12473;CUNEIFORM PUNCTUATION SIGN DIAGONAL TRICOLON;Po;0;L;;;;;N;;;;; +12474;CUNEIFORM PUNCTUATION SIGN DIAGONAL QUADCOLON;Po;0;L;;;;;N;;;;; +12480;CUNEIFORM SIGN AB TIMES NUN TENU;Lo;0;L;;;;;N;;;;; +12481;CUNEIFORM SIGN AB TIMES SHU2;Lo;0;L;;;;;N;;;;; +12482;CUNEIFORM SIGN AD TIMES ESH2;Lo;0;L;;;;;N;;;;; +12483;CUNEIFORM SIGN BAD TIMES DISH TENU;Lo;0;L;;;;;N;;;;; +12484;CUNEIFORM SIGN BAHAR2 TIMES AB2;Lo;0;L;;;;;N;;;;; +12485;CUNEIFORM SIGN BAHAR2 TIMES NI;Lo;0;L;;;;;N;;;;; +12486;CUNEIFORM SIGN BAHAR2 TIMES ZA;Lo;0;L;;;;;N;;;;; +12487;CUNEIFORM SIGN BU OVER BU TIMES NA2;Lo;0;L;;;;;N;;;;; +12488;CUNEIFORM SIGN DA TIMES TAK4;Lo;0;L;;;;;N;;;;; +12489;CUNEIFORM SIGN DAG TIMES KUR;Lo;0;L;;;;;N;;;;; +1248A;CUNEIFORM SIGN DIM TIMES IGI;Lo;0;L;;;;;N;;;;; +1248B;CUNEIFORM SIGN DIM TIMES U U U;Lo;0;L;;;;;N;;;;; +1248C;CUNEIFORM SIGN DIM2 TIMES UD;Lo;0;L;;;;;N;;;;; +1248D;CUNEIFORM SIGN DUG TIMES ANSHE;Lo;0;L;;;;;N;;;;; +1248E;CUNEIFORM SIGN DUG TIMES ASH;Lo;0;L;;;;;N;;;;; +1248F;CUNEIFORM SIGN DUG TIMES ASH AT LEFT;Lo;0;L;;;;;N;;;;; +12490;CUNEIFORM SIGN DUG TIMES DIN;Lo;0;L;;;;;N;;;;; +12491;CUNEIFORM SIGN DUG TIMES DUN;Lo;0;L;;;;;N;;;;; +12492;CUNEIFORM SIGN DUG TIMES ERIN2;Lo;0;L;;;;;N;;;;; +12493;CUNEIFORM SIGN DUG TIMES GA;Lo;0;L;;;;;N;;;;; +12494;CUNEIFORM SIGN DUG TIMES GI;Lo;0;L;;;;;N;;;;; +12495;CUNEIFORM SIGN DUG TIMES GIR2 GUNU;Lo;0;L;;;;;N;;;;; +12496;CUNEIFORM SIGN DUG TIMES GISH;Lo;0;L;;;;;N;;;;; +12497;CUNEIFORM SIGN DUG TIMES HA;Lo;0;L;;;;;N;;;;; +12498;CUNEIFORM SIGN DUG TIMES HI;Lo;0;L;;;;;N;;;;; +12499;CUNEIFORM SIGN DUG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +1249A;CUNEIFORM SIGN DUG TIMES KASKAL;Lo;0;L;;;;;N;;;;; +1249B;CUNEIFORM SIGN DUG TIMES KUR;Lo;0;L;;;;;N;;;;; +1249C;CUNEIFORM SIGN DUG TIMES KUSHU2;Lo;0;L;;;;;N;;;;; +1249D;CUNEIFORM SIGN DUG TIMES KUSHU2 PLUS KASKAL;Lo;0;L;;;;;N;;;;; +1249E;CUNEIFORM SIGN DUG TIMES LAK-020;Lo;0;L;;;;;N;;;;; +1249F;CUNEIFORM SIGN DUG TIMES LAM;Lo;0;L;;;;;N;;;;; +124A0;CUNEIFORM SIGN DUG TIMES LAM TIMES KUR;Lo;0;L;;;;;N;;;;; +124A1;CUNEIFORM SIGN DUG TIMES LUH PLUS GISH;Lo;0;L;;;;;N;;;;; +124A2;CUNEIFORM SIGN DUG TIMES MASH;Lo;0;L;;;;;N;;;;; +124A3;CUNEIFORM SIGN DUG TIMES MES;Lo;0;L;;;;;N;;;;; +124A4;CUNEIFORM SIGN DUG TIMES MI;Lo;0;L;;;;;N;;;;; +124A5;CUNEIFORM SIGN DUG TIMES NI;Lo;0;L;;;;;N;;;;; +124A6;CUNEIFORM SIGN DUG TIMES PI;Lo;0;L;;;;;N;;;;; +124A7;CUNEIFORM SIGN DUG TIMES SHE;Lo;0;L;;;;;N;;;;; +124A8;CUNEIFORM SIGN DUG TIMES SI GUNU;Lo;0;L;;;;;N;;;;; +124A9;CUNEIFORM SIGN E2 TIMES KUR;Lo;0;L;;;;;N;;;;; +124AA;CUNEIFORM SIGN E2 TIMES PAP;Lo;0;L;;;;;N;;;;; +124AB;CUNEIFORM SIGN ERIN2 X;Lo;0;L;;;;;N;;;;; +124AC;CUNEIFORM SIGN ESH2 CROSSING ESH2;Lo;0;L;;;;;N;;;;; +124AD;CUNEIFORM SIGN EZEN SHESHIG TIMES ASH;Lo;0;L;;;;;N;;;;; +124AE;CUNEIFORM SIGN EZEN SHESHIG TIMES HI;Lo;0;L;;;;;N;;;;; +124AF;CUNEIFORM SIGN EZEN SHESHIG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +124B0;CUNEIFORM SIGN EZEN SHESHIG TIMES LA;Lo;0;L;;;;;N;;;;; +124B1;CUNEIFORM SIGN EZEN SHESHIG TIMES LAL;Lo;0;L;;;;;N;;;;; +124B2;CUNEIFORM SIGN EZEN SHESHIG TIMES ME;Lo;0;L;;;;;N;;;;; +124B3;CUNEIFORM SIGN EZEN SHESHIG TIMES MES;Lo;0;L;;;;;N;;;;; +124B4;CUNEIFORM SIGN EZEN SHESHIG TIMES SU;Lo;0;L;;;;;N;;;;; +124B5;CUNEIFORM SIGN EZEN TIMES SU;Lo;0;L;;;;;N;;;;; +124B6;CUNEIFORM SIGN GA2 TIMES BAHAR2;Lo;0;L;;;;;N;;;;; +124B7;CUNEIFORM SIGN GA2 TIMES DIM GUNU;Lo;0;L;;;;;N;;;;; +124B8;CUNEIFORM SIGN GA2 TIMES DUG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +124B9;CUNEIFORM SIGN GA2 TIMES DUG TIMES KASKAL;Lo;0;L;;;;;N;;;;; +124BA;CUNEIFORM SIGN GA2 TIMES EREN;Lo;0;L;;;;;N;;;;; +124BB;CUNEIFORM SIGN GA2 TIMES GA;Lo;0;L;;;;;N;;;;; +124BC;CUNEIFORM SIGN GA2 TIMES GAR PLUS DI;Lo;0;L;;;;;N;;;;; +124BD;CUNEIFORM SIGN GA2 TIMES GAR PLUS NE;Lo;0;L;;;;;N;;;;; +124BE;CUNEIFORM SIGN GA2 TIMES HA PLUS A;Lo;0;L;;;;;N;;;;; +124BF;CUNEIFORM SIGN GA2 TIMES KUSHU2 PLUS KASKAL;Lo;0;L;;;;;N;;;;; +124C0;CUNEIFORM SIGN GA2 TIMES LAM;Lo;0;L;;;;;N;;;;; +124C1;CUNEIFORM SIGN GA2 TIMES LAM TIMES KUR;Lo;0;L;;;;;N;;;;; +124C2;CUNEIFORM SIGN GA2 TIMES LUH;Lo;0;L;;;;;N;;;;; +124C3;CUNEIFORM SIGN GA2 TIMES MUSH;Lo;0;L;;;;;N;;;;; +124C4;CUNEIFORM SIGN GA2 TIMES NE;Lo;0;L;;;;;N;;;;; +124C5;CUNEIFORM SIGN GA2 TIMES NE PLUS E2;Lo;0;L;;;;;N;;;;; +124C6;CUNEIFORM SIGN GA2 TIMES NE PLUS GI;Lo;0;L;;;;;N;;;;; +124C7;CUNEIFORM SIGN GA2 TIMES SHIM;Lo;0;L;;;;;N;;;;; +124C8;CUNEIFORM SIGN GA2 TIMES ZIZ2;Lo;0;L;;;;;N;;;;; +124C9;CUNEIFORM SIGN GABA ROTATED NINETY DEGREES;Lo;0;L;;;;;N;;;;; +124CA;CUNEIFORM SIGN GESHTIN TIMES U;Lo;0;L;;;;;N;;;;; +124CB;CUNEIFORM SIGN GISH TIMES GISH CROSSING GISH;Lo;0;L;;;;;N;;;;; +124CC;CUNEIFORM SIGN GU2 TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +124CD;CUNEIFORM SIGN GUD PLUS GISH TIMES TAK4;Lo;0;L;;;;;N;;;;; +124CE;CUNEIFORM SIGN HA TENU GUNU;Lo;0;L;;;;;N;;;;; +124CF;CUNEIFORM SIGN HI TIMES ASH OVER HI TIMES ASH;Lo;0;L;;;;;N;;;;; +124D0;CUNEIFORM SIGN KA TIMES BU;Lo;0;L;;;;;N;;;;; +124D1;CUNEIFORM SIGN KA TIMES KA;Lo;0;L;;;;;N;;;;; +124D2;CUNEIFORM SIGN KA TIMES U U U;Lo;0;L;;;;;N;;;;; +124D3;CUNEIFORM SIGN KA TIMES UR;Lo;0;L;;;;;N;;;;; +124D4;CUNEIFORM SIGN LAGAB TIMES ZU OVER ZU;Lo;0;L;;;;;N;;;;; +124D5;CUNEIFORM SIGN LAK-003;Lo;0;L;;;;;N;;;;; +124D6;CUNEIFORM SIGN LAK-021;Lo;0;L;;;;;N;;;;; +124D7;CUNEIFORM SIGN LAK-025;Lo;0;L;;;;;N;;;;; +124D8;CUNEIFORM SIGN LAK-030;Lo;0;L;;;;;N;;;;; +124D9;CUNEIFORM SIGN LAK-050;Lo;0;L;;;;;N;;;;; +124DA;CUNEIFORM SIGN LAK-051;Lo;0;L;;;;;N;;;;; +124DB;CUNEIFORM SIGN LAK-062;Lo;0;L;;;;;N;;;;; +124DC;CUNEIFORM SIGN LAK-079 OVER LAK-079 GUNU;Lo;0;L;;;;;N;;;;; +124DD;CUNEIFORM SIGN LAK-080;Lo;0;L;;;;;N;;;;; +124DE;CUNEIFORM SIGN LAK-081 OVER LAK-081;Lo;0;L;;;;;N;;;;; +124DF;CUNEIFORM SIGN LAK-092;Lo;0;L;;;;;N;;;;; +124E0;CUNEIFORM SIGN LAK-130;Lo;0;L;;;;;N;;;;; +124E1;CUNEIFORM SIGN LAK-142;Lo;0;L;;;;;N;;;;; +124E2;CUNEIFORM SIGN LAK-210;Lo;0;L;;;;;N;;;;; +124E3;CUNEIFORM SIGN LAK-219;Lo;0;L;;;;;N;;;;; +124E4;CUNEIFORM SIGN LAK-220;Lo;0;L;;;;;N;;;;; +124E5;CUNEIFORM SIGN LAK-225;Lo;0;L;;;;;N;;;;; +124E6;CUNEIFORM SIGN LAK-228;Lo;0;L;;;;;N;;;;; +124E7;CUNEIFORM SIGN LAK-238;Lo;0;L;;;;;N;;;;; +124E8;CUNEIFORM SIGN LAK-265;Lo;0;L;;;;;N;;;;; +124E9;CUNEIFORM SIGN LAK-266;Lo;0;L;;;;;N;;;;; +124EA;CUNEIFORM SIGN LAK-343;Lo;0;L;;;;;N;;;;; +124EB;CUNEIFORM SIGN LAK-347;Lo;0;L;;;;;N;;;;; +124EC;CUNEIFORM SIGN LAK-348;Lo;0;L;;;;;N;;;;; +124ED;CUNEIFORM SIGN LAK-383;Lo;0;L;;;;;N;;;;; +124EE;CUNEIFORM SIGN LAK-384;Lo;0;L;;;;;N;;;;; +124EF;CUNEIFORM SIGN LAK-390;Lo;0;L;;;;;N;;;;; +124F0;CUNEIFORM SIGN LAK-441;Lo;0;L;;;;;N;;;;; +124F1;CUNEIFORM SIGN LAK-449;Lo;0;L;;;;;N;;;;; +124F2;CUNEIFORM SIGN LAK-449 TIMES GU;Lo;0;L;;;;;N;;;;; +124F3;CUNEIFORM SIGN LAK-449 TIMES IGI;Lo;0;L;;;;;N;;;;; +124F4;CUNEIFORM SIGN LAK-449 TIMES PAP PLUS LU3;Lo;0;L;;;;;N;;;;; +124F5;CUNEIFORM SIGN LAK-449 TIMES PAP PLUS PAP PLUS LU3;Lo;0;L;;;;;N;;;;; +124F6;CUNEIFORM SIGN LAK-449 TIMES U2 PLUS BA;Lo;0;L;;;;;N;;;;; +124F7;CUNEIFORM SIGN LAK-450;Lo;0;L;;;;;N;;;;; +124F8;CUNEIFORM SIGN LAK-457;Lo;0;L;;;;;N;;;;; +124F9;CUNEIFORM SIGN LAK-470;Lo;0;L;;;;;N;;;;; +124FA;CUNEIFORM SIGN LAK-483;Lo;0;L;;;;;N;;;;; +124FB;CUNEIFORM SIGN LAK-490;Lo;0;L;;;;;N;;;;; +124FC;CUNEIFORM SIGN LAK-492;Lo;0;L;;;;;N;;;;; +124FD;CUNEIFORM SIGN LAK-493;Lo;0;L;;;;;N;;;;; +124FE;CUNEIFORM SIGN LAK-495;Lo;0;L;;;;;N;;;;; +124FF;CUNEIFORM SIGN LAK-550;Lo;0;L;;;;;N;;;;; +12500;CUNEIFORM SIGN LAK-608;Lo;0;L;;;;;N;;;;; +12501;CUNEIFORM SIGN LAK-617;Lo;0;L;;;;;N;;;;; +12502;CUNEIFORM SIGN LAK-617 TIMES ASH;Lo;0;L;;;;;N;;;;; +12503;CUNEIFORM SIGN LAK-617 TIMES BAD;Lo;0;L;;;;;N;;;;; +12504;CUNEIFORM SIGN LAK-617 TIMES DUN3 GUNU GUNU;Lo;0;L;;;;;N;;;;; +12505;CUNEIFORM SIGN LAK-617 TIMES KU3;Lo;0;L;;;;;N;;;;; +12506;CUNEIFORM SIGN LAK-617 TIMES LA;Lo;0;L;;;;;N;;;;; +12507;CUNEIFORM SIGN LAK-617 TIMES TAR;Lo;0;L;;;;;N;;;;; +12508;CUNEIFORM SIGN LAK-617 TIMES TE;Lo;0;L;;;;;N;;;;; +12509;CUNEIFORM SIGN LAK-617 TIMES U2;Lo;0;L;;;;;N;;;;; +1250A;CUNEIFORM SIGN LAK-617 TIMES UD;Lo;0;L;;;;;N;;;;; +1250B;CUNEIFORM SIGN LAK-617 TIMES URUDA;Lo;0;L;;;;;N;;;;; +1250C;CUNEIFORM SIGN LAK-636;Lo;0;L;;;;;N;;;;; +1250D;CUNEIFORM SIGN LAK-648;Lo;0;L;;;;;N;;;;; +1250E;CUNEIFORM SIGN LAK-648 TIMES DUB;Lo;0;L;;;;;N;;;;; +1250F;CUNEIFORM SIGN LAK-648 TIMES GA;Lo;0;L;;;;;N;;;;; +12510;CUNEIFORM SIGN LAK-648 TIMES IGI;Lo;0;L;;;;;N;;;;; +12511;CUNEIFORM SIGN LAK-648 TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; +12512;CUNEIFORM SIGN LAK-648 TIMES NI;Lo;0;L;;;;;N;;;;; +12513;CUNEIFORM SIGN LAK-648 TIMES PAP PLUS PAP PLUS LU3;Lo;0;L;;;;;N;;;;; +12514;CUNEIFORM SIGN LAK-648 TIMES SHESH PLUS KI;Lo;0;L;;;;;N;;;;; +12515;CUNEIFORM SIGN LAK-648 TIMES UD;Lo;0;L;;;;;N;;;;; +12516;CUNEIFORM SIGN LAK-648 TIMES URUDA;Lo;0;L;;;;;N;;;;; +12517;CUNEIFORM SIGN LAK-724;Lo;0;L;;;;;N;;;;; +12518;CUNEIFORM SIGN LAK-749;Lo;0;L;;;;;N;;;;; +12519;CUNEIFORM SIGN LU2 GUNU TIMES ASH;Lo;0;L;;;;;N;;;;; +1251A;CUNEIFORM SIGN LU2 TIMES DISH;Lo;0;L;;;;;N;;;;; +1251B;CUNEIFORM SIGN LU2 TIMES HAL;Lo;0;L;;;;;N;;;;; +1251C;CUNEIFORM SIGN LU2 TIMES PAP;Lo;0;L;;;;;N;;;;; +1251D;CUNEIFORM SIGN LU2 TIMES PAP PLUS PAP PLUS LU3;Lo;0;L;;;;;N;;;;; +1251E;CUNEIFORM SIGN LU2 TIMES TAK4;Lo;0;L;;;;;N;;;;; +1251F;CUNEIFORM SIGN MI PLUS ZA7;Lo;0;L;;;;;N;;;;; +12520;CUNEIFORM SIGN MUSH OVER MUSH TIMES GA;Lo;0;L;;;;;N;;;;; +12521;CUNEIFORM SIGN MUSH OVER MUSH TIMES KAK;Lo;0;L;;;;;N;;;;; +12522;CUNEIFORM SIGN NINDA2 TIMES DIM GUNU;Lo;0;L;;;;;N;;;;; +12523;CUNEIFORM SIGN NINDA2 TIMES GISH;Lo;0;L;;;;;N;;;;; +12524;CUNEIFORM SIGN NINDA2 TIMES GUL;Lo;0;L;;;;;N;;;;; +12525;CUNEIFORM SIGN NINDA2 TIMES HI;Lo;0;L;;;;;N;;;;; +12526;CUNEIFORM SIGN NINDA2 TIMES KESH2;Lo;0;L;;;;;N;;;;; +12527;CUNEIFORM SIGN NINDA2 TIMES LAK-050;Lo;0;L;;;;;N;;;;; +12528;CUNEIFORM SIGN NINDA2 TIMES MASH;Lo;0;L;;;;;N;;;;; +12529;CUNEIFORM SIGN NINDA2 TIMES PAP PLUS PAP;Lo;0;L;;;;;N;;;;; +1252A;CUNEIFORM SIGN NINDA2 TIMES U;Lo;0;L;;;;;N;;;;; +1252B;CUNEIFORM SIGN NINDA2 TIMES U PLUS U;Lo;0;L;;;;;N;;;;; +1252C;CUNEIFORM SIGN NINDA2 TIMES URUDA;Lo;0;L;;;;;N;;;;; +1252D;CUNEIFORM SIGN SAG GUNU TIMES HA;Lo;0;L;;;;;N;;;;; +1252E;CUNEIFORM SIGN SAG TIMES EN;Lo;0;L;;;;;N;;;;; +1252F;CUNEIFORM SIGN SAG TIMES SHE AT LEFT;Lo;0;L;;;;;N;;;;; +12530;CUNEIFORM SIGN SAG TIMES TAK4;Lo;0;L;;;;;N;;;;; +12531;CUNEIFORM SIGN SHA6 TENU;Lo;0;L;;;;;N;;;;; +12532;CUNEIFORM SIGN SHE OVER SHE;Lo;0;L;;;;;N;;;;; +12533;CUNEIFORM SIGN SHE PLUS HUB2;Lo;0;L;;;;;N;;;;; +12534;CUNEIFORM SIGN SHE PLUS NAM2;Lo;0;L;;;;;N;;;;; +12535;CUNEIFORM SIGN SHE PLUS SAR;Lo;0;L;;;;;N;;;;; +12536;CUNEIFORM SIGN SHU2 PLUS DUG TIMES NI;Lo;0;L;;;;;N;;;;; +12537;CUNEIFORM SIGN SHU2 PLUS E2 TIMES AN;Lo;0;L;;;;;N;;;;; +12538;CUNEIFORM SIGN SI TIMES TAK4;Lo;0;L;;;;;N;;;;; +12539;CUNEIFORM SIGN TAK4 PLUS SAG;Lo;0;L;;;;;N;;;;; +1253A;CUNEIFORM SIGN TUM TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;; +1253B;CUNEIFORM SIGN TUM TIMES THREE DISH;Lo;0;L;;;;;N;;;;; +1253C;CUNEIFORM SIGN UR2 INVERTED;Lo;0;L;;;;;N;;;;; +1253D;CUNEIFORM SIGN UR2 TIMES UD;Lo;0;L;;;;;N;;;;; +1253E;CUNEIFORM SIGN URU TIMES DARA3;Lo;0;L;;;;;N;;;;; +1253F;CUNEIFORM SIGN URU TIMES LAK-668;Lo;0;L;;;;;N;;;;; +12540;CUNEIFORM SIGN URU TIMES LU3;Lo;0;L;;;;;N;;;;; +12541;CUNEIFORM SIGN ZA7;Lo;0;L;;;;;N;;;;; +12542;CUNEIFORM SIGN ZU OVER ZU PLUS SAR;Lo;0;L;;;;;N;;;;; +12543;CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU;Lo;0;L;;;;;N;;;;; +13000;EGYPTIAN HIEROGLYPH A001;Lo;0;L;;;;;N;;;;; +13001;EGYPTIAN HIEROGLYPH A002;Lo;0;L;;;;;N;;;;; +13002;EGYPTIAN HIEROGLYPH A003;Lo;0;L;;;;;N;;;;; +13003;EGYPTIAN HIEROGLYPH A004;Lo;0;L;;;;;N;;;;; +13004;EGYPTIAN HIEROGLYPH A005;Lo;0;L;;;;;N;;;;; +13005;EGYPTIAN HIEROGLYPH A005A;Lo;0;L;;;;;N;;;;; +13006;EGYPTIAN HIEROGLYPH A006;Lo;0;L;;;;;N;;;;; +13007;EGYPTIAN HIEROGLYPH A006A;Lo;0;L;;;;;N;;;;; +13008;EGYPTIAN HIEROGLYPH A006B;Lo;0;L;;;;;N;;;;; +13009;EGYPTIAN HIEROGLYPH A007;Lo;0;L;;;;;N;;;;; +1300A;EGYPTIAN HIEROGLYPH A008;Lo;0;L;;;;;N;;;;; +1300B;EGYPTIAN HIEROGLYPH A009;Lo;0;L;;;;;N;;;;; +1300C;EGYPTIAN HIEROGLYPH A010;Lo;0;L;;;;;N;;;;; +1300D;EGYPTIAN HIEROGLYPH A011;Lo;0;L;;;;;N;;;;; +1300E;EGYPTIAN HIEROGLYPH A012;Lo;0;L;;;;;N;;;;; +1300F;EGYPTIAN HIEROGLYPH A013;Lo;0;L;;;;;N;;;;; +13010;EGYPTIAN HIEROGLYPH A014;Lo;0;L;;;;;N;;;;; +13011;EGYPTIAN HIEROGLYPH A014A;Lo;0;L;;;;;N;;;;; +13012;EGYPTIAN HIEROGLYPH A015;Lo;0;L;;;;;N;;;;; +13013;EGYPTIAN HIEROGLYPH A016;Lo;0;L;;;;;N;;;;; +13014;EGYPTIAN HIEROGLYPH A017;Lo;0;L;;;;;N;;;;; +13015;EGYPTIAN HIEROGLYPH A017A;Lo;0;L;;;;;N;;;;; +13016;EGYPTIAN HIEROGLYPH A018;Lo;0;L;;;;;N;;;;; +13017;EGYPTIAN HIEROGLYPH A019;Lo;0;L;;;;;N;;;;; +13018;EGYPTIAN HIEROGLYPH A020;Lo;0;L;;;;;N;;;;; +13019;EGYPTIAN HIEROGLYPH A021;Lo;0;L;;;;;N;;;;; +1301A;EGYPTIAN HIEROGLYPH A022;Lo;0;L;;;;;N;;;;; +1301B;EGYPTIAN HIEROGLYPH A023;Lo;0;L;;;;;N;;;;; +1301C;EGYPTIAN HIEROGLYPH A024;Lo;0;L;;;;;N;;;;; +1301D;EGYPTIAN HIEROGLYPH A025;Lo;0;L;;;;;N;;;;; +1301E;EGYPTIAN HIEROGLYPH A026;Lo;0;L;;;;;N;;;;; +1301F;EGYPTIAN HIEROGLYPH A027;Lo;0;L;;;;;N;;;;; +13020;EGYPTIAN HIEROGLYPH A028;Lo;0;L;;;;;N;;;;; +13021;EGYPTIAN HIEROGLYPH A029;Lo;0;L;;;;;N;;;;; +13022;EGYPTIAN HIEROGLYPH A030;Lo;0;L;;;;;N;;;;; +13023;EGYPTIAN HIEROGLYPH A031;Lo;0;L;;;;;N;;;;; +13024;EGYPTIAN HIEROGLYPH A032;Lo;0;L;;;;;N;;;;; +13025;EGYPTIAN HIEROGLYPH A032A;Lo;0;L;;;;;N;;;;; +13026;EGYPTIAN HIEROGLYPH A033;Lo;0;L;;;;;N;;;;; +13027;EGYPTIAN HIEROGLYPH A034;Lo;0;L;;;;;N;;;;; +13028;EGYPTIAN HIEROGLYPH A035;Lo;0;L;;;;;N;;;;; +13029;EGYPTIAN HIEROGLYPH A036;Lo;0;L;;;;;N;;;;; +1302A;EGYPTIAN HIEROGLYPH A037;Lo;0;L;;;;;N;;;;; +1302B;EGYPTIAN HIEROGLYPH A038;Lo;0;L;;;;;N;;;;; +1302C;EGYPTIAN HIEROGLYPH A039;Lo;0;L;;;;;N;;;;; +1302D;EGYPTIAN HIEROGLYPH A040;Lo;0;L;;;;;N;;;;; +1302E;EGYPTIAN HIEROGLYPH A040A;Lo;0;L;;;;;N;;;;; +1302F;EGYPTIAN HIEROGLYPH A041;Lo;0;L;;;;;N;;;;; +13030;EGYPTIAN HIEROGLYPH A042;Lo;0;L;;;;;N;;;;; +13031;EGYPTIAN HIEROGLYPH A042A;Lo;0;L;;;;;N;;;;; +13032;EGYPTIAN HIEROGLYPH A043;Lo;0;L;;;;;N;;;;; +13033;EGYPTIAN HIEROGLYPH A043A;Lo;0;L;;;;;N;;;;; +13034;EGYPTIAN HIEROGLYPH A044;Lo;0;L;;;;;N;;;;; +13035;EGYPTIAN HIEROGLYPH A045;Lo;0;L;;;;;N;;;;; +13036;EGYPTIAN HIEROGLYPH A045A;Lo;0;L;;;;;N;;;;; +13037;EGYPTIAN HIEROGLYPH A046;Lo;0;L;;;;;N;;;;; +13038;EGYPTIAN HIEROGLYPH A047;Lo;0;L;;;;;N;;;;; +13039;EGYPTIAN HIEROGLYPH A048;Lo;0;L;;;;;N;;;;; +1303A;EGYPTIAN HIEROGLYPH A049;Lo;0;L;;;;;N;;;;; +1303B;EGYPTIAN HIEROGLYPH A050;Lo;0;L;;;;;N;;;;; +1303C;EGYPTIAN HIEROGLYPH A051;Lo;0;L;;;;;N;;;;; +1303D;EGYPTIAN HIEROGLYPH A052;Lo;0;L;;;;;N;;;;; +1303E;EGYPTIAN HIEROGLYPH A053;Lo;0;L;;;;;N;;;;; +1303F;EGYPTIAN HIEROGLYPH A054;Lo;0;L;;;;;N;;;;; +13040;EGYPTIAN HIEROGLYPH A055;Lo;0;L;;;;;N;;;;; +13041;EGYPTIAN HIEROGLYPH A056;Lo;0;L;;;;;N;;;;; +13042;EGYPTIAN HIEROGLYPH A057;Lo;0;L;;;;;N;;;;; +13043;EGYPTIAN HIEROGLYPH A058;Lo;0;L;;;;;N;;;;; +13044;EGYPTIAN HIEROGLYPH A059;Lo;0;L;;;;;N;;;;; +13045;EGYPTIAN HIEROGLYPH A060;Lo;0;L;;;;;N;;;;; +13046;EGYPTIAN HIEROGLYPH A061;Lo;0;L;;;;;N;;;;; +13047;EGYPTIAN HIEROGLYPH A062;Lo;0;L;;;;;N;;;;; +13048;EGYPTIAN HIEROGLYPH A063;Lo;0;L;;;;;N;;;;; +13049;EGYPTIAN HIEROGLYPH A064;Lo;0;L;;;;;N;;;;; +1304A;EGYPTIAN HIEROGLYPH A065;Lo;0;L;;;;;N;;;;; +1304B;EGYPTIAN HIEROGLYPH A066;Lo;0;L;;;;;N;;;;; +1304C;EGYPTIAN HIEROGLYPH A067;Lo;0;L;;;;;N;;;;; +1304D;EGYPTIAN HIEROGLYPH A068;Lo;0;L;;;;;N;;;;; +1304E;EGYPTIAN HIEROGLYPH A069;Lo;0;L;;;;;N;;;;; +1304F;EGYPTIAN HIEROGLYPH A070;Lo;0;L;;;;;N;;;;; +13050;EGYPTIAN HIEROGLYPH B001;Lo;0;L;;;;;N;;;;; +13051;EGYPTIAN HIEROGLYPH B002;Lo;0;L;;;;;N;;;;; +13052;EGYPTIAN HIEROGLYPH B003;Lo;0;L;;;;;N;;;;; +13053;EGYPTIAN HIEROGLYPH B004;Lo;0;L;;;;;N;;;;; +13054;EGYPTIAN HIEROGLYPH B005;Lo;0;L;;;;;N;;;;; +13055;EGYPTIAN HIEROGLYPH B005A;Lo;0;L;;;;;N;;;;; +13056;EGYPTIAN HIEROGLYPH B006;Lo;0;L;;;;;N;;;;; +13057;EGYPTIAN HIEROGLYPH B007;Lo;0;L;;;;;N;;;;; +13058;EGYPTIAN HIEROGLYPH B008;Lo;0;L;;;;;N;;;;; +13059;EGYPTIAN HIEROGLYPH B009;Lo;0;L;;;;;N;;;;; +1305A;EGYPTIAN HIEROGLYPH C001;Lo;0;L;;;;;N;;;;; +1305B;EGYPTIAN HIEROGLYPH C002;Lo;0;L;;;;;N;;;;; +1305C;EGYPTIAN HIEROGLYPH C002A;Lo;0;L;;;;;N;;;;; +1305D;EGYPTIAN HIEROGLYPH C002B;Lo;0;L;;;;;N;;;;; +1305E;EGYPTIAN HIEROGLYPH C002C;Lo;0;L;;;;;N;;;;; +1305F;EGYPTIAN HIEROGLYPH C003;Lo;0;L;;;;;N;;;;; +13060;EGYPTIAN HIEROGLYPH C004;Lo;0;L;;;;;N;;;;; +13061;EGYPTIAN HIEROGLYPH C005;Lo;0;L;;;;;N;;;;; +13062;EGYPTIAN HIEROGLYPH C006;Lo;0;L;;;;;N;;;;; +13063;EGYPTIAN HIEROGLYPH C007;Lo;0;L;;;;;N;;;;; +13064;EGYPTIAN HIEROGLYPH C008;Lo;0;L;;;;;N;;;;; +13065;EGYPTIAN HIEROGLYPH C009;Lo;0;L;;;;;N;;;;; +13066;EGYPTIAN HIEROGLYPH C010;Lo;0;L;;;;;N;;;;; +13067;EGYPTIAN HIEROGLYPH C010A;Lo;0;L;;;;;N;;;;; +13068;EGYPTIAN HIEROGLYPH C011;Lo;0;L;;;;;N;;;;; +13069;EGYPTIAN HIEROGLYPH C012;Lo;0;L;;;;;N;;;;; +1306A;EGYPTIAN HIEROGLYPH C013;Lo;0;L;;;;;N;;;;; +1306B;EGYPTIAN HIEROGLYPH C014;Lo;0;L;;;;;N;;;;; +1306C;EGYPTIAN HIEROGLYPH C015;Lo;0;L;;;;;N;;;;; +1306D;EGYPTIAN HIEROGLYPH C016;Lo;0;L;;;;;N;;;;; +1306E;EGYPTIAN HIEROGLYPH C017;Lo;0;L;;;;;N;;;;; +1306F;EGYPTIAN HIEROGLYPH C018;Lo;0;L;;;;;N;;;;; +13070;EGYPTIAN HIEROGLYPH C019;Lo;0;L;;;;;N;;;;; +13071;EGYPTIAN HIEROGLYPH C020;Lo;0;L;;;;;N;;;;; +13072;EGYPTIAN HIEROGLYPH C021;Lo;0;L;;;;;N;;;;; +13073;EGYPTIAN HIEROGLYPH C022;Lo;0;L;;;;;N;;;;; +13074;EGYPTIAN HIEROGLYPH C023;Lo;0;L;;;;;N;;;;; +13075;EGYPTIAN HIEROGLYPH C024;Lo;0;L;;;;;N;;;;; +13076;EGYPTIAN HIEROGLYPH D001;Lo;0;L;;;;;N;;;;; +13077;EGYPTIAN HIEROGLYPH D002;Lo;0;L;;;;;N;;;;; +13078;EGYPTIAN HIEROGLYPH D003;Lo;0;L;;;;;N;;;;; +13079;EGYPTIAN HIEROGLYPH D004;Lo;0;L;;;;;N;;;;; +1307A;EGYPTIAN HIEROGLYPH D005;Lo;0;L;;;;;N;;;;; +1307B;EGYPTIAN HIEROGLYPH D006;Lo;0;L;;;;;N;;;;; +1307C;EGYPTIAN HIEROGLYPH D007;Lo;0;L;;;;;N;;;;; +1307D;EGYPTIAN HIEROGLYPH D008;Lo;0;L;;;;;N;;;;; +1307E;EGYPTIAN HIEROGLYPH D008A;Lo;0;L;;;;;N;;;;; +1307F;EGYPTIAN HIEROGLYPH D009;Lo;0;L;;;;;N;;;;; +13080;EGYPTIAN HIEROGLYPH D010;Lo;0;L;;;;;N;;;;; +13081;EGYPTIAN HIEROGLYPH D011;Lo;0;L;;;;;N;;;;; +13082;EGYPTIAN HIEROGLYPH D012;Lo;0;L;;;;;N;;;;; +13083;EGYPTIAN HIEROGLYPH D013;Lo;0;L;;;;;N;;;;; +13084;EGYPTIAN HIEROGLYPH D014;Lo;0;L;;;;;N;;;;; +13085;EGYPTIAN HIEROGLYPH D015;Lo;0;L;;;;;N;;;;; +13086;EGYPTIAN HIEROGLYPH D016;Lo;0;L;;;;;N;;;;; +13087;EGYPTIAN HIEROGLYPH D017;Lo;0;L;;;;;N;;;;; +13088;EGYPTIAN HIEROGLYPH D018;Lo;0;L;;;;;N;;;;; +13089;EGYPTIAN HIEROGLYPH D019;Lo;0;L;;;;;N;;;;; +1308A;EGYPTIAN HIEROGLYPH D020;Lo;0;L;;;;;N;;;;; +1308B;EGYPTIAN HIEROGLYPH D021;Lo;0;L;;;;;N;;;;; +1308C;EGYPTIAN HIEROGLYPH D022;Lo;0;L;;;;;N;;;;; +1308D;EGYPTIAN HIEROGLYPH D023;Lo;0;L;;;;;N;;;;; +1308E;EGYPTIAN HIEROGLYPH D024;Lo;0;L;;;;;N;;;;; +1308F;EGYPTIAN HIEROGLYPH D025;Lo;0;L;;;;;N;;;;; +13090;EGYPTIAN HIEROGLYPH D026;Lo;0;L;;;;;N;;;;; +13091;EGYPTIAN HIEROGLYPH D027;Lo;0;L;;;;;N;;;;; +13092;EGYPTIAN HIEROGLYPH D027A;Lo;0;L;;;;;N;;;;; +13093;EGYPTIAN HIEROGLYPH D028;Lo;0;L;;;;;N;;;;; +13094;EGYPTIAN HIEROGLYPH D029;Lo;0;L;;;;;N;;;;; +13095;EGYPTIAN HIEROGLYPH D030;Lo;0;L;;;;;N;;;;; +13096;EGYPTIAN HIEROGLYPH D031;Lo;0;L;;;;;N;;;;; +13097;EGYPTIAN HIEROGLYPH D031A;Lo;0;L;;;;;N;;;;; +13098;EGYPTIAN HIEROGLYPH D032;Lo;0;L;;;;;N;;;;; +13099;EGYPTIAN HIEROGLYPH D033;Lo;0;L;;;;;N;;;;; +1309A;EGYPTIAN HIEROGLYPH D034;Lo;0;L;;;;;N;;;;; +1309B;EGYPTIAN HIEROGLYPH D034A;Lo;0;L;;;;;N;;;;; +1309C;EGYPTIAN HIEROGLYPH D035;Lo;0;L;;;;;N;;;;; +1309D;EGYPTIAN HIEROGLYPH D036;Lo;0;L;;;;;N;;;;; +1309E;EGYPTIAN HIEROGLYPH D037;Lo;0;L;;;;;N;;;;; +1309F;EGYPTIAN HIEROGLYPH D038;Lo;0;L;;;;;N;;;;; +130A0;EGYPTIAN HIEROGLYPH D039;Lo;0;L;;;;;N;;;;; +130A1;EGYPTIAN HIEROGLYPH D040;Lo;0;L;;;;;N;;;;; +130A2;EGYPTIAN HIEROGLYPH D041;Lo;0;L;;;;;N;;;;; +130A3;EGYPTIAN HIEROGLYPH D042;Lo;0;L;;;;;N;;;;; +130A4;EGYPTIAN HIEROGLYPH D043;Lo;0;L;;;;;N;;;;; +130A5;EGYPTIAN HIEROGLYPH D044;Lo;0;L;;;;;N;;;;; +130A6;EGYPTIAN HIEROGLYPH D045;Lo;0;L;;;;;N;;;;; +130A7;EGYPTIAN HIEROGLYPH D046;Lo;0;L;;;;;N;;;;; +130A8;EGYPTIAN HIEROGLYPH D046A;Lo;0;L;;;;;N;;;;; +130A9;EGYPTIAN HIEROGLYPH D047;Lo;0;L;;;;;N;;;;; +130AA;EGYPTIAN HIEROGLYPH D048;Lo;0;L;;;;;N;;;;; +130AB;EGYPTIAN HIEROGLYPH D048A;Lo;0;L;;;;;N;;;;; +130AC;EGYPTIAN HIEROGLYPH D049;Lo;0;L;;;;;N;;;;; +130AD;EGYPTIAN HIEROGLYPH D050;Lo;0;L;;;;;N;;;;; +130AE;EGYPTIAN HIEROGLYPH D050A;Lo;0;L;;;;;N;;;;; +130AF;EGYPTIAN HIEROGLYPH D050B;Lo;0;L;;;;;N;;;;; +130B0;EGYPTIAN HIEROGLYPH D050C;Lo;0;L;;;;;N;;;;; +130B1;EGYPTIAN HIEROGLYPH D050D;Lo;0;L;;;;;N;;;;; +130B2;EGYPTIAN HIEROGLYPH D050E;Lo;0;L;;;;;N;;;;; +130B3;EGYPTIAN HIEROGLYPH D050F;Lo;0;L;;;;;N;;;;; +130B4;EGYPTIAN HIEROGLYPH D050G;Lo;0;L;;;;;N;;;;; +130B5;EGYPTIAN HIEROGLYPH D050H;Lo;0;L;;;;;N;;;;; +130B6;EGYPTIAN HIEROGLYPH D050I;Lo;0;L;;;;;N;;;;; +130B7;EGYPTIAN HIEROGLYPH D051;Lo;0;L;;;;;N;;;;; +130B8;EGYPTIAN HIEROGLYPH D052;Lo;0;L;;;;;N;;;;; +130B9;EGYPTIAN HIEROGLYPH D052A;Lo;0;L;;;;;N;;;;; +130BA;EGYPTIAN HIEROGLYPH D053;Lo;0;L;;;;;N;;;;; +130BB;EGYPTIAN HIEROGLYPH D054;Lo;0;L;;;;;N;;;;; +130BC;EGYPTIAN HIEROGLYPH D054A;Lo;0;L;;;;;N;;;;; +130BD;EGYPTIAN HIEROGLYPH D055;Lo;0;L;;;;;N;;;;; +130BE;EGYPTIAN HIEROGLYPH D056;Lo;0;L;;;;;N;;;;; +130BF;EGYPTIAN HIEROGLYPH D057;Lo;0;L;;;;;N;;;;; +130C0;EGYPTIAN HIEROGLYPH D058;Lo;0;L;;;;;N;;;;; +130C1;EGYPTIAN HIEROGLYPH D059;Lo;0;L;;;;;N;;;;; +130C2;EGYPTIAN HIEROGLYPH D060;Lo;0;L;;;;;N;;;;; +130C3;EGYPTIAN HIEROGLYPH D061;Lo;0;L;;;;;N;;;;; +130C4;EGYPTIAN HIEROGLYPH D062;Lo;0;L;;;;;N;;;;; +130C5;EGYPTIAN HIEROGLYPH D063;Lo;0;L;;;;;N;;;;; +130C6;EGYPTIAN HIEROGLYPH D064;Lo;0;L;;;;;N;;;;; +130C7;EGYPTIAN HIEROGLYPH D065;Lo;0;L;;;;;N;;;;; +130C8;EGYPTIAN HIEROGLYPH D066;Lo;0;L;;;;;N;;;;; +130C9;EGYPTIAN HIEROGLYPH D067;Lo;0;L;;;;;N;;;;; +130CA;EGYPTIAN HIEROGLYPH D067A;Lo;0;L;;;;;N;;;;; +130CB;EGYPTIAN HIEROGLYPH D067B;Lo;0;L;;;;;N;;;;; +130CC;EGYPTIAN HIEROGLYPH D067C;Lo;0;L;;;;;N;;;;; +130CD;EGYPTIAN HIEROGLYPH D067D;Lo;0;L;;;;;N;;;;; +130CE;EGYPTIAN HIEROGLYPH D067E;Lo;0;L;;;;;N;;;;; +130CF;EGYPTIAN HIEROGLYPH D067F;Lo;0;L;;;;;N;;;;; +130D0;EGYPTIAN HIEROGLYPH D067G;Lo;0;L;;;;;N;;;;; +130D1;EGYPTIAN HIEROGLYPH D067H;Lo;0;L;;;;;N;;;;; +130D2;EGYPTIAN HIEROGLYPH E001;Lo;0;L;;;;;N;;;;; +130D3;EGYPTIAN HIEROGLYPH E002;Lo;0;L;;;;;N;;;;; +130D4;EGYPTIAN HIEROGLYPH E003;Lo;0;L;;;;;N;;;;; +130D5;EGYPTIAN HIEROGLYPH E004;Lo;0;L;;;;;N;;;;; +130D6;EGYPTIAN HIEROGLYPH E005;Lo;0;L;;;;;N;;;;; +130D7;EGYPTIAN HIEROGLYPH E006;Lo;0;L;;;;;N;;;;; +130D8;EGYPTIAN HIEROGLYPH E007;Lo;0;L;;;;;N;;;;; +130D9;EGYPTIAN HIEROGLYPH E008;Lo;0;L;;;;;N;;;;; +130DA;EGYPTIAN HIEROGLYPH E008A;Lo;0;L;;;;;N;;;;; +130DB;EGYPTIAN HIEROGLYPH E009;Lo;0;L;;;;;N;;;;; +130DC;EGYPTIAN HIEROGLYPH E009A;Lo;0;L;;;;;N;;;;; +130DD;EGYPTIAN HIEROGLYPH E010;Lo;0;L;;;;;N;;;;; +130DE;EGYPTIAN HIEROGLYPH E011;Lo;0;L;;;;;N;;;;; +130DF;EGYPTIAN HIEROGLYPH E012;Lo;0;L;;;;;N;;;;; +130E0;EGYPTIAN HIEROGLYPH E013;Lo;0;L;;;;;N;;;;; +130E1;EGYPTIAN HIEROGLYPH E014;Lo;0;L;;;;;N;;;;; +130E2;EGYPTIAN HIEROGLYPH E015;Lo;0;L;;;;;N;;;;; +130E3;EGYPTIAN HIEROGLYPH E016;Lo;0;L;;;;;N;;;;; +130E4;EGYPTIAN HIEROGLYPH E016A;Lo;0;L;;;;;N;;;;; +130E5;EGYPTIAN HIEROGLYPH E017;Lo;0;L;;;;;N;;;;; +130E6;EGYPTIAN HIEROGLYPH E017A;Lo;0;L;;;;;N;;;;; +130E7;EGYPTIAN HIEROGLYPH E018;Lo;0;L;;;;;N;;;;; +130E8;EGYPTIAN HIEROGLYPH E019;Lo;0;L;;;;;N;;;;; +130E9;EGYPTIAN HIEROGLYPH E020;Lo;0;L;;;;;N;;;;; +130EA;EGYPTIAN HIEROGLYPH E020A;Lo;0;L;;;;;N;;;;; +130EB;EGYPTIAN HIEROGLYPH E021;Lo;0;L;;;;;N;;;;; +130EC;EGYPTIAN HIEROGLYPH E022;Lo;0;L;;;;;N;;;;; +130ED;EGYPTIAN HIEROGLYPH E023;Lo;0;L;;;;;N;;;;; +130EE;EGYPTIAN HIEROGLYPH E024;Lo;0;L;;;;;N;;;;; +130EF;EGYPTIAN HIEROGLYPH E025;Lo;0;L;;;;;N;;;;; +130F0;EGYPTIAN HIEROGLYPH E026;Lo;0;L;;;;;N;;;;; +130F1;EGYPTIAN HIEROGLYPH E027;Lo;0;L;;;;;N;;;;; +130F2;EGYPTIAN HIEROGLYPH E028;Lo;0;L;;;;;N;;;;; +130F3;EGYPTIAN HIEROGLYPH E028A;Lo;0;L;;;;;N;;;;; +130F4;EGYPTIAN HIEROGLYPH E029;Lo;0;L;;;;;N;;;;; +130F5;EGYPTIAN HIEROGLYPH E030;Lo;0;L;;;;;N;;;;; +130F6;EGYPTIAN HIEROGLYPH E031;Lo;0;L;;;;;N;;;;; +130F7;EGYPTIAN HIEROGLYPH E032;Lo;0;L;;;;;N;;;;; +130F8;EGYPTIAN HIEROGLYPH E033;Lo;0;L;;;;;N;;;;; +130F9;EGYPTIAN HIEROGLYPH E034;Lo;0;L;;;;;N;;;;; +130FA;EGYPTIAN HIEROGLYPH E034A;Lo;0;L;;;;;N;;;;; +130FB;EGYPTIAN HIEROGLYPH E036;Lo;0;L;;;;;N;;;;; +130FC;EGYPTIAN HIEROGLYPH E037;Lo;0;L;;;;;N;;;;; +130FD;EGYPTIAN HIEROGLYPH E038;Lo;0;L;;;;;N;;;;; +130FE;EGYPTIAN HIEROGLYPH F001;Lo;0;L;;;;;N;;;;; +130FF;EGYPTIAN HIEROGLYPH F001A;Lo;0;L;;;;;N;;;;; +13100;EGYPTIAN HIEROGLYPH F002;Lo;0;L;;;;;N;;;;; +13101;EGYPTIAN HIEROGLYPH F003;Lo;0;L;;;;;N;;;;; +13102;EGYPTIAN HIEROGLYPH F004;Lo;0;L;;;;;N;;;;; +13103;EGYPTIAN HIEROGLYPH F005;Lo;0;L;;;;;N;;;;; +13104;EGYPTIAN HIEROGLYPH F006;Lo;0;L;;;;;N;;;;; +13105;EGYPTIAN HIEROGLYPH F007;Lo;0;L;;;;;N;;;;; +13106;EGYPTIAN HIEROGLYPH F008;Lo;0;L;;;;;N;;;;; +13107;EGYPTIAN HIEROGLYPH F009;Lo;0;L;;;;;N;;;;; +13108;EGYPTIAN HIEROGLYPH F010;Lo;0;L;;;;;N;;;;; +13109;EGYPTIAN HIEROGLYPH F011;Lo;0;L;;;;;N;;;;; +1310A;EGYPTIAN HIEROGLYPH F012;Lo;0;L;;;;;N;;;;; +1310B;EGYPTIAN HIEROGLYPH F013;Lo;0;L;;;;;N;;;;; +1310C;EGYPTIAN HIEROGLYPH F013A;Lo;0;L;;;;;N;;;;; +1310D;EGYPTIAN HIEROGLYPH F014;Lo;0;L;;;;;N;;;;; +1310E;EGYPTIAN HIEROGLYPH F015;Lo;0;L;;;;;N;;;;; +1310F;EGYPTIAN HIEROGLYPH F016;Lo;0;L;;;;;N;;;;; +13110;EGYPTIAN HIEROGLYPH F017;Lo;0;L;;;;;N;;;;; +13111;EGYPTIAN HIEROGLYPH F018;Lo;0;L;;;;;N;;;;; +13112;EGYPTIAN HIEROGLYPH F019;Lo;0;L;;;;;N;;;;; +13113;EGYPTIAN HIEROGLYPH F020;Lo;0;L;;;;;N;;;;; +13114;EGYPTIAN HIEROGLYPH F021;Lo;0;L;;;;;N;;;;; +13115;EGYPTIAN HIEROGLYPH F021A;Lo;0;L;;;;;N;;;;; +13116;EGYPTIAN HIEROGLYPH F022;Lo;0;L;;;;;N;;;;; +13117;EGYPTIAN HIEROGLYPH F023;Lo;0;L;;;;;N;;;;; +13118;EGYPTIAN HIEROGLYPH F024;Lo;0;L;;;;;N;;;;; +13119;EGYPTIAN HIEROGLYPH F025;Lo;0;L;;;;;N;;;;; +1311A;EGYPTIAN HIEROGLYPH F026;Lo;0;L;;;;;N;;;;; +1311B;EGYPTIAN HIEROGLYPH F027;Lo;0;L;;;;;N;;;;; +1311C;EGYPTIAN HIEROGLYPH F028;Lo;0;L;;;;;N;;;;; +1311D;EGYPTIAN HIEROGLYPH F029;Lo;0;L;;;;;N;;;;; +1311E;EGYPTIAN HIEROGLYPH F030;Lo;0;L;;;;;N;;;;; +1311F;EGYPTIAN HIEROGLYPH F031;Lo;0;L;;;;;N;;;;; +13120;EGYPTIAN HIEROGLYPH F031A;Lo;0;L;;;;;N;;;;; +13121;EGYPTIAN HIEROGLYPH F032;Lo;0;L;;;;;N;;;;; +13122;EGYPTIAN HIEROGLYPH F033;Lo;0;L;;;;;N;;;;; +13123;EGYPTIAN HIEROGLYPH F034;Lo;0;L;;;;;N;;;;; +13124;EGYPTIAN HIEROGLYPH F035;Lo;0;L;;;;;N;;;;; +13125;EGYPTIAN HIEROGLYPH F036;Lo;0;L;;;;;N;;;;; +13126;EGYPTIAN HIEROGLYPH F037;Lo;0;L;;;;;N;;;;; +13127;EGYPTIAN HIEROGLYPH F037A;Lo;0;L;;;;;N;;;;; +13128;EGYPTIAN HIEROGLYPH F038;Lo;0;L;;;;;N;;;;; +13129;EGYPTIAN HIEROGLYPH F038A;Lo;0;L;;;;;N;;;;; +1312A;EGYPTIAN HIEROGLYPH F039;Lo;0;L;;;;;N;;;;; +1312B;EGYPTIAN HIEROGLYPH F040;Lo;0;L;;;;;N;;;;; +1312C;EGYPTIAN HIEROGLYPH F041;Lo;0;L;;;;;N;;;;; +1312D;EGYPTIAN HIEROGLYPH F042;Lo;0;L;;;;;N;;;;; +1312E;EGYPTIAN HIEROGLYPH F043;Lo;0;L;;;;;N;;;;; +1312F;EGYPTIAN HIEROGLYPH F044;Lo;0;L;;;;;N;;;;; +13130;EGYPTIAN HIEROGLYPH F045;Lo;0;L;;;;;N;;;;; +13131;EGYPTIAN HIEROGLYPH F045A;Lo;0;L;;;;;N;;;;; +13132;EGYPTIAN HIEROGLYPH F046;Lo;0;L;;;;;N;;;;; +13133;EGYPTIAN HIEROGLYPH F046A;Lo;0;L;;;;;N;;;;; +13134;EGYPTIAN HIEROGLYPH F047;Lo;0;L;;;;;N;;;;; +13135;EGYPTIAN HIEROGLYPH F047A;Lo;0;L;;;;;N;;;;; +13136;EGYPTIAN HIEROGLYPH F048;Lo;0;L;;;;;N;;;;; +13137;EGYPTIAN HIEROGLYPH F049;Lo;0;L;;;;;N;;;;; +13138;EGYPTIAN HIEROGLYPH F050;Lo;0;L;;;;;N;;;;; +13139;EGYPTIAN HIEROGLYPH F051;Lo;0;L;;;;;N;;;;; +1313A;EGYPTIAN HIEROGLYPH F051A;Lo;0;L;;;;;N;;;;; +1313B;EGYPTIAN HIEROGLYPH F051B;Lo;0;L;;;;;N;;;;; +1313C;EGYPTIAN HIEROGLYPH F051C;Lo;0;L;;;;;N;;;;; +1313D;EGYPTIAN HIEROGLYPH F052;Lo;0;L;;;;;N;;;;; +1313E;EGYPTIAN HIEROGLYPH F053;Lo;0;L;;;;;N;;;;; +1313F;EGYPTIAN HIEROGLYPH G001;Lo;0;L;;;;;N;;;;; +13140;EGYPTIAN HIEROGLYPH G002;Lo;0;L;;;;;N;;;;; +13141;EGYPTIAN HIEROGLYPH G003;Lo;0;L;;;;;N;;;;; +13142;EGYPTIAN HIEROGLYPH G004;Lo;0;L;;;;;N;;;;; +13143;EGYPTIAN HIEROGLYPH G005;Lo;0;L;;;;;N;;;;; +13144;EGYPTIAN HIEROGLYPH G006;Lo;0;L;;;;;N;;;;; +13145;EGYPTIAN HIEROGLYPH G006A;Lo;0;L;;;;;N;;;;; +13146;EGYPTIAN HIEROGLYPH G007;Lo;0;L;;;;;N;;;;; +13147;EGYPTIAN HIEROGLYPH G007A;Lo;0;L;;;;;N;;;;; +13148;EGYPTIAN HIEROGLYPH G007B;Lo;0;L;;;;;N;;;;; +13149;EGYPTIAN HIEROGLYPH G008;Lo;0;L;;;;;N;;;;; +1314A;EGYPTIAN HIEROGLYPH G009;Lo;0;L;;;;;N;;;;; +1314B;EGYPTIAN HIEROGLYPH G010;Lo;0;L;;;;;N;;;;; +1314C;EGYPTIAN HIEROGLYPH G011;Lo;0;L;;;;;N;;;;; +1314D;EGYPTIAN HIEROGLYPH G011A;Lo;0;L;;;;;N;;;;; +1314E;EGYPTIAN HIEROGLYPH G012;Lo;0;L;;;;;N;;;;; +1314F;EGYPTIAN HIEROGLYPH G013;Lo;0;L;;;;;N;;;;; +13150;EGYPTIAN HIEROGLYPH G014;Lo;0;L;;;;;N;;;;; +13151;EGYPTIAN HIEROGLYPH G015;Lo;0;L;;;;;N;;;;; +13152;EGYPTIAN HIEROGLYPH G016;Lo;0;L;;;;;N;;;;; +13153;EGYPTIAN HIEROGLYPH G017;Lo;0;L;;;;;N;;;;; +13154;EGYPTIAN HIEROGLYPH G018;Lo;0;L;;;;;N;;;;; +13155;EGYPTIAN HIEROGLYPH G019;Lo;0;L;;;;;N;;;;; +13156;EGYPTIAN HIEROGLYPH G020;Lo;0;L;;;;;N;;;;; +13157;EGYPTIAN HIEROGLYPH G020A;Lo;0;L;;;;;N;;;;; +13158;EGYPTIAN HIEROGLYPH G021;Lo;0;L;;;;;N;;;;; +13159;EGYPTIAN HIEROGLYPH G022;Lo;0;L;;;;;N;;;;; +1315A;EGYPTIAN HIEROGLYPH G023;Lo;0;L;;;;;N;;;;; +1315B;EGYPTIAN HIEROGLYPH G024;Lo;0;L;;;;;N;;;;; +1315C;EGYPTIAN HIEROGLYPH G025;Lo;0;L;;;;;N;;;;; +1315D;EGYPTIAN HIEROGLYPH G026;Lo;0;L;;;;;N;;;;; +1315E;EGYPTIAN HIEROGLYPH G026A;Lo;0;L;;;;;N;;;;; +1315F;EGYPTIAN HIEROGLYPH G027;Lo;0;L;;;;;N;;;;; +13160;EGYPTIAN HIEROGLYPH G028;Lo;0;L;;;;;N;;;;; +13161;EGYPTIAN HIEROGLYPH G029;Lo;0;L;;;;;N;;;;; +13162;EGYPTIAN HIEROGLYPH G030;Lo;0;L;;;;;N;;;;; +13163;EGYPTIAN HIEROGLYPH G031;Lo;0;L;;;;;N;;;;; +13164;EGYPTIAN HIEROGLYPH G032;Lo;0;L;;;;;N;;;;; +13165;EGYPTIAN HIEROGLYPH G033;Lo;0;L;;;;;N;;;;; +13166;EGYPTIAN HIEROGLYPH G034;Lo;0;L;;;;;N;;;;; +13167;EGYPTIAN HIEROGLYPH G035;Lo;0;L;;;;;N;;;;; +13168;EGYPTIAN HIEROGLYPH G036;Lo;0;L;;;;;N;;;;; +13169;EGYPTIAN HIEROGLYPH G036A;Lo;0;L;;;;;N;;;;; +1316A;EGYPTIAN HIEROGLYPH G037;Lo;0;L;;;;;N;;;;; +1316B;EGYPTIAN HIEROGLYPH G037A;Lo;0;L;;;;;N;;;;; +1316C;EGYPTIAN HIEROGLYPH G038;Lo;0;L;;;;;N;;;;; +1316D;EGYPTIAN HIEROGLYPH G039;Lo;0;L;;;;;N;;;;; +1316E;EGYPTIAN HIEROGLYPH G040;Lo;0;L;;;;;N;;;;; +1316F;EGYPTIAN HIEROGLYPH G041;Lo;0;L;;;;;N;;;;; +13170;EGYPTIAN HIEROGLYPH G042;Lo;0;L;;;;;N;;;;; +13171;EGYPTIAN HIEROGLYPH G043;Lo;0;L;;;;;N;;;;; +13172;EGYPTIAN HIEROGLYPH G043A;Lo;0;L;;;;;N;;;;; +13173;EGYPTIAN HIEROGLYPH G044;Lo;0;L;;;;;N;;;;; +13174;EGYPTIAN HIEROGLYPH G045;Lo;0;L;;;;;N;;;;; +13175;EGYPTIAN HIEROGLYPH G045A;Lo;0;L;;;;;N;;;;; +13176;EGYPTIAN HIEROGLYPH G046;Lo;0;L;;;;;N;;;;; +13177;EGYPTIAN HIEROGLYPH G047;Lo;0;L;;;;;N;;;;; +13178;EGYPTIAN HIEROGLYPH G048;Lo;0;L;;;;;N;;;;; +13179;EGYPTIAN HIEROGLYPH G049;Lo;0;L;;;;;N;;;;; +1317A;EGYPTIAN HIEROGLYPH G050;Lo;0;L;;;;;N;;;;; +1317B;EGYPTIAN HIEROGLYPH G051;Lo;0;L;;;;;N;;;;; +1317C;EGYPTIAN HIEROGLYPH G052;Lo;0;L;;;;;N;;;;; +1317D;EGYPTIAN HIEROGLYPH G053;Lo;0;L;;;;;N;;;;; +1317E;EGYPTIAN HIEROGLYPH G054;Lo;0;L;;;;;N;;;;; +1317F;EGYPTIAN HIEROGLYPH H001;Lo;0;L;;;;;N;;;;; +13180;EGYPTIAN HIEROGLYPH H002;Lo;0;L;;;;;N;;;;; +13181;EGYPTIAN HIEROGLYPH H003;Lo;0;L;;;;;N;;;;; +13182;EGYPTIAN HIEROGLYPH H004;Lo;0;L;;;;;N;;;;; +13183;EGYPTIAN HIEROGLYPH H005;Lo;0;L;;;;;N;;;;; +13184;EGYPTIAN HIEROGLYPH H006;Lo;0;L;;;;;N;;;;; +13185;EGYPTIAN HIEROGLYPH H006A;Lo;0;L;;;;;N;;;;; +13186;EGYPTIAN HIEROGLYPH H007;Lo;0;L;;;;;N;;;;; +13187;EGYPTIAN HIEROGLYPH H008;Lo;0;L;;;;;N;;;;; +13188;EGYPTIAN HIEROGLYPH I001;Lo;0;L;;;;;N;;;;; +13189;EGYPTIAN HIEROGLYPH I002;Lo;0;L;;;;;N;;;;; +1318A;EGYPTIAN HIEROGLYPH I003;Lo;0;L;;;;;N;;;;; +1318B;EGYPTIAN HIEROGLYPH I004;Lo;0;L;;;;;N;;;;; +1318C;EGYPTIAN HIEROGLYPH I005;Lo;0;L;;;;;N;;;;; +1318D;EGYPTIAN HIEROGLYPH I005A;Lo;0;L;;;;;N;;;;; +1318E;EGYPTIAN HIEROGLYPH I006;Lo;0;L;;;;;N;;;;; +1318F;EGYPTIAN HIEROGLYPH I007;Lo;0;L;;;;;N;;;;; +13190;EGYPTIAN HIEROGLYPH I008;Lo;0;L;;;;;N;;;;; +13191;EGYPTIAN HIEROGLYPH I009;Lo;0;L;;;;;N;;;;; +13192;EGYPTIAN HIEROGLYPH I009A;Lo;0;L;;;;;N;;;;; +13193;EGYPTIAN HIEROGLYPH I010;Lo;0;L;;;;;N;;;;; +13194;EGYPTIAN HIEROGLYPH I010A;Lo;0;L;;;;;N;;;;; +13195;EGYPTIAN HIEROGLYPH I011;Lo;0;L;;;;;N;;;;; +13196;EGYPTIAN HIEROGLYPH I011A;Lo;0;L;;;;;N;;;;; +13197;EGYPTIAN HIEROGLYPH I012;Lo;0;L;;;;;N;;;;; +13198;EGYPTIAN HIEROGLYPH I013;Lo;0;L;;;;;N;;;;; +13199;EGYPTIAN HIEROGLYPH I014;Lo;0;L;;;;;N;;;;; +1319A;EGYPTIAN HIEROGLYPH I015;Lo;0;L;;;;;N;;;;; +1319B;EGYPTIAN HIEROGLYPH K001;Lo;0;L;;;;;N;;;;; +1319C;EGYPTIAN HIEROGLYPH K002;Lo;0;L;;;;;N;;;;; +1319D;EGYPTIAN HIEROGLYPH K003;Lo;0;L;;;;;N;;;;; +1319E;EGYPTIAN HIEROGLYPH K004;Lo;0;L;;;;;N;;;;; +1319F;EGYPTIAN HIEROGLYPH K005;Lo;0;L;;;;;N;;;;; +131A0;EGYPTIAN HIEROGLYPH K006;Lo;0;L;;;;;N;;;;; +131A1;EGYPTIAN HIEROGLYPH K007;Lo;0;L;;;;;N;;;;; +131A2;EGYPTIAN HIEROGLYPH K008;Lo;0;L;;;;;N;;;;; +131A3;EGYPTIAN HIEROGLYPH L001;Lo;0;L;;;;;N;;;;; +131A4;EGYPTIAN HIEROGLYPH L002;Lo;0;L;;;;;N;;;;; +131A5;EGYPTIAN HIEROGLYPH L002A;Lo;0;L;;;;;N;;;;; +131A6;EGYPTIAN HIEROGLYPH L003;Lo;0;L;;;;;N;;;;; +131A7;EGYPTIAN HIEROGLYPH L004;Lo;0;L;;;;;N;;;;; +131A8;EGYPTIAN HIEROGLYPH L005;Lo;0;L;;;;;N;;;;; +131A9;EGYPTIAN HIEROGLYPH L006;Lo;0;L;;;;;N;;;;; +131AA;EGYPTIAN HIEROGLYPH L006A;Lo;0;L;;;;;N;;;;; +131AB;EGYPTIAN HIEROGLYPH L007;Lo;0;L;;;;;N;;;;; +131AC;EGYPTIAN HIEROGLYPH L008;Lo;0;L;;;;;N;;;;; +131AD;EGYPTIAN HIEROGLYPH M001;Lo;0;L;;;;;N;;;;; +131AE;EGYPTIAN HIEROGLYPH M001A;Lo;0;L;;;;;N;;;;; +131AF;EGYPTIAN HIEROGLYPH M001B;Lo;0;L;;;;;N;;;;; +131B0;EGYPTIAN HIEROGLYPH M002;Lo;0;L;;;;;N;;;;; +131B1;EGYPTIAN HIEROGLYPH M003;Lo;0;L;;;;;N;;;;; +131B2;EGYPTIAN HIEROGLYPH M003A;Lo;0;L;;;;;N;;;;; +131B3;EGYPTIAN HIEROGLYPH M004;Lo;0;L;;;;;N;;;;; +131B4;EGYPTIAN HIEROGLYPH M005;Lo;0;L;;;;;N;;;;; +131B5;EGYPTIAN HIEROGLYPH M006;Lo;0;L;;;;;N;;;;; +131B6;EGYPTIAN HIEROGLYPH M007;Lo;0;L;;;;;N;;;;; +131B7;EGYPTIAN HIEROGLYPH M008;Lo;0;L;;;;;N;;;;; +131B8;EGYPTIAN HIEROGLYPH M009;Lo;0;L;;;;;N;;;;; +131B9;EGYPTIAN HIEROGLYPH M010;Lo;0;L;;;;;N;;;;; +131BA;EGYPTIAN HIEROGLYPH M010A;Lo;0;L;;;;;N;;;;; +131BB;EGYPTIAN HIEROGLYPH M011;Lo;0;L;;;;;N;;;;; +131BC;EGYPTIAN HIEROGLYPH M012;Lo;0;L;;;;;N;;;;; +131BD;EGYPTIAN HIEROGLYPH M012A;Lo;0;L;;;;;N;;;;; +131BE;EGYPTIAN HIEROGLYPH M012B;Lo;0;L;;;;;N;;;;; +131BF;EGYPTIAN HIEROGLYPH M012C;Lo;0;L;;;;;N;;;;; +131C0;EGYPTIAN HIEROGLYPH M012D;Lo;0;L;;;;;N;;;;; +131C1;EGYPTIAN HIEROGLYPH M012E;Lo;0;L;;;;;N;;;;; +131C2;EGYPTIAN HIEROGLYPH M012F;Lo;0;L;;;;;N;;;;; +131C3;EGYPTIAN HIEROGLYPH M012G;Lo;0;L;;;;;N;;;;; +131C4;EGYPTIAN HIEROGLYPH M012H;Lo;0;L;;;;;N;;;;; +131C5;EGYPTIAN HIEROGLYPH M013;Lo;0;L;;;;;N;;;;; +131C6;EGYPTIAN HIEROGLYPH M014;Lo;0;L;;;;;N;;;;; +131C7;EGYPTIAN HIEROGLYPH M015;Lo;0;L;;;;;N;;;;; +131C8;EGYPTIAN HIEROGLYPH M015A;Lo;0;L;;;;;N;;;;; +131C9;EGYPTIAN HIEROGLYPH M016;Lo;0;L;;;;;N;;;;; +131CA;EGYPTIAN HIEROGLYPH M016A;Lo;0;L;;;;;N;;;;; +131CB;EGYPTIAN HIEROGLYPH M017;Lo;0;L;;;;;N;;;;; +131CC;EGYPTIAN HIEROGLYPH M017A;Lo;0;L;;;;;N;;;;; +131CD;EGYPTIAN HIEROGLYPH M018;Lo;0;L;;;;;N;;;;; +131CE;EGYPTIAN HIEROGLYPH M019;Lo;0;L;;;;;N;;;;; +131CF;EGYPTIAN HIEROGLYPH M020;Lo;0;L;;;;;N;;;;; +131D0;EGYPTIAN HIEROGLYPH M021;Lo;0;L;;;;;N;;;;; +131D1;EGYPTIAN HIEROGLYPH M022;Lo;0;L;;;;;N;;;;; +131D2;EGYPTIAN HIEROGLYPH M022A;Lo;0;L;;;;;N;;;;; +131D3;EGYPTIAN HIEROGLYPH M023;Lo;0;L;;;;;N;;;;; +131D4;EGYPTIAN HIEROGLYPH M024;Lo;0;L;;;;;N;;;;; +131D5;EGYPTIAN HIEROGLYPH M024A;Lo;0;L;;;;;N;;;;; +131D6;EGYPTIAN HIEROGLYPH M025;Lo;0;L;;;;;N;;;;; +131D7;EGYPTIAN HIEROGLYPH M026;Lo;0;L;;;;;N;;;;; +131D8;EGYPTIAN HIEROGLYPH M027;Lo;0;L;;;;;N;;;;; +131D9;EGYPTIAN HIEROGLYPH M028;Lo;0;L;;;;;N;;;;; +131DA;EGYPTIAN HIEROGLYPH M028A;Lo;0;L;;;;;N;;;;; +131DB;EGYPTIAN HIEROGLYPH M029;Lo;0;L;;;;;N;;;;; +131DC;EGYPTIAN HIEROGLYPH M030;Lo;0;L;;;;;N;;;;; +131DD;EGYPTIAN HIEROGLYPH M031;Lo;0;L;;;;;N;;;;; +131DE;EGYPTIAN HIEROGLYPH M031A;Lo;0;L;;;;;N;;;;; +131DF;EGYPTIAN HIEROGLYPH M032;Lo;0;L;;;;;N;;;;; +131E0;EGYPTIAN HIEROGLYPH M033;Lo;0;L;;;;;N;;;;; +131E1;EGYPTIAN HIEROGLYPH M033A;Lo;0;L;;;;;N;;;;; +131E2;EGYPTIAN HIEROGLYPH M033B;Lo;0;L;;;;;N;;;;; +131E3;EGYPTIAN HIEROGLYPH M034;Lo;0;L;;;;;N;;;;; +131E4;EGYPTIAN HIEROGLYPH M035;Lo;0;L;;;;;N;;;;; +131E5;EGYPTIAN HIEROGLYPH M036;Lo;0;L;;;;;N;;;;; +131E6;EGYPTIAN HIEROGLYPH M037;Lo;0;L;;;;;N;;;;; +131E7;EGYPTIAN HIEROGLYPH M038;Lo;0;L;;;;;N;;;;; +131E8;EGYPTIAN HIEROGLYPH M039;Lo;0;L;;;;;N;;;;; +131E9;EGYPTIAN HIEROGLYPH M040;Lo;0;L;;;;;N;;;;; +131EA;EGYPTIAN HIEROGLYPH M040A;Lo;0;L;;;;;N;;;;; +131EB;EGYPTIAN HIEROGLYPH M041;Lo;0;L;;;;;N;;;;; +131EC;EGYPTIAN HIEROGLYPH M042;Lo;0;L;;;;;N;;;;; +131ED;EGYPTIAN HIEROGLYPH M043;Lo;0;L;;;;;N;;;;; +131EE;EGYPTIAN HIEROGLYPH M044;Lo;0;L;;;;;N;;;;; +131EF;EGYPTIAN HIEROGLYPH N001;Lo;0;L;;;;;N;;;;; +131F0;EGYPTIAN HIEROGLYPH N002;Lo;0;L;;;;;N;;;;; +131F1;EGYPTIAN HIEROGLYPH N003;Lo;0;L;;;;;N;;;;; +131F2;EGYPTIAN HIEROGLYPH N004;Lo;0;L;;;;;N;;;;; +131F3;EGYPTIAN HIEROGLYPH N005;Lo;0;L;;;;;N;;;;; +131F4;EGYPTIAN HIEROGLYPH N006;Lo;0;L;;;;;N;;;;; +131F5;EGYPTIAN HIEROGLYPH N007;Lo;0;L;;;;;N;;;;; +131F6;EGYPTIAN HIEROGLYPH N008;Lo;0;L;;;;;N;;;;; +131F7;EGYPTIAN HIEROGLYPH N009;Lo;0;L;;;;;N;;;;; +131F8;EGYPTIAN HIEROGLYPH N010;Lo;0;L;;;;;N;;;;; +131F9;EGYPTIAN HIEROGLYPH N011;Lo;0;L;;;;;N;;;;; +131FA;EGYPTIAN HIEROGLYPH N012;Lo;0;L;;;;;N;;;;; +131FB;EGYPTIAN HIEROGLYPH N013;Lo;0;L;;;;;N;;;;; +131FC;EGYPTIAN HIEROGLYPH N014;Lo;0;L;;;;;N;;;;; +131FD;EGYPTIAN HIEROGLYPH N015;Lo;0;L;;;;;N;;;;; +131FE;EGYPTIAN HIEROGLYPH N016;Lo;0;L;;;;;N;;;;; +131FF;EGYPTIAN HIEROGLYPH N017;Lo;0;L;;;;;N;;;;; +13200;EGYPTIAN HIEROGLYPH N018;Lo;0;L;;;;;N;;;;; +13201;EGYPTIAN HIEROGLYPH N018A;Lo;0;L;;;;;N;;;;; +13202;EGYPTIAN HIEROGLYPH N018B;Lo;0;L;;;;;N;;;;; +13203;EGYPTIAN HIEROGLYPH N019;Lo;0;L;;;;;N;;;;; +13204;EGYPTIAN HIEROGLYPH N020;Lo;0;L;;;;;N;;;;; +13205;EGYPTIAN HIEROGLYPH N021;Lo;0;L;;;;;N;;;;; +13206;EGYPTIAN HIEROGLYPH N022;Lo;0;L;;;;;N;;;;; +13207;EGYPTIAN HIEROGLYPH N023;Lo;0;L;;;;;N;;;;; +13208;EGYPTIAN HIEROGLYPH N024;Lo;0;L;;;;;N;;;;; +13209;EGYPTIAN HIEROGLYPH N025;Lo;0;L;;;;;N;;;;; +1320A;EGYPTIAN HIEROGLYPH N025A;Lo;0;L;;;;;N;;;;; +1320B;EGYPTIAN HIEROGLYPH N026;Lo;0;L;;;;;N;;;;; +1320C;EGYPTIAN HIEROGLYPH N027;Lo;0;L;;;;;N;;;;; +1320D;EGYPTIAN HIEROGLYPH N028;Lo;0;L;;;;;N;;;;; +1320E;EGYPTIAN HIEROGLYPH N029;Lo;0;L;;;;;N;;;;; +1320F;EGYPTIAN HIEROGLYPH N030;Lo;0;L;;;;;N;;;;; +13210;EGYPTIAN HIEROGLYPH N031;Lo;0;L;;;;;N;;;;; +13211;EGYPTIAN HIEROGLYPH N032;Lo;0;L;;;;;N;;;;; +13212;EGYPTIAN HIEROGLYPH N033;Lo;0;L;;;;;N;;;;; +13213;EGYPTIAN HIEROGLYPH N033A;Lo;0;L;;;;;N;;;;; +13214;EGYPTIAN HIEROGLYPH N034;Lo;0;L;;;;;N;;;;; +13215;EGYPTIAN HIEROGLYPH N034A;Lo;0;L;;;;;N;;;;; +13216;EGYPTIAN HIEROGLYPH N035;Lo;0;L;;;;;N;;;;; +13217;EGYPTIAN HIEROGLYPH N035A;Lo;0;L;;;;;N;;;;; +13218;EGYPTIAN HIEROGLYPH N036;Lo;0;L;;;;;N;;;;; +13219;EGYPTIAN HIEROGLYPH N037;Lo;0;L;;;;;N;;;;; +1321A;EGYPTIAN HIEROGLYPH N037A;Lo;0;L;;;;;N;;;;; +1321B;EGYPTIAN HIEROGLYPH N038;Lo;0;L;;;;;N;;;;; +1321C;EGYPTIAN HIEROGLYPH N039;Lo;0;L;;;;;N;;;;; +1321D;EGYPTIAN HIEROGLYPH N040;Lo;0;L;;;;;N;;;;; +1321E;EGYPTIAN HIEROGLYPH N041;Lo;0;L;;;;;N;;;;; +1321F;EGYPTIAN HIEROGLYPH N042;Lo;0;L;;;;;N;;;;; +13220;EGYPTIAN HIEROGLYPH NL001;Lo;0;L;;;;;N;;;;; +13221;EGYPTIAN HIEROGLYPH NL002;Lo;0;L;;;;;N;;;;; +13222;EGYPTIAN HIEROGLYPH NL003;Lo;0;L;;;;;N;;;;; +13223;EGYPTIAN HIEROGLYPH NL004;Lo;0;L;;;;;N;;;;; +13224;EGYPTIAN HIEROGLYPH NL005;Lo;0;L;;;;;N;;;;; +13225;EGYPTIAN HIEROGLYPH NL005A;Lo;0;L;;;;;N;;;;; +13226;EGYPTIAN HIEROGLYPH NL006;Lo;0;L;;;;;N;;;;; +13227;EGYPTIAN HIEROGLYPH NL007;Lo;0;L;;;;;N;;;;; +13228;EGYPTIAN HIEROGLYPH NL008;Lo;0;L;;;;;N;;;;; +13229;EGYPTIAN HIEROGLYPH NL009;Lo;0;L;;;;;N;;;;; +1322A;EGYPTIAN HIEROGLYPH NL010;Lo;0;L;;;;;N;;;;; +1322B;EGYPTIAN HIEROGLYPH NL011;Lo;0;L;;;;;N;;;;; +1322C;EGYPTIAN HIEROGLYPH NL012;Lo;0;L;;;;;N;;;;; +1322D;EGYPTIAN HIEROGLYPH NL013;Lo;0;L;;;;;N;;;;; +1322E;EGYPTIAN HIEROGLYPH NL014;Lo;0;L;;;;;N;;;;; +1322F;EGYPTIAN HIEROGLYPH NL015;Lo;0;L;;;;;N;;;;; +13230;EGYPTIAN HIEROGLYPH NL016;Lo;0;L;;;;;N;;;;; +13231;EGYPTIAN HIEROGLYPH NL017;Lo;0;L;;;;;N;;;;; +13232;EGYPTIAN HIEROGLYPH NL017A;Lo;0;L;;;;;N;;;;; +13233;EGYPTIAN HIEROGLYPH NL018;Lo;0;L;;;;;N;;;;; +13234;EGYPTIAN HIEROGLYPH NL019;Lo;0;L;;;;;N;;;;; +13235;EGYPTIAN HIEROGLYPH NL020;Lo;0;L;;;;;N;;;;; +13236;EGYPTIAN HIEROGLYPH NU001;Lo;0;L;;;;;N;;;;; +13237;EGYPTIAN HIEROGLYPH NU002;Lo;0;L;;;;;N;;;;; +13238;EGYPTIAN HIEROGLYPH NU003;Lo;0;L;;;;;N;;;;; +13239;EGYPTIAN HIEROGLYPH NU004;Lo;0;L;;;;;N;;;;; +1323A;EGYPTIAN HIEROGLYPH NU005;Lo;0;L;;;;;N;;;;; +1323B;EGYPTIAN HIEROGLYPH NU006;Lo;0;L;;;;;N;;;;; +1323C;EGYPTIAN HIEROGLYPH NU007;Lo;0;L;;;;;N;;;;; +1323D;EGYPTIAN HIEROGLYPH NU008;Lo;0;L;;;;;N;;;;; +1323E;EGYPTIAN HIEROGLYPH NU009;Lo;0;L;;;;;N;;;;; +1323F;EGYPTIAN HIEROGLYPH NU010;Lo;0;L;;;;;N;;;;; +13240;EGYPTIAN HIEROGLYPH NU010A;Lo;0;L;;;;;N;;;;; +13241;EGYPTIAN HIEROGLYPH NU011;Lo;0;L;;;;;N;;;;; +13242;EGYPTIAN HIEROGLYPH NU011A;Lo;0;L;;;;;N;;;;; +13243;EGYPTIAN HIEROGLYPH NU012;Lo;0;L;;;;;N;;;;; +13244;EGYPTIAN HIEROGLYPH NU013;Lo;0;L;;;;;N;;;;; +13245;EGYPTIAN HIEROGLYPH NU014;Lo;0;L;;;;;N;;;;; +13246;EGYPTIAN HIEROGLYPH NU015;Lo;0;L;;;;;N;;;;; +13247;EGYPTIAN HIEROGLYPH NU016;Lo;0;L;;;;;N;;;;; +13248;EGYPTIAN HIEROGLYPH NU017;Lo;0;L;;;;;N;;;;; +13249;EGYPTIAN HIEROGLYPH NU018;Lo;0;L;;;;;N;;;;; +1324A;EGYPTIAN HIEROGLYPH NU018A;Lo;0;L;;;;;N;;;;; +1324B;EGYPTIAN HIEROGLYPH NU019;Lo;0;L;;;;;N;;;;; +1324C;EGYPTIAN HIEROGLYPH NU020;Lo;0;L;;;;;N;;;;; +1324D;EGYPTIAN HIEROGLYPH NU021;Lo;0;L;;;;;N;;;;; +1324E;EGYPTIAN HIEROGLYPH NU022;Lo;0;L;;;;;N;;;;; +1324F;EGYPTIAN HIEROGLYPH NU022A;Lo;0;L;;;;;N;;;;; +13250;EGYPTIAN HIEROGLYPH O001;Lo;0;L;;;;;N;;;;; +13251;EGYPTIAN HIEROGLYPH O001A;Lo;0;L;;;;;N;;;;; +13252;EGYPTIAN HIEROGLYPH O002;Lo;0;L;;;;;N;;;;; +13253;EGYPTIAN HIEROGLYPH O003;Lo;0;L;;;;;N;;;;; +13254;EGYPTIAN HIEROGLYPH O004;Lo;0;L;;;;;N;;;;; +13255;EGYPTIAN HIEROGLYPH O005;Lo;0;L;;;;;N;;;;; +13256;EGYPTIAN HIEROGLYPH O005A;Lo;0;L;;;;;N;;;;; +13257;EGYPTIAN HIEROGLYPH O006;Lo;0;L;;;;;N;;;;; +13258;EGYPTIAN HIEROGLYPH O006A;Lo;0;L;;;;;N;;;;; +13259;EGYPTIAN HIEROGLYPH O006B;Lo;0;L;;;;;N;;;;; +1325A;EGYPTIAN HIEROGLYPH O006C;Lo;0;L;;;;;N;;;;; +1325B;EGYPTIAN HIEROGLYPH O006D;Lo;0;L;;;;;N;;;;; +1325C;EGYPTIAN HIEROGLYPH O006E;Lo;0;L;;;;;N;;;;; +1325D;EGYPTIAN HIEROGLYPH O006F;Lo;0;L;;;;;N;;;;; +1325E;EGYPTIAN HIEROGLYPH O007;Lo;0;L;;;;;N;;;;; +1325F;EGYPTIAN HIEROGLYPH O008;Lo;0;L;;;;;N;;;;; +13260;EGYPTIAN HIEROGLYPH O009;Lo;0;L;;;;;N;;;;; +13261;EGYPTIAN HIEROGLYPH O010;Lo;0;L;;;;;N;;;;; +13262;EGYPTIAN HIEROGLYPH O010A;Lo;0;L;;;;;N;;;;; +13263;EGYPTIAN HIEROGLYPH O010B;Lo;0;L;;;;;N;;;;; +13264;EGYPTIAN HIEROGLYPH O010C;Lo;0;L;;;;;N;;;;; +13265;EGYPTIAN HIEROGLYPH O011;Lo;0;L;;;;;N;;;;; +13266;EGYPTIAN HIEROGLYPH O012;Lo;0;L;;;;;N;;;;; +13267;EGYPTIAN HIEROGLYPH O013;Lo;0;L;;;;;N;;;;; +13268;EGYPTIAN HIEROGLYPH O014;Lo;0;L;;;;;N;;;;; +13269;EGYPTIAN HIEROGLYPH O015;Lo;0;L;;;;;N;;;;; +1326A;EGYPTIAN HIEROGLYPH O016;Lo;0;L;;;;;N;;;;; +1326B;EGYPTIAN HIEROGLYPH O017;Lo;0;L;;;;;N;;;;; +1326C;EGYPTIAN HIEROGLYPH O018;Lo;0;L;;;;;N;;;;; +1326D;EGYPTIAN HIEROGLYPH O019;Lo;0;L;;;;;N;;;;; +1326E;EGYPTIAN HIEROGLYPH O019A;Lo;0;L;;;;;N;;;;; +1326F;EGYPTIAN HIEROGLYPH O020;Lo;0;L;;;;;N;;;;; +13270;EGYPTIAN HIEROGLYPH O020A;Lo;0;L;;;;;N;;;;; +13271;EGYPTIAN HIEROGLYPH O021;Lo;0;L;;;;;N;;;;; +13272;EGYPTIAN HIEROGLYPH O022;Lo;0;L;;;;;N;;;;; +13273;EGYPTIAN HIEROGLYPH O023;Lo;0;L;;;;;N;;;;; +13274;EGYPTIAN HIEROGLYPH O024;Lo;0;L;;;;;N;;;;; +13275;EGYPTIAN HIEROGLYPH O024A;Lo;0;L;;;;;N;;;;; +13276;EGYPTIAN HIEROGLYPH O025;Lo;0;L;;;;;N;;;;; +13277;EGYPTIAN HIEROGLYPH O025A;Lo;0;L;;;;;N;;;;; +13278;EGYPTIAN HIEROGLYPH O026;Lo;0;L;;;;;N;;;;; +13279;EGYPTIAN HIEROGLYPH O027;Lo;0;L;;;;;N;;;;; +1327A;EGYPTIAN HIEROGLYPH O028;Lo;0;L;;;;;N;;;;; +1327B;EGYPTIAN HIEROGLYPH O029;Lo;0;L;;;;;N;;;;; +1327C;EGYPTIAN HIEROGLYPH O029A;Lo;0;L;;;;;N;;;;; +1327D;EGYPTIAN HIEROGLYPH O030;Lo;0;L;;;;;N;;;;; +1327E;EGYPTIAN HIEROGLYPH O030A;Lo;0;L;;;;;N;;;;; +1327F;EGYPTIAN HIEROGLYPH O031;Lo;0;L;;;;;N;;;;; +13280;EGYPTIAN HIEROGLYPH O032;Lo;0;L;;;;;N;;;;; +13281;EGYPTIAN HIEROGLYPH O033;Lo;0;L;;;;;N;;;;; +13282;EGYPTIAN HIEROGLYPH O033A;Lo;0;L;;;;;N;;;;; +13283;EGYPTIAN HIEROGLYPH O034;Lo;0;L;;;;;N;;;;; +13284;EGYPTIAN HIEROGLYPH O035;Lo;0;L;;;;;N;;;;; +13285;EGYPTIAN HIEROGLYPH O036;Lo;0;L;;;;;N;;;;; +13286;EGYPTIAN HIEROGLYPH O036A;Lo;0;L;;;;;N;;;;; +13287;EGYPTIAN HIEROGLYPH O036B;Lo;0;L;;;;;N;;;;; +13288;EGYPTIAN HIEROGLYPH O036C;Lo;0;L;;;;;N;;;;; +13289;EGYPTIAN HIEROGLYPH O036D;Lo;0;L;;;;;N;;;;; +1328A;EGYPTIAN HIEROGLYPH O037;Lo;0;L;;;;;N;;;;; +1328B;EGYPTIAN HIEROGLYPH O038;Lo;0;L;;;;;N;;;;; +1328C;EGYPTIAN HIEROGLYPH O039;Lo;0;L;;;;;N;;;;; +1328D;EGYPTIAN HIEROGLYPH O040;Lo;0;L;;;;;N;;;;; +1328E;EGYPTIAN HIEROGLYPH O041;Lo;0;L;;;;;N;;;;; +1328F;EGYPTIAN HIEROGLYPH O042;Lo;0;L;;;;;N;;;;; +13290;EGYPTIAN HIEROGLYPH O043;Lo;0;L;;;;;N;;;;; +13291;EGYPTIAN HIEROGLYPH O044;Lo;0;L;;;;;N;;;;; +13292;EGYPTIAN HIEROGLYPH O045;Lo;0;L;;;;;N;;;;; +13293;EGYPTIAN HIEROGLYPH O046;Lo;0;L;;;;;N;;;;; +13294;EGYPTIAN HIEROGLYPH O047;Lo;0;L;;;;;N;;;;; +13295;EGYPTIAN HIEROGLYPH O048;Lo;0;L;;;;;N;;;;; +13296;EGYPTIAN HIEROGLYPH O049;Lo;0;L;;;;;N;;;;; +13297;EGYPTIAN HIEROGLYPH O050;Lo;0;L;;;;;N;;;;; +13298;EGYPTIAN HIEROGLYPH O050A;Lo;0;L;;;;;N;;;;; +13299;EGYPTIAN HIEROGLYPH O050B;Lo;0;L;;;;;N;;;;; +1329A;EGYPTIAN HIEROGLYPH O051;Lo;0;L;;;;;N;;;;; +1329B;EGYPTIAN HIEROGLYPH P001;Lo;0;L;;;;;N;;;;; +1329C;EGYPTIAN HIEROGLYPH P001A;Lo;0;L;;;;;N;;;;; +1329D;EGYPTIAN HIEROGLYPH P002;Lo;0;L;;;;;N;;;;; +1329E;EGYPTIAN HIEROGLYPH P003;Lo;0;L;;;;;N;;;;; +1329F;EGYPTIAN HIEROGLYPH P003A;Lo;0;L;;;;;N;;;;; +132A0;EGYPTIAN HIEROGLYPH P004;Lo;0;L;;;;;N;;;;; +132A1;EGYPTIAN HIEROGLYPH P005;Lo;0;L;;;;;N;;;;; +132A2;EGYPTIAN HIEROGLYPH P006;Lo;0;L;;;;;N;;;;; +132A3;EGYPTIAN HIEROGLYPH P007;Lo;0;L;;;;;N;;;;; +132A4;EGYPTIAN HIEROGLYPH P008;Lo;0;L;;;;;N;;;;; +132A5;EGYPTIAN HIEROGLYPH P009;Lo;0;L;;;;;N;;;;; +132A6;EGYPTIAN HIEROGLYPH P010;Lo;0;L;;;;;N;;;;; +132A7;EGYPTIAN HIEROGLYPH P011;Lo;0;L;;;;;N;;;;; +132A8;EGYPTIAN HIEROGLYPH Q001;Lo;0;L;;;;;N;;;;; +132A9;EGYPTIAN HIEROGLYPH Q002;Lo;0;L;;;;;N;;;;; +132AA;EGYPTIAN HIEROGLYPH Q003;Lo;0;L;;;;;N;;;;; +132AB;EGYPTIAN HIEROGLYPH Q004;Lo;0;L;;;;;N;;;;; +132AC;EGYPTIAN HIEROGLYPH Q005;Lo;0;L;;;;;N;;;;; +132AD;EGYPTIAN HIEROGLYPH Q006;Lo;0;L;;;;;N;;;;; +132AE;EGYPTIAN HIEROGLYPH Q007;Lo;0;L;;;;;N;;;;; +132AF;EGYPTIAN HIEROGLYPH R001;Lo;0;L;;;;;N;;;;; +132B0;EGYPTIAN HIEROGLYPH R002;Lo;0;L;;;;;N;;;;; +132B1;EGYPTIAN HIEROGLYPH R002A;Lo;0;L;;;;;N;;;;; +132B2;EGYPTIAN HIEROGLYPH R003;Lo;0;L;;;;;N;;;;; +132B3;EGYPTIAN HIEROGLYPH R003A;Lo;0;L;;;;;N;;;;; +132B4;EGYPTIAN HIEROGLYPH R003B;Lo;0;L;;;;;N;;;;; +132B5;EGYPTIAN HIEROGLYPH R004;Lo;0;L;;;;;N;;;;; +132B6;EGYPTIAN HIEROGLYPH R005;Lo;0;L;;;;;N;;;;; +132B7;EGYPTIAN HIEROGLYPH R006;Lo;0;L;;;;;N;;;;; +132B8;EGYPTIAN HIEROGLYPH R007;Lo;0;L;;;;;N;;;;; +132B9;EGYPTIAN HIEROGLYPH R008;Lo;0;L;;;;;N;;;;; +132BA;EGYPTIAN HIEROGLYPH R009;Lo;0;L;;;;;N;;;;; +132BB;EGYPTIAN HIEROGLYPH R010;Lo;0;L;;;;;N;;;;; +132BC;EGYPTIAN HIEROGLYPH R010A;Lo;0;L;;;;;N;;;;; +132BD;EGYPTIAN HIEROGLYPH R011;Lo;0;L;;;;;N;;;;; +132BE;EGYPTIAN HIEROGLYPH R012;Lo;0;L;;;;;N;;;;; +132BF;EGYPTIAN HIEROGLYPH R013;Lo;0;L;;;;;N;;;;; +132C0;EGYPTIAN HIEROGLYPH R014;Lo;0;L;;;;;N;;;;; +132C1;EGYPTIAN HIEROGLYPH R015;Lo;0;L;;;;;N;;;;; +132C2;EGYPTIAN HIEROGLYPH R016;Lo;0;L;;;;;N;;;;; +132C3;EGYPTIAN HIEROGLYPH R016A;Lo;0;L;;;;;N;;;;; +132C4;EGYPTIAN HIEROGLYPH R017;Lo;0;L;;;;;N;;;;; +132C5;EGYPTIAN HIEROGLYPH R018;Lo;0;L;;;;;N;;;;; +132C6;EGYPTIAN HIEROGLYPH R019;Lo;0;L;;;;;N;;;;; +132C7;EGYPTIAN HIEROGLYPH R020;Lo;0;L;;;;;N;;;;; +132C8;EGYPTIAN HIEROGLYPH R021;Lo;0;L;;;;;N;;;;; +132C9;EGYPTIAN HIEROGLYPH R022;Lo;0;L;;;;;N;;;;; +132CA;EGYPTIAN HIEROGLYPH R023;Lo;0;L;;;;;N;;;;; +132CB;EGYPTIAN HIEROGLYPH R024;Lo;0;L;;;;;N;;;;; +132CC;EGYPTIAN HIEROGLYPH R025;Lo;0;L;;;;;N;;;;; +132CD;EGYPTIAN HIEROGLYPH R026;Lo;0;L;;;;;N;;;;; +132CE;EGYPTIAN HIEROGLYPH R027;Lo;0;L;;;;;N;;;;; +132CF;EGYPTIAN HIEROGLYPH R028;Lo;0;L;;;;;N;;;;; +132D0;EGYPTIAN HIEROGLYPH R029;Lo;0;L;;;;;N;;;;; +132D1;EGYPTIAN HIEROGLYPH S001;Lo;0;L;;;;;N;;;;; +132D2;EGYPTIAN HIEROGLYPH S002;Lo;0;L;;;;;N;;;;; +132D3;EGYPTIAN HIEROGLYPH S002A;Lo;0;L;;;;;N;;;;; +132D4;EGYPTIAN HIEROGLYPH S003;Lo;0;L;;;;;N;;;;; +132D5;EGYPTIAN HIEROGLYPH S004;Lo;0;L;;;;;N;;;;; +132D6;EGYPTIAN HIEROGLYPH S005;Lo;0;L;;;;;N;;;;; +132D7;EGYPTIAN HIEROGLYPH S006;Lo;0;L;;;;;N;;;;; +132D8;EGYPTIAN HIEROGLYPH S006A;Lo;0;L;;;;;N;;;;; +132D9;EGYPTIAN HIEROGLYPH S007;Lo;0;L;;;;;N;;;;; +132DA;EGYPTIAN HIEROGLYPH S008;Lo;0;L;;;;;N;;;;; +132DB;EGYPTIAN HIEROGLYPH S009;Lo;0;L;;;;;N;;;;; +132DC;EGYPTIAN HIEROGLYPH S010;Lo;0;L;;;;;N;;;;; +132DD;EGYPTIAN HIEROGLYPH S011;Lo;0;L;;;;;N;;;;; +132DE;EGYPTIAN HIEROGLYPH S012;Lo;0;L;;;;;N;;;;; +132DF;EGYPTIAN HIEROGLYPH S013;Lo;0;L;;;;;N;;;;; +132E0;EGYPTIAN HIEROGLYPH S014;Lo;0;L;;;;;N;;;;; +132E1;EGYPTIAN HIEROGLYPH S014A;Lo;0;L;;;;;N;;;;; +132E2;EGYPTIAN HIEROGLYPH S014B;Lo;0;L;;;;;N;;;;; +132E3;EGYPTIAN HIEROGLYPH S015;Lo;0;L;;;;;N;;;;; +132E4;EGYPTIAN HIEROGLYPH S016;Lo;0;L;;;;;N;;;;; +132E5;EGYPTIAN HIEROGLYPH S017;Lo;0;L;;;;;N;;;;; +132E6;EGYPTIAN HIEROGLYPH S017A;Lo;0;L;;;;;N;;;;; +132E7;EGYPTIAN HIEROGLYPH S018;Lo;0;L;;;;;N;;;;; +132E8;EGYPTIAN HIEROGLYPH S019;Lo;0;L;;;;;N;;;;; +132E9;EGYPTIAN HIEROGLYPH S020;Lo;0;L;;;;;N;;;;; +132EA;EGYPTIAN HIEROGLYPH S021;Lo;0;L;;;;;N;;;;; +132EB;EGYPTIAN HIEROGLYPH S022;Lo;0;L;;;;;N;;;;; +132EC;EGYPTIAN HIEROGLYPH S023;Lo;0;L;;;;;N;;;;; +132ED;EGYPTIAN HIEROGLYPH S024;Lo;0;L;;;;;N;;;;; +132EE;EGYPTIAN HIEROGLYPH S025;Lo;0;L;;;;;N;;;;; +132EF;EGYPTIAN HIEROGLYPH S026;Lo;0;L;;;;;N;;;;; +132F0;EGYPTIAN HIEROGLYPH S026A;Lo;0;L;;;;;N;;;;; +132F1;EGYPTIAN HIEROGLYPH S026B;Lo;0;L;;;;;N;;;;; +132F2;EGYPTIAN HIEROGLYPH S027;Lo;0;L;;;;;N;;;;; +132F3;EGYPTIAN HIEROGLYPH S028;Lo;0;L;;;;;N;;;;; +132F4;EGYPTIAN HIEROGLYPH S029;Lo;0;L;;;;;N;;;;; +132F5;EGYPTIAN HIEROGLYPH S030;Lo;0;L;;;;;N;;;;; +132F6;EGYPTIAN HIEROGLYPH S031;Lo;0;L;;;;;N;;;;; +132F7;EGYPTIAN HIEROGLYPH S032;Lo;0;L;;;;;N;;;;; +132F8;EGYPTIAN HIEROGLYPH S033;Lo;0;L;;;;;N;;;;; +132F9;EGYPTIAN HIEROGLYPH S034;Lo;0;L;;;;;N;;;;; +132FA;EGYPTIAN HIEROGLYPH S035;Lo;0;L;;;;;N;;;;; +132FB;EGYPTIAN HIEROGLYPH S035A;Lo;0;L;;;;;N;;;;; +132FC;EGYPTIAN HIEROGLYPH S036;Lo;0;L;;;;;N;;;;; +132FD;EGYPTIAN HIEROGLYPH S037;Lo;0;L;;;;;N;;;;; +132FE;EGYPTIAN HIEROGLYPH S038;Lo;0;L;;;;;N;;;;; +132FF;EGYPTIAN HIEROGLYPH S039;Lo;0;L;;;;;N;;;;; +13300;EGYPTIAN HIEROGLYPH S040;Lo;0;L;;;;;N;;;;; +13301;EGYPTIAN HIEROGLYPH S041;Lo;0;L;;;;;N;;;;; +13302;EGYPTIAN HIEROGLYPH S042;Lo;0;L;;;;;N;;;;; +13303;EGYPTIAN HIEROGLYPH S043;Lo;0;L;;;;;N;;;;; +13304;EGYPTIAN HIEROGLYPH S044;Lo;0;L;;;;;N;;;;; +13305;EGYPTIAN HIEROGLYPH S045;Lo;0;L;;;;;N;;;;; +13306;EGYPTIAN HIEROGLYPH S046;Lo;0;L;;;;;N;;;;; +13307;EGYPTIAN HIEROGLYPH T001;Lo;0;L;;;;;N;;;;; +13308;EGYPTIAN HIEROGLYPH T002;Lo;0;L;;;;;N;;;;; +13309;EGYPTIAN HIEROGLYPH T003;Lo;0;L;;;;;N;;;;; +1330A;EGYPTIAN HIEROGLYPH T003A;Lo;0;L;;;;;N;;;;; +1330B;EGYPTIAN HIEROGLYPH T004;Lo;0;L;;;;;N;;;;; +1330C;EGYPTIAN HIEROGLYPH T005;Lo;0;L;;;;;N;;;;; +1330D;EGYPTIAN HIEROGLYPH T006;Lo;0;L;;;;;N;;;;; +1330E;EGYPTIAN HIEROGLYPH T007;Lo;0;L;;;;;N;;;;; +1330F;EGYPTIAN HIEROGLYPH T007A;Lo;0;L;;;;;N;;;;; +13310;EGYPTIAN HIEROGLYPH T008;Lo;0;L;;;;;N;;;;; +13311;EGYPTIAN HIEROGLYPH T008A;Lo;0;L;;;;;N;;;;; +13312;EGYPTIAN HIEROGLYPH T009;Lo;0;L;;;;;N;;;;; +13313;EGYPTIAN HIEROGLYPH T009A;Lo;0;L;;;;;N;;;;; +13314;EGYPTIAN HIEROGLYPH T010;Lo;0;L;;;;;N;;;;; +13315;EGYPTIAN HIEROGLYPH T011;Lo;0;L;;;;;N;;;;; +13316;EGYPTIAN HIEROGLYPH T011A;Lo;0;L;;;;;N;;;;; +13317;EGYPTIAN HIEROGLYPH T012;Lo;0;L;;;;;N;;;;; +13318;EGYPTIAN HIEROGLYPH T013;Lo;0;L;;;;;N;;;;; +13319;EGYPTIAN HIEROGLYPH T014;Lo;0;L;;;;;N;;;;; +1331A;EGYPTIAN HIEROGLYPH T015;Lo;0;L;;;;;N;;;;; +1331B;EGYPTIAN HIEROGLYPH T016;Lo;0;L;;;;;N;;;;; +1331C;EGYPTIAN HIEROGLYPH T016A;Lo;0;L;;;;;N;;;;; +1331D;EGYPTIAN HIEROGLYPH T017;Lo;0;L;;;;;N;;;;; +1331E;EGYPTIAN HIEROGLYPH T018;Lo;0;L;;;;;N;;;;; +1331F;EGYPTIAN HIEROGLYPH T019;Lo;0;L;;;;;N;;;;; +13320;EGYPTIAN HIEROGLYPH T020;Lo;0;L;;;;;N;;;;; +13321;EGYPTIAN HIEROGLYPH T021;Lo;0;L;;;;;N;;;;; +13322;EGYPTIAN HIEROGLYPH T022;Lo;0;L;;;;;N;;;;; +13323;EGYPTIAN HIEROGLYPH T023;Lo;0;L;;;;;N;;;;; +13324;EGYPTIAN HIEROGLYPH T024;Lo;0;L;;;;;N;;;;; +13325;EGYPTIAN HIEROGLYPH T025;Lo;0;L;;;;;N;;;;; +13326;EGYPTIAN HIEROGLYPH T026;Lo;0;L;;;;;N;;;;; +13327;EGYPTIAN HIEROGLYPH T027;Lo;0;L;;;;;N;;;;; +13328;EGYPTIAN HIEROGLYPH T028;Lo;0;L;;;;;N;;;;; +13329;EGYPTIAN HIEROGLYPH T029;Lo;0;L;;;;;N;;;;; +1332A;EGYPTIAN HIEROGLYPH T030;Lo;0;L;;;;;N;;;;; +1332B;EGYPTIAN HIEROGLYPH T031;Lo;0;L;;;;;N;;;;; +1332C;EGYPTIAN HIEROGLYPH T032;Lo;0;L;;;;;N;;;;; +1332D;EGYPTIAN HIEROGLYPH T032A;Lo;0;L;;;;;N;;;;; +1332E;EGYPTIAN HIEROGLYPH T033;Lo;0;L;;;;;N;;;;; +1332F;EGYPTIAN HIEROGLYPH T033A;Lo;0;L;;;;;N;;;;; +13330;EGYPTIAN HIEROGLYPH T034;Lo;0;L;;;;;N;;;;; +13331;EGYPTIAN HIEROGLYPH T035;Lo;0;L;;;;;N;;;;; +13332;EGYPTIAN HIEROGLYPH T036;Lo;0;L;;;;;N;;;;; +13333;EGYPTIAN HIEROGLYPH U001;Lo;0;L;;;;;N;;;;; +13334;EGYPTIAN HIEROGLYPH U002;Lo;0;L;;;;;N;;;;; +13335;EGYPTIAN HIEROGLYPH U003;Lo;0;L;;;;;N;;;;; +13336;EGYPTIAN HIEROGLYPH U004;Lo;0;L;;;;;N;;;;; +13337;EGYPTIAN HIEROGLYPH U005;Lo;0;L;;;;;N;;;;; +13338;EGYPTIAN HIEROGLYPH U006;Lo;0;L;;;;;N;;;;; +13339;EGYPTIAN HIEROGLYPH U006A;Lo;0;L;;;;;N;;;;; +1333A;EGYPTIAN HIEROGLYPH U006B;Lo;0;L;;;;;N;;;;; +1333B;EGYPTIAN HIEROGLYPH U007;Lo;0;L;;;;;N;;;;; +1333C;EGYPTIAN HIEROGLYPH U008;Lo;0;L;;;;;N;;;;; +1333D;EGYPTIAN HIEROGLYPH U009;Lo;0;L;;;;;N;;;;; +1333E;EGYPTIAN HIEROGLYPH U010;Lo;0;L;;;;;N;;;;; +1333F;EGYPTIAN HIEROGLYPH U011;Lo;0;L;;;;;N;;;;; +13340;EGYPTIAN HIEROGLYPH U012;Lo;0;L;;;;;N;;;;; +13341;EGYPTIAN HIEROGLYPH U013;Lo;0;L;;;;;N;;;;; +13342;EGYPTIAN HIEROGLYPH U014;Lo;0;L;;;;;N;;;;; +13343;EGYPTIAN HIEROGLYPH U015;Lo;0;L;;;;;N;;;;; +13344;EGYPTIAN HIEROGLYPH U016;Lo;0;L;;;;;N;;;;; +13345;EGYPTIAN HIEROGLYPH U017;Lo;0;L;;;;;N;;;;; +13346;EGYPTIAN HIEROGLYPH U018;Lo;0;L;;;;;N;;;;; +13347;EGYPTIAN HIEROGLYPH U019;Lo;0;L;;;;;N;;;;; +13348;EGYPTIAN HIEROGLYPH U020;Lo;0;L;;;;;N;;;;; +13349;EGYPTIAN HIEROGLYPH U021;Lo;0;L;;;;;N;;;;; +1334A;EGYPTIAN HIEROGLYPH U022;Lo;0;L;;;;;N;;;;; +1334B;EGYPTIAN HIEROGLYPH U023;Lo;0;L;;;;;N;;;;; +1334C;EGYPTIAN HIEROGLYPH U023A;Lo;0;L;;;;;N;;;;; +1334D;EGYPTIAN HIEROGLYPH U024;Lo;0;L;;;;;N;;;;; +1334E;EGYPTIAN HIEROGLYPH U025;Lo;0;L;;;;;N;;;;; +1334F;EGYPTIAN HIEROGLYPH U026;Lo;0;L;;;;;N;;;;; +13350;EGYPTIAN HIEROGLYPH U027;Lo;0;L;;;;;N;;;;; +13351;EGYPTIAN HIEROGLYPH U028;Lo;0;L;;;;;N;;;;; +13352;EGYPTIAN HIEROGLYPH U029;Lo;0;L;;;;;N;;;;; +13353;EGYPTIAN HIEROGLYPH U029A;Lo;0;L;;;;;N;;;;; +13354;EGYPTIAN HIEROGLYPH U030;Lo;0;L;;;;;N;;;;; +13355;EGYPTIAN HIEROGLYPH U031;Lo;0;L;;;;;N;;;;; +13356;EGYPTIAN HIEROGLYPH U032;Lo;0;L;;;;;N;;;;; +13357;EGYPTIAN HIEROGLYPH U032A;Lo;0;L;;;;;N;;;;; +13358;EGYPTIAN HIEROGLYPH U033;Lo;0;L;;;;;N;;;;; +13359;EGYPTIAN HIEROGLYPH U034;Lo;0;L;;;;;N;;;;; +1335A;EGYPTIAN HIEROGLYPH U035;Lo;0;L;;;;;N;;;;; +1335B;EGYPTIAN HIEROGLYPH U036;Lo;0;L;;;;;N;;;;; +1335C;EGYPTIAN HIEROGLYPH U037;Lo;0;L;;;;;N;;;;; +1335D;EGYPTIAN HIEROGLYPH U038;Lo;0;L;;;;;N;;;;; +1335E;EGYPTIAN HIEROGLYPH U039;Lo;0;L;;;;;N;;;;; +1335F;EGYPTIAN HIEROGLYPH U040;Lo;0;L;;;;;N;;;;; +13360;EGYPTIAN HIEROGLYPH U041;Lo;0;L;;;;;N;;;;; +13361;EGYPTIAN HIEROGLYPH U042;Lo;0;L;;;;;N;;;;; +13362;EGYPTIAN HIEROGLYPH V001;Lo;0;L;;;;;N;;;;; +13363;EGYPTIAN HIEROGLYPH V001A;Lo;0;L;;;;;N;;;;; +13364;EGYPTIAN HIEROGLYPH V001B;Lo;0;L;;;;;N;;;;; +13365;EGYPTIAN HIEROGLYPH V001C;Lo;0;L;;;;;N;;;;; +13366;EGYPTIAN HIEROGLYPH V001D;Lo;0;L;;;;;N;;;;; +13367;EGYPTIAN HIEROGLYPH V001E;Lo;0;L;;;;;N;;;;; +13368;EGYPTIAN HIEROGLYPH V001F;Lo;0;L;;;;;N;;;;; +13369;EGYPTIAN HIEROGLYPH V001G;Lo;0;L;;;;;N;;;;; +1336A;EGYPTIAN HIEROGLYPH V001H;Lo;0;L;;;;;N;;;;; +1336B;EGYPTIAN HIEROGLYPH V001I;Lo;0;L;;;;;N;;;;; +1336C;EGYPTIAN HIEROGLYPH V002;Lo;0;L;;;;;N;;;;; +1336D;EGYPTIAN HIEROGLYPH V002A;Lo;0;L;;;;;N;;;;; +1336E;EGYPTIAN HIEROGLYPH V003;Lo;0;L;;;;;N;;;;; +1336F;EGYPTIAN HIEROGLYPH V004;Lo;0;L;;;;;N;;;;; +13370;EGYPTIAN HIEROGLYPH V005;Lo;0;L;;;;;N;;;;; +13371;EGYPTIAN HIEROGLYPH V006;Lo;0;L;;;;;N;;;;; +13372;EGYPTIAN HIEROGLYPH V007;Lo;0;L;;;;;N;;;;; +13373;EGYPTIAN HIEROGLYPH V007A;Lo;0;L;;;;;N;;;;; +13374;EGYPTIAN HIEROGLYPH V007B;Lo;0;L;;;;;N;;;;; +13375;EGYPTIAN HIEROGLYPH V008;Lo;0;L;;;;;N;;;;; +13376;EGYPTIAN HIEROGLYPH V009;Lo;0;L;;;;;N;;;;; +13377;EGYPTIAN HIEROGLYPH V010;Lo;0;L;;;;;N;;;;; +13378;EGYPTIAN HIEROGLYPH V011;Lo;0;L;;;;;N;;;;; +13379;EGYPTIAN HIEROGLYPH V011A;Lo;0;L;;;;;N;;;;; +1337A;EGYPTIAN HIEROGLYPH V011B;Lo;0;L;;;;;N;;;;; +1337B;EGYPTIAN HIEROGLYPH V011C;Lo;0;L;;;;;N;;;;; +1337C;EGYPTIAN HIEROGLYPH V012;Lo;0;L;;;;;N;;;;; +1337D;EGYPTIAN HIEROGLYPH V012A;Lo;0;L;;;;;N;;;;; +1337E;EGYPTIAN HIEROGLYPH V012B;Lo;0;L;;;;;N;;;;; +1337F;EGYPTIAN HIEROGLYPH V013;Lo;0;L;;;;;N;;;;; +13380;EGYPTIAN HIEROGLYPH V014;Lo;0;L;;;;;N;;;;; +13381;EGYPTIAN HIEROGLYPH V015;Lo;0;L;;;;;N;;;;; +13382;EGYPTIAN HIEROGLYPH V016;Lo;0;L;;;;;N;;;;; +13383;EGYPTIAN HIEROGLYPH V017;Lo;0;L;;;;;N;;;;; +13384;EGYPTIAN HIEROGLYPH V018;Lo;0;L;;;;;N;;;;; +13385;EGYPTIAN HIEROGLYPH V019;Lo;0;L;;;;;N;;;;; +13386;EGYPTIAN HIEROGLYPH V020;Lo;0;L;;;;;N;;;;; +13387;EGYPTIAN HIEROGLYPH V020A;Lo;0;L;;;;;N;;;;; +13388;EGYPTIAN HIEROGLYPH V020B;Lo;0;L;;;;;N;;;;; +13389;EGYPTIAN HIEROGLYPH V020C;Lo;0;L;;;;;N;;;;; +1338A;EGYPTIAN HIEROGLYPH V020D;Lo;0;L;;;;;N;;;;; +1338B;EGYPTIAN HIEROGLYPH V020E;Lo;0;L;;;;;N;;;;; +1338C;EGYPTIAN HIEROGLYPH V020F;Lo;0;L;;;;;N;;;;; +1338D;EGYPTIAN HIEROGLYPH V020G;Lo;0;L;;;;;N;;;;; +1338E;EGYPTIAN HIEROGLYPH V020H;Lo;0;L;;;;;N;;;;; +1338F;EGYPTIAN HIEROGLYPH V020I;Lo;0;L;;;;;N;;;;; +13390;EGYPTIAN HIEROGLYPH V020J;Lo;0;L;;;;;N;;;;; +13391;EGYPTIAN HIEROGLYPH V020K;Lo;0;L;;;;;N;;;;; +13392;EGYPTIAN HIEROGLYPH V020L;Lo;0;L;;;;;N;;;;; +13393;EGYPTIAN HIEROGLYPH V021;Lo;0;L;;;;;N;;;;; +13394;EGYPTIAN HIEROGLYPH V022;Lo;0;L;;;;;N;;;;; +13395;EGYPTIAN HIEROGLYPH V023;Lo;0;L;;;;;N;;;;; +13396;EGYPTIAN HIEROGLYPH V023A;Lo;0;L;;;;;N;;;;; +13397;EGYPTIAN HIEROGLYPH V024;Lo;0;L;;;;;N;;;;; +13398;EGYPTIAN HIEROGLYPH V025;Lo;0;L;;;;;N;;;;; +13399;EGYPTIAN HIEROGLYPH V026;Lo;0;L;;;;;N;;;;; +1339A;EGYPTIAN HIEROGLYPH V027;Lo;0;L;;;;;N;;;;; +1339B;EGYPTIAN HIEROGLYPH V028;Lo;0;L;;;;;N;;;;; +1339C;EGYPTIAN HIEROGLYPH V028A;Lo;0;L;;;;;N;;;;; +1339D;EGYPTIAN HIEROGLYPH V029;Lo;0;L;;;;;N;;;;; +1339E;EGYPTIAN HIEROGLYPH V029A;Lo;0;L;;;;;N;;;;; +1339F;EGYPTIAN HIEROGLYPH V030;Lo;0;L;;;;;N;;;;; +133A0;EGYPTIAN HIEROGLYPH V030A;Lo;0;L;;;;;N;;;;; +133A1;EGYPTIAN HIEROGLYPH V031;Lo;0;L;;;;;N;;;;; +133A2;EGYPTIAN HIEROGLYPH V031A;Lo;0;L;;;;;N;;;;; +133A3;EGYPTIAN HIEROGLYPH V032;Lo;0;L;;;;;N;;;;; +133A4;EGYPTIAN HIEROGLYPH V033;Lo;0;L;;;;;N;;;;; +133A5;EGYPTIAN HIEROGLYPH V033A;Lo;0;L;;;;;N;;;;; +133A6;EGYPTIAN HIEROGLYPH V034;Lo;0;L;;;;;N;;;;; +133A7;EGYPTIAN HIEROGLYPH V035;Lo;0;L;;;;;N;;;;; +133A8;EGYPTIAN HIEROGLYPH V036;Lo;0;L;;;;;N;;;;; +133A9;EGYPTIAN HIEROGLYPH V037;Lo;0;L;;;;;N;;;;; +133AA;EGYPTIAN HIEROGLYPH V037A;Lo;0;L;;;;;N;;;;; +133AB;EGYPTIAN HIEROGLYPH V038;Lo;0;L;;;;;N;;;;; +133AC;EGYPTIAN HIEROGLYPH V039;Lo;0;L;;;;;N;;;;; +133AD;EGYPTIAN HIEROGLYPH V040;Lo;0;L;;;;;N;;;;; +133AE;EGYPTIAN HIEROGLYPH V040A;Lo;0;L;;;;;N;;;;; +133AF;EGYPTIAN HIEROGLYPH W001;Lo;0;L;;;;;N;;;;; +133B0;EGYPTIAN HIEROGLYPH W002;Lo;0;L;;;;;N;;;;; +133B1;EGYPTIAN HIEROGLYPH W003;Lo;0;L;;;;;N;;;;; +133B2;EGYPTIAN HIEROGLYPH W003A;Lo;0;L;;;;;N;;;;; +133B3;EGYPTIAN HIEROGLYPH W004;Lo;0;L;;;;;N;;;;; +133B4;EGYPTIAN HIEROGLYPH W005;Lo;0;L;;;;;N;;;;; +133B5;EGYPTIAN HIEROGLYPH W006;Lo;0;L;;;;;N;;;;; +133B6;EGYPTIAN HIEROGLYPH W007;Lo;0;L;;;;;N;;;;; +133B7;EGYPTIAN HIEROGLYPH W008;Lo;0;L;;;;;N;;;;; +133B8;EGYPTIAN HIEROGLYPH W009;Lo;0;L;;;;;N;;;;; +133B9;EGYPTIAN HIEROGLYPH W009A;Lo;0;L;;;;;N;;;;; +133BA;EGYPTIAN HIEROGLYPH W010;Lo;0;L;;;;;N;;;;; +133BB;EGYPTIAN HIEROGLYPH W010A;Lo;0;L;;;;;N;;;;; +133BC;EGYPTIAN HIEROGLYPH W011;Lo;0;L;;;;;N;;;;; +133BD;EGYPTIAN HIEROGLYPH W012;Lo;0;L;;;;;N;;;;; +133BE;EGYPTIAN HIEROGLYPH W013;Lo;0;L;;;;;N;;;;; +133BF;EGYPTIAN HIEROGLYPH W014;Lo;0;L;;;;;N;;;;; +133C0;EGYPTIAN HIEROGLYPH W014A;Lo;0;L;;;;;N;;;;; +133C1;EGYPTIAN HIEROGLYPH W015;Lo;0;L;;;;;N;;;;; +133C2;EGYPTIAN HIEROGLYPH W016;Lo;0;L;;;;;N;;;;; +133C3;EGYPTIAN HIEROGLYPH W017;Lo;0;L;;;;;N;;;;; +133C4;EGYPTIAN HIEROGLYPH W017A;Lo;0;L;;;;;N;;;;; +133C5;EGYPTIAN HIEROGLYPH W018;Lo;0;L;;;;;N;;;;; +133C6;EGYPTIAN HIEROGLYPH W018A;Lo;0;L;;;;;N;;;;; +133C7;EGYPTIAN HIEROGLYPH W019;Lo;0;L;;;;;N;;;;; +133C8;EGYPTIAN HIEROGLYPH W020;Lo;0;L;;;;;N;;;;; +133C9;EGYPTIAN HIEROGLYPH W021;Lo;0;L;;;;;N;;;;; +133CA;EGYPTIAN HIEROGLYPH W022;Lo;0;L;;;;;N;;;;; +133CB;EGYPTIAN HIEROGLYPH W023;Lo;0;L;;;;;N;;;;; +133CC;EGYPTIAN HIEROGLYPH W024;Lo;0;L;;;;;N;;;;; +133CD;EGYPTIAN HIEROGLYPH W024A;Lo;0;L;;;;;N;;;;; +133CE;EGYPTIAN HIEROGLYPH W025;Lo;0;L;;;;;N;;;;; +133CF;EGYPTIAN HIEROGLYPH X001;Lo;0;L;;;;;N;;;;; +133D0;EGYPTIAN HIEROGLYPH X002;Lo;0;L;;;;;N;;;;; +133D1;EGYPTIAN HIEROGLYPH X003;Lo;0;L;;;;;N;;;;; +133D2;EGYPTIAN HIEROGLYPH X004;Lo;0;L;;;;;N;;;;; +133D3;EGYPTIAN HIEROGLYPH X004A;Lo;0;L;;;;;N;;;;; +133D4;EGYPTIAN HIEROGLYPH X004B;Lo;0;L;;;;;N;;;;; +133D5;EGYPTIAN HIEROGLYPH X005;Lo;0;L;;;;;N;;;;; +133D6;EGYPTIAN HIEROGLYPH X006;Lo;0;L;;;;;N;;;;; +133D7;EGYPTIAN HIEROGLYPH X006A;Lo;0;L;;;;;N;;;;; +133D8;EGYPTIAN HIEROGLYPH X007;Lo;0;L;;;;;N;;;;; +133D9;EGYPTIAN HIEROGLYPH X008;Lo;0;L;;;;;N;;;;; +133DA;EGYPTIAN HIEROGLYPH X008A;Lo;0;L;;;;;N;;;;; +133DB;EGYPTIAN HIEROGLYPH Y001;Lo;0;L;;;;;N;;;;; +133DC;EGYPTIAN HIEROGLYPH Y001A;Lo;0;L;;;;;N;;;;; +133DD;EGYPTIAN HIEROGLYPH Y002;Lo;0;L;;;;;N;;;;; +133DE;EGYPTIAN HIEROGLYPH Y003;Lo;0;L;;;;;N;;;;; +133DF;EGYPTIAN HIEROGLYPH Y004;Lo;0;L;;;;;N;;;;; +133E0;EGYPTIAN HIEROGLYPH Y005;Lo;0;L;;;;;N;;;;; +133E1;EGYPTIAN HIEROGLYPH Y006;Lo;0;L;;;;;N;;;;; +133E2;EGYPTIAN HIEROGLYPH Y007;Lo;0;L;;;;;N;;;;; +133E3;EGYPTIAN HIEROGLYPH Y008;Lo;0;L;;;;;N;;;;; +133E4;EGYPTIAN HIEROGLYPH Z001;Lo;0;L;;;;;N;;;;; +133E5;EGYPTIAN HIEROGLYPH Z002;Lo;0;L;;;;;N;;;;; +133E6;EGYPTIAN HIEROGLYPH Z002A;Lo;0;L;;;;;N;;;;; +133E7;EGYPTIAN HIEROGLYPH Z002B;Lo;0;L;;;;;N;;;;; +133E8;EGYPTIAN HIEROGLYPH Z002C;Lo;0;L;;;;;N;;;;; +133E9;EGYPTIAN HIEROGLYPH Z002D;Lo;0;L;;;;;N;;;;; +133EA;EGYPTIAN HIEROGLYPH Z003;Lo;0;L;;;;;N;;;;; +133EB;EGYPTIAN HIEROGLYPH Z003A;Lo;0;L;;;;;N;;;;; +133EC;EGYPTIAN HIEROGLYPH Z003B;Lo;0;L;;;;;N;;;;; +133ED;EGYPTIAN HIEROGLYPH Z004;Lo;0;L;;;;;N;;;;; +133EE;EGYPTIAN HIEROGLYPH Z004A;Lo;0;L;;;;;N;;;;; +133EF;EGYPTIAN HIEROGLYPH Z005;Lo;0;L;;;;;N;;;;; +133F0;EGYPTIAN HIEROGLYPH Z005A;Lo;0;L;;;;;N;;;;; +133F1;EGYPTIAN HIEROGLYPH Z006;Lo;0;L;;;;;N;;;;; +133F2;EGYPTIAN HIEROGLYPH Z007;Lo;0;L;;;;;N;;;;; +133F3;EGYPTIAN HIEROGLYPH Z008;Lo;0;L;;;;;N;;;;; +133F4;EGYPTIAN HIEROGLYPH Z009;Lo;0;L;;;;;N;;;;; +133F5;EGYPTIAN HIEROGLYPH Z010;Lo;0;L;;;;;N;;;;; +133F6;EGYPTIAN HIEROGLYPH Z011;Lo;0;L;;;;;N;;;;; +133F7;EGYPTIAN HIEROGLYPH Z012;Lo;0;L;;;;;N;;;;; +133F8;EGYPTIAN HIEROGLYPH Z013;Lo;0;L;;;;;N;;;;; +133F9;EGYPTIAN HIEROGLYPH Z014;Lo;0;L;;;;;N;;;;; +133FA;EGYPTIAN HIEROGLYPH Z015;Lo;0;L;;;;;N;;;;; +133FB;EGYPTIAN HIEROGLYPH Z015A;Lo;0;L;;;;;N;;;;; +133FC;EGYPTIAN HIEROGLYPH Z015B;Lo;0;L;;;;;N;;;;; +133FD;EGYPTIAN HIEROGLYPH Z015C;Lo;0;L;;;;;N;;;;; +133FE;EGYPTIAN HIEROGLYPH Z015D;Lo;0;L;;;;;N;;;;; +133FF;EGYPTIAN HIEROGLYPH Z015E;Lo;0;L;;;;;N;;;;; +13400;EGYPTIAN HIEROGLYPH Z015F;Lo;0;L;;;;;N;;;;; +13401;EGYPTIAN HIEROGLYPH Z015G;Lo;0;L;;;;;N;;;;; +13402;EGYPTIAN HIEROGLYPH Z015H;Lo;0;L;;;;;N;;;;; +13403;EGYPTIAN HIEROGLYPH Z015I;Lo;0;L;;;;;N;;;;; +13404;EGYPTIAN HIEROGLYPH Z016;Lo;0;L;;;;;N;;;;; +13405;EGYPTIAN HIEROGLYPH Z016A;Lo;0;L;;;;;N;;;;; +13406;EGYPTIAN HIEROGLYPH Z016B;Lo;0;L;;;;;N;;;;; +13407;EGYPTIAN HIEROGLYPH Z016C;Lo;0;L;;;;;N;;;;; +13408;EGYPTIAN HIEROGLYPH Z016D;Lo;0;L;;;;;N;;;;; +13409;EGYPTIAN HIEROGLYPH Z016E;Lo;0;L;;;;;N;;;;; +1340A;EGYPTIAN HIEROGLYPH Z016F;Lo;0;L;;;;;N;;;;; +1340B;EGYPTIAN HIEROGLYPH Z016G;Lo;0;L;;;;;N;;;;; +1340C;EGYPTIAN HIEROGLYPH Z016H;Lo;0;L;;;;;N;;;;; +1340D;EGYPTIAN HIEROGLYPH AA001;Lo;0;L;;;;;N;;;;; +1340E;EGYPTIAN HIEROGLYPH AA002;Lo;0;L;;;;;N;;;;; +1340F;EGYPTIAN HIEROGLYPH AA003;Lo;0;L;;;;;N;;;;; +13410;EGYPTIAN HIEROGLYPH AA004;Lo;0;L;;;;;N;;;;; +13411;EGYPTIAN HIEROGLYPH AA005;Lo;0;L;;;;;N;;;;; +13412;EGYPTIAN HIEROGLYPH AA006;Lo;0;L;;;;;N;;;;; +13413;EGYPTIAN HIEROGLYPH AA007;Lo;0;L;;;;;N;;;;; +13414;EGYPTIAN HIEROGLYPH AA007A;Lo;0;L;;;;;N;;;;; +13415;EGYPTIAN HIEROGLYPH AA007B;Lo;0;L;;;;;N;;;;; +13416;EGYPTIAN HIEROGLYPH AA008;Lo;0;L;;;;;N;;;;; +13417;EGYPTIAN HIEROGLYPH AA009;Lo;0;L;;;;;N;;;;; +13418;EGYPTIAN HIEROGLYPH AA010;Lo;0;L;;;;;N;;;;; +13419;EGYPTIAN HIEROGLYPH AA011;Lo;0;L;;;;;N;;;;; +1341A;EGYPTIAN HIEROGLYPH AA012;Lo;0;L;;;;;N;;;;; +1341B;EGYPTIAN HIEROGLYPH AA013;Lo;0;L;;;;;N;;;;; +1341C;EGYPTIAN HIEROGLYPH AA014;Lo;0;L;;;;;N;;;;; +1341D;EGYPTIAN HIEROGLYPH AA015;Lo;0;L;;;;;N;;;;; +1341E;EGYPTIAN HIEROGLYPH AA016;Lo;0;L;;;;;N;;;;; +1341F;EGYPTIAN HIEROGLYPH AA017;Lo;0;L;;;;;N;;;;; +13420;EGYPTIAN HIEROGLYPH AA018;Lo;0;L;;;;;N;;;;; +13421;EGYPTIAN HIEROGLYPH AA019;Lo;0;L;;;;;N;;;;; +13422;EGYPTIAN HIEROGLYPH AA020;Lo;0;L;;;;;N;;;;; +13423;EGYPTIAN HIEROGLYPH AA021;Lo;0;L;;;;;N;;;;; +13424;EGYPTIAN HIEROGLYPH AA022;Lo;0;L;;;;;N;;;;; +13425;EGYPTIAN HIEROGLYPH AA023;Lo;0;L;;;;;N;;;;; +13426;EGYPTIAN HIEROGLYPH AA024;Lo;0;L;;;;;N;;;;; +13427;EGYPTIAN HIEROGLYPH AA025;Lo;0;L;;;;;N;;;;; +13428;EGYPTIAN HIEROGLYPH AA026;Lo;0;L;;;;;N;;;;; +13429;EGYPTIAN HIEROGLYPH AA027;Lo;0;L;;;;;N;;;;; +1342A;EGYPTIAN HIEROGLYPH AA028;Lo;0;L;;;;;N;;;;; +1342B;EGYPTIAN HIEROGLYPH AA029;Lo;0;L;;;;;N;;;;; +1342C;EGYPTIAN HIEROGLYPH AA030;Lo;0;L;;;;;N;;;;; +1342D;EGYPTIAN HIEROGLYPH AA031;Lo;0;L;;;;;N;;;;; +1342E;EGYPTIAN HIEROGLYPH AA032;Lo;0;L;;;;;N;;;;; +14400;ANATOLIAN HIEROGLYPH A001;Lo;0;L;;;;;N;;;;; +14401;ANATOLIAN HIEROGLYPH A002;Lo;0;L;;;;;N;;;;; +14402;ANATOLIAN HIEROGLYPH A003;Lo;0;L;;;;;N;;;;; +14403;ANATOLIAN HIEROGLYPH A004;Lo;0;L;;;;;N;;;;; +14404;ANATOLIAN HIEROGLYPH A005;Lo;0;L;;;;;N;;;;; +14405;ANATOLIAN HIEROGLYPH A006;Lo;0;L;;;;;N;;;;; +14406;ANATOLIAN HIEROGLYPH A007;Lo;0;L;;;;;N;;;;; +14407;ANATOLIAN HIEROGLYPH A008;Lo;0;L;;;;;N;;;;; +14408;ANATOLIAN HIEROGLYPH A009;Lo;0;L;;;;;N;;;;; +14409;ANATOLIAN HIEROGLYPH A010;Lo;0;L;;;;;N;;;;; +1440A;ANATOLIAN HIEROGLYPH A010A;Lo;0;L;;;;;N;;;;; +1440B;ANATOLIAN HIEROGLYPH A011;Lo;0;L;;;;;N;;;;; +1440C;ANATOLIAN HIEROGLYPH A012;Lo;0;L;;;;;N;;;;; +1440D;ANATOLIAN HIEROGLYPH A013;Lo;0;L;;;;;N;;;;; +1440E;ANATOLIAN HIEROGLYPH A014;Lo;0;L;;;;;N;;;;; +1440F;ANATOLIAN HIEROGLYPH A015;Lo;0;L;;;;;N;;;;; +14410;ANATOLIAN HIEROGLYPH A016;Lo;0;L;;;;;N;;;;; +14411;ANATOLIAN HIEROGLYPH A017;Lo;0;L;;;;;N;;;;; +14412;ANATOLIAN HIEROGLYPH A018;Lo;0;L;;;;;N;;;;; +14413;ANATOLIAN HIEROGLYPH A019;Lo;0;L;;;;;N;;;;; +14414;ANATOLIAN HIEROGLYPH A020;Lo;0;L;;;;;N;;;;; +14415;ANATOLIAN HIEROGLYPH A021;Lo;0;L;;;;;N;;;;; +14416;ANATOLIAN HIEROGLYPH A022;Lo;0;L;;;;;N;;;;; +14417;ANATOLIAN HIEROGLYPH A023;Lo;0;L;;;;;N;;;;; +14418;ANATOLIAN HIEROGLYPH A024;Lo;0;L;;;;;N;;;;; +14419;ANATOLIAN HIEROGLYPH A025;Lo;0;L;;;;;N;;;;; +1441A;ANATOLIAN HIEROGLYPH A026;Lo;0;L;;;;;N;;;;; +1441B;ANATOLIAN HIEROGLYPH A026A;Lo;0;L;;;;;N;;;;; +1441C;ANATOLIAN HIEROGLYPH A027;Lo;0;L;;;;;N;;;;; +1441D;ANATOLIAN HIEROGLYPH A028;Lo;0;L;;;;;N;;;;; +1441E;ANATOLIAN HIEROGLYPH A029;Lo;0;L;;;;;N;;;;; +1441F;ANATOLIAN HIEROGLYPH A030;Lo;0;L;;;;;N;;;;; +14420;ANATOLIAN HIEROGLYPH A031;Lo;0;L;;;;;N;;;;; +14421;ANATOLIAN HIEROGLYPH A032;Lo;0;L;;;;;N;;;;; +14422;ANATOLIAN HIEROGLYPH A033;Lo;0;L;;;;;N;;;;; +14423;ANATOLIAN HIEROGLYPH A034;Lo;0;L;;;;;N;;;;; +14424;ANATOLIAN HIEROGLYPH A035;Lo;0;L;;;;;N;;;;; +14425;ANATOLIAN HIEROGLYPH A036;Lo;0;L;;;;;N;;;;; +14426;ANATOLIAN HIEROGLYPH A037;Lo;0;L;;;;;N;;;;; +14427;ANATOLIAN HIEROGLYPH A038;Lo;0;L;;;;;N;;;;; +14428;ANATOLIAN HIEROGLYPH A039;Lo;0;L;;;;;N;;;;; +14429;ANATOLIAN HIEROGLYPH A039A;Lo;0;L;;;;;N;;;;; +1442A;ANATOLIAN HIEROGLYPH A040;Lo;0;L;;;;;N;;;;; +1442B;ANATOLIAN HIEROGLYPH A041;Lo;0;L;;;;;N;;;;; +1442C;ANATOLIAN HIEROGLYPH A041A;Lo;0;L;;;;;N;;;;; +1442D;ANATOLIAN HIEROGLYPH A042;Lo;0;L;;;;;N;;;;; +1442E;ANATOLIAN HIEROGLYPH A043;Lo;0;L;;;;;N;;;;; +1442F;ANATOLIAN HIEROGLYPH A044;Lo;0;L;;;;;N;;;;; +14430;ANATOLIAN HIEROGLYPH A045;Lo;0;L;;;;;N;;;;; +14431;ANATOLIAN HIEROGLYPH A045A;Lo;0;L;;;;;N;;;;; +14432;ANATOLIAN HIEROGLYPH A046;Lo;0;L;;;;;N;;;;; +14433;ANATOLIAN HIEROGLYPH A046A;Lo;0;L;;;;;N;;;;; +14434;ANATOLIAN HIEROGLYPH A046B;Lo;0;L;;;;;N;;;;; +14435;ANATOLIAN HIEROGLYPH A047;Lo;0;L;;;;;N;;;;; +14436;ANATOLIAN HIEROGLYPH A048;Lo;0;L;;;;;N;;;;; +14437;ANATOLIAN HIEROGLYPH A049;Lo;0;L;;;;;N;;;;; +14438;ANATOLIAN HIEROGLYPH A050;Lo;0;L;;;;;N;;;;; +14439;ANATOLIAN HIEROGLYPH A051;Lo;0;L;;;;;N;;;;; +1443A;ANATOLIAN HIEROGLYPH A052;Lo;0;L;;;;;N;;;;; +1443B;ANATOLIAN HIEROGLYPH A053;Lo;0;L;;;;;N;;;;; +1443C;ANATOLIAN HIEROGLYPH A054;Lo;0;L;;;;;N;;;;; +1443D;ANATOLIAN HIEROGLYPH A055;Lo;0;L;;;;;N;;;;; +1443E;ANATOLIAN HIEROGLYPH A056;Lo;0;L;;;;;N;;;;; +1443F;ANATOLIAN HIEROGLYPH A057;Lo;0;L;;;;;N;;;;; +14440;ANATOLIAN HIEROGLYPH A058;Lo;0;L;;;;;N;;;;; +14441;ANATOLIAN HIEROGLYPH A059;Lo;0;L;;;;;N;;;;; +14442;ANATOLIAN HIEROGLYPH A060;Lo;0;L;;;;;N;;;;; +14443;ANATOLIAN HIEROGLYPH A061;Lo;0;L;;;;;N;;;;; +14444;ANATOLIAN HIEROGLYPH A062;Lo;0;L;;;;;N;;;;; +14445;ANATOLIAN HIEROGLYPH A063;Lo;0;L;;;;;N;;;;; +14446;ANATOLIAN HIEROGLYPH A064;Lo;0;L;;;;;N;;;;; +14447;ANATOLIAN HIEROGLYPH A065;Lo;0;L;;;;;N;;;;; +14448;ANATOLIAN HIEROGLYPH A066;Lo;0;L;;;;;N;;;;; +14449;ANATOLIAN HIEROGLYPH A066A;Lo;0;L;;;;;N;;;;; +1444A;ANATOLIAN HIEROGLYPH A066B;Lo;0;L;;;;;N;;;;; +1444B;ANATOLIAN HIEROGLYPH A066C;Lo;0;L;;;;;N;;;;; +1444C;ANATOLIAN HIEROGLYPH A067;Lo;0;L;;;;;N;;;;; +1444D;ANATOLIAN HIEROGLYPH A068;Lo;0;L;;;;;N;;;;; +1444E;ANATOLIAN HIEROGLYPH A069;Lo;0;L;;;;;N;;;;; +1444F;ANATOLIAN HIEROGLYPH A070;Lo;0;L;;;;;N;;;;; +14450;ANATOLIAN HIEROGLYPH A071;Lo;0;L;;;;;N;;;;; +14451;ANATOLIAN HIEROGLYPH A072;Lo;0;L;;;;;N;;;;; +14452;ANATOLIAN HIEROGLYPH A073;Lo;0;L;;;;;N;;;;; +14453;ANATOLIAN HIEROGLYPH A074;Lo;0;L;;;;;N;;;;; +14454;ANATOLIAN HIEROGLYPH A075;Lo;0;L;;;;;N;;;;; +14455;ANATOLIAN HIEROGLYPH A076;Lo;0;L;;;;;N;;;;; +14456;ANATOLIAN HIEROGLYPH A077;Lo;0;L;;;;;N;;;;; +14457;ANATOLIAN HIEROGLYPH A078;Lo;0;L;;;;;N;;;;; +14458;ANATOLIAN HIEROGLYPH A079;Lo;0;L;;;;;N;;;;; +14459;ANATOLIAN HIEROGLYPH A080;Lo;0;L;;;;;N;;;;; +1445A;ANATOLIAN HIEROGLYPH A081;Lo;0;L;;;;;N;;;;; +1445B;ANATOLIAN HIEROGLYPH A082;Lo;0;L;;;;;N;;;;; +1445C;ANATOLIAN HIEROGLYPH A083;Lo;0;L;;;;;N;;;;; +1445D;ANATOLIAN HIEROGLYPH A084;Lo;0;L;;;;;N;;;;; +1445E;ANATOLIAN HIEROGLYPH A085;Lo;0;L;;;;;N;;;;; +1445F;ANATOLIAN HIEROGLYPH A086;Lo;0;L;;;;;N;;;;; +14460;ANATOLIAN HIEROGLYPH A087;Lo;0;L;;;;;N;;;;; +14461;ANATOLIAN HIEROGLYPH A088;Lo;0;L;;;;;N;;;;; +14462;ANATOLIAN HIEROGLYPH A089;Lo;0;L;;;;;N;;;;; +14463;ANATOLIAN HIEROGLYPH A090;Lo;0;L;;;;;N;;;;; +14464;ANATOLIAN HIEROGLYPH A091;Lo;0;L;;;;;N;;;;; +14465;ANATOLIAN HIEROGLYPH A092;Lo;0;L;;;;;N;;;;; +14466;ANATOLIAN HIEROGLYPH A093;Lo;0;L;;;;;N;;;;; +14467;ANATOLIAN HIEROGLYPH A094;Lo;0;L;;;;;N;;;;; +14468;ANATOLIAN HIEROGLYPH A095;Lo;0;L;;;;;N;;;;; +14469;ANATOLIAN HIEROGLYPH A096;Lo;0;L;;;;;N;;;;; +1446A;ANATOLIAN HIEROGLYPH A097;Lo;0;L;;;;;N;;;;; +1446B;ANATOLIAN HIEROGLYPH A097A;Lo;0;L;;;;;N;;;;; +1446C;ANATOLIAN HIEROGLYPH A098;Lo;0;L;;;;;N;;;;; +1446D;ANATOLIAN HIEROGLYPH A098A;Lo;0;L;;;;;N;;;;; +1446E;ANATOLIAN HIEROGLYPH A099;Lo;0;L;;;;;N;;;;; +1446F;ANATOLIAN HIEROGLYPH A100;Lo;0;L;;;;;N;;;;; +14470;ANATOLIAN HIEROGLYPH A100A;Lo;0;L;;;;;N;;;;; +14471;ANATOLIAN HIEROGLYPH A101;Lo;0;L;;;;;N;;;;; +14472;ANATOLIAN HIEROGLYPH A101A;Lo;0;L;;;;;N;;;;; +14473;ANATOLIAN HIEROGLYPH A102;Lo;0;L;;;;;N;;;;; +14474;ANATOLIAN HIEROGLYPH A102A;Lo;0;L;;;;;N;;;;; +14475;ANATOLIAN HIEROGLYPH A103;Lo;0;L;;;;;N;;;;; +14476;ANATOLIAN HIEROGLYPH A104;Lo;0;L;;;;;N;;;;; +14477;ANATOLIAN HIEROGLYPH A104A;Lo;0;L;;;;;N;;;;; +14478;ANATOLIAN HIEROGLYPH A104B;Lo;0;L;;;;;N;;;;; +14479;ANATOLIAN HIEROGLYPH A104C;Lo;0;L;;;;;N;;;;; +1447A;ANATOLIAN HIEROGLYPH A105;Lo;0;L;;;;;N;;;;; +1447B;ANATOLIAN HIEROGLYPH A105A;Lo;0;L;;;;;N;;;;; +1447C;ANATOLIAN HIEROGLYPH A105B;Lo;0;L;;;;;N;;;;; +1447D;ANATOLIAN HIEROGLYPH A106;Lo;0;L;;;;;N;;;;; +1447E;ANATOLIAN HIEROGLYPH A107;Lo;0;L;;;;;N;;;;; +1447F;ANATOLIAN HIEROGLYPH A107A;Lo;0;L;;;;;N;;;;; +14480;ANATOLIAN HIEROGLYPH A107B;Lo;0;L;;;;;N;;;;; +14481;ANATOLIAN HIEROGLYPH A107C;Lo;0;L;;;;;N;;;;; +14482;ANATOLIAN HIEROGLYPH A108;Lo;0;L;;;;;N;;;;; +14483;ANATOLIAN HIEROGLYPH A109;Lo;0;L;;;;;N;;;;; +14484;ANATOLIAN HIEROGLYPH A110;Lo;0;L;;;;;N;;;;; +14485;ANATOLIAN HIEROGLYPH A110A;Lo;0;L;;;;;N;;;;; +14486;ANATOLIAN HIEROGLYPH A110B;Lo;0;L;;;;;N;;;;; +14487;ANATOLIAN HIEROGLYPH A111;Lo;0;L;;;;;N;;;;; +14488;ANATOLIAN HIEROGLYPH A112;Lo;0;L;;;;;N;;;;; +14489;ANATOLIAN HIEROGLYPH A113;Lo;0;L;;;;;N;;;;; +1448A;ANATOLIAN HIEROGLYPH A114;Lo;0;L;;;;;N;;;;; +1448B;ANATOLIAN HIEROGLYPH A115;Lo;0;L;;;;;N;;;;; +1448C;ANATOLIAN HIEROGLYPH A115A;Lo;0;L;;;;;N;;;;; +1448D;ANATOLIAN HIEROGLYPH A116;Lo;0;L;;;;;N;;;;; +1448E;ANATOLIAN HIEROGLYPH A117;Lo;0;L;;;;;N;;;;; +1448F;ANATOLIAN HIEROGLYPH A118;Lo;0;L;;;;;N;;;;; +14490;ANATOLIAN HIEROGLYPH A119;Lo;0;L;;;;;N;;;;; +14491;ANATOLIAN HIEROGLYPH A120;Lo;0;L;;;;;N;;;;; +14492;ANATOLIAN HIEROGLYPH A121;Lo;0;L;;;;;N;;;;; +14493;ANATOLIAN HIEROGLYPH A122;Lo;0;L;;;;;N;;;;; +14494;ANATOLIAN HIEROGLYPH A123;Lo;0;L;;;;;N;;;;; +14495;ANATOLIAN HIEROGLYPH A124;Lo;0;L;;;;;N;;;;; +14496;ANATOLIAN HIEROGLYPH A125;Lo;0;L;;;;;N;;;;; +14497;ANATOLIAN HIEROGLYPH A125A;Lo;0;L;;;;;N;;;;; +14498;ANATOLIAN HIEROGLYPH A126;Lo;0;L;;;;;N;;;;; +14499;ANATOLIAN HIEROGLYPH A127;Lo;0;L;;;;;N;;;;; +1449A;ANATOLIAN HIEROGLYPH A128;Lo;0;L;;;;;N;;;;; +1449B;ANATOLIAN HIEROGLYPH A129;Lo;0;L;;;;;N;;;;; +1449C;ANATOLIAN HIEROGLYPH A130;Lo;0;L;;;;;N;;;;; +1449D;ANATOLIAN HIEROGLYPH A131;Lo;0;L;;;;;N;;;;; +1449E;ANATOLIAN HIEROGLYPH A132;Lo;0;L;;;;;N;;;;; +1449F;ANATOLIAN HIEROGLYPH A133;Lo;0;L;;;;;N;;;;; +144A0;ANATOLIAN HIEROGLYPH A134;Lo;0;L;;;;;N;;;;; +144A1;ANATOLIAN HIEROGLYPH A135;Lo;0;L;;;;;N;;;;; +144A2;ANATOLIAN HIEROGLYPH A135A;Lo;0;L;;;;;N;;;;; +144A3;ANATOLIAN HIEROGLYPH A136;Lo;0;L;;;;;N;;;;; +144A4;ANATOLIAN HIEROGLYPH A137;Lo;0;L;;;;;N;;;;; +144A5;ANATOLIAN HIEROGLYPH A138;Lo;0;L;;;;;N;;;;; +144A6;ANATOLIAN HIEROGLYPH A139;Lo;0;L;;;;;N;;;;; +144A7;ANATOLIAN HIEROGLYPH A140;Lo;0;L;;;;;N;;;;; +144A8;ANATOLIAN HIEROGLYPH A141;Lo;0;L;;;;;N;;;;; +144A9;ANATOLIAN HIEROGLYPH A142;Lo;0;L;;;;;N;;;;; +144AA;ANATOLIAN HIEROGLYPH A143;Lo;0;L;;;;;N;;;;; +144AB;ANATOLIAN HIEROGLYPH A144;Lo;0;L;;;;;N;;;;; +144AC;ANATOLIAN HIEROGLYPH A145;Lo;0;L;;;;;N;;;;; +144AD;ANATOLIAN HIEROGLYPH A146;Lo;0;L;;;;;N;;;;; +144AE;ANATOLIAN HIEROGLYPH A147;Lo;0;L;;;;;N;;;;; +144AF;ANATOLIAN HIEROGLYPH A148;Lo;0;L;;;;;N;;;;; +144B0;ANATOLIAN HIEROGLYPH A149;Lo;0;L;;;;;N;;;;; +144B1;ANATOLIAN HIEROGLYPH A150;Lo;0;L;;;;;N;;;;; +144B2;ANATOLIAN HIEROGLYPH A151;Lo;0;L;;;;;N;;;;; +144B3;ANATOLIAN HIEROGLYPH A152;Lo;0;L;;;;;N;;;;; +144B4;ANATOLIAN HIEROGLYPH A153;Lo;0;L;;;;;N;;;;; +144B5;ANATOLIAN HIEROGLYPH A154;Lo;0;L;;;;;N;;;;; +144B6;ANATOLIAN HIEROGLYPH A155;Lo;0;L;;;;;N;;;;; +144B7;ANATOLIAN HIEROGLYPH A156;Lo;0;L;;;;;N;;;;; +144B8;ANATOLIAN HIEROGLYPH A157;Lo;0;L;;;;;N;;;;; +144B9;ANATOLIAN HIEROGLYPH A158;Lo;0;L;;;;;N;;;;; +144BA;ANATOLIAN HIEROGLYPH A159;Lo;0;L;;;;;N;;;;; +144BB;ANATOLIAN HIEROGLYPH A160;Lo;0;L;;;;;N;;;;; +144BC;ANATOLIAN HIEROGLYPH A161;Lo;0;L;;;;;N;;;;; +144BD;ANATOLIAN HIEROGLYPH A162;Lo;0;L;;;;;N;;;;; +144BE;ANATOLIAN HIEROGLYPH A163;Lo;0;L;;;;;N;;;;; +144BF;ANATOLIAN HIEROGLYPH A164;Lo;0;L;;;;;N;;;;; +144C0;ANATOLIAN HIEROGLYPH A165;Lo;0;L;;;;;N;;;;; +144C1;ANATOLIAN HIEROGLYPH A166;Lo;0;L;;;;;N;;;;; +144C2;ANATOLIAN HIEROGLYPH A167;Lo;0;L;;;;;N;;;;; +144C3;ANATOLIAN HIEROGLYPH A168;Lo;0;L;;;;;N;;;;; +144C4;ANATOLIAN HIEROGLYPH A169;Lo;0;L;;;;;N;;;;; +144C5;ANATOLIAN HIEROGLYPH A170;Lo;0;L;;;;;N;;;;; +144C6;ANATOLIAN HIEROGLYPH A171;Lo;0;L;;;;;N;;;;; +144C7;ANATOLIAN HIEROGLYPH A172;Lo;0;L;;;;;N;;;;; +144C8;ANATOLIAN HIEROGLYPH A173;Lo;0;L;;;;;N;;;;; +144C9;ANATOLIAN HIEROGLYPH A174;Lo;0;L;;;;;N;;;;; +144CA;ANATOLIAN HIEROGLYPH A175;Lo;0;L;;;;;N;;;;; +144CB;ANATOLIAN HIEROGLYPH A176;Lo;0;L;;;;;N;;;;; +144CC;ANATOLIAN HIEROGLYPH A177;Lo;0;L;;;;;N;;;;; +144CD;ANATOLIAN HIEROGLYPH A178;Lo;0;L;;;;;N;;;;; +144CE;ANATOLIAN HIEROGLYPH A179;Lo;0;L;;;;;N;;;;; +144CF;ANATOLIAN HIEROGLYPH A180;Lo;0;L;;;;;N;;;;; +144D0;ANATOLIAN HIEROGLYPH A181;Lo;0;L;;;;;N;;;;; +144D1;ANATOLIAN HIEROGLYPH A182;Lo;0;L;;;;;N;;;;; +144D2;ANATOLIAN HIEROGLYPH A183;Lo;0;L;;;;;N;;;;; +144D3;ANATOLIAN HIEROGLYPH A184;Lo;0;L;;;;;N;;;;; +144D4;ANATOLIAN HIEROGLYPH A185;Lo;0;L;;;;;N;;;;; +144D5;ANATOLIAN HIEROGLYPH A186;Lo;0;L;;;;;N;;;;; +144D6;ANATOLIAN HIEROGLYPH A187;Lo;0;L;;;;;N;;;;; +144D7;ANATOLIAN HIEROGLYPH A188;Lo;0;L;;;;;N;;;;; +144D8;ANATOLIAN HIEROGLYPH A189;Lo;0;L;;;;;N;;;;; +144D9;ANATOLIAN HIEROGLYPH A190;Lo;0;L;;;;;N;;;;; +144DA;ANATOLIAN HIEROGLYPH A191;Lo;0;L;;;;;N;;;;; +144DB;ANATOLIAN HIEROGLYPH A192;Lo;0;L;;;;;N;;;;; +144DC;ANATOLIAN HIEROGLYPH A193;Lo;0;L;;;;;N;;;;; +144DD;ANATOLIAN HIEROGLYPH A194;Lo;0;L;;;;;N;;;;; +144DE;ANATOLIAN HIEROGLYPH A195;Lo;0;L;;;;;N;;;;; +144DF;ANATOLIAN HIEROGLYPH A196;Lo;0;L;;;;;N;;;;; +144E0;ANATOLIAN HIEROGLYPH A197;Lo;0;L;;;;;N;;;;; +144E1;ANATOLIAN HIEROGLYPH A198;Lo;0;L;;;;;N;;;;; +144E2;ANATOLIAN HIEROGLYPH A199;Lo;0;L;;;;;N;;;;; +144E3;ANATOLIAN HIEROGLYPH A200;Lo;0;L;;;;;N;;;;; +144E4;ANATOLIAN HIEROGLYPH A201;Lo;0;L;;;;;N;;;;; +144E5;ANATOLIAN HIEROGLYPH A202;Lo;0;L;;;;;N;;;;; +144E6;ANATOLIAN HIEROGLYPH A202A;Lo;0;L;;;;;N;;;;; +144E7;ANATOLIAN HIEROGLYPH A202B;Lo;0;L;;;;;N;;;;; +144E8;ANATOLIAN HIEROGLYPH A203;Lo;0;L;;;;;N;;;;; +144E9;ANATOLIAN HIEROGLYPH A204;Lo;0;L;;;;;N;;;;; +144EA;ANATOLIAN HIEROGLYPH A205;Lo;0;L;;;;;N;;;;; +144EB;ANATOLIAN HIEROGLYPH A206;Lo;0;L;;;;;N;;;;; +144EC;ANATOLIAN HIEROGLYPH A207;Lo;0;L;;;;;N;;;;; +144ED;ANATOLIAN HIEROGLYPH A207A;Lo;0;L;;;;;N;;;;; +144EE;ANATOLIAN HIEROGLYPH A208;Lo;0;L;;;;;N;;;;; +144EF;ANATOLIAN HIEROGLYPH A209;Lo;0;L;;;;;N;;;;; +144F0;ANATOLIAN HIEROGLYPH A209A;Lo;0;L;;;;;N;;;;; +144F1;ANATOLIAN HIEROGLYPH A210;Lo;0;L;;;;;N;;;;; +144F2;ANATOLIAN HIEROGLYPH A211;Lo;0;L;;;;;N;;;;; +144F3;ANATOLIAN HIEROGLYPH A212;Lo;0;L;;;;;N;;;;; +144F4;ANATOLIAN HIEROGLYPH A213;Lo;0;L;;;;;N;;;;; +144F5;ANATOLIAN HIEROGLYPH A214;Lo;0;L;;;;;N;;;;; +144F6;ANATOLIAN HIEROGLYPH A215;Lo;0;L;;;;;N;;;;; +144F7;ANATOLIAN HIEROGLYPH A215A;Lo;0;L;;;;;N;;;;; +144F8;ANATOLIAN HIEROGLYPH A216;Lo;0;L;;;;;N;;;;; +144F9;ANATOLIAN HIEROGLYPH A216A;Lo;0;L;;;;;N;;;;; +144FA;ANATOLIAN HIEROGLYPH A217;Lo;0;L;;;;;N;;;;; +144FB;ANATOLIAN HIEROGLYPH A218;Lo;0;L;;;;;N;;;;; +144FC;ANATOLIAN HIEROGLYPH A219;Lo;0;L;;;;;N;;;;; +144FD;ANATOLIAN HIEROGLYPH A220;Lo;0;L;;;;;N;;;;; +144FE;ANATOLIAN HIEROGLYPH A221;Lo;0;L;;;;;N;;;;; +144FF;ANATOLIAN HIEROGLYPH A222;Lo;0;L;;;;;N;;;;; +14500;ANATOLIAN HIEROGLYPH A223;Lo;0;L;;;;;N;;;;; +14501;ANATOLIAN HIEROGLYPH A224;Lo;0;L;;;;;N;;;;; +14502;ANATOLIAN HIEROGLYPH A225;Lo;0;L;;;;;N;;;;; +14503;ANATOLIAN HIEROGLYPH A226;Lo;0;L;;;;;N;;;;; +14504;ANATOLIAN HIEROGLYPH A227;Lo;0;L;;;;;N;;;;; +14505;ANATOLIAN HIEROGLYPH A227A;Lo;0;L;;;;;N;;;;; +14506;ANATOLIAN HIEROGLYPH A228;Lo;0;L;;;;;N;;;;; +14507;ANATOLIAN HIEROGLYPH A229;Lo;0;L;;;;;N;;;;; +14508;ANATOLIAN HIEROGLYPH A230;Lo;0;L;;;;;N;;;;; +14509;ANATOLIAN HIEROGLYPH A231;Lo;0;L;;;;;N;;;;; +1450A;ANATOLIAN HIEROGLYPH A232;Lo;0;L;;;;;N;;;;; +1450B;ANATOLIAN HIEROGLYPH A233;Lo;0;L;;;;;N;;;;; +1450C;ANATOLIAN HIEROGLYPH A234;Lo;0;L;;;;;N;;;;; +1450D;ANATOLIAN HIEROGLYPH A235;Lo;0;L;;;;;N;;;;; +1450E;ANATOLIAN HIEROGLYPH A236;Lo;0;L;;;;;N;;;;; +1450F;ANATOLIAN HIEROGLYPH A237;Lo;0;L;;;;;N;;;;; +14510;ANATOLIAN HIEROGLYPH A238;Lo;0;L;;;;;N;;;;; +14511;ANATOLIAN HIEROGLYPH A239;Lo;0;L;;;;;N;;;;; +14512;ANATOLIAN HIEROGLYPH A240;Lo;0;L;;;;;N;;;;; +14513;ANATOLIAN HIEROGLYPH A241;Lo;0;L;;;;;N;;;;; +14514;ANATOLIAN HIEROGLYPH A242;Lo;0;L;;;;;N;;;;; +14515;ANATOLIAN HIEROGLYPH A243;Lo;0;L;;;;;N;;;;; +14516;ANATOLIAN HIEROGLYPH A244;Lo;0;L;;;;;N;;;;; +14517;ANATOLIAN HIEROGLYPH A245;Lo;0;L;;;;;N;;;;; +14518;ANATOLIAN HIEROGLYPH A246;Lo;0;L;;;;;N;;;;; +14519;ANATOLIAN HIEROGLYPH A247;Lo;0;L;;;;;N;;;;; +1451A;ANATOLIAN HIEROGLYPH A248;Lo;0;L;;;;;N;;;;; +1451B;ANATOLIAN HIEROGLYPH A249;Lo;0;L;;;;;N;;;;; +1451C;ANATOLIAN HIEROGLYPH A250;Lo;0;L;;;;;N;;;;; +1451D;ANATOLIAN HIEROGLYPH A251;Lo;0;L;;;;;N;;;;; +1451E;ANATOLIAN HIEROGLYPH A252;Lo;0;L;;;;;N;;;;; +1451F;ANATOLIAN HIEROGLYPH A253;Lo;0;L;;;;;N;;;;; +14520;ANATOLIAN HIEROGLYPH A254;Lo;0;L;;;;;N;;;;; +14521;ANATOLIAN HIEROGLYPH A255;Lo;0;L;;;;;N;;;;; +14522;ANATOLIAN HIEROGLYPH A256;Lo;0;L;;;;;N;;;;; +14523;ANATOLIAN HIEROGLYPH A257;Lo;0;L;;;;;N;;;;; +14524;ANATOLIAN HIEROGLYPH A258;Lo;0;L;;;;;N;;;;; +14525;ANATOLIAN HIEROGLYPH A259;Lo;0;L;;;;;N;;;;; +14526;ANATOLIAN HIEROGLYPH A260;Lo;0;L;;;;;N;;;;; +14527;ANATOLIAN HIEROGLYPH A261;Lo;0;L;;;;;N;;;;; +14528;ANATOLIAN HIEROGLYPH A262;Lo;0;L;;;;;N;;;;; +14529;ANATOLIAN HIEROGLYPH A263;Lo;0;L;;;;;N;;;;; +1452A;ANATOLIAN HIEROGLYPH A264;Lo;0;L;;;;;N;;;;; +1452B;ANATOLIAN HIEROGLYPH A265;Lo;0;L;;;;;N;;;;; +1452C;ANATOLIAN HIEROGLYPH A266;Lo;0;L;;;;;N;;;;; +1452D;ANATOLIAN HIEROGLYPH A267;Lo;0;L;;;;;N;;;;; +1452E;ANATOLIAN HIEROGLYPH A267A;Lo;0;L;;;;;N;;;;; +1452F;ANATOLIAN HIEROGLYPH A268;Lo;0;L;;;;;N;;;;; +14530;ANATOLIAN HIEROGLYPH A269;Lo;0;L;;;;;N;;;;; +14531;ANATOLIAN HIEROGLYPH A270;Lo;0;L;;;;;N;;;;; +14532;ANATOLIAN HIEROGLYPH A271;Lo;0;L;;;;;N;;;;; +14533;ANATOLIAN HIEROGLYPH A272;Lo;0;L;;;;;N;;;;; +14534;ANATOLIAN HIEROGLYPH A273;Lo;0;L;;;;;N;;;;; +14535;ANATOLIAN HIEROGLYPH A274;Lo;0;L;;;;;N;;;;; +14536;ANATOLIAN HIEROGLYPH A275;Lo;0;L;;;;;N;;;;; +14537;ANATOLIAN HIEROGLYPH A276;Lo;0;L;;;;;N;;;;; +14538;ANATOLIAN HIEROGLYPH A277;Lo;0;L;;;;;N;;;;; +14539;ANATOLIAN HIEROGLYPH A278;Lo;0;L;;;;;N;;;;; +1453A;ANATOLIAN HIEROGLYPH A279;Lo;0;L;;;;;N;;;;; +1453B;ANATOLIAN HIEROGLYPH A280;Lo;0;L;;;;;N;;;;; +1453C;ANATOLIAN HIEROGLYPH A281;Lo;0;L;;;;;N;;;;; +1453D;ANATOLIAN HIEROGLYPH A282;Lo;0;L;;;;;N;;;;; +1453E;ANATOLIAN HIEROGLYPH A283;Lo;0;L;;;;;N;;;;; +1453F;ANATOLIAN HIEROGLYPH A284;Lo;0;L;;;;;N;;;;; +14540;ANATOLIAN HIEROGLYPH A285;Lo;0;L;;;;;N;;;;; +14541;ANATOLIAN HIEROGLYPH A286;Lo;0;L;;;;;N;;;;; +14542;ANATOLIAN HIEROGLYPH A287;Lo;0;L;;;;;N;;;;; +14543;ANATOLIAN HIEROGLYPH A288;Lo;0;L;;;;;N;;;;; +14544;ANATOLIAN HIEROGLYPH A289;Lo;0;L;;;;;N;;;;; +14545;ANATOLIAN HIEROGLYPH A289A;Lo;0;L;;;;;N;;;;; +14546;ANATOLIAN HIEROGLYPH A290;Lo;0;L;;;;;N;;;;; +14547;ANATOLIAN HIEROGLYPH A291;Lo;0;L;;;;;N;;;;; +14548;ANATOLIAN HIEROGLYPH A292;Lo;0;L;;;;;N;;;;; +14549;ANATOLIAN HIEROGLYPH A293;Lo;0;L;;;;;N;;;;; +1454A;ANATOLIAN HIEROGLYPH A294;Lo;0;L;;;;;N;;;;; +1454B;ANATOLIAN HIEROGLYPH A294A;Lo;0;L;;;;;N;;;;; +1454C;ANATOLIAN HIEROGLYPH A295;Lo;0;L;;;;;N;;;;; +1454D;ANATOLIAN HIEROGLYPH A296;Lo;0;L;;;;;N;;;;; +1454E;ANATOLIAN HIEROGLYPH A297;Lo;0;L;;;;;N;;;;; +1454F;ANATOLIAN HIEROGLYPH A298;Lo;0;L;;;;;N;;;;; +14550;ANATOLIAN HIEROGLYPH A299;Lo;0;L;;;;;N;;;;; +14551;ANATOLIAN HIEROGLYPH A299A;Lo;0;L;;;;;N;;;;; +14552;ANATOLIAN HIEROGLYPH A300;Lo;0;L;;;;;N;;;;; +14553;ANATOLIAN HIEROGLYPH A301;Lo;0;L;;;;;N;;;;; +14554;ANATOLIAN HIEROGLYPH A302;Lo;0;L;;;;;N;;;;; +14555;ANATOLIAN HIEROGLYPH A303;Lo;0;L;;;;;N;;;;; +14556;ANATOLIAN HIEROGLYPH A304;Lo;0;L;;;;;N;;;;; +14557;ANATOLIAN HIEROGLYPH A305;Lo;0;L;;;;;N;;;;; +14558;ANATOLIAN HIEROGLYPH A306;Lo;0;L;;;;;N;;;;; +14559;ANATOLIAN HIEROGLYPH A307;Lo;0;L;;;;;N;;;;; +1455A;ANATOLIAN HIEROGLYPH A308;Lo;0;L;;;;;N;;;;; +1455B;ANATOLIAN HIEROGLYPH A309;Lo;0;L;;;;;N;;;;; +1455C;ANATOLIAN HIEROGLYPH A309A;Lo;0;L;;;;;N;;;;; +1455D;ANATOLIAN HIEROGLYPH A310;Lo;0;L;;;;;N;;;;; +1455E;ANATOLIAN HIEROGLYPH A311;Lo;0;L;;;;;N;;;;; +1455F;ANATOLIAN HIEROGLYPH A312;Lo;0;L;;;;;N;;;;; +14560;ANATOLIAN HIEROGLYPH A313;Lo;0;L;;;;;N;;;;; +14561;ANATOLIAN HIEROGLYPH A314;Lo;0;L;;;;;N;;;;; +14562;ANATOLIAN HIEROGLYPH A315;Lo;0;L;;;;;N;;;;; +14563;ANATOLIAN HIEROGLYPH A316;Lo;0;L;;;;;N;;;;; +14564;ANATOLIAN HIEROGLYPH A317;Lo;0;L;;;;;N;;;;; +14565;ANATOLIAN HIEROGLYPH A318;Lo;0;L;;;;;N;;;;; +14566;ANATOLIAN HIEROGLYPH A319;Lo;0;L;;;;;N;;;;; +14567;ANATOLIAN HIEROGLYPH A320;Lo;0;L;;;;;N;;;;; +14568;ANATOLIAN HIEROGLYPH A321;Lo;0;L;;;;;N;;;;; +14569;ANATOLIAN HIEROGLYPH A322;Lo;0;L;;;;;N;;;;; +1456A;ANATOLIAN HIEROGLYPH A323;Lo;0;L;;;;;N;;;;; +1456B;ANATOLIAN HIEROGLYPH A324;Lo;0;L;;;;;N;;;;; +1456C;ANATOLIAN HIEROGLYPH A325;Lo;0;L;;;;;N;;;;; +1456D;ANATOLIAN HIEROGLYPH A326;Lo;0;L;;;;;N;;;;; +1456E;ANATOLIAN HIEROGLYPH A327;Lo;0;L;;;;;N;;;;; +1456F;ANATOLIAN HIEROGLYPH A328;Lo;0;L;;;;;N;;;;; +14570;ANATOLIAN HIEROGLYPH A329;Lo;0;L;;;;;N;;;;; +14571;ANATOLIAN HIEROGLYPH A329A;Lo;0;L;;;;;N;;;;; +14572;ANATOLIAN HIEROGLYPH A330;Lo;0;L;;;;;N;;;;; +14573;ANATOLIAN HIEROGLYPH A331;Lo;0;L;;;;;N;;;;; +14574;ANATOLIAN HIEROGLYPH A332A;Lo;0;L;;;;;N;;;;; +14575;ANATOLIAN HIEROGLYPH A332B;Lo;0;L;;;;;N;;;;; +14576;ANATOLIAN HIEROGLYPH A332C;Lo;0;L;;;;;N;;;;; +14577;ANATOLIAN HIEROGLYPH A333;Lo;0;L;;;;;N;;;;; +14578;ANATOLIAN HIEROGLYPH A334;Lo;0;L;;;;;N;;;;; +14579;ANATOLIAN HIEROGLYPH A335;Lo;0;L;;;;;N;;;;; +1457A;ANATOLIAN HIEROGLYPH A336;Lo;0;L;;;;;N;;;;; +1457B;ANATOLIAN HIEROGLYPH A336A;Lo;0;L;;;;;N;;;;; +1457C;ANATOLIAN HIEROGLYPH A336B;Lo;0;L;;;;;N;;;;; +1457D;ANATOLIAN HIEROGLYPH A336C;Lo;0;L;;;;;N;;;;; +1457E;ANATOLIAN HIEROGLYPH A337;Lo;0;L;;;;;N;;;;; +1457F;ANATOLIAN HIEROGLYPH A338;Lo;0;L;;;;;N;;;;; +14580;ANATOLIAN HIEROGLYPH A339;Lo;0;L;;;;;N;;;;; +14581;ANATOLIAN HIEROGLYPH A340;Lo;0;L;;;;;N;;;;; +14582;ANATOLIAN HIEROGLYPH A341;Lo;0;L;;;;;N;;;;; +14583;ANATOLIAN HIEROGLYPH A342;Lo;0;L;;;;;N;;;;; +14584;ANATOLIAN HIEROGLYPH A343;Lo;0;L;;;;;N;;;;; +14585;ANATOLIAN HIEROGLYPH A344;Lo;0;L;;;;;N;;;;; +14586;ANATOLIAN HIEROGLYPH A345;Lo;0;L;;;;;N;;;;; +14587;ANATOLIAN HIEROGLYPH A346;Lo;0;L;;;;;N;;;;; +14588;ANATOLIAN HIEROGLYPH A347;Lo;0;L;;;;;N;;;;; +14589;ANATOLIAN HIEROGLYPH A348;Lo;0;L;;;;;N;;;;; +1458A;ANATOLIAN HIEROGLYPH A349;Lo;0;L;;;;;N;;;;; +1458B;ANATOLIAN HIEROGLYPH A350;Lo;0;L;;;;;N;;;;; +1458C;ANATOLIAN HIEROGLYPH A351;Lo;0;L;;;;;N;;;;; +1458D;ANATOLIAN HIEROGLYPH A352;Lo;0;L;;;;;N;;;;; +1458E;ANATOLIAN HIEROGLYPH A353;Lo;0;L;;;;;N;;;;; +1458F;ANATOLIAN HIEROGLYPH A354;Lo;0;L;;;;;N;;;;; +14590;ANATOLIAN HIEROGLYPH A355;Lo;0;L;;;;;N;;;;; +14591;ANATOLIAN HIEROGLYPH A356;Lo;0;L;;;;;N;;;;; +14592;ANATOLIAN HIEROGLYPH A357;Lo;0;L;;;;;N;;;;; +14593;ANATOLIAN HIEROGLYPH A358;Lo;0;L;;;;;N;;;;; +14594;ANATOLIAN HIEROGLYPH A359;Lo;0;L;;;;;N;;;;; +14595;ANATOLIAN HIEROGLYPH A359A;Lo;0;L;;;;;N;;;;; +14596;ANATOLIAN HIEROGLYPH A360;Lo;0;L;;;;;N;;;;; +14597;ANATOLIAN HIEROGLYPH A361;Lo;0;L;;;;;N;;;;; +14598;ANATOLIAN HIEROGLYPH A362;Lo;0;L;;;;;N;;;;; +14599;ANATOLIAN HIEROGLYPH A363;Lo;0;L;;;;;N;;;;; +1459A;ANATOLIAN HIEROGLYPH A364;Lo;0;L;;;;;N;;;;; +1459B;ANATOLIAN HIEROGLYPH A364A;Lo;0;L;;;;;N;;;;; +1459C;ANATOLIAN HIEROGLYPH A365;Lo;0;L;;;;;N;;;;; +1459D;ANATOLIAN HIEROGLYPH A366;Lo;0;L;;;;;N;;;;; +1459E;ANATOLIAN HIEROGLYPH A367;Lo;0;L;;;;;N;;;;; +1459F;ANATOLIAN HIEROGLYPH A368;Lo;0;L;;;;;N;;;;; +145A0;ANATOLIAN HIEROGLYPH A368A;Lo;0;L;;;;;N;;;;; +145A1;ANATOLIAN HIEROGLYPH A369;Lo;0;L;;;;;N;;;;; +145A2;ANATOLIAN HIEROGLYPH A370;Lo;0;L;;;;;N;;;;; +145A3;ANATOLIAN HIEROGLYPH A371;Lo;0;L;;;;;N;;;;; +145A4;ANATOLIAN HIEROGLYPH A371A;Lo;0;L;;;;;N;;;;; +145A5;ANATOLIAN HIEROGLYPH A372;Lo;0;L;;;;;N;;;;; +145A6;ANATOLIAN HIEROGLYPH A373;Lo;0;L;;;;;N;;;;; +145A7;ANATOLIAN HIEROGLYPH A374;Lo;0;L;;;;;N;;;;; +145A8;ANATOLIAN HIEROGLYPH A375;Lo;0;L;;;;;N;;;;; +145A9;ANATOLIAN HIEROGLYPH A376;Lo;0;L;;;;;N;;;;; +145AA;ANATOLIAN HIEROGLYPH A377;Lo;0;L;;;;;N;;;;; +145AB;ANATOLIAN HIEROGLYPH A378;Lo;0;L;;;;;N;;;;; +145AC;ANATOLIAN HIEROGLYPH A379;Lo;0;L;;;;;N;;;;; +145AD;ANATOLIAN HIEROGLYPH A380;Lo;0;L;;;;;N;;;;; +145AE;ANATOLIAN HIEROGLYPH A381;Lo;0;L;;;;;N;;;;; +145AF;ANATOLIAN HIEROGLYPH A381A;Lo;0;L;;;;;N;;;;; +145B0;ANATOLIAN HIEROGLYPH A382;Lo;0;L;;;;;N;;;;; +145B1;ANATOLIAN HIEROGLYPH A383 RA OR RI;Lo;0;L;;;;;N;;;;; +145B2;ANATOLIAN HIEROGLYPH A383A;Lo;0;L;;;;;N;;;;; +145B3;ANATOLIAN HIEROGLYPH A384;Lo;0;L;;;;;N;;;;; +145B4;ANATOLIAN HIEROGLYPH A385;Lo;0;L;;;;;N;;;;; +145B5;ANATOLIAN HIEROGLYPH A386;Lo;0;L;;;;;N;;;;; +145B6;ANATOLIAN HIEROGLYPH A386A;Lo;0;L;;;;;N;;;;; +145B7;ANATOLIAN HIEROGLYPH A387;Lo;0;L;;;;;N;;;;; +145B8;ANATOLIAN HIEROGLYPH A388;Lo;0;L;;;;;N;;;;; +145B9;ANATOLIAN HIEROGLYPH A389;Lo;0;L;;;;;N;;;;; +145BA;ANATOLIAN HIEROGLYPH A390;Lo;0;L;;;;;N;;;;; +145BB;ANATOLIAN HIEROGLYPH A391;Lo;0;L;;;;;N;;;;; +145BC;ANATOLIAN HIEROGLYPH A392;Lo;0;L;;;;;N;;;;; +145BD;ANATOLIAN HIEROGLYPH A393 EIGHT;Lo;0;L;;;;;N;;;;; +145BE;ANATOLIAN HIEROGLYPH A394;Lo;0;L;;;;;N;;;;; +145BF;ANATOLIAN HIEROGLYPH A395;Lo;0;L;;;;;N;;;;; +145C0;ANATOLIAN HIEROGLYPH A396;Lo;0;L;;;;;N;;;;; +145C1;ANATOLIAN HIEROGLYPH A397;Lo;0;L;;;;;N;;;;; +145C2;ANATOLIAN HIEROGLYPH A398;Lo;0;L;;;;;N;;;;; +145C3;ANATOLIAN HIEROGLYPH A399;Lo;0;L;;;;;N;;;;; +145C4;ANATOLIAN HIEROGLYPH A400;Lo;0;L;;;;;N;;;;; +145C5;ANATOLIAN HIEROGLYPH A401;Lo;0;L;;;;;N;;;;; +145C6;ANATOLIAN HIEROGLYPH A402;Lo;0;L;;;;;N;;;;; +145C7;ANATOLIAN HIEROGLYPH A403;Lo;0;L;;;;;N;;;;; +145C8;ANATOLIAN HIEROGLYPH A404;Lo;0;L;;;;;N;;;;; +145C9;ANATOLIAN HIEROGLYPH A405;Lo;0;L;;;;;N;;;;; +145CA;ANATOLIAN HIEROGLYPH A406;Lo;0;L;;;;;N;;;;; +145CB;ANATOLIAN HIEROGLYPH A407;Lo;0;L;;;;;N;;;;; +145CC;ANATOLIAN HIEROGLYPH A408;Lo;0;L;;;;;N;;;;; +145CD;ANATOLIAN HIEROGLYPH A409;Lo;0;L;;;;;N;;;;; +145CE;ANATOLIAN HIEROGLYPH A410 BEGIN LOGOGRAM MARK;Lo;0;L;;;;;N;;;;; +145CF;ANATOLIAN HIEROGLYPH A410A END LOGOGRAM MARK;Lo;0;L;;;;;N;;;;; +145D0;ANATOLIAN HIEROGLYPH A411;Lo;0;L;;;;;N;;;;; +145D1;ANATOLIAN HIEROGLYPH A412;Lo;0;L;;;;;N;;;;; +145D2;ANATOLIAN HIEROGLYPH A413;Lo;0;L;;;;;N;;;;; +145D3;ANATOLIAN HIEROGLYPH A414;Lo;0;L;;;;;N;;;;; +145D4;ANATOLIAN HIEROGLYPH A415;Lo;0;L;;;;;N;;;;; +145D5;ANATOLIAN HIEROGLYPH A416;Lo;0;L;;;;;N;;;;; +145D6;ANATOLIAN HIEROGLYPH A417;Lo;0;L;;;;;N;;;;; +145D7;ANATOLIAN HIEROGLYPH A418;Lo;0;L;;;;;N;;;;; +145D8;ANATOLIAN HIEROGLYPH A419;Lo;0;L;;;;;N;;;;; +145D9;ANATOLIAN HIEROGLYPH A420;Lo;0;L;;;;;N;;;;; +145DA;ANATOLIAN HIEROGLYPH A421;Lo;0;L;;;;;N;;;;; +145DB;ANATOLIAN HIEROGLYPH A422;Lo;0;L;;;;;N;;;;; +145DC;ANATOLIAN HIEROGLYPH A423;Lo;0;L;;;;;N;;;;; +145DD;ANATOLIAN HIEROGLYPH A424;Lo;0;L;;;;;N;;;;; +145DE;ANATOLIAN HIEROGLYPH A425;Lo;0;L;;;;;N;;;;; +145DF;ANATOLIAN HIEROGLYPH A426;Lo;0;L;;;;;N;;;;; +145E0;ANATOLIAN HIEROGLYPH A427;Lo;0;L;;;;;N;;;;; +145E1;ANATOLIAN HIEROGLYPH A428;Lo;0;L;;;;;N;;;;; +145E2;ANATOLIAN HIEROGLYPH A429;Lo;0;L;;;;;N;;;;; +145E3;ANATOLIAN HIEROGLYPH A430;Lo;0;L;;;;;N;;;;; +145E4;ANATOLIAN HIEROGLYPH A431;Lo;0;L;;;;;N;;;;; +145E5;ANATOLIAN HIEROGLYPH A432;Lo;0;L;;;;;N;;;;; +145E6;ANATOLIAN HIEROGLYPH A433;Lo;0;L;;;;;N;;;;; +145E7;ANATOLIAN HIEROGLYPH A434;Lo;0;L;;;;;N;;;;; +145E8;ANATOLIAN HIEROGLYPH A435;Lo;0;L;;;;;N;;;;; +145E9;ANATOLIAN HIEROGLYPH A436;Lo;0;L;;;;;N;;;;; +145EA;ANATOLIAN HIEROGLYPH A437;Lo;0;L;;;;;N;;;;; +145EB;ANATOLIAN HIEROGLYPH A438;Lo;0;L;;;;;N;;;;; +145EC;ANATOLIAN HIEROGLYPH A439;Lo;0;L;;;;;N;;;;; +145ED;ANATOLIAN HIEROGLYPH A440;Lo;0;L;;;;;N;;;;; +145EE;ANATOLIAN HIEROGLYPH A441;Lo;0;L;;;;;N;;;;; +145EF;ANATOLIAN HIEROGLYPH A442;Lo;0;L;;;;;N;;;;; +145F0;ANATOLIAN HIEROGLYPH A443;Lo;0;L;;;;;N;;;;; +145F1;ANATOLIAN HIEROGLYPH A444;Lo;0;L;;;;;N;;;;; +145F2;ANATOLIAN HIEROGLYPH A445;Lo;0;L;;;;;N;;;;; +145F3;ANATOLIAN HIEROGLYPH A446;Lo;0;L;;;;;N;;;;; +145F4;ANATOLIAN HIEROGLYPH A447;Lo;0;L;;;;;N;;;;; +145F5;ANATOLIAN HIEROGLYPH A448;Lo;0;L;;;;;N;;;;; +145F6;ANATOLIAN HIEROGLYPH A449;Lo;0;L;;;;;N;;;;; +145F7;ANATOLIAN HIEROGLYPH A450;Lo;0;L;;;;;N;;;;; +145F8;ANATOLIAN HIEROGLYPH A450A;Lo;0;L;;;;;N;;;;; +145F9;ANATOLIAN HIEROGLYPH A451;Lo;0;L;;;;;N;;;;; +145FA;ANATOLIAN HIEROGLYPH A452;Lo;0;L;;;;;N;;;;; +145FB;ANATOLIAN HIEROGLYPH A453;Lo;0;L;;;;;N;;;;; +145FC;ANATOLIAN HIEROGLYPH A454;Lo;0;L;;;;;N;;;;; +145FD;ANATOLIAN HIEROGLYPH A455;Lo;0;L;;;;;N;;;;; +145FE;ANATOLIAN HIEROGLYPH A456;Lo;0;L;;;;;N;;;;; +145FF;ANATOLIAN HIEROGLYPH A457;Lo;0;L;;;;;N;;;;; +14600;ANATOLIAN HIEROGLYPH A457A;Lo;0;L;;;;;N;;;;; +14601;ANATOLIAN HIEROGLYPH A458;Lo;0;L;;;;;N;;;;; +14602;ANATOLIAN HIEROGLYPH A459;Lo;0;L;;;;;N;;;;; +14603;ANATOLIAN HIEROGLYPH A460;Lo;0;L;;;;;N;;;;; +14604;ANATOLIAN HIEROGLYPH A461;Lo;0;L;;;;;N;;;;; +14605;ANATOLIAN HIEROGLYPH A462;Lo;0;L;;;;;N;;;;; +14606;ANATOLIAN HIEROGLYPH A463;Lo;0;L;;;;;N;;;;; +14607;ANATOLIAN HIEROGLYPH A464;Lo;0;L;;;;;N;;;;; +14608;ANATOLIAN HIEROGLYPH A465;Lo;0;L;;;;;N;;;;; +14609;ANATOLIAN HIEROGLYPH A466;Lo;0;L;;;;;N;;;;; +1460A;ANATOLIAN HIEROGLYPH A467;Lo;0;L;;;;;N;;;;; +1460B;ANATOLIAN HIEROGLYPH A468;Lo;0;L;;;;;N;;;;; +1460C;ANATOLIAN HIEROGLYPH A469;Lo;0;L;;;;;N;;;;; +1460D;ANATOLIAN HIEROGLYPH A470;Lo;0;L;;;;;N;;;;; +1460E;ANATOLIAN HIEROGLYPH A471;Lo;0;L;;;;;N;;;;; +1460F;ANATOLIAN HIEROGLYPH A472;Lo;0;L;;;;;N;;;;; +14610;ANATOLIAN HIEROGLYPH A473;Lo;0;L;;;;;N;;;;; +14611;ANATOLIAN HIEROGLYPH A474;Lo;0;L;;;;;N;;;;; +14612;ANATOLIAN HIEROGLYPH A475;Lo;0;L;;;;;N;;;;; +14613;ANATOLIAN HIEROGLYPH A476;Lo;0;L;;;;;N;;;;; +14614;ANATOLIAN HIEROGLYPH A477;Lo;0;L;;;;;N;;;;; +14615;ANATOLIAN HIEROGLYPH A478;Lo;0;L;;;;;N;;;;; +14616;ANATOLIAN HIEROGLYPH A479;Lo;0;L;;;;;N;;;;; +14617;ANATOLIAN HIEROGLYPH A480;Lo;0;L;;;;;N;;;;; +14618;ANATOLIAN HIEROGLYPH A481;Lo;0;L;;;;;N;;;;; +14619;ANATOLIAN HIEROGLYPH A482;Lo;0;L;;;;;N;;;;; +1461A;ANATOLIAN HIEROGLYPH A483;Lo;0;L;;;;;N;;;;; +1461B;ANATOLIAN HIEROGLYPH A484;Lo;0;L;;;;;N;;;;; +1461C;ANATOLIAN HIEROGLYPH A485;Lo;0;L;;;;;N;;;;; +1461D;ANATOLIAN HIEROGLYPH A486;Lo;0;L;;;;;N;;;;; +1461E;ANATOLIAN HIEROGLYPH A487;Lo;0;L;;;;;N;;;;; +1461F;ANATOLIAN HIEROGLYPH A488;Lo;0;L;;;;;N;;;;; +14620;ANATOLIAN HIEROGLYPH A489;Lo;0;L;;;;;N;;;;; +14621;ANATOLIAN HIEROGLYPH A490;Lo;0;L;;;;;N;;;;; +14622;ANATOLIAN HIEROGLYPH A491;Lo;0;L;;;;;N;;;;; +14623;ANATOLIAN HIEROGLYPH A492;Lo;0;L;;;;;N;;;;; +14624;ANATOLIAN HIEROGLYPH A493;Lo;0;L;;;;;N;;;;; +14625;ANATOLIAN HIEROGLYPH A494;Lo;0;L;;;;;N;;;;; +14626;ANATOLIAN HIEROGLYPH A495;Lo;0;L;;;;;N;;;;; +14627;ANATOLIAN HIEROGLYPH A496;Lo;0;L;;;;;N;;;;; +14628;ANATOLIAN HIEROGLYPH A497;Lo;0;L;;;;;N;;;;; +14629;ANATOLIAN HIEROGLYPH A501;Lo;0;L;;;;;N;;;;; +1462A;ANATOLIAN HIEROGLYPH A502;Lo;0;L;;;;;N;;;;; +1462B;ANATOLIAN HIEROGLYPH A503;Lo;0;L;;;;;N;;;;; +1462C;ANATOLIAN HIEROGLYPH A504;Lo;0;L;;;;;N;;;;; +1462D;ANATOLIAN HIEROGLYPH A505;Lo;0;L;;;;;N;;;;; +1462E;ANATOLIAN HIEROGLYPH A506;Lo;0;L;;;;;N;;;;; +1462F;ANATOLIAN HIEROGLYPH A507;Lo;0;L;;;;;N;;;;; +14630;ANATOLIAN HIEROGLYPH A508;Lo;0;L;;;;;N;;;;; +14631;ANATOLIAN HIEROGLYPH A509;Lo;0;L;;;;;N;;;;; +14632;ANATOLIAN HIEROGLYPH A510;Lo;0;L;;;;;N;;;;; +14633;ANATOLIAN HIEROGLYPH A511;Lo;0;L;;;;;N;;;;; +14634;ANATOLIAN HIEROGLYPH A512;Lo;0;L;;;;;N;;;;; +14635;ANATOLIAN HIEROGLYPH A513;Lo;0;L;;;;;N;;;;; +14636;ANATOLIAN HIEROGLYPH A514;Lo;0;L;;;;;N;;;;; +14637;ANATOLIAN HIEROGLYPH A515;Lo;0;L;;;;;N;;;;; +14638;ANATOLIAN HIEROGLYPH A516;Lo;0;L;;;;;N;;;;; +14639;ANATOLIAN HIEROGLYPH A517;Lo;0;L;;;;;N;;;;; +1463A;ANATOLIAN HIEROGLYPH A518;Lo;0;L;;;;;N;;;;; +1463B;ANATOLIAN HIEROGLYPH A519;Lo;0;L;;;;;N;;;;; +1463C;ANATOLIAN HIEROGLYPH A520;Lo;0;L;;;;;N;;;;; +1463D;ANATOLIAN HIEROGLYPH A521;Lo;0;L;;;;;N;;;;; +1463E;ANATOLIAN HIEROGLYPH A522;Lo;0;L;;;;;N;;;;; +1463F;ANATOLIAN HIEROGLYPH A523;Lo;0;L;;;;;N;;;;; +14640;ANATOLIAN HIEROGLYPH A524;Lo;0;L;;;;;N;;;;; +14641;ANATOLIAN HIEROGLYPH A525;Lo;0;L;;;;;N;;;;; +14642;ANATOLIAN HIEROGLYPH A526;Lo;0;L;;;;;N;;;;; +14643;ANATOLIAN HIEROGLYPH A527;Lo;0;L;;;;;N;;;;; +14644;ANATOLIAN HIEROGLYPH A528;Lo;0;L;;;;;N;;;;; +14645;ANATOLIAN HIEROGLYPH A529;Lo;0;L;;;;;N;;;;; +14646;ANATOLIAN HIEROGLYPH A530;Lo;0;L;;;;;N;;;;; +16800;BAMUM LETTER PHASE-A NGKUE MFON;Lo;0;L;;;;;N;;;;; +16801;BAMUM LETTER PHASE-A GBIEE FON;Lo;0;L;;;;;N;;;;; +16802;BAMUM LETTER PHASE-A PON MFON PIPAEMGBIEE;Lo;0;L;;;;;N;;;;; +16803;BAMUM LETTER PHASE-A PON MFON PIPAEMBA;Lo;0;L;;;;;N;;;;; +16804;BAMUM LETTER PHASE-A NAA MFON;Lo;0;L;;;;;N;;;;; +16805;BAMUM LETTER PHASE-A SHUENSHUET;Lo;0;L;;;;;N;;;;; +16806;BAMUM LETTER PHASE-A TITA MFON;Lo;0;L;;;;;N;;;;; +16807;BAMUM LETTER PHASE-A NZA MFON;Lo;0;L;;;;;N;;;;; +16808;BAMUM LETTER PHASE-A SHINDA PA NJI;Lo;0;L;;;;;N;;;;; +16809;BAMUM LETTER PHASE-A PON PA NJI PIPAEMGBIEE;Lo;0;L;;;;;N;;;;; +1680A;BAMUM LETTER PHASE-A PON PA NJI PIPAEMBA;Lo;0;L;;;;;N;;;;; +1680B;BAMUM LETTER PHASE-A MAEMBGBIEE;Lo;0;L;;;;;N;;;;; +1680C;BAMUM LETTER PHASE-A TU MAEMBA;Lo;0;L;;;;;N;;;;; +1680D;BAMUM LETTER PHASE-A NGANGU;Lo;0;L;;;;;N;;;;; +1680E;BAMUM LETTER PHASE-A MAEMVEUX;Lo;0;L;;;;;N;;;;; +1680F;BAMUM LETTER PHASE-A MANSUAE;Lo;0;L;;;;;N;;;;; +16810;BAMUM LETTER PHASE-A MVEUAENGAM;Lo;0;L;;;;;N;;;;; +16811;BAMUM LETTER PHASE-A SEUNYAM;Lo;0;L;;;;;N;;;;; +16812;BAMUM LETTER PHASE-A NTOQPEN;Lo;0;L;;;;;N;;;;; +16813;BAMUM LETTER PHASE-A KEUKEUTNDA;Lo;0;L;;;;;N;;;;; +16814;BAMUM LETTER PHASE-A NKINDI;Lo;0;L;;;;;N;;;;; +16815;BAMUM LETTER PHASE-A SUU;Lo;0;L;;;;;N;;;;; +16816;BAMUM LETTER PHASE-A NGKUENZEUM;Lo;0;L;;;;;N;;;;; +16817;BAMUM LETTER PHASE-A LAPAQ;Lo;0;L;;;;;N;;;;; +16818;BAMUM LETTER PHASE-A LET KUT;Lo;0;L;;;;;N;;;;; +16819;BAMUM LETTER PHASE-A NTAP MFAA;Lo;0;L;;;;;N;;;;; +1681A;BAMUM LETTER PHASE-A MAEKEUP;Lo;0;L;;;;;N;;;;; +1681B;BAMUM LETTER PHASE-A PASHAE;Lo;0;L;;;;;N;;;;; +1681C;BAMUM LETTER PHASE-A GHEUAERAE;Lo;0;L;;;;;N;;;;; +1681D;BAMUM LETTER PHASE-A PAMSHAE;Lo;0;L;;;;;N;;;;; +1681E;BAMUM LETTER PHASE-A MON NGGEUAET;Lo;0;L;;;;;N;;;;; +1681F;BAMUM LETTER PHASE-A NZUN MEUT;Lo;0;L;;;;;N;;;;; +16820;BAMUM LETTER PHASE-A U YUQ NAE;Lo;0;L;;;;;N;;;;; +16821;BAMUM LETTER PHASE-A GHEUAEGHEUAE;Lo;0;L;;;;;N;;;;; +16822;BAMUM LETTER PHASE-A NTAP NTAA;Lo;0;L;;;;;N;;;;; +16823;BAMUM LETTER PHASE-A SISA;Lo;0;L;;;;;N;;;;; +16824;BAMUM LETTER PHASE-A MGBASA;Lo;0;L;;;;;N;;;;; +16825;BAMUM LETTER PHASE-A MEUNJOMNDEUQ;Lo;0;L;;;;;N;;;;; +16826;BAMUM LETTER PHASE-A MOOMPUQ;Lo;0;L;;;;;N;;;;; +16827;BAMUM LETTER PHASE-A KAFA;Lo;0;L;;;;;N;;;;; +16828;BAMUM LETTER PHASE-A PA LEERAEWA;Lo;0;L;;;;;N;;;;; +16829;BAMUM LETTER PHASE-A NDA LEERAEWA;Lo;0;L;;;;;N;;;;; +1682A;BAMUM LETTER PHASE-A PET;Lo;0;L;;;;;N;;;;; +1682B;BAMUM LETTER PHASE-A MAEMKPEN;Lo;0;L;;;;;N;;;;; +1682C;BAMUM LETTER PHASE-A NIKA;Lo;0;L;;;;;N;;;;; +1682D;BAMUM LETTER PHASE-A PUP;Lo;0;L;;;;;N;;;;; +1682E;BAMUM LETTER PHASE-A TUAEP;Lo;0;L;;;;;N;;;;; +1682F;BAMUM LETTER PHASE-A LUAEP;Lo;0;L;;;;;N;;;;; +16830;BAMUM LETTER PHASE-A SONJAM;Lo;0;L;;;;;N;;;;; +16831;BAMUM LETTER PHASE-A TEUTEUWEN;Lo;0;L;;;;;N;;;;; +16832;BAMUM LETTER PHASE-A MAENYI;Lo;0;L;;;;;N;;;;; +16833;BAMUM LETTER PHASE-A KET;Lo;0;L;;;;;N;;;;; +16834;BAMUM LETTER PHASE-A NDAANGGEUAET;Lo;0;L;;;;;N;;;;; +16835;BAMUM LETTER PHASE-A KUOQ;Lo;0;L;;;;;N;;;;; +16836;BAMUM LETTER PHASE-A MOOMEUT;Lo;0;L;;;;;N;;;;; +16837;BAMUM LETTER PHASE-A SHUM;Lo;0;L;;;;;N;;;;; +16838;BAMUM LETTER PHASE-A LOMMAE;Lo;0;L;;;;;N;;;;; +16839;BAMUM LETTER PHASE-A FIRI;Lo;0;L;;;;;N;;;;; +1683A;BAMUM LETTER PHASE-A ROM;Lo;0;L;;;;;N;;;;; +1683B;BAMUM LETTER PHASE-A KPOQ;Lo;0;L;;;;;N;;;;; +1683C;BAMUM LETTER PHASE-A SOQ;Lo;0;L;;;;;N;;;;; +1683D;BAMUM LETTER PHASE-A MAP PIEET;Lo;0;L;;;;;N;;;;; +1683E;BAMUM LETTER PHASE-A SHIRAE;Lo;0;L;;;;;N;;;;; +1683F;BAMUM LETTER PHASE-A NTAP;Lo;0;L;;;;;N;;;;; +16840;BAMUM LETTER PHASE-A SHOQ NSHUT YUM;Lo;0;L;;;;;N;;;;; +16841;BAMUM LETTER PHASE-A NYIT MONGKEUAEQ;Lo;0;L;;;;;N;;;;; +16842;BAMUM LETTER PHASE-A PAARAE;Lo;0;L;;;;;N;;;;; +16843;BAMUM LETTER PHASE-A NKAARAE;Lo;0;L;;;;;N;;;;; +16844;BAMUM LETTER PHASE-A UNKNOWN;Lo;0;L;;;;;N;;;;; +16845;BAMUM LETTER PHASE-A NGGEN;Lo;0;L;;;;;N;;;;; +16846;BAMUM LETTER PHASE-A MAESI;Lo;0;L;;;;;N;;;;; +16847;BAMUM LETTER PHASE-A NJAM;Lo;0;L;;;;;N;;;;; +16848;BAMUM LETTER PHASE-A MBANYI;Lo;0;L;;;;;N;;;;; +16849;BAMUM LETTER PHASE-A NYET;Lo;0;L;;;;;N;;;;; +1684A;BAMUM LETTER PHASE-A TEUAEN;Lo;0;L;;;;;N;;;;; +1684B;BAMUM LETTER PHASE-A SOT;Lo;0;L;;;;;N;;;;; +1684C;BAMUM LETTER PHASE-A PAAM;Lo;0;L;;;;;N;;;;; +1684D;BAMUM LETTER PHASE-A NSHIEE;Lo;0;L;;;;;N;;;;; +1684E;BAMUM LETTER PHASE-A MAEM;Lo;0;L;;;;;N;;;;; +1684F;BAMUM LETTER PHASE-A NYI;Lo;0;L;;;;;N;;;;; +16850;BAMUM LETTER PHASE-A KAQ;Lo;0;L;;;;;N;;;;; +16851;BAMUM LETTER PHASE-A NSHA;Lo;0;L;;;;;N;;;;; +16852;BAMUM LETTER PHASE-A VEE;Lo;0;L;;;;;N;;;;; +16853;BAMUM LETTER PHASE-A LU;Lo;0;L;;;;;N;;;;; +16854;BAMUM LETTER PHASE-A NEN;Lo;0;L;;;;;N;;;;; +16855;BAMUM LETTER PHASE-A NAQ;Lo;0;L;;;;;N;;;;; +16856;BAMUM LETTER PHASE-A MBAQ;Lo;0;L;;;;;N;;;;; +16857;BAMUM LETTER PHASE-B NSHUET;Lo;0;L;;;;;N;;;;; +16858;BAMUM LETTER PHASE-B TU MAEMGBIEE;Lo;0;L;;;;;N;;;;; +16859;BAMUM LETTER PHASE-B SIEE;Lo;0;L;;;;;N;;;;; +1685A;BAMUM LETTER PHASE-B SET TU;Lo;0;L;;;;;N;;;;; +1685B;BAMUM LETTER PHASE-B LOM NTEUM;Lo;0;L;;;;;N;;;;; +1685C;BAMUM LETTER PHASE-B MBA MAELEE;Lo;0;L;;;;;N;;;;; +1685D;BAMUM LETTER PHASE-B KIEEM;Lo;0;L;;;;;N;;;;; +1685E;BAMUM LETTER PHASE-B YEURAE;Lo;0;L;;;;;N;;;;; +1685F;BAMUM LETTER PHASE-B MBAARAE;Lo;0;L;;;;;N;;;;; +16860;BAMUM LETTER PHASE-B KAM;Lo;0;L;;;;;N;;;;; +16861;BAMUM LETTER PHASE-B PEESHI;Lo;0;L;;;;;N;;;;; +16862;BAMUM LETTER PHASE-B YAFU LEERAEWA;Lo;0;L;;;;;N;;;;; +16863;BAMUM LETTER PHASE-B LAM NSHUT NYAM;Lo;0;L;;;;;N;;;;; +16864;BAMUM LETTER PHASE-B NTIEE SHEUOQ;Lo;0;L;;;;;N;;;;; +16865;BAMUM LETTER PHASE-B NDU NJAA;Lo;0;L;;;;;N;;;;; +16866;BAMUM LETTER PHASE-B GHEUGHEUAEM;Lo;0;L;;;;;N;;;;; +16867;BAMUM LETTER PHASE-B PIT;Lo;0;L;;;;;N;;;;; +16868;BAMUM LETTER PHASE-B TU NSIEE;Lo;0;L;;;;;N;;;;; +16869;BAMUM LETTER PHASE-B SHET NJAQ;Lo;0;L;;;;;N;;;;; +1686A;BAMUM LETTER PHASE-B SHEUAEQTU;Lo;0;L;;;;;N;;;;; +1686B;BAMUM LETTER PHASE-B MFON TEUAEQ;Lo;0;L;;;;;N;;;;; +1686C;BAMUM LETTER PHASE-B MBIT MBAAKET;Lo;0;L;;;;;N;;;;; +1686D;BAMUM LETTER PHASE-B NYI NTEUM;Lo;0;L;;;;;N;;;;; +1686E;BAMUM LETTER PHASE-B KEUPUQ;Lo;0;L;;;;;N;;;;; +1686F;BAMUM LETTER PHASE-B GHEUGHEN;Lo;0;L;;;;;N;;;;; +16870;BAMUM LETTER PHASE-B KEUYEUX;Lo;0;L;;;;;N;;;;; +16871;BAMUM LETTER PHASE-B LAANAE;Lo;0;L;;;;;N;;;;; +16872;BAMUM LETTER PHASE-B PARUM;Lo;0;L;;;;;N;;;;; +16873;BAMUM LETTER PHASE-B VEUM;Lo;0;L;;;;;N;;;;; +16874;BAMUM LETTER PHASE-B NGKINDI MVOP;Lo;0;L;;;;;N;;;;; +16875;BAMUM LETTER PHASE-B NGGEU MBU;Lo;0;L;;;;;N;;;;; +16876;BAMUM LETTER PHASE-B WUAET;Lo;0;L;;;;;N;;;;; +16877;BAMUM LETTER PHASE-B SAKEUAE;Lo;0;L;;;;;N;;;;; +16878;BAMUM LETTER PHASE-B TAAM;Lo;0;L;;;;;N;;;;; +16879;BAMUM LETTER PHASE-B MEUQ;Lo;0;L;;;;;N;;;;; +1687A;BAMUM LETTER PHASE-B NGGUOQ;Lo;0;L;;;;;N;;;;; +1687B;BAMUM LETTER PHASE-B NGGUOQ LARGE;Lo;0;L;;;;;N;;;;; +1687C;BAMUM LETTER PHASE-B MFIYAQ;Lo;0;L;;;;;N;;;;; +1687D;BAMUM LETTER PHASE-B SUE;Lo;0;L;;;;;N;;;;; +1687E;BAMUM LETTER PHASE-B MBEURI;Lo;0;L;;;;;N;;;;; +1687F;BAMUM LETTER PHASE-B MONTIEEN;Lo;0;L;;;;;N;;;;; +16880;BAMUM LETTER PHASE-B NYAEMAE;Lo;0;L;;;;;N;;;;; +16881;BAMUM LETTER PHASE-B PUNGAAM;Lo;0;L;;;;;N;;;;; +16882;BAMUM LETTER PHASE-B MEUT NGGEET;Lo;0;L;;;;;N;;;;; +16883;BAMUM LETTER PHASE-B FEUX;Lo;0;L;;;;;N;;;;; +16884;BAMUM LETTER PHASE-B MBUOQ;Lo;0;L;;;;;N;;;;; +16885;BAMUM LETTER PHASE-B FEE;Lo;0;L;;;;;N;;;;; +16886;BAMUM LETTER PHASE-B KEUAEM;Lo;0;L;;;;;N;;;;; +16887;BAMUM LETTER PHASE-B MA NJEUAENA;Lo;0;L;;;;;N;;;;; +16888;BAMUM LETTER PHASE-B MA NJUQA;Lo;0;L;;;;;N;;;;; +16889;BAMUM LETTER PHASE-B LET;Lo;0;L;;;;;N;;;;; +1688A;BAMUM LETTER PHASE-B NGGAAM;Lo;0;L;;;;;N;;;;; +1688B;BAMUM LETTER PHASE-B NSEN;Lo;0;L;;;;;N;;;;; +1688C;BAMUM LETTER PHASE-B MA;Lo;0;L;;;;;N;;;;; +1688D;BAMUM LETTER PHASE-B KIQ;Lo;0;L;;;;;N;;;;; +1688E;BAMUM LETTER PHASE-B NGOM;Lo;0;L;;;;;N;;;;; +1688F;BAMUM LETTER PHASE-C NGKUE MAEMBA;Lo;0;L;;;;;N;;;;; +16890;BAMUM LETTER PHASE-C NZA;Lo;0;L;;;;;N;;;;; +16891;BAMUM LETTER PHASE-C YUM;Lo;0;L;;;;;N;;;;; +16892;BAMUM LETTER PHASE-C WANGKUOQ;Lo;0;L;;;;;N;;;;; +16893;BAMUM LETTER PHASE-C NGGEN;Lo;0;L;;;;;N;;;;; +16894;BAMUM LETTER PHASE-C NDEUAEREE;Lo;0;L;;;;;N;;;;; +16895;BAMUM LETTER PHASE-C NGKAQ;Lo;0;L;;;;;N;;;;; +16896;BAMUM LETTER PHASE-C GHARAE;Lo;0;L;;;;;N;;;;; +16897;BAMUM LETTER PHASE-C MBEEKEET;Lo;0;L;;;;;N;;;;; +16898;BAMUM LETTER PHASE-C GBAYI;Lo;0;L;;;;;N;;;;; +16899;BAMUM LETTER PHASE-C NYIR MKPARAQ MEUN;Lo;0;L;;;;;N;;;;; +1689A;BAMUM LETTER PHASE-C NTU MBIT;Lo;0;L;;;;;N;;;;; +1689B;BAMUM LETTER PHASE-C MBEUM;Lo;0;L;;;;;N;;;;; +1689C;BAMUM LETTER PHASE-C PIRIEEN;Lo;0;L;;;;;N;;;;; +1689D;BAMUM LETTER PHASE-C NDOMBU;Lo;0;L;;;;;N;;;;; +1689E;BAMUM LETTER PHASE-C MBAA CABBAGE-TREE;Lo;0;L;;;;;N;;;;; +1689F;BAMUM LETTER PHASE-C KEUSHEUAEP;Lo;0;L;;;;;N;;;;; +168A0;BAMUM LETTER PHASE-C GHAP;Lo;0;L;;;;;N;;;;; +168A1;BAMUM LETTER PHASE-C KEUKAQ;Lo;0;L;;;;;N;;;;; +168A2;BAMUM LETTER PHASE-C YU MUOMAE;Lo;0;L;;;;;N;;;;; +168A3;BAMUM LETTER PHASE-C NZEUM;Lo;0;L;;;;;N;;;;; +168A4;BAMUM LETTER PHASE-C MBUE;Lo;0;L;;;;;N;;;;; +168A5;BAMUM LETTER PHASE-C NSEUAEN;Lo;0;L;;;;;N;;;;; +168A6;BAMUM LETTER PHASE-C MBIT;Lo;0;L;;;;;N;;;;; +168A7;BAMUM LETTER PHASE-C YEUQ;Lo;0;L;;;;;N;;;;; +168A8;BAMUM LETTER PHASE-C KPARAQ;Lo;0;L;;;;;N;;;;; +168A9;BAMUM LETTER PHASE-C KAA;Lo;0;L;;;;;N;;;;; +168AA;BAMUM LETTER PHASE-C SEUX;Lo;0;L;;;;;N;;;;; +168AB;BAMUM LETTER PHASE-C NDIDA;Lo;0;L;;;;;N;;;;; +168AC;BAMUM LETTER PHASE-C TAASHAE;Lo;0;L;;;;;N;;;;; +168AD;BAMUM LETTER PHASE-C NJUEQ;Lo;0;L;;;;;N;;;;; +168AE;BAMUM LETTER PHASE-C TITA YUE;Lo;0;L;;;;;N;;;;; +168AF;BAMUM LETTER PHASE-C SUAET;Lo;0;L;;;;;N;;;;; +168B0;BAMUM LETTER PHASE-C NGGUAEN NYAM;Lo;0;L;;;;;N;;;;; +168B1;BAMUM LETTER PHASE-C VEUX;Lo;0;L;;;;;N;;;;; +168B2;BAMUM LETTER PHASE-C NANSANAQ;Lo;0;L;;;;;N;;;;; +168B3;BAMUM LETTER PHASE-C MA KEUAERI;Lo;0;L;;;;;N;;;;; +168B4;BAMUM LETTER PHASE-C NTAA;Lo;0;L;;;;;N;;;;; +168B5;BAMUM LETTER PHASE-C NGGUON;Lo;0;L;;;;;N;;;;; +168B6;BAMUM LETTER PHASE-C LAP;Lo;0;L;;;;;N;;;;; +168B7;BAMUM LETTER PHASE-C MBIRIEEN;Lo;0;L;;;;;N;;;;; +168B8;BAMUM LETTER PHASE-C MGBASAQ;Lo;0;L;;;;;N;;;;; +168B9;BAMUM LETTER PHASE-C NTEUNGBA;Lo;0;L;;;;;N;;;;; +168BA;BAMUM LETTER PHASE-C TEUTEUX;Lo;0;L;;;;;N;;;;; +168BB;BAMUM LETTER PHASE-C NGGUM;Lo;0;L;;;;;N;;;;; +168BC;BAMUM LETTER PHASE-C FUE;Lo;0;L;;;;;N;;;;; +168BD;BAMUM LETTER PHASE-C NDEUT;Lo;0;L;;;;;N;;;;; +168BE;BAMUM LETTER PHASE-C NSA;Lo;0;L;;;;;N;;;;; +168BF;BAMUM LETTER PHASE-C NSHAQ;Lo;0;L;;;;;N;;;;; +168C0;BAMUM LETTER PHASE-C BUNG;Lo;0;L;;;;;N;;;;; +168C1;BAMUM LETTER PHASE-C VEUAEPEN;Lo;0;L;;;;;N;;;;; +168C2;BAMUM LETTER PHASE-C MBERAE;Lo;0;L;;;;;N;;;;; +168C3;BAMUM LETTER PHASE-C RU;Lo;0;L;;;;;N;;;;; +168C4;BAMUM LETTER PHASE-C NJAEM;Lo;0;L;;;;;N;;;;; +168C5;BAMUM LETTER PHASE-C LAM;Lo;0;L;;;;;N;;;;; +168C6;BAMUM LETTER PHASE-C TITUAEP;Lo;0;L;;;;;N;;;;; +168C7;BAMUM LETTER PHASE-C NSUOT NGOM;Lo;0;L;;;;;N;;;;; +168C8;BAMUM LETTER PHASE-C NJEEEE;Lo;0;L;;;;;N;;;;; +168C9;BAMUM LETTER PHASE-C KET;Lo;0;L;;;;;N;;;;; +168CA;BAMUM LETTER PHASE-C NGGU;Lo;0;L;;;;;N;;;;; +168CB;BAMUM LETTER PHASE-C MAESI;Lo;0;L;;;;;N;;;;; +168CC;BAMUM LETTER PHASE-C MBUAEM;Lo;0;L;;;;;N;;;;; +168CD;BAMUM LETTER PHASE-C LU;Lo;0;L;;;;;N;;;;; +168CE;BAMUM LETTER PHASE-C KUT;Lo;0;L;;;;;N;;;;; +168CF;BAMUM LETTER PHASE-C NJAM;Lo;0;L;;;;;N;;;;; +168D0;BAMUM LETTER PHASE-C NGOM;Lo;0;L;;;;;N;;;;; +168D1;BAMUM LETTER PHASE-C WUP;Lo;0;L;;;;;N;;;;; +168D2;BAMUM LETTER PHASE-C NGGUEET;Lo;0;L;;;;;N;;;;; +168D3;BAMUM LETTER PHASE-C NSOM;Lo;0;L;;;;;N;;;;; +168D4;BAMUM LETTER PHASE-C NTEN;Lo;0;L;;;;;N;;;;; +168D5;BAMUM LETTER PHASE-C KUOP NKAARAE;Lo;0;L;;;;;N;;;;; +168D6;BAMUM LETTER PHASE-C NSUN;Lo;0;L;;;;;N;;;;; +168D7;BAMUM LETTER PHASE-C NDAM;Lo;0;L;;;;;N;;;;; +168D8;BAMUM LETTER PHASE-C MA NSIEE;Lo;0;L;;;;;N;;;;; +168D9;BAMUM LETTER PHASE-C YAA;Lo;0;L;;;;;N;;;;; +168DA;BAMUM LETTER PHASE-C NDAP;Lo;0;L;;;;;N;;;;; +168DB;BAMUM LETTER PHASE-C SHUEQ;Lo;0;L;;;;;N;;;;; +168DC;BAMUM LETTER PHASE-C SETFON;Lo;0;L;;;;;N;;;;; +168DD;BAMUM LETTER PHASE-C MBI;Lo;0;L;;;;;N;;;;; +168DE;BAMUM LETTER PHASE-C MAEMBA;Lo;0;L;;;;;N;;;;; +168DF;BAMUM LETTER PHASE-C MBANYI;Lo;0;L;;;;;N;;;;; +168E0;BAMUM LETTER PHASE-C KEUSEUX;Lo;0;L;;;;;N;;;;; +168E1;BAMUM LETTER PHASE-C MBEUX;Lo;0;L;;;;;N;;;;; +168E2;BAMUM LETTER PHASE-C KEUM;Lo;0;L;;;;;N;;;;; +168E3;BAMUM LETTER PHASE-C MBAA PICKET;Lo;0;L;;;;;N;;;;; +168E4;BAMUM LETTER PHASE-C YUWOQ;Lo;0;L;;;;;N;;;;; +168E5;BAMUM LETTER PHASE-C NJEUX;Lo;0;L;;;;;N;;;;; +168E6;BAMUM LETTER PHASE-C MIEE;Lo;0;L;;;;;N;;;;; +168E7;BAMUM LETTER PHASE-C MUAE;Lo;0;L;;;;;N;;;;; +168E8;BAMUM LETTER PHASE-C SHIQ;Lo;0;L;;;;;N;;;;; +168E9;BAMUM LETTER PHASE-C KEN LAW;Lo;0;L;;;;;N;;;;; +168EA;BAMUM LETTER PHASE-C KEN FATIGUE;Lo;0;L;;;;;N;;;;; +168EB;BAMUM LETTER PHASE-C NGAQ;Lo;0;L;;;;;N;;;;; +168EC;BAMUM LETTER PHASE-C NAQ;Lo;0;L;;;;;N;;;;; +168ED;BAMUM LETTER PHASE-C LIQ;Lo;0;L;;;;;N;;;;; +168EE;BAMUM LETTER PHASE-C PIN;Lo;0;L;;;;;N;;;;; +168EF;BAMUM LETTER PHASE-C PEN;Lo;0;L;;;;;N;;;;; +168F0;BAMUM LETTER PHASE-C TET;Lo;0;L;;;;;N;;;;; +168F1;BAMUM LETTER PHASE-D MBUO;Lo;0;L;;;;;N;;;;; +168F2;BAMUM LETTER PHASE-D WAP;Lo;0;L;;;;;N;;;;; +168F3;BAMUM LETTER PHASE-D NJI;Lo;0;L;;;;;N;;;;; +168F4;BAMUM LETTER PHASE-D MFON;Lo;0;L;;;;;N;;;;; +168F5;BAMUM LETTER PHASE-D NJIEE;Lo;0;L;;;;;N;;;;; +168F6;BAMUM LETTER PHASE-D LIEE;Lo;0;L;;;;;N;;;;; +168F7;BAMUM LETTER PHASE-D NJEUT;Lo;0;L;;;;;N;;;;; +168F8;BAMUM LETTER PHASE-D NSHEE;Lo;0;L;;;;;N;;;;; +168F9;BAMUM LETTER PHASE-D NGGAAMAE;Lo;0;L;;;;;N;;;;; +168FA;BAMUM LETTER PHASE-D NYAM;Lo;0;L;;;;;N;;;;; +168FB;BAMUM LETTER PHASE-D WUAEN;Lo;0;L;;;;;N;;;;; +168FC;BAMUM LETTER PHASE-D NGKUN;Lo;0;L;;;;;N;;;;; +168FD;BAMUM LETTER PHASE-D SHEE;Lo;0;L;;;;;N;;;;; +168FE;BAMUM LETTER PHASE-D NGKAP;Lo;0;L;;;;;N;;;;; +168FF;BAMUM LETTER PHASE-D KEUAETMEUN;Lo;0;L;;;;;N;;;;; +16900;BAMUM LETTER PHASE-D TEUT;Lo;0;L;;;;;N;;;;; +16901;BAMUM LETTER PHASE-D SHEUAE;Lo;0;L;;;;;N;;;;; +16902;BAMUM LETTER PHASE-D NJAP;Lo;0;L;;;;;N;;;;; +16903;BAMUM LETTER PHASE-D SUE;Lo;0;L;;;;;N;;;;; +16904;BAMUM LETTER PHASE-D KET;Lo;0;L;;;;;N;;;;; +16905;BAMUM LETTER PHASE-D YAEMMAE;Lo;0;L;;;;;N;;;;; +16906;BAMUM LETTER PHASE-D KUOM;Lo;0;L;;;;;N;;;;; +16907;BAMUM LETTER PHASE-D SAP;Lo;0;L;;;;;N;;;;; +16908;BAMUM LETTER PHASE-D MFEUT;Lo;0;L;;;;;N;;;;; +16909;BAMUM LETTER PHASE-D NDEUX;Lo;0;L;;;;;N;;;;; +1690A;BAMUM LETTER PHASE-D MALEERI;Lo;0;L;;;;;N;;;;; +1690B;BAMUM LETTER PHASE-D MEUT;Lo;0;L;;;;;N;;;;; +1690C;BAMUM LETTER PHASE-D SEUAEQ;Lo;0;L;;;;;N;;;;; +1690D;BAMUM LETTER PHASE-D YEN;Lo;0;L;;;;;N;;;;; +1690E;BAMUM LETTER PHASE-D NJEUAEM;Lo;0;L;;;;;N;;;;; +1690F;BAMUM LETTER PHASE-D KEUOT MBUAE;Lo;0;L;;;;;N;;;;; +16910;BAMUM LETTER PHASE-D NGKEURI;Lo;0;L;;;;;N;;;;; +16911;BAMUM LETTER PHASE-D TU;Lo;0;L;;;;;N;;;;; +16912;BAMUM LETTER PHASE-D GHAA;Lo;0;L;;;;;N;;;;; +16913;BAMUM LETTER PHASE-D NGKYEE;Lo;0;L;;;;;N;;;;; +16914;BAMUM LETTER PHASE-D FEUFEUAET;Lo;0;L;;;;;N;;;;; +16915;BAMUM LETTER PHASE-D NDEE;Lo;0;L;;;;;N;;;;; +16916;BAMUM LETTER PHASE-D MGBOFUM;Lo;0;L;;;;;N;;;;; +16917;BAMUM LETTER PHASE-D LEUAEP;Lo;0;L;;;;;N;;;;; +16918;BAMUM LETTER PHASE-D NDON;Lo;0;L;;;;;N;;;;; +16919;BAMUM LETTER PHASE-D MONI;Lo;0;L;;;;;N;;;;; +1691A;BAMUM LETTER PHASE-D MGBEUN;Lo;0;L;;;;;N;;;;; +1691B;BAMUM LETTER PHASE-D PUUT;Lo;0;L;;;;;N;;;;; +1691C;BAMUM LETTER PHASE-D MGBIEE;Lo;0;L;;;;;N;;;;; +1691D;BAMUM LETTER PHASE-D MFO;Lo;0;L;;;;;N;;;;; +1691E;BAMUM LETTER PHASE-D LUM;Lo;0;L;;;;;N;;;;; +1691F;BAMUM LETTER PHASE-D NSIEEP;Lo;0;L;;;;;N;;;;; +16920;BAMUM LETTER PHASE-D MBAA;Lo;0;L;;;;;N;;;;; +16921;BAMUM LETTER PHASE-D KWAET;Lo;0;L;;;;;N;;;;; +16922;BAMUM LETTER PHASE-D NYET;Lo;0;L;;;;;N;;;;; +16923;BAMUM LETTER PHASE-D TEUAEN;Lo;0;L;;;;;N;;;;; +16924;BAMUM LETTER PHASE-D SOT;Lo;0;L;;;;;N;;;;; +16925;BAMUM LETTER PHASE-D YUWOQ;Lo;0;L;;;;;N;;;;; +16926;BAMUM LETTER PHASE-D KEUM;Lo;0;L;;;;;N;;;;; +16927;BAMUM LETTER PHASE-D RAEM;Lo;0;L;;;;;N;;;;; +16928;BAMUM LETTER PHASE-D TEEEE;Lo;0;L;;;;;N;;;;; +16929;BAMUM LETTER PHASE-D NGKEUAEQ;Lo;0;L;;;;;N;;;;; +1692A;BAMUM LETTER PHASE-D MFEUAE;Lo;0;L;;;;;N;;;;; +1692B;BAMUM LETTER PHASE-D NSIEET;Lo;0;L;;;;;N;;;;; +1692C;BAMUM LETTER PHASE-D KEUP;Lo;0;L;;;;;N;;;;; +1692D;BAMUM LETTER PHASE-D PIP;Lo;0;L;;;;;N;;;;; +1692E;BAMUM LETTER PHASE-D PEUTAE;Lo;0;L;;;;;N;;;;; +1692F;BAMUM LETTER PHASE-D NYUE;Lo;0;L;;;;;N;;;;; +16930;BAMUM LETTER PHASE-D LET;Lo;0;L;;;;;N;;;;; +16931;BAMUM LETTER PHASE-D NGGAAM;Lo;0;L;;;;;N;;;;; +16932;BAMUM LETTER PHASE-D MFIEE;Lo;0;L;;;;;N;;;;; +16933;BAMUM LETTER PHASE-D NGGWAEN;Lo;0;L;;;;;N;;;;; +16934;BAMUM LETTER PHASE-D YUOM;Lo;0;L;;;;;N;;;;; +16935;BAMUM LETTER PHASE-D PAP;Lo;0;L;;;;;N;;;;; +16936;BAMUM LETTER PHASE-D YUOP;Lo;0;L;;;;;N;;;;; +16937;BAMUM LETTER PHASE-D NDAM;Lo;0;L;;;;;N;;;;; +16938;BAMUM LETTER PHASE-D NTEUM;Lo;0;L;;;;;N;;;;; +16939;BAMUM LETTER PHASE-D SUAE;Lo;0;L;;;;;N;;;;; +1693A;BAMUM LETTER PHASE-D KUN;Lo;0;L;;;;;N;;;;; +1693B;BAMUM LETTER PHASE-D NGGEUX;Lo;0;L;;;;;N;;;;; +1693C;BAMUM LETTER PHASE-D NGKIEE;Lo;0;L;;;;;N;;;;; +1693D;BAMUM LETTER PHASE-D TUOT;Lo;0;L;;;;;N;;;;; +1693E;BAMUM LETTER PHASE-D MEUN;Lo;0;L;;;;;N;;;;; +1693F;BAMUM LETTER PHASE-D KUQ;Lo;0;L;;;;;N;;;;; +16940;BAMUM LETTER PHASE-D NSUM;Lo;0;L;;;;;N;;;;; +16941;BAMUM LETTER PHASE-D TEUN;Lo;0;L;;;;;N;;;;; +16942;BAMUM LETTER PHASE-D MAENJET;Lo;0;L;;;;;N;;;;; +16943;BAMUM LETTER PHASE-D NGGAP;Lo;0;L;;;;;N;;;;; +16944;BAMUM LETTER PHASE-D LEUM;Lo;0;L;;;;;N;;;;; +16945;BAMUM LETTER PHASE-D NGGUOM;Lo;0;L;;;;;N;;;;; +16946;BAMUM LETTER PHASE-D NSHUT;Lo;0;L;;;;;N;;;;; +16947;BAMUM LETTER PHASE-D NJUEQ;Lo;0;L;;;;;N;;;;; +16948;BAMUM LETTER PHASE-D GHEUAE;Lo;0;L;;;;;N;;;;; +16949;BAMUM LETTER PHASE-D KU;Lo;0;L;;;;;N;;;;; +1694A;BAMUM LETTER PHASE-D REN OLD;Lo;0;L;;;;;N;;;;; +1694B;BAMUM LETTER PHASE-D TAE;Lo;0;L;;;;;N;;;;; +1694C;BAMUM LETTER PHASE-D TOQ;Lo;0;L;;;;;N;;;;; +1694D;BAMUM LETTER PHASE-D NYI;Lo;0;L;;;;;N;;;;; +1694E;BAMUM LETTER PHASE-D RII;Lo;0;L;;;;;N;;;;; +1694F;BAMUM LETTER PHASE-D LEEEE;Lo;0;L;;;;;N;;;;; +16950;BAMUM LETTER PHASE-D MEEEE;Lo;0;L;;;;;N;;;;; +16951;BAMUM LETTER PHASE-D M;Lo;0;L;;;;;N;;;;; +16952;BAMUM LETTER PHASE-D SUU;Lo;0;L;;;;;N;;;;; +16953;BAMUM LETTER PHASE-D MU;Lo;0;L;;;;;N;;;;; +16954;BAMUM LETTER PHASE-D SHII;Lo;0;L;;;;;N;;;;; +16955;BAMUM LETTER PHASE-D SHEUX;Lo;0;L;;;;;N;;;;; +16956;BAMUM LETTER PHASE-D KYEE;Lo;0;L;;;;;N;;;;; +16957;BAMUM LETTER PHASE-D NU;Lo;0;L;;;;;N;;;;; +16958;BAMUM LETTER PHASE-D SHU;Lo;0;L;;;;;N;;;;; +16959;BAMUM LETTER PHASE-D NTEE;Lo;0;L;;;;;N;;;;; +1695A;BAMUM LETTER PHASE-D PEE;Lo;0;L;;;;;N;;;;; +1695B;BAMUM LETTER PHASE-D NI;Lo;0;L;;;;;N;;;;; +1695C;BAMUM LETTER PHASE-D SHOQ;Lo;0;L;;;;;N;;;;; +1695D;BAMUM LETTER PHASE-D PUQ;Lo;0;L;;;;;N;;;;; +1695E;BAMUM LETTER PHASE-D MVOP;Lo;0;L;;;;;N;;;;; +1695F;BAMUM LETTER PHASE-D LOQ;Lo;0;L;;;;;N;;;;; +16960;BAMUM LETTER PHASE-D REN MUCH;Lo;0;L;;;;;N;;;;; +16961;BAMUM LETTER PHASE-D TI;Lo;0;L;;;;;N;;;;; +16962;BAMUM LETTER PHASE-D NTUU;Lo;0;L;;;;;N;;;;; +16963;BAMUM LETTER PHASE-D MBAA SEVEN;Lo;0;L;;;;;N;;;;; +16964;BAMUM LETTER PHASE-D SAQ;Lo;0;L;;;;;N;;;;; +16965;BAMUM LETTER PHASE-D FAA;Lo;0;L;;;;;N;;;;; +16966;BAMUM LETTER PHASE-E NDAP;Lo;0;L;;;;;N;;;;; +16967;BAMUM LETTER PHASE-E TOON;Lo;0;L;;;;;N;;;;; +16968;BAMUM LETTER PHASE-E MBEUM;Lo;0;L;;;;;N;;;;; +16969;BAMUM LETTER PHASE-E LAP;Lo;0;L;;;;;N;;;;; +1696A;BAMUM LETTER PHASE-E VOM;Lo;0;L;;;;;N;;;;; +1696B;BAMUM LETTER PHASE-E LOON;Lo;0;L;;;;;N;;;;; +1696C;BAMUM LETTER PHASE-E PAA;Lo;0;L;;;;;N;;;;; +1696D;BAMUM LETTER PHASE-E SOM;Lo;0;L;;;;;N;;;;; +1696E;BAMUM LETTER PHASE-E RAQ;Lo;0;L;;;;;N;;;;; +1696F;BAMUM LETTER PHASE-E NSHUOP;Lo;0;L;;;;;N;;;;; +16970;BAMUM LETTER PHASE-E NDUN;Lo;0;L;;;;;N;;;;; +16971;BAMUM LETTER PHASE-E PUAE;Lo;0;L;;;;;N;;;;; +16972;BAMUM LETTER PHASE-E TAM;Lo;0;L;;;;;N;;;;; +16973;BAMUM LETTER PHASE-E NGKA;Lo;0;L;;;;;N;;;;; +16974;BAMUM LETTER PHASE-E KPEUX;Lo;0;L;;;;;N;;;;; +16975;BAMUM LETTER PHASE-E WUO;Lo;0;L;;;;;N;;;;; +16976;BAMUM LETTER PHASE-E SEE;Lo;0;L;;;;;N;;;;; +16977;BAMUM LETTER PHASE-E NGGEUAET;Lo;0;L;;;;;N;;;;; +16978;BAMUM LETTER PHASE-E PAAM;Lo;0;L;;;;;N;;;;; +16979;BAMUM LETTER PHASE-E TOO;Lo;0;L;;;;;N;;;;; +1697A;BAMUM LETTER PHASE-E KUOP;Lo;0;L;;;;;N;;;;; +1697B;BAMUM LETTER PHASE-E LOM;Lo;0;L;;;;;N;;;;; +1697C;BAMUM LETTER PHASE-E NSHIEE;Lo;0;L;;;;;N;;;;; +1697D;BAMUM LETTER PHASE-E NGOP;Lo;0;L;;;;;N;;;;; +1697E;BAMUM LETTER PHASE-E MAEM;Lo;0;L;;;;;N;;;;; +1697F;BAMUM LETTER PHASE-E NGKEUX;Lo;0;L;;;;;N;;;;; +16980;BAMUM LETTER PHASE-E NGOQ;Lo;0;L;;;;;N;;;;; +16981;BAMUM LETTER PHASE-E NSHUE;Lo;0;L;;;;;N;;;;; +16982;BAMUM LETTER PHASE-E RIMGBA;Lo;0;L;;;;;N;;;;; +16983;BAMUM LETTER PHASE-E NJEUX;Lo;0;L;;;;;N;;;;; +16984;BAMUM LETTER PHASE-E PEEM;Lo;0;L;;;;;N;;;;; +16985;BAMUM LETTER PHASE-E SAA;Lo;0;L;;;;;N;;;;; +16986;BAMUM LETTER PHASE-E NGGURAE;Lo;0;L;;;;;N;;;;; +16987;BAMUM LETTER PHASE-E MGBA;Lo;0;L;;;;;N;;;;; +16988;BAMUM LETTER PHASE-E GHEUX;Lo;0;L;;;;;N;;;;; +16989;BAMUM LETTER PHASE-E NGKEUAEM;Lo;0;L;;;;;N;;;;; +1698A;BAMUM LETTER PHASE-E NJAEMLI;Lo;0;L;;;;;N;;;;; +1698B;BAMUM LETTER PHASE-E MAP;Lo;0;L;;;;;N;;;;; +1698C;BAMUM LETTER PHASE-E LOOT;Lo;0;L;;;;;N;;;;; +1698D;BAMUM LETTER PHASE-E NGGEEEE;Lo;0;L;;;;;N;;;;; +1698E;BAMUM LETTER PHASE-E NDIQ;Lo;0;L;;;;;N;;;;; +1698F;BAMUM LETTER PHASE-E TAEN NTEUM;Lo;0;L;;;;;N;;;;; +16990;BAMUM LETTER PHASE-E SET;Lo;0;L;;;;;N;;;;; +16991;BAMUM LETTER PHASE-E PUM;Lo;0;L;;;;;N;;;;; +16992;BAMUM LETTER PHASE-E NDAA SOFTNESS;Lo;0;L;;;;;N;;;;; +16993;BAMUM LETTER PHASE-E NGGUAESHAE NYAM;Lo;0;L;;;;;N;;;;; +16994;BAMUM LETTER PHASE-E YIEE;Lo;0;L;;;;;N;;;;; +16995;BAMUM LETTER PHASE-E GHEUN;Lo;0;L;;;;;N;;;;; +16996;BAMUM LETTER PHASE-E TUAE;Lo;0;L;;;;;N;;;;; +16997;BAMUM LETTER PHASE-E YEUAE;Lo;0;L;;;;;N;;;;; +16998;BAMUM LETTER PHASE-E PO;Lo;0;L;;;;;N;;;;; +16999;BAMUM LETTER PHASE-E TUMAE;Lo;0;L;;;;;N;;;;; +1699A;BAMUM LETTER PHASE-E KEUAE;Lo;0;L;;;;;N;;;;; +1699B;BAMUM LETTER PHASE-E SUAEN;Lo;0;L;;;;;N;;;;; +1699C;BAMUM LETTER PHASE-E TEUAEQ;Lo;0;L;;;;;N;;;;; +1699D;BAMUM LETTER PHASE-E VEUAE;Lo;0;L;;;;;N;;;;; +1699E;BAMUM LETTER PHASE-E WEUX;Lo;0;L;;;;;N;;;;; +1699F;BAMUM LETTER PHASE-E LAAM;Lo;0;L;;;;;N;;;;; +169A0;BAMUM LETTER PHASE-E PU;Lo;0;L;;;;;N;;;;; +169A1;BAMUM LETTER PHASE-E TAAQ;Lo;0;L;;;;;N;;;;; +169A2;BAMUM LETTER PHASE-E GHAAMAE;Lo;0;L;;;;;N;;;;; +169A3;BAMUM LETTER PHASE-E NGEUREUT;Lo;0;L;;;;;N;;;;; +169A4;BAMUM LETTER PHASE-E SHEUAEQ;Lo;0;L;;;;;N;;;;; +169A5;BAMUM LETTER PHASE-E MGBEN;Lo;0;L;;;;;N;;;;; +169A6;BAMUM LETTER PHASE-E MBEE;Lo;0;L;;;;;N;;;;; +169A7;BAMUM LETTER PHASE-E NZAQ;Lo;0;L;;;;;N;;;;; +169A8;BAMUM LETTER PHASE-E NKOM;Lo;0;L;;;;;N;;;;; +169A9;BAMUM LETTER PHASE-E GBET;Lo;0;L;;;;;N;;;;; +169AA;BAMUM LETTER PHASE-E TUM;Lo;0;L;;;;;N;;;;; +169AB;BAMUM LETTER PHASE-E KUET;Lo;0;L;;;;;N;;;;; +169AC;BAMUM LETTER PHASE-E YAP;Lo;0;L;;;;;N;;;;; +169AD;BAMUM LETTER PHASE-E NYI CLEAVER;Lo;0;L;;;;;N;;;;; +169AE;BAMUM LETTER PHASE-E YIT;Lo;0;L;;;;;N;;;;; +169AF;BAMUM LETTER PHASE-E MFEUQ;Lo;0;L;;;;;N;;;;; +169B0;BAMUM LETTER PHASE-E NDIAQ;Lo;0;L;;;;;N;;;;; +169B1;BAMUM LETTER PHASE-E PIEEQ;Lo;0;L;;;;;N;;;;; +169B2;BAMUM LETTER PHASE-E YUEQ;Lo;0;L;;;;;N;;;;; +169B3;BAMUM LETTER PHASE-E LEUAEM;Lo;0;L;;;;;N;;;;; +169B4;BAMUM LETTER PHASE-E FUE;Lo;0;L;;;;;N;;;;; +169B5;BAMUM LETTER PHASE-E GBEUX;Lo;0;L;;;;;N;;;;; +169B6;BAMUM LETTER PHASE-E NGKUP;Lo;0;L;;;;;N;;;;; +169B7;BAMUM LETTER PHASE-E KET;Lo;0;L;;;;;N;;;;; +169B8;BAMUM LETTER PHASE-E MAE;Lo;0;L;;;;;N;;;;; +169B9;BAMUM LETTER PHASE-E NGKAAMI;Lo;0;L;;;;;N;;;;; +169BA;BAMUM LETTER PHASE-E GHET;Lo;0;L;;;;;N;;;;; +169BB;BAMUM LETTER PHASE-E FA;Lo;0;L;;;;;N;;;;; +169BC;BAMUM LETTER PHASE-E NTUM;Lo;0;L;;;;;N;;;;; +169BD;BAMUM LETTER PHASE-E PEUT;Lo;0;L;;;;;N;;;;; +169BE;BAMUM LETTER PHASE-E YEUM;Lo;0;L;;;;;N;;;;; +169BF;BAMUM LETTER PHASE-E NGGEUAE;Lo;0;L;;;;;N;;;;; +169C0;BAMUM LETTER PHASE-E NYI BETWEEN;Lo;0;L;;;;;N;;;;; +169C1;BAMUM LETTER PHASE-E NZUQ;Lo;0;L;;;;;N;;;;; +169C2;BAMUM LETTER PHASE-E POON;Lo;0;L;;;;;N;;;;; +169C3;BAMUM LETTER PHASE-E MIEE;Lo;0;L;;;;;N;;;;; +169C4;BAMUM LETTER PHASE-E FUET;Lo;0;L;;;;;N;;;;; +169C5;BAMUM LETTER PHASE-E NAE;Lo;0;L;;;;;N;;;;; +169C6;BAMUM LETTER PHASE-E MUAE;Lo;0;L;;;;;N;;;;; +169C7;BAMUM LETTER PHASE-E GHEUAE;Lo;0;L;;;;;N;;;;; +169C8;BAMUM LETTER PHASE-E FU I;Lo;0;L;;;;;N;;;;; +169C9;BAMUM LETTER PHASE-E MVI;Lo;0;L;;;;;N;;;;; +169CA;BAMUM LETTER PHASE-E PUAQ;Lo;0;L;;;;;N;;;;; +169CB;BAMUM LETTER PHASE-E NGKUM;Lo;0;L;;;;;N;;;;; +169CC;BAMUM LETTER PHASE-E KUT;Lo;0;L;;;;;N;;;;; +169CD;BAMUM LETTER PHASE-E PIET;Lo;0;L;;;;;N;;;;; +169CE;BAMUM LETTER PHASE-E NTAP;Lo;0;L;;;;;N;;;;; +169CF;BAMUM LETTER PHASE-E YEUAET;Lo;0;L;;;;;N;;;;; +169D0;BAMUM LETTER PHASE-E NGGUP;Lo;0;L;;;;;N;;;;; +169D1;BAMUM LETTER PHASE-E PA PEOPLE;Lo;0;L;;;;;N;;;;; +169D2;BAMUM LETTER PHASE-E FU CALL;Lo;0;L;;;;;N;;;;; +169D3;BAMUM LETTER PHASE-E FOM;Lo;0;L;;;;;N;;;;; +169D4;BAMUM LETTER PHASE-E NJEE;Lo;0;L;;;;;N;;;;; +169D5;BAMUM LETTER PHASE-E A;Lo;0;L;;;;;N;;;;; +169D6;BAMUM LETTER PHASE-E TOQ;Lo;0;L;;;;;N;;;;; +169D7;BAMUM LETTER PHASE-E O;Lo;0;L;;;;;N;;;;; +169D8;BAMUM LETTER PHASE-E I;Lo;0;L;;;;;N;;;;; +169D9;BAMUM LETTER PHASE-E LAQ;Lo;0;L;;;;;N;;;;; +169DA;BAMUM LETTER PHASE-E PA PLURAL;Lo;0;L;;;;;N;;;;; +169DB;BAMUM LETTER PHASE-E TAA;Lo;0;L;;;;;N;;;;; +169DC;BAMUM LETTER PHASE-E TAQ;Lo;0;L;;;;;N;;;;; +169DD;BAMUM LETTER PHASE-E NDAA MY HOUSE;Lo;0;L;;;;;N;;;;; +169DE;BAMUM LETTER PHASE-E SHIQ;Lo;0;L;;;;;N;;;;; +169DF;BAMUM LETTER PHASE-E YEUX;Lo;0;L;;;;;N;;;;; +169E0;BAMUM LETTER PHASE-E NGUAE;Lo;0;L;;;;;N;;;;; +169E1;BAMUM LETTER PHASE-E YUAEN;Lo;0;L;;;;;N;;;;; +169E2;BAMUM LETTER PHASE-E YOQ SWIMMING;Lo;0;L;;;;;N;;;;; +169E3;BAMUM LETTER PHASE-E YOQ COVER;Lo;0;L;;;;;N;;;;; +169E4;BAMUM LETTER PHASE-E YUQ;Lo;0;L;;;;;N;;;;; +169E5;BAMUM LETTER PHASE-E YUN;Lo;0;L;;;;;N;;;;; +169E6;BAMUM LETTER PHASE-E KEUX;Lo;0;L;;;;;N;;;;; +169E7;BAMUM LETTER PHASE-E PEUX;Lo;0;L;;;;;N;;;;; +169E8;BAMUM LETTER PHASE-E NJEE EPOCH;Lo;0;L;;;;;N;;;;; +169E9;BAMUM LETTER PHASE-E PUE;Lo;0;L;;;;;N;;;;; +169EA;BAMUM LETTER PHASE-E WUE;Lo;0;L;;;;;N;;;;; +169EB;BAMUM LETTER PHASE-E FEE;Lo;0;L;;;;;N;;;;; +169EC;BAMUM LETTER PHASE-E VEE;Lo;0;L;;;;;N;;;;; +169ED;BAMUM LETTER PHASE-E LU;Lo;0;L;;;;;N;;;;; +169EE;BAMUM LETTER PHASE-E MI;Lo;0;L;;;;;N;;;;; +169EF;BAMUM LETTER PHASE-E REUX;Lo;0;L;;;;;N;;;;; +169F0;BAMUM LETTER PHASE-E RAE;Lo;0;L;;;;;N;;;;; +169F1;BAMUM LETTER PHASE-E NGUAET;Lo;0;L;;;;;N;;;;; +169F2;BAMUM LETTER PHASE-E NGA;Lo;0;L;;;;;N;;;;; +169F3;BAMUM LETTER PHASE-E SHO;Lo;0;L;;;;;N;;;;; +169F4;BAMUM LETTER PHASE-E SHOQ;Lo;0;L;;;;;N;;;;; +169F5;BAMUM LETTER PHASE-E FU REMEDY;Lo;0;L;;;;;N;;;;; +169F6;BAMUM LETTER PHASE-E NA;Lo;0;L;;;;;N;;;;; +169F7;BAMUM LETTER PHASE-E PI;Lo;0;L;;;;;N;;;;; +169F8;BAMUM LETTER PHASE-E LOQ;Lo;0;L;;;;;N;;;;; +169F9;BAMUM LETTER PHASE-E KO;Lo;0;L;;;;;N;;;;; +169FA;BAMUM LETTER PHASE-E MEN;Lo;0;L;;;;;N;;;;; +169FB;BAMUM LETTER PHASE-E MA;Lo;0;L;;;;;N;;;;; +169FC;BAMUM LETTER PHASE-E MAQ;Lo;0;L;;;;;N;;;;; +169FD;BAMUM LETTER PHASE-E TEU;Lo;0;L;;;;;N;;;;; +169FE;BAMUM LETTER PHASE-E KI;Lo;0;L;;;;;N;;;;; +169FF;BAMUM LETTER PHASE-E MON;Lo;0;L;;;;;N;;;;; +16A00;BAMUM LETTER PHASE-E TEN;Lo;0;L;;;;;N;;;;; +16A01;BAMUM LETTER PHASE-E FAQ;Lo;0;L;;;;;N;;;;; +16A02;BAMUM LETTER PHASE-E GHOM;Lo;0;L;;;;;N;;;;; +16A03;BAMUM LETTER PHASE-F KA;Lo;0;L;;;;;N;;;;; +16A04;BAMUM LETTER PHASE-F U;Lo;0;L;;;;;N;;;;; +16A05;BAMUM LETTER PHASE-F KU;Lo;0;L;;;;;N;;;;; +16A06;BAMUM LETTER PHASE-F EE;Lo;0;L;;;;;N;;;;; +16A07;BAMUM LETTER PHASE-F REE;Lo;0;L;;;;;N;;;;; +16A08;BAMUM LETTER PHASE-F TAE;Lo;0;L;;;;;N;;;;; +16A09;BAMUM LETTER PHASE-F NYI;Lo;0;L;;;;;N;;;;; +16A0A;BAMUM LETTER PHASE-F LA;Lo;0;L;;;;;N;;;;; +16A0B;BAMUM LETTER PHASE-F RII;Lo;0;L;;;;;N;;;;; +16A0C;BAMUM LETTER PHASE-F RIEE;Lo;0;L;;;;;N;;;;; +16A0D;BAMUM LETTER PHASE-F MEEEE;Lo;0;L;;;;;N;;;;; +16A0E;BAMUM LETTER PHASE-F TAA;Lo;0;L;;;;;N;;;;; +16A0F;BAMUM LETTER PHASE-F NDAA;Lo;0;L;;;;;N;;;;; +16A10;BAMUM LETTER PHASE-F NJAEM;Lo;0;L;;;;;N;;;;; +16A11;BAMUM LETTER PHASE-F M;Lo;0;L;;;;;N;;;;; +16A12;BAMUM LETTER PHASE-F SUU;Lo;0;L;;;;;N;;;;; +16A13;BAMUM LETTER PHASE-F SHII;Lo;0;L;;;;;N;;;;; +16A14;BAMUM LETTER PHASE-F SI;Lo;0;L;;;;;N;;;;; +16A15;BAMUM LETTER PHASE-F SEUX;Lo;0;L;;;;;N;;;;; +16A16;BAMUM LETTER PHASE-F KYEE;Lo;0;L;;;;;N;;;;; +16A17;BAMUM LETTER PHASE-F KET;Lo;0;L;;;;;N;;;;; +16A18;BAMUM LETTER PHASE-F NUAE;Lo;0;L;;;;;N;;;;; +16A19;BAMUM LETTER PHASE-F NU;Lo;0;L;;;;;N;;;;; +16A1A;BAMUM LETTER PHASE-F NJUAE;Lo;0;L;;;;;N;;;;; +16A1B;BAMUM LETTER PHASE-F YOQ;Lo;0;L;;;;;N;;;;; +16A1C;BAMUM LETTER PHASE-F SHU;Lo;0;L;;;;;N;;;;; +16A1D;BAMUM LETTER PHASE-F YA;Lo;0;L;;;;;N;;;;; +16A1E;BAMUM LETTER PHASE-F NSHA;Lo;0;L;;;;;N;;;;; +16A1F;BAMUM LETTER PHASE-F PEUX;Lo;0;L;;;;;N;;;;; +16A20;BAMUM LETTER PHASE-F NTEE;Lo;0;L;;;;;N;;;;; +16A21;BAMUM LETTER PHASE-F WUE;Lo;0;L;;;;;N;;;;; +16A22;BAMUM LETTER PHASE-F PEE;Lo;0;L;;;;;N;;;;; +16A23;BAMUM LETTER PHASE-F RU;Lo;0;L;;;;;N;;;;; +16A24;BAMUM LETTER PHASE-F NI;Lo;0;L;;;;;N;;;;; +16A25;BAMUM LETTER PHASE-F REUX;Lo;0;L;;;;;N;;;;; +16A26;BAMUM LETTER PHASE-F KEN;Lo;0;L;;;;;N;;;;; +16A27;BAMUM LETTER PHASE-F NGKWAEN;Lo;0;L;;;;;N;;;;; +16A28;BAMUM LETTER PHASE-F NGGA;Lo;0;L;;;;;N;;;;; +16A29;BAMUM LETTER PHASE-F SHO;Lo;0;L;;;;;N;;;;; +16A2A;BAMUM LETTER PHASE-F PUAE;Lo;0;L;;;;;N;;;;; +16A2B;BAMUM LETTER PHASE-F FOM;Lo;0;L;;;;;N;;;;; +16A2C;BAMUM LETTER PHASE-F WA;Lo;0;L;;;;;N;;;;; +16A2D;BAMUM LETTER PHASE-F LI;Lo;0;L;;;;;N;;;;; +16A2E;BAMUM LETTER PHASE-F LOQ;Lo;0;L;;;;;N;;;;; +16A2F;BAMUM LETTER PHASE-F KO;Lo;0;L;;;;;N;;;;; +16A30;BAMUM LETTER PHASE-F MBEN;Lo;0;L;;;;;N;;;;; +16A31;BAMUM LETTER PHASE-F REN;Lo;0;L;;;;;N;;;;; +16A32;BAMUM LETTER PHASE-F MA;Lo;0;L;;;;;N;;;;; +16A33;BAMUM LETTER PHASE-F MO;Lo;0;L;;;;;N;;;;; +16A34;BAMUM LETTER PHASE-F MBAA;Lo;0;L;;;;;N;;;;; +16A35;BAMUM LETTER PHASE-F TET;Lo;0;L;;;;;N;;;;; +16A36;BAMUM LETTER PHASE-F KPA;Lo;0;L;;;;;N;;;;; +16A37;BAMUM LETTER PHASE-F SAMBA;Lo;0;L;;;;;N;;;;; +16A38;BAMUM LETTER PHASE-F VUEQ;Lo;0;L;;;;;N;;;;; +16A40;MRO LETTER TA;Lo;0;L;;;;;N;;;;; +16A41;MRO LETTER NGI;Lo;0;L;;;;;N;;;;; +16A42;MRO LETTER YO;Lo;0;L;;;;;N;;;;; +16A43;MRO LETTER MIM;Lo;0;L;;;;;N;;;;; +16A44;MRO LETTER BA;Lo;0;L;;;;;N;;;;; +16A45;MRO LETTER DA;Lo;0;L;;;;;N;;;;; +16A46;MRO LETTER A;Lo;0;L;;;;;N;;;;; +16A47;MRO LETTER PHI;Lo;0;L;;;;;N;;;;; +16A48;MRO LETTER KHAI;Lo;0;L;;;;;N;;;;; +16A49;MRO LETTER HAO;Lo;0;L;;;;;N;;;;; +16A4A;MRO LETTER DAI;Lo;0;L;;;;;N;;;;; +16A4B;MRO LETTER CHU;Lo;0;L;;;;;N;;;;; +16A4C;MRO LETTER KEAAE;Lo;0;L;;;;;N;;;;; +16A4D;MRO LETTER OL;Lo;0;L;;;;;N;;;;; +16A4E;MRO LETTER MAEM;Lo;0;L;;;;;N;;;;; +16A4F;MRO LETTER NIN;Lo;0;L;;;;;N;;;;; +16A50;MRO LETTER PA;Lo;0;L;;;;;N;;;;; +16A51;MRO LETTER OO;Lo;0;L;;;;;N;;;;; +16A52;MRO LETTER O;Lo;0;L;;;;;N;;;;; +16A53;MRO LETTER RO;Lo;0;L;;;;;N;;;;; +16A54;MRO LETTER SHI;Lo;0;L;;;;;N;;;;; +16A55;MRO LETTER THEA;Lo;0;L;;;;;N;;;;; +16A56;MRO LETTER EA;Lo;0;L;;;;;N;;;;; +16A57;MRO LETTER WA;Lo;0;L;;;;;N;;;;; +16A58;MRO LETTER E;Lo;0;L;;;;;N;;;;; +16A59;MRO LETTER KO;Lo;0;L;;;;;N;;;;; +16A5A;MRO LETTER LAN;Lo;0;L;;;;;N;;;;; +16A5B;MRO LETTER LA;Lo;0;L;;;;;N;;;;; +16A5C;MRO LETTER HAI;Lo;0;L;;;;;N;;;;; +16A5D;MRO LETTER RI;Lo;0;L;;;;;N;;;;; +16A5E;MRO LETTER TEK;Lo;0;L;;;;;N;;;;; +16A60;MRO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +16A61;MRO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +16A62;MRO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +16A63;MRO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +16A64;MRO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +16A65;MRO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +16A66;MRO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +16A67;MRO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +16A68;MRO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +16A69;MRO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +16A6E;MRO DANDA;Po;0;L;;;;;N;;;;; +16A6F;MRO DOUBLE DANDA;Po;0;L;;;;;N;;;;; +16AD0;BASSA VAH LETTER ENNI;Lo;0;L;;;;;N;;;;; +16AD1;BASSA VAH LETTER KA;Lo;0;L;;;;;N;;;;; +16AD2;BASSA VAH LETTER SE;Lo;0;L;;;;;N;;;;; +16AD3;BASSA VAH LETTER FA;Lo;0;L;;;;;N;;;;; +16AD4;BASSA VAH LETTER MBE;Lo;0;L;;;;;N;;;;; +16AD5;BASSA VAH LETTER YIE;Lo;0;L;;;;;N;;;;; +16AD6;BASSA VAH LETTER GAH;Lo;0;L;;;;;N;;;;; +16AD7;BASSA VAH LETTER DHII;Lo;0;L;;;;;N;;;;; +16AD8;BASSA VAH LETTER KPAH;Lo;0;L;;;;;N;;;;; +16AD9;BASSA VAH LETTER JO;Lo;0;L;;;;;N;;;;; +16ADA;BASSA VAH LETTER HWAH;Lo;0;L;;;;;N;;;;; +16ADB;BASSA VAH LETTER WA;Lo;0;L;;;;;N;;;;; +16ADC;BASSA VAH LETTER ZO;Lo;0;L;;;;;N;;;;; +16ADD;BASSA VAH LETTER GBU;Lo;0;L;;;;;N;;;;; +16ADE;BASSA VAH LETTER DO;Lo;0;L;;;;;N;;;;; +16ADF;BASSA VAH LETTER CE;Lo;0;L;;;;;N;;;;; +16AE0;BASSA VAH LETTER UWU;Lo;0;L;;;;;N;;;;; +16AE1;BASSA VAH LETTER TO;Lo;0;L;;;;;N;;;;; +16AE2;BASSA VAH LETTER BA;Lo;0;L;;;;;N;;;;; +16AE3;BASSA VAH LETTER VU;Lo;0;L;;;;;N;;;;; +16AE4;BASSA VAH LETTER YEIN;Lo;0;L;;;;;N;;;;; +16AE5;BASSA VAH LETTER PA;Lo;0;L;;;;;N;;;;; +16AE6;BASSA VAH LETTER WADDA;Lo;0;L;;;;;N;;;;; +16AE7;BASSA VAH LETTER A;Lo;0;L;;;;;N;;;;; +16AE8;BASSA VAH LETTER O;Lo;0;L;;;;;N;;;;; +16AE9;BASSA VAH LETTER OO;Lo;0;L;;;;;N;;;;; +16AEA;BASSA VAH LETTER U;Lo;0;L;;;;;N;;;;; +16AEB;BASSA VAH LETTER EE;Lo;0;L;;;;;N;;;;; +16AEC;BASSA VAH LETTER E;Lo;0;L;;;;;N;;;;; +16AED;BASSA VAH LETTER I;Lo;0;L;;;;;N;;;;; +16AF0;BASSA VAH COMBINING HIGH TONE;Mn;1;NSM;;;;;N;;;;; +16AF1;BASSA VAH COMBINING LOW TONE;Mn;1;NSM;;;;;N;;;;; +16AF2;BASSA VAH COMBINING MID TONE;Mn;1;NSM;;;;;N;;;;; +16AF3;BASSA VAH COMBINING LOW-MID TONE;Mn;1;NSM;;;;;N;;;;; +16AF4;BASSA VAH COMBINING HIGH-LOW TONE;Mn;1;NSM;;;;;N;;;;; +16AF5;BASSA VAH FULL STOP;Po;0;L;;;;;N;;;;; +16B00;PAHAWH HMONG VOWEL KEEB;Lo;0;L;;;;;N;;;;; +16B01;PAHAWH HMONG VOWEL KEEV;Lo;0;L;;;;;N;;;;; +16B02;PAHAWH HMONG VOWEL KIB;Lo;0;L;;;;;N;;;;; +16B03;PAHAWH HMONG VOWEL KIV;Lo;0;L;;;;;N;;;;; +16B04;PAHAWH HMONG VOWEL KAUB;Lo;0;L;;;;;N;;;;; +16B05;PAHAWH HMONG VOWEL KAUV;Lo;0;L;;;;;N;;;;; +16B06;PAHAWH HMONG VOWEL KUB;Lo;0;L;;;;;N;;;;; +16B07;PAHAWH HMONG VOWEL KUV;Lo;0;L;;;;;N;;;;; +16B08;PAHAWH HMONG VOWEL KEB;Lo;0;L;;;;;N;;;;; +16B09;PAHAWH HMONG VOWEL KEV;Lo;0;L;;;;;N;;;;; +16B0A;PAHAWH HMONG VOWEL KAIB;Lo;0;L;;;;;N;;;;; +16B0B;PAHAWH HMONG VOWEL KAIV;Lo;0;L;;;;;N;;;;; +16B0C;PAHAWH HMONG VOWEL KOOB;Lo;0;L;;;;;N;;;;; +16B0D;PAHAWH HMONG VOWEL KOOV;Lo;0;L;;;;;N;;;;; +16B0E;PAHAWH HMONG VOWEL KAWB;Lo;0;L;;;;;N;;;;; +16B0F;PAHAWH HMONG VOWEL KAWV;Lo;0;L;;;;;N;;;;; +16B10;PAHAWH HMONG VOWEL KUAB;Lo;0;L;;;;;N;;;;; +16B11;PAHAWH HMONG VOWEL KUAV;Lo;0;L;;;;;N;;;;; +16B12;PAHAWH HMONG VOWEL KOB;Lo;0;L;;;;;N;;;;; +16B13;PAHAWH HMONG VOWEL KOV;Lo;0;L;;;;;N;;;;; +16B14;PAHAWH HMONG VOWEL KIAB;Lo;0;L;;;;;N;;;;; +16B15;PAHAWH HMONG VOWEL KIAV;Lo;0;L;;;;;N;;;;; +16B16;PAHAWH HMONG VOWEL KAB;Lo;0;L;;;;;N;;;;; +16B17;PAHAWH HMONG VOWEL KAV;Lo;0;L;;;;;N;;;;; +16B18;PAHAWH HMONG VOWEL KWB;Lo;0;L;;;;;N;;;;; +16B19;PAHAWH HMONG VOWEL KWV;Lo;0;L;;;;;N;;;;; +16B1A;PAHAWH HMONG VOWEL KAAB;Lo;0;L;;;;;N;;;;; +16B1B;PAHAWH HMONG VOWEL KAAV;Lo;0;L;;;;;N;;;;; +16B1C;PAHAWH HMONG CONSONANT VAU;Lo;0;L;;;;;N;;;;; +16B1D;PAHAWH HMONG CONSONANT NTSAU;Lo;0;L;;;;;N;;;;; +16B1E;PAHAWH HMONG CONSONANT LAU;Lo;0;L;;;;;N;;;;; +16B1F;PAHAWH HMONG CONSONANT HAU;Lo;0;L;;;;;N;;;;; +16B20;PAHAWH HMONG CONSONANT NLAU;Lo;0;L;;;;;N;;;;; +16B21;PAHAWH HMONG CONSONANT RAU;Lo;0;L;;;;;N;;;;; +16B22;PAHAWH HMONG CONSONANT NKAU;Lo;0;L;;;;;N;;;;; +16B23;PAHAWH HMONG CONSONANT QHAU;Lo;0;L;;;;;N;;;;; +16B24;PAHAWH HMONG CONSONANT YAU;Lo;0;L;;;;;N;;;;; +16B25;PAHAWH HMONG CONSONANT HLAU;Lo;0;L;;;;;N;;;;; +16B26;PAHAWH HMONG CONSONANT MAU;Lo;0;L;;;;;N;;;;; +16B27;PAHAWH HMONG CONSONANT CHAU;Lo;0;L;;;;;N;;;;; +16B28;PAHAWH HMONG CONSONANT NCHAU;Lo;0;L;;;;;N;;;;; +16B29;PAHAWH HMONG CONSONANT HNAU;Lo;0;L;;;;;N;;;;; +16B2A;PAHAWH HMONG CONSONANT PLHAU;Lo;0;L;;;;;N;;;;; +16B2B;PAHAWH HMONG CONSONANT NTHAU;Lo;0;L;;;;;N;;;;; +16B2C;PAHAWH HMONG CONSONANT NAU;Lo;0;L;;;;;N;;;;; +16B2D;PAHAWH HMONG CONSONANT AU;Lo;0;L;;;;;N;;;;; +16B2E;PAHAWH HMONG CONSONANT XAU;Lo;0;L;;;;;N;;;;; +16B2F;PAHAWH HMONG CONSONANT CAU;Lo;0;L;;;;;N;;;;; +16B30;PAHAWH HMONG MARK CIM TUB;Mn;230;NSM;;;;;N;;;;; +16B31;PAHAWH HMONG MARK CIM SO;Mn;230;NSM;;;;;N;;;;; +16B32;PAHAWH HMONG MARK CIM KES;Mn;230;NSM;;;;;N;;;;; +16B33;PAHAWH HMONG MARK CIM KHAV;Mn;230;NSM;;;;;N;;;;; +16B34;PAHAWH HMONG MARK CIM SUAM;Mn;230;NSM;;;;;N;;;;; +16B35;PAHAWH HMONG MARK CIM HOM;Mn;230;NSM;;;;;N;;;;; +16B36;PAHAWH HMONG MARK CIM TAUM;Mn;230;NSM;;;;;N;;;;; +16B37;PAHAWH HMONG SIGN VOS THOM;Po;0;L;;;;;N;;;;; +16B38;PAHAWH HMONG SIGN VOS TSHAB CEEB;Po;0;L;;;;;N;;;;; +16B39;PAHAWH HMONG SIGN CIM CHEEM;Po;0;L;;;;;N;;;;; +16B3A;PAHAWH HMONG SIGN VOS THIAB;Po;0;L;;;;;N;;;;; +16B3B;PAHAWH HMONG SIGN VOS FEEM;Po;0;L;;;;;N;;;;; +16B3C;PAHAWH HMONG SIGN XYEEM NTXIV;So;0;L;;;;;N;;;;; +16B3D;PAHAWH HMONG SIGN XYEEM RHO;So;0;L;;;;;N;;;;; +16B3E;PAHAWH HMONG SIGN XYEEM TOV;So;0;L;;;;;N;;;;; +16B3F;PAHAWH HMONG SIGN XYEEM FAIB;So;0;L;;;;;N;;;;; +16B40;PAHAWH HMONG SIGN VOS SEEV;Lm;0;L;;;;;N;;;;; +16B41;PAHAWH HMONG SIGN MEEJ SUAB;Lm;0;L;;;;;N;;;;; +16B42;PAHAWH HMONG SIGN VOS NRUA;Lm;0;L;;;;;N;;;;; +16B43;PAHAWH HMONG SIGN IB YAM;Lm;0;L;;;;;N;;;;; +16B44;PAHAWH HMONG SIGN XAUS;Po;0;L;;;;;N;;;;; +16B45;PAHAWH HMONG SIGN CIM TSOV ROG;So;0;L;;;;;N;;;;; +16B50;PAHAWH HMONG DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +16B51;PAHAWH HMONG DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +16B52;PAHAWH HMONG DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +16B53;PAHAWH HMONG DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +16B54;PAHAWH HMONG DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +16B55;PAHAWH HMONG DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +16B56;PAHAWH HMONG DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +16B57;PAHAWH HMONG DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +16B58;PAHAWH HMONG DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +16B59;PAHAWH HMONG DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +16B5B;PAHAWH HMONG NUMBER TENS;No;0;L;;;;10;N;;;;; +16B5C;PAHAWH HMONG NUMBER HUNDREDS;No;0;L;;;;100;N;;;;; +16B5D;PAHAWH HMONG NUMBER TEN THOUSANDS;No;0;L;;;;10000;N;;;;; +16B5E;PAHAWH HMONG NUMBER MILLIONS;No;0;L;;;;1000000;N;;;;; +16B5F;PAHAWH HMONG NUMBER HUNDRED MILLIONS;No;0;L;;;;100000000;N;;;;; +16B60;PAHAWH HMONG NUMBER TEN BILLIONS;No;0;L;;;;10000000000;N;;;;; +16B61;PAHAWH HMONG NUMBER TRILLIONS;No;0;L;;;;1000000000000;N;;;;; +16B63;PAHAWH HMONG SIGN VOS LUB;Lo;0;L;;;;;N;;;;; +16B64;PAHAWH HMONG SIGN XYOO;Lo;0;L;;;;;N;;;;; +16B65;PAHAWH HMONG SIGN HLI;Lo;0;L;;;;;N;;;;; +16B66;PAHAWH HMONG SIGN THIRD-STAGE HLI;Lo;0;L;;;;;N;;;;; +16B67;PAHAWH HMONG SIGN ZWJ THAJ;Lo;0;L;;;;;N;;;;; +16B68;PAHAWH HMONG SIGN HNUB;Lo;0;L;;;;;N;;;;; +16B69;PAHAWH HMONG SIGN NQIG;Lo;0;L;;;;;N;;;;; +16B6A;PAHAWH HMONG SIGN XIAB;Lo;0;L;;;;;N;;;;; +16B6B;PAHAWH HMONG SIGN NTUJ;Lo;0;L;;;;;N;;;;; +16B6C;PAHAWH HMONG SIGN AV;Lo;0;L;;;;;N;;;;; +16B6D;PAHAWH HMONG SIGN TXHEEJ CEEV;Lo;0;L;;;;;N;;;;; +16B6E;PAHAWH HMONG SIGN MEEJ TSEEB;Lo;0;L;;;;;N;;;;; +16B6F;PAHAWH HMONG SIGN TAU;Lo;0;L;;;;;N;;;;; +16B70;PAHAWH HMONG SIGN LOS;Lo;0;L;;;;;N;;;;; +16B71;PAHAWH HMONG SIGN MUS;Lo;0;L;;;;;N;;;;; +16B72;PAHAWH HMONG SIGN CIM HAIS LUS NTOG NTOG;Lo;0;L;;;;;N;;;;; +16B73;PAHAWH HMONG SIGN CIM CUAM TSHOOJ;Lo;0;L;;;;;N;;;;; +16B74;PAHAWH HMONG SIGN CIM TXWV;Lo;0;L;;;;;N;;;;; +16B75;PAHAWH HMONG SIGN CIM TXWV CHWV;Lo;0;L;;;;;N;;;;; +16B76;PAHAWH HMONG SIGN CIM PUB DAWB;Lo;0;L;;;;;N;;;;; +16B77;PAHAWH HMONG SIGN CIM NRES TOS;Lo;0;L;;;;;N;;;;; +16B7D;PAHAWH HMONG CLAN SIGN TSHEEJ;Lo;0;L;;;;;N;;;;; +16B7E;PAHAWH HMONG CLAN SIGN YEEG;Lo;0;L;;;;;N;;;;; +16B7F;PAHAWH HMONG CLAN SIGN LIS;Lo;0;L;;;;;N;;;;; +16B80;PAHAWH HMONG CLAN SIGN LAUJ;Lo;0;L;;;;;N;;;;; +16B81;PAHAWH HMONG CLAN SIGN XYOOJ;Lo;0;L;;;;;N;;;;; +16B82;PAHAWH HMONG CLAN SIGN KOO;Lo;0;L;;;;;N;;;;; +16B83;PAHAWH HMONG CLAN SIGN HAWJ;Lo;0;L;;;;;N;;;;; +16B84;PAHAWH HMONG CLAN SIGN MUAS;Lo;0;L;;;;;N;;;;; +16B85;PAHAWH HMONG CLAN SIGN THOJ;Lo;0;L;;;;;N;;;;; +16B86;PAHAWH HMONG CLAN SIGN TSAB;Lo;0;L;;;;;N;;;;; +16B87;PAHAWH HMONG CLAN SIGN PHAB;Lo;0;L;;;;;N;;;;; +16B88;PAHAWH HMONG CLAN SIGN KHAB;Lo;0;L;;;;;N;;;;; +16B89;PAHAWH HMONG CLAN SIGN HAM;Lo;0;L;;;;;N;;;;; +16B8A;PAHAWH HMONG CLAN SIGN VAJ;Lo;0;L;;;;;N;;;;; +16B8B;PAHAWH HMONG CLAN SIGN FAJ;Lo;0;L;;;;;N;;;;; +16B8C;PAHAWH HMONG CLAN SIGN YAJ;Lo;0;L;;;;;N;;;;; +16B8D;PAHAWH HMONG CLAN SIGN TSWB;Lo;0;L;;;;;N;;;;; +16B8E;PAHAWH HMONG CLAN SIGN KWM;Lo;0;L;;;;;N;;;;; +16B8F;PAHAWH HMONG CLAN SIGN VWJ;Lo;0;L;;;;;N;;;;; +16F00;MIAO LETTER PA;Lo;0;L;;;;;N;;;;; +16F01;MIAO LETTER BA;Lo;0;L;;;;;N;;;;; +16F02;MIAO LETTER YI PA;Lo;0;L;;;;;N;;;;; +16F03;MIAO LETTER PLA;Lo;0;L;;;;;N;;;;; +16F04;MIAO LETTER MA;Lo;0;L;;;;;N;;;;; +16F05;MIAO LETTER MHA;Lo;0;L;;;;;N;;;;; +16F06;MIAO LETTER ARCHAIC MA;Lo;0;L;;;;;N;;;;; +16F07;MIAO LETTER FA;Lo;0;L;;;;;N;;;;; +16F08;MIAO LETTER VA;Lo;0;L;;;;;N;;;;; +16F09;MIAO LETTER VFA;Lo;0;L;;;;;N;;;;; +16F0A;MIAO LETTER TA;Lo;0;L;;;;;N;;;;; +16F0B;MIAO LETTER DA;Lo;0;L;;;;;N;;;;; +16F0C;MIAO LETTER YI TTA;Lo;0;L;;;;;N;;;;; +16F0D;MIAO LETTER YI TA;Lo;0;L;;;;;N;;;;; +16F0E;MIAO LETTER TTA;Lo;0;L;;;;;N;;;;; +16F0F;MIAO LETTER DDA;Lo;0;L;;;;;N;;;;; +16F10;MIAO LETTER NA;Lo;0;L;;;;;N;;;;; +16F11;MIAO LETTER NHA;Lo;0;L;;;;;N;;;;; +16F12;MIAO LETTER YI NNA;Lo;0;L;;;;;N;;;;; +16F13;MIAO LETTER ARCHAIC NA;Lo;0;L;;;;;N;;;;; +16F14;MIAO LETTER NNA;Lo;0;L;;;;;N;;;;; +16F15;MIAO LETTER NNHA;Lo;0;L;;;;;N;;;;; +16F16;MIAO LETTER LA;Lo;0;L;;;;;N;;;;; +16F17;MIAO LETTER LYA;Lo;0;L;;;;;N;;;;; +16F18;MIAO LETTER LHA;Lo;0;L;;;;;N;;;;; +16F19;MIAO LETTER LHYA;Lo;0;L;;;;;N;;;;; +16F1A;MIAO LETTER TLHA;Lo;0;L;;;;;N;;;;; +16F1B;MIAO LETTER DLHA;Lo;0;L;;;;;N;;;;; +16F1C;MIAO LETTER TLHYA;Lo;0;L;;;;;N;;;;; +16F1D;MIAO LETTER DLHYA;Lo;0;L;;;;;N;;;;; +16F1E;MIAO LETTER KA;Lo;0;L;;;;;N;;;;; +16F1F;MIAO LETTER GA;Lo;0;L;;;;;N;;;;; +16F20;MIAO LETTER YI KA;Lo;0;L;;;;;N;;;;; +16F21;MIAO LETTER QA;Lo;0;L;;;;;N;;;;; +16F22;MIAO LETTER QGA;Lo;0;L;;;;;N;;;;; +16F23;MIAO LETTER NGA;Lo;0;L;;;;;N;;;;; +16F24;MIAO LETTER NGHA;Lo;0;L;;;;;N;;;;; +16F25;MIAO LETTER ARCHAIC NGA;Lo;0;L;;;;;N;;;;; +16F26;MIAO LETTER HA;Lo;0;L;;;;;N;;;;; +16F27;MIAO LETTER XA;Lo;0;L;;;;;N;;;;; +16F28;MIAO LETTER GHA;Lo;0;L;;;;;N;;;;; +16F29;MIAO LETTER GHHA;Lo;0;L;;;;;N;;;;; +16F2A;MIAO LETTER TSSA;Lo;0;L;;;;;N;;;;; +16F2B;MIAO LETTER DZZA;Lo;0;L;;;;;N;;;;; +16F2C;MIAO LETTER NYA;Lo;0;L;;;;;N;;;;; +16F2D;MIAO LETTER NYHA;Lo;0;L;;;;;N;;;;; +16F2E;MIAO LETTER TSHA;Lo;0;L;;;;;N;;;;; +16F2F;MIAO LETTER DZHA;Lo;0;L;;;;;N;;;;; +16F30;MIAO LETTER YI TSHA;Lo;0;L;;;;;N;;;;; +16F31;MIAO LETTER YI DZHA;Lo;0;L;;;;;N;;;;; +16F32;MIAO LETTER REFORMED TSHA;Lo;0;L;;;;;N;;;;; +16F33;MIAO LETTER SHA;Lo;0;L;;;;;N;;;;; +16F34;MIAO LETTER SSA;Lo;0;L;;;;;N;;;;; +16F35;MIAO LETTER ZHA;Lo;0;L;;;;;N;;;;; +16F36;MIAO LETTER ZSHA;Lo;0;L;;;;;N;;;;; +16F37;MIAO LETTER TSA;Lo;0;L;;;;;N;;;;; +16F38;MIAO LETTER DZA;Lo;0;L;;;;;N;;;;; +16F39;MIAO LETTER YI TSA;Lo;0;L;;;;;N;;;;; +16F3A;MIAO LETTER SA;Lo;0;L;;;;;N;;;;; +16F3B;MIAO LETTER ZA;Lo;0;L;;;;;N;;;;; +16F3C;MIAO LETTER ZSA;Lo;0;L;;;;;N;;;;; +16F3D;MIAO LETTER ZZA;Lo;0;L;;;;;N;;;;; +16F3E;MIAO LETTER ZZSA;Lo;0;L;;;;;N;;;;; +16F3F;MIAO LETTER ARCHAIC ZZA;Lo;0;L;;;;;N;;;;; +16F40;MIAO LETTER ZZYA;Lo;0;L;;;;;N;;;;; +16F41;MIAO LETTER ZZSYA;Lo;0;L;;;;;N;;;;; +16F42;MIAO LETTER WA;Lo;0;L;;;;;N;;;;; +16F43;MIAO LETTER AH;Lo;0;L;;;;;N;;;;; +16F44;MIAO LETTER HHA;Lo;0;L;;;;;N;;;;; +16F50;MIAO LETTER NASALIZATION;Lo;0;L;;;;;N;;;;; +16F51;MIAO SIGN ASPIRATION;Mc;0;L;;;;;N;;;;; +16F52;MIAO SIGN REFORMED VOICING;Mc;0;L;;;;;N;;;;; +16F53;MIAO SIGN REFORMED ASPIRATION;Mc;0;L;;;;;N;;;;; +16F54;MIAO VOWEL SIGN A;Mc;0;L;;;;;N;;;;; +16F55;MIAO VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +16F56;MIAO VOWEL SIGN AHH;Mc;0;L;;;;;N;;;;; +16F57;MIAO VOWEL SIGN AN;Mc;0;L;;;;;N;;;;; +16F58;MIAO VOWEL SIGN ANG;Mc;0;L;;;;;N;;;;; +16F59;MIAO VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +16F5A;MIAO VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; +16F5B;MIAO VOWEL SIGN WO;Mc;0;L;;;;;N;;;;; +16F5C;MIAO VOWEL SIGN W;Mc;0;L;;;;;N;;;;; +16F5D;MIAO VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +16F5E;MIAO VOWEL SIGN EN;Mc;0;L;;;;;N;;;;; +16F5F;MIAO VOWEL SIGN ENG;Mc;0;L;;;;;N;;;;; +16F60;MIAO VOWEL SIGN OEY;Mc;0;L;;;;;N;;;;; +16F61;MIAO VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +16F62;MIAO VOWEL SIGN IA;Mc;0;L;;;;;N;;;;; +16F63;MIAO VOWEL SIGN IAN;Mc;0;L;;;;;N;;;;; +16F64;MIAO VOWEL SIGN IANG;Mc;0;L;;;;;N;;;;; +16F65;MIAO VOWEL SIGN IO;Mc;0;L;;;;;N;;;;; +16F66;MIAO VOWEL SIGN IE;Mc;0;L;;;;;N;;;;; +16F67;MIAO VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +16F68;MIAO VOWEL SIGN IU;Mc;0;L;;;;;N;;;;; +16F69;MIAO VOWEL SIGN ING;Mc;0;L;;;;;N;;;;; +16F6A;MIAO VOWEL SIGN U;Mc;0;L;;;;;N;;;;; +16F6B;MIAO VOWEL SIGN UA;Mc;0;L;;;;;N;;;;; +16F6C;MIAO VOWEL SIGN UAN;Mc;0;L;;;;;N;;;;; +16F6D;MIAO VOWEL SIGN UANG;Mc;0;L;;;;;N;;;;; +16F6E;MIAO VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; +16F6F;MIAO VOWEL SIGN UEI;Mc;0;L;;;;;N;;;;; +16F70;MIAO VOWEL SIGN UNG;Mc;0;L;;;;;N;;;;; +16F71;MIAO VOWEL SIGN Y;Mc;0;L;;;;;N;;;;; +16F72;MIAO VOWEL SIGN YI;Mc;0;L;;;;;N;;;;; +16F73;MIAO VOWEL SIGN AE;Mc;0;L;;;;;N;;;;; +16F74;MIAO VOWEL SIGN AEE;Mc;0;L;;;;;N;;;;; +16F75;MIAO VOWEL SIGN ERR;Mc;0;L;;;;;N;;;;; +16F76;MIAO VOWEL SIGN ROUNDED ERR;Mc;0;L;;;;;N;;;;; +16F77;MIAO VOWEL SIGN ER;Mc;0;L;;;;;N;;;;; +16F78;MIAO VOWEL SIGN ROUNDED ER;Mc;0;L;;;;;N;;;;; +16F79;MIAO VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +16F7A;MIAO VOWEL SIGN EI;Mc;0;L;;;;;N;;;;; +16F7B;MIAO VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +16F7C;MIAO VOWEL SIGN OU;Mc;0;L;;;;;N;;;;; +16F7D;MIAO VOWEL SIGN N;Mc;0;L;;;;;N;;;;; +16F7E;MIAO VOWEL SIGN NG;Mc;0;L;;;;;N;;;;; +16F8F;MIAO TONE RIGHT;Mn;0;NSM;;;;;N;;;;; +16F90;MIAO TONE TOP RIGHT;Mn;0;NSM;;;;;N;;;;; +16F91;MIAO TONE ABOVE;Mn;0;NSM;;;;;N;;;;; +16F92;MIAO TONE BELOW;Mn;0;NSM;;;;;N;;;;; +16F93;MIAO LETTER TONE-2;Lm;0;L;;;;;N;;;;; +16F94;MIAO LETTER TONE-3;Lm;0;L;;;;;N;;;;; +16F95;MIAO LETTER TONE-4;Lm;0;L;;;;;N;;;;; +16F96;MIAO LETTER TONE-5;Lm;0;L;;;;;N;;;;; +16F97;MIAO LETTER TONE-6;Lm;0;L;;;;;N;;;;; +16F98;MIAO LETTER TONE-7;Lm;0;L;;;;;N;;;;; +16F99;MIAO LETTER TONE-8;Lm;0;L;;;;;N;;;;; +16F9A;MIAO LETTER REFORMED TONE-1;Lm;0;L;;;;;N;;;;; +16F9B;MIAO LETTER REFORMED TONE-2;Lm;0;L;;;;;N;;;;; +16F9C;MIAO LETTER REFORMED TONE-4;Lm;0;L;;;;;N;;;;; +16F9D;MIAO LETTER REFORMED TONE-5;Lm;0;L;;;;;N;;;;; +16F9E;MIAO LETTER REFORMED TONE-6;Lm;0;L;;;;;N;;;;; +16F9F;MIAO LETTER REFORMED TONE-8;Lm;0;L;;;;;N;;;;; +16FE0;TANGUT ITERATION MARK;Lm;0;L;;;;;N;;;;; +17000;<Tangut Ideograph, First>;Lo;0;L;;;;;N;;;;; +187EC;<Tangut Ideograph, Last>;Lo;0;L;;;;;N;;;;; +18800;TANGUT COMPONENT-001;Lo;0;L;;;;;N;;;;; +18801;TANGUT COMPONENT-002;Lo;0;L;;;;;N;;;;; +18802;TANGUT COMPONENT-003;Lo;0;L;;;;;N;;;;; +18803;TANGUT COMPONENT-004;Lo;0;L;;;;;N;;;;; +18804;TANGUT COMPONENT-005;Lo;0;L;;;;;N;;;;; +18805;TANGUT COMPONENT-006;Lo;0;L;;;;;N;;;;; +18806;TANGUT COMPONENT-007;Lo;0;L;;;;;N;;;;; +18807;TANGUT COMPONENT-008;Lo;0;L;;;;;N;;;;; +18808;TANGUT COMPONENT-009;Lo;0;L;;;;;N;;;;; +18809;TANGUT COMPONENT-010;Lo;0;L;;;;;N;;;;; +1880A;TANGUT COMPONENT-011;Lo;0;L;;;;;N;;;;; +1880B;TANGUT COMPONENT-012;Lo;0;L;;;;;N;;;;; +1880C;TANGUT COMPONENT-013;Lo;0;L;;;;;N;;;;; +1880D;TANGUT COMPONENT-014;Lo;0;L;;;;;N;;;;; +1880E;TANGUT COMPONENT-015;Lo;0;L;;;;;N;;;;; +1880F;TANGUT COMPONENT-016;Lo;0;L;;;;;N;;;;; +18810;TANGUT COMPONENT-017;Lo;0;L;;;;;N;;;;; +18811;TANGUT COMPONENT-018;Lo;0;L;;;;;N;;;;; +18812;TANGUT COMPONENT-019;Lo;0;L;;;;;N;;;;; +18813;TANGUT COMPONENT-020;Lo;0;L;;;;;N;;;;; +18814;TANGUT COMPONENT-021;Lo;0;L;;;;;N;;;;; +18815;TANGUT COMPONENT-022;Lo;0;L;;;;;N;;;;; +18816;TANGUT COMPONENT-023;Lo;0;L;;;;;N;;;;; +18817;TANGUT COMPONENT-024;Lo;0;L;;;;;N;;;;; +18818;TANGUT COMPONENT-025;Lo;0;L;;;;;N;;;;; +18819;TANGUT COMPONENT-026;Lo;0;L;;;;;N;;;;; +1881A;TANGUT COMPONENT-027;Lo;0;L;;;;;N;;;;; +1881B;TANGUT COMPONENT-028;Lo;0;L;;;;;N;;;;; +1881C;TANGUT COMPONENT-029;Lo;0;L;;;;;N;;;;; +1881D;TANGUT COMPONENT-030;Lo;0;L;;;;;N;;;;; +1881E;TANGUT COMPONENT-031;Lo;0;L;;;;;N;;;;; +1881F;TANGUT COMPONENT-032;Lo;0;L;;;;;N;;;;; +18820;TANGUT COMPONENT-033;Lo;0;L;;;;;N;;;;; +18821;TANGUT COMPONENT-034;Lo;0;L;;;;;N;;;;; +18822;TANGUT COMPONENT-035;Lo;0;L;;;;;N;;;;; +18823;TANGUT COMPONENT-036;Lo;0;L;;;;;N;;;;; +18824;TANGUT COMPONENT-037;Lo;0;L;;;;;N;;;;; +18825;TANGUT COMPONENT-038;Lo;0;L;;;;;N;;;;; +18826;TANGUT COMPONENT-039;Lo;0;L;;;;;N;;;;; +18827;TANGUT COMPONENT-040;Lo;0;L;;;;;N;;;;; +18828;TANGUT COMPONENT-041;Lo;0;L;;;;;N;;;;; +18829;TANGUT COMPONENT-042;Lo;0;L;;;;;N;;;;; +1882A;TANGUT COMPONENT-043;Lo;0;L;;;;;N;;;;; +1882B;TANGUT COMPONENT-044;Lo;0;L;;;;;N;;;;; +1882C;TANGUT COMPONENT-045;Lo;0;L;;;;;N;;;;; +1882D;TANGUT COMPONENT-046;Lo;0;L;;;;;N;;;;; +1882E;TANGUT COMPONENT-047;Lo;0;L;;;;;N;;;;; +1882F;TANGUT COMPONENT-048;Lo;0;L;;;;;N;;;;; +18830;TANGUT COMPONENT-049;Lo;0;L;;;;;N;;;;; +18831;TANGUT COMPONENT-050;Lo;0;L;;;;;N;;;;; +18832;TANGUT COMPONENT-051;Lo;0;L;;;;;N;;;;; +18833;TANGUT COMPONENT-052;Lo;0;L;;;;;N;;;;; +18834;TANGUT COMPONENT-053;Lo;0;L;;;;;N;;;;; +18835;TANGUT COMPONENT-054;Lo;0;L;;;;;N;;;;; +18836;TANGUT COMPONENT-055;Lo;0;L;;;;;N;;;;; +18837;TANGUT COMPONENT-056;Lo;0;L;;;;;N;;;;; +18838;TANGUT COMPONENT-057;Lo;0;L;;;;;N;;;;; +18839;TANGUT COMPONENT-058;Lo;0;L;;;;;N;;;;; +1883A;TANGUT COMPONENT-059;Lo;0;L;;;;;N;;;;; +1883B;TANGUT COMPONENT-060;Lo;0;L;;;;;N;;;;; +1883C;TANGUT COMPONENT-061;Lo;0;L;;;;;N;;;;; +1883D;TANGUT COMPONENT-062;Lo;0;L;;;;;N;;;;; +1883E;TANGUT COMPONENT-063;Lo;0;L;;;;;N;;;;; +1883F;TANGUT COMPONENT-064;Lo;0;L;;;;;N;;;;; +18840;TANGUT COMPONENT-065;Lo;0;L;;;;;N;;;;; +18841;TANGUT COMPONENT-066;Lo;0;L;;;;;N;;;;; +18842;TANGUT COMPONENT-067;Lo;0;L;;;;;N;;;;; +18843;TANGUT COMPONENT-068;Lo;0;L;;;;;N;;;;; +18844;TANGUT COMPONENT-069;Lo;0;L;;;;;N;;;;; +18845;TANGUT COMPONENT-070;Lo;0;L;;;;;N;;;;; +18846;TANGUT COMPONENT-071;Lo;0;L;;;;;N;;;;; +18847;TANGUT COMPONENT-072;Lo;0;L;;;;;N;;;;; +18848;TANGUT COMPONENT-073;Lo;0;L;;;;;N;;;;; +18849;TANGUT COMPONENT-074;Lo;0;L;;;;;N;;;;; +1884A;TANGUT COMPONENT-075;Lo;0;L;;;;;N;;;;; +1884B;TANGUT COMPONENT-076;Lo;0;L;;;;;N;;;;; +1884C;TANGUT COMPONENT-077;Lo;0;L;;;;;N;;;;; +1884D;TANGUT COMPONENT-078;Lo;0;L;;;;;N;;;;; +1884E;TANGUT COMPONENT-079;Lo;0;L;;;;;N;;;;; +1884F;TANGUT COMPONENT-080;Lo;0;L;;;;;N;;;;; +18850;TANGUT COMPONENT-081;Lo;0;L;;;;;N;;;;; +18851;TANGUT COMPONENT-082;Lo;0;L;;;;;N;;;;; +18852;TANGUT COMPONENT-083;Lo;0;L;;;;;N;;;;; +18853;TANGUT COMPONENT-084;Lo;0;L;;;;;N;;;;; +18854;TANGUT COMPONENT-085;Lo;0;L;;;;;N;;;;; +18855;TANGUT COMPONENT-086;Lo;0;L;;;;;N;;;;; +18856;TANGUT COMPONENT-087;Lo;0;L;;;;;N;;;;; +18857;TANGUT COMPONENT-088;Lo;0;L;;;;;N;;;;; +18858;TANGUT COMPONENT-089;Lo;0;L;;;;;N;;;;; +18859;TANGUT COMPONENT-090;Lo;0;L;;;;;N;;;;; +1885A;TANGUT COMPONENT-091;Lo;0;L;;;;;N;;;;; +1885B;TANGUT COMPONENT-092;Lo;0;L;;;;;N;;;;; +1885C;TANGUT COMPONENT-093;Lo;0;L;;;;;N;;;;; +1885D;TANGUT COMPONENT-094;Lo;0;L;;;;;N;;;;; +1885E;TANGUT COMPONENT-095;Lo;0;L;;;;;N;;;;; +1885F;TANGUT COMPONENT-096;Lo;0;L;;;;;N;;;;; +18860;TANGUT COMPONENT-097;Lo;0;L;;;;;N;;;;; +18861;TANGUT COMPONENT-098;Lo;0;L;;;;;N;;;;; +18862;TANGUT COMPONENT-099;Lo;0;L;;;;;N;;;;; +18863;TANGUT COMPONENT-100;Lo;0;L;;;;;N;;;;; +18864;TANGUT COMPONENT-101;Lo;0;L;;;;;N;;;;; +18865;TANGUT COMPONENT-102;Lo;0;L;;;;;N;;;;; +18866;TANGUT COMPONENT-103;Lo;0;L;;;;;N;;;;; +18867;TANGUT COMPONENT-104;Lo;0;L;;;;;N;;;;; +18868;TANGUT COMPONENT-105;Lo;0;L;;;;;N;;;;; +18869;TANGUT COMPONENT-106;Lo;0;L;;;;;N;;;;; +1886A;TANGUT COMPONENT-107;Lo;0;L;;;;;N;;;;; +1886B;TANGUT COMPONENT-108;Lo;0;L;;;;;N;;;;; +1886C;TANGUT COMPONENT-109;Lo;0;L;;;;;N;;;;; +1886D;TANGUT COMPONENT-110;Lo;0;L;;;;;N;;;;; +1886E;TANGUT COMPONENT-111;Lo;0;L;;;;;N;;;;; +1886F;TANGUT COMPONENT-112;Lo;0;L;;;;;N;;;;; +18870;TANGUT COMPONENT-113;Lo;0;L;;;;;N;;;;; +18871;TANGUT COMPONENT-114;Lo;0;L;;;;;N;;;;; +18872;TANGUT COMPONENT-115;Lo;0;L;;;;;N;;;;; +18873;TANGUT COMPONENT-116;Lo;0;L;;;;;N;;;;; +18874;TANGUT COMPONENT-117;Lo;0;L;;;;;N;;;;; +18875;TANGUT COMPONENT-118;Lo;0;L;;;;;N;;;;; +18876;TANGUT COMPONENT-119;Lo;0;L;;;;;N;;;;; +18877;TANGUT COMPONENT-120;Lo;0;L;;;;;N;;;;; +18878;TANGUT COMPONENT-121;Lo;0;L;;;;;N;;;;; +18879;TANGUT COMPONENT-122;Lo;0;L;;;;;N;;;;; +1887A;TANGUT COMPONENT-123;Lo;0;L;;;;;N;;;;; +1887B;TANGUT COMPONENT-124;Lo;0;L;;;;;N;;;;; +1887C;TANGUT COMPONENT-125;Lo;0;L;;;;;N;;;;; +1887D;TANGUT COMPONENT-126;Lo;0;L;;;;;N;;;;; +1887E;TANGUT COMPONENT-127;Lo;0;L;;;;;N;;;;; +1887F;TANGUT COMPONENT-128;Lo;0;L;;;;;N;;;;; +18880;TANGUT COMPONENT-129;Lo;0;L;;;;;N;;;;; +18881;TANGUT COMPONENT-130;Lo;0;L;;;;;N;;;;; +18882;TANGUT COMPONENT-131;Lo;0;L;;;;;N;;;;; +18883;TANGUT COMPONENT-132;Lo;0;L;;;;;N;;;;; +18884;TANGUT COMPONENT-133;Lo;0;L;;;;;N;;;;; +18885;TANGUT COMPONENT-134;Lo;0;L;;;;;N;;;;; +18886;TANGUT COMPONENT-135;Lo;0;L;;;;;N;;;;; +18887;TANGUT COMPONENT-136;Lo;0;L;;;;;N;;;;; +18888;TANGUT COMPONENT-137;Lo;0;L;;;;;N;;;;; +18889;TANGUT COMPONENT-138;Lo;0;L;;;;;N;;;;; +1888A;TANGUT COMPONENT-139;Lo;0;L;;;;;N;;;;; +1888B;TANGUT COMPONENT-140;Lo;0;L;;;;;N;;;;; +1888C;TANGUT COMPONENT-141;Lo;0;L;;;;;N;;;;; +1888D;TANGUT COMPONENT-142;Lo;0;L;;;;;N;;;;; +1888E;TANGUT COMPONENT-143;Lo;0;L;;;;;N;;;;; +1888F;TANGUT COMPONENT-144;Lo;0;L;;;;;N;;;;; +18890;TANGUT COMPONENT-145;Lo;0;L;;;;;N;;;;; +18891;TANGUT COMPONENT-146;Lo;0;L;;;;;N;;;;; +18892;TANGUT COMPONENT-147;Lo;0;L;;;;;N;;;;; +18893;TANGUT COMPONENT-148;Lo;0;L;;;;;N;;;;; +18894;TANGUT COMPONENT-149;Lo;0;L;;;;;N;;;;; +18895;TANGUT COMPONENT-150;Lo;0;L;;;;;N;;;;; +18896;TANGUT COMPONENT-151;Lo;0;L;;;;;N;;;;; +18897;TANGUT COMPONENT-152;Lo;0;L;;;;;N;;;;; +18898;TANGUT COMPONENT-153;Lo;0;L;;;;;N;;;;; +18899;TANGUT COMPONENT-154;Lo;0;L;;;;;N;;;;; +1889A;TANGUT COMPONENT-155;Lo;0;L;;;;;N;;;;; +1889B;TANGUT COMPONENT-156;Lo;0;L;;;;;N;;;;; +1889C;TANGUT COMPONENT-157;Lo;0;L;;;;;N;;;;; +1889D;TANGUT COMPONENT-158;Lo;0;L;;;;;N;;;;; +1889E;TANGUT COMPONENT-159;Lo;0;L;;;;;N;;;;; +1889F;TANGUT COMPONENT-160;Lo;0;L;;;;;N;;;;; +188A0;TANGUT COMPONENT-161;Lo;0;L;;;;;N;;;;; +188A1;TANGUT COMPONENT-162;Lo;0;L;;;;;N;;;;; +188A2;TANGUT COMPONENT-163;Lo;0;L;;;;;N;;;;; +188A3;TANGUT COMPONENT-164;Lo;0;L;;;;;N;;;;; +188A4;TANGUT COMPONENT-165;Lo;0;L;;;;;N;;;;; +188A5;TANGUT COMPONENT-166;Lo;0;L;;;;;N;;;;; +188A6;TANGUT COMPONENT-167;Lo;0;L;;;;;N;;;;; +188A7;TANGUT COMPONENT-168;Lo;0;L;;;;;N;;;;; +188A8;TANGUT COMPONENT-169;Lo;0;L;;;;;N;;;;; +188A9;TANGUT COMPONENT-170;Lo;0;L;;;;;N;;;;; +188AA;TANGUT COMPONENT-171;Lo;0;L;;;;;N;;;;; +188AB;TANGUT COMPONENT-172;Lo;0;L;;;;;N;;;;; +188AC;TANGUT COMPONENT-173;Lo;0;L;;;;;N;;;;; +188AD;TANGUT COMPONENT-174;Lo;0;L;;;;;N;;;;; +188AE;TANGUT COMPONENT-175;Lo;0;L;;;;;N;;;;; +188AF;TANGUT COMPONENT-176;Lo;0;L;;;;;N;;;;; +188B0;TANGUT COMPONENT-177;Lo;0;L;;;;;N;;;;; +188B1;TANGUT COMPONENT-178;Lo;0;L;;;;;N;;;;; +188B2;TANGUT COMPONENT-179;Lo;0;L;;;;;N;;;;; +188B3;TANGUT COMPONENT-180;Lo;0;L;;;;;N;;;;; +188B4;TANGUT COMPONENT-181;Lo;0;L;;;;;N;;;;; +188B5;TANGUT COMPONENT-182;Lo;0;L;;;;;N;;;;; +188B6;TANGUT COMPONENT-183;Lo;0;L;;;;;N;;;;; +188B7;TANGUT COMPONENT-184;Lo;0;L;;;;;N;;;;; +188B8;TANGUT COMPONENT-185;Lo;0;L;;;;;N;;;;; +188B9;TANGUT COMPONENT-186;Lo;0;L;;;;;N;;;;; +188BA;TANGUT COMPONENT-187;Lo;0;L;;;;;N;;;;; +188BB;TANGUT COMPONENT-188;Lo;0;L;;;;;N;;;;; +188BC;TANGUT COMPONENT-189;Lo;0;L;;;;;N;;;;; +188BD;TANGUT COMPONENT-190;Lo;0;L;;;;;N;;;;; +188BE;TANGUT COMPONENT-191;Lo;0;L;;;;;N;;;;; +188BF;TANGUT COMPONENT-192;Lo;0;L;;;;;N;;;;; +188C0;TANGUT COMPONENT-193;Lo;0;L;;;;;N;;;;; +188C1;TANGUT COMPONENT-194;Lo;0;L;;;;;N;;;;; +188C2;TANGUT COMPONENT-195;Lo;0;L;;;;;N;;;;; +188C3;TANGUT COMPONENT-196;Lo;0;L;;;;;N;;;;; +188C4;TANGUT COMPONENT-197;Lo;0;L;;;;;N;;;;; +188C5;TANGUT COMPONENT-198;Lo;0;L;;;;;N;;;;; +188C6;TANGUT COMPONENT-199;Lo;0;L;;;;;N;;;;; +188C7;TANGUT COMPONENT-200;Lo;0;L;;;;;N;;;;; +188C8;TANGUT COMPONENT-201;Lo;0;L;;;;;N;;;;; +188C9;TANGUT COMPONENT-202;Lo;0;L;;;;;N;;;;; +188CA;TANGUT COMPONENT-203;Lo;0;L;;;;;N;;;;; +188CB;TANGUT COMPONENT-204;Lo;0;L;;;;;N;;;;; +188CC;TANGUT COMPONENT-205;Lo;0;L;;;;;N;;;;; +188CD;TANGUT COMPONENT-206;Lo;0;L;;;;;N;;;;; +188CE;TANGUT COMPONENT-207;Lo;0;L;;;;;N;;;;; +188CF;TANGUT COMPONENT-208;Lo;0;L;;;;;N;;;;; +188D0;TANGUT COMPONENT-209;Lo;0;L;;;;;N;;;;; +188D1;TANGUT COMPONENT-210;Lo;0;L;;;;;N;;;;; +188D2;TANGUT COMPONENT-211;Lo;0;L;;;;;N;;;;; +188D3;TANGUT COMPONENT-212;Lo;0;L;;;;;N;;;;; +188D4;TANGUT COMPONENT-213;Lo;0;L;;;;;N;;;;; +188D5;TANGUT COMPONENT-214;Lo;0;L;;;;;N;;;;; +188D6;TANGUT COMPONENT-215;Lo;0;L;;;;;N;;;;; +188D7;TANGUT COMPONENT-216;Lo;0;L;;;;;N;;;;; +188D8;TANGUT COMPONENT-217;Lo;0;L;;;;;N;;;;; +188D9;TANGUT COMPONENT-218;Lo;0;L;;;;;N;;;;; +188DA;TANGUT COMPONENT-219;Lo;0;L;;;;;N;;;;; +188DB;TANGUT COMPONENT-220;Lo;0;L;;;;;N;;;;; +188DC;TANGUT COMPONENT-221;Lo;0;L;;;;;N;;;;; +188DD;TANGUT COMPONENT-222;Lo;0;L;;;;;N;;;;; +188DE;TANGUT COMPONENT-223;Lo;0;L;;;;;N;;;;; +188DF;TANGUT COMPONENT-224;Lo;0;L;;;;;N;;;;; +188E0;TANGUT COMPONENT-225;Lo;0;L;;;;;N;;;;; +188E1;TANGUT COMPONENT-226;Lo;0;L;;;;;N;;;;; +188E2;TANGUT COMPONENT-227;Lo;0;L;;;;;N;;;;; +188E3;TANGUT COMPONENT-228;Lo;0;L;;;;;N;;;;; +188E4;TANGUT COMPONENT-229;Lo;0;L;;;;;N;;;;; +188E5;TANGUT COMPONENT-230;Lo;0;L;;;;;N;;;;; +188E6;TANGUT COMPONENT-231;Lo;0;L;;;;;N;;;;; +188E7;TANGUT COMPONENT-232;Lo;0;L;;;;;N;;;;; +188E8;TANGUT COMPONENT-233;Lo;0;L;;;;;N;;;;; +188E9;TANGUT COMPONENT-234;Lo;0;L;;;;;N;;;;; +188EA;TANGUT COMPONENT-235;Lo;0;L;;;;;N;;;;; +188EB;TANGUT COMPONENT-236;Lo;0;L;;;;;N;;;;; +188EC;TANGUT COMPONENT-237;Lo;0;L;;;;;N;;;;; +188ED;TANGUT COMPONENT-238;Lo;0;L;;;;;N;;;;; +188EE;TANGUT COMPONENT-239;Lo;0;L;;;;;N;;;;; +188EF;TANGUT COMPONENT-240;Lo;0;L;;;;;N;;;;; +188F0;TANGUT COMPONENT-241;Lo;0;L;;;;;N;;;;; +188F1;TANGUT COMPONENT-242;Lo;0;L;;;;;N;;;;; +188F2;TANGUT COMPONENT-243;Lo;0;L;;;;;N;;;;; +188F3;TANGUT COMPONENT-244;Lo;0;L;;;;;N;;;;; +188F4;TANGUT COMPONENT-245;Lo;0;L;;;;;N;;;;; +188F5;TANGUT COMPONENT-246;Lo;0;L;;;;;N;;;;; +188F6;TANGUT COMPONENT-247;Lo;0;L;;;;;N;;;;; +188F7;TANGUT COMPONENT-248;Lo;0;L;;;;;N;;;;; +188F8;TANGUT COMPONENT-249;Lo;0;L;;;;;N;;;;; +188F9;TANGUT COMPONENT-250;Lo;0;L;;;;;N;;;;; +188FA;TANGUT COMPONENT-251;Lo;0;L;;;;;N;;;;; +188FB;TANGUT COMPONENT-252;Lo;0;L;;;;;N;;;;; +188FC;TANGUT COMPONENT-253;Lo;0;L;;;;;N;;;;; +188FD;TANGUT COMPONENT-254;Lo;0;L;;;;;N;;;;; +188FE;TANGUT COMPONENT-255;Lo;0;L;;;;;N;;;;; +188FF;TANGUT COMPONENT-256;Lo;0;L;;;;;N;;;;; +18900;TANGUT COMPONENT-257;Lo;0;L;;;;;N;;;;; +18901;TANGUT COMPONENT-258;Lo;0;L;;;;;N;;;;; +18902;TANGUT COMPONENT-259;Lo;0;L;;;;;N;;;;; +18903;TANGUT COMPONENT-260;Lo;0;L;;;;;N;;;;; +18904;TANGUT COMPONENT-261;Lo;0;L;;;;;N;;;;; +18905;TANGUT COMPONENT-262;Lo;0;L;;;;;N;;;;; +18906;TANGUT COMPONENT-263;Lo;0;L;;;;;N;;;;; +18907;TANGUT COMPONENT-264;Lo;0;L;;;;;N;;;;; +18908;TANGUT COMPONENT-265;Lo;0;L;;;;;N;;;;; +18909;TANGUT COMPONENT-266;Lo;0;L;;;;;N;;;;; +1890A;TANGUT COMPONENT-267;Lo;0;L;;;;;N;;;;; +1890B;TANGUT COMPONENT-268;Lo;0;L;;;;;N;;;;; +1890C;TANGUT COMPONENT-269;Lo;0;L;;;;;N;;;;; +1890D;TANGUT COMPONENT-270;Lo;0;L;;;;;N;;;;; +1890E;TANGUT COMPONENT-271;Lo;0;L;;;;;N;;;;; +1890F;TANGUT COMPONENT-272;Lo;0;L;;;;;N;;;;; +18910;TANGUT COMPONENT-273;Lo;0;L;;;;;N;;;;; +18911;TANGUT COMPONENT-274;Lo;0;L;;;;;N;;;;; +18912;TANGUT COMPONENT-275;Lo;0;L;;;;;N;;;;; +18913;TANGUT COMPONENT-276;Lo;0;L;;;;;N;;;;; +18914;TANGUT COMPONENT-277;Lo;0;L;;;;;N;;;;; +18915;TANGUT COMPONENT-278;Lo;0;L;;;;;N;;;;; +18916;TANGUT COMPONENT-279;Lo;0;L;;;;;N;;;;; +18917;TANGUT COMPONENT-280;Lo;0;L;;;;;N;;;;; +18918;TANGUT COMPONENT-281;Lo;0;L;;;;;N;;;;; +18919;TANGUT COMPONENT-282;Lo;0;L;;;;;N;;;;; +1891A;TANGUT COMPONENT-283;Lo;0;L;;;;;N;;;;; +1891B;TANGUT COMPONENT-284;Lo;0;L;;;;;N;;;;; +1891C;TANGUT COMPONENT-285;Lo;0;L;;;;;N;;;;; +1891D;TANGUT COMPONENT-286;Lo;0;L;;;;;N;;;;; +1891E;TANGUT COMPONENT-287;Lo;0;L;;;;;N;;;;; +1891F;TANGUT COMPONENT-288;Lo;0;L;;;;;N;;;;; +18920;TANGUT COMPONENT-289;Lo;0;L;;;;;N;;;;; +18921;TANGUT COMPONENT-290;Lo;0;L;;;;;N;;;;; +18922;TANGUT COMPONENT-291;Lo;0;L;;;;;N;;;;; +18923;TANGUT COMPONENT-292;Lo;0;L;;;;;N;;;;; +18924;TANGUT COMPONENT-293;Lo;0;L;;;;;N;;;;; +18925;TANGUT COMPONENT-294;Lo;0;L;;;;;N;;;;; +18926;TANGUT COMPONENT-295;Lo;0;L;;;;;N;;;;; +18927;TANGUT COMPONENT-296;Lo;0;L;;;;;N;;;;; +18928;TANGUT COMPONENT-297;Lo;0;L;;;;;N;;;;; +18929;TANGUT COMPONENT-298;Lo;0;L;;;;;N;;;;; +1892A;TANGUT COMPONENT-299;Lo;0;L;;;;;N;;;;; +1892B;TANGUT COMPONENT-300;Lo;0;L;;;;;N;;;;; +1892C;TANGUT COMPONENT-301;Lo;0;L;;;;;N;;;;; +1892D;TANGUT COMPONENT-302;Lo;0;L;;;;;N;;;;; +1892E;TANGUT COMPONENT-303;Lo;0;L;;;;;N;;;;; +1892F;TANGUT COMPONENT-304;Lo;0;L;;;;;N;;;;; +18930;TANGUT COMPONENT-305;Lo;0;L;;;;;N;;;;; +18931;TANGUT COMPONENT-306;Lo;0;L;;;;;N;;;;; +18932;TANGUT COMPONENT-307;Lo;0;L;;;;;N;;;;; +18933;TANGUT COMPONENT-308;Lo;0;L;;;;;N;;;;; +18934;TANGUT COMPONENT-309;Lo;0;L;;;;;N;;;;; +18935;TANGUT COMPONENT-310;Lo;0;L;;;;;N;;;;; +18936;TANGUT COMPONENT-311;Lo;0;L;;;;;N;;;;; +18937;TANGUT COMPONENT-312;Lo;0;L;;;;;N;;;;; +18938;TANGUT COMPONENT-313;Lo;0;L;;;;;N;;;;; +18939;TANGUT COMPONENT-314;Lo;0;L;;;;;N;;;;; +1893A;TANGUT COMPONENT-315;Lo;0;L;;;;;N;;;;; +1893B;TANGUT COMPONENT-316;Lo;0;L;;;;;N;;;;; +1893C;TANGUT COMPONENT-317;Lo;0;L;;;;;N;;;;; +1893D;TANGUT COMPONENT-318;Lo;0;L;;;;;N;;;;; +1893E;TANGUT COMPONENT-319;Lo;0;L;;;;;N;;;;; +1893F;TANGUT COMPONENT-320;Lo;0;L;;;;;N;;;;; +18940;TANGUT COMPONENT-321;Lo;0;L;;;;;N;;;;; +18941;TANGUT COMPONENT-322;Lo;0;L;;;;;N;;;;; +18942;TANGUT COMPONENT-323;Lo;0;L;;;;;N;;;;; +18943;TANGUT COMPONENT-324;Lo;0;L;;;;;N;;;;; +18944;TANGUT COMPONENT-325;Lo;0;L;;;;;N;;;;; +18945;TANGUT COMPONENT-326;Lo;0;L;;;;;N;;;;; +18946;TANGUT COMPONENT-327;Lo;0;L;;;;;N;;;;; +18947;TANGUT COMPONENT-328;Lo;0;L;;;;;N;;;;; +18948;TANGUT COMPONENT-329;Lo;0;L;;;;;N;;;;; +18949;TANGUT COMPONENT-330;Lo;0;L;;;;;N;;;;; +1894A;TANGUT COMPONENT-331;Lo;0;L;;;;;N;;;;; +1894B;TANGUT COMPONENT-332;Lo;0;L;;;;;N;;;;; +1894C;TANGUT COMPONENT-333;Lo;0;L;;;;;N;;;;; +1894D;TANGUT COMPONENT-334;Lo;0;L;;;;;N;;;;; +1894E;TANGUT COMPONENT-335;Lo;0;L;;;;;N;;;;; +1894F;TANGUT COMPONENT-336;Lo;0;L;;;;;N;;;;; +18950;TANGUT COMPONENT-337;Lo;0;L;;;;;N;;;;; +18951;TANGUT COMPONENT-338;Lo;0;L;;;;;N;;;;; +18952;TANGUT COMPONENT-339;Lo;0;L;;;;;N;;;;; +18953;TANGUT COMPONENT-340;Lo;0;L;;;;;N;;;;; +18954;TANGUT COMPONENT-341;Lo;0;L;;;;;N;;;;; +18955;TANGUT COMPONENT-342;Lo;0;L;;;;;N;;;;; +18956;TANGUT COMPONENT-343;Lo;0;L;;;;;N;;;;; +18957;TANGUT COMPONENT-344;Lo;0;L;;;;;N;;;;; +18958;TANGUT COMPONENT-345;Lo;0;L;;;;;N;;;;; +18959;TANGUT COMPONENT-346;Lo;0;L;;;;;N;;;;; +1895A;TANGUT COMPONENT-347;Lo;0;L;;;;;N;;;;; +1895B;TANGUT COMPONENT-348;Lo;0;L;;;;;N;;;;; +1895C;TANGUT COMPONENT-349;Lo;0;L;;;;;N;;;;; +1895D;TANGUT COMPONENT-350;Lo;0;L;;;;;N;;;;; +1895E;TANGUT COMPONENT-351;Lo;0;L;;;;;N;;;;; +1895F;TANGUT COMPONENT-352;Lo;0;L;;;;;N;;;;; +18960;TANGUT COMPONENT-353;Lo;0;L;;;;;N;;;;; +18961;TANGUT COMPONENT-354;Lo;0;L;;;;;N;;;;; +18962;TANGUT COMPONENT-355;Lo;0;L;;;;;N;;;;; +18963;TANGUT COMPONENT-356;Lo;0;L;;;;;N;;;;; +18964;TANGUT COMPONENT-357;Lo;0;L;;;;;N;;;;; +18965;TANGUT COMPONENT-358;Lo;0;L;;;;;N;;;;; +18966;TANGUT COMPONENT-359;Lo;0;L;;;;;N;;;;; +18967;TANGUT COMPONENT-360;Lo;0;L;;;;;N;;;;; +18968;TANGUT COMPONENT-361;Lo;0;L;;;;;N;;;;; +18969;TANGUT COMPONENT-362;Lo;0;L;;;;;N;;;;; +1896A;TANGUT COMPONENT-363;Lo;0;L;;;;;N;;;;; +1896B;TANGUT COMPONENT-364;Lo;0;L;;;;;N;;;;; +1896C;TANGUT COMPONENT-365;Lo;0;L;;;;;N;;;;; +1896D;TANGUT COMPONENT-366;Lo;0;L;;;;;N;;;;; +1896E;TANGUT COMPONENT-367;Lo;0;L;;;;;N;;;;; +1896F;TANGUT COMPONENT-368;Lo;0;L;;;;;N;;;;; +18970;TANGUT COMPONENT-369;Lo;0;L;;;;;N;;;;; +18971;TANGUT COMPONENT-370;Lo;0;L;;;;;N;;;;; +18972;TANGUT COMPONENT-371;Lo;0;L;;;;;N;;;;; +18973;TANGUT COMPONENT-372;Lo;0;L;;;;;N;;;;; +18974;TANGUT COMPONENT-373;Lo;0;L;;;;;N;;;;; +18975;TANGUT COMPONENT-374;Lo;0;L;;;;;N;;;;; +18976;TANGUT COMPONENT-375;Lo;0;L;;;;;N;;;;; +18977;TANGUT COMPONENT-376;Lo;0;L;;;;;N;;;;; +18978;TANGUT COMPONENT-377;Lo;0;L;;;;;N;;;;; +18979;TANGUT COMPONENT-378;Lo;0;L;;;;;N;;;;; +1897A;TANGUT COMPONENT-379;Lo;0;L;;;;;N;;;;; +1897B;TANGUT COMPONENT-380;Lo;0;L;;;;;N;;;;; +1897C;TANGUT COMPONENT-381;Lo;0;L;;;;;N;;;;; +1897D;TANGUT COMPONENT-382;Lo;0;L;;;;;N;;;;; +1897E;TANGUT COMPONENT-383;Lo;0;L;;;;;N;;;;; +1897F;TANGUT COMPONENT-384;Lo;0;L;;;;;N;;;;; +18980;TANGUT COMPONENT-385;Lo;0;L;;;;;N;;;;; +18981;TANGUT COMPONENT-386;Lo;0;L;;;;;N;;;;; +18982;TANGUT COMPONENT-387;Lo;0;L;;;;;N;;;;; +18983;TANGUT COMPONENT-388;Lo;0;L;;;;;N;;;;; +18984;TANGUT COMPONENT-389;Lo;0;L;;;;;N;;;;; +18985;TANGUT COMPONENT-390;Lo;0;L;;;;;N;;;;; +18986;TANGUT COMPONENT-391;Lo;0;L;;;;;N;;;;; +18987;TANGUT COMPONENT-392;Lo;0;L;;;;;N;;;;; +18988;TANGUT COMPONENT-393;Lo;0;L;;;;;N;;;;; +18989;TANGUT COMPONENT-394;Lo;0;L;;;;;N;;;;; +1898A;TANGUT COMPONENT-395;Lo;0;L;;;;;N;;;;; +1898B;TANGUT COMPONENT-396;Lo;0;L;;;;;N;;;;; +1898C;TANGUT COMPONENT-397;Lo;0;L;;;;;N;;;;; +1898D;TANGUT COMPONENT-398;Lo;0;L;;;;;N;;;;; +1898E;TANGUT COMPONENT-399;Lo;0;L;;;;;N;;;;; +1898F;TANGUT COMPONENT-400;Lo;0;L;;;;;N;;;;; +18990;TANGUT COMPONENT-401;Lo;0;L;;;;;N;;;;; +18991;TANGUT COMPONENT-402;Lo;0;L;;;;;N;;;;; +18992;TANGUT COMPONENT-403;Lo;0;L;;;;;N;;;;; +18993;TANGUT COMPONENT-404;Lo;0;L;;;;;N;;;;; +18994;TANGUT COMPONENT-405;Lo;0;L;;;;;N;;;;; +18995;TANGUT COMPONENT-406;Lo;0;L;;;;;N;;;;; +18996;TANGUT COMPONENT-407;Lo;0;L;;;;;N;;;;; +18997;TANGUT COMPONENT-408;Lo;0;L;;;;;N;;;;; +18998;TANGUT COMPONENT-409;Lo;0;L;;;;;N;;;;; +18999;TANGUT COMPONENT-410;Lo;0;L;;;;;N;;;;; +1899A;TANGUT COMPONENT-411;Lo;0;L;;;;;N;;;;; +1899B;TANGUT COMPONENT-412;Lo;0;L;;;;;N;;;;; +1899C;TANGUT COMPONENT-413;Lo;0;L;;;;;N;;;;; +1899D;TANGUT COMPONENT-414;Lo;0;L;;;;;N;;;;; +1899E;TANGUT COMPONENT-415;Lo;0;L;;;;;N;;;;; +1899F;TANGUT COMPONENT-416;Lo;0;L;;;;;N;;;;; +189A0;TANGUT COMPONENT-417;Lo;0;L;;;;;N;;;;; +189A1;TANGUT COMPONENT-418;Lo;0;L;;;;;N;;;;; +189A2;TANGUT COMPONENT-419;Lo;0;L;;;;;N;;;;; +189A3;TANGUT COMPONENT-420;Lo;0;L;;;;;N;;;;; +189A4;TANGUT COMPONENT-421;Lo;0;L;;;;;N;;;;; +189A5;TANGUT COMPONENT-422;Lo;0;L;;;;;N;;;;; +189A6;TANGUT COMPONENT-423;Lo;0;L;;;;;N;;;;; +189A7;TANGUT COMPONENT-424;Lo;0;L;;;;;N;;;;; +189A8;TANGUT COMPONENT-425;Lo;0;L;;;;;N;;;;; +189A9;TANGUT COMPONENT-426;Lo;0;L;;;;;N;;;;; +189AA;TANGUT COMPONENT-427;Lo;0;L;;;;;N;;;;; +189AB;TANGUT COMPONENT-428;Lo;0;L;;;;;N;;;;; +189AC;TANGUT COMPONENT-429;Lo;0;L;;;;;N;;;;; +189AD;TANGUT COMPONENT-430;Lo;0;L;;;;;N;;;;; +189AE;TANGUT COMPONENT-431;Lo;0;L;;;;;N;;;;; +189AF;TANGUT COMPONENT-432;Lo;0;L;;;;;N;;;;; +189B0;TANGUT COMPONENT-433;Lo;0;L;;;;;N;;;;; +189B1;TANGUT COMPONENT-434;Lo;0;L;;;;;N;;;;; +189B2;TANGUT COMPONENT-435;Lo;0;L;;;;;N;;;;; +189B3;TANGUT COMPONENT-436;Lo;0;L;;;;;N;;;;; +189B4;TANGUT COMPONENT-437;Lo;0;L;;;;;N;;;;; +189B5;TANGUT COMPONENT-438;Lo;0;L;;;;;N;;;;; +189B6;TANGUT COMPONENT-439;Lo;0;L;;;;;N;;;;; +189B7;TANGUT COMPONENT-440;Lo;0;L;;;;;N;;;;; +189B8;TANGUT COMPONENT-441;Lo;0;L;;;;;N;;;;; +189B9;TANGUT COMPONENT-442;Lo;0;L;;;;;N;;;;; +189BA;TANGUT COMPONENT-443;Lo;0;L;;;;;N;;;;; +189BB;TANGUT COMPONENT-444;Lo;0;L;;;;;N;;;;; +189BC;TANGUT COMPONENT-445;Lo;0;L;;;;;N;;;;; +189BD;TANGUT COMPONENT-446;Lo;0;L;;;;;N;;;;; +189BE;TANGUT COMPONENT-447;Lo;0;L;;;;;N;;;;; +189BF;TANGUT COMPONENT-448;Lo;0;L;;;;;N;;;;; +189C0;TANGUT COMPONENT-449;Lo;0;L;;;;;N;;;;; +189C1;TANGUT COMPONENT-450;Lo;0;L;;;;;N;;;;; +189C2;TANGUT COMPONENT-451;Lo;0;L;;;;;N;;;;; +189C3;TANGUT COMPONENT-452;Lo;0;L;;;;;N;;;;; +189C4;TANGUT COMPONENT-453;Lo;0;L;;;;;N;;;;; +189C5;TANGUT COMPONENT-454;Lo;0;L;;;;;N;;;;; +189C6;TANGUT COMPONENT-455;Lo;0;L;;;;;N;;;;; +189C7;TANGUT COMPONENT-456;Lo;0;L;;;;;N;;;;; +189C8;TANGUT COMPONENT-457;Lo;0;L;;;;;N;;;;; +189C9;TANGUT COMPONENT-458;Lo;0;L;;;;;N;;;;; +189CA;TANGUT COMPONENT-459;Lo;0;L;;;;;N;;;;; +189CB;TANGUT COMPONENT-460;Lo;0;L;;;;;N;;;;; +189CC;TANGUT COMPONENT-461;Lo;0;L;;;;;N;;;;; +189CD;TANGUT COMPONENT-462;Lo;0;L;;;;;N;;;;; +189CE;TANGUT COMPONENT-463;Lo;0;L;;;;;N;;;;; +189CF;TANGUT COMPONENT-464;Lo;0;L;;;;;N;;;;; +189D0;TANGUT COMPONENT-465;Lo;0;L;;;;;N;;;;; +189D1;TANGUT COMPONENT-466;Lo;0;L;;;;;N;;;;; +189D2;TANGUT COMPONENT-467;Lo;0;L;;;;;N;;;;; +189D3;TANGUT COMPONENT-468;Lo;0;L;;;;;N;;;;; +189D4;TANGUT COMPONENT-469;Lo;0;L;;;;;N;;;;; +189D5;TANGUT COMPONENT-470;Lo;0;L;;;;;N;;;;; +189D6;TANGUT COMPONENT-471;Lo;0;L;;;;;N;;;;; +189D7;TANGUT COMPONENT-472;Lo;0;L;;;;;N;;;;; +189D8;TANGUT COMPONENT-473;Lo;0;L;;;;;N;;;;; +189D9;TANGUT COMPONENT-474;Lo;0;L;;;;;N;;;;; +189DA;TANGUT COMPONENT-475;Lo;0;L;;;;;N;;;;; +189DB;TANGUT COMPONENT-476;Lo;0;L;;;;;N;;;;; +189DC;TANGUT COMPONENT-477;Lo;0;L;;;;;N;;;;; +189DD;TANGUT COMPONENT-478;Lo;0;L;;;;;N;;;;; +189DE;TANGUT COMPONENT-479;Lo;0;L;;;;;N;;;;; +189DF;TANGUT COMPONENT-480;Lo;0;L;;;;;N;;;;; +189E0;TANGUT COMPONENT-481;Lo;0;L;;;;;N;;;;; +189E1;TANGUT COMPONENT-482;Lo;0;L;;;;;N;;;;; +189E2;TANGUT COMPONENT-483;Lo;0;L;;;;;N;;;;; +189E3;TANGUT COMPONENT-484;Lo;0;L;;;;;N;;;;; +189E4;TANGUT COMPONENT-485;Lo;0;L;;;;;N;;;;; +189E5;TANGUT COMPONENT-486;Lo;0;L;;;;;N;;;;; +189E6;TANGUT COMPONENT-487;Lo;0;L;;;;;N;;;;; +189E7;TANGUT COMPONENT-488;Lo;0;L;;;;;N;;;;; +189E8;TANGUT COMPONENT-489;Lo;0;L;;;;;N;;;;; +189E9;TANGUT COMPONENT-490;Lo;0;L;;;;;N;;;;; +189EA;TANGUT COMPONENT-491;Lo;0;L;;;;;N;;;;; +189EB;TANGUT COMPONENT-492;Lo;0;L;;;;;N;;;;; +189EC;TANGUT COMPONENT-493;Lo;0;L;;;;;N;;;;; +189ED;TANGUT COMPONENT-494;Lo;0;L;;;;;N;;;;; +189EE;TANGUT COMPONENT-495;Lo;0;L;;;;;N;;;;; +189EF;TANGUT COMPONENT-496;Lo;0;L;;;;;N;;;;; +189F0;TANGUT COMPONENT-497;Lo;0;L;;;;;N;;;;; +189F1;TANGUT COMPONENT-498;Lo;0;L;;;;;N;;;;; +189F2;TANGUT COMPONENT-499;Lo;0;L;;;;;N;;;;; +189F3;TANGUT COMPONENT-500;Lo;0;L;;;;;N;;;;; +189F4;TANGUT COMPONENT-501;Lo;0;L;;;;;N;;;;; +189F5;TANGUT COMPONENT-502;Lo;0;L;;;;;N;;;;; +189F6;TANGUT COMPONENT-503;Lo;0;L;;;;;N;;;;; +189F7;TANGUT COMPONENT-504;Lo;0;L;;;;;N;;;;; +189F8;TANGUT COMPONENT-505;Lo;0;L;;;;;N;;;;; +189F9;TANGUT COMPONENT-506;Lo;0;L;;;;;N;;;;; +189FA;TANGUT COMPONENT-507;Lo;0;L;;;;;N;;;;; +189FB;TANGUT COMPONENT-508;Lo;0;L;;;;;N;;;;; +189FC;TANGUT COMPONENT-509;Lo;0;L;;;;;N;;;;; +189FD;TANGUT COMPONENT-510;Lo;0;L;;;;;N;;;;; +189FE;TANGUT COMPONENT-511;Lo;0;L;;;;;N;;;;; +189FF;TANGUT COMPONENT-512;Lo;0;L;;;;;N;;;;; +18A00;TANGUT COMPONENT-513;Lo;0;L;;;;;N;;;;; +18A01;TANGUT COMPONENT-514;Lo;0;L;;;;;N;;;;; +18A02;TANGUT COMPONENT-515;Lo;0;L;;;;;N;;;;; +18A03;TANGUT COMPONENT-516;Lo;0;L;;;;;N;;;;; +18A04;TANGUT COMPONENT-517;Lo;0;L;;;;;N;;;;; +18A05;TANGUT COMPONENT-518;Lo;0;L;;;;;N;;;;; +18A06;TANGUT COMPONENT-519;Lo;0;L;;;;;N;;;;; +18A07;TANGUT COMPONENT-520;Lo;0;L;;;;;N;;;;; +18A08;TANGUT COMPONENT-521;Lo;0;L;;;;;N;;;;; +18A09;TANGUT COMPONENT-522;Lo;0;L;;;;;N;;;;; +18A0A;TANGUT COMPONENT-523;Lo;0;L;;;;;N;;;;; +18A0B;TANGUT COMPONENT-524;Lo;0;L;;;;;N;;;;; +18A0C;TANGUT COMPONENT-525;Lo;0;L;;;;;N;;;;; +18A0D;TANGUT COMPONENT-526;Lo;0;L;;;;;N;;;;; +18A0E;TANGUT COMPONENT-527;Lo;0;L;;;;;N;;;;; +18A0F;TANGUT COMPONENT-528;Lo;0;L;;;;;N;;;;; +18A10;TANGUT COMPONENT-529;Lo;0;L;;;;;N;;;;; +18A11;TANGUT COMPONENT-530;Lo;0;L;;;;;N;;;;; +18A12;TANGUT COMPONENT-531;Lo;0;L;;;;;N;;;;; +18A13;TANGUT COMPONENT-532;Lo;0;L;;;;;N;;;;; +18A14;TANGUT COMPONENT-533;Lo;0;L;;;;;N;;;;; +18A15;TANGUT COMPONENT-534;Lo;0;L;;;;;N;;;;; +18A16;TANGUT COMPONENT-535;Lo;0;L;;;;;N;;;;; +18A17;TANGUT COMPONENT-536;Lo;0;L;;;;;N;;;;; +18A18;TANGUT COMPONENT-537;Lo;0;L;;;;;N;;;;; +18A19;TANGUT COMPONENT-538;Lo;0;L;;;;;N;;;;; +18A1A;TANGUT COMPONENT-539;Lo;0;L;;;;;N;;;;; +18A1B;TANGUT COMPONENT-540;Lo;0;L;;;;;N;;;;; +18A1C;TANGUT COMPONENT-541;Lo;0;L;;;;;N;;;;; +18A1D;TANGUT COMPONENT-542;Lo;0;L;;;;;N;;;;; +18A1E;TANGUT COMPONENT-543;Lo;0;L;;;;;N;;;;; +18A1F;TANGUT COMPONENT-544;Lo;0;L;;;;;N;;;;; +18A20;TANGUT COMPONENT-545;Lo;0;L;;;;;N;;;;; +18A21;TANGUT COMPONENT-546;Lo;0;L;;;;;N;;;;; +18A22;TANGUT COMPONENT-547;Lo;0;L;;;;;N;;;;; +18A23;TANGUT COMPONENT-548;Lo;0;L;;;;;N;;;;; +18A24;TANGUT COMPONENT-549;Lo;0;L;;;;;N;;;;; +18A25;TANGUT COMPONENT-550;Lo;0;L;;;;;N;;;;; +18A26;TANGUT COMPONENT-551;Lo;0;L;;;;;N;;;;; +18A27;TANGUT COMPONENT-552;Lo;0;L;;;;;N;;;;; +18A28;TANGUT COMPONENT-553;Lo;0;L;;;;;N;;;;; +18A29;TANGUT COMPONENT-554;Lo;0;L;;;;;N;;;;; +18A2A;TANGUT COMPONENT-555;Lo;0;L;;;;;N;;;;; +18A2B;TANGUT COMPONENT-556;Lo;0;L;;;;;N;;;;; +18A2C;TANGUT COMPONENT-557;Lo;0;L;;;;;N;;;;; +18A2D;TANGUT COMPONENT-558;Lo;0;L;;;;;N;;;;; +18A2E;TANGUT COMPONENT-559;Lo;0;L;;;;;N;;;;; +18A2F;TANGUT COMPONENT-560;Lo;0;L;;;;;N;;;;; +18A30;TANGUT COMPONENT-561;Lo;0;L;;;;;N;;;;; +18A31;TANGUT COMPONENT-562;Lo;0;L;;;;;N;;;;; +18A32;TANGUT COMPONENT-563;Lo;0;L;;;;;N;;;;; +18A33;TANGUT COMPONENT-564;Lo;0;L;;;;;N;;;;; +18A34;TANGUT COMPONENT-565;Lo;0;L;;;;;N;;;;; +18A35;TANGUT COMPONENT-566;Lo;0;L;;;;;N;;;;; +18A36;TANGUT COMPONENT-567;Lo;0;L;;;;;N;;;;; +18A37;TANGUT COMPONENT-568;Lo;0;L;;;;;N;;;;; +18A38;TANGUT COMPONENT-569;Lo;0;L;;;;;N;;;;; +18A39;TANGUT COMPONENT-570;Lo;0;L;;;;;N;;;;; +18A3A;TANGUT COMPONENT-571;Lo;0;L;;;;;N;;;;; +18A3B;TANGUT COMPONENT-572;Lo;0;L;;;;;N;;;;; +18A3C;TANGUT COMPONENT-573;Lo;0;L;;;;;N;;;;; +18A3D;TANGUT COMPONENT-574;Lo;0;L;;;;;N;;;;; +18A3E;TANGUT COMPONENT-575;Lo;0;L;;;;;N;;;;; +18A3F;TANGUT COMPONENT-576;Lo;0;L;;;;;N;;;;; +18A40;TANGUT COMPONENT-577;Lo;0;L;;;;;N;;;;; +18A41;TANGUT COMPONENT-578;Lo;0;L;;;;;N;;;;; +18A42;TANGUT COMPONENT-579;Lo;0;L;;;;;N;;;;; +18A43;TANGUT COMPONENT-580;Lo;0;L;;;;;N;;;;; +18A44;TANGUT COMPONENT-581;Lo;0;L;;;;;N;;;;; +18A45;TANGUT COMPONENT-582;Lo;0;L;;;;;N;;;;; +18A46;TANGUT COMPONENT-583;Lo;0;L;;;;;N;;;;; +18A47;TANGUT COMPONENT-584;Lo;0;L;;;;;N;;;;; +18A48;TANGUT COMPONENT-585;Lo;0;L;;;;;N;;;;; +18A49;TANGUT COMPONENT-586;Lo;0;L;;;;;N;;;;; +18A4A;TANGUT COMPONENT-587;Lo;0;L;;;;;N;;;;; +18A4B;TANGUT COMPONENT-588;Lo;0;L;;;;;N;;;;; +18A4C;TANGUT COMPONENT-589;Lo;0;L;;;;;N;;;;; +18A4D;TANGUT COMPONENT-590;Lo;0;L;;;;;N;;;;; +18A4E;TANGUT COMPONENT-591;Lo;0;L;;;;;N;;;;; +18A4F;TANGUT COMPONENT-592;Lo;0;L;;;;;N;;;;; +18A50;TANGUT COMPONENT-593;Lo;0;L;;;;;N;;;;; +18A51;TANGUT COMPONENT-594;Lo;0;L;;;;;N;;;;; +18A52;TANGUT COMPONENT-595;Lo;0;L;;;;;N;;;;; +18A53;TANGUT COMPONENT-596;Lo;0;L;;;;;N;;;;; +18A54;TANGUT COMPONENT-597;Lo;0;L;;;;;N;;;;; +18A55;TANGUT COMPONENT-598;Lo;0;L;;;;;N;;;;; +18A56;TANGUT COMPONENT-599;Lo;0;L;;;;;N;;;;; +18A57;TANGUT COMPONENT-600;Lo;0;L;;;;;N;;;;; +18A58;TANGUT COMPONENT-601;Lo;0;L;;;;;N;;;;; +18A59;TANGUT COMPONENT-602;Lo;0;L;;;;;N;;;;; +18A5A;TANGUT COMPONENT-603;Lo;0;L;;;;;N;;;;; +18A5B;TANGUT COMPONENT-604;Lo;0;L;;;;;N;;;;; +18A5C;TANGUT COMPONENT-605;Lo;0;L;;;;;N;;;;; +18A5D;TANGUT COMPONENT-606;Lo;0;L;;;;;N;;;;; +18A5E;TANGUT COMPONENT-607;Lo;0;L;;;;;N;;;;; +18A5F;TANGUT COMPONENT-608;Lo;0;L;;;;;N;;;;; +18A60;TANGUT COMPONENT-609;Lo;0;L;;;;;N;;;;; +18A61;TANGUT COMPONENT-610;Lo;0;L;;;;;N;;;;; +18A62;TANGUT COMPONENT-611;Lo;0;L;;;;;N;;;;; +18A63;TANGUT COMPONENT-612;Lo;0;L;;;;;N;;;;; +18A64;TANGUT COMPONENT-613;Lo;0;L;;;;;N;;;;; +18A65;TANGUT COMPONENT-614;Lo;0;L;;;;;N;;;;; +18A66;TANGUT COMPONENT-615;Lo;0;L;;;;;N;;;;; +18A67;TANGUT COMPONENT-616;Lo;0;L;;;;;N;;;;; +18A68;TANGUT COMPONENT-617;Lo;0;L;;;;;N;;;;; +18A69;TANGUT COMPONENT-618;Lo;0;L;;;;;N;;;;; +18A6A;TANGUT COMPONENT-619;Lo;0;L;;;;;N;;;;; +18A6B;TANGUT COMPONENT-620;Lo;0;L;;;;;N;;;;; +18A6C;TANGUT COMPONENT-621;Lo;0;L;;;;;N;;;;; +18A6D;TANGUT COMPONENT-622;Lo;0;L;;;;;N;;;;; +18A6E;TANGUT COMPONENT-623;Lo;0;L;;;;;N;;;;; +18A6F;TANGUT COMPONENT-624;Lo;0;L;;;;;N;;;;; +18A70;TANGUT COMPONENT-625;Lo;0;L;;;;;N;;;;; +18A71;TANGUT COMPONENT-626;Lo;0;L;;;;;N;;;;; +18A72;TANGUT COMPONENT-627;Lo;0;L;;;;;N;;;;; +18A73;TANGUT COMPONENT-628;Lo;0;L;;;;;N;;;;; +18A74;TANGUT COMPONENT-629;Lo;0;L;;;;;N;;;;; +18A75;TANGUT COMPONENT-630;Lo;0;L;;;;;N;;;;; +18A76;TANGUT COMPONENT-631;Lo;0;L;;;;;N;;;;; +18A77;TANGUT COMPONENT-632;Lo;0;L;;;;;N;;;;; +18A78;TANGUT COMPONENT-633;Lo;0;L;;;;;N;;;;; +18A79;TANGUT COMPONENT-634;Lo;0;L;;;;;N;;;;; +18A7A;TANGUT COMPONENT-635;Lo;0;L;;;;;N;;;;; +18A7B;TANGUT COMPONENT-636;Lo;0;L;;;;;N;;;;; +18A7C;TANGUT COMPONENT-637;Lo;0;L;;;;;N;;;;; +18A7D;TANGUT COMPONENT-638;Lo;0;L;;;;;N;;;;; +18A7E;TANGUT COMPONENT-639;Lo;0;L;;;;;N;;;;; +18A7F;TANGUT COMPONENT-640;Lo;0;L;;;;;N;;;;; +18A80;TANGUT COMPONENT-641;Lo;0;L;;;;;N;;;;; +18A81;TANGUT COMPONENT-642;Lo;0;L;;;;;N;;;;; +18A82;TANGUT COMPONENT-643;Lo;0;L;;;;;N;;;;; +18A83;TANGUT COMPONENT-644;Lo;0;L;;;;;N;;;;; +18A84;TANGUT COMPONENT-645;Lo;0;L;;;;;N;;;;; +18A85;TANGUT COMPONENT-646;Lo;0;L;;;;;N;;;;; +18A86;TANGUT COMPONENT-647;Lo;0;L;;;;;N;;;;; +18A87;TANGUT COMPONENT-648;Lo;0;L;;;;;N;;;;; +18A88;TANGUT COMPONENT-649;Lo;0;L;;;;;N;;;;; +18A89;TANGUT COMPONENT-650;Lo;0;L;;;;;N;;;;; +18A8A;TANGUT COMPONENT-651;Lo;0;L;;;;;N;;;;; +18A8B;TANGUT COMPONENT-652;Lo;0;L;;;;;N;;;;; +18A8C;TANGUT COMPONENT-653;Lo;0;L;;;;;N;;;;; +18A8D;TANGUT COMPONENT-654;Lo;0;L;;;;;N;;;;; +18A8E;TANGUT COMPONENT-655;Lo;0;L;;;;;N;;;;; +18A8F;TANGUT COMPONENT-656;Lo;0;L;;;;;N;;;;; +18A90;TANGUT COMPONENT-657;Lo;0;L;;;;;N;;;;; +18A91;TANGUT COMPONENT-658;Lo;0;L;;;;;N;;;;; +18A92;TANGUT COMPONENT-659;Lo;0;L;;;;;N;;;;; +18A93;TANGUT COMPONENT-660;Lo;0;L;;;;;N;;;;; +18A94;TANGUT COMPONENT-661;Lo;0;L;;;;;N;;;;; +18A95;TANGUT COMPONENT-662;Lo;0;L;;;;;N;;;;; +18A96;TANGUT COMPONENT-663;Lo;0;L;;;;;N;;;;; +18A97;TANGUT COMPONENT-664;Lo;0;L;;;;;N;;;;; +18A98;TANGUT COMPONENT-665;Lo;0;L;;;;;N;;;;; +18A99;TANGUT COMPONENT-666;Lo;0;L;;;;;N;;;;; +18A9A;TANGUT COMPONENT-667;Lo;0;L;;;;;N;;;;; +18A9B;TANGUT COMPONENT-668;Lo;0;L;;;;;N;;;;; +18A9C;TANGUT COMPONENT-669;Lo;0;L;;;;;N;;;;; +18A9D;TANGUT COMPONENT-670;Lo;0;L;;;;;N;;;;; +18A9E;TANGUT COMPONENT-671;Lo;0;L;;;;;N;;;;; +18A9F;TANGUT COMPONENT-672;Lo;0;L;;;;;N;;;;; +18AA0;TANGUT COMPONENT-673;Lo;0;L;;;;;N;;;;; +18AA1;TANGUT COMPONENT-674;Lo;0;L;;;;;N;;;;; +18AA2;TANGUT COMPONENT-675;Lo;0;L;;;;;N;;;;; +18AA3;TANGUT COMPONENT-676;Lo;0;L;;;;;N;;;;; +18AA4;TANGUT COMPONENT-677;Lo;0;L;;;;;N;;;;; +18AA5;TANGUT COMPONENT-678;Lo;0;L;;;;;N;;;;; +18AA6;TANGUT COMPONENT-679;Lo;0;L;;;;;N;;;;; +18AA7;TANGUT COMPONENT-680;Lo;0;L;;;;;N;;;;; +18AA8;TANGUT COMPONENT-681;Lo;0;L;;;;;N;;;;; +18AA9;TANGUT COMPONENT-682;Lo;0;L;;;;;N;;;;; +18AAA;TANGUT COMPONENT-683;Lo;0;L;;;;;N;;;;; +18AAB;TANGUT COMPONENT-684;Lo;0;L;;;;;N;;;;; +18AAC;TANGUT COMPONENT-685;Lo;0;L;;;;;N;;;;; +18AAD;TANGUT COMPONENT-686;Lo;0;L;;;;;N;;;;; +18AAE;TANGUT COMPONENT-687;Lo;0;L;;;;;N;;;;; +18AAF;TANGUT COMPONENT-688;Lo;0;L;;;;;N;;;;; +18AB0;TANGUT COMPONENT-689;Lo;0;L;;;;;N;;;;; +18AB1;TANGUT COMPONENT-690;Lo;0;L;;;;;N;;;;; +18AB2;TANGUT COMPONENT-691;Lo;0;L;;;;;N;;;;; +18AB3;TANGUT COMPONENT-692;Lo;0;L;;;;;N;;;;; +18AB4;TANGUT COMPONENT-693;Lo;0;L;;;;;N;;;;; +18AB5;TANGUT COMPONENT-694;Lo;0;L;;;;;N;;;;; +18AB6;TANGUT COMPONENT-695;Lo;0;L;;;;;N;;;;; +18AB7;TANGUT COMPONENT-696;Lo;0;L;;;;;N;;;;; +18AB8;TANGUT COMPONENT-697;Lo;0;L;;;;;N;;;;; +18AB9;TANGUT COMPONENT-698;Lo;0;L;;;;;N;;;;; +18ABA;TANGUT COMPONENT-699;Lo;0;L;;;;;N;;;;; +18ABB;TANGUT COMPONENT-700;Lo;0;L;;;;;N;;;;; +18ABC;TANGUT COMPONENT-701;Lo;0;L;;;;;N;;;;; +18ABD;TANGUT COMPONENT-702;Lo;0;L;;;;;N;;;;; +18ABE;TANGUT COMPONENT-703;Lo;0;L;;;;;N;;;;; +18ABF;TANGUT COMPONENT-704;Lo;0;L;;;;;N;;;;; +18AC0;TANGUT COMPONENT-705;Lo;0;L;;;;;N;;;;; +18AC1;TANGUT COMPONENT-706;Lo;0;L;;;;;N;;;;; +18AC2;TANGUT COMPONENT-707;Lo;0;L;;;;;N;;;;; +18AC3;TANGUT COMPONENT-708;Lo;0;L;;;;;N;;;;; +18AC4;TANGUT COMPONENT-709;Lo;0;L;;;;;N;;;;; +18AC5;TANGUT COMPONENT-710;Lo;0;L;;;;;N;;;;; +18AC6;TANGUT COMPONENT-711;Lo;0;L;;;;;N;;;;; +18AC7;TANGUT COMPONENT-712;Lo;0;L;;;;;N;;;;; +18AC8;TANGUT COMPONENT-713;Lo;0;L;;;;;N;;;;; +18AC9;TANGUT COMPONENT-714;Lo;0;L;;;;;N;;;;; +18ACA;TANGUT COMPONENT-715;Lo;0;L;;;;;N;;;;; +18ACB;TANGUT COMPONENT-716;Lo;0;L;;;;;N;;;;; +18ACC;TANGUT COMPONENT-717;Lo;0;L;;;;;N;;;;; +18ACD;TANGUT COMPONENT-718;Lo;0;L;;;;;N;;;;; +18ACE;TANGUT COMPONENT-719;Lo;0;L;;;;;N;;;;; +18ACF;TANGUT COMPONENT-720;Lo;0;L;;;;;N;;;;; +18AD0;TANGUT COMPONENT-721;Lo;0;L;;;;;N;;;;; +18AD1;TANGUT COMPONENT-722;Lo;0;L;;;;;N;;;;; +18AD2;TANGUT COMPONENT-723;Lo;0;L;;;;;N;;;;; +18AD3;TANGUT COMPONENT-724;Lo;0;L;;;;;N;;;;; +18AD4;TANGUT COMPONENT-725;Lo;0;L;;;;;N;;;;; +18AD5;TANGUT COMPONENT-726;Lo;0;L;;;;;N;;;;; +18AD6;TANGUT COMPONENT-727;Lo;0;L;;;;;N;;;;; +18AD7;TANGUT COMPONENT-728;Lo;0;L;;;;;N;;;;; +18AD8;TANGUT COMPONENT-729;Lo;0;L;;;;;N;;;;; +18AD9;TANGUT COMPONENT-730;Lo;0;L;;;;;N;;;;; +18ADA;TANGUT COMPONENT-731;Lo;0;L;;;;;N;;;;; +18ADB;TANGUT COMPONENT-732;Lo;0;L;;;;;N;;;;; +18ADC;TANGUT COMPONENT-733;Lo;0;L;;;;;N;;;;; +18ADD;TANGUT COMPONENT-734;Lo;0;L;;;;;N;;;;; +18ADE;TANGUT COMPONENT-735;Lo;0;L;;;;;N;;;;; +18ADF;TANGUT COMPONENT-736;Lo;0;L;;;;;N;;;;; +18AE0;TANGUT COMPONENT-737;Lo;0;L;;;;;N;;;;; +18AE1;TANGUT COMPONENT-738;Lo;0;L;;;;;N;;;;; +18AE2;TANGUT COMPONENT-739;Lo;0;L;;;;;N;;;;; +18AE3;TANGUT COMPONENT-740;Lo;0;L;;;;;N;;;;; +18AE4;TANGUT COMPONENT-741;Lo;0;L;;;;;N;;;;; +18AE5;TANGUT COMPONENT-742;Lo;0;L;;;;;N;;;;; +18AE6;TANGUT COMPONENT-743;Lo;0;L;;;;;N;;;;; +18AE7;TANGUT COMPONENT-744;Lo;0;L;;;;;N;;;;; +18AE8;TANGUT COMPONENT-745;Lo;0;L;;;;;N;;;;; +18AE9;TANGUT COMPONENT-746;Lo;0;L;;;;;N;;;;; +18AEA;TANGUT COMPONENT-747;Lo;0;L;;;;;N;;;;; +18AEB;TANGUT COMPONENT-748;Lo;0;L;;;;;N;;;;; +18AEC;TANGUT COMPONENT-749;Lo;0;L;;;;;N;;;;; +18AED;TANGUT COMPONENT-750;Lo;0;L;;;;;N;;;;; +18AEE;TANGUT COMPONENT-751;Lo;0;L;;;;;N;;;;; +18AEF;TANGUT COMPONENT-752;Lo;0;L;;;;;N;;;;; +18AF0;TANGUT COMPONENT-753;Lo;0;L;;;;;N;;;;; +18AF1;TANGUT COMPONENT-754;Lo;0;L;;;;;N;;;;; +18AF2;TANGUT COMPONENT-755;Lo;0;L;;;;;N;;;;; +1B000;KATAKANA LETTER ARCHAIC E;Lo;0;L;;;;;N;;;;; +1B001;HIRAGANA LETTER ARCHAIC YE;Lo;0;L;;;;;N;;;;; +1BC00;DUPLOYAN LETTER H;Lo;0;L;;;;;N;;;;; +1BC01;DUPLOYAN LETTER X;Lo;0;L;;;;;N;;;;; +1BC02;DUPLOYAN LETTER P;Lo;0;L;;;;;N;;;;; +1BC03;DUPLOYAN LETTER T;Lo;0;L;;;;;N;;;;; +1BC04;DUPLOYAN LETTER F;Lo;0;L;;;;;N;;;;; +1BC05;DUPLOYAN LETTER K;Lo;0;L;;;;;N;;;;; +1BC06;DUPLOYAN LETTER L;Lo;0;L;;;;;N;;;;; +1BC07;DUPLOYAN LETTER B;Lo;0;L;;;;;N;;;;; +1BC08;DUPLOYAN LETTER D;Lo;0;L;;;;;N;;;;; +1BC09;DUPLOYAN LETTER V;Lo;0;L;;;;;N;;;;; +1BC0A;DUPLOYAN LETTER G;Lo;0;L;;;;;N;;;;; +1BC0B;DUPLOYAN LETTER R;Lo;0;L;;;;;N;;;;; +1BC0C;DUPLOYAN LETTER P N;Lo;0;L;;;;;N;;;;; +1BC0D;DUPLOYAN LETTER D S;Lo;0;L;;;;;N;;;;; +1BC0E;DUPLOYAN LETTER F N;Lo;0;L;;;;;N;;;;; +1BC0F;DUPLOYAN LETTER K M;Lo;0;L;;;;;N;;;;; +1BC10;DUPLOYAN LETTER R S;Lo;0;L;;;;;N;;;;; +1BC11;DUPLOYAN LETTER TH;Lo;0;L;;;;;N;;;;; +1BC12;DUPLOYAN LETTER SLOAN DH;Lo;0;L;;;;;N;;;;; +1BC13;DUPLOYAN LETTER DH;Lo;0;L;;;;;N;;;;; +1BC14;DUPLOYAN LETTER KK;Lo;0;L;;;;;N;;;;; +1BC15;DUPLOYAN LETTER SLOAN J;Lo;0;L;;;;;N;;;;; +1BC16;DUPLOYAN LETTER HL;Lo;0;L;;;;;N;;;;; +1BC17;DUPLOYAN LETTER LH;Lo;0;L;;;;;N;;;;; +1BC18;DUPLOYAN LETTER RH;Lo;0;L;;;;;N;;;;; +1BC19;DUPLOYAN LETTER M;Lo;0;L;;;;;N;;;;; +1BC1A;DUPLOYAN LETTER N;Lo;0;L;;;;;N;;;;; +1BC1B;DUPLOYAN LETTER J;Lo;0;L;;;;;N;;;;; +1BC1C;DUPLOYAN LETTER S;Lo;0;L;;;;;N;;;;; +1BC1D;DUPLOYAN LETTER M N;Lo;0;L;;;;;N;;;;; +1BC1E;DUPLOYAN LETTER N M;Lo;0;L;;;;;N;;;;; +1BC1F;DUPLOYAN LETTER J M;Lo;0;L;;;;;N;;;;; +1BC20;DUPLOYAN LETTER S J;Lo;0;L;;;;;N;;;;; +1BC21;DUPLOYAN LETTER M WITH DOT;Lo;0;L;;;;;N;;;;; +1BC22;DUPLOYAN LETTER N WITH DOT;Lo;0;L;;;;;N;;;;; +1BC23;DUPLOYAN LETTER J WITH DOT;Lo;0;L;;;;;N;;;;; +1BC24;DUPLOYAN LETTER J WITH DOTS INSIDE AND ABOVE;Lo;0;L;;;;;N;;;;; +1BC25;DUPLOYAN LETTER S WITH DOT;Lo;0;L;;;;;N;;;;; +1BC26;DUPLOYAN LETTER S WITH DOT BELOW;Lo;0;L;;;;;N;;;;; +1BC27;DUPLOYAN LETTER M S;Lo;0;L;;;;;N;;;;; +1BC28;DUPLOYAN LETTER N S;Lo;0;L;;;;;N;;;;; +1BC29;DUPLOYAN LETTER J S;Lo;0;L;;;;;N;;;;; +1BC2A;DUPLOYAN LETTER S S;Lo;0;L;;;;;N;;;;; +1BC2B;DUPLOYAN LETTER M N S;Lo;0;L;;;;;N;;;;; +1BC2C;DUPLOYAN LETTER N M S;Lo;0;L;;;;;N;;;;; +1BC2D;DUPLOYAN LETTER J M S;Lo;0;L;;;;;N;;;;; +1BC2E;DUPLOYAN LETTER S J S;Lo;0;L;;;;;N;;;;; +1BC2F;DUPLOYAN LETTER J S WITH DOT;Lo;0;L;;;;;N;;;;; +1BC30;DUPLOYAN LETTER J N;Lo;0;L;;;;;N;;;;; +1BC31;DUPLOYAN LETTER J N S;Lo;0;L;;;;;N;;;;; +1BC32;DUPLOYAN LETTER S T;Lo;0;L;;;;;N;;;;; +1BC33;DUPLOYAN LETTER S T R;Lo;0;L;;;;;N;;;;; +1BC34;DUPLOYAN LETTER S P;Lo;0;L;;;;;N;;;;; +1BC35;DUPLOYAN LETTER S P R;Lo;0;L;;;;;N;;;;; +1BC36;DUPLOYAN LETTER T S;Lo;0;L;;;;;N;;;;; +1BC37;DUPLOYAN LETTER T R S;Lo;0;L;;;;;N;;;;; +1BC38;DUPLOYAN LETTER W;Lo;0;L;;;;;N;;;;; +1BC39;DUPLOYAN LETTER WH;Lo;0;L;;;;;N;;;;; +1BC3A;DUPLOYAN LETTER W R;Lo;0;L;;;;;N;;;;; +1BC3B;DUPLOYAN LETTER S N;Lo;0;L;;;;;N;;;;; +1BC3C;DUPLOYAN LETTER S M;Lo;0;L;;;;;N;;;;; +1BC3D;DUPLOYAN LETTER K R S;Lo;0;L;;;;;N;;;;; +1BC3E;DUPLOYAN LETTER G R S;Lo;0;L;;;;;N;;;;; +1BC3F;DUPLOYAN LETTER S K;Lo;0;L;;;;;N;;;;; +1BC40;DUPLOYAN LETTER S K R;Lo;0;L;;;;;N;;;;; +1BC41;DUPLOYAN LETTER A;Lo;0;L;;;;;N;;;;; +1BC42;DUPLOYAN LETTER SLOAN OW;Lo;0;L;;;;;N;;;;; +1BC43;DUPLOYAN LETTER OA;Lo;0;L;;;;;N;;;;; +1BC44;DUPLOYAN LETTER O;Lo;0;L;;;;;N;;;;; +1BC45;DUPLOYAN LETTER AOU;Lo;0;L;;;;;N;;;;; +1BC46;DUPLOYAN LETTER I;Lo;0;L;;;;;N;;;;; +1BC47;DUPLOYAN LETTER E;Lo;0;L;;;;;N;;;;; +1BC48;DUPLOYAN LETTER IE;Lo;0;L;;;;;N;;;;; +1BC49;DUPLOYAN LETTER SHORT I;Lo;0;L;;;;;N;;;;; +1BC4A;DUPLOYAN LETTER UI;Lo;0;L;;;;;N;;;;; +1BC4B;DUPLOYAN LETTER EE;Lo;0;L;;;;;N;;;;; +1BC4C;DUPLOYAN LETTER SLOAN EH;Lo;0;L;;;;;N;;;;; +1BC4D;DUPLOYAN LETTER ROMANIAN I;Lo;0;L;;;;;N;;;;; +1BC4E;DUPLOYAN LETTER SLOAN EE;Lo;0;L;;;;;N;;;;; +1BC4F;DUPLOYAN LETTER LONG I;Lo;0;L;;;;;N;;;;; +1BC50;DUPLOYAN LETTER YE;Lo;0;L;;;;;N;;;;; +1BC51;DUPLOYAN LETTER U;Lo;0;L;;;;;N;;;;; +1BC52;DUPLOYAN LETTER EU;Lo;0;L;;;;;N;;;;; +1BC53;DUPLOYAN LETTER XW;Lo;0;L;;;;;N;;;;; +1BC54;DUPLOYAN LETTER U N;Lo;0;L;;;;;N;;;;; +1BC55;DUPLOYAN LETTER LONG U;Lo;0;L;;;;;N;;;;; +1BC56;DUPLOYAN LETTER ROMANIAN U;Lo;0;L;;;;;N;;;;; +1BC57;DUPLOYAN LETTER UH;Lo;0;L;;;;;N;;;;; +1BC58;DUPLOYAN LETTER SLOAN U;Lo;0;L;;;;;N;;;;; +1BC59;DUPLOYAN LETTER OOH;Lo;0;L;;;;;N;;;;; +1BC5A;DUPLOYAN LETTER OW;Lo;0;L;;;;;N;;;;; +1BC5B;DUPLOYAN LETTER OU;Lo;0;L;;;;;N;;;;; +1BC5C;DUPLOYAN LETTER WA;Lo;0;L;;;;;N;;;;; +1BC5D;DUPLOYAN LETTER WO;Lo;0;L;;;;;N;;;;; +1BC5E;DUPLOYAN LETTER WI;Lo;0;L;;;;;N;;;;; +1BC5F;DUPLOYAN LETTER WEI;Lo;0;L;;;;;N;;;;; +1BC60;DUPLOYAN LETTER WOW;Lo;0;L;;;;;N;;;;; +1BC61;DUPLOYAN LETTER NASAL U;Lo;0;L;;;;;N;;;;; +1BC62;DUPLOYAN LETTER NASAL O;Lo;0;L;;;;;N;;;;; +1BC63;DUPLOYAN LETTER NASAL I;Lo;0;L;;;;;N;;;;; +1BC64;DUPLOYAN LETTER NASAL A;Lo;0;L;;;;;N;;;;; +1BC65;DUPLOYAN LETTER PERNIN AN;Lo;0;L;;;;;N;;;;; +1BC66;DUPLOYAN LETTER PERNIN AM;Lo;0;L;;;;;N;;;;; +1BC67;DUPLOYAN LETTER SLOAN EN;Lo;0;L;;;;;N;;;;; +1BC68;DUPLOYAN LETTER SLOAN AN;Lo;0;L;;;;;N;;;;; +1BC69;DUPLOYAN LETTER SLOAN ON;Lo;0;L;;;;;N;;;;; +1BC6A;DUPLOYAN LETTER VOCALIC M;Lo;0;L;;;;;N;;;;; +1BC70;DUPLOYAN AFFIX LEFT HORIZONTAL SECANT;Lo;0;L;;;;;N;;;;; +1BC71;DUPLOYAN AFFIX MID HORIZONTAL SECANT;Lo;0;L;;;;;N;;;;; +1BC72;DUPLOYAN AFFIX RIGHT HORIZONTAL SECANT;Lo;0;L;;;;;N;;;;; +1BC73;DUPLOYAN AFFIX LOW VERTICAL SECANT;Lo;0;L;;;;;N;;;;; +1BC74;DUPLOYAN AFFIX MID VERTICAL SECANT;Lo;0;L;;;;;N;;;;; +1BC75;DUPLOYAN AFFIX HIGH VERTICAL SECANT;Lo;0;L;;;;;N;;;;; +1BC76;DUPLOYAN AFFIX ATTACHED SECANT;Lo;0;L;;;;;N;;;;; +1BC77;DUPLOYAN AFFIX ATTACHED LEFT-TO-RIGHT SECANT;Lo;0;L;;;;;N;;;;; +1BC78;DUPLOYAN AFFIX ATTACHED TANGENT;Lo;0;L;;;;;N;;;;; +1BC79;DUPLOYAN AFFIX ATTACHED TAIL;Lo;0;L;;;;;N;;;;; +1BC7A;DUPLOYAN AFFIX ATTACHED E HOOK;Lo;0;L;;;;;N;;;;; +1BC7B;DUPLOYAN AFFIX ATTACHED I HOOK;Lo;0;L;;;;;N;;;;; +1BC7C;DUPLOYAN AFFIX ATTACHED TANGENT HOOK;Lo;0;L;;;;;N;;;;; +1BC80;DUPLOYAN AFFIX HIGH ACUTE;Lo;0;L;;;;;N;;;;; +1BC81;DUPLOYAN AFFIX HIGH TIGHT ACUTE;Lo;0;L;;;;;N;;;;; +1BC82;DUPLOYAN AFFIX HIGH GRAVE;Lo;0;L;;;;;N;;;;; +1BC83;DUPLOYAN AFFIX HIGH LONG GRAVE;Lo;0;L;;;;;N;;;;; +1BC84;DUPLOYAN AFFIX HIGH DOT;Lo;0;L;;;;;N;;;;; +1BC85;DUPLOYAN AFFIX HIGH CIRCLE;Lo;0;L;;;;;N;;;;; +1BC86;DUPLOYAN AFFIX HIGH LINE;Lo;0;L;;;;;N;;;;; +1BC87;DUPLOYAN AFFIX HIGH WAVE;Lo;0;L;;;;;N;;;;; +1BC88;DUPLOYAN AFFIX HIGH VERTICAL;Lo;0;L;;;;;N;;;;; +1BC90;DUPLOYAN AFFIX LOW ACUTE;Lo;0;L;;;;;N;;;;; +1BC91;DUPLOYAN AFFIX LOW TIGHT ACUTE;Lo;0;L;;;;;N;;;;; +1BC92;DUPLOYAN AFFIX LOW GRAVE;Lo;0;L;;;;;N;;;;; +1BC93;DUPLOYAN AFFIX LOW LONG GRAVE;Lo;0;L;;;;;N;;;;; +1BC94;DUPLOYAN AFFIX LOW DOT;Lo;0;L;;;;;N;;;;; +1BC95;DUPLOYAN AFFIX LOW CIRCLE;Lo;0;L;;;;;N;;;;; +1BC96;DUPLOYAN AFFIX LOW LINE;Lo;0;L;;;;;N;;;;; +1BC97;DUPLOYAN AFFIX LOW WAVE;Lo;0;L;;;;;N;;;;; +1BC98;DUPLOYAN AFFIX LOW VERTICAL;Lo;0;L;;;;;N;;;;; +1BC99;DUPLOYAN AFFIX LOW ARROW;Lo;0;L;;;;;N;;;;; +1BC9C;DUPLOYAN SIGN O WITH CROSS;So;0;L;;;;;N;;;;; +1BC9D;DUPLOYAN THICK LETTER SELECTOR;Mn;0;NSM;;;;;N;;;;; +1BC9E;DUPLOYAN DOUBLE MARK;Mn;1;NSM;;;;;N;;;;; +1BC9F;DUPLOYAN PUNCTUATION CHINOOK FULL STOP;Po;0;L;;;;;N;;;;; +1BCA0;SHORTHAND FORMAT LETTER OVERLAP;Cf;0;BN;;;;;N;;;;; +1BCA1;SHORTHAND FORMAT CONTINUING OVERLAP;Cf;0;BN;;;;;N;;;;; +1BCA2;SHORTHAND FORMAT DOWN STEP;Cf;0;BN;;;;;N;;;;; +1BCA3;SHORTHAND FORMAT UP STEP;Cf;0;BN;;;;;N;;;;; +1D000;BYZANTINE MUSICAL SYMBOL PSILI;So;0;L;;;;;N;;;;; +1D001;BYZANTINE MUSICAL SYMBOL DASEIA;So;0;L;;;;;N;;;;; +1D002;BYZANTINE MUSICAL SYMBOL PERISPOMENI;So;0;L;;;;;N;;;;; +1D003;BYZANTINE MUSICAL SYMBOL OXEIA EKFONITIKON;So;0;L;;;;;N;;;;; +1D004;BYZANTINE MUSICAL SYMBOL OXEIA DIPLI;So;0;L;;;;;N;;;;; +1D005;BYZANTINE MUSICAL SYMBOL VAREIA EKFONITIKON;So;0;L;;;;;N;;;;; +1D006;BYZANTINE MUSICAL SYMBOL VAREIA DIPLI;So;0;L;;;;;N;;;;; +1D007;BYZANTINE MUSICAL SYMBOL KATHISTI;So;0;L;;;;;N;;;;; +1D008;BYZANTINE MUSICAL SYMBOL SYRMATIKI;So;0;L;;;;;N;;;;; +1D009;BYZANTINE MUSICAL SYMBOL PARAKLITIKI;So;0;L;;;;;N;;;;; +1D00A;BYZANTINE MUSICAL SYMBOL YPOKRISIS;So;0;L;;;;;N;;;;; +1D00B;BYZANTINE MUSICAL SYMBOL YPOKRISIS DIPLI;So;0;L;;;;;N;;;;; +1D00C;BYZANTINE MUSICAL SYMBOL KREMASTI;So;0;L;;;;;N;;;;; +1D00D;BYZANTINE MUSICAL SYMBOL APESO EKFONITIKON;So;0;L;;;;;N;;;;; +1D00E;BYZANTINE MUSICAL SYMBOL EXO EKFONITIKON;So;0;L;;;;;N;;;;; +1D00F;BYZANTINE MUSICAL SYMBOL TELEIA;So;0;L;;;;;N;;;;; +1D010;BYZANTINE MUSICAL SYMBOL KENTIMATA;So;0;L;;;;;N;;;;; +1D011;BYZANTINE MUSICAL SYMBOL APOSTROFOS;So;0;L;;;;;N;;;;; +1D012;BYZANTINE MUSICAL SYMBOL APOSTROFOS DIPLI;So;0;L;;;;;N;;;;; +1D013;BYZANTINE MUSICAL SYMBOL SYNEVMA;So;0;L;;;;;N;;;;; +1D014;BYZANTINE MUSICAL SYMBOL THITA;So;0;L;;;;;N;;;;; +1D015;BYZANTINE MUSICAL SYMBOL OLIGON ARCHAION;So;0;L;;;;;N;;;;; +1D016;BYZANTINE MUSICAL SYMBOL GORGON ARCHAION;So;0;L;;;;;N;;;;; +1D017;BYZANTINE MUSICAL SYMBOL PSILON;So;0;L;;;;;N;;;;; +1D018;BYZANTINE MUSICAL SYMBOL CHAMILON;So;0;L;;;;;N;;;;; +1D019;BYZANTINE MUSICAL SYMBOL VATHY;So;0;L;;;;;N;;;;; +1D01A;BYZANTINE MUSICAL SYMBOL ISON ARCHAION;So;0;L;;;;;N;;;;; +1D01B;BYZANTINE MUSICAL SYMBOL KENTIMA ARCHAION;So;0;L;;;;;N;;;;; +1D01C;BYZANTINE MUSICAL SYMBOL KENTIMATA ARCHAION;So;0;L;;;;;N;;;;; +1D01D;BYZANTINE MUSICAL SYMBOL SAXIMATA;So;0;L;;;;;N;;;;; +1D01E;BYZANTINE MUSICAL SYMBOL PARICHON;So;0;L;;;;;N;;;;; +1D01F;BYZANTINE MUSICAL SYMBOL STAVROS APODEXIA;So;0;L;;;;;N;;;;; +1D020;BYZANTINE MUSICAL SYMBOL OXEIAI ARCHAION;So;0;L;;;;;N;;;;; +1D021;BYZANTINE MUSICAL SYMBOL VAREIAI ARCHAION;So;0;L;;;;;N;;;;; +1D022;BYZANTINE MUSICAL SYMBOL APODERMA ARCHAION;So;0;L;;;;;N;;;;; +1D023;BYZANTINE MUSICAL SYMBOL APOTHEMA;So;0;L;;;;;N;;;;; +1D024;BYZANTINE MUSICAL SYMBOL KLASMA;So;0;L;;;;;N;;;;; +1D025;BYZANTINE MUSICAL SYMBOL REVMA;So;0;L;;;;;N;;;;; +1D026;BYZANTINE MUSICAL SYMBOL PIASMA ARCHAION;So;0;L;;;;;N;;;;; +1D027;BYZANTINE MUSICAL SYMBOL TINAGMA;So;0;L;;;;;N;;;;; +1D028;BYZANTINE MUSICAL SYMBOL ANATRICHISMA;So;0;L;;;;;N;;;;; +1D029;BYZANTINE MUSICAL SYMBOL SEISMA;So;0;L;;;;;N;;;;; +1D02A;BYZANTINE MUSICAL SYMBOL SYNAGMA ARCHAION;So;0;L;;;;;N;;;;; +1D02B;BYZANTINE MUSICAL SYMBOL SYNAGMA META STAVROU;So;0;L;;;;;N;;;;; +1D02C;BYZANTINE MUSICAL SYMBOL OYRANISMA ARCHAION;So;0;L;;;;;N;;;;; +1D02D;BYZANTINE MUSICAL SYMBOL THEMA;So;0;L;;;;;N;;;;; +1D02E;BYZANTINE MUSICAL SYMBOL LEMOI;So;0;L;;;;;N;;;;; +1D02F;BYZANTINE MUSICAL SYMBOL DYO;So;0;L;;;;;N;;;;; +1D030;BYZANTINE MUSICAL SYMBOL TRIA;So;0;L;;;;;N;;;;; +1D031;BYZANTINE MUSICAL SYMBOL TESSERA;So;0;L;;;;;N;;;;; +1D032;BYZANTINE MUSICAL SYMBOL KRATIMATA;So;0;L;;;;;N;;;;; +1D033;BYZANTINE MUSICAL SYMBOL APESO EXO NEO;So;0;L;;;;;N;;;;; +1D034;BYZANTINE MUSICAL SYMBOL FTHORA ARCHAION;So;0;L;;;;;N;;;;; +1D035;BYZANTINE MUSICAL SYMBOL IMIFTHORA;So;0;L;;;;;N;;;;; +1D036;BYZANTINE MUSICAL SYMBOL TROMIKON ARCHAION;So;0;L;;;;;N;;;;; +1D037;BYZANTINE MUSICAL SYMBOL KATAVA TROMIKON;So;0;L;;;;;N;;;;; +1D038;BYZANTINE MUSICAL SYMBOL PELASTON;So;0;L;;;;;N;;;;; +1D039;BYZANTINE MUSICAL SYMBOL PSIFISTON;So;0;L;;;;;N;;;;; +1D03A;BYZANTINE MUSICAL SYMBOL KONTEVMA;So;0;L;;;;;N;;;;; +1D03B;BYZANTINE MUSICAL SYMBOL CHOREVMA ARCHAION;So;0;L;;;;;N;;;;; +1D03C;BYZANTINE MUSICAL SYMBOL RAPISMA;So;0;L;;;;;N;;;;; +1D03D;BYZANTINE MUSICAL SYMBOL PARAKALESMA ARCHAION;So;0;L;;;;;N;;;;; +1D03E;BYZANTINE MUSICAL SYMBOL PARAKLITIKI ARCHAION;So;0;L;;;;;N;;;;; +1D03F;BYZANTINE MUSICAL SYMBOL ICHADIN;So;0;L;;;;;N;;;;; +1D040;BYZANTINE MUSICAL SYMBOL NANA;So;0;L;;;;;N;;;;; +1D041;BYZANTINE MUSICAL SYMBOL PETASMA;So;0;L;;;;;N;;;;; +1D042;BYZANTINE MUSICAL SYMBOL KONTEVMA ALLO;So;0;L;;;;;N;;;;; +1D043;BYZANTINE MUSICAL SYMBOL TROMIKON ALLO;So;0;L;;;;;N;;;;; +1D044;BYZANTINE MUSICAL SYMBOL STRAGGISMATA;So;0;L;;;;;N;;;;; +1D045;BYZANTINE MUSICAL SYMBOL GRONTHISMATA;So;0;L;;;;;N;;;;; +1D046;BYZANTINE MUSICAL SYMBOL ISON NEO;So;0;L;;;;;N;;;;; +1D047;BYZANTINE MUSICAL SYMBOL OLIGON NEO;So;0;L;;;;;N;;;;; +1D048;BYZANTINE MUSICAL SYMBOL OXEIA NEO;So;0;L;;;;;N;;;;; +1D049;BYZANTINE MUSICAL SYMBOL PETASTI;So;0;L;;;;;N;;;;; +1D04A;BYZANTINE MUSICAL SYMBOL KOUFISMA;So;0;L;;;;;N;;;;; +1D04B;BYZANTINE MUSICAL SYMBOL PETASTOKOUFISMA;So;0;L;;;;;N;;;;; +1D04C;BYZANTINE MUSICAL SYMBOL KRATIMOKOUFISMA;So;0;L;;;;;N;;;;; +1D04D;BYZANTINE MUSICAL SYMBOL PELASTON NEO;So;0;L;;;;;N;;;;; +1D04E;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO ANO;So;0;L;;;;;N;;;;; +1D04F;BYZANTINE MUSICAL SYMBOL KENTIMA NEO ANO;So;0;L;;;;;N;;;;; +1D050;BYZANTINE MUSICAL SYMBOL YPSILI;So;0;L;;;;;N;;;;; +1D051;BYZANTINE MUSICAL SYMBOL APOSTROFOS NEO;So;0;L;;;;;N;;;;; +1D052;BYZANTINE MUSICAL SYMBOL APOSTROFOI SYNDESMOS NEO;So;0;L;;;;;N;;;;; +1D053;BYZANTINE MUSICAL SYMBOL YPORROI;So;0;L;;;;;N;;;;; +1D054;BYZANTINE MUSICAL SYMBOL KRATIMOYPORROON;So;0;L;;;;;N;;;;; +1D055;BYZANTINE MUSICAL SYMBOL ELAFRON;So;0;L;;;;;N;;;;; +1D056;BYZANTINE MUSICAL SYMBOL CHAMILI;So;0;L;;;;;N;;;;; +1D057;BYZANTINE MUSICAL SYMBOL MIKRON ISON;So;0;L;;;;;N;;;;; +1D058;BYZANTINE MUSICAL SYMBOL VAREIA NEO;So;0;L;;;;;N;;;;; +1D059;BYZANTINE MUSICAL SYMBOL PIASMA NEO;So;0;L;;;;;N;;;;; +1D05A;BYZANTINE MUSICAL SYMBOL PSIFISTON NEO;So;0;L;;;;;N;;;;; +1D05B;BYZANTINE MUSICAL SYMBOL OMALON;So;0;L;;;;;N;;;;; +1D05C;BYZANTINE MUSICAL SYMBOL ANTIKENOMA;So;0;L;;;;;N;;;;; +1D05D;BYZANTINE MUSICAL SYMBOL LYGISMA;So;0;L;;;;;N;;;;; +1D05E;BYZANTINE MUSICAL SYMBOL PARAKLITIKI NEO;So;0;L;;;;;N;;;;; +1D05F;BYZANTINE MUSICAL SYMBOL PARAKALESMA NEO;So;0;L;;;;;N;;;;; +1D060;BYZANTINE MUSICAL SYMBOL ETERON PARAKALESMA;So;0;L;;;;;N;;;;; +1D061;BYZANTINE MUSICAL SYMBOL KYLISMA;So;0;L;;;;;N;;;;; +1D062;BYZANTINE MUSICAL SYMBOL ANTIKENOKYLISMA;So;0;L;;;;;N;;;;; +1D063;BYZANTINE MUSICAL SYMBOL TROMIKON NEO;So;0;L;;;;;N;;;;; +1D064;BYZANTINE MUSICAL SYMBOL EKSTREPTON;So;0;L;;;;;N;;;;; +1D065;BYZANTINE MUSICAL SYMBOL SYNAGMA NEO;So;0;L;;;;;N;;;;; +1D066;BYZANTINE MUSICAL SYMBOL SYRMA;So;0;L;;;;;N;;;;; +1D067;BYZANTINE MUSICAL SYMBOL CHOREVMA NEO;So;0;L;;;;;N;;;;; +1D068;BYZANTINE MUSICAL SYMBOL EPEGERMA;So;0;L;;;;;N;;;;; +1D069;BYZANTINE MUSICAL SYMBOL SEISMA NEO;So;0;L;;;;;N;;;;; +1D06A;BYZANTINE MUSICAL SYMBOL XIRON KLASMA;So;0;L;;;;;N;;;;; +1D06B;BYZANTINE MUSICAL SYMBOL TROMIKOPSIFISTON;So;0;L;;;;;N;;;;; +1D06C;BYZANTINE MUSICAL SYMBOL PSIFISTOLYGISMA;So;0;L;;;;;N;;;;; +1D06D;BYZANTINE MUSICAL SYMBOL TROMIKOLYGISMA;So;0;L;;;;;N;;;;; +1D06E;BYZANTINE MUSICAL SYMBOL TROMIKOPARAKALESMA;So;0;L;;;;;N;;;;; +1D06F;BYZANTINE MUSICAL SYMBOL PSIFISTOPARAKALESMA;So;0;L;;;;;N;;;;; +1D070;BYZANTINE MUSICAL SYMBOL TROMIKOSYNAGMA;So;0;L;;;;;N;;;;; +1D071;BYZANTINE MUSICAL SYMBOL PSIFISTOSYNAGMA;So;0;L;;;;;N;;;;; +1D072;BYZANTINE MUSICAL SYMBOL GORGOSYNTHETON;So;0;L;;;;;N;;;;; +1D073;BYZANTINE MUSICAL SYMBOL ARGOSYNTHETON;So;0;L;;;;;N;;;;; +1D074;BYZANTINE MUSICAL SYMBOL ETERON ARGOSYNTHETON;So;0;L;;;;;N;;;;; +1D075;BYZANTINE MUSICAL SYMBOL OYRANISMA NEO;So;0;L;;;;;N;;;;; +1D076;BYZANTINE MUSICAL SYMBOL THEMATISMOS ESO;So;0;L;;;;;N;;;;; +1D077;BYZANTINE MUSICAL SYMBOL THEMATISMOS EXO;So;0;L;;;;;N;;;;; +1D078;BYZANTINE MUSICAL SYMBOL THEMA APLOUN;So;0;L;;;;;N;;;;; +1D079;BYZANTINE MUSICAL SYMBOL THES KAI APOTHES;So;0;L;;;;;N;;;;; +1D07A;BYZANTINE MUSICAL SYMBOL KATAVASMA;So;0;L;;;;;N;;;;; +1D07B;BYZANTINE MUSICAL SYMBOL ENDOFONON;So;0;L;;;;;N;;;;; +1D07C;BYZANTINE MUSICAL SYMBOL YFEN KATO;So;0;L;;;;;N;;;;; +1D07D;BYZANTINE MUSICAL SYMBOL YFEN ANO;So;0;L;;;;;N;;;;; +1D07E;BYZANTINE MUSICAL SYMBOL STAVROS;So;0;L;;;;;N;;;;; +1D07F;BYZANTINE MUSICAL SYMBOL KLASMA ANO;So;0;L;;;;;N;;;;; +1D080;BYZANTINE MUSICAL SYMBOL DIPLI ARCHAION;So;0;L;;;;;N;;;;; +1D081;BYZANTINE MUSICAL SYMBOL KRATIMA ARCHAION;So;0;L;;;;;N;;;;; +1D082;BYZANTINE MUSICAL SYMBOL KRATIMA ALLO;So;0;L;;;;;N;;;;; +1D083;BYZANTINE MUSICAL SYMBOL KRATIMA NEO;So;0;L;;;;;N;;;;; +1D084;BYZANTINE MUSICAL SYMBOL APODERMA NEO;So;0;L;;;;;N;;;;; +1D085;BYZANTINE MUSICAL SYMBOL APLI;So;0;L;;;;;N;;;;; +1D086;BYZANTINE MUSICAL SYMBOL DIPLI;So;0;L;;;;;N;;;;; +1D087;BYZANTINE MUSICAL SYMBOL TRIPLI;So;0;L;;;;;N;;;;; +1D088;BYZANTINE MUSICAL SYMBOL TETRAPLI;So;0;L;;;;;N;;;;; +1D089;BYZANTINE MUSICAL SYMBOL KORONIS;So;0;L;;;;;N;;;;; +1D08A;BYZANTINE MUSICAL SYMBOL LEIMMA ENOS CHRONOU;So;0;L;;;;;N;;;;; +1D08B;BYZANTINE MUSICAL SYMBOL LEIMMA DYO CHRONON;So;0;L;;;;;N;;;;; +1D08C;BYZANTINE MUSICAL SYMBOL LEIMMA TRION CHRONON;So;0;L;;;;;N;;;;; +1D08D;BYZANTINE MUSICAL SYMBOL LEIMMA TESSARON CHRONON;So;0;L;;;;;N;;;;; +1D08E;BYZANTINE MUSICAL SYMBOL LEIMMA IMISEOS CHRONOU;So;0;L;;;;;N;;;;; +1D08F;BYZANTINE MUSICAL SYMBOL GORGON NEO ANO;So;0;L;;;;;N;;;;; +1D090;BYZANTINE MUSICAL SYMBOL GORGON PARESTIGMENON ARISTERA;So;0;L;;;;;N;;;;; +1D091;BYZANTINE MUSICAL SYMBOL GORGON PARESTIGMENON DEXIA;So;0;L;;;;;N;;;;; +1D092;BYZANTINE MUSICAL SYMBOL DIGORGON;So;0;L;;;;;N;;;;; +1D093;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON ARISTERA KATO;So;0;L;;;;;N;;;;; +1D094;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON ARISTERA ANO;So;0;L;;;;;N;;;;; +1D095;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON DEXIA;So;0;L;;;;;N;;;;; +1D096;BYZANTINE MUSICAL SYMBOL TRIGORGON;So;0;L;;;;;N;;;;; +1D097;BYZANTINE MUSICAL SYMBOL ARGON;So;0;L;;;;;N;;;;; +1D098;BYZANTINE MUSICAL SYMBOL IMIDIARGON;So;0;L;;;;;N;;;;; +1D099;BYZANTINE MUSICAL SYMBOL DIARGON;So;0;L;;;;;N;;;;; +1D09A;BYZANTINE MUSICAL SYMBOL AGOGI POLI ARGI;So;0;L;;;;;N;;;;; +1D09B;BYZANTINE MUSICAL SYMBOL AGOGI ARGOTERI;So;0;L;;;;;N;;;;; +1D09C;BYZANTINE MUSICAL SYMBOL AGOGI ARGI;So;0;L;;;;;N;;;;; +1D09D;BYZANTINE MUSICAL SYMBOL AGOGI METRIA;So;0;L;;;;;N;;;;; +1D09E;BYZANTINE MUSICAL SYMBOL AGOGI MESI;So;0;L;;;;;N;;;;; +1D09F;BYZANTINE MUSICAL SYMBOL AGOGI GORGI;So;0;L;;;;;N;;;;; +1D0A0;BYZANTINE MUSICAL SYMBOL AGOGI GORGOTERI;So;0;L;;;;;N;;;;; +1D0A1;BYZANTINE MUSICAL SYMBOL AGOGI POLI GORGI;So;0;L;;;;;N;;;;; +1D0A2;BYZANTINE MUSICAL SYMBOL MARTYRIA PROTOS ICHOS;So;0;L;;;;;N;;;;; +1D0A3;BYZANTINE MUSICAL SYMBOL MARTYRIA ALLI PROTOS ICHOS;So;0;L;;;;;N;;;;; +1D0A4;BYZANTINE MUSICAL SYMBOL MARTYRIA DEYTEROS ICHOS;So;0;L;;;;;N;;;;; +1D0A5;BYZANTINE MUSICAL SYMBOL MARTYRIA ALLI DEYTEROS ICHOS;So;0;L;;;;;N;;;;; +1D0A6;BYZANTINE MUSICAL SYMBOL MARTYRIA TRITOS ICHOS;So;0;L;;;;;N;;;;; +1D0A7;BYZANTINE MUSICAL SYMBOL MARTYRIA TRIFONIAS;So;0;L;;;;;N;;;;; +1D0A8;BYZANTINE MUSICAL SYMBOL MARTYRIA TETARTOS ICHOS;So;0;L;;;;;N;;;;; +1D0A9;BYZANTINE MUSICAL SYMBOL MARTYRIA TETARTOS LEGETOS ICHOS;So;0;L;;;;;N;;;;; +1D0AA;BYZANTINE MUSICAL SYMBOL MARTYRIA LEGETOS ICHOS;So;0;L;;;;;N;;;;; +1D0AB;BYZANTINE MUSICAL SYMBOL MARTYRIA PLAGIOS ICHOS;So;0;L;;;;;N;;;;; +1D0AC;BYZANTINE MUSICAL SYMBOL ISAKIA TELOUS ICHIMATOS;So;0;L;;;;;N;;;;; +1D0AD;BYZANTINE MUSICAL SYMBOL APOSTROFOI TELOUS ICHIMATOS;So;0;L;;;;;N;;;;; +1D0AE;BYZANTINE MUSICAL SYMBOL FANEROSIS TETRAFONIAS;So;0;L;;;;;N;;;;; +1D0AF;BYZANTINE MUSICAL SYMBOL FANEROSIS MONOFONIAS;So;0;L;;;;;N;;;;; +1D0B0;BYZANTINE MUSICAL SYMBOL FANEROSIS DIFONIAS;So;0;L;;;;;N;;;;; +1D0B1;BYZANTINE MUSICAL SYMBOL MARTYRIA VARYS ICHOS;So;0;L;;;;;N;;;;; +1D0B2;BYZANTINE MUSICAL SYMBOL MARTYRIA PROTOVARYS ICHOS;So;0;L;;;;;N;;;;; +1D0B3;BYZANTINE MUSICAL SYMBOL MARTYRIA PLAGIOS TETARTOS ICHOS;So;0;L;;;;;N;;;;; +1D0B4;BYZANTINE MUSICAL SYMBOL GORTHMIKON N APLOUN;So;0;L;;;;;N;;;;; +1D0B5;BYZANTINE MUSICAL SYMBOL GORTHMIKON N DIPLOUN;So;0;L;;;;;N;;;;; +1D0B6;BYZANTINE MUSICAL SYMBOL ENARXIS KAI FTHORA VOU;So;0;L;;;;;N;;;;; +1D0B7;BYZANTINE MUSICAL SYMBOL IMIFONON;So;0;L;;;;;N;;;;; +1D0B8;BYZANTINE MUSICAL SYMBOL IMIFTHORON;So;0;L;;;;;N;;;;; +1D0B9;BYZANTINE MUSICAL SYMBOL FTHORA ARCHAION DEYTEROU ICHOU;So;0;L;;;;;N;;;;; +1D0BA;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI PA;So;0;L;;;;;N;;;;; +1D0BB;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NANA;So;0;L;;;;;N;;;;; +1D0BC;BYZANTINE MUSICAL SYMBOL FTHORA NAOS ICHOS;So;0;L;;;;;N;;;;; +1D0BD;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI DI;So;0;L;;;;;N;;;;; +1D0BE;BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON DIATONON DI;So;0;L;;;;;N;;;;; +1D0BF;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI KE;So;0;L;;;;;N;;;;; +1D0C0;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI ZO;So;0;L;;;;;N;;;;; +1D0C1;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NI KATO;So;0;L;;;;;N;;;;; +1D0C2;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NI ANO;So;0;L;;;;;N;;;;; +1D0C3;BYZANTINE MUSICAL SYMBOL FTHORA MALAKON CHROMA DIFONIAS;So;0;L;;;;;N;;;;; +1D0C4;BYZANTINE MUSICAL SYMBOL FTHORA MALAKON CHROMA MONOFONIAS;So;0;L;;;;;N;;;;; +1D0C5;BYZANTINE MUSICAL SYMBOL FHTORA SKLIRON CHROMA VASIS;So;0;L;;;;;N;;;;; +1D0C6;BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON CHROMA SYNAFI;So;0;L;;;;;N;;;;; +1D0C7;BYZANTINE MUSICAL SYMBOL FTHORA NENANO;So;0;L;;;;;N;;;;; +1D0C8;BYZANTINE MUSICAL SYMBOL CHROA ZYGOS;So;0;L;;;;;N;;;;; +1D0C9;BYZANTINE MUSICAL SYMBOL CHROA KLITON;So;0;L;;;;;N;;;;; +1D0CA;BYZANTINE MUSICAL SYMBOL CHROA SPATHI;So;0;L;;;;;N;;;;; +1D0CB;BYZANTINE MUSICAL SYMBOL FTHORA I YFESIS TETARTIMORION;So;0;L;;;;;N;;;;; +1D0CC;BYZANTINE MUSICAL SYMBOL FTHORA ENARMONIOS ANTIFONIA;So;0;L;;;;;N;;;;; +1D0CD;BYZANTINE MUSICAL SYMBOL YFESIS TRITIMORION;So;0;L;;;;;N;;;;; +1D0CE;BYZANTINE MUSICAL SYMBOL DIESIS TRITIMORION;So;0;L;;;;;N;;;;; +1D0CF;BYZANTINE MUSICAL SYMBOL DIESIS TETARTIMORION;So;0;L;;;;;N;;;;; +1D0D0;BYZANTINE MUSICAL SYMBOL DIESIS APLI DYO DODEKATA;So;0;L;;;;;N;;;;; +1D0D1;BYZANTINE MUSICAL SYMBOL DIESIS MONOGRAMMOS TESSERA DODEKATA;So;0;L;;;;;N;;;;; +1D0D2;BYZANTINE MUSICAL SYMBOL DIESIS DIGRAMMOS EX DODEKATA;So;0;L;;;;;N;;;;; +1D0D3;BYZANTINE MUSICAL SYMBOL DIESIS TRIGRAMMOS OKTO DODEKATA;So;0;L;;;;;N;;;;; +1D0D4;BYZANTINE MUSICAL SYMBOL YFESIS APLI DYO DODEKATA;So;0;L;;;;;N;;;;; +1D0D5;BYZANTINE MUSICAL SYMBOL YFESIS MONOGRAMMOS TESSERA DODEKATA;So;0;L;;;;;N;;;;; +1D0D6;BYZANTINE MUSICAL SYMBOL YFESIS DIGRAMMOS EX DODEKATA;So;0;L;;;;;N;;;;; +1D0D7;BYZANTINE MUSICAL SYMBOL YFESIS TRIGRAMMOS OKTO DODEKATA;So;0;L;;;;;N;;;;; +1D0D8;BYZANTINE MUSICAL SYMBOL GENIKI DIESIS;So;0;L;;;;;N;;;;; +1D0D9;BYZANTINE MUSICAL SYMBOL GENIKI YFESIS;So;0;L;;;;;N;;;;; +1D0DA;BYZANTINE MUSICAL SYMBOL DIASTOLI APLI MIKRI;So;0;L;;;;;N;;;;; +1D0DB;BYZANTINE MUSICAL SYMBOL DIASTOLI APLI MEGALI;So;0;L;;;;;N;;;;; +1D0DC;BYZANTINE MUSICAL SYMBOL DIASTOLI DIPLI;So;0;L;;;;;N;;;;; +1D0DD;BYZANTINE MUSICAL SYMBOL DIASTOLI THESEOS;So;0;L;;;;;N;;;;; +1D0DE;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS;So;0;L;;;;;N;;;;; +1D0DF;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS DISIMOU;So;0;L;;;;;N;;;;; +1D0E0;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS TRISIMOU;So;0;L;;;;;N;;;;; +1D0E1;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS TETRASIMOU;So;0;L;;;;;N;;;;; +1D0E2;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS;So;0;L;;;;;N;;;;; +1D0E3;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS DISIMOU;So;0;L;;;;;N;;;;; +1D0E4;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS TRISIMOU;So;0;L;;;;;N;;;;; +1D0E5;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS TETRASIMOU;So;0;L;;;;;N;;;;; +1D0E6;BYZANTINE MUSICAL SYMBOL DIGRAMMA GG;So;0;L;;;;;N;;;;; +1D0E7;BYZANTINE MUSICAL SYMBOL DIFTOGGOS OU;So;0;L;;;;;N;;;;; +1D0E8;BYZANTINE MUSICAL SYMBOL STIGMA;So;0;L;;;;;N;;;;; +1D0E9;BYZANTINE MUSICAL SYMBOL ARKTIKO PA;So;0;L;;;;;N;;;;; +1D0EA;BYZANTINE MUSICAL SYMBOL ARKTIKO VOU;So;0;L;;;;;N;;;;; +1D0EB;BYZANTINE MUSICAL SYMBOL ARKTIKO GA;So;0;L;;;;;N;;;;; +1D0EC;BYZANTINE MUSICAL SYMBOL ARKTIKO DI;So;0;L;;;;;N;;;;; +1D0ED;BYZANTINE MUSICAL SYMBOL ARKTIKO KE;So;0;L;;;;;N;;;;; +1D0EE;BYZANTINE MUSICAL SYMBOL ARKTIKO ZO;So;0;L;;;;;N;;;;; +1D0EF;BYZANTINE MUSICAL SYMBOL ARKTIKO NI;So;0;L;;;;;N;;;;; +1D0F0;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO MESO;So;0;L;;;;;N;;;;; +1D0F1;BYZANTINE MUSICAL SYMBOL KENTIMA NEO MESO;So;0;L;;;;;N;;;;; +1D0F2;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO KATO;So;0;L;;;;;N;;;;; +1D0F3;BYZANTINE MUSICAL SYMBOL KENTIMA NEO KATO;So;0;L;;;;;N;;;;; +1D0F4;BYZANTINE MUSICAL SYMBOL KLASMA KATO;So;0;L;;;;;N;;;;; +1D0F5;BYZANTINE MUSICAL SYMBOL GORGON NEO KATO;So;0;L;;;;;N;;;;; +1D100;MUSICAL SYMBOL SINGLE BARLINE;So;0;L;;;;;N;;;;; +1D101;MUSICAL SYMBOL DOUBLE BARLINE;So;0;L;;;;;N;;;;; +1D102;MUSICAL SYMBOL FINAL BARLINE;So;0;L;;;;;N;;;;; +1D103;MUSICAL SYMBOL REVERSE FINAL BARLINE;So;0;L;;;;;N;;;;; +1D104;MUSICAL SYMBOL DASHED BARLINE;So;0;L;;;;;N;;;;; +1D105;MUSICAL SYMBOL SHORT BARLINE;So;0;L;;;;;N;;;;; +1D106;MUSICAL SYMBOL LEFT REPEAT SIGN;So;0;L;;;;;N;;;;; +1D107;MUSICAL SYMBOL RIGHT REPEAT SIGN;So;0;L;;;;;N;;;;; +1D108;MUSICAL SYMBOL REPEAT DOTS;So;0;L;;;;;N;;;;; +1D109;MUSICAL SYMBOL DAL SEGNO;So;0;L;;;;;N;;;;; +1D10A;MUSICAL SYMBOL DA CAPO;So;0;L;;;;;N;;;;; +1D10B;MUSICAL SYMBOL SEGNO;So;0;L;;;;;N;;;;; +1D10C;MUSICAL SYMBOL CODA;So;0;L;;;;;N;;;;; +1D10D;MUSICAL SYMBOL REPEATED FIGURE-1;So;0;L;;;;;N;;;;; +1D10E;MUSICAL SYMBOL REPEATED FIGURE-2;So;0;L;;;;;N;;;;; +1D10F;MUSICAL SYMBOL REPEATED FIGURE-3;So;0;L;;;;;N;;;;; +1D110;MUSICAL SYMBOL FERMATA;So;0;L;;;;;N;;;;; +1D111;MUSICAL SYMBOL FERMATA BELOW;So;0;L;;;;;N;;;;; +1D112;MUSICAL SYMBOL BREATH MARK;So;0;L;;;;;N;;;;; +1D113;MUSICAL SYMBOL CAESURA;So;0;L;;;;;N;;;;; +1D114;MUSICAL SYMBOL BRACE;So;0;L;;;;;N;;;;; +1D115;MUSICAL SYMBOL BRACKET;So;0;L;;;;;N;;;;; +1D116;MUSICAL SYMBOL ONE-LINE STAFF;So;0;L;;;;;N;;;;; +1D117;MUSICAL SYMBOL TWO-LINE STAFF;So;0;L;;;;;N;;;;; +1D118;MUSICAL SYMBOL THREE-LINE STAFF;So;0;L;;;;;N;;;;; +1D119;MUSICAL SYMBOL FOUR-LINE STAFF;So;0;L;;;;;N;;;;; +1D11A;MUSICAL SYMBOL FIVE-LINE STAFF;So;0;L;;;;;N;;;;; +1D11B;MUSICAL SYMBOL SIX-LINE STAFF;So;0;L;;;;;N;;;;; +1D11C;MUSICAL SYMBOL SIX-STRING FRETBOARD;So;0;L;;;;;N;;;;; +1D11D;MUSICAL SYMBOL FOUR-STRING FRETBOARD;So;0;L;;;;;N;;;;; +1D11E;MUSICAL SYMBOL G CLEF;So;0;L;;;;;N;;;;; +1D11F;MUSICAL SYMBOL G CLEF OTTAVA ALTA;So;0;L;;;;;N;;;;; +1D120;MUSICAL SYMBOL G CLEF OTTAVA BASSA;So;0;L;;;;;N;;;;; +1D121;MUSICAL SYMBOL C CLEF;So;0;L;;;;;N;;;;; +1D122;MUSICAL SYMBOL F CLEF;So;0;L;;;;;N;;;;; +1D123;MUSICAL SYMBOL F CLEF OTTAVA ALTA;So;0;L;;;;;N;;;;; +1D124;MUSICAL SYMBOL F CLEF OTTAVA BASSA;So;0;L;;;;;N;;;;; +1D125;MUSICAL SYMBOL DRUM CLEF-1;So;0;L;;;;;N;;;;; +1D126;MUSICAL SYMBOL DRUM CLEF-2;So;0;L;;;;;N;;;;; +1D129;MUSICAL SYMBOL MULTIPLE MEASURE REST;So;0;L;;;;;N;;;;; +1D12A;MUSICAL SYMBOL DOUBLE SHARP;So;0;L;;;;;N;;;;; +1D12B;MUSICAL SYMBOL DOUBLE FLAT;So;0;L;;;;;N;;;;; +1D12C;MUSICAL SYMBOL FLAT UP;So;0;L;;;;;N;;;;; +1D12D;MUSICAL SYMBOL FLAT DOWN;So;0;L;;;;;N;;;;; +1D12E;MUSICAL SYMBOL NATURAL UP;So;0;L;;;;;N;;;;; +1D12F;MUSICAL SYMBOL NATURAL DOWN;So;0;L;;;;;N;;;;; +1D130;MUSICAL SYMBOL SHARP UP;So;0;L;;;;;N;;;;; +1D131;MUSICAL SYMBOL SHARP DOWN;So;0;L;;;;;N;;;;; +1D132;MUSICAL SYMBOL QUARTER TONE SHARP;So;0;L;;;;;N;;;;; +1D133;MUSICAL SYMBOL QUARTER TONE FLAT;So;0;L;;;;;N;;;;; +1D134;MUSICAL SYMBOL COMMON TIME;So;0;L;;;;;N;;;;; +1D135;MUSICAL SYMBOL CUT TIME;So;0;L;;;;;N;;;;; +1D136;MUSICAL SYMBOL OTTAVA ALTA;So;0;L;;;;;N;;;;; +1D137;MUSICAL SYMBOL OTTAVA BASSA;So;0;L;;;;;N;;;;; +1D138;MUSICAL SYMBOL QUINDICESIMA ALTA;So;0;L;;;;;N;;;;; +1D139;MUSICAL SYMBOL QUINDICESIMA BASSA;So;0;L;;;;;N;;;;; +1D13A;MUSICAL SYMBOL MULTI REST;So;0;L;;;;;N;;;;; +1D13B;MUSICAL SYMBOL WHOLE REST;So;0;L;;;;;N;;;;; +1D13C;MUSICAL SYMBOL HALF REST;So;0;L;;;;;N;;;;; +1D13D;MUSICAL SYMBOL QUARTER REST;So;0;L;;;;;N;;;;; +1D13E;MUSICAL SYMBOL EIGHTH REST;So;0;L;;;;;N;;;;; +1D13F;MUSICAL SYMBOL SIXTEENTH REST;So;0;L;;;;;N;;;;; +1D140;MUSICAL SYMBOL THIRTY-SECOND REST;So;0;L;;;;;N;;;;; +1D141;MUSICAL SYMBOL SIXTY-FOURTH REST;So;0;L;;;;;N;;;;; +1D142;MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH REST;So;0;L;;;;;N;;;;; +1D143;MUSICAL SYMBOL X NOTEHEAD;So;0;L;;;;;N;;;;; +1D144;MUSICAL SYMBOL PLUS NOTEHEAD;So;0;L;;;;;N;;;;; +1D145;MUSICAL SYMBOL CIRCLE X NOTEHEAD;So;0;L;;;;;N;;;;; +1D146;MUSICAL SYMBOL SQUARE NOTEHEAD WHITE;So;0;L;;;;;N;;;;; +1D147;MUSICAL SYMBOL SQUARE NOTEHEAD BLACK;So;0;L;;;;;N;;;;; +1D148;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP WHITE;So;0;L;;;;;N;;;;; +1D149;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP BLACK;So;0;L;;;;;N;;;;; +1D14A;MUSICAL SYMBOL TRIANGLE NOTEHEAD LEFT WHITE;So;0;L;;;;;N;;;;; +1D14B;MUSICAL SYMBOL TRIANGLE NOTEHEAD LEFT BLACK;So;0;L;;;;;N;;;;; +1D14C;MUSICAL SYMBOL TRIANGLE NOTEHEAD RIGHT WHITE;So;0;L;;;;;N;;;;; +1D14D;MUSICAL SYMBOL TRIANGLE NOTEHEAD RIGHT BLACK;So;0;L;;;;;N;;;;; +1D14E;MUSICAL SYMBOL TRIANGLE NOTEHEAD DOWN WHITE;So;0;L;;;;;N;;;;; +1D14F;MUSICAL SYMBOL TRIANGLE NOTEHEAD DOWN BLACK;So;0;L;;;;;N;;;;; +1D150;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP RIGHT WHITE;So;0;L;;;;;N;;;;; +1D151;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP RIGHT BLACK;So;0;L;;;;;N;;;;; +1D152;MUSICAL SYMBOL MOON NOTEHEAD WHITE;So;0;L;;;;;N;;;;; +1D153;MUSICAL SYMBOL MOON NOTEHEAD BLACK;So;0;L;;;;;N;;;;; +1D154;MUSICAL SYMBOL TRIANGLE-ROUND NOTEHEAD DOWN WHITE;So;0;L;;;;;N;;;;; +1D155;MUSICAL SYMBOL TRIANGLE-ROUND NOTEHEAD DOWN BLACK;So;0;L;;;;;N;;;;; +1D156;MUSICAL SYMBOL PARENTHESIS NOTEHEAD;So;0;L;;;;;N;;;;; +1D157;MUSICAL SYMBOL VOID NOTEHEAD;So;0;L;;;;;N;;;;; +1D158;MUSICAL SYMBOL NOTEHEAD BLACK;So;0;L;;;;;N;;;;; +1D159;MUSICAL SYMBOL NULL NOTEHEAD;So;0;L;;;;;N;;;;; +1D15A;MUSICAL SYMBOL CLUSTER NOTEHEAD WHITE;So;0;L;;;;;N;;;;; +1D15B;MUSICAL SYMBOL CLUSTER NOTEHEAD BLACK;So;0;L;;;;;N;;;;; +1D15C;MUSICAL SYMBOL BREVE;So;0;L;;;;;N;;;;; +1D15D;MUSICAL SYMBOL WHOLE NOTE;So;0;L;;;;;N;;;;; +1D15E;MUSICAL SYMBOL HALF NOTE;So;0;L;1D157 1D165;;;;N;;;;; +1D15F;MUSICAL SYMBOL QUARTER NOTE;So;0;L;1D158 1D165;;;;N;;;;; +1D160;MUSICAL SYMBOL EIGHTH NOTE;So;0;L;1D15F 1D16E;;;;N;;;;; +1D161;MUSICAL SYMBOL SIXTEENTH NOTE;So;0;L;1D15F 1D16F;;;;N;;;;; +1D162;MUSICAL SYMBOL THIRTY-SECOND NOTE;So;0;L;1D15F 1D170;;;;N;;;;; +1D163;MUSICAL SYMBOL SIXTY-FOURTH NOTE;So;0;L;1D15F 1D171;;;;N;;;;; +1D164;MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE;So;0;L;1D15F 1D172;;;;N;;;;; +1D165;MUSICAL SYMBOL COMBINING STEM;Mc;216;L;;;;;N;;;;; +1D166;MUSICAL SYMBOL COMBINING SPRECHGESANG STEM;Mc;216;L;;;;;N;;;;; +1D167;MUSICAL SYMBOL COMBINING TREMOLO-1;Mn;1;NSM;;;;;N;;;;; +1D168;MUSICAL SYMBOL COMBINING TREMOLO-2;Mn;1;NSM;;;;;N;;;;; +1D169;MUSICAL SYMBOL COMBINING TREMOLO-3;Mn;1;NSM;;;;;N;;;;; +1D16A;MUSICAL SYMBOL FINGERED TREMOLO-1;So;0;L;;;;;N;;;;; +1D16B;MUSICAL SYMBOL FINGERED TREMOLO-2;So;0;L;;;;;N;;;;; +1D16C;MUSICAL SYMBOL FINGERED TREMOLO-3;So;0;L;;;;;N;;;;; +1D16D;MUSICAL SYMBOL COMBINING AUGMENTATION DOT;Mc;226;L;;;;;N;;;;; +1D16E;MUSICAL SYMBOL COMBINING FLAG-1;Mc;216;L;;;;;N;;;;; +1D16F;MUSICAL SYMBOL COMBINING FLAG-2;Mc;216;L;;;;;N;;;;; +1D170;MUSICAL SYMBOL COMBINING FLAG-3;Mc;216;L;;;;;N;;;;; +1D171;MUSICAL SYMBOL COMBINING FLAG-4;Mc;216;L;;;;;N;;;;; +1D172;MUSICAL SYMBOL COMBINING FLAG-5;Mc;216;L;;;;;N;;;;; +1D173;MUSICAL SYMBOL BEGIN BEAM;Cf;0;BN;;;;;N;;;;; +1D174;MUSICAL SYMBOL END BEAM;Cf;0;BN;;;;;N;;;;; +1D175;MUSICAL SYMBOL BEGIN TIE;Cf;0;BN;;;;;N;;;;; +1D176;MUSICAL SYMBOL END TIE;Cf;0;BN;;;;;N;;;;; +1D177;MUSICAL SYMBOL BEGIN SLUR;Cf;0;BN;;;;;N;;;;; +1D178;MUSICAL SYMBOL END SLUR;Cf;0;BN;;;;;N;;;;; +1D179;MUSICAL SYMBOL BEGIN PHRASE;Cf;0;BN;;;;;N;;;;; +1D17A;MUSICAL SYMBOL END PHRASE;Cf;0;BN;;;;;N;;;;; +1D17B;MUSICAL SYMBOL COMBINING ACCENT;Mn;220;NSM;;;;;N;;;;; +1D17C;MUSICAL SYMBOL COMBINING STACCATO;Mn;220;NSM;;;;;N;;;;; +1D17D;MUSICAL SYMBOL COMBINING TENUTO;Mn;220;NSM;;;;;N;;;;; +1D17E;MUSICAL SYMBOL COMBINING STACCATISSIMO;Mn;220;NSM;;;;;N;;;;; +1D17F;MUSICAL SYMBOL COMBINING MARCATO;Mn;220;NSM;;;;;N;;;;; +1D180;MUSICAL SYMBOL COMBINING MARCATO-STACCATO;Mn;220;NSM;;;;;N;;;;; +1D181;MUSICAL SYMBOL COMBINING ACCENT-STACCATO;Mn;220;NSM;;;;;N;;;;; +1D182;MUSICAL SYMBOL COMBINING LOURE;Mn;220;NSM;;;;;N;;;;; +1D183;MUSICAL SYMBOL ARPEGGIATO UP;So;0;L;;;;;N;;;;; +1D184;MUSICAL SYMBOL ARPEGGIATO DOWN;So;0;L;;;;;N;;;;; +1D185;MUSICAL SYMBOL COMBINING DOIT;Mn;230;NSM;;;;;N;;;;; +1D186;MUSICAL SYMBOL COMBINING RIP;Mn;230;NSM;;;;;N;;;;; +1D187;MUSICAL SYMBOL COMBINING FLIP;Mn;230;NSM;;;;;N;;;;; +1D188;MUSICAL SYMBOL COMBINING SMEAR;Mn;230;NSM;;;;;N;;;;; +1D189;MUSICAL SYMBOL COMBINING BEND;Mn;230;NSM;;;;;N;;;;; +1D18A;MUSICAL SYMBOL COMBINING DOUBLE TONGUE;Mn;220;NSM;;;;;N;;;;; +1D18B;MUSICAL SYMBOL COMBINING TRIPLE TONGUE;Mn;220;NSM;;;;;N;;;;; +1D18C;MUSICAL SYMBOL RINFORZANDO;So;0;L;;;;;N;;;;; +1D18D;MUSICAL SYMBOL SUBITO;So;0;L;;;;;N;;;;; +1D18E;MUSICAL SYMBOL Z;So;0;L;;;;;N;;;;; +1D18F;MUSICAL SYMBOL PIANO;So;0;L;;;;;N;;;;; +1D190;MUSICAL SYMBOL MEZZO;So;0;L;;;;;N;;;;; +1D191;MUSICAL SYMBOL FORTE;So;0;L;;;;;N;;;;; +1D192;MUSICAL SYMBOL CRESCENDO;So;0;L;;;;;N;;;;; +1D193;MUSICAL SYMBOL DECRESCENDO;So;0;L;;;;;N;;;;; +1D194;MUSICAL SYMBOL GRACE NOTE SLASH;So;0;L;;;;;N;;;;; +1D195;MUSICAL SYMBOL GRACE NOTE NO SLASH;So;0;L;;;;;N;;;;; +1D196;MUSICAL SYMBOL TR;So;0;L;;;;;N;;;;; +1D197;MUSICAL SYMBOL TURN;So;0;L;;;;;N;;;;; +1D198;MUSICAL SYMBOL INVERTED TURN;So;0;L;;;;;N;;;;; +1D199;MUSICAL SYMBOL TURN SLASH;So;0;L;;;;;N;;;;; +1D19A;MUSICAL SYMBOL TURN UP;So;0;L;;;;;N;;;;; +1D19B;MUSICAL SYMBOL ORNAMENT STROKE-1;So;0;L;;;;;N;;;;; +1D19C;MUSICAL SYMBOL ORNAMENT STROKE-2;So;0;L;;;;;N;;;;; +1D19D;MUSICAL SYMBOL ORNAMENT STROKE-3;So;0;L;;;;;N;;;;; +1D19E;MUSICAL SYMBOL ORNAMENT STROKE-4;So;0;L;;;;;N;;;;; +1D19F;MUSICAL SYMBOL ORNAMENT STROKE-5;So;0;L;;;;;N;;;;; +1D1A0;MUSICAL SYMBOL ORNAMENT STROKE-6;So;0;L;;;;;N;;;;; +1D1A1;MUSICAL SYMBOL ORNAMENT STROKE-7;So;0;L;;;;;N;;;;; +1D1A2;MUSICAL SYMBOL ORNAMENT STROKE-8;So;0;L;;;;;N;;;;; +1D1A3;MUSICAL SYMBOL ORNAMENT STROKE-9;So;0;L;;;;;N;;;;; +1D1A4;MUSICAL SYMBOL ORNAMENT STROKE-10;So;0;L;;;;;N;;;;; +1D1A5;MUSICAL SYMBOL ORNAMENT STROKE-11;So;0;L;;;;;N;;;;; +1D1A6;MUSICAL SYMBOL HAUPTSTIMME;So;0;L;;;;;N;;;;; +1D1A7;MUSICAL SYMBOL NEBENSTIMME;So;0;L;;;;;N;;;;; +1D1A8;MUSICAL SYMBOL END OF STIMME;So;0;L;;;;;N;;;;; +1D1A9;MUSICAL SYMBOL DEGREE SLASH;So;0;L;;;;;N;;;;; +1D1AA;MUSICAL SYMBOL COMBINING DOWN BOW;Mn;230;NSM;;;;;N;;;;; +1D1AB;MUSICAL SYMBOL COMBINING UP BOW;Mn;230;NSM;;;;;N;;;;; +1D1AC;MUSICAL SYMBOL COMBINING HARMONIC;Mn;230;NSM;;;;;N;;;;; +1D1AD;MUSICAL SYMBOL COMBINING SNAP PIZZICATO;Mn;230;NSM;;;;;N;;;;; +1D1AE;MUSICAL SYMBOL PEDAL MARK;So;0;L;;;;;N;;;;; +1D1AF;MUSICAL SYMBOL PEDAL UP MARK;So;0;L;;;;;N;;;;; +1D1B0;MUSICAL SYMBOL HALF PEDAL MARK;So;0;L;;;;;N;;;;; +1D1B1;MUSICAL SYMBOL GLISSANDO UP;So;0;L;;;;;N;;;;; +1D1B2;MUSICAL SYMBOL GLISSANDO DOWN;So;0;L;;;;;N;;;;; +1D1B3;MUSICAL SYMBOL WITH FINGERNAILS;So;0;L;;;;;N;;;;; +1D1B4;MUSICAL SYMBOL DAMP;So;0;L;;;;;N;;;;; +1D1B5;MUSICAL SYMBOL DAMP ALL;So;0;L;;;;;N;;;;; +1D1B6;MUSICAL SYMBOL MAXIMA;So;0;L;;;;;N;;;;; +1D1B7;MUSICAL SYMBOL LONGA;So;0;L;;;;;N;;;;; +1D1B8;MUSICAL SYMBOL BREVIS;So;0;L;;;;;N;;;;; +1D1B9;MUSICAL SYMBOL SEMIBREVIS WHITE;So;0;L;;;;;N;;;;; +1D1BA;MUSICAL SYMBOL SEMIBREVIS BLACK;So;0;L;;;;;N;;;;; +1D1BB;MUSICAL SYMBOL MINIMA;So;0;L;1D1B9 1D165;;;;N;;;;; +1D1BC;MUSICAL SYMBOL MINIMA BLACK;So;0;L;1D1BA 1D165;;;;N;;;;; +1D1BD;MUSICAL SYMBOL SEMIMINIMA WHITE;So;0;L;1D1BB 1D16E;;;;N;;;;; +1D1BE;MUSICAL SYMBOL SEMIMINIMA BLACK;So;0;L;1D1BC 1D16E;;;;N;;;;; +1D1BF;MUSICAL SYMBOL FUSA WHITE;So;0;L;1D1BB 1D16F;;;;N;;;;; +1D1C0;MUSICAL SYMBOL FUSA BLACK;So;0;L;1D1BC 1D16F;;;;N;;;;; +1D1C1;MUSICAL SYMBOL LONGA PERFECTA REST;So;0;L;;;;;N;;;;; +1D1C2;MUSICAL SYMBOL LONGA IMPERFECTA REST;So;0;L;;;;;N;;;;; +1D1C3;MUSICAL SYMBOL BREVIS REST;So;0;L;;;;;N;;;;; +1D1C4;MUSICAL SYMBOL SEMIBREVIS REST;So;0;L;;;;;N;;;;; +1D1C5;MUSICAL SYMBOL MINIMA REST;So;0;L;;;;;N;;;;; +1D1C6;MUSICAL SYMBOL SEMIMINIMA REST;So;0;L;;;;;N;;;;; +1D1C7;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE PERFECTA;So;0;L;;;;;N;;;;; +1D1C8;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE IMPERFECTA;So;0;L;;;;;N;;;;; +1D1C9;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE PERFECTA DIMINUTION-1;So;0;L;;;;;N;;;;; +1D1CA;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE PERFECTA;So;0;L;;;;;N;;;;; +1D1CB;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA;So;0;L;;;;;N;;;;; +1D1CC;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-1;So;0;L;;;;;N;;;;; +1D1CD;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-2;So;0;L;;;;;N;;;;; +1D1CE;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-3;So;0;L;;;;;N;;;;; +1D1CF;MUSICAL SYMBOL CROIX;So;0;L;;;;;N;;;;; +1D1D0;MUSICAL SYMBOL GREGORIAN C CLEF;So;0;L;;;;;N;;;;; +1D1D1;MUSICAL SYMBOL GREGORIAN F CLEF;So;0;L;;;;;N;;;;; +1D1D2;MUSICAL SYMBOL SQUARE B;So;0;L;;;;;N;;;;; +1D1D3;MUSICAL SYMBOL VIRGA;So;0;L;;;;;N;;;;; +1D1D4;MUSICAL SYMBOL PODATUS;So;0;L;;;;;N;;;;; +1D1D5;MUSICAL SYMBOL CLIVIS;So;0;L;;;;;N;;;;; +1D1D6;MUSICAL SYMBOL SCANDICUS;So;0;L;;;;;N;;;;; +1D1D7;MUSICAL SYMBOL CLIMACUS;So;0;L;;;;;N;;;;; +1D1D8;MUSICAL SYMBOL TORCULUS;So;0;L;;;;;N;;;;; +1D1D9;MUSICAL SYMBOL PORRECTUS;So;0;L;;;;;N;;;;; +1D1DA;MUSICAL SYMBOL PORRECTUS FLEXUS;So;0;L;;;;;N;;;;; +1D1DB;MUSICAL SYMBOL SCANDICUS FLEXUS;So;0;L;;;;;N;;;;; +1D1DC;MUSICAL SYMBOL TORCULUS RESUPINUS;So;0;L;;;;;N;;;;; +1D1DD;MUSICAL SYMBOL PES SUBPUNCTIS;So;0;L;;;;;N;;;;; +1D1DE;MUSICAL SYMBOL KIEVAN C CLEF;So;0;L;;;;;N;;;;; +1D1DF;MUSICAL SYMBOL KIEVAN END OF PIECE;So;0;L;;;;;N;;;;; +1D1E0;MUSICAL SYMBOL KIEVAN FINAL NOTE;So;0;L;;;;;N;;;;; +1D1E1;MUSICAL SYMBOL KIEVAN RECITATIVE MARK;So;0;L;;;;;N;;;;; +1D1E2;MUSICAL SYMBOL KIEVAN WHOLE NOTE;So;0;L;;;;;N;;;;; +1D1E3;MUSICAL SYMBOL KIEVAN HALF NOTE;So;0;L;;;;;N;;;;; +1D1E4;MUSICAL SYMBOL KIEVAN QUARTER NOTE STEM DOWN;So;0;L;;;;;N;;;;; +1D1E5;MUSICAL SYMBOL KIEVAN QUARTER NOTE STEM UP;So;0;L;;;;;N;;;;; +1D1E6;MUSICAL SYMBOL KIEVAN EIGHTH NOTE STEM DOWN;So;0;L;;;;;N;;;;; +1D1E7;MUSICAL SYMBOL KIEVAN EIGHTH NOTE STEM UP;So;0;L;;;;;N;;;;; +1D1E8;MUSICAL SYMBOL KIEVAN FLAT SIGN;So;0;L;;;;;N;;;;; +1D200;GREEK VOCAL NOTATION SYMBOL-1;So;0;ON;;;;;N;;;;; +1D201;GREEK VOCAL NOTATION SYMBOL-2;So;0;ON;;;;;N;;;;; +1D202;GREEK VOCAL NOTATION SYMBOL-3;So;0;ON;;;;;N;;;;; +1D203;GREEK VOCAL NOTATION SYMBOL-4;So;0;ON;;;;;N;;;;; +1D204;GREEK VOCAL NOTATION SYMBOL-5;So;0;ON;;;;;N;;;;; +1D205;GREEK VOCAL NOTATION SYMBOL-6;So;0;ON;;;;;N;;;;; +1D206;GREEK VOCAL NOTATION SYMBOL-7;So;0;ON;;;;;N;;;;; +1D207;GREEK VOCAL NOTATION SYMBOL-8;So;0;ON;;;;;N;;;;; +1D208;GREEK VOCAL NOTATION SYMBOL-9;So;0;ON;;;;;N;;;;; +1D209;GREEK VOCAL NOTATION SYMBOL-10;So;0;ON;;;;;N;;;;; +1D20A;GREEK VOCAL NOTATION SYMBOL-11;So;0;ON;;;;;N;;;;; +1D20B;GREEK VOCAL NOTATION SYMBOL-12;So;0;ON;;;;;N;;;;; +1D20C;GREEK VOCAL NOTATION SYMBOL-13;So;0;ON;;;;;N;;;;; +1D20D;GREEK VOCAL NOTATION SYMBOL-14;So;0;ON;;;;;N;;;;; +1D20E;GREEK VOCAL NOTATION SYMBOL-15;So;0;ON;;;;;N;;;;; +1D20F;GREEK VOCAL NOTATION SYMBOL-16;So;0;ON;;;;;N;;;;; +1D210;GREEK VOCAL NOTATION SYMBOL-17;So;0;ON;;;;;N;;;;; +1D211;GREEK VOCAL NOTATION SYMBOL-18;So;0;ON;;;;;N;;;;; +1D212;GREEK VOCAL NOTATION SYMBOL-19;So;0;ON;;;;;N;;;;; +1D213;GREEK VOCAL NOTATION SYMBOL-20;So;0;ON;;;;;N;;;;; +1D214;GREEK VOCAL NOTATION SYMBOL-21;So;0;ON;;;;;N;;;;; +1D215;GREEK VOCAL NOTATION SYMBOL-22;So;0;ON;;;;;N;;;;; +1D216;GREEK VOCAL NOTATION SYMBOL-23;So;0;ON;;;;;N;;;;; +1D217;GREEK VOCAL NOTATION SYMBOL-24;So;0;ON;;;;;N;;;;; +1D218;GREEK VOCAL NOTATION SYMBOL-50;So;0;ON;;;;;N;;;;; +1D219;GREEK VOCAL NOTATION SYMBOL-51;So;0;ON;;;;;N;;;;; +1D21A;GREEK VOCAL NOTATION SYMBOL-52;So;0;ON;;;;;N;;;;; +1D21B;GREEK VOCAL NOTATION SYMBOL-53;So;0;ON;;;;;N;;;;; +1D21C;GREEK VOCAL NOTATION SYMBOL-54;So;0;ON;;;;;N;;;;; +1D21D;GREEK INSTRUMENTAL NOTATION SYMBOL-1;So;0;ON;;;;;N;;;;; +1D21E;GREEK INSTRUMENTAL NOTATION SYMBOL-2;So;0;ON;;;;;N;;;;; +1D21F;GREEK INSTRUMENTAL NOTATION SYMBOL-4;So;0;ON;;;;;N;;;;; +1D220;GREEK INSTRUMENTAL NOTATION SYMBOL-5;So;0;ON;;;;;N;;;;; +1D221;GREEK INSTRUMENTAL NOTATION SYMBOL-7;So;0;ON;;;;;N;;;;; +1D222;GREEK INSTRUMENTAL NOTATION SYMBOL-8;So;0;ON;;;;;N;;;;; +1D223;GREEK INSTRUMENTAL NOTATION SYMBOL-11;So;0;ON;;;;;N;;;;; +1D224;GREEK INSTRUMENTAL NOTATION SYMBOL-12;So;0;ON;;;;;N;;;;; +1D225;GREEK INSTRUMENTAL NOTATION SYMBOL-13;So;0;ON;;;;;N;;;;; +1D226;GREEK INSTRUMENTAL NOTATION SYMBOL-14;So;0;ON;;;;;N;;;;; +1D227;GREEK INSTRUMENTAL NOTATION SYMBOL-17;So;0;ON;;;;;N;;;;; +1D228;GREEK INSTRUMENTAL NOTATION SYMBOL-18;So;0;ON;;;;;N;;;;; +1D229;GREEK INSTRUMENTAL NOTATION SYMBOL-19;So;0;ON;;;;;N;;;;; +1D22A;GREEK INSTRUMENTAL NOTATION SYMBOL-23;So;0;ON;;;;;N;;;;; +1D22B;GREEK INSTRUMENTAL NOTATION SYMBOL-24;So;0;ON;;;;;N;;;;; +1D22C;GREEK INSTRUMENTAL NOTATION SYMBOL-25;So;0;ON;;;;;N;;;;; +1D22D;GREEK INSTRUMENTAL NOTATION SYMBOL-26;So;0;ON;;;;;N;;;;; +1D22E;GREEK INSTRUMENTAL NOTATION SYMBOL-27;So;0;ON;;;;;N;;;;; +1D22F;GREEK INSTRUMENTAL NOTATION SYMBOL-29;So;0;ON;;;;;N;;;;; +1D230;GREEK INSTRUMENTAL NOTATION SYMBOL-30;So;0;ON;;;;;N;;;;; +1D231;GREEK INSTRUMENTAL NOTATION SYMBOL-32;So;0;ON;;;;;N;;;;; +1D232;GREEK INSTRUMENTAL NOTATION SYMBOL-36;So;0;ON;;;;;N;;;;; +1D233;GREEK INSTRUMENTAL NOTATION SYMBOL-37;So;0;ON;;;;;N;;;;; +1D234;GREEK INSTRUMENTAL NOTATION SYMBOL-38;So;0;ON;;;;;N;;;;; +1D235;GREEK INSTRUMENTAL NOTATION SYMBOL-39;So;0;ON;;;;;N;;;;; +1D236;GREEK INSTRUMENTAL NOTATION SYMBOL-40;So;0;ON;;;;;N;;;;; +1D237;GREEK INSTRUMENTAL NOTATION SYMBOL-42;So;0;ON;;;;;N;;;;; +1D238;GREEK INSTRUMENTAL NOTATION SYMBOL-43;So;0;ON;;;;;N;;;;; +1D239;GREEK INSTRUMENTAL NOTATION SYMBOL-45;So;0;ON;;;;;N;;;;; +1D23A;GREEK INSTRUMENTAL NOTATION SYMBOL-47;So;0;ON;;;;;N;;;;; +1D23B;GREEK INSTRUMENTAL NOTATION SYMBOL-48;So;0;ON;;;;;N;;;;; +1D23C;GREEK INSTRUMENTAL NOTATION SYMBOL-49;So;0;ON;;;;;N;;;;; +1D23D;GREEK INSTRUMENTAL NOTATION SYMBOL-50;So;0;ON;;;;;N;;;;; +1D23E;GREEK INSTRUMENTAL NOTATION SYMBOL-51;So;0;ON;;;;;N;;;;; +1D23F;GREEK INSTRUMENTAL NOTATION SYMBOL-52;So;0;ON;;;;;N;;;;; +1D240;GREEK INSTRUMENTAL NOTATION SYMBOL-53;So;0;ON;;;;;N;;;;; +1D241;GREEK INSTRUMENTAL NOTATION SYMBOL-54;So;0;ON;;;;;N;;;;; +1D242;COMBINING GREEK MUSICAL TRISEME;Mn;230;NSM;;;;;N;;;;; +1D243;COMBINING GREEK MUSICAL TETRASEME;Mn;230;NSM;;;;;N;;;;; +1D244;COMBINING GREEK MUSICAL PENTASEME;Mn;230;NSM;;;;;N;;;;; +1D245;GREEK MUSICAL LEIMMA;So;0;ON;;;;;N;;;;; +1D300;MONOGRAM FOR EARTH;So;0;ON;;;;;N;;;;; +1D301;DIGRAM FOR HEAVENLY EARTH;So;0;ON;;;;;N;;;;; +1D302;DIGRAM FOR HUMAN EARTH;So;0;ON;;;;;N;;;;; +1D303;DIGRAM FOR EARTHLY HEAVEN;So;0;ON;;;;;N;;;;; +1D304;DIGRAM FOR EARTHLY HUMAN;So;0;ON;;;;;N;;;;; +1D305;DIGRAM FOR EARTH;So;0;ON;;;;;N;;;;; +1D306;TETRAGRAM FOR CENTRE;So;0;ON;;;;;N;;;;; +1D307;TETRAGRAM FOR FULL CIRCLE;So;0;ON;;;;;N;;;;; +1D308;TETRAGRAM FOR MIRED;So;0;ON;;;;;N;;;;; +1D309;TETRAGRAM FOR BARRIER;So;0;ON;;;;;N;;;;; +1D30A;TETRAGRAM FOR KEEPING SMALL;So;0;ON;;;;;N;;;;; +1D30B;TETRAGRAM FOR CONTRARIETY;So;0;ON;;;;;N;;;;; +1D30C;TETRAGRAM FOR ASCENT;So;0;ON;;;;;N;;;;; +1D30D;TETRAGRAM FOR OPPOSITION;So;0;ON;;;;;N;;;;; +1D30E;TETRAGRAM FOR BRANCHING OUT;So;0;ON;;;;;N;;;;; +1D30F;TETRAGRAM FOR DEFECTIVENESS OR DISTORTION;So;0;ON;;;;;N;;;;; +1D310;TETRAGRAM FOR DIVERGENCE;So;0;ON;;;;;N;;;;; +1D311;TETRAGRAM FOR YOUTHFULNESS;So;0;ON;;;;;N;;;;; +1D312;TETRAGRAM FOR INCREASE;So;0;ON;;;;;N;;;;; +1D313;TETRAGRAM FOR PENETRATION;So;0;ON;;;;;N;;;;; +1D314;TETRAGRAM FOR REACH;So;0;ON;;;;;N;;;;; +1D315;TETRAGRAM FOR CONTACT;So;0;ON;;;;;N;;;;; +1D316;TETRAGRAM FOR HOLDING BACK;So;0;ON;;;;;N;;;;; +1D317;TETRAGRAM FOR WAITING;So;0;ON;;;;;N;;;;; +1D318;TETRAGRAM FOR FOLLOWING;So;0;ON;;;;;N;;;;; +1D319;TETRAGRAM FOR ADVANCE;So;0;ON;;;;;N;;;;; +1D31A;TETRAGRAM FOR RELEASE;So;0;ON;;;;;N;;;;; +1D31B;TETRAGRAM FOR RESISTANCE;So;0;ON;;;;;N;;;;; +1D31C;TETRAGRAM FOR EASE;So;0;ON;;;;;N;;;;; +1D31D;TETRAGRAM FOR JOY;So;0;ON;;;;;N;;;;; +1D31E;TETRAGRAM FOR CONTENTION;So;0;ON;;;;;N;;;;; +1D31F;TETRAGRAM FOR ENDEAVOUR;So;0;ON;;;;;N;;;;; +1D320;TETRAGRAM FOR DUTIES;So;0;ON;;;;;N;;;;; +1D321;TETRAGRAM FOR CHANGE;So;0;ON;;;;;N;;;;; +1D322;TETRAGRAM FOR DECISIVENESS;So;0;ON;;;;;N;;;;; +1D323;TETRAGRAM FOR BOLD RESOLUTION;So;0;ON;;;;;N;;;;; +1D324;TETRAGRAM FOR PACKING;So;0;ON;;;;;N;;;;; +1D325;TETRAGRAM FOR LEGION;So;0;ON;;;;;N;;;;; +1D326;TETRAGRAM FOR CLOSENESS;So;0;ON;;;;;N;;;;; +1D327;TETRAGRAM FOR KINSHIP;So;0;ON;;;;;N;;;;; +1D328;TETRAGRAM FOR GATHERING;So;0;ON;;;;;N;;;;; +1D329;TETRAGRAM FOR STRENGTH;So;0;ON;;;;;N;;;;; +1D32A;TETRAGRAM FOR PURITY;So;0;ON;;;;;N;;;;; +1D32B;TETRAGRAM FOR FULLNESS;So;0;ON;;;;;N;;;;; +1D32C;TETRAGRAM FOR RESIDENCE;So;0;ON;;;;;N;;;;; +1D32D;TETRAGRAM FOR LAW OR MODEL;So;0;ON;;;;;N;;;;; +1D32E;TETRAGRAM FOR RESPONSE;So;0;ON;;;;;N;;;;; +1D32F;TETRAGRAM FOR GOING TO MEET;So;0;ON;;;;;N;;;;; +1D330;TETRAGRAM FOR ENCOUNTERS;So;0;ON;;;;;N;;;;; +1D331;TETRAGRAM FOR STOVE;So;0;ON;;;;;N;;;;; +1D332;TETRAGRAM FOR GREATNESS;So;0;ON;;;;;N;;;;; +1D333;TETRAGRAM FOR ENLARGEMENT;So;0;ON;;;;;N;;;;; +1D334;TETRAGRAM FOR PATTERN;So;0;ON;;;;;N;;;;; +1D335;TETRAGRAM FOR RITUAL;So;0;ON;;;;;N;;;;; +1D336;TETRAGRAM FOR FLIGHT;So;0;ON;;;;;N;;;;; +1D337;TETRAGRAM FOR VASTNESS OR WASTING;So;0;ON;;;;;N;;;;; +1D338;TETRAGRAM FOR CONSTANCY;So;0;ON;;;;;N;;;;; +1D339;TETRAGRAM FOR MEASURE;So;0;ON;;;;;N;;;;; +1D33A;TETRAGRAM FOR ETERNITY;So;0;ON;;;;;N;;;;; +1D33B;TETRAGRAM FOR UNITY;So;0;ON;;;;;N;;;;; +1D33C;TETRAGRAM FOR DIMINISHMENT;So;0;ON;;;;;N;;;;; +1D33D;TETRAGRAM FOR CLOSED MOUTH;So;0;ON;;;;;N;;;;; +1D33E;TETRAGRAM FOR GUARDEDNESS;So;0;ON;;;;;N;;;;; +1D33F;TETRAGRAM FOR GATHERING IN;So;0;ON;;;;;N;;;;; +1D340;TETRAGRAM FOR MASSING;So;0;ON;;;;;N;;;;; +1D341;TETRAGRAM FOR ACCUMULATION;So;0;ON;;;;;N;;;;; +1D342;TETRAGRAM FOR EMBELLISHMENT;So;0;ON;;;;;N;;;;; +1D343;TETRAGRAM FOR DOUBT;So;0;ON;;;;;N;;;;; +1D344;TETRAGRAM FOR WATCH;So;0;ON;;;;;N;;;;; +1D345;TETRAGRAM FOR SINKING;So;0;ON;;;;;N;;;;; +1D346;TETRAGRAM FOR INNER;So;0;ON;;;;;N;;;;; +1D347;TETRAGRAM FOR DEPARTURE;So;0;ON;;;;;N;;;;; +1D348;TETRAGRAM FOR DARKENING;So;0;ON;;;;;N;;;;; +1D349;TETRAGRAM FOR DIMMING;So;0;ON;;;;;N;;;;; +1D34A;TETRAGRAM FOR EXHAUSTION;So;0;ON;;;;;N;;;;; +1D34B;TETRAGRAM FOR SEVERANCE;So;0;ON;;;;;N;;;;; +1D34C;TETRAGRAM FOR STOPPAGE;So;0;ON;;;;;N;;;;; +1D34D;TETRAGRAM FOR HARDNESS;So;0;ON;;;;;N;;;;; +1D34E;TETRAGRAM FOR COMPLETION;So;0;ON;;;;;N;;;;; +1D34F;TETRAGRAM FOR CLOSURE;So;0;ON;;;;;N;;;;; +1D350;TETRAGRAM FOR FAILURE;So;0;ON;;;;;N;;;;; +1D351;TETRAGRAM FOR AGGRAVATION;So;0;ON;;;;;N;;;;; +1D352;TETRAGRAM FOR COMPLIANCE;So;0;ON;;;;;N;;;;; +1D353;TETRAGRAM FOR ON THE VERGE;So;0;ON;;;;;N;;;;; +1D354;TETRAGRAM FOR DIFFICULTIES;So;0;ON;;;;;N;;;;; +1D355;TETRAGRAM FOR LABOURING;So;0;ON;;;;;N;;;;; +1D356;TETRAGRAM FOR FOSTERING;So;0;ON;;;;;N;;;;; +1D360;COUNTING ROD UNIT DIGIT ONE;No;0;L;;;;1;N;;;;; +1D361;COUNTING ROD UNIT DIGIT TWO;No;0;L;;;;2;N;;;;; +1D362;COUNTING ROD UNIT DIGIT THREE;No;0;L;;;;3;N;;;;; +1D363;COUNTING ROD UNIT DIGIT FOUR;No;0;L;;;;4;N;;;;; +1D364;COUNTING ROD UNIT DIGIT FIVE;No;0;L;;;;5;N;;;;; +1D365;COUNTING ROD UNIT DIGIT SIX;No;0;L;;;;6;N;;;;; +1D366;COUNTING ROD UNIT DIGIT SEVEN;No;0;L;;;;7;N;;;;; +1D367;COUNTING ROD UNIT DIGIT EIGHT;No;0;L;;;;8;N;;;;; +1D368;COUNTING ROD UNIT DIGIT NINE;No;0;L;;;;9;N;;;;; +1D369;COUNTING ROD TENS DIGIT ONE;No;0;L;;;;10;N;;;;; +1D36A;COUNTING ROD TENS DIGIT TWO;No;0;L;;;;20;N;;;;; +1D36B;COUNTING ROD TENS DIGIT THREE;No;0;L;;;;30;N;;;;; +1D36C;COUNTING ROD TENS DIGIT FOUR;No;0;L;;;;40;N;;;;; +1D36D;COUNTING ROD TENS DIGIT FIVE;No;0;L;;;;50;N;;;;; +1D36E;COUNTING ROD TENS DIGIT SIX;No;0;L;;;;60;N;;;;; +1D36F;COUNTING ROD TENS DIGIT SEVEN;No;0;L;;;;70;N;;;;; +1D370;COUNTING ROD TENS DIGIT EIGHT;No;0;L;;;;80;N;;;;; +1D371;COUNTING ROD TENS DIGIT NINE;No;0;L;;;;90;N;;;;; +1D400;MATHEMATICAL BOLD CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D401;MATHEMATICAL BOLD CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D402;MATHEMATICAL BOLD CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D403;MATHEMATICAL BOLD CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D404;MATHEMATICAL BOLD CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D405;MATHEMATICAL BOLD CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D406;MATHEMATICAL BOLD CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D407;MATHEMATICAL BOLD CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D408;MATHEMATICAL BOLD CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D409;MATHEMATICAL BOLD CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D40A;MATHEMATICAL BOLD CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D40B;MATHEMATICAL BOLD CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D40C;MATHEMATICAL BOLD CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D40D;MATHEMATICAL BOLD CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D40E;MATHEMATICAL BOLD CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D40F;MATHEMATICAL BOLD CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D410;MATHEMATICAL BOLD CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D411;MATHEMATICAL BOLD CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D412;MATHEMATICAL BOLD CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D413;MATHEMATICAL BOLD CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D414;MATHEMATICAL BOLD CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D415;MATHEMATICAL BOLD CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D416;MATHEMATICAL BOLD CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D417;MATHEMATICAL BOLD CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D418;MATHEMATICAL BOLD CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D419;MATHEMATICAL BOLD CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D41A;MATHEMATICAL BOLD SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D41B;MATHEMATICAL BOLD SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D41C;MATHEMATICAL BOLD SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D41D;MATHEMATICAL BOLD SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D41E;MATHEMATICAL BOLD SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D41F;MATHEMATICAL BOLD SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D420;MATHEMATICAL BOLD SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D421;MATHEMATICAL BOLD SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D422;MATHEMATICAL BOLD SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D423;MATHEMATICAL BOLD SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D424;MATHEMATICAL BOLD SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D425;MATHEMATICAL BOLD SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D426;MATHEMATICAL BOLD SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D427;MATHEMATICAL BOLD SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D428;MATHEMATICAL BOLD SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D429;MATHEMATICAL BOLD SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D42A;MATHEMATICAL BOLD SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D42B;MATHEMATICAL BOLD SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D42C;MATHEMATICAL BOLD SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D42D;MATHEMATICAL BOLD SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D42E;MATHEMATICAL BOLD SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D42F;MATHEMATICAL BOLD SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D430;MATHEMATICAL BOLD SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D431;MATHEMATICAL BOLD SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D432;MATHEMATICAL BOLD SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D433;MATHEMATICAL BOLD SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D434;MATHEMATICAL ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D435;MATHEMATICAL ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D436;MATHEMATICAL ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D437;MATHEMATICAL ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D438;MATHEMATICAL ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D439;MATHEMATICAL ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D43A;MATHEMATICAL ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D43B;MATHEMATICAL ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D43C;MATHEMATICAL ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D43D;MATHEMATICAL ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D43E;MATHEMATICAL ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D43F;MATHEMATICAL ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D440;MATHEMATICAL ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D441;MATHEMATICAL ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D442;MATHEMATICAL ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D443;MATHEMATICAL ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D444;MATHEMATICAL ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D445;MATHEMATICAL ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D446;MATHEMATICAL ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D447;MATHEMATICAL ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D448;MATHEMATICAL ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D449;MATHEMATICAL ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D44A;MATHEMATICAL ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D44B;MATHEMATICAL ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D44C;MATHEMATICAL ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D44D;MATHEMATICAL ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D44E;MATHEMATICAL ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D44F;MATHEMATICAL ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D450;MATHEMATICAL ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D451;MATHEMATICAL ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D452;MATHEMATICAL ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D453;MATHEMATICAL ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D454;MATHEMATICAL ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D456;MATHEMATICAL ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D457;MATHEMATICAL ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D458;MATHEMATICAL ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D459;MATHEMATICAL ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D45A;MATHEMATICAL ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D45B;MATHEMATICAL ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D45C;MATHEMATICAL ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D45D;MATHEMATICAL ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D45E;MATHEMATICAL ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D45F;MATHEMATICAL ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D460;MATHEMATICAL ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D461;MATHEMATICAL ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D462;MATHEMATICAL ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D463;MATHEMATICAL ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D464;MATHEMATICAL ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D465;MATHEMATICAL ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D466;MATHEMATICAL ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D467;MATHEMATICAL ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D468;MATHEMATICAL BOLD ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D469;MATHEMATICAL BOLD ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D46A;MATHEMATICAL BOLD ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D46B;MATHEMATICAL BOLD ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D46C;MATHEMATICAL BOLD ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D46D;MATHEMATICAL BOLD ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D46E;MATHEMATICAL BOLD ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D46F;MATHEMATICAL BOLD ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D470;MATHEMATICAL BOLD ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D471;MATHEMATICAL BOLD ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D472;MATHEMATICAL BOLD ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D473;MATHEMATICAL BOLD ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D474;MATHEMATICAL BOLD ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D475;MATHEMATICAL BOLD ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D476;MATHEMATICAL BOLD ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D477;MATHEMATICAL BOLD ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D478;MATHEMATICAL BOLD ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D479;MATHEMATICAL BOLD ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D47A;MATHEMATICAL BOLD ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D47B;MATHEMATICAL BOLD ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D47C;MATHEMATICAL BOLD ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D47D;MATHEMATICAL BOLD ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D47E;MATHEMATICAL BOLD ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D47F;MATHEMATICAL BOLD ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D480;MATHEMATICAL BOLD ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D481;MATHEMATICAL BOLD ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D482;MATHEMATICAL BOLD ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D483;MATHEMATICAL BOLD ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D484;MATHEMATICAL BOLD ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D485;MATHEMATICAL BOLD ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D486;MATHEMATICAL BOLD ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D487;MATHEMATICAL BOLD ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D488;MATHEMATICAL BOLD ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D489;MATHEMATICAL BOLD ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D48A;MATHEMATICAL BOLD ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D48B;MATHEMATICAL BOLD ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D48C;MATHEMATICAL BOLD ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D48D;MATHEMATICAL BOLD ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D48E;MATHEMATICAL BOLD ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D48F;MATHEMATICAL BOLD ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D490;MATHEMATICAL BOLD ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D491;MATHEMATICAL BOLD ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D492;MATHEMATICAL BOLD ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D493;MATHEMATICAL BOLD ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D494;MATHEMATICAL BOLD ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D495;MATHEMATICAL BOLD ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D496;MATHEMATICAL BOLD ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D497;MATHEMATICAL BOLD ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D498;MATHEMATICAL BOLD ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D499;MATHEMATICAL BOLD ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D49A;MATHEMATICAL BOLD ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D49B;MATHEMATICAL BOLD ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D49C;MATHEMATICAL SCRIPT CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D49E;MATHEMATICAL SCRIPT CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D49F;MATHEMATICAL SCRIPT CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D4A2;MATHEMATICAL SCRIPT CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D4A5;MATHEMATICAL SCRIPT CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D4A6;MATHEMATICAL SCRIPT CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D4A9;MATHEMATICAL SCRIPT CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D4AA;MATHEMATICAL SCRIPT CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D4AB;MATHEMATICAL SCRIPT CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D4AC;MATHEMATICAL SCRIPT CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D4AE;MATHEMATICAL SCRIPT CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D4AF;MATHEMATICAL SCRIPT CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D4B0;MATHEMATICAL SCRIPT CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D4B1;MATHEMATICAL SCRIPT CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D4B2;MATHEMATICAL SCRIPT CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D4B3;MATHEMATICAL SCRIPT CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D4B4;MATHEMATICAL SCRIPT CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D4B5;MATHEMATICAL SCRIPT CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D4B6;MATHEMATICAL SCRIPT SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D4B7;MATHEMATICAL SCRIPT SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D4B8;MATHEMATICAL SCRIPT SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D4B9;MATHEMATICAL SCRIPT SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D4BB;MATHEMATICAL SCRIPT SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D4BD;MATHEMATICAL SCRIPT SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D4BE;MATHEMATICAL SCRIPT SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D4BF;MATHEMATICAL SCRIPT SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D4C0;MATHEMATICAL SCRIPT SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D4C1;MATHEMATICAL SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D4C2;MATHEMATICAL SCRIPT SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D4C3;MATHEMATICAL SCRIPT SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D4C5;MATHEMATICAL SCRIPT SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D4C6;MATHEMATICAL SCRIPT SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D4C7;MATHEMATICAL SCRIPT SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D4C8;MATHEMATICAL SCRIPT SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D4C9;MATHEMATICAL SCRIPT SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D4CA;MATHEMATICAL SCRIPT SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D4CB;MATHEMATICAL SCRIPT SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D4CC;MATHEMATICAL SCRIPT SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D4CD;MATHEMATICAL SCRIPT SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D4CE;MATHEMATICAL SCRIPT SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D4CF;MATHEMATICAL SCRIPT SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D4D0;MATHEMATICAL BOLD SCRIPT CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D4D1;MATHEMATICAL BOLD SCRIPT CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D4D2;MATHEMATICAL BOLD SCRIPT CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D4D3;MATHEMATICAL BOLD SCRIPT CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D4D4;MATHEMATICAL BOLD SCRIPT CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D4D5;MATHEMATICAL BOLD SCRIPT CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D4D6;MATHEMATICAL BOLD SCRIPT CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D4D7;MATHEMATICAL BOLD SCRIPT CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D4D8;MATHEMATICAL BOLD SCRIPT CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D4D9;MATHEMATICAL BOLD SCRIPT CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D4DA;MATHEMATICAL BOLD SCRIPT CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D4DB;MATHEMATICAL BOLD SCRIPT CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D4DC;MATHEMATICAL BOLD SCRIPT CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D4DD;MATHEMATICAL BOLD SCRIPT CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D4DE;MATHEMATICAL BOLD SCRIPT CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D4DF;MATHEMATICAL BOLD SCRIPT CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D4E0;MATHEMATICAL BOLD SCRIPT CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D4E1;MATHEMATICAL BOLD SCRIPT CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D4E2;MATHEMATICAL BOLD SCRIPT CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D4E3;MATHEMATICAL BOLD SCRIPT CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D4E4;MATHEMATICAL BOLD SCRIPT CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D4E5;MATHEMATICAL BOLD SCRIPT CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D4E6;MATHEMATICAL BOLD SCRIPT CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D4E7;MATHEMATICAL BOLD SCRIPT CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D4E8;MATHEMATICAL BOLD SCRIPT CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D4E9;MATHEMATICAL BOLD SCRIPT CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D4EA;MATHEMATICAL BOLD SCRIPT SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D4EB;MATHEMATICAL BOLD SCRIPT SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D4EC;MATHEMATICAL BOLD SCRIPT SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D4ED;MATHEMATICAL BOLD SCRIPT SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D4EE;MATHEMATICAL BOLD SCRIPT SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D4EF;MATHEMATICAL BOLD SCRIPT SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D4F0;MATHEMATICAL BOLD SCRIPT SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D4F1;MATHEMATICAL BOLD SCRIPT SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D4F2;MATHEMATICAL BOLD SCRIPT SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D4F3;MATHEMATICAL BOLD SCRIPT SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D4F4;MATHEMATICAL BOLD SCRIPT SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D4F5;MATHEMATICAL BOLD SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D4F6;MATHEMATICAL BOLD SCRIPT SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D4F7;MATHEMATICAL BOLD SCRIPT SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D4F8;MATHEMATICAL BOLD SCRIPT SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D4F9;MATHEMATICAL BOLD SCRIPT SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D4FA;MATHEMATICAL BOLD SCRIPT SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D4FB;MATHEMATICAL BOLD SCRIPT SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D4FC;MATHEMATICAL BOLD SCRIPT SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D4FD;MATHEMATICAL BOLD SCRIPT SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D4FE;MATHEMATICAL BOLD SCRIPT SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D4FF;MATHEMATICAL BOLD SCRIPT SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D500;MATHEMATICAL BOLD SCRIPT SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D501;MATHEMATICAL BOLD SCRIPT SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D502;MATHEMATICAL BOLD SCRIPT SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D503;MATHEMATICAL BOLD SCRIPT SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D504;MATHEMATICAL FRAKTUR CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D505;MATHEMATICAL FRAKTUR CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D507;MATHEMATICAL FRAKTUR CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D508;MATHEMATICAL FRAKTUR CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D509;MATHEMATICAL FRAKTUR CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D50A;MATHEMATICAL FRAKTUR CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D50D;MATHEMATICAL FRAKTUR CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D50E;MATHEMATICAL FRAKTUR CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D50F;MATHEMATICAL FRAKTUR CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D510;MATHEMATICAL FRAKTUR CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D511;MATHEMATICAL FRAKTUR CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D512;MATHEMATICAL FRAKTUR CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D513;MATHEMATICAL FRAKTUR CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D514;MATHEMATICAL FRAKTUR CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D516;MATHEMATICAL FRAKTUR CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D517;MATHEMATICAL FRAKTUR CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D518;MATHEMATICAL FRAKTUR CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D519;MATHEMATICAL FRAKTUR CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D51A;MATHEMATICAL FRAKTUR CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D51B;MATHEMATICAL FRAKTUR CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D51C;MATHEMATICAL FRAKTUR CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D51E;MATHEMATICAL FRAKTUR SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D51F;MATHEMATICAL FRAKTUR SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D520;MATHEMATICAL FRAKTUR SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D521;MATHEMATICAL FRAKTUR SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D522;MATHEMATICAL FRAKTUR SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D523;MATHEMATICAL FRAKTUR SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D524;MATHEMATICAL FRAKTUR SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D525;MATHEMATICAL FRAKTUR SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D526;MATHEMATICAL FRAKTUR SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D527;MATHEMATICAL FRAKTUR SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D528;MATHEMATICAL FRAKTUR SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D529;MATHEMATICAL FRAKTUR SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D52A;MATHEMATICAL FRAKTUR SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D52B;MATHEMATICAL FRAKTUR SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D52C;MATHEMATICAL FRAKTUR SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D52D;MATHEMATICAL FRAKTUR SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D52E;MATHEMATICAL FRAKTUR SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D52F;MATHEMATICAL FRAKTUR SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D530;MATHEMATICAL FRAKTUR SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D531;MATHEMATICAL FRAKTUR SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D532;MATHEMATICAL FRAKTUR SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D533;MATHEMATICAL FRAKTUR SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D534;MATHEMATICAL FRAKTUR SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D535;MATHEMATICAL FRAKTUR SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D536;MATHEMATICAL FRAKTUR SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D537;MATHEMATICAL FRAKTUR SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D538;MATHEMATICAL DOUBLE-STRUCK CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D539;MATHEMATICAL DOUBLE-STRUCK CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D53B;MATHEMATICAL DOUBLE-STRUCK CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D53C;MATHEMATICAL DOUBLE-STRUCK CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D53D;MATHEMATICAL DOUBLE-STRUCK CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D53E;MATHEMATICAL DOUBLE-STRUCK CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D540;MATHEMATICAL DOUBLE-STRUCK CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D541;MATHEMATICAL DOUBLE-STRUCK CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D542;MATHEMATICAL DOUBLE-STRUCK CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D543;MATHEMATICAL DOUBLE-STRUCK CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D544;MATHEMATICAL DOUBLE-STRUCK CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D546;MATHEMATICAL DOUBLE-STRUCK CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D54A;MATHEMATICAL DOUBLE-STRUCK CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D54B;MATHEMATICAL DOUBLE-STRUCK CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D54C;MATHEMATICAL DOUBLE-STRUCK CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D54D;MATHEMATICAL DOUBLE-STRUCK CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D54E;MATHEMATICAL DOUBLE-STRUCK CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D54F;MATHEMATICAL DOUBLE-STRUCK CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D550;MATHEMATICAL DOUBLE-STRUCK CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D552;MATHEMATICAL DOUBLE-STRUCK SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D553;MATHEMATICAL DOUBLE-STRUCK SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D554;MATHEMATICAL DOUBLE-STRUCK SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D555;MATHEMATICAL DOUBLE-STRUCK SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D556;MATHEMATICAL DOUBLE-STRUCK SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D557;MATHEMATICAL DOUBLE-STRUCK SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D558;MATHEMATICAL DOUBLE-STRUCK SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D559;MATHEMATICAL DOUBLE-STRUCK SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D55A;MATHEMATICAL DOUBLE-STRUCK SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D55B;MATHEMATICAL DOUBLE-STRUCK SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D55C;MATHEMATICAL DOUBLE-STRUCK SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D55D;MATHEMATICAL DOUBLE-STRUCK SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D55E;MATHEMATICAL DOUBLE-STRUCK SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D55F;MATHEMATICAL DOUBLE-STRUCK SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D560;MATHEMATICAL DOUBLE-STRUCK SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D561;MATHEMATICAL DOUBLE-STRUCK SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D562;MATHEMATICAL DOUBLE-STRUCK SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D563;MATHEMATICAL DOUBLE-STRUCK SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D564;MATHEMATICAL DOUBLE-STRUCK SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D565;MATHEMATICAL DOUBLE-STRUCK SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D566;MATHEMATICAL DOUBLE-STRUCK SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D567;MATHEMATICAL DOUBLE-STRUCK SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D568;MATHEMATICAL DOUBLE-STRUCK SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D569;MATHEMATICAL DOUBLE-STRUCK SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D56A;MATHEMATICAL DOUBLE-STRUCK SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D56B;MATHEMATICAL DOUBLE-STRUCK SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D56C;MATHEMATICAL BOLD FRAKTUR CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D56D;MATHEMATICAL BOLD FRAKTUR CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D56E;MATHEMATICAL BOLD FRAKTUR CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D56F;MATHEMATICAL BOLD FRAKTUR CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D570;MATHEMATICAL BOLD FRAKTUR CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D571;MATHEMATICAL BOLD FRAKTUR CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D572;MATHEMATICAL BOLD FRAKTUR CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D573;MATHEMATICAL BOLD FRAKTUR CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D574;MATHEMATICAL BOLD FRAKTUR CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D575;MATHEMATICAL BOLD FRAKTUR CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D576;MATHEMATICAL BOLD FRAKTUR CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D577;MATHEMATICAL BOLD FRAKTUR CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D578;MATHEMATICAL BOLD FRAKTUR CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D579;MATHEMATICAL BOLD FRAKTUR CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D57A;MATHEMATICAL BOLD FRAKTUR CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D57B;MATHEMATICAL BOLD FRAKTUR CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D57C;MATHEMATICAL BOLD FRAKTUR CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D57D;MATHEMATICAL BOLD FRAKTUR CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D57E;MATHEMATICAL BOLD FRAKTUR CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D57F;MATHEMATICAL BOLD FRAKTUR CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D580;MATHEMATICAL BOLD FRAKTUR CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D581;MATHEMATICAL BOLD FRAKTUR CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D582;MATHEMATICAL BOLD FRAKTUR CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D583;MATHEMATICAL BOLD FRAKTUR CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D584;MATHEMATICAL BOLD FRAKTUR CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D585;MATHEMATICAL BOLD FRAKTUR CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D586;MATHEMATICAL BOLD FRAKTUR SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D587;MATHEMATICAL BOLD FRAKTUR SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D588;MATHEMATICAL BOLD FRAKTUR SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D589;MATHEMATICAL BOLD FRAKTUR SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D58A;MATHEMATICAL BOLD FRAKTUR SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D58B;MATHEMATICAL BOLD FRAKTUR SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D58C;MATHEMATICAL BOLD FRAKTUR SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D58D;MATHEMATICAL BOLD FRAKTUR SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D58E;MATHEMATICAL BOLD FRAKTUR SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D58F;MATHEMATICAL BOLD FRAKTUR SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D590;MATHEMATICAL BOLD FRAKTUR SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D591;MATHEMATICAL BOLD FRAKTUR SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D592;MATHEMATICAL BOLD FRAKTUR SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D593;MATHEMATICAL BOLD FRAKTUR SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D594;MATHEMATICAL BOLD FRAKTUR SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D595;MATHEMATICAL BOLD FRAKTUR SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D596;MATHEMATICAL BOLD FRAKTUR SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D597;MATHEMATICAL BOLD FRAKTUR SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D598;MATHEMATICAL BOLD FRAKTUR SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D599;MATHEMATICAL BOLD FRAKTUR SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D59A;MATHEMATICAL BOLD FRAKTUR SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D59B;MATHEMATICAL BOLD FRAKTUR SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D59C;MATHEMATICAL BOLD FRAKTUR SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D59D;MATHEMATICAL BOLD FRAKTUR SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D59E;MATHEMATICAL BOLD FRAKTUR SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D59F;MATHEMATICAL BOLD FRAKTUR SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D5A0;MATHEMATICAL SANS-SERIF CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D5A1;MATHEMATICAL SANS-SERIF CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D5A2;MATHEMATICAL SANS-SERIF CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D5A3;MATHEMATICAL SANS-SERIF CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D5A4;MATHEMATICAL SANS-SERIF CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D5A5;MATHEMATICAL SANS-SERIF CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D5A6;MATHEMATICAL SANS-SERIF CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D5A7;MATHEMATICAL SANS-SERIF CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D5A8;MATHEMATICAL SANS-SERIF CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D5A9;MATHEMATICAL SANS-SERIF CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D5AA;MATHEMATICAL SANS-SERIF CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D5AB;MATHEMATICAL SANS-SERIF CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D5AC;MATHEMATICAL SANS-SERIF CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D5AD;MATHEMATICAL SANS-SERIF CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D5AE;MATHEMATICAL SANS-SERIF CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D5AF;MATHEMATICAL SANS-SERIF CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D5B0;MATHEMATICAL SANS-SERIF CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D5B1;MATHEMATICAL SANS-SERIF CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D5B2;MATHEMATICAL SANS-SERIF CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D5B3;MATHEMATICAL SANS-SERIF CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D5B4;MATHEMATICAL SANS-SERIF CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D5B5;MATHEMATICAL SANS-SERIF CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D5B6;MATHEMATICAL SANS-SERIF CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D5B7;MATHEMATICAL SANS-SERIF CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D5B8;MATHEMATICAL SANS-SERIF CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D5B9;MATHEMATICAL SANS-SERIF CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D5BA;MATHEMATICAL SANS-SERIF SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D5BB;MATHEMATICAL SANS-SERIF SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D5BC;MATHEMATICAL SANS-SERIF SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D5BD;MATHEMATICAL SANS-SERIF SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D5BE;MATHEMATICAL SANS-SERIF SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D5BF;MATHEMATICAL SANS-SERIF SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D5C0;MATHEMATICAL SANS-SERIF SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D5C1;MATHEMATICAL SANS-SERIF SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D5C2;MATHEMATICAL SANS-SERIF SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D5C3;MATHEMATICAL SANS-SERIF SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D5C4;MATHEMATICAL SANS-SERIF SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D5C5;MATHEMATICAL SANS-SERIF SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D5C6;MATHEMATICAL SANS-SERIF SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D5C7;MATHEMATICAL SANS-SERIF SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D5C8;MATHEMATICAL SANS-SERIF SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D5C9;MATHEMATICAL SANS-SERIF SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D5CA;MATHEMATICAL SANS-SERIF SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D5CB;MATHEMATICAL SANS-SERIF SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D5CC;MATHEMATICAL SANS-SERIF SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D5CD;MATHEMATICAL SANS-SERIF SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D5CE;MATHEMATICAL SANS-SERIF SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D5CF;MATHEMATICAL SANS-SERIF SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D5D0;MATHEMATICAL SANS-SERIF SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D5D1;MATHEMATICAL SANS-SERIF SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D5D2;MATHEMATICAL SANS-SERIF SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D5D3;MATHEMATICAL SANS-SERIF SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D5D4;MATHEMATICAL SANS-SERIF BOLD CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D5D5;MATHEMATICAL SANS-SERIF BOLD CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D5D6;MATHEMATICAL SANS-SERIF BOLD CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D5D7;MATHEMATICAL SANS-SERIF BOLD CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D5D8;MATHEMATICAL SANS-SERIF BOLD CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D5D9;MATHEMATICAL SANS-SERIF BOLD CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D5DA;MATHEMATICAL SANS-SERIF BOLD CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D5DB;MATHEMATICAL SANS-SERIF BOLD CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D5DC;MATHEMATICAL SANS-SERIF BOLD CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D5DD;MATHEMATICAL SANS-SERIF BOLD CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D5DE;MATHEMATICAL SANS-SERIF BOLD CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D5DF;MATHEMATICAL SANS-SERIF BOLD CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D5E0;MATHEMATICAL SANS-SERIF BOLD CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D5E1;MATHEMATICAL SANS-SERIF BOLD CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D5E2;MATHEMATICAL SANS-SERIF BOLD CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D5E3;MATHEMATICAL SANS-SERIF BOLD CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D5E4;MATHEMATICAL SANS-SERIF BOLD CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D5E5;MATHEMATICAL SANS-SERIF BOLD CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D5E6;MATHEMATICAL SANS-SERIF BOLD CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D5E7;MATHEMATICAL SANS-SERIF BOLD CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D5E8;MATHEMATICAL SANS-SERIF BOLD CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D5E9;MATHEMATICAL SANS-SERIF BOLD CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D5EA;MATHEMATICAL SANS-SERIF BOLD CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D5EB;MATHEMATICAL SANS-SERIF BOLD CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D5EC;MATHEMATICAL SANS-SERIF BOLD CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D5ED;MATHEMATICAL SANS-SERIF BOLD CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D5EE;MATHEMATICAL SANS-SERIF BOLD SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D5EF;MATHEMATICAL SANS-SERIF BOLD SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D5F0;MATHEMATICAL SANS-SERIF BOLD SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D5F1;MATHEMATICAL SANS-SERIF BOLD SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D5F2;MATHEMATICAL SANS-SERIF BOLD SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D5F3;MATHEMATICAL SANS-SERIF BOLD SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D5F4;MATHEMATICAL SANS-SERIF BOLD SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D5F5;MATHEMATICAL SANS-SERIF BOLD SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D5F6;MATHEMATICAL SANS-SERIF BOLD SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D5F7;MATHEMATICAL SANS-SERIF BOLD SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D5F8;MATHEMATICAL SANS-SERIF BOLD SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D5F9;MATHEMATICAL SANS-SERIF BOLD SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D5FA;MATHEMATICAL SANS-SERIF BOLD SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D5FB;MATHEMATICAL SANS-SERIF BOLD SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D5FC;MATHEMATICAL SANS-SERIF BOLD SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D5FD;MATHEMATICAL SANS-SERIF BOLD SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D5FE;MATHEMATICAL SANS-SERIF BOLD SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D5FF;MATHEMATICAL SANS-SERIF BOLD SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D600;MATHEMATICAL SANS-SERIF BOLD SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D601;MATHEMATICAL SANS-SERIF BOLD SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D602;MATHEMATICAL SANS-SERIF BOLD SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D603;MATHEMATICAL SANS-SERIF BOLD SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D604;MATHEMATICAL SANS-SERIF BOLD SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D605;MATHEMATICAL SANS-SERIF BOLD SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D606;MATHEMATICAL SANS-SERIF BOLD SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D607;MATHEMATICAL SANS-SERIF BOLD SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D608;MATHEMATICAL SANS-SERIF ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D609;MATHEMATICAL SANS-SERIF ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D60A;MATHEMATICAL SANS-SERIF ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D60B;MATHEMATICAL SANS-SERIF ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D60C;MATHEMATICAL SANS-SERIF ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D60D;MATHEMATICAL SANS-SERIF ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D60E;MATHEMATICAL SANS-SERIF ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D60F;MATHEMATICAL SANS-SERIF ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D610;MATHEMATICAL SANS-SERIF ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D611;MATHEMATICAL SANS-SERIF ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D612;MATHEMATICAL SANS-SERIF ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D613;MATHEMATICAL SANS-SERIF ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D614;MATHEMATICAL SANS-SERIF ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D615;MATHEMATICAL SANS-SERIF ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D616;MATHEMATICAL SANS-SERIF ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D617;MATHEMATICAL SANS-SERIF ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D618;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D619;MATHEMATICAL SANS-SERIF ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D61A;MATHEMATICAL SANS-SERIF ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D61B;MATHEMATICAL SANS-SERIF ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D61C;MATHEMATICAL SANS-SERIF ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D61D;MATHEMATICAL SANS-SERIF ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D61E;MATHEMATICAL SANS-SERIF ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D61F;MATHEMATICAL SANS-SERIF ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D620;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D621;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D622;MATHEMATICAL SANS-SERIF ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D623;MATHEMATICAL SANS-SERIF ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D624;MATHEMATICAL SANS-SERIF ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D625;MATHEMATICAL SANS-SERIF ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D626;MATHEMATICAL SANS-SERIF ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D627;MATHEMATICAL SANS-SERIF ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D628;MATHEMATICAL SANS-SERIF ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D629;MATHEMATICAL SANS-SERIF ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D62A;MATHEMATICAL SANS-SERIF ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D62B;MATHEMATICAL SANS-SERIF ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D62C;MATHEMATICAL SANS-SERIF ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D62D;MATHEMATICAL SANS-SERIF ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D62E;MATHEMATICAL SANS-SERIF ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D62F;MATHEMATICAL SANS-SERIF ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D630;MATHEMATICAL SANS-SERIF ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D631;MATHEMATICAL SANS-SERIF ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D632;MATHEMATICAL SANS-SERIF ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D633;MATHEMATICAL SANS-SERIF ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D634;MATHEMATICAL SANS-SERIF ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D635;MATHEMATICAL SANS-SERIF ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D636;MATHEMATICAL SANS-SERIF ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D637;MATHEMATICAL SANS-SERIF ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D638;MATHEMATICAL SANS-SERIF ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D639;MATHEMATICAL SANS-SERIF ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D63A;MATHEMATICAL SANS-SERIF ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D63B;MATHEMATICAL SANS-SERIF ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D63C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D63D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D63E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D63F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D640;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D641;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D642;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D643;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D644;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D645;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D646;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D647;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D648;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D649;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D64A;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D64B;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D64C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D64D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D64E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D64F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D650;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D651;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D652;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D653;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D654;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D655;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D656;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D657;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D658;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D659;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D65A;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D65B;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D65C;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D65D;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D65E;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D65F;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D660;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D661;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D662;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D663;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D664;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D665;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D666;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D667;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D668;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D669;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D66A;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D66B;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D66C;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D66D;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D66E;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D66F;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D670;MATHEMATICAL MONOSPACE CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D671;MATHEMATICAL MONOSPACE CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D672;MATHEMATICAL MONOSPACE CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D673;MATHEMATICAL MONOSPACE CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D674;MATHEMATICAL MONOSPACE CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D675;MATHEMATICAL MONOSPACE CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D676;MATHEMATICAL MONOSPACE CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D677;MATHEMATICAL MONOSPACE CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D678;MATHEMATICAL MONOSPACE CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D679;MATHEMATICAL MONOSPACE CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D67A;MATHEMATICAL MONOSPACE CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D67B;MATHEMATICAL MONOSPACE CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D67C;MATHEMATICAL MONOSPACE CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D67D;MATHEMATICAL MONOSPACE CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D67E;MATHEMATICAL MONOSPACE CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D67F;MATHEMATICAL MONOSPACE CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D680;MATHEMATICAL MONOSPACE CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D681;MATHEMATICAL MONOSPACE CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D682;MATHEMATICAL MONOSPACE CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D683;MATHEMATICAL MONOSPACE CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D684;MATHEMATICAL MONOSPACE CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D685;MATHEMATICAL MONOSPACE CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D686;MATHEMATICAL MONOSPACE CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D687;MATHEMATICAL MONOSPACE CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D688;MATHEMATICAL MONOSPACE CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D689;MATHEMATICAL MONOSPACE CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D68A;MATHEMATICAL MONOSPACE SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D68B;MATHEMATICAL MONOSPACE SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D68C;MATHEMATICAL MONOSPACE SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D68D;MATHEMATICAL MONOSPACE SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D68E;MATHEMATICAL MONOSPACE SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D68F;MATHEMATICAL MONOSPACE SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D690;MATHEMATICAL MONOSPACE SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D691;MATHEMATICAL MONOSPACE SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D692;MATHEMATICAL MONOSPACE SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D693;MATHEMATICAL MONOSPACE SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D694;MATHEMATICAL MONOSPACE SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D695;MATHEMATICAL MONOSPACE SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D696;MATHEMATICAL MONOSPACE SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D697;MATHEMATICAL MONOSPACE SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D698;MATHEMATICAL MONOSPACE SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D699;MATHEMATICAL MONOSPACE SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D69A;MATHEMATICAL MONOSPACE SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D69B;MATHEMATICAL MONOSPACE SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D69C;MATHEMATICAL MONOSPACE SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D69D;MATHEMATICAL MONOSPACE SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D69E;MATHEMATICAL MONOSPACE SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D69F;MATHEMATICAL MONOSPACE SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D6A0;MATHEMATICAL MONOSPACE SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D6A1;MATHEMATICAL MONOSPACE SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D6A2;MATHEMATICAL MONOSPACE SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D6A3;MATHEMATICAL MONOSPACE SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D6A4;MATHEMATICAL ITALIC SMALL DOTLESS I;Ll;0;L;<font> 0131;;;;N;;;;; +1D6A5;MATHEMATICAL ITALIC SMALL DOTLESS J;Ll;0;L;<font> 0237;;;;N;;;;; +1D6A8;MATHEMATICAL BOLD CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;; +1D6A9;MATHEMATICAL BOLD CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;; +1D6AA;MATHEMATICAL BOLD CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;; +1D6AB;MATHEMATICAL BOLD CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;; +1D6AC;MATHEMATICAL BOLD CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;; +1D6AD;MATHEMATICAL BOLD CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;; +1D6AE;MATHEMATICAL BOLD CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;; +1D6AF;MATHEMATICAL BOLD CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;; +1D6B0;MATHEMATICAL BOLD CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;; +1D6B1;MATHEMATICAL BOLD CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;; +1D6B2;MATHEMATICAL BOLD CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;; +1D6B3;MATHEMATICAL BOLD CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;; +1D6B4;MATHEMATICAL BOLD CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;; +1D6B5;MATHEMATICAL BOLD CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;; +1D6B6;MATHEMATICAL BOLD CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;; +1D6B7;MATHEMATICAL BOLD CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;; +1D6B8;MATHEMATICAL BOLD CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;; +1D6B9;MATHEMATICAL BOLD CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;; +1D6BA;MATHEMATICAL BOLD CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;; +1D6BB;MATHEMATICAL BOLD CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;; +1D6BC;MATHEMATICAL BOLD CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;; +1D6BD;MATHEMATICAL BOLD CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;; +1D6BE;MATHEMATICAL BOLD CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;; +1D6BF;MATHEMATICAL BOLD CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;; +1D6C0;MATHEMATICAL BOLD CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;; +1D6C1;MATHEMATICAL BOLD NABLA;Sm;0;L;<font> 2207;;;;N;;;;; +1D6C2;MATHEMATICAL BOLD SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;; +1D6C3;MATHEMATICAL BOLD SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;; +1D6C4;MATHEMATICAL BOLD SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;; +1D6C5;MATHEMATICAL BOLD SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;; +1D6C6;MATHEMATICAL BOLD SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;; +1D6C7;MATHEMATICAL BOLD SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;; +1D6C8;MATHEMATICAL BOLD SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;; +1D6C9;MATHEMATICAL BOLD SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;; +1D6CA;MATHEMATICAL BOLD SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;; +1D6CB;MATHEMATICAL BOLD SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;; +1D6CC;MATHEMATICAL BOLD SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;; +1D6CD;MATHEMATICAL BOLD SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;; +1D6CE;MATHEMATICAL BOLD SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;; +1D6CF;MATHEMATICAL BOLD SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;; +1D6D0;MATHEMATICAL BOLD SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;; +1D6D1;MATHEMATICAL BOLD SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;; +1D6D2;MATHEMATICAL BOLD SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;; +1D6D3;MATHEMATICAL BOLD SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;; +1D6D4;MATHEMATICAL BOLD SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;; +1D6D5;MATHEMATICAL BOLD SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;; +1D6D6;MATHEMATICAL BOLD SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;; +1D6D7;MATHEMATICAL BOLD SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;; +1D6D8;MATHEMATICAL BOLD SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;; +1D6D9;MATHEMATICAL BOLD SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;; +1D6DA;MATHEMATICAL BOLD SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;; +1D6DB;MATHEMATICAL BOLD PARTIAL DIFFERENTIAL;Sm;0;ON;<font> 2202;;;;Y;;;;; +1D6DC;MATHEMATICAL BOLD EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;; +1D6DD;MATHEMATICAL BOLD THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;; +1D6DE;MATHEMATICAL BOLD KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;; +1D6DF;MATHEMATICAL BOLD PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;; +1D6E0;MATHEMATICAL BOLD RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;; +1D6E1;MATHEMATICAL BOLD PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;; +1D6E2;MATHEMATICAL ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;; +1D6E3;MATHEMATICAL ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;; +1D6E4;MATHEMATICAL ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;; +1D6E5;MATHEMATICAL ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;; +1D6E6;MATHEMATICAL ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;; +1D6E7;MATHEMATICAL ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;; +1D6E8;MATHEMATICAL ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;; +1D6E9;MATHEMATICAL ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;; +1D6EA;MATHEMATICAL ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;; +1D6EB;MATHEMATICAL ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;; +1D6EC;MATHEMATICAL ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;; +1D6ED;MATHEMATICAL ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;; +1D6EE;MATHEMATICAL ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;; +1D6EF;MATHEMATICAL ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;; +1D6F0;MATHEMATICAL ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;; +1D6F1;MATHEMATICAL ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;; +1D6F2;MATHEMATICAL ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;; +1D6F3;MATHEMATICAL ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;; +1D6F4;MATHEMATICAL ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;; +1D6F5;MATHEMATICAL ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;; +1D6F6;MATHEMATICAL ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;; +1D6F7;MATHEMATICAL ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;; +1D6F8;MATHEMATICAL ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;; +1D6F9;MATHEMATICAL ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;; +1D6FA;MATHEMATICAL ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;; +1D6FB;MATHEMATICAL ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;; +1D6FC;MATHEMATICAL ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;; +1D6FD;MATHEMATICAL ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;; +1D6FE;MATHEMATICAL ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;; +1D6FF;MATHEMATICAL ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;; +1D700;MATHEMATICAL ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;; +1D701;MATHEMATICAL ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;; +1D702;MATHEMATICAL ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;; +1D703;MATHEMATICAL ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;; +1D704;MATHEMATICAL ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;; +1D705;MATHEMATICAL ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;; +1D706;MATHEMATICAL ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;; +1D707;MATHEMATICAL ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;; +1D708;MATHEMATICAL ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;; +1D709;MATHEMATICAL ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;; +1D70A;MATHEMATICAL ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;; +1D70B;MATHEMATICAL ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;; +1D70C;MATHEMATICAL ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;; +1D70D;MATHEMATICAL ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;; +1D70E;MATHEMATICAL ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;; +1D70F;MATHEMATICAL ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;; +1D710;MATHEMATICAL ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;; +1D711;MATHEMATICAL ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;; +1D712;MATHEMATICAL ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;; +1D713;MATHEMATICAL ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;; +1D714;MATHEMATICAL ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;; +1D715;MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL;Sm;0;ON;<font> 2202;;;;Y;;;;; +1D716;MATHEMATICAL ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;; +1D717;MATHEMATICAL ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;; +1D718;MATHEMATICAL ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;; +1D719;MATHEMATICAL ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;; +1D71A;MATHEMATICAL ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;; +1D71B;MATHEMATICAL ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;; +1D71C;MATHEMATICAL BOLD ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;; +1D71D;MATHEMATICAL BOLD ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;; +1D71E;MATHEMATICAL BOLD ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;; +1D71F;MATHEMATICAL BOLD ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;; +1D720;MATHEMATICAL BOLD ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;; +1D721;MATHEMATICAL BOLD ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;; +1D722;MATHEMATICAL BOLD ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;; +1D723;MATHEMATICAL BOLD ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;; +1D724;MATHEMATICAL BOLD ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;; +1D725;MATHEMATICAL BOLD ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;; +1D726;MATHEMATICAL BOLD ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;; +1D727;MATHEMATICAL BOLD ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;; +1D728;MATHEMATICAL BOLD ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;; +1D729;MATHEMATICAL BOLD ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;; +1D72A;MATHEMATICAL BOLD ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;; +1D72B;MATHEMATICAL BOLD ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;; +1D72C;MATHEMATICAL BOLD ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;; +1D72D;MATHEMATICAL BOLD ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;; +1D72E;MATHEMATICAL BOLD ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;; +1D72F;MATHEMATICAL BOLD ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;; +1D730;MATHEMATICAL BOLD ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;; +1D731;MATHEMATICAL BOLD ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;; +1D732;MATHEMATICAL BOLD ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;; +1D733;MATHEMATICAL BOLD ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;; +1D734;MATHEMATICAL BOLD ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;; +1D735;MATHEMATICAL BOLD ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;; +1D736;MATHEMATICAL BOLD ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;; +1D737;MATHEMATICAL BOLD ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;; +1D738;MATHEMATICAL BOLD ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;; +1D739;MATHEMATICAL BOLD ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;; +1D73A;MATHEMATICAL BOLD ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;; +1D73B;MATHEMATICAL BOLD ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;; +1D73C;MATHEMATICAL BOLD ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;; +1D73D;MATHEMATICAL BOLD ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;; +1D73E;MATHEMATICAL BOLD ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;; +1D73F;MATHEMATICAL BOLD ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;; +1D740;MATHEMATICAL BOLD ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;; +1D741;MATHEMATICAL BOLD ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;; +1D742;MATHEMATICAL BOLD ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;; +1D743;MATHEMATICAL BOLD ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;; +1D744;MATHEMATICAL BOLD ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;; +1D745;MATHEMATICAL BOLD ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;; +1D746;MATHEMATICAL BOLD ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;; +1D747;MATHEMATICAL BOLD ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;; +1D748;MATHEMATICAL BOLD ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;; +1D749;MATHEMATICAL BOLD ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;; +1D74A;MATHEMATICAL BOLD ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;; +1D74B;MATHEMATICAL BOLD ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;; +1D74C;MATHEMATICAL BOLD ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;; +1D74D;MATHEMATICAL BOLD ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;; +1D74E;MATHEMATICAL BOLD ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;; +1D74F;MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL;Sm;0;ON;<font> 2202;;;;Y;;;;; +1D750;MATHEMATICAL BOLD ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;; +1D751;MATHEMATICAL BOLD ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;; +1D752;MATHEMATICAL BOLD ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;; +1D753;MATHEMATICAL BOLD ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;; +1D754;MATHEMATICAL BOLD ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;; +1D755;MATHEMATICAL BOLD ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;; +1D756;MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;; +1D757;MATHEMATICAL SANS-SERIF BOLD CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;; +1D758;MATHEMATICAL SANS-SERIF BOLD CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;; +1D759;MATHEMATICAL SANS-SERIF BOLD CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;; +1D75A;MATHEMATICAL SANS-SERIF BOLD CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;; +1D75B;MATHEMATICAL SANS-SERIF BOLD CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;; +1D75C;MATHEMATICAL SANS-SERIF BOLD CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;; +1D75D;MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;; +1D75E;MATHEMATICAL SANS-SERIF BOLD CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;; +1D75F;MATHEMATICAL SANS-SERIF BOLD CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;; +1D760;MATHEMATICAL SANS-SERIF BOLD CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;; +1D761;MATHEMATICAL SANS-SERIF BOLD CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;; +1D762;MATHEMATICAL SANS-SERIF BOLD CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;; +1D763;MATHEMATICAL SANS-SERIF BOLD CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;; +1D764;MATHEMATICAL SANS-SERIF BOLD CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;; +1D765;MATHEMATICAL SANS-SERIF BOLD CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;; +1D766;MATHEMATICAL SANS-SERIF BOLD CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;; +1D767;MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;; +1D768;MATHEMATICAL SANS-SERIF BOLD CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;; +1D769;MATHEMATICAL SANS-SERIF BOLD CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;; +1D76A;MATHEMATICAL SANS-SERIF BOLD CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;; +1D76B;MATHEMATICAL SANS-SERIF BOLD CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;; +1D76C;MATHEMATICAL SANS-SERIF BOLD CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;; +1D76D;MATHEMATICAL SANS-SERIF BOLD CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;; +1D76E;MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;; +1D76F;MATHEMATICAL SANS-SERIF BOLD NABLA;Sm;0;L;<font> 2207;;;;N;;;;; +1D770;MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;; +1D771;MATHEMATICAL SANS-SERIF BOLD SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;; +1D772;MATHEMATICAL SANS-SERIF BOLD SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;; +1D773;MATHEMATICAL SANS-SERIF BOLD SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;; +1D774;MATHEMATICAL SANS-SERIF BOLD SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;; +1D775;MATHEMATICAL SANS-SERIF BOLD SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;; +1D776;MATHEMATICAL SANS-SERIF BOLD SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;; +1D777;MATHEMATICAL SANS-SERIF BOLD SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;; +1D778;MATHEMATICAL SANS-SERIF BOLD SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;; +1D779;MATHEMATICAL SANS-SERIF BOLD SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;; +1D77A;MATHEMATICAL SANS-SERIF BOLD SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;; +1D77B;MATHEMATICAL SANS-SERIF BOLD SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;; +1D77C;MATHEMATICAL SANS-SERIF BOLD SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;; +1D77D;MATHEMATICAL SANS-SERIF BOLD SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;; +1D77E;MATHEMATICAL SANS-SERIF BOLD SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;; +1D77F;MATHEMATICAL SANS-SERIF BOLD SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;; +1D780;MATHEMATICAL SANS-SERIF BOLD SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;; +1D781;MATHEMATICAL SANS-SERIF BOLD SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;; +1D782;MATHEMATICAL SANS-SERIF BOLD SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;; +1D783;MATHEMATICAL SANS-SERIF BOLD SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;; +1D784;MATHEMATICAL SANS-SERIF BOLD SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;; +1D785;MATHEMATICAL SANS-SERIF BOLD SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;; +1D786;MATHEMATICAL SANS-SERIF BOLD SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;; +1D787;MATHEMATICAL SANS-SERIF BOLD SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;; +1D788;MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;; +1D789;MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL;Sm;0;ON;<font> 2202;;;;Y;;;;; +1D78A;MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;; +1D78B;MATHEMATICAL SANS-SERIF BOLD THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;; +1D78C;MATHEMATICAL SANS-SERIF BOLD KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;; +1D78D;MATHEMATICAL SANS-SERIF BOLD PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;; +1D78E;MATHEMATICAL SANS-SERIF BOLD RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;; +1D78F;MATHEMATICAL SANS-SERIF BOLD PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;; +1D790;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;; +1D791;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;; +1D792;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;; +1D793;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;; +1D794;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;; +1D795;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;; +1D796;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;; +1D797;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;; +1D798;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;; +1D799;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;; +1D79A;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;; +1D79B;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;; +1D79C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;; +1D79D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;; +1D79E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;; +1D79F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;; +1D7A0;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;; +1D7A1;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;; +1D7A2;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;; +1D7A3;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;; +1D7A4;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;; +1D7A5;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;; +1D7A6;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;; +1D7A7;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;; +1D7A8;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;; +1D7A9;MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;; +1D7AA;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;; +1D7AB;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;; +1D7AC;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;; +1D7AD;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;; +1D7AE;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;; +1D7AF;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;; +1D7B0;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;; +1D7B1;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;; +1D7B2;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;; +1D7B3;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;; +1D7B4;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;; +1D7B5;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;; +1D7B6;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;; +1D7B7;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;; +1D7B8;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;; +1D7B9;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;; +1D7BA;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;; +1D7BB;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;; +1D7BC;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;; +1D7BD;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;; +1D7BE;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;; +1D7BF;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;; +1D7C0;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;; +1D7C1;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;; +1D7C2;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;; +1D7C3;MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL;Sm;0;ON;<font> 2202;;;;Y;;;;; +1D7C4;MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;; +1D7C5;MATHEMATICAL SANS-SERIF BOLD ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;; +1D7C6;MATHEMATICAL SANS-SERIF BOLD ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;; +1D7C7;MATHEMATICAL SANS-SERIF BOLD ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;; +1D7C8;MATHEMATICAL SANS-SERIF BOLD ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;; +1D7C9;MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;; +1D7CA;MATHEMATICAL BOLD CAPITAL DIGAMMA;Lu;0;L;<font> 03DC;;;;N;;;;; +1D7CB;MATHEMATICAL BOLD SMALL DIGAMMA;Ll;0;L;<font> 03DD;;;;N;;;;; +1D7CE;MATHEMATICAL BOLD DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;; +1D7CF;MATHEMATICAL BOLD DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;; +1D7D0;MATHEMATICAL BOLD DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;; +1D7D1;MATHEMATICAL BOLD DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;; +1D7D2;MATHEMATICAL BOLD DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;; +1D7D3;MATHEMATICAL BOLD DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;; +1D7D4;MATHEMATICAL BOLD DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;; +1D7D5;MATHEMATICAL BOLD DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;; +1D7D6;MATHEMATICAL BOLD DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;; +1D7D7;MATHEMATICAL BOLD DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;; +1D7D8;MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;; +1D7D9;MATHEMATICAL DOUBLE-STRUCK DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;; +1D7DA;MATHEMATICAL DOUBLE-STRUCK DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;; +1D7DB;MATHEMATICAL DOUBLE-STRUCK DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;; +1D7DC;MATHEMATICAL DOUBLE-STRUCK DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;; +1D7DD;MATHEMATICAL DOUBLE-STRUCK DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;; +1D7DE;MATHEMATICAL DOUBLE-STRUCK DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;; +1D7DF;MATHEMATICAL DOUBLE-STRUCK DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;; +1D7E0;MATHEMATICAL DOUBLE-STRUCK DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;; +1D7E1;MATHEMATICAL DOUBLE-STRUCK DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;; +1D7E2;MATHEMATICAL SANS-SERIF DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;; +1D7E3;MATHEMATICAL SANS-SERIF DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;; +1D7E4;MATHEMATICAL SANS-SERIF DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;; +1D7E5;MATHEMATICAL SANS-SERIF DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;; +1D7E6;MATHEMATICAL SANS-SERIF DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;; +1D7E7;MATHEMATICAL SANS-SERIF DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;; +1D7E8;MATHEMATICAL SANS-SERIF DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;; +1D7E9;MATHEMATICAL SANS-SERIF DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;; +1D7EA;MATHEMATICAL SANS-SERIF DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;; +1D7EB;MATHEMATICAL SANS-SERIF DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;; +1D7EC;MATHEMATICAL SANS-SERIF BOLD DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;; +1D7ED;MATHEMATICAL SANS-SERIF BOLD DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;; +1D7EE;MATHEMATICAL SANS-SERIF BOLD DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;; +1D7EF;MATHEMATICAL SANS-SERIF BOLD DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;; +1D7F0;MATHEMATICAL SANS-SERIF BOLD DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;; +1D7F1;MATHEMATICAL SANS-SERIF BOLD DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;; +1D7F2;MATHEMATICAL SANS-SERIF BOLD DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;; +1D7F3;MATHEMATICAL SANS-SERIF BOLD DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;; +1D7F4;MATHEMATICAL SANS-SERIF BOLD DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;; +1D7F5;MATHEMATICAL SANS-SERIF BOLD DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;; +1D7F6;MATHEMATICAL MONOSPACE DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;; +1D7F7;MATHEMATICAL MONOSPACE DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;; +1D7F8;MATHEMATICAL MONOSPACE DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;; +1D7F9;MATHEMATICAL MONOSPACE DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;; +1D7FA;MATHEMATICAL MONOSPACE DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;; +1D7FB;MATHEMATICAL MONOSPACE DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;; +1D7FC;MATHEMATICAL MONOSPACE DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;; +1D7FD;MATHEMATICAL MONOSPACE DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;; +1D7FE;MATHEMATICAL MONOSPACE DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;; +1D7FF;MATHEMATICAL MONOSPACE DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;; +1D800;SIGNWRITING HAND-FIST INDEX;So;0;L;;;;;N;;;;; +1D801;SIGNWRITING HAND-CIRCLE INDEX;So;0;L;;;;;N;;;;; +1D802;SIGNWRITING HAND-CUP INDEX;So;0;L;;;;;N;;;;; +1D803;SIGNWRITING HAND-OVAL INDEX;So;0;L;;;;;N;;;;; +1D804;SIGNWRITING HAND-HINGE INDEX;So;0;L;;;;;N;;;;; +1D805;SIGNWRITING HAND-ANGLE INDEX;So;0;L;;;;;N;;;;; +1D806;SIGNWRITING HAND-FIST INDEX BENT;So;0;L;;;;;N;;;;; +1D807;SIGNWRITING HAND-CIRCLE INDEX BENT;So;0;L;;;;;N;;;;; +1D808;SIGNWRITING HAND-FIST THUMB UNDER INDEX BENT;So;0;L;;;;;N;;;;; +1D809;SIGNWRITING HAND-FIST INDEX RAISED KNUCKLE;So;0;L;;;;;N;;;;; +1D80A;SIGNWRITING HAND-FIST INDEX CUPPED;So;0;L;;;;;N;;;;; +1D80B;SIGNWRITING HAND-FIST INDEX HINGED;So;0;L;;;;;N;;;;; +1D80C;SIGNWRITING HAND-FIST INDEX HINGED LOW;So;0;L;;;;;N;;;;; +1D80D;SIGNWRITING HAND-CIRCLE INDEX HINGE;So;0;L;;;;;N;;;;; +1D80E;SIGNWRITING HAND-FIST INDEX MIDDLE;So;0;L;;;;;N;;;;; +1D80F;SIGNWRITING HAND-CIRCLE INDEX MIDDLE;So;0;L;;;;;N;;;;; +1D810;SIGNWRITING HAND-FIST INDEX MIDDLE BENT;So;0;L;;;;;N;;;;; +1D811;SIGNWRITING HAND-FIST INDEX MIDDLE RAISED KNUCKLES;So;0;L;;;;;N;;;;; +1D812;SIGNWRITING HAND-FIST INDEX MIDDLE HINGED;So;0;L;;;;;N;;;;; +1D813;SIGNWRITING HAND-FIST INDEX UP MIDDLE HINGED;So;0;L;;;;;N;;;;; +1D814;SIGNWRITING HAND-FIST INDEX HINGED MIDDLE UP;So;0;L;;;;;N;;;;; +1D815;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED;So;0;L;;;;;N;;;;; +1D816;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED INDEX BENT;So;0;L;;;;;N;;;;; +1D817;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED MIDDLE BENT;So;0;L;;;;;N;;;;; +1D818;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED CUPPED;So;0;L;;;;;N;;;;; +1D819;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED HINGED;So;0;L;;;;;N;;;;; +1D81A;SIGNWRITING HAND-FIST INDEX MIDDLE CROSSED;So;0;L;;;;;N;;;;; +1D81B;SIGNWRITING HAND-CIRCLE INDEX MIDDLE CROSSED;So;0;L;;;;;N;;;;; +1D81C;SIGNWRITING HAND-FIST MIDDLE BENT OVER INDEX;So;0;L;;;;;N;;;;; +1D81D;SIGNWRITING HAND-FIST INDEX BENT OVER MIDDLE;So;0;L;;;;;N;;;;; +1D81E;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB;So;0;L;;;;;N;;;;; +1D81F;SIGNWRITING HAND-CIRCLE INDEX MIDDLE THUMB;So;0;L;;;;;N;;;;; +1D820;SIGNWRITING HAND-FIST INDEX MIDDLE STRAIGHT THUMB BENT;So;0;L;;;;;N;;;;; +1D821;SIGNWRITING HAND-FIST INDEX MIDDLE BENT THUMB STRAIGHT;So;0;L;;;;;N;;;;; +1D822;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB BENT;So;0;L;;;;;N;;;;; +1D823;SIGNWRITING HAND-FIST INDEX MIDDLE HINGED SPREAD THUMB SIDE;So;0;L;;;;;N;;;;; +1D824;SIGNWRITING HAND-FIST INDEX UP MIDDLE HINGED THUMB SIDE;So;0;L;;;;;N;;;;; +1D825;SIGNWRITING HAND-FIST INDEX UP MIDDLE HINGED THUMB CONJOINED;So;0;L;;;;;N;;;;; +1D826;SIGNWRITING HAND-FIST INDEX HINGED MIDDLE UP THUMB SIDE;So;0;L;;;;;N;;;;; +1D827;SIGNWRITING HAND-FIST INDEX MIDDLE UP SPREAD THUMB FORWARD;So;0;L;;;;;N;;;;; +1D828;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB CUPPED;So;0;L;;;;;N;;;;; +1D829;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB CIRCLED;So;0;L;;;;;N;;;;; +1D82A;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB HOOKED;So;0;L;;;;;N;;;;; +1D82B;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB HINGED;So;0;L;;;;;N;;;;; +1D82C;SIGNWRITING HAND-FIST THUMB BETWEEN INDEX MIDDLE STRAIGHT;So;0;L;;;;;N;;;;; +1D82D;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED THUMB SIDE;So;0;L;;;;;N;;;;; +1D82E;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED THUMB SIDE CONJOINED;So;0;L;;;;;N;;;;; +1D82F;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED THUMB SIDE BENT;So;0;L;;;;;N;;;;; +1D830;SIGNWRITING HAND-FIST MIDDLE THUMB HOOKED INDEX UP;So;0;L;;;;;N;;;;; +1D831;SIGNWRITING HAND-FIST INDEX THUMB HOOKED MIDDLE UP;So;0;L;;;;;N;;;;; +1D832;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED HINGED THUMB SIDE;So;0;L;;;;;N;;;;; +1D833;SIGNWRITING HAND-FIST INDEX MIDDLE CROSSED THUMB SIDE;So;0;L;;;;;N;;;;; +1D834;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED THUMB FORWARD;So;0;L;;;;;N;;;;; +1D835;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED CUPPED THUMB FORWARD;So;0;L;;;;;N;;;;; +1D836;SIGNWRITING HAND-FIST MIDDLE THUMB CUPPED INDEX UP;So;0;L;;;;;N;;;;; +1D837;SIGNWRITING HAND-FIST INDEX THUMB CUPPED MIDDLE UP;So;0;L;;;;;N;;;;; +1D838;SIGNWRITING HAND-FIST MIDDLE THUMB CIRCLED INDEX UP;So;0;L;;;;;N;;;;; +1D839;SIGNWRITING HAND-FIST MIDDLE THUMB CIRCLED INDEX HINGED;So;0;L;;;;;N;;;;; +1D83A;SIGNWRITING HAND-FIST INDEX THUMB ANGLED OUT MIDDLE UP;So;0;L;;;;;N;;;;; +1D83B;SIGNWRITING HAND-FIST INDEX THUMB ANGLED IN MIDDLE UP;So;0;L;;;;;N;;;;; +1D83C;SIGNWRITING HAND-FIST INDEX THUMB CIRCLED MIDDLE UP;So;0;L;;;;;N;;;;; +1D83D;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB CONJOINED HINGED;So;0;L;;;;;N;;;;; +1D83E;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB ANGLED OUT;So;0;L;;;;;N;;;;; +1D83F;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB ANGLED;So;0;L;;;;;N;;;;; +1D840;SIGNWRITING HAND-FIST MIDDLE THUMB ANGLED OUT INDEX UP;So;0;L;;;;;N;;;;; +1D841;SIGNWRITING HAND-FIST MIDDLE THUMB ANGLED OUT INDEX CROSSED;So;0;L;;;;;N;;;;; +1D842;SIGNWRITING HAND-FIST MIDDLE THUMB ANGLED INDEX UP;So;0;L;;;;;N;;;;; +1D843;SIGNWRITING HAND-FIST INDEX THUMB HOOKED MIDDLE HINGED;So;0;L;;;;;N;;;;; +1D844;SIGNWRITING HAND-FLAT FOUR FINGERS;So;0;L;;;;;N;;;;; +1D845;SIGNWRITING HAND-FLAT FOUR FINGERS BENT;So;0;L;;;;;N;;;;; +1D846;SIGNWRITING HAND-FLAT FOUR FINGERS HINGED;So;0;L;;;;;N;;;;; +1D847;SIGNWRITING HAND-FLAT FOUR FINGERS CONJOINED;So;0;L;;;;;N;;;;; +1D848;SIGNWRITING HAND-FLAT FOUR FINGERS CONJOINED SPLIT;So;0;L;;;;;N;;;;; +1D849;SIGNWRITING HAND-CLAW FOUR FINGERS CONJOINED;So;0;L;;;;;N;;;;; +1D84A;SIGNWRITING HAND-FIST FOUR FINGERS CONJOINED BENT;So;0;L;;;;;N;;;;; +1D84B;SIGNWRITING HAND-HINGE FOUR FINGERS CONJOINED;So;0;L;;;;;N;;;;; +1D84C;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD;So;0;L;;;;;N;;;;; +1D84D;SIGNWRITING HAND-FLAT HEEL FIVE FINGERS SPREAD;So;0;L;;;;;N;;;;; +1D84E;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD FOUR BENT;So;0;L;;;;;N;;;;; +1D84F;SIGNWRITING HAND-FLAT HEEL FIVE FINGERS SPREAD FOUR BENT;So;0;L;;;;;N;;;;; +1D850;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD BENT;So;0;L;;;;;N;;;;; +1D851;SIGNWRITING HAND-FLAT HEEL FIVE FINGERS SPREAD BENT;So;0;L;;;;;N;;;;; +1D852;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD THUMB FORWARD;So;0;L;;;;;N;;;;; +1D853;SIGNWRITING HAND-CUP FIVE FINGERS SPREAD;So;0;L;;;;;N;;;;; +1D854;SIGNWRITING HAND-CUP FIVE FINGERS SPREAD OPEN;So;0;L;;;;;N;;;;; +1D855;SIGNWRITING HAND-HINGE FIVE FINGERS SPREAD OPEN;So;0;L;;;;;N;;;;; +1D856;SIGNWRITING HAND-OVAL FIVE FINGERS SPREAD;So;0;L;;;;;N;;;;; +1D857;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD HINGED;So;0;L;;;;;N;;;;; +1D858;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD HINGED THUMB SIDE;So;0;L;;;;;N;;;;; +1D859;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD HINGED NO THUMB;So;0;L;;;;;N;;;;; +1D85A;SIGNWRITING HAND-FLAT;So;0;L;;;;;N;;;;; +1D85B;SIGNWRITING HAND-FLAT BETWEEN PALM FACINGS;So;0;L;;;;;N;;;;; +1D85C;SIGNWRITING HAND-FLAT HEEL;So;0;L;;;;;N;;;;; +1D85D;SIGNWRITING HAND-FLAT THUMB SIDE;So;0;L;;;;;N;;;;; +1D85E;SIGNWRITING HAND-FLAT HEEL THUMB SIDE;So;0;L;;;;;N;;;;; +1D85F;SIGNWRITING HAND-FLAT THUMB BENT;So;0;L;;;;;N;;;;; +1D860;SIGNWRITING HAND-FLAT THUMB FORWARD;So;0;L;;;;;N;;;;; +1D861;SIGNWRITING HAND-FLAT SPLIT INDEX THUMB SIDE;So;0;L;;;;;N;;;;; +1D862;SIGNWRITING HAND-FLAT SPLIT CENTRE;So;0;L;;;;;N;;;;; +1D863;SIGNWRITING HAND-FLAT SPLIT CENTRE THUMB SIDE;So;0;L;;;;;N;;;;; +1D864;SIGNWRITING HAND-FLAT SPLIT CENTRE THUMB SIDE BENT;So;0;L;;;;;N;;;;; +1D865;SIGNWRITING HAND-FLAT SPLIT LITTLE;So;0;L;;;;;N;;;;; +1D866;SIGNWRITING HAND-CLAW;So;0;L;;;;;N;;;;; +1D867;SIGNWRITING HAND-CLAW THUMB SIDE;So;0;L;;;;;N;;;;; +1D868;SIGNWRITING HAND-CLAW NO THUMB;So;0;L;;;;;N;;;;; +1D869;SIGNWRITING HAND-CLAW THUMB FORWARD;So;0;L;;;;;N;;;;; +1D86A;SIGNWRITING HAND-HOOK CURLICUE;So;0;L;;;;;N;;;;; +1D86B;SIGNWRITING HAND-HOOK;So;0;L;;;;;N;;;;; +1D86C;SIGNWRITING HAND-CUP OPEN;So;0;L;;;;;N;;;;; +1D86D;SIGNWRITING HAND-CUP;So;0;L;;;;;N;;;;; +1D86E;SIGNWRITING HAND-CUP OPEN THUMB SIDE;So;0;L;;;;;N;;;;; +1D86F;SIGNWRITING HAND-CUP THUMB SIDE;So;0;L;;;;;N;;;;; +1D870;SIGNWRITING HAND-CUP OPEN NO THUMB;So;0;L;;;;;N;;;;; +1D871;SIGNWRITING HAND-CUP NO THUMB;So;0;L;;;;;N;;;;; +1D872;SIGNWRITING HAND-CUP OPEN THUMB FORWARD;So;0;L;;;;;N;;;;; +1D873;SIGNWRITING HAND-CUP THUMB FORWARD;So;0;L;;;;;N;;;;; +1D874;SIGNWRITING HAND-CURLICUE OPEN;So;0;L;;;;;N;;;;; +1D875;SIGNWRITING HAND-CURLICUE;So;0;L;;;;;N;;;;; +1D876;SIGNWRITING HAND-CIRCLE;So;0;L;;;;;N;;;;; +1D877;SIGNWRITING HAND-OVAL;So;0;L;;;;;N;;;;; +1D878;SIGNWRITING HAND-OVAL THUMB SIDE;So;0;L;;;;;N;;;;; +1D879;SIGNWRITING HAND-OVAL NO THUMB;So;0;L;;;;;N;;;;; +1D87A;SIGNWRITING HAND-OVAL THUMB FORWARD;So;0;L;;;;;N;;;;; +1D87B;SIGNWRITING HAND-HINGE OPEN;So;0;L;;;;;N;;;;; +1D87C;SIGNWRITING HAND-HINGE OPEN THUMB FORWARD;So;0;L;;;;;N;;;;; +1D87D;SIGNWRITING HAND-HINGE;So;0;L;;;;;N;;;;; +1D87E;SIGNWRITING HAND-HINGE SMALL;So;0;L;;;;;N;;;;; +1D87F;SIGNWRITING HAND-HINGE OPEN THUMB SIDE;So;0;L;;;;;N;;;;; +1D880;SIGNWRITING HAND-HINGE THUMB SIDE;So;0;L;;;;;N;;;;; +1D881;SIGNWRITING HAND-HINGE OPEN NO THUMB;So;0;L;;;;;N;;;;; +1D882;SIGNWRITING HAND-HINGE NO THUMB;So;0;L;;;;;N;;;;; +1D883;SIGNWRITING HAND-HINGE THUMB SIDE TOUCHING INDEX;So;0;L;;;;;N;;;;; +1D884;SIGNWRITING HAND-HINGE THUMB BETWEEN MIDDLE RING;So;0;L;;;;;N;;;;; +1D885;SIGNWRITING HAND-ANGLE;So;0;L;;;;;N;;;;; +1D886;SIGNWRITING HAND-FIST INDEX MIDDLE RING;So;0;L;;;;;N;;;;; +1D887;SIGNWRITING HAND-CIRCLE INDEX MIDDLE RING;So;0;L;;;;;N;;;;; +1D888;SIGNWRITING HAND-HINGE INDEX MIDDLE RING;So;0;L;;;;;N;;;;; +1D889;SIGNWRITING HAND-ANGLE INDEX MIDDLE RING;So;0;L;;;;;N;;;;; +1D88A;SIGNWRITING HAND-HINGE LITTLE;So;0;L;;;;;N;;;;; +1D88B;SIGNWRITING HAND-FIST INDEX MIDDLE RING BENT;So;0;L;;;;;N;;;;; +1D88C;SIGNWRITING HAND-FIST INDEX MIDDLE RING CONJOINED;So;0;L;;;;;N;;;;; +1D88D;SIGNWRITING HAND-HINGE INDEX MIDDLE RING CONJOINED;So;0;L;;;;;N;;;;; +1D88E;SIGNWRITING HAND-FIST LITTLE DOWN;So;0;L;;;;;N;;;;; +1D88F;SIGNWRITING HAND-FIST LITTLE DOWN RIPPLE STRAIGHT;So;0;L;;;;;N;;;;; +1D890;SIGNWRITING HAND-FIST LITTLE DOWN RIPPLE CURVED;So;0;L;;;;;N;;;;; +1D891;SIGNWRITING HAND-FIST LITTLE DOWN OTHERS CIRCLED;So;0;L;;;;;N;;;;; +1D892;SIGNWRITING HAND-FIST LITTLE UP;So;0;L;;;;;N;;;;; +1D893;SIGNWRITING HAND-FIST THUMB UNDER LITTLE UP;So;0;L;;;;;N;;;;; +1D894;SIGNWRITING HAND-CIRCLE LITTLE UP;So;0;L;;;;;N;;;;; +1D895;SIGNWRITING HAND-OVAL LITTLE UP;So;0;L;;;;;N;;;;; +1D896;SIGNWRITING HAND-ANGLE LITTLE UP;So;0;L;;;;;N;;;;; +1D897;SIGNWRITING HAND-FIST LITTLE RAISED KNUCKLE;So;0;L;;;;;N;;;;; +1D898;SIGNWRITING HAND-FIST LITTLE BENT;So;0;L;;;;;N;;;;; +1D899;SIGNWRITING HAND-FIST LITTLE TOUCHES THUMB;So;0;L;;;;;N;;;;; +1D89A;SIGNWRITING HAND-FIST LITTLE THUMB;So;0;L;;;;;N;;;;; +1D89B;SIGNWRITING HAND-HINGE LITTLE THUMB;So;0;L;;;;;N;;;;; +1D89C;SIGNWRITING HAND-FIST LITTLE INDEX THUMB;So;0;L;;;;;N;;;;; +1D89D;SIGNWRITING HAND-HINGE LITTLE INDEX THUMB;So;0;L;;;;;N;;;;; +1D89E;SIGNWRITING HAND-ANGLE LITTLE INDEX THUMB INDEX THUMB OUT;So;0;L;;;;;N;;;;; +1D89F;SIGNWRITING HAND-ANGLE LITTLE INDEX THUMB INDEX THUMB;So;0;L;;;;;N;;;;; +1D8A0;SIGNWRITING HAND-FIST LITTLE INDEX;So;0;L;;;;;N;;;;; +1D8A1;SIGNWRITING HAND-CIRCLE LITTLE INDEX;So;0;L;;;;;N;;;;; +1D8A2;SIGNWRITING HAND-HINGE LITTLE INDEX;So;0;L;;;;;N;;;;; +1D8A3;SIGNWRITING HAND-ANGLE LITTLE INDEX;So;0;L;;;;;N;;;;; +1D8A4;SIGNWRITING HAND-FIST INDEX MIDDLE LITTLE;So;0;L;;;;;N;;;;; +1D8A5;SIGNWRITING HAND-CIRCLE INDEX MIDDLE LITTLE;So;0;L;;;;;N;;;;; +1D8A6;SIGNWRITING HAND-HINGE INDEX MIDDLE LITTLE;So;0;L;;;;;N;;;;; +1D8A7;SIGNWRITING HAND-HINGE RING;So;0;L;;;;;N;;;;; +1D8A8;SIGNWRITING HAND-ANGLE INDEX MIDDLE LITTLE;So;0;L;;;;;N;;;;; +1D8A9;SIGNWRITING HAND-FIST INDEX MIDDLE CROSS LITTLE;So;0;L;;;;;N;;;;; +1D8AA;SIGNWRITING HAND-CIRCLE INDEX MIDDLE CROSS LITTLE;So;0;L;;;;;N;;;;; +1D8AB;SIGNWRITING HAND-FIST RING DOWN;So;0;L;;;;;N;;;;; +1D8AC;SIGNWRITING HAND-HINGE RING DOWN INDEX THUMB HOOK MIDDLE;So;0;L;;;;;N;;;;; +1D8AD;SIGNWRITING HAND-ANGLE RING DOWN MIDDLE THUMB INDEX CROSS;So;0;L;;;;;N;;;;; +1D8AE;SIGNWRITING HAND-FIST RING UP;So;0;L;;;;;N;;;;; +1D8AF;SIGNWRITING HAND-FIST RING RAISED KNUCKLE;So;0;L;;;;;N;;;;; +1D8B0;SIGNWRITING HAND-FIST RING LITTLE;So;0;L;;;;;N;;;;; +1D8B1;SIGNWRITING HAND-CIRCLE RING LITTLE;So;0;L;;;;;N;;;;; +1D8B2;SIGNWRITING HAND-OVAL RING LITTLE;So;0;L;;;;;N;;;;; +1D8B3;SIGNWRITING HAND-ANGLE RING LITTLE;So;0;L;;;;;N;;;;; +1D8B4;SIGNWRITING HAND-FIST RING MIDDLE;So;0;L;;;;;N;;;;; +1D8B5;SIGNWRITING HAND-FIST RING MIDDLE CONJOINED;So;0;L;;;;;N;;;;; +1D8B6;SIGNWRITING HAND-FIST RING MIDDLE RAISED KNUCKLES;So;0;L;;;;;N;;;;; +1D8B7;SIGNWRITING HAND-FIST RING INDEX;So;0;L;;;;;N;;;;; +1D8B8;SIGNWRITING HAND-FIST RING THUMB;So;0;L;;;;;N;;;;; +1D8B9;SIGNWRITING HAND-HOOK RING THUMB;So;0;L;;;;;N;;;;; +1D8BA;SIGNWRITING HAND-FIST INDEX RING LITTLE;So;0;L;;;;;N;;;;; +1D8BB;SIGNWRITING HAND-CIRCLE INDEX RING LITTLE;So;0;L;;;;;N;;;;; +1D8BC;SIGNWRITING HAND-CURLICUE INDEX RING LITTLE ON;So;0;L;;;;;N;;;;; +1D8BD;SIGNWRITING HAND-HOOK INDEX RING LITTLE OUT;So;0;L;;;;;N;;;;; +1D8BE;SIGNWRITING HAND-HOOK INDEX RING LITTLE IN;So;0;L;;;;;N;;;;; +1D8BF;SIGNWRITING HAND-HOOK INDEX RING LITTLE UNDER;So;0;L;;;;;N;;;;; +1D8C0;SIGNWRITING HAND-CUP INDEX RING LITTLE;So;0;L;;;;;N;;;;; +1D8C1;SIGNWRITING HAND-HINGE INDEX RING LITTLE;So;0;L;;;;;N;;;;; +1D8C2;SIGNWRITING HAND-ANGLE INDEX RING LITTLE OUT;So;0;L;;;;;N;;;;; +1D8C3;SIGNWRITING HAND-ANGLE INDEX RING LITTLE;So;0;L;;;;;N;;;;; +1D8C4;SIGNWRITING HAND-FIST MIDDLE DOWN;So;0;L;;;;;N;;;;; +1D8C5;SIGNWRITING HAND-HINGE MIDDLE;So;0;L;;;;;N;;;;; +1D8C6;SIGNWRITING HAND-FIST MIDDLE UP;So;0;L;;;;;N;;;;; +1D8C7;SIGNWRITING HAND-CIRCLE MIDDLE UP;So;0;L;;;;;N;;;;; +1D8C8;SIGNWRITING HAND-FIST MIDDLE RAISED KNUCKLE;So;0;L;;;;;N;;;;; +1D8C9;SIGNWRITING HAND-FIST MIDDLE UP THUMB SIDE;So;0;L;;;;;N;;;;; +1D8CA;SIGNWRITING HAND-HOOK MIDDLE THUMB;So;0;L;;;;;N;;;;; +1D8CB;SIGNWRITING HAND-FIST MIDDLE THUMB LITTLE;So;0;L;;;;;N;;;;; +1D8CC;SIGNWRITING HAND-FIST MIDDLE LITTLE;So;0;L;;;;;N;;;;; +1D8CD;SIGNWRITING HAND-FIST MIDDLE RING LITTLE;So;0;L;;;;;N;;;;; +1D8CE;SIGNWRITING HAND-CIRCLE MIDDLE RING LITTLE;So;0;L;;;;;N;;;;; +1D8CF;SIGNWRITING HAND-CURLICUE MIDDLE RING LITTLE ON;So;0;L;;;;;N;;;;; +1D8D0;SIGNWRITING HAND-CUP MIDDLE RING LITTLE;So;0;L;;;;;N;;;;; +1D8D1;SIGNWRITING HAND-HINGE MIDDLE RING LITTLE;So;0;L;;;;;N;;;;; +1D8D2;SIGNWRITING HAND-ANGLE MIDDLE RING LITTLE OUT;So;0;L;;;;;N;;;;; +1D8D3;SIGNWRITING HAND-ANGLE MIDDLE RING LITTLE IN;So;0;L;;;;;N;;;;; +1D8D4;SIGNWRITING HAND-ANGLE MIDDLE RING LITTLE;So;0;L;;;;;N;;;;; +1D8D5;SIGNWRITING HAND-CIRCLE MIDDLE RING LITTLE BENT;So;0;L;;;;;N;;;;; +1D8D6;SIGNWRITING HAND-CLAW MIDDLE RING LITTLE CONJOINED;So;0;L;;;;;N;;;;; +1D8D7;SIGNWRITING HAND-CLAW MIDDLE RING LITTLE CONJOINED SIDE;So;0;L;;;;;N;;;;; +1D8D8;SIGNWRITING HAND-HOOK MIDDLE RING LITTLE CONJOINED OUT;So;0;L;;;;;N;;;;; +1D8D9;SIGNWRITING HAND-HOOK MIDDLE RING LITTLE CONJOINED IN;So;0;L;;;;;N;;;;; +1D8DA;SIGNWRITING HAND-HOOK MIDDLE RING LITTLE CONJOINED;So;0;L;;;;;N;;;;; +1D8DB;SIGNWRITING HAND-HINGE INDEX HINGED;So;0;L;;;;;N;;;;; +1D8DC;SIGNWRITING HAND-FIST INDEX THUMB SIDE;So;0;L;;;;;N;;;;; +1D8DD;SIGNWRITING HAND-HINGE INDEX THUMB SIDE;So;0;L;;;;;N;;;;; +1D8DE;SIGNWRITING HAND-FIST INDEX THUMB SIDE THUMB DIAGONAL;So;0;L;;;;;N;;;;; +1D8DF;SIGNWRITING HAND-FIST INDEX THUMB SIDE THUMB CONJOINED;So;0;L;;;;;N;;;;; +1D8E0;SIGNWRITING HAND-FIST INDEX THUMB SIDE THUMB BENT;So;0;L;;;;;N;;;;; +1D8E1;SIGNWRITING HAND-FIST INDEX THUMB SIDE INDEX BENT;So;0;L;;;;;N;;;;; +1D8E2;SIGNWRITING HAND-FIST INDEX THUMB SIDE BOTH BENT;So;0;L;;;;;N;;;;; +1D8E3;SIGNWRITING HAND-FIST INDEX THUMB SIDE INDEX HINGE;So;0;L;;;;;N;;;;; +1D8E4;SIGNWRITING HAND-FIST INDEX THUMB FORWARD INDEX STRAIGHT;So;0;L;;;;;N;;;;; +1D8E5;SIGNWRITING HAND-FIST INDEX THUMB FORWARD INDEX BENT;So;0;L;;;;;N;;;;; +1D8E6;SIGNWRITING HAND-FIST INDEX THUMB HOOK;So;0;L;;;;;N;;;;; +1D8E7;SIGNWRITING HAND-FIST INDEX THUMB CURLICUE;So;0;L;;;;;N;;;;; +1D8E8;SIGNWRITING HAND-FIST INDEX THUMB CURVE THUMB INSIDE;So;0;L;;;;;N;;;;; +1D8E9;SIGNWRITING HAND-CLAW INDEX THUMB CURVE THUMB INSIDE;So;0;L;;;;;N;;;;; +1D8EA;SIGNWRITING HAND-FIST INDEX THUMB CURVE THUMB UNDER;So;0;L;;;;;N;;;;; +1D8EB;SIGNWRITING HAND-FIST INDEX THUMB CIRCLE;So;0;L;;;;;N;;;;; +1D8EC;SIGNWRITING HAND-CUP INDEX THUMB;So;0;L;;;;;N;;;;; +1D8ED;SIGNWRITING HAND-CUP INDEX THUMB OPEN;So;0;L;;;;;N;;;;; +1D8EE;SIGNWRITING HAND-HINGE INDEX THUMB OPEN;So;0;L;;;;;N;;;;; +1D8EF;SIGNWRITING HAND-HINGE INDEX THUMB LARGE;So;0;L;;;;;N;;;;; +1D8F0;SIGNWRITING HAND-HINGE INDEX THUMB;So;0;L;;;;;N;;;;; +1D8F1;SIGNWRITING HAND-HINGE INDEX THUMB SMALL;So;0;L;;;;;N;;;;; +1D8F2;SIGNWRITING HAND-ANGLE INDEX THUMB OUT;So;0;L;;;;;N;;;;; +1D8F3;SIGNWRITING HAND-ANGLE INDEX THUMB IN;So;0;L;;;;;N;;;;; +1D8F4;SIGNWRITING HAND-ANGLE INDEX THUMB;So;0;L;;;;;N;;;;; +1D8F5;SIGNWRITING HAND-FIST THUMB;So;0;L;;;;;N;;;;; +1D8F6;SIGNWRITING HAND-FIST THUMB HEEL;So;0;L;;;;;N;;;;; +1D8F7;SIGNWRITING HAND-FIST THUMB SIDE DIAGONAL;So;0;L;;;;;N;;;;; +1D8F8;SIGNWRITING HAND-FIST THUMB SIDE CONJOINED;So;0;L;;;;;N;;;;; +1D8F9;SIGNWRITING HAND-FIST THUMB SIDE BENT;So;0;L;;;;;N;;;;; +1D8FA;SIGNWRITING HAND-FIST THUMB FORWARD;So;0;L;;;;;N;;;;; +1D8FB;SIGNWRITING HAND-FIST THUMB BETWEEN INDEX MIDDLE;So;0;L;;;;;N;;;;; +1D8FC;SIGNWRITING HAND-FIST THUMB BETWEEN MIDDLE RING;So;0;L;;;;;N;;;;; +1D8FD;SIGNWRITING HAND-FIST THUMB BETWEEN RING LITTLE;So;0;L;;;;;N;;;;; +1D8FE;SIGNWRITING HAND-FIST THUMB UNDER TWO FINGERS;So;0;L;;;;;N;;;;; +1D8FF;SIGNWRITING HAND-FIST THUMB OVER TWO FINGERS;So;0;L;;;;;N;;;;; +1D900;SIGNWRITING HAND-FIST THUMB UNDER THREE FINGERS;So;0;L;;;;;N;;;;; +1D901;SIGNWRITING HAND-FIST THUMB UNDER FOUR FINGERS;So;0;L;;;;;N;;;;; +1D902;SIGNWRITING HAND-FIST THUMB OVER FOUR RAISED KNUCKLES;So;0;L;;;;;N;;;;; +1D903;SIGNWRITING HAND-FIST;So;0;L;;;;;N;;;;; +1D904;SIGNWRITING HAND-FIST HEEL;So;0;L;;;;;N;;;;; +1D905;SIGNWRITING TOUCH SINGLE;So;0;L;;;;;N;;;;; +1D906;SIGNWRITING TOUCH MULTIPLE;So;0;L;;;;;N;;;;; +1D907;SIGNWRITING TOUCH BETWEEN;So;0;L;;;;;N;;;;; +1D908;SIGNWRITING GRASP SINGLE;So;0;L;;;;;N;;;;; +1D909;SIGNWRITING GRASP MULTIPLE;So;0;L;;;;;N;;;;; +1D90A;SIGNWRITING GRASP BETWEEN;So;0;L;;;;;N;;;;; +1D90B;SIGNWRITING STRIKE SINGLE;So;0;L;;;;;N;;;;; +1D90C;SIGNWRITING STRIKE MULTIPLE;So;0;L;;;;;N;;;;; +1D90D;SIGNWRITING STRIKE BETWEEN;So;0;L;;;;;N;;;;; +1D90E;SIGNWRITING BRUSH SINGLE;So;0;L;;;;;N;;;;; +1D90F;SIGNWRITING BRUSH MULTIPLE;So;0;L;;;;;N;;;;; +1D910;SIGNWRITING BRUSH BETWEEN;So;0;L;;;;;N;;;;; +1D911;SIGNWRITING RUB SINGLE;So;0;L;;;;;N;;;;; +1D912;SIGNWRITING RUB MULTIPLE;So;0;L;;;;;N;;;;; +1D913;SIGNWRITING RUB BETWEEN;So;0;L;;;;;N;;;;; +1D914;SIGNWRITING SURFACE SYMBOLS;So;0;L;;;;;N;;;;; +1D915;SIGNWRITING SURFACE BETWEEN;So;0;L;;;;;N;;;;; +1D916;SIGNWRITING SQUEEZE LARGE SINGLE;So;0;L;;;;;N;;;;; +1D917;SIGNWRITING SQUEEZE SMALL SINGLE;So;0;L;;;;;N;;;;; +1D918;SIGNWRITING SQUEEZE LARGE MULTIPLE;So;0;L;;;;;N;;;;; +1D919;SIGNWRITING SQUEEZE SMALL MULTIPLE;So;0;L;;;;;N;;;;; +1D91A;SIGNWRITING SQUEEZE SEQUENTIAL;So;0;L;;;;;N;;;;; +1D91B;SIGNWRITING FLICK LARGE SINGLE;So;0;L;;;;;N;;;;; +1D91C;SIGNWRITING FLICK SMALL SINGLE;So;0;L;;;;;N;;;;; +1D91D;SIGNWRITING FLICK LARGE MULTIPLE;So;0;L;;;;;N;;;;; +1D91E;SIGNWRITING FLICK SMALL MULTIPLE;So;0;L;;;;;N;;;;; +1D91F;SIGNWRITING FLICK SEQUENTIAL;So;0;L;;;;;N;;;;; +1D920;SIGNWRITING SQUEEZE FLICK ALTERNATING;So;0;L;;;;;N;;;;; +1D921;SIGNWRITING MOVEMENT-HINGE UP DOWN LARGE;So;0;L;;;;;N;;;;; +1D922;SIGNWRITING MOVEMENT-HINGE UP DOWN SMALL;So;0;L;;;;;N;;;;; +1D923;SIGNWRITING MOVEMENT-HINGE UP SEQUENTIAL;So;0;L;;;;;N;;;;; +1D924;SIGNWRITING MOVEMENT-HINGE DOWN SEQUENTIAL;So;0;L;;;;;N;;;;; +1D925;SIGNWRITING MOVEMENT-HINGE UP DOWN ALTERNATING LARGE;So;0;L;;;;;N;;;;; +1D926;SIGNWRITING MOVEMENT-HINGE UP DOWN ALTERNATING SMALL;So;0;L;;;;;N;;;;; +1D927;SIGNWRITING MOVEMENT-HINGE SIDE TO SIDE SCISSORS;So;0;L;;;;;N;;;;; +1D928;SIGNWRITING MOVEMENT-WALLPLANE FINGER CONTACT;So;0;L;;;;;N;;;;; +1D929;SIGNWRITING MOVEMENT-FLOORPLANE FINGER CONTACT;So;0;L;;;;;N;;;;; +1D92A;SIGNWRITING MOVEMENT-WALLPLANE SINGLE STRAIGHT SMALL;So;0;L;;;;;N;;;;; +1D92B;SIGNWRITING MOVEMENT-WALLPLANE SINGLE STRAIGHT MEDIUM;So;0;L;;;;;N;;;;; +1D92C;SIGNWRITING MOVEMENT-WALLPLANE SINGLE STRAIGHT LARGE;So;0;L;;;;;N;;;;; +1D92D;SIGNWRITING MOVEMENT-WALLPLANE SINGLE STRAIGHT LARGEST;So;0;L;;;;;N;;;;; +1D92E;SIGNWRITING MOVEMENT-WALLPLANE SINGLE WRIST FLEX;So;0;L;;;;;N;;;;; +1D92F;SIGNWRITING MOVEMENT-WALLPLANE DOUBLE STRAIGHT;So;0;L;;;;;N;;;;; +1D930;SIGNWRITING MOVEMENT-WALLPLANE DOUBLE WRIST FLEX;So;0;L;;;;;N;;;;; +1D931;SIGNWRITING MOVEMENT-WALLPLANE DOUBLE ALTERNATING;So;0;L;;;;;N;;;;; +1D932;SIGNWRITING MOVEMENT-WALLPLANE DOUBLE ALTERNATING WRIST FLEX;So;0;L;;;;;N;;;;; +1D933;SIGNWRITING MOVEMENT-WALLPLANE CROSS;So;0;L;;;;;N;;;;; +1D934;SIGNWRITING MOVEMENT-WALLPLANE TRIPLE STRAIGHT MOVEMENT;So;0;L;;;;;N;;;;; +1D935;SIGNWRITING MOVEMENT-WALLPLANE TRIPLE WRIST FLEX;So;0;L;;;;;N;;;;; +1D936;SIGNWRITING MOVEMENT-WALLPLANE TRIPLE ALTERNATING;So;0;L;;;;;N;;;;; +1D937;SIGNWRITING MOVEMENT-WALLPLANE TRIPLE ALTERNATING WRIST FLEX;So;0;L;;;;;N;;;;; +1D938;SIGNWRITING MOVEMENT-WALLPLANE BEND SMALL;So;0;L;;;;;N;;;;; +1D939;SIGNWRITING MOVEMENT-WALLPLANE BEND MEDIUM;So;0;L;;;;;N;;;;; +1D93A;SIGNWRITING MOVEMENT-WALLPLANE BEND LARGE;So;0;L;;;;;N;;;;; +1D93B;SIGNWRITING MOVEMENT-WALLPLANE CORNER SMALL;So;0;L;;;;;N;;;;; +1D93C;SIGNWRITING MOVEMENT-WALLPLANE CORNER MEDIUM;So;0;L;;;;;N;;;;; +1D93D;SIGNWRITING MOVEMENT-WALLPLANE CORNER LARGE;So;0;L;;;;;N;;;;; +1D93E;SIGNWRITING MOVEMENT-WALLPLANE CORNER ROTATION;So;0;L;;;;;N;;;;; +1D93F;SIGNWRITING MOVEMENT-WALLPLANE CHECK SMALL;So;0;L;;;;;N;;;;; +1D940;SIGNWRITING MOVEMENT-WALLPLANE CHECK MEDIUM;So;0;L;;;;;N;;;;; +1D941;SIGNWRITING MOVEMENT-WALLPLANE CHECK LARGE;So;0;L;;;;;N;;;;; +1D942;SIGNWRITING MOVEMENT-WALLPLANE BOX SMALL;So;0;L;;;;;N;;;;; +1D943;SIGNWRITING MOVEMENT-WALLPLANE BOX MEDIUM;So;0;L;;;;;N;;;;; +1D944;SIGNWRITING MOVEMENT-WALLPLANE BOX LARGE;So;0;L;;;;;N;;;;; +1D945;SIGNWRITING MOVEMENT-WALLPLANE ZIGZAG SMALL;So;0;L;;;;;N;;;;; +1D946;SIGNWRITING MOVEMENT-WALLPLANE ZIGZAG MEDIUM;So;0;L;;;;;N;;;;; +1D947;SIGNWRITING MOVEMENT-WALLPLANE ZIGZAG LARGE;So;0;L;;;;;N;;;;; +1D948;SIGNWRITING MOVEMENT-WALLPLANE PEAKS SMALL;So;0;L;;;;;N;;;;; +1D949;SIGNWRITING MOVEMENT-WALLPLANE PEAKS MEDIUM;So;0;L;;;;;N;;;;; +1D94A;SIGNWRITING MOVEMENT-WALLPLANE PEAKS LARGE;So;0;L;;;;;N;;;;; +1D94B;SIGNWRITING TRAVEL-WALLPLANE ROTATION-WALLPLANE SINGLE;So;0;L;;;;;N;;;;; +1D94C;SIGNWRITING TRAVEL-WALLPLANE ROTATION-WALLPLANE DOUBLE;So;0;L;;;;;N;;;;; +1D94D;SIGNWRITING TRAVEL-WALLPLANE ROTATION-WALLPLANE ALTERNATING;So;0;L;;;;;N;;;;; +1D94E;SIGNWRITING TRAVEL-WALLPLANE ROTATION-FLOORPLANE SINGLE;So;0;L;;;;;N;;;;; +1D94F;SIGNWRITING TRAVEL-WALLPLANE ROTATION-FLOORPLANE DOUBLE;So;0;L;;;;;N;;;;; +1D950;SIGNWRITING TRAVEL-WALLPLANE ROTATION-FLOORPLANE ALTERNATING;So;0;L;;;;;N;;;;; +1D951;SIGNWRITING TRAVEL-WALLPLANE SHAKING;So;0;L;;;;;N;;;;; +1D952;SIGNWRITING TRAVEL-WALLPLANE ARM SPIRAL SINGLE;So;0;L;;;;;N;;;;; +1D953;SIGNWRITING TRAVEL-WALLPLANE ARM SPIRAL DOUBLE;So;0;L;;;;;N;;;;; +1D954;SIGNWRITING TRAVEL-WALLPLANE ARM SPIRAL TRIPLE;So;0;L;;;;;N;;;;; +1D955;SIGNWRITING MOVEMENT-DIAGONAL AWAY SMALL;So;0;L;;;;;N;;;;; +1D956;SIGNWRITING MOVEMENT-DIAGONAL AWAY MEDIUM;So;0;L;;;;;N;;;;; +1D957;SIGNWRITING MOVEMENT-DIAGONAL AWAY LARGE;So;0;L;;;;;N;;;;; +1D958;SIGNWRITING MOVEMENT-DIAGONAL AWAY LARGEST;So;0;L;;;;;N;;;;; +1D959;SIGNWRITING MOVEMENT-DIAGONAL TOWARDS SMALL;So;0;L;;;;;N;;;;; +1D95A;SIGNWRITING MOVEMENT-DIAGONAL TOWARDS MEDIUM;So;0;L;;;;;N;;;;; +1D95B;SIGNWRITING MOVEMENT-DIAGONAL TOWARDS LARGE;So;0;L;;;;;N;;;;; +1D95C;SIGNWRITING MOVEMENT-DIAGONAL TOWARDS LARGEST;So;0;L;;;;;N;;;;; +1D95D;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN AWAY SMALL;So;0;L;;;;;N;;;;; +1D95E;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN AWAY MEDIUM;So;0;L;;;;;N;;;;; +1D95F;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN AWAY LARGE;So;0;L;;;;;N;;;;; +1D960;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN AWAY LARGEST;So;0;L;;;;;N;;;;; +1D961;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN TOWARDS SMALL;So;0;L;;;;;N;;;;; +1D962;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN TOWARDS MEDIUM;So;0;L;;;;;N;;;;; +1D963;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN TOWARDS LARGE;So;0;L;;;;;N;;;;; +1D964;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN TOWARDS LARGEST;So;0;L;;;;;N;;;;; +1D965;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE STRAIGHT SMALL;So;0;L;;;;;N;;;;; +1D966;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE STRAIGHT MEDIUM;So;0;L;;;;;N;;;;; +1D967;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE STRAIGHT LARGE;So;0;L;;;;;N;;;;; +1D968;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE STRAIGHT LARGEST;So;0;L;;;;;N;;;;; +1D969;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE WRIST FLEX;So;0;L;;;;;N;;;;; +1D96A;SIGNWRITING MOVEMENT-FLOORPLANE DOUBLE STRAIGHT;So;0;L;;;;;N;;;;; +1D96B;SIGNWRITING MOVEMENT-FLOORPLANE DOUBLE WRIST FLEX;So;0;L;;;;;N;;;;; +1D96C;SIGNWRITING MOVEMENT-FLOORPLANE DOUBLE ALTERNATING;So;0;L;;;;;N;;;;; +1D96D;SIGNWRITING MOVEMENT-FLOORPLANE DOUBLE ALTERNATING WRIST FLEX;So;0;L;;;;;N;;;;; +1D96E;SIGNWRITING MOVEMENT-FLOORPLANE CROSS;So;0;L;;;;;N;;;;; +1D96F;SIGNWRITING MOVEMENT-FLOORPLANE TRIPLE STRAIGHT MOVEMENT;So;0;L;;;;;N;;;;; +1D970;SIGNWRITING MOVEMENT-FLOORPLANE TRIPLE WRIST FLEX;So;0;L;;;;;N;;;;; +1D971;SIGNWRITING MOVEMENT-FLOORPLANE TRIPLE ALTERNATING MOVEMENT;So;0;L;;;;;N;;;;; +1D972;SIGNWRITING MOVEMENT-FLOORPLANE TRIPLE ALTERNATING WRIST FLEX;So;0;L;;;;;N;;;;; +1D973;SIGNWRITING MOVEMENT-FLOORPLANE BEND;So;0;L;;;;;N;;;;; +1D974;SIGNWRITING MOVEMENT-FLOORPLANE CORNER SMALL;So;0;L;;;;;N;;;;; +1D975;SIGNWRITING MOVEMENT-FLOORPLANE CORNER MEDIUM;So;0;L;;;;;N;;;;; +1D976;SIGNWRITING MOVEMENT-FLOORPLANE CORNER LARGE;So;0;L;;;;;N;;;;; +1D977;SIGNWRITING MOVEMENT-FLOORPLANE CHECK;So;0;L;;;;;N;;;;; +1D978;SIGNWRITING MOVEMENT-FLOORPLANE BOX SMALL;So;0;L;;;;;N;;;;; +1D979;SIGNWRITING MOVEMENT-FLOORPLANE BOX MEDIUM;So;0;L;;;;;N;;;;; +1D97A;SIGNWRITING MOVEMENT-FLOORPLANE BOX LARGE;So;0;L;;;;;N;;;;; +1D97B;SIGNWRITING MOVEMENT-FLOORPLANE ZIGZAG SMALL;So;0;L;;;;;N;;;;; +1D97C;SIGNWRITING MOVEMENT-FLOORPLANE ZIGZAG MEDIUM;So;0;L;;;;;N;;;;; +1D97D;SIGNWRITING MOVEMENT-FLOORPLANE ZIGZAG LARGE;So;0;L;;;;;N;;;;; +1D97E;SIGNWRITING MOVEMENT-FLOORPLANE PEAKS SMALL;So;0;L;;;;;N;;;;; +1D97F;SIGNWRITING MOVEMENT-FLOORPLANE PEAKS MEDIUM;So;0;L;;;;;N;;;;; +1D980;SIGNWRITING MOVEMENT-FLOORPLANE PEAKS LARGE;So;0;L;;;;;N;;;;; +1D981;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-FLOORPLANE SINGLE;So;0;L;;;;;N;;;;; +1D982;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-FLOORPLANE DOUBLE;So;0;L;;;;;N;;;;; +1D983;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-FLOORPLANE ALTERNATING;So;0;L;;;;;N;;;;; +1D984;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-WALLPLANE SINGLE;So;0;L;;;;;N;;;;; +1D985;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-WALLPLANE DOUBLE;So;0;L;;;;;N;;;;; +1D986;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-WALLPLANE ALTERNATING;So;0;L;;;;;N;;;;; +1D987;SIGNWRITING TRAVEL-FLOORPLANE SHAKING;So;0;L;;;;;N;;;;; +1D988;SIGNWRITING MOVEMENT-WALLPLANE CURVE QUARTER SMALL;So;0;L;;;;;N;;;;; +1D989;SIGNWRITING MOVEMENT-WALLPLANE CURVE QUARTER MEDIUM;So;0;L;;;;;N;;;;; +1D98A;SIGNWRITING MOVEMENT-WALLPLANE CURVE QUARTER LARGE;So;0;L;;;;;N;;;;; +1D98B;SIGNWRITING MOVEMENT-WALLPLANE CURVE QUARTER LARGEST;So;0;L;;;;;N;;;;; +1D98C;SIGNWRITING MOVEMENT-WALLPLANE CURVE HALF-CIRCLE SMALL;So;0;L;;;;;N;;;;; +1D98D;SIGNWRITING MOVEMENT-WALLPLANE CURVE HALF-CIRCLE MEDIUM;So;0;L;;;;;N;;;;; +1D98E;SIGNWRITING MOVEMENT-WALLPLANE CURVE HALF-CIRCLE LARGE;So;0;L;;;;;N;;;;; +1D98F;SIGNWRITING MOVEMENT-WALLPLANE CURVE HALF-CIRCLE LARGEST;So;0;L;;;;;N;;;;; +1D990;SIGNWRITING MOVEMENT-WALLPLANE CURVE THREE-QUARTER CIRCLE SMALL;So;0;L;;;;;N;;;;; +1D991;SIGNWRITING MOVEMENT-WALLPLANE CURVE THREE-QUARTER CIRCLE MEDIUM;So;0;L;;;;;N;;;;; +1D992;SIGNWRITING MOVEMENT-WALLPLANE HUMP SMALL;So;0;L;;;;;N;;;;; +1D993;SIGNWRITING MOVEMENT-WALLPLANE HUMP MEDIUM;So;0;L;;;;;N;;;;; +1D994;SIGNWRITING MOVEMENT-WALLPLANE HUMP LARGE;So;0;L;;;;;N;;;;; +1D995;SIGNWRITING MOVEMENT-WALLPLANE LOOP SMALL;So;0;L;;;;;N;;;;; +1D996;SIGNWRITING MOVEMENT-WALLPLANE LOOP MEDIUM;So;0;L;;;;;N;;;;; +1D997;SIGNWRITING MOVEMENT-WALLPLANE LOOP LARGE;So;0;L;;;;;N;;;;; +1D998;SIGNWRITING MOVEMENT-WALLPLANE LOOP SMALL DOUBLE;So;0;L;;;;;N;;;;; +1D999;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE DOUBLE SMALL;So;0;L;;;;;N;;;;; +1D99A;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE DOUBLE MEDIUM;So;0;L;;;;;N;;;;; +1D99B;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE DOUBLE LARGE;So;0;L;;;;;N;;;;; +1D99C;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE TRIPLE SMALL;So;0;L;;;;;N;;;;; +1D99D;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE TRIPLE MEDIUM;So;0;L;;;;;N;;;;; +1D99E;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE TRIPLE LARGE;So;0;L;;;;;N;;;;; +1D99F;SIGNWRITING MOVEMENT-WALLPLANE CURVE THEN STRAIGHT;So;0;L;;;;;N;;;;; +1D9A0;SIGNWRITING MOVEMENT-WALLPLANE CURVED CROSS SMALL;So;0;L;;;;;N;;;;; +1D9A1;SIGNWRITING MOVEMENT-WALLPLANE CURVED CROSS MEDIUM;So;0;L;;;;;N;;;;; +1D9A2;SIGNWRITING ROTATION-WALLPLANE SINGLE;So;0;L;;;;;N;;;;; +1D9A3;SIGNWRITING ROTATION-WALLPLANE DOUBLE;So;0;L;;;;;N;;;;; +1D9A4;SIGNWRITING ROTATION-WALLPLANE ALTERNATE;So;0;L;;;;;N;;;;; +1D9A5;SIGNWRITING MOVEMENT-WALLPLANE SHAKING;So;0;L;;;;;N;;;;; +1D9A6;SIGNWRITING MOVEMENT-WALLPLANE CURVE HITTING FRONT WALL;So;0;L;;;;;N;;;;; +1D9A7;SIGNWRITING MOVEMENT-WALLPLANE HUMP HITTING FRONT WALL;So;0;L;;;;;N;;;;; +1D9A8;SIGNWRITING MOVEMENT-WALLPLANE LOOP HITTING FRONT WALL;So;0;L;;;;;N;;;;; +1D9A9;SIGNWRITING MOVEMENT-WALLPLANE WAVE HITTING FRONT WALL;So;0;L;;;;;N;;;;; +1D9AA;SIGNWRITING ROTATION-WALLPLANE SINGLE HITTING FRONT WALL;So;0;L;;;;;N;;;;; +1D9AB;SIGNWRITING ROTATION-WALLPLANE DOUBLE HITTING FRONT WALL;So;0;L;;;;;N;;;;; +1D9AC;SIGNWRITING ROTATION-WALLPLANE ALTERNATING HITTING FRONT WALL;So;0;L;;;;;N;;;;; +1D9AD;SIGNWRITING MOVEMENT-WALLPLANE CURVE HITTING CHEST;So;0;L;;;;;N;;;;; +1D9AE;SIGNWRITING MOVEMENT-WALLPLANE HUMP HITTING CHEST;So;0;L;;;;;N;;;;; +1D9AF;SIGNWRITING MOVEMENT-WALLPLANE LOOP HITTING CHEST;So;0;L;;;;;N;;;;; +1D9B0;SIGNWRITING MOVEMENT-WALLPLANE WAVE HITTING CHEST;So;0;L;;;;;N;;;;; +1D9B1;SIGNWRITING ROTATION-WALLPLANE SINGLE HITTING CHEST;So;0;L;;;;;N;;;;; +1D9B2;SIGNWRITING ROTATION-WALLPLANE DOUBLE HITTING CHEST;So;0;L;;;;;N;;;;; +1D9B3;SIGNWRITING ROTATION-WALLPLANE ALTERNATING HITTING CHEST;So;0;L;;;;;N;;;;; +1D9B4;SIGNWRITING MOVEMENT-WALLPLANE WAVE DIAGONAL PATH SMALL;So;0;L;;;;;N;;;;; +1D9B5;SIGNWRITING MOVEMENT-WALLPLANE WAVE DIAGONAL PATH MEDIUM;So;0;L;;;;;N;;;;; +1D9B6;SIGNWRITING MOVEMENT-WALLPLANE WAVE DIAGONAL PATH LARGE;So;0;L;;;;;N;;;;; +1D9B7;SIGNWRITING MOVEMENT-FLOORPLANE CURVE HITTING CEILING SMALL;So;0;L;;;;;N;;;;; +1D9B8;SIGNWRITING MOVEMENT-FLOORPLANE CURVE HITTING CEILING LARGE;So;0;L;;;;;N;;;;; +1D9B9;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING CEILING SMALL DOUBLE;So;0;L;;;;;N;;;;; +1D9BA;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING CEILING LARGE DOUBLE;So;0;L;;;;;N;;;;; +1D9BB;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING CEILING SMALL TRIPLE;So;0;L;;;;;N;;;;; +1D9BC;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING CEILING LARGE TRIPLE;So;0;L;;;;;N;;;;; +1D9BD;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING CEILING SMALL SINGLE;So;0;L;;;;;N;;;;; +1D9BE;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING CEILING LARGE SINGLE;So;0;L;;;;;N;;;;; +1D9BF;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING CEILING SMALL DOUBLE;So;0;L;;;;;N;;;;; +1D9C0;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING CEILING LARGE DOUBLE;So;0;L;;;;;N;;;;; +1D9C1;SIGNWRITING MOVEMENT-FLOORPLANE WAVE HITTING CEILING SMALL;So;0;L;;;;;N;;;;; +1D9C2;SIGNWRITING MOVEMENT-FLOORPLANE WAVE HITTING CEILING LARGE;So;0;L;;;;;N;;;;; +1D9C3;SIGNWRITING ROTATION-FLOORPLANE SINGLE HITTING CEILING;So;0;L;;;;;N;;;;; +1D9C4;SIGNWRITING ROTATION-FLOORPLANE DOUBLE HITTING CEILING;So;0;L;;;;;N;;;;; +1D9C5;SIGNWRITING ROTATION-FLOORPLANE ALTERNATING HITTING CEILING;So;0;L;;;;;N;;;;; +1D9C6;SIGNWRITING MOVEMENT-FLOORPLANE CURVE HITTING FLOOR SMALL;So;0;L;;;;;N;;;;; +1D9C7;SIGNWRITING MOVEMENT-FLOORPLANE CURVE HITTING FLOOR LARGE;So;0;L;;;;;N;;;;; +1D9C8;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING FLOOR SMALL DOUBLE;So;0;L;;;;;N;;;;; +1D9C9;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING FLOOR LARGE DOUBLE;So;0;L;;;;;N;;;;; +1D9CA;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING FLOOR TRIPLE SMALL TRIPLE;So;0;L;;;;;N;;;;; +1D9CB;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING FLOOR TRIPLE LARGE TRIPLE;So;0;L;;;;;N;;;;; +1D9CC;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING FLOOR SMALL SINGLE;So;0;L;;;;;N;;;;; +1D9CD;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING FLOOR LARGE SINGLE;So;0;L;;;;;N;;;;; +1D9CE;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING FLOOR SMALL DOUBLE;So;0;L;;;;;N;;;;; +1D9CF;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING FLOOR LARGE DOUBLE;So;0;L;;;;;N;;;;; +1D9D0;SIGNWRITING MOVEMENT-FLOORPLANE WAVE HITTING FLOOR SMALL;So;0;L;;;;;N;;;;; +1D9D1;SIGNWRITING MOVEMENT-FLOORPLANE WAVE HITTING FLOOR LARGE;So;0;L;;;;;N;;;;; +1D9D2;SIGNWRITING ROTATION-FLOORPLANE SINGLE HITTING FLOOR;So;0;L;;;;;N;;;;; +1D9D3;SIGNWRITING ROTATION-FLOORPLANE DOUBLE HITTING FLOOR;So;0;L;;;;;N;;;;; +1D9D4;SIGNWRITING ROTATION-FLOORPLANE ALTERNATING HITTING FLOOR;So;0;L;;;;;N;;;;; +1D9D5;SIGNWRITING MOVEMENT-FLOORPLANE CURVE SMALL;So;0;L;;;;;N;;;;; +1D9D6;SIGNWRITING MOVEMENT-FLOORPLANE CURVE MEDIUM;So;0;L;;;;;N;;;;; +1D9D7;SIGNWRITING MOVEMENT-FLOORPLANE CURVE LARGE;So;0;L;;;;;N;;;;; +1D9D8;SIGNWRITING MOVEMENT-FLOORPLANE CURVE LARGEST;So;0;L;;;;;N;;;;; +1D9D9;SIGNWRITING MOVEMENT-FLOORPLANE CURVE COMBINED;So;0;L;;;;;N;;;;; +1D9DA;SIGNWRITING MOVEMENT-FLOORPLANE HUMP SMALL;So;0;L;;;;;N;;;;; +1D9DB;SIGNWRITING MOVEMENT-FLOORPLANE LOOP SMALL;So;0;L;;;;;N;;;;; +1D9DC;SIGNWRITING MOVEMENT-FLOORPLANE WAVE SNAKE;So;0;L;;;;;N;;;;; +1D9DD;SIGNWRITING MOVEMENT-FLOORPLANE WAVE SMALL;So;0;L;;;;;N;;;;; +1D9DE;SIGNWRITING MOVEMENT-FLOORPLANE WAVE LARGE;So;0;L;;;;;N;;;;; +1D9DF;SIGNWRITING ROTATION-FLOORPLANE SINGLE;So;0;L;;;;;N;;;;; +1D9E0;SIGNWRITING ROTATION-FLOORPLANE DOUBLE;So;0;L;;;;;N;;;;; +1D9E1;SIGNWRITING ROTATION-FLOORPLANE ALTERNATING;So;0;L;;;;;N;;;;; +1D9E2;SIGNWRITING MOVEMENT-FLOORPLANE SHAKING PARALLEL;So;0;L;;;;;N;;;;; +1D9E3;SIGNWRITING MOVEMENT-WALLPLANE ARM CIRCLE SMALL SINGLE;So;0;L;;;;;N;;;;; +1D9E4;SIGNWRITING MOVEMENT-WALLPLANE ARM CIRCLE MEDIUM SINGLE;So;0;L;;;;;N;;;;; +1D9E5;SIGNWRITING MOVEMENT-WALLPLANE ARM CIRCLE SMALL DOUBLE;So;0;L;;;;;N;;;;; +1D9E6;SIGNWRITING MOVEMENT-WALLPLANE ARM CIRCLE MEDIUM DOUBLE;So;0;L;;;;;N;;;;; +1D9E7;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL SMALL SINGLE;So;0;L;;;;;N;;;;; +1D9E8;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL MEDIUM SINGLE;So;0;L;;;;;N;;;;; +1D9E9;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL LARGE SINGLE;So;0;L;;;;;N;;;;; +1D9EA;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL SMALL DOUBLE;So;0;L;;;;;N;;;;; +1D9EB;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL MEDIUM DOUBLE;So;0;L;;;;;N;;;;; +1D9EC;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL LARGE DOUBLE;So;0;L;;;;;N;;;;; +1D9ED;SIGNWRITING MOVEMENT-WALLPLANE WRIST CIRCLE FRONT SINGLE;So;0;L;;;;;N;;;;; +1D9EE;SIGNWRITING MOVEMENT-WALLPLANE WRIST CIRCLE FRONT DOUBLE;So;0;L;;;;;N;;;;; +1D9EF;SIGNWRITING MOVEMENT-FLOORPLANE WRIST CIRCLE HITTING WALL SINGLE;So;0;L;;;;;N;;;;; +1D9F0;SIGNWRITING MOVEMENT-FLOORPLANE WRIST CIRCLE HITTING WALL DOUBLE;So;0;L;;;;;N;;;;; +1D9F1;SIGNWRITING MOVEMENT-WALLPLANE FINGER CIRCLES SINGLE;So;0;L;;;;;N;;;;; +1D9F2;SIGNWRITING MOVEMENT-WALLPLANE FINGER CIRCLES DOUBLE;So;0;L;;;;;N;;;;; +1D9F3;SIGNWRITING MOVEMENT-FLOORPLANE FINGER CIRCLES HITTING WALL SINGLE;So;0;L;;;;;N;;;;; +1D9F4;SIGNWRITING MOVEMENT-FLOORPLANE FINGER CIRCLES HITTING WALL DOUBLE;So;0;L;;;;;N;;;;; +1D9F5;SIGNWRITING DYNAMIC ARROWHEAD SMALL;So;0;L;;;;;N;;;;; +1D9F6;SIGNWRITING DYNAMIC ARROWHEAD LARGE;So;0;L;;;;;N;;;;; +1D9F7;SIGNWRITING DYNAMIC FAST;So;0;L;;;;;N;;;;; +1D9F8;SIGNWRITING DYNAMIC SLOW;So;0;L;;;;;N;;;;; +1D9F9;SIGNWRITING DYNAMIC TENSE;So;0;L;;;;;N;;;;; +1D9FA;SIGNWRITING DYNAMIC RELAXED;So;0;L;;;;;N;;;;; +1D9FB;SIGNWRITING DYNAMIC SIMULTANEOUS;So;0;L;;;;;N;;;;; +1D9FC;SIGNWRITING DYNAMIC SIMULTANEOUS ALTERNATING;So;0;L;;;;;N;;;;; +1D9FD;SIGNWRITING DYNAMIC EVERY OTHER TIME;So;0;L;;;;;N;;;;; +1D9FE;SIGNWRITING DYNAMIC GRADUAL;So;0;L;;;;;N;;;;; +1D9FF;SIGNWRITING HEAD;So;0;L;;;;;N;;;;; +1DA00;SIGNWRITING HEAD RIM;Mn;0;NSM;;;;;N;;;;; +1DA01;SIGNWRITING HEAD MOVEMENT-WALLPLANE STRAIGHT;Mn;0;NSM;;;;;N;;;;; +1DA02;SIGNWRITING HEAD MOVEMENT-WALLPLANE TILT;Mn;0;NSM;;;;;N;;;;; +1DA03;SIGNWRITING HEAD MOVEMENT-FLOORPLANE STRAIGHT;Mn;0;NSM;;;;;N;;;;; +1DA04;SIGNWRITING HEAD MOVEMENT-WALLPLANE CURVE;Mn;0;NSM;;;;;N;;;;; +1DA05;SIGNWRITING HEAD MOVEMENT-FLOORPLANE CURVE;Mn;0;NSM;;;;;N;;;;; +1DA06;SIGNWRITING HEAD MOVEMENT CIRCLE;Mn;0;NSM;;;;;N;;;;; +1DA07;SIGNWRITING FACE DIRECTION POSITION NOSE FORWARD TILTING;Mn;0;NSM;;;;;N;;;;; +1DA08;SIGNWRITING FACE DIRECTION POSITION NOSE UP OR DOWN;Mn;0;NSM;;;;;N;;;;; +1DA09;SIGNWRITING FACE DIRECTION POSITION NOSE UP OR DOWN TILTING;Mn;0;NSM;;;;;N;;;;; +1DA0A;SIGNWRITING EYEBROWS STRAIGHT UP;Mn;0;NSM;;;;;N;;;;; +1DA0B;SIGNWRITING EYEBROWS STRAIGHT NEUTRAL;Mn;0;NSM;;;;;N;;;;; +1DA0C;SIGNWRITING EYEBROWS STRAIGHT DOWN;Mn;0;NSM;;;;;N;;;;; +1DA0D;SIGNWRITING DREAMY EYEBROWS NEUTRAL DOWN;Mn;0;NSM;;;;;N;;;;; +1DA0E;SIGNWRITING DREAMY EYEBROWS DOWN NEUTRAL;Mn;0;NSM;;;;;N;;;;; +1DA0F;SIGNWRITING DREAMY EYEBROWS UP NEUTRAL;Mn;0;NSM;;;;;N;;;;; +1DA10;SIGNWRITING DREAMY EYEBROWS NEUTRAL UP;Mn;0;NSM;;;;;N;;;;; +1DA11;SIGNWRITING FOREHEAD NEUTRAL;Mn;0;NSM;;;;;N;;;;; +1DA12;SIGNWRITING FOREHEAD CONTACT;Mn;0;NSM;;;;;N;;;;; +1DA13;SIGNWRITING FOREHEAD WRINKLED;Mn;0;NSM;;;;;N;;;;; +1DA14;SIGNWRITING EYES OPEN;Mn;0;NSM;;;;;N;;;;; +1DA15;SIGNWRITING EYES SQUEEZED;Mn;0;NSM;;;;;N;;;;; +1DA16;SIGNWRITING EYES CLOSED;Mn;0;NSM;;;;;N;;;;; +1DA17;SIGNWRITING EYE BLINK SINGLE;Mn;0;NSM;;;;;N;;;;; +1DA18;SIGNWRITING EYE BLINK MULTIPLE;Mn;0;NSM;;;;;N;;;;; +1DA19;SIGNWRITING EYES HALF OPEN;Mn;0;NSM;;;;;N;;;;; +1DA1A;SIGNWRITING EYES WIDE OPEN;Mn;0;NSM;;;;;N;;;;; +1DA1B;SIGNWRITING EYES HALF CLOSED;Mn;0;NSM;;;;;N;;;;; +1DA1C;SIGNWRITING EYES WIDENING MOVEMENT;Mn;0;NSM;;;;;N;;;;; +1DA1D;SIGNWRITING EYE WINK;Mn;0;NSM;;;;;N;;;;; +1DA1E;SIGNWRITING EYELASHES UP;Mn;0;NSM;;;;;N;;;;; +1DA1F;SIGNWRITING EYELASHES DOWN;Mn;0;NSM;;;;;N;;;;; +1DA20;SIGNWRITING EYELASHES FLUTTERING;Mn;0;NSM;;;;;N;;;;; +1DA21;SIGNWRITING EYEGAZE-WALLPLANE STRAIGHT;Mn;0;NSM;;;;;N;;;;; +1DA22;SIGNWRITING EYEGAZE-WALLPLANE STRAIGHT DOUBLE;Mn;0;NSM;;;;;N;;;;; +1DA23;SIGNWRITING EYEGAZE-WALLPLANE STRAIGHT ALTERNATING;Mn;0;NSM;;;;;N;;;;; +1DA24;SIGNWRITING EYEGAZE-FLOORPLANE STRAIGHT;Mn;0;NSM;;;;;N;;;;; +1DA25;SIGNWRITING EYEGAZE-FLOORPLANE STRAIGHT DOUBLE;Mn;0;NSM;;;;;N;;;;; +1DA26;SIGNWRITING EYEGAZE-FLOORPLANE STRAIGHT ALTERNATING;Mn;0;NSM;;;;;N;;;;; +1DA27;SIGNWRITING EYEGAZE-WALLPLANE CURVED;Mn;0;NSM;;;;;N;;;;; +1DA28;SIGNWRITING EYEGAZE-FLOORPLANE CURVED;Mn;0;NSM;;;;;N;;;;; +1DA29;SIGNWRITING EYEGAZE-WALLPLANE CIRCLING;Mn;0;NSM;;;;;N;;;;; +1DA2A;SIGNWRITING CHEEKS PUFFED;Mn;0;NSM;;;;;N;;;;; +1DA2B;SIGNWRITING CHEEKS NEUTRAL;Mn;0;NSM;;;;;N;;;;; +1DA2C;SIGNWRITING CHEEKS SUCKED;Mn;0;NSM;;;;;N;;;;; +1DA2D;SIGNWRITING TENSE CHEEKS HIGH;Mn;0;NSM;;;;;N;;;;; +1DA2E;SIGNWRITING TENSE CHEEKS MIDDLE;Mn;0;NSM;;;;;N;;;;; +1DA2F;SIGNWRITING TENSE CHEEKS LOW;Mn;0;NSM;;;;;N;;;;; +1DA30;SIGNWRITING EARS;Mn;0;NSM;;;;;N;;;;; +1DA31;SIGNWRITING NOSE NEUTRAL;Mn;0;NSM;;;;;N;;;;; +1DA32;SIGNWRITING NOSE CONTACT;Mn;0;NSM;;;;;N;;;;; +1DA33;SIGNWRITING NOSE WRINKLES;Mn;0;NSM;;;;;N;;;;; +1DA34;SIGNWRITING NOSE WIGGLES;Mn;0;NSM;;;;;N;;;;; +1DA35;SIGNWRITING AIR BLOWING OUT;Mn;0;NSM;;;;;N;;;;; +1DA36;SIGNWRITING AIR SUCKING IN;Mn;0;NSM;;;;;N;;;;; +1DA37;SIGNWRITING AIR BLOW SMALL ROTATIONS;So;0;L;;;;;N;;;;; +1DA38;SIGNWRITING AIR SUCK SMALL ROTATIONS;So;0;L;;;;;N;;;;; +1DA39;SIGNWRITING BREATH INHALE;So;0;L;;;;;N;;;;; +1DA3A;SIGNWRITING BREATH EXHALE;So;0;L;;;;;N;;;;; +1DA3B;SIGNWRITING MOUTH CLOSED NEUTRAL;Mn;0;NSM;;;;;N;;;;; +1DA3C;SIGNWRITING MOUTH CLOSED FORWARD;Mn;0;NSM;;;;;N;;;;; +1DA3D;SIGNWRITING MOUTH CLOSED CONTACT;Mn;0;NSM;;;;;N;;;;; +1DA3E;SIGNWRITING MOUTH SMILE;Mn;0;NSM;;;;;N;;;;; +1DA3F;SIGNWRITING MOUTH SMILE WRINKLED;Mn;0;NSM;;;;;N;;;;; +1DA40;SIGNWRITING MOUTH SMILE OPEN;Mn;0;NSM;;;;;N;;;;; +1DA41;SIGNWRITING MOUTH FROWN;Mn;0;NSM;;;;;N;;;;; +1DA42;SIGNWRITING MOUTH FROWN WRINKLED;Mn;0;NSM;;;;;N;;;;; +1DA43;SIGNWRITING MOUTH FROWN OPEN;Mn;0;NSM;;;;;N;;;;; +1DA44;SIGNWRITING MOUTH OPEN CIRCLE;Mn;0;NSM;;;;;N;;;;; +1DA45;SIGNWRITING MOUTH OPEN FORWARD;Mn;0;NSM;;;;;N;;;;; +1DA46;SIGNWRITING MOUTH OPEN WRINKLED;Mn;0;NSM;;;;;N;;;;; +1DA47;SIGNWRITING MOUTH OPEN OVAL;Mn;0;NSM;;;;;N;;;;; +1DA48;SIGNWRITING MOUTH OPEN OVAL WRINKLED;Mn;0;NSM;;;;;N;;;;; +1DA49;SIGNWRITING MOUTH OPEN OVAL YAWN;Mn;0;NSM;;;;;N;;;;; +1DA4A;SIGNWRITING MOUTH OPEN RECTANGLE;Mn;0;NSM;;;;;N;;;;; +1DA4B;SIGNWRITING MOUTH OPEN RECTANGLE WRINKLED;Mn;0;NSM;;;;;N;;;;; +1DA4C;SIGNWRITING MOUTH OPEN RECTANGLE YAWN;Mn;0;NSM;;;;;N;;;;; +1DA4D;SIGNWRITING MOUTH KISS;Mn;0;NSM;;;;;N;;;;; +1DA4E;SIGNWRITING MOUTH KISS FORWARD;Mn;0;NSM;;;;;N;;;;; +1DA4F;SIGNWRITING MOUTH KISS WRINKLED;Mn;0;NSM;;;;;N;;;;; +1DA50;SIGNWRITING MOUTH TENSE;Mn;0;NSM;;;;;N;;;;; +1DA51;SIGNWRITING MOUTH TENSE FORWARD;Mn;0;NSM;;;;;N;;;;; +1DA52;SIGNWRITING MOUTH TENSE SUCKED;Mn;0;NSM;;;;;N;;;;; +1DA53;SIGNWRITING LIPS PRESSED TOGETHER;Mn;0;NSM;;;;;N;;;;; +1DA54;SIGNWRITING LIP LOWER OVER UPPER;Mn;0;NSM;;;;;N;;;;; +1DA55;SIGNWRITING LIP UPPER OVER LOWER;Mn;0;NSM;;;;;N;;;;; +1DA56;SIGNWRITING MOUTH CORNERS;Mn;0;NSM;;;;;N;;;;; +1DA57;SIGNWRITING MOUTH WRINKLES SINGLE;Mn;0;NSM;;;;;N;;;;; +1DA58;SIGNWRITING MOUTH WRINKLES DOUBLE;Mn;0;NSM;;;;;N;;;;; +1DA59;SIGNWRITING TONGUE STICKING OUT FAR;Mn;0;NSM;;;;;N;;;;; +1DA5A;SIGNWRITING TONGUE LICKING LIPS;Mn;0;NSM;;;;;N;;;;; +1DA5B;SIGNWRITING TONGUE TIP BETWEEN LIPS;Mn;0;NSM;;;;;N;;;;; +1DA5C;SIGNWRITING TONGUE TIP TOUCHING INSIDE MOUTH;Mn;0;NSM;;;;;N;;;;; +1DA5D;SIGNWRITING TONGUE INSIDE MOUTH RELAXED;Mn;0;NSM;;;;;N;;;;; +1DA5E;SIGNWRITING TONGUE MOVES AGAINST CHEEK;Mn;0;NSM;;;;;N;;;;; +1DA5F;SIGNWRITING TONGUE CENTRE STICKING OUT;Mn;0;NSM;;;;;N;;;;; +1DA60;SIGNWRITING TONGUE CENTRE INSIDE MOUTH;Mn;0;NSM;;;;;N;;;;; +1DA61;SIGNWRITING TEETH;Mn;0;NSM;;;;;N;;;;; +1DA62;SIGNWRITING TEETH MOVEMENT;Mn;0;NSM;;;;;N;;;;; +1DA63;SIGNWRITING TEETH ON TONGUE;Mn;0;NSM;;;;;N;;;;; +1DA64;SIGNWRITING TEETH ON TONGUE MOVEMENT;Mn;0;NSM;;;;;N;;;;; +1DA65;SIGNWRITING TEETH ON LIPS;Mn;0;NSM;;;;;N;;;;; +1DA66;SIGNWRITING TEETH ON LIPS MOVEMENT;Mn;0;NSM;;;;;N;;;;; +1DA67;SIGNWRITING TEETH BITE LIPS;Mn;0;NSM;;;;;N;;;;; +1DA68;SIGNWRITING MOVEMENT-WALLPLANE JAW;Mn;0;NSM;;;;;N;;;;; +1DA69;SIGNWRITING MOVEMENT-FLOORPLANE JAW;Mn;0;NSM;;;;;N;;;;; +1DA6A;SIGNWRITING NECK;Mn;0;NSM;;;;;N;;;;; +1DA6B;SIGNWRITING HAIR;Mn;0;NSM;;;;;N;;;;; +1DA6C;SIGNWRITING EXCITEMENT;Mn;0;NSM;;;;;N;;;;; +1DA6D;SIGNWRITING SHOULDER HIP SPINE;So;0;L;;;;;N;;;;; +1DA6E;SIGNWRITING SHOULDER HIP POSITIONS;So;0;L;;;;;N;;;;; +1DA6F;SIGNWRITING WALLPLANE SHOULDER HIP MOVE;So;0;L;;;;;N;;;;; +1DA70;SIGNWRITING FLOORPLANE SHOULDER HIP MOVE;So;0;L;;;;;N;;;;; +1DA71;SIGNWRITING SHOULDER TILTING FROM WAIST;So;0;L;;;;;N;;;;; +1DA72;SIGNWRITING TORSO-WALLPLANE STRAIGHT STRETCH;So;0;L;;;;;N;;;;; +1DA73;SIGNWRITING TORSO-WALLPLANE CURVED BEND;So;0;L;;;;;N;;;;; +1DA74;SIGNWRITING TORSO-FLOORPLANE TWISTING;So;0;L;;;;;N;;;;; +1DA75;SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS;Mn;0;NSM;;;;;N;;;;; +1DA76;SIGNWRITING LIMB COMBINATION;So;0;L;;;;;N;;;;; +1DA77;SIGNWRITING LIMB LENGTH-1;So;0;L;;;;;N;;;;; +1DA78;SIGNWRITING LIMB LENGTH-2;So;0;L;;;;;N;;;;; +1DA79;SIGNWRITING LIMB LENGTH-3;So;0;L;;;;;N;;;;; +1DA7A;SIGNWRITING LIMB LENGTH-4;So;0;L;;;;;N;;;;; +1DA7B;SIGNWRITING LIMB LENGTH-5;So;0;L;;;;;N;;;;; +1DA7C;SIGNWRITING LIMB LENGTH-6;So;0;L;;;;;N;;;;; +1DA7D;SIGNWRITING LIMB LENGTH-7;So;0;L;;;;;N;;;;; +1DA7E;SIGNWRITING FINGER;So;0;L;;;;;N;;;;; +1DA7F;SIGNWRITING LOCATION-WALLPLANE SPACE;So;0;L;;;;;N;;;;; +1DA80;SIGNWRITING LOCATION-FLOORPLANE SPACE;So;0;L;;;;;N;;;;; +1DA81;SIGNWRITING LOCATION HEIGHT;So;0;L;;;;;N;;;;; +1DA82;SIGNWRITING LOCATION WIDTH;So;0;L;;;;;N;;;;; +1DA83;SIGNWRITING LOCATION DEPTH;So;0;L;;;;;N;;;;; +1DA84;SIGNWRITING LOCATION HEAD NECK;Mn;0;NSM;;;;;N;;;;; +1DA85;SIGNWRITING LOCATION TORSO;So;0;L;;;;;N;;;;; +1DA86;SIGNWRITING LOCATION LIMBS DIGITS;So;0;L;;;;;N;;;;; +1DA87;SIGNWRITING COMMA;Po;0;L;;;;;N;;;;; +1DA88;SIGNWRITING FULL STOP;Po;0;L;;;;;N;;;;; +1DA89;SIGNWRITING SEMICOLON;Po;0;L;;;;;N;;;;; +1DA8A;SIGNWRITING COLON;Po;0;L;;;;;N;;;;; +1DA8B;SIGNWRITING PARENTHESIS;Po;0;L;;;;;N;;;;; +1DA9B;SIGNWRITING FILL MODIFIER-2;Mn;0;NSM;;;;;N;;;;; +1DA9C;SIGNWRITING FILL MODIFIER-3;Mn;0;NSM;;;;;N;;;;; +1DA9D;SIGNWRITING FILL MODIFIER-4;Mn;0;NSM;;;;;N;;;;; +1DA9E;SIGNWRITING FILL MODIFIER-5;Mn;0;NSM;;;;;N;;;;; +1DA9F;SIGNWRITING FILL MODIFIER-6;Mn;0;NSM;;;;;N;;;;; +1DAA1;SIGNWRITING ROTATION MODIFIER-2;Mn;0;NSM;;;;;N;;;;; +1DAA2;SIGNWRITING ROTATION MODIFIER-3;Mn;0;NSM;;;;;N;;;;; +1DAA3;SIGNWRITING ROTATION MODIFIER-4;Mn;0;NSM;;;;;N;;;;; +1DAA4;SIGNWRITING ROTATION MODIFIER-5;Mn;0;NSM;;;;;N;;;;; +1DAA5;SIGNWRITING ROTATION MODIFIER-6;Mn;0;NSM;;;;;N;;;;; +1DAA6;SIGNWRITING ROTATION MODIFIER-7;Mn;0;NSM;;;;;N;;;;; +1DAA7;SIGNWRITING ROTATION MODIFIER-8;Mn;0;NSM;;;;;N;;;;; +1DAA8;SIGNWRITING ROTATION MODIFIER-9;Mn;0;NSM;;;;;N;;;;; +1DAA9;SIGNWRITING ROTATION MODIFIER-10;Mn;0;NSM;;;;;N;;;;; +1DAAA;SIGNWRITING ROTATION MODIFIER-11;Mn;0;NSM;;;;;N;;;;; +1DAAB;SIGNWRITING ROTATION MODIFIER-12;Mn;0;NSM;;;;;N;;;;; +1DAAC;SIGNWRITING ROTATION MODIFIER-13;Mn;0;NSM;;;;;N;;;;; +1DAAD;SIGNWRITING ROTATION MODIFIER-14;Mn;0;NSM;;;;;N;;;;; +1DAAE;SIGNWRITING ROTATION MODIFIER-15;Mn;0;NSM;;;;;N;;;;; +1DAAF;SIGNWRITING ROTATION MODIFIER-16;Mn;0;NSM;;;;;N;;;;; +1E000;COMBINING GLAGOLITIC LETTER AZU;Mn;230;NSM;;;;;N;;;;; +1E001;COMBINING GLAGOLITIC LETTER BUKY;Mn;230;NSM;;;;;N;;;;; +1E002;COMBINING GLAGOLITIC LETTER VEDE;Mn;230;NSM;;;;;N;;;;; +1E003;COMBINING GLAGOLITIC LETTER GLAGOLI;Mn;230;NSM;;;;;N;;;;; +1E004;COMBINING GLAGOLITIC LETTER DOBRO;Mn;230;NSM;;;;;N;;;;; +1E005;COMBINING GLAGOLITIC LETTER YESTU;Mn;230;NSM;;;;;N;;;;; +1E006;COMBINING GLAGOLITIC LETTER ZHIVETE;Mn;230;NSM;;;;;N;;;;; +1E008;COMBINING GLAGOLITIC LETTER ZEMLJA;Mn;230;NSM;;;;;N;;;;; +1E009;COMBINING GLAGOLITIC LETTER IZHE;Mn;230;NSM;;;;;N;;;;; +1E00A;COMBINING GLAGOLITIC LETTER INITIAL IZHE;Mn;230;NSM;;;;;N;;;;; +1E00B;COMBINING GLAGOLITIC LETTER I;Mn;230;NSM;;;;;N;;;;; +1E00C;COMBINING GLAGOLITIC LETTER DJERVI;Mn;230;NSM;;;;;N;;;;; +1E00D;COMBINING GLAGOLITIC LETTER KAKO;Mn;230;NSM;;;;;N;;;;; +1E00E;COMBINING GLAGOLITIC LETTER LJUDIJE;Mn;230;NSM;;;;;N;;;;; +1E00F;COMBINING GLAGOLITIC LETTER MYSLITE;Mn;230;NSM;;;;;N;;;;; +1E010;COMBINING GLAGOLITIC LETTER NASHI;Mn;230;NSM;;;;;N;;;;; +1E011;COMBINING GLAGOLITIC LETTER ONU;Mn;230;NSM;;;;;N;;;;; +1E012;COMBINING GLAGOLITIC LETTER POKOJI;Mn;230;NSM;;;;;N;;;;; +1E013;COMBINING GLAGOLITIC LETTER RITSI;Mn;230;NSM;;;;;N;;;;; +1E014;COMBINING GLAGOLITIC LETTER SLOVO;Mn;230;NSM;;;;;N;;;;; +1E015;COMBINING GLAGOLITIC LETTER TVRIDO;Mn;230;NSM;;;;;N;;;;; +1E016;COMBINING GLAGOLITIC LETTER UKU;Mn;230;NSM;;;;;N;;;;; +1E017;COMBINING GLAGOLITIC LETTER FRITU;Mn;230;NSM;;;;;N;;;;; +1E018;COMBINING GLAGOLITIC LETTER HERU;Mn;230;NSM;;;;;N;;;;; +1E01B;COMBINING GLAGOLITIC LETTER SHTA;Mn;230;NSM;;;;;N;;;;; +1E01C;COMBINING GLAGOLITIC LETTER TSI;Mn;230;NSM;;;;;N;;;;; +1E01D;COMBINING GLAGOLITIC LETTER CHRIVI;Mn;230;NSM;;;;;N;;;;; +1E01E;COMBINING GLAGOLITIC LETTER SHA;Mn;230;NSM;;;;;N;;;;; +1E01F;COMBINING GLAGOLITIC LETTER YERU;Mn;230;NSM;;;;;N;;;;; +1E020;COMBINING GLAGOLITIC LETTER YERI;Mn;230;NSM;;;;;N;;;;; +1E021;COMBINING GLAGOLITIC LETTER YATI;Mn;230;NSM;;;;;N;;;;; +1E023;COMBINING GLAGOLITIC LETTER YU;Mn;230;NSM;;;;;N;;;;; +1E024;COMBINING GLAGOLITIC LETTER SMALL YUS;Mn;230;NSM;;;;;N;;;;; +1E026;COMBINING GLAGOLITIC LETTER YO;Mn;230;NSM;;;;;N;;;;; +1E027;COMBINING GLAGOLITIC LETTER IOTATED SMALL YUS;Mn;230;NSM;;;;;N;;;;; +1E028;COMBINING GLAGOLITIC LETTER BIG YUS;Mn;230;NSM;;;;;N;;;;; +1E029;COMBINING GLAGOLITIC LETTER IOTATED BIG YUS;Mn;230;NSM;;;;;N;;;;; +1E02A;COMBINING GLAGOLITIC LETTER FITA;Mn;230;NSM;;;;;N;;;;; +1E800;MENDE KIKAKUI SYLLABLE M001 KI;Lo;0;R;;;;;N;;;;; +1E801;MENDE KIKAKUI SYLLABLE M002 KA;Lo;0;R;;;;;N;;;;; +1E802;MENDE KIKAKUI SYLLABLE M003 KU;Lo;0;R;;;;;N;;;;; +1E803;MENDE KIKAKUI SYLLABLE M065 KEE;Lo;0;R;;;;;N;;;;; +1E804;MENDE KIKAKUI SYLLABLE M095 KE;Lo;0;R;;;;;N;;;;; +1E805;MENDE KIKAKUI SYLLABLE M076 KOO;Lo;0;R;;;;;N;;;;; +1E806;MENDE KIKAKUI SYLLABLE M048 KO;Lo;0;R;;;;;N;;;;; +1E807;MENDE KIKAKUI SYLLABLE M179 KUA;Lo;0;R;;;;;N;;;;; +1E808;MENDE KIKAKUI SYLLABLE M004 WI;Lo;0;R;;;;;N;;;;; +1E809;MENDE KIKAKUI SYLLABLE M005 WA;Lo;0;R;;;;;N;;;;; +1E80A;MENDE KIKAKUI SYLLABLE M006 WU;Lo;0;R;;;;;N;;;;; +1E80B;MENDE KIKAKUI SYLLABLE M126 WEE;Lo;0;R;;;;;N;;;;; +1E80C;MENDE KIKAKUI SYLLABLE M118 WE;Lo;0;R;;;;;N;;;;; +1E80D;MENDE KIKAKUI SYLLABLE M114 WOO;Lo;0;R;;;;;N;;;;; +1E80E;MENDE KIKAKUI SYLLABLE M045 WO;Lo;0;R;;;;;N;;;;; +1E80F;MENDE KIKAKUI SYLLABLE M194 WUI;Lo;0;R;;;;;N;;;;; +1E810;MENDE KIKAKUI SYLLABLE M143 WEI;Lo;0;R;;;;;N;;;;; +1E811;MENDE KIKAKUI SYLLABLE M061 WVI;Lo;0;R;;;;;N;;;;; +1E812;MENDE KIKAKUI SYLLABLE M049 WVA;Lo;0;R;;;;;N;;;;; +1E813;MENDE KIKAKUI SYLLABLE M139 WVE;Lo;0;R;;;;;N;;;;; +1E814;MENDE KIKAKUI SYLLABLE M007 MIN;Lo;0;R;;;;;N;;;;; +1E815;MENDE KIKAKUI SYLLABLE M008 MAN;Lo;0;R;;;;;N;;;;; +1E816;MENDE KIKAKUI SYLLABLE M009 MUN;Lo;0;R;;;;;N;;;;; +1E817;MENDE KIKAKUI SYLLABLE M059 MEN;Lo;0;R;;;;;N;;;;; +1E818;MENDE KIKAKUI SYLLABLE M094 MON;Lo;0;R;;;;;N;;;;; +1E819;MENDE KIKAKUI SYLLABLE M154 MUAN;Lo;0;R;;;;;N;;;;; +1E81A;MENDE KIKAKUI SYLLABLE M189 MUEN;Lo;0;R;;;;;N;;;;; +1E81B;MENDE KIKAKUI SYLLABLE M010 BI;Lo;0;R;;;;;N;;;;; +1E81C;MENDE KIKAKUI SYLLABLE M011 BA;Lo;0;R;;;;;N;;;;; +1E81D;MENDE KIKAKUI SYLLABLE M012 BU;Lo;0;R;;;;;N;;;;; +1E81E;MENDE KIKAKUI SYLLABLE M150 BEE;Lo;0;R;;;;;N;;;;; +1E81F;MENDE KIKAKUI SYLLABLE M097 BE;Lo;0;R;;;;;N;;;;; +1E820;MENDE KIKAKUI SYLLABLE M103 BOO;Lo;0;R;;;;;N;;;;; +1E821;MENDE KIKAKUI SYLLABLE M138 BO;Lo;0;R;;;;;N;;;;; +1E822;MENDE KIKAKUI SYLLABLE M013 I;Lo;0;R;;;;;N;;;;; +1E823;MENDE KIKAKUI SYLLABLE M014 A;Lo;0;R;;;;;N;;;;; +1E824;MENDE KIKAKUI SYLLABLE M015 U;Lo;0;R;;;;;N;;;;; +1E825;MENDE KIKAKUI SYLLABLE M163 EE;Lo;0;R;;;;;N;;;;; +1E826;MENDE KIKAKUI SYLLABLE M100 E;Lo;0;R;;;;;N;;;;; +1E827;MENDE KIKAKUI SYLLABLE M165 OO;Lo;0;R;;;;;N;;;;; +1E828;MENDE KIKAKUI SYLLABLE M147 O;Lo;0;R;;;;;N;;;;; +1E829;MENDE KIKAKUI SYLLABLE M137 EI;Lo;0;R;;;;;N;;;;; +1E82A;MENDE KIKAKUI SYLLABLE M131 IN;Lo;0;R;;;;;N;;;;; +1E82B;MENDE KIKAKUI SYLLABLE M135 IN;Lo;0;R;;;;;N;;;;; +1E82C;MENDE KIKAKUI SYLLABLE M195 AN;Lo;0;R;;;;;N;;;;; +1E82D;MENDE KIKAKUI SYLLABLE M178 EN;Lo;0;R;;;;;N;;;;; +1E82E;MENDE KIKAKUI SYLLABLE M019 SI;Lo;0;R;;;;;N;;;;; +1E82F;MENDE KIKAKUI SYLLABLE M020 SA;Lo;0;R;;;;;N;;;;; +1E830;MENDE KIKAKUI SYLLABLE M021 SU;Lo;0;R;;;;;N;;;;; +1E831;MENDE KIKAKUI SYLLABLE M162 SEE;Lo;0;R;;;;;N;;;;; +1E832;MENDE KIKAKUI SYLLABLE M116 SE;Lo;0;R;;;;;N;;;;; +1E833;MENDE KIKAKUI SYLLABLE M136 SOO;Lo;0;R;;;;;N;;;;; +1E834;MENDE KIKAKUI SYLLABLE M079 SO;Lo;0;R;;;;;N;;;;; +1E835;MENDE KIKAKUI SYLLABLE M196 SIA;Lo;0;R;;;;;N;;;;; +1E836;MENDE KIKAKUI SYLLABLE M025 LI;Lo;0;R;;;;;N;;;;; +1E837;MENDE KIKAKUI SYLLABLE M026 LA;Lo;0;R;;;;;N;;;;; +1E838;MENDE KIKAKUI SYLLABLE M027 LU;Lo;0;R;;;;;N;;;;; +1E839;MENDE KIKAKUI SYLLABLE M084 LEE;Lo;0;R;;;;;N;;;;; +1E83A;MENDE KIKAKUI SYLLABLE M073 LE;Lo;0;R;;;;;N;;;;; +1E83B;MENDE KIKAKUI SYLLABLE M054 LOO;Lo;0;R;;;;;N;;;;; +1E83C;MENDE KIKAKUI SYLLABLE M153 LO;Lo;0;R;;;;;N;;;;; +1E83D;MENDE KIKAKUI SYLLABLE M110 LONG LE;Lo;0;R;;;;;N;;;;; +1E83E;MENDE KIKAKUI SYLLABLE M016 DI;Lo;0;R;;;;;N;;;;; +1E83F;MENDE KIKAKUI SYLLABLE M017 DA;Lo;0;R;;;;;N;;;;; +1E840;MENDE KIKAKUI SYLLABLE M018 DU;Lo;0;R;;;;;N;;;;; +1E841;MENDE KIKAKUI SYLLABLE M089 DEE;Lo;0;R;;;;;N;;;;; +1E842;MENDE KIKAKUI SYLLABLE M180 DOO;Lo;0;R;;;;;N;;;;; +1E843;MENDE KIKAKUI SYLLABLE M181 DO;Lo;0;R;;;;;N;;;;; +1E844;MENDE KIKAKUI SYLLABLE M022 TI;Lo;0;R;;;;;N;;;;; +1E845;MENDE KIKAKUI SYLLABLE M023 TA;Lo;0;R;;;;;N;;;;; +1E846;MENDE KIKAKUI SYLLABLE M024 TU;Lo;0;R;;;;;N;;;;; +1E847;MENDE KIKAKUI SYLLABLE M091 TEE;Lo;0;R;;;;;N;;;;; +1E848;MENDE KIKAKUI SYLLABLE M055 TE;Lo;0;R;;;;;N;;;;; +1E849;MENDE KIKAKUI SYLLABLE M104 TOO;Lo;0;R;;;;;N;;;;; +1E84A;MENDE KIKAKUI SYLLABLE M069 TO;Lo;0;R;;;;;N;;;;; +1E84B;MENDE KIKAKUI SYLLABLE M028 JI;Lo;0;R;;;;;N;;;;; +1E84C;MENDE KIKAKUI SYLLABLE M029 JA;Lo;0;R;;;;;N;;;;; +1E84D;MENDE KIKAKUI SYLLABLE M030 JU;Lo;0;R;;;;;N;;;;; +1E84E;MENDE KIKAKUI SYLLABLE M157 JEE;Lo;0;R;;;;;N;;;;; +1E84F;MENDE KIKAKUI SYLLABLE M113 JE;Lo;0;R;;;;;N;;;;; +1E850;MENDE KIKAKUI SYLLABLE M160 JOO;Lo;0;R;;;;;N;;;;; +1E851;MENDE KIKAKUI SYLLABLE M063 JO;Lo;0;R;;;;;N;;;;; +1E852;MENDE KIKAKUI SYLLABLE M175 LONG JO;Lo;0;R;;;;;N;;;;; +1E853;MENDE KIKAKUI SYLLABLE M031 YI;Lo;0;R;;;;;N;;;;; +1E854;MENDE KIKAKUI SYLLABLE M032 YA;Lo;0;R;;;;;N;;;;; +1E855;MENDE KIKAKUI SYLLABLE M033 YU;Lo;0;R;;;;;N;;;;; +1E856;MENDE KIKAKUI SYLLABLE M109 YEE;Lo;0;R;;;;;N;;;;; +1E857;MENDE KIKAKUI SYLLABLE M080 YE;Lo;0;R;;;;;N;;;;; +1E858;MENDE KIKAKUI SYLLABLE M141 YOO;Lo;0;R;;;;;N;;;;; +1E859;MENDE KIKAKUI SYLLABLE M121 YO;Lo;0;R;;;;;N;;;;; +1E85A;MENDE KIKAKUI SYLLABLE M034 FI;Lo;0;R;;;;;N;;;;; +1E85B;MENDE KIKAKUI SYLLABLE M035 FA;Lo;0;R;;;;;N;;;;; +1E85C;MENDE KIKAKUI SYLLABLE M036 FU;Lo;0;R;;;;;N;;;;; +1E85D;MENDE KIKAKUI SYLLABLE M078 FEE;Lo;0;R;;;;;N;;;;; +1E85E;MENDE KIKAKUI SYLLABLE M075 FE;Lo;0;R;;;;;N;;;;; +1E85F;MENDE KIKAKUI SYLLABLE M133 FOO;Lo;0;R;;;;;N;;;;; +1E860;MENDE KIKAKUI SYLLABLE M088 FO;Lo;0;R;;;;;N;;;;; +1E861;MENDE KIKAKUI SYLLABLE M197 FUA;Lo;0;R;;;;;N;;;;; +1E862;MENDE KIKAKUI SYLLABLE M101 FAN;Lo;0;R;;;;;N;;;;; +1E863;MENDE KIKAKUI SYLLABLE M037 NIN;Lo;0;R;;;;;N;;;;; +1E864;MENDE KIKAKUI SYLLABLE M038 NAN;Lo;0;R;;;;;N;;;;; +1E865;MENDE KIKAKUI SYLLABLE M039 NUN;Lo;0;R;;;;;N;;;;; +1E866;MENDE KIKAKUI SYLLABLE M117 NEN;Lo;0;R;;;;;N;;;;; +1E867;MENDE KIKAKUI SYLLABLE M169 NON;Lo;0;R;;;;;N;;;;; +1E868;MENDE KIKAKUI SYLLABLE M176 HI;Lo;0;R;;;;;N;;;;; +1E869;MENDE KIKAKUI SYLLABLE M041 HA;Lo;0;R;;;;;N;;;;; +1E86A;MENDE KIKAKUI SYLLABLE M186 HU;Lo;0;R;;;;;N;;;;; +1E86B;MENDE KIKAKUI SYLLABLE M040 HEE;Lo;0;R;;;;;N;;;;; +1E86C;MENDE KIKAKUI SYLLABLE M096 HE;Lo;0;R;;;;;N;;;;; +1E86D;MENDE KIKAKUI SYLLABLE M042 HOO;Lo;0;R;;;;;N;;;;; +1E86E;MENDE KIKAKUI SYLLABLE M140 HO;Lo;0;R;;;;;N;;;;; +1E86F;MENDE KIKAKUI SYLLABLE M083 HEEI;Lo;0;R;;;;;N;;;;; +1E870;MENDE KIKAKUI SYLLABLE M128 HOOU;Lo;0;R;;;;;N;;;;; +1E871;MENDE KIKAKUI SYLLABLE M053 HIN;Lo;0;R;;;;;N;;;;; +1E872;MENDE KIKAKUI SYLLABLE M130 HAN;Lo;0;R;;;;;N;;;;; +1E873;MENDE KIKAKUI SYLLABLE M087 HUN;Lo;0;R;;;;;N;;;;; +1E874;MENDE KIKAKUI SYLLABLE M052 HEN;Lo;0;R;;;;;N;;;;; +1E875;MENDE KIKAKUI SYLLABLE M193 HON;Lo;0;R;;;;;N;;;;; +1E876;MENDE KIKAKUI SYLLABLE M046 HUAN;Lo;0;R;;;;;N;;;;; +1E877;MENDE KIKAKUI SYLLABLE M090 NGGI;Lo;0;R;;;;;N;;;;; +1E878;MENDE KIKAKUI SYLLABLE M043 NGGA;Lo;0;R;;;;;N;;;;; +1E879;MENDE KIKAKUI SYLLABLE M082 NGGU;Lo;0;R;;;;;N;;;;; +1E87A;MENDE KIKAKUI SYLLABLE M115 NGGEE;Lo;0;R;;;;;N;;;;; +1E87B;MENDE KIKAKUI SYLLABLE M146 NGGE;Lo;0;R;;;;;N;;;;; +1E87C;MENDE KIKAKUI SYLLABLE M156 NGGOO;Lo;0;R;;;;;N;;;;; +1E87D;MENDE KIKAKUI SYLLABLE M120 NGGO;Lo;0;R;;;;;N;;;;; +1E87E;MENDE KIKAKUI SYLLABLE M159 NGGAA;Lo;0;R;;;;;N;;;;; +1E87F;MENDE KIKAKUI SYLLABLE M127 NGGUA;Lo;0;R;;;;;N;;;;; +1E880;MENDE KIKAKUI SYLLABLE M086 LONG NGGE;Lo;0;R;;;;;N;;;;; +1E881;MENDE KIKAKUI SYLLABLE M106 LONG NGGOO;Lo;0;R;;;;;N;;;;; +1E882;MENDE KIKAKUI SYLLABLE M183 LONG NGGO;Lo;0;R;;;;;N;;;;; +1E883;MENDE KIKAKUI SYLLABLE M155 GI;Lo;0;R;;;;;N;;;;; +1E884;MENDE KIKAKUI SYLLABLE M111 GA;Lo;0;R;;;;;N;;;;; +1E885;MENDE KIKAKUI SYLLABLE M168 GU;Lo;0;R;;;;;N;;;;; +1E886;MENDE KIKAKUI SYLLABLE M190 GEE;Lo;0;R;;;;;N;;;;; +1E887;MENDE KIKAKUI SYLLABLE M166 GUEI;Lo;0;R;;;;;N;;;;; +1E888;MENDE KIKAKUI SYLLABLE M167 GUAN;Lo;0;R;;;;;N;;;;; +1E889;MENDE KIKAKUI SYLLABLE M184 NGEN;Lo;0;R;;;;;N;;;;; +1E88A;MENDE KIKAKUI SYLLABLE M057 NGON;Lo;0;R;;;;;N;;;;; +1E88B;MENDE KIKAKUI SYLLABLE M177 NGUAN;Lo;0;R;;;;;N;;;;; +1E88C;MENDE KIKAKUI SYLLABLE M068 PI;Lo;0;R;;;;;N;;;;; +1E88D;MENDE KIKAKUI SYLLABLE M099 PA;Lo;0;R;;;;;N;;;;; +1E88E;MENDE KIKAKUI SYLLABLE M050 PU;Lo;0;R;;;;;N;;;;; +1E88F;MENDE KIKAKUI SYLLABLE M081 PEE;Lo;0;R;;;;;N;;;;; +1E890;MENDE KIKAKUI SYLLABLE M051 PE;Lo;0;R;;;;;N;;;;; +1E891;MENDE KIKAKUI SYLLABLE M102 POO;Lo;0;R;;;;;N;;;;; +1E892;MENDE KIKAKUI SYLLABLE M066 PO;Lo;0;R;;;;;N;;;;; +1E893;MENDE KIKAKUI SYLLABLE M145 MBI;Lo;0;R;;;;;N;;;;; +1E894;MENDE KIKAKUI SYLLABLE M062 MBA;Lo;0;R;;;;;N;;;;; +1E895;MENDE KIKAKUI SYLLABLE M122 MBU;Lo;0;R;;;;;N;;;;; +1E896;MENDE KIKAKUI SYLLABLE M047 MBEE;Lo;0;R;;;;;N;;;;; +1E897;MENDE KIKAKUI SYLLABLE M188 MBEE;Lo;0;R;;;;;N;;;;; +1E898;MENDE KIKAKUI SYLLABLE M072 MBE;Lo;0;R;;;;;N;;;;; +1E899;MENDE KIKAKUI SYLLABLE M172 MBOO;Lo;0;R;;;;;N;;;;; +1E89A;MENDE KIKAKUI SYLLABLE M174 MBO;Lo;0;R;;;;;N;;;;; +1E89B;MENDE KIKAKUI SYLLABLE M187 MBUU;Lo;0;R;;;;;N;;;;; +1E89C;MENDE KIKAKUI SYLLABLE M161 LONG MBE;Lo;0;R;;;;;N;;;;; +1E89D;MENDE KIKAKUI SYLLABLE M105 LONG MBOO;Lo;0;R;;;;;N;;;;; +1E89E;MENDE KIKAKUI SYLLABLE M142 LONG MBO;Lo;0;R;;;;;N;;;;; +1E89F;MENDE KIKAKUI SYLLABLE M132 KPI;Lo;0;R;;;;;N;;;;; +1E8A0;MENDE KIKAKUI SYLLABLE M092 KPA;Lo;0;R;;;;;N;;;;; +1E8A1;MENDE KIKAKUI SYLLABLE M074 KPU;Lo;0;R;;;;;N;;;;; +1E8A2;MENDE KIKAKUI SYLLABLE M044 KPEE;Lo;0;R;;;;;N;;;;; +1E8A3;MENDE KIKAKUI SYLLABLE M108 KPE;Lo;0;R;;;;;N;;;;; +1E8A4;MENDE KIKAKUI SYLLABLE M112 KPOO;Lo;0;R;;;;;N;;;;; +1E8A5;MENDE KIKAKUI SYLLABLE M158 KPO;Lo;0;R;;;;;N;;;;; +1E8A6;MENDE KIKAKUI SYLLABLE M124 GBI;Lo;0;R;;;;;N;;;;; +1E8A7;MENDE KIKAKUI SYLLABLE M056 GBA;Lo;0;R;;;;;N;;;;; +1E8A8;MENDE KIKAKUI SYLLABLE M148 GBU;Lo;0;R;;;;;N;;;;; +1E8A9;MENDE KIKAKUI SYLLABLE M093 GBEE;Lo;0;R;;;;;N;;;;; +1E8AA;MENDE KIKAKUI SYLLABLE M107 GBE;Lo;0;R;;;;;N;;;;; +1E8AB;MENDE KIKAKUI SYLLABLE M071 GBOO;Lo;0;R;;;;;N;;;;; +1E8AC;MENDE KIKAKUI SYLLABLE M070 GBO;Lo;0;R;;;;;N;;;;; +1E8AD;MENDE KIKAKUI SYLLABLE M171 RA;Lo;0;R;;;;;N;;;;; +1E8AE;MENDE KIKAKUI SYLLABLE M123 NDI;Lo;0;R;;;;;N;;;;; +1E8AF;MENDE KIKAKUI SYLLABLE M129 NDA;Lo;0;R;;;;;N;;;;; +1E8B0;MENDE KIKAKUI SYLLABLE M125 NDU;Lo;0;R;;;;;N;;;;; +1E8B1;MENDE KIKAKUI SYLLABLE M191 NDEE;Lo;0;R;;;;;N;;;;; +1E8B2;MENDE KIKAKUI SYLLABLE M119 NDE;Lo;0;R;;;;;N;;;;; +1E8B3;MENDE KIKAKUI SYLLABLE M067 NDOO;Lo;0;R;;;;;N;;;;; +1E8B4;MENDE KIKAKUI SYLLABLE M064 NDO;Lo;0;R;;;;;N;;;;; +1E8B5;MENDE KIKAKUI SYLLABLE M152 NJA;Lo;0;R;;;;;N;;;;; +1E8B6;MENDE KIKAKUI SYLLABLE M192 NJU;Lo;0;R;;;;;N;;;;; +1E8B7;MENDE KIKAKUI SYLLABLE M149 NJEE;Lo;0;R;;;;;N;;;;; +1E8B8;MENDE KIKAKUI SYLLABLE M134 NJOO;Lo;0;R;;;;;N;;;;; +1E8B9;MENDE KIKAKUI SYLLABLE M182 VI;Lo;0;R;;;;;N;;;;; +1E8BA;MENDE KIKAKUI SYLLABLE M185 VA;Lo;0;R;;;;;N;;;;; +1E8BB;MENDE KIKAKUI SYLLABLE M151 VU;Lo;0;R;;;;;N;;;;; +1E8BC;MENDE KIKAKUI SYLLABLE M173 VEE;Lo;0;R;;;;;N;;;;; +1E8BD;MENDE KIKAKUI SYLLABLE M085 VE;Lo;0;R;;;;;N;;;;; +1E8BE;MENDE KIKAKUI SYLLABLE M144 VOO;Lo;0;R;;;;;N;;;;; +1E8BF;MENDE KIKAKUI SYLLABLE M077 VO;Lo;0;R;;;;;N;;;;; +1E8C0;MENDE KIKAKUI SYLLABLE M164 NYIN;Lo;0;R;;;;;N;;;;; +1E8C1;MENDE KIKAKUI SYLLABLE M058 NYAN;Lo;0;R;;;;;N;;;;; +1E8C2;MENDE KIKAKUI SYLLABLE M170 NYUN;Lo;0;R;;;;;N;;;;; +1E8C3;MENDE KIKAKUI SYLLABLE M098 NYEN;Lo;0;R;;;;;N;;;;; +1E8C4;MENDE KIKAKUI SYLLABLE M060 NYON;Lo;0;R;;;;;N;;;;; +1E8C7;MENDE KIKAKUI DIGIT ONE;No;0;R;;;;1;N;;;;; +1E8C8;MENDE KIKAKUI DIGIT TWO;No;0;R;;;;2;N;;;;; +1E8C9;MENDE KIKAKUI DIGIT THREE;No;0;R;;;;3;N;;;;; +1E8CA;MENDE KIKAKUI DIGIT FOUR;No;0;R;;;;4;N;;;;; +1E8CB;MENDE KIKAKUI DIGIT FIVE;No;0;R;;;;5;N;;;;; +1E8CC;MENDE KIKAKUI DIGIT SIX;No;0;R;;;;6;N;;;;; +1E8CD;MENDE KIKAKUI DIGIT SEVEN;No;0;R;;;;7;N;;;;; +1E8CE;MENDE KIKAKUI DIGIT EIGHT;No;0;R;;;;8;N;;;;; +1E8CF;MENDE KIKAKUI DIGIT NINE;No;0;R;;;;9;N;;;;; +1E8D0;MENDE KIKAKUI COMBINING NUMBER TEENS;Mn;220;NSM;;;;;N;;;;; +1E8D1;MENDE KIKAKUI COMBINING NUMBER TENS;Mn;220;NSM;;;;;N;;;;; +1E8D2;MENDE KIKAKUI COMBINING NUMBER HUNDREDS;Mn;220;NSM;;;;;N;;;;; +1E8D3;MENDE KIKAKUI COMBINING NUMBER THOUSANDS;Mn;220;NSM;;;;;N;;;;; +1E8D4;MENDE KIKAKUI COMBINING NUMBER TEN THOUSANDS;Mn;220;NSM;;;;;N;;;;; +1E8D5;MENDE KIKAKUI COMBINING NUMBER HUNDRED THOUSANDS;Mn;220;NSM;;;;;N;;;;; +1E8D6;MENDE KIKAKUI COMBINING NUMBER MILLIONS;Mn;220;NSM;;;;;N;;;;; +1E900;ADLAM CAPITAL LETTER ALIF;Lu;0;R;;;;;N;;;;1E922; +1E901;ADLAM CAPITAL LETTER DAALI;Lu;0;R;;;;;N;;;;1E923; +1E902;ADLAM CAPITAL LETTER LAAM;Lu;0;R;;;;;N;;;;1E924; +1E903;ADLAM CAPITAL LETTER MIIM;Lu;0;R;;;;;N;;;;1E925; +1E904;ADLAM CAPITAL LETTER BA;Lu;0;R;;;;;N;;;;1E926; +1E905;ADLAM CAPITAL LETTER SINNYIIYHE;Lu;0;R;;;;;N;;;;1E927; +1E906;ADLAM CAPITAL LETTER PE;Lu;0;R;;;;;N;;;;1E928; +1E907;ADLAM CAPITAL LETTER BHE;Lu;0;R;;;;;N;;;;1E929; +1E908;ADLAM CAPITAL LETTER RA;Lu;0;R;;;;;N;;;;1E92A; +1E909;ADLAM CAPITAL LETTER E;Lu;0;R;;;;;N;;;;1E92B; +1E90A;ADLAM CAPITAL LETTER FA;Lu;0;R;;;;;N;;;;1E92C; +1E90B;ADLAM CAPITAL LETTER I;Lu;0;R;;;;;N;;;;1E92D; +1E90C;ADLAM CAPITAL LETTER O;Lu;0;R;;;;;N;;;;1E92E; +1E90D;ADLAM CAPITAL LETTER DHA;Lu;0;R;;;;;N;;;;1E92F; +1E90E;ADLAM CAPITAL LETTER YHE;Lu;0;R;;;;;N;;;;1E930; +1E90F;ADLAM CAPITAL LETTER WAW;Lu;0;R;;;;;N;;;;1E931; +1E910;ADLAM CAPITAL LETTER NUN;Lu;0;R;;;;;N;;;;1E932; +1E911;ADLAM CAPITAL LETTER KAF;Lu;0;R;;;;;N;;;;1E933; +1E912;ADLAM CAPITAL LETTER YA;Lu;0;R;;;;;N;;;;1E934; +1E913;ADLAM CAPITAL LETTER U;Lu;0;R;;;;;N;;;;1E935; +1E914;ADLAM CAPITAL LETTER JIIM;Lu;0;R;;;;;N;;;;1E936; +1E915;ADLAM CAPITAL LETTER CHI;Lu;0;R;;;;;N;;;;1E937; +1E916;ADLAM CAPITAL LETTER HA;Lu;0;R;;;;;N;;;;1E938; +1E917;ADLAM CAPITAL LETTER QAAF;Lu;0;R;;;;;N;;;;1E939; +1E918;ADLAM CAPITAL LETTER GA;Lu;0;R;;;;;N;;;;1E93A; +1E919;ADLAM CAPITAL LETTER NYA;Lu;0;R;;;;;N;;;;1E93B; +1E91A;ADLAM CAPITAL LETTER TU;Lu;0;R;;;;;N;;;;1E93C; +1E91B;ADLAM CAPITAL LETTER NHA;Lu;0;R;;;;;N;;;;1E93D; +1E91C;ADLAM CAPITAL LETTER VA;Lu;0;R;;;;;N;;;;1E93E; +1E91D;ADLAM CAPITAL LETTER KHA;Lu;0;R;;;;;N;;;;1E93F; +1E91E;ADLAM CAPITAL LETTER GBE;Lu;0;R;;;;;N;;;;1E940; +1E91F;ADLAM CAPITAL LETTER ZAL;Lu;0;R;;;;;N;;;;1E941; +1E920;ADLAM CAPITAL LETTER KPO;Lu;0;R;;;;;N;;;;1E942; +1E921;ADLAM CAPITAL LETTER SHA;Lu;0;R;;;;;N;;;;1E943; +1E922;ADLAM SMALL LETTER ALIF;Ll;0;R;;;;;N;;;1E900;;1E900 +1E923;ADLAM SMALL LETTER DAALI;Ll;0;R;;;;;N;;;1E901;;1E901 +1E924;ADLAM SMALL LETTER LAAM;Ll;0;R;;;;;N;;;1E902;;1E902 +1E925;ADLAM SMALL LETTER MIIM;Ll;0;R;;;;;N;;;1E903;;1E903 +1E926;ADLAM SMALL LETTER BA;Ll;0;R;;;;;N;;;1E904;;1E904 +1E927;ADLAM SMALL LETTER SINNYIIYHE;Ll;0;R;;;;;N;;;1E905;;1E905 +1E928;ADLAM SMALL LETTER PE;Ll;0;R;;;;;N;;;1E906;;1E906 +1E929;ADLAM SMALL LETTER BHE;Ll;0;R;;;;;N;;;1E907;;1E907 +1E92A;ADLAM SMALL LETTER RA;Ll;0;R;;;;;N;;;1E908;;1E908 +1E92B;ADLAM SMALL LETTER E;Ll;0;R;;;;;N;;;1E909;;1E909 +1E92C;ADLAM SMALL LETTER FA;Ll;0;R;;;;;N;;;1E90A;;1E90A +1E92D;ADLAM SMALL LETTER I;Ll;0;R;;;;;N;;;1E90B;;1E90B +1E92E;ADLAM SMALL LETTER O;Ll;0;R;;;;;N;;;1E90C;;1E90C +1E92F;ADLAM SMALL LETTER DHA;Ll;0;R;;;;;N;;;1E90D;;1E90D +1E930;ADLAM SMALL LETTER YHE;Ll;0;R;;;;;N;;;1E90E;;1E90E +1E931;ADLAM SMALL LETTER WAW;Ll;0;R;;;;;N;;;1E90F;;1E90F +1E932;ADLAM SMALL LETTER NUN;Ll;0;R;;;;;N;;;1E910;;1E910 +1E933;ADLAM SMALL LETTER KAF;Ll;0;R;;;;;N;;;1E911;;1E911 +1E934;ADLAM SMALL LETTER YA;Ll;0;R;;;;;N;;;1E912;;1E912 +1E935;ADLAM SMALL LETTER U;Ll;0;R;;;;;N;;;1E913;;1E913 +1E936;ADLAM SMALL LETTER JIIM;Ll;0;R;;;;;N;;;1E914;;1E914 +1E937;ADLAM SMALL LETTER CHI;Ll;0;R;;;;;N;;;1E915;;1E915 +1E938;ADLAM SMALL LETTER HA;Ll;0;R;;;;;N;;;1E916;;1E916 +1E939;ADLAM SMALL LETTER QAAF;Ll;0;R;;;;;N;;;1E917;;1E917 +1E93A;ADLAM SMALL LETTER GA;Ll;0;R;;;;;N;;;1E918;;1E918 +1E93B;ADLAM SMALL LETTER NYA;Ll;0;R;;;;;N;;;1E919;;1E919 +1E93C;ADLAM SMALL LETTER TU;Ll;0;R;;;;;N;;;1E91A;;1E91A +1E93D;ADLAM SMALL LETTER NHA;Ll;0;R;;;;;N;;;1E91B;;1E91B +1E93E;ADLAM SMALL LETTER VA;Ll;0;R;;;;;N;;;1E91C;;1E91C +1E93F;ADLAM SMALL LETTER KHA;Ll;0;R;;;;;N;;;1E91D;;1E91D +1E940;ADLAM SMALL LETTER GBE;Ll;0;R;;;;;N;;;1E91E;;1E91E +1E941;ADLAM SMALL LETTER ZAL;Ll;0;R;;;;;N;;;1E91F;;1E91F +1E942;ADLAM SMALL LETTER KPO;Ll;0;R;;;;;N;;;1E920;;1E920 +1E943;ADLAM SMALL LETTER SHA;Ll;0;R;;;;;N;;;1E921;;1E921 +1E944;ADLAM ALIF LENGTHENER;Mn;230;NSM;;;;;N;;;;; +1E945;ADLAM VOWEL LENGTHENER;Mn;230;NSM;;;;;N;;;;; +1E946;ADLAM GEMINATION MARK;Mn;230;NSM;;;;;N;;;;; +1E947;ADLAM HAMZA;Mn;230;NSM;;;;;N;;;;; +1E948;ADLAM CONSONANT MODIFIER;Mn;230;NSM;;;;;N;;;;; +1E949;ADLAM GEMINATE CONSONANT MODIFIER;Mn;230;NSM;;;;;N;;;;; +1E94A;ADLAM NUKTA;Mn;7;NSM;;;;;N;;;;; +1E950;ADLAM DIGIT ZERO;Nd;0;R;;0;0;0;N;;;;; +1E951;ADLAM DIGIT ONE;Nd;0;R;;1;1;1;N;;;;; +1E952;ADLAM DIGIT TWO;Nd;0;R;;2;2;2;N;;;;; +1E953;ADLAM DIGIT THREE;Nd;0;R;;3;3;3;N;;;;; +1E954;ADLAM DIGIT FOUR;Nd;0;R;;4;4;4;N;;;;; +1E955;ADLAM DIGIT FIVE;Nd;0;R;;5;5;5;N;;;;; +1E956;ADLAM DIGIT SIX;Nd;0;R;;6;6;6;N;;;;; +1E957;ADLAM DIGIT SEVEN;Nd;0;R;;7;7;7;N;;;;; +1E958;ADLAM DIGIT EIGHT;Nd;0;R;;8;8;8;N;;;;; +1E959;ADLAM DIGIT NINE;Nd;0;R;;9;9;9;N;;;;; +1E95E;ADLAM INITIAL EXCLAMATION MARK;Po;0;R;;;;;N;;;;; +1E95F;ADLAM INITIAL QUESTION MARK;Po;0;R;;;;;N;;;;; +1EE00;ARABIC MATHEMATICAL ALEF;Lo;0;AL;<font> 0627;;;;N;;;;; +1EE01;ARABIC MATHEMATICAL BEH;Lo;0;AL;<font> 0628;;;;N;;;;; +1EE02;ARABIC MATHEMATICAL JEEM;Lo;0;AL;<font> 062C;;;;N;;;;; +1EE03;ARABIC MATHEMATICAL DAL;Lo;0;AL;<font> 062F;;;;N;;;;; +1EE05;ARABIC MATHEMATICAL WAW;Lo;0;AL;<font> 0648;;;;N;;;;; +1EE06;ARABIC MATHEMATICAL ZAIN;Lo;0;AL;<font> 0632;;;;N;;;;; +1EE07;ARABIC MATHEMATICAL HAH;Lo;0;AL;<font> 062D;;;;N;;;;; +1EE08;ARABIC MATHEMATICAL TAH;Lo;0;AL;<font> 0637;;;;N;;;;; +1EE09;ARABIC MATHEMATICAL YEH;Lo;0;AL;<font> 064A;;;;N;;;;; +1EE0A;ARABIC MATHEMATICAL KAF;Lo;0;AL;<font> 0643;;;;N;;;;; +1EE0B;ARABIC MATHEMATICAL LAM;Lo;0;AL;<font> 0644;;;;N;;;;; +1EE0C;ARABIC MATHEMATICAL MEEM;Lo;0;AL;<font> 0645;;;;N;;;;; +1EE0D;ARABIC MATHEMATICAL NOON;Lo;0;AL;<font> 0646;;;;N;;;;; +1EE0E;ARABIC MATHEMATICAL SEEN;Lo;0;AL;<font> 0633;;;;N;;;;; +1EE0F;ARABIC MATHEMATICAL AIN;Lo;0;AL;<font> 0639;;;;N;;;;; +1EE10;ARABIC MATHEMATICAL FEH;Lo;0;AL;<font> 0641;;;;N;;;;; +1EE11;ARABIC MATHEMATICAL SAD;Lo;0;AL;<font> 0635;;;;N;;;;; +1EE12;ARABIC MATHEMATICAL QAF;Lo;0;AL;<font> 0642;;;;N;;;;; +1EE13;ARABIC MATHEMATICAL REH;Lo;0;AL;<font> 0631;;;;N;;;;; +1EE14;ARABIC MATHEMATICAL SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;; +1EE15;ARABIC MATHEMATICAL TEH;Lo;0;AL;<font> 062A;;;;N;;;;; +1EE16;ARABIC MATHEMATICAL THEH;Lo;0;AL;<font> 062B;;;;N;;;;; +1EE17;ARABIC MATHEMATICAL KHAH;Lo;0;AL;<font> 062E;;;;N;;;;; +1EE18;ARABIC MATHEMATICAL THAL;Lo;0;AL;<font> 0630;;;;N;;;;; +1EE19;ARABIC MATHEMATICAL DAD;Lo;0;AL;<font> 0636;;;;N;;;;; +1EE1A;ARABIC MATHEMATICAL ZAH;Lo;0;AL;<font> 0638;;;;N;;;;; +1EE1B;ARABIC MATHEMATICAL GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;; +1EE1C;ARABIC MATHEMATICAL DOTLESS BEH;Lo;0;AL;<font> 066E;;;;N;;;;; +1EE1D;ARABIC MATHEMATICAL DOTLESS NOON;Lo;0;AL;<font> 06BA;;;;N;;;;; +1EE1E;ARABIC MATHEMATICAL DOTLESS FEH;Lo;0;AL;<font> 06A1;;;;N;;;;; +1EE1F;ARABIC MATHEMATICAL DOTLESS QAF;Lo;0;AL;<font> 066F;;;;N;;;;; +1EE21;ARABIC MATHEMATICAL INITIAL BEH;Lo;0;AL;<font> 0628;;;;N;;;;; +1EE22;ARABIC MATHEMATICAL INITIAL JEEM;Lo;0;AL;<font> 062C;;;;N;;;;; +1EE24;ARABIC MATHEMATICAL INITIAL HEH;Lo;0;AL;<font> 0647;;;;N;;;;; +1EE27;ARABIC MATHEMATICAL INITIAL HAH;Lo;0;AL;<font> 062D;;;;N;;;;; +1EE29;ARABIC MATHEMATICAL INITIAL YEH;Lo;0;AL;<font> 064A;;;;N;;;;; +1EE2A;ARABIC MATHEMATICAL INITIAL KAF;Lo;0;AL;<font> 0643;;;;N;;;;; +1EE2B;ARABIC MATHEMATICAL INITIAL LAM;Lo;0;AL;<font> 0644;;;;N;;;;; +1EE2C;ARABIC MATHEMATICAL INITIAL MEEM;Lo;0;AL;<font> 0645;;;;N;;;;; +1EE2D;ARABIC MATHEMATICAL INITIAL NOON;Lo;0;AL;<font> 0646;;;;N;;;;; +1EE2E;ARABIC MATHEMATICAL INITIAL SEEN;Lo;0;AL;<font> 0633;;;;N;;;;; +1EE2F;ARABIC MATHEMATICAL INITIAL AIN;Lo;0;AL;<font> 0639;;;;N;;;;; +1EE30;ARABIC MATHEMATICAL INITIAL FEH;Lo;0;AL;<font> 0641;;;;N;;;;; +1EE31;ARABIC MATHEMATICAL INITIAL SAD;Lo;0;AL;<font> 0635;;;;N;;;;; +1EE32;ARABIC MATHEMATICAL INITIAL QAF;Lo;0;AL;<font> 0642;;;;N;;;;; +1EE34;ARABIC MATHEMATICAL INITIAL SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;; +1EE35;ARABIC MATHEMATICAL INITIAL TEH;Lo;0;AL;<font> 062A;;;;N;;;;; +1EE36;ARABIC MATHEMATICAL INITIAL THEH;Lo;0;AL;<font> 062B;;;;N;;;;; +1EE37;ARABIC MATHEMATICAL INITIAL KHAH;Lo;0;AL;<font> 062E;;;;N;;;;; +1EE39;ARABIC MATHEMATICAL INITIAL DAD;Lo;0;AL;<font> 0636;;;;N;;;;; +1EE3B;ARABIC MATHEMATICAL INITIAL GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;; +1EE42;ARABIC MATHEMATICAL TAILED JEEM;Lo;0;AL;<font> 062C;;;;N;;;;; +1EE47;ARABIC MATHEMATICAL TAILED HAH;Lo;0;AL;<font> 062D;;;;N;;;;; +1EE49;ARABIC MATHEMATICAL TAILED YEH;Lo;0;AL;<font> 064A;;;;N;;;;; +1EE4B;ARABIC MATHEMATICAL TAILED LAM;Lo;0;AL;<font> 0644;;;;N;;;;; +1EE4D;ARABIC MATHEMATICAL TAILED NOON;Lo;0;AL;<font> 0646;;;;N;;;;; +1EE4E;ARABIC MATHEMATICAL TAILED SEEN;Lo;0;AL;<font> 0633;;;;N;;;;; +1EE4F;ARABIC MATHEMATICAL TAILED AIN;Lo;0;AL;<font> 0639;;;;N;;;;; +1EE51;ARABIC MATHEMATICAL TAILED SAD;Lo;0;AL;<font> 0635;;;;N;;;;; +1EE52;ARABIC MATHEMATICAL TAILED QAF;Lo;0;AL;<font> 0642;;;;N;;;;; +1EE54;ARABIC MATHEMATICAL TAILED SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;; +1EE57;ARABIC MATHEMATICAL TAILED KHAH;Lo;0;AL;<font> 062E;;;;N;;;;; +1EE59;ARABIC MATHEMATICAL TAILED DAD;Lo;0;AL;<font> 0636;;;;N;;;;; +1EE5B;ARABIC MATHEMATICAL TAILED GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;; +1EE5D;ARABIC MATHEMATICAL TAILED DOTLESS NOON;Lo;0;AL;<font> 06BA;;;;N;;;;; +1EE5F;ARABIC MATHEMATICAL TAILED DOTLESS QAF;Lo;0;AL;<font> 066F;;;;N;;;;; +1EE61;ARABIC MATHEMATICAL STRETCHED BEH;Lo;0;AL;<font> 0628;;;;N;;;;; +1EE62;ARABIC MATHEMATICAL STRETCHED JEEM;Lo;0;AL;<font> 062C;;;;N;;;;; +1EE64;ARABIC MATHEMATICAL STRETCHED HEH;Lo;0;AL;<font> 0647;;;;N;;;;; +1EE67;ARABIC MATHEMATICAL STRETCHED HAH;Lo;0;AL;<font> 062D;;;;N;;;;; +1EE68;ARABIC MATHEMATICAL STRETCHED TAH;Lo;0;AL;<font> 0637;;;;N;;;;; +1EE69;ARABIC MATHEMATICAL STRETCHED YEH;Lo;0;AL;<font> 064A;;;;N;;;;; +1EE6A;ARABIC MATHEMATICAL STRETCHED KAF;Lo;0;AL;<font> 0643;;;;N;;;;; +1EE6C;ARABIC MATHEMATICAL STRETCHED MEEM;Lo;0;AL;<font> 0645;;;;N;;;;; +1EE6D;ARABIC MATHEMATICAL STRETCHED NOON;Lo;0;AL;<font> 0646;;;;N;;;;; +1EE6E;ARABIC MATHEMATICAL STRETCHED SEEN;Lo;0;AL;<font> 0633;;;;N;;;;; +1EE6F;ARABIC MATHEMATICAL STRETCHED AIN;Lo;0;AL;<font> 0639;;;;N;;;;; +1EE70;ARABIC MATHEMATICAL STRETCHED FEH;Lo;0;AL;<font> 0641;;;;N;;;;; +1EE71;ARABIC MATHEMATICAL STRETCHED SAD;Lo;0;AL;<font> 0635;;;;N;;;;; +1EE72;ARABIC MATHEMATICAL STRETCHED QAF;Lo;0;AL;<font> 0642;;;;N;;;;; +1EE74;ARABIC MATHEMATICAL STRETCHED SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;; +1EE75;ARABIC MATHEMATICAL STRETCHED TEH;Lo;0;AL;<font> 062A;;;;N;;;;; +1EE76;ARABIC MATHEMATICAL STRETCHED THEH;Lo;0;AL;<font> 062B;;;;N;;;;; +1EE77;ARABIC MATHEMATICAL STRETCHED KHAH;Lo;0;AL;<font> 062E;;;;N;;;;; +1EE79;ARABIC MATHEMATICAL STRETCHED DAD;Lo;0;AL;<font> 0636;;;;N;;;;; +1EE7A;ARABIC MATHEMATICAL STRETCHED ZAH;Lo;0;AL;<font> 0638;;;;N;;;;; +1EE7B;ARABIC MATHEMATICAL STRETCHED GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;; +1EE7C;ARABIC MATHEMATICAL STRETCHED DOTLESS BEH;Lo;0;AL;<font> 066E;;;;N;;;;; +1EE7E;ARABIC MATHEMATICAL STRETCHED DOTLESS FEH;Lo;0;AL;<font> 06A1;;;;N;;;;; +1EE80;ARABIC MATHEMATICAL LOOPED ALEF;Lo;0;AL;<font> 0627;;;;N;;;;; +1EE81;ARABIC MATHEMATICAL LOOPED BEH;Lo;0;AL;<font> 0628;;;;N;;;;; +1EE82;ARABIC MATHEMATICAL LOOPED JEEM;Lo;0;AL;<font> 062C;;;;N;;;;; +1EE83;ARABIC MATHEMATICAL LOOPED DAL;Lo;0;AL;<font> 062F;;;;N;;;;; +1EE84;ARABIC MATHEMATICAL LOOPED HEH;Lo;0;AL;<font> 0647;;;;N;;;;; +1EE85;ARABIC MATHEMATICAL LOOPED WAW;Lo;0;AL;<font> 0648;;;;N;;;;; +1EE86;ARABIC MATHEMATICAL LOOPED ZAIN;Lo;0;AL;<font> 0632;;;;N;;;;; +1EE87;ARABIC MATHEMATICAL LOOPED HAH;Lo;0;AL;<font> 062D;;;;N;;;;; +1EE88;ARABIC MATHEMATICAL LOOPED TAH;Lo;0;AL;<font> 0637;;;;N;;;;; +1EE89;ARABIC MATHEMATICAL LOOPED YEH;Lo;0;AL;<font> 064A;;;;N;;;;; +1EE8B;ARABIC MATHEMATICAL LOOPED LAM;Lo;0;AL;<font> 0644;;;;N;;;;; +1EE8C;ARABIC MATHEMATICAL LOOPED MEEM;Lo;0;AL;<font> 0645;;;;N;;;;; +1EE8D;ARABIC MATHEMATICAL LOOPED NOON;Lo;0;AL;<font> 0646;;;;N;;;;; +1EE8E;ARABIC MATHEMATICAL LOOPED SEEN;Lo;0;AL;<font> 0633;;;;N;;;;; +1EE8F;ARABIC MATHEMATICAL LOOPED AIN;Lo;0;AL;<font> 0639;;;;N;;;;; +1EE90;ARABIC MATHEMATICAL LOOPED FEH;Lo;0;AL;<font> 0641;;;;N;;;;; +1EE91;ARABIC MATHEMATICAL LOOPED SAD;Lo;0;AL;<font> 0635;;;;N;;;;; +1EE92;ARABIC MATHEMATICAL LOOPED QAF;Lo;0;AL;<font> 0642;;;;N;;;;; +1EE93;ARABIC MATHEMATICAL LOOPED REH;Lo;0;AL;<font> 0631;;;;N;;;;; +1EE94;ARABIC MATHEMATICAL LOOPED SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;; +1EE95;ARABIC MATHEMATICAL LOOPED TEH;Lo;0;AL;<font> 062A;;;;N;;;;; +1EE96;ARABIC MATHEMATICAL LOOPED THEH;Lo;0;AL;<font> 062B;;;;N;;;;; +1EE97;ARABIC MATHEMATICAL LOOPED KHAH;Lo;0;AL;<font> 062E;;;;N;;;;; +1EE98;ARABIC MATHEMATICAL LOOPED THAL;Lo;0;AL;<font> 0630;;;;N;;;;; +1EE99;ARABIC MATHEMATICAL LOOPED DAD;Lo;0;AL;<font> 0636;;;;N;;;;; +1EE9A;ARABIC MATHEMATICAL LOOPED ZAH;Lo;0;AL;<font> 0638;;;;N;;;;; +1EE9B;ARABIC MATHEMATICAL LOOPED GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;; +1EEA1;ARABIC MATHEMATICAL DOUBLE-STRUCK BEH;Lo;0;AL;<font> 0628;;;;N;;;;; +1EEA2;ARABIC MATHEMATICAL DOUBLE-STRUCK JEEM;Lo;0;AL;<font> 062C;;;;N;;;;; +1EEA3;ARABIC MATHEMATICAL DOUBLE-STRUCK DAL;Lo;0;AL;<font> 062F;;;;N;;;;; +1EEA5;ARABIC MATHEMATICAL DOUBLE-STRUCK WAW;Lo;0;AL;<font> 0648;;;;N;;;;; +1EEA6;ARABIC MATHEMATICAL DOUBLE-STRUCK ZAIN;Lo;0;AL;<font> 0632;;;;N;;;;; +1EEA7;ARABIC MATHEMATICAL DOUBLE-STRUCK HAH;Lo;0;AL;<font> 062D;;;;N;;;;; +1EEA8;ARABIC MATHEMATICAL DOUBLE-STRUCK TAH;Lo;0;AL;<font> 0637;;;;N;;;;; +1EEA9;ARABIC MATHEMATICAL DOUBLE-STRUCK YEH;Lo;0;AL;<font> 064A;;;;N;;;;; +1EEAB;ARABIC MATHEMATICAL DOUBLE-STRUCK LAM;Lo;0;AL;<font> 0644;;;;N;;;;; +1EEAC;ARABIC MATHEMATICAL DOUBLE-STRUCK MEEM;Lo;0;AL;<font> 0645;;;;N;;;;; +1EEAD;ARABIC MATHEMATICAL DOUBLE-STRUCK NOON;Lo;0;AL;<font> 0646;;;;N;;;;; +1EEAE;ARABIC MATHEMATICAL DOUBLE-STRUCK SEEN;Lo;0;AL;<font> 0633;;;;N;;;;; +1EEAF;ARABIC MATHEMATICAL DOUBLE-STRUCK AIN;Lo;0;AL;<font> 0639;;;;N;;;;; +1EEB0;ARABIC MATHEMATICAL DOUBLE-STRUCK FEH;Lo;0;AL;<font> 0641;;;;N;;;;; +1EEB1;ARABIC MATHEMATICAL DOUBLE-STRUCK SAD;Lo;0;AL;<font> 0635;;;;N;;;;; +1EEB2;ARABIC MATHEMATICAL DOUBLE-STRUCK QAF;Lo;0;AL;<font> 0642;;;;N;;;;; +1EEB3;ARABIC MATHEMATICAL DOUBLE-STRUCK REH;Lo;0;AL;<font> 0631;;;;N;;;;; +1EEB4;ARABIC MATHEMATICAL DOUBLE-STRUCK SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;; +1EEB5;ARABIC MATHEMATICAL DOUBLE-STRUCK TEH;Lo;0;AL;<font> 062A;;;;N;;;;; +1EEB6;ARABIC MATHEMATICAL DOUBLE-STRUCK THEH;Lo;0;AL;<font> 062B;;;;N;;;;; +1EEB7;ARABIC MATHEMATICAL DOUBLE-STRUCK KHAH;Lo;0;AL;<font> 062E;;;;N;;;;; +1EEB8;ARABIC MATHEMATICAL DOUBLE-STRUCK THAL;Lo;0;AL;<font> 0630;;;;N;;;;; +1EEB9;ARABIC MATHEMATICAL DOUBLE-STRUCK DAD;Lo;0;AL;<font> 0636;;;;N;;;;; +1EEBA;ARABIC MATHEMATICAL DOUBLE-STRUCK ZAH;Lo;0;AL;<font> 0638;;;;N;;;;; +1EEBB;ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;; +1EEF0;ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH WITH TATWEEL;Sm;0;ON;;;;;N;;;;; +1EEF1;ARABIC MATHEMATICAL OPERATOR HAH WITH DAL;Sm;0;ON;;;;;N;;;;; +1F000;MAHJONG TILE EAST WIND;So;0;ON;;;;;N;;;;; +1F001;MAHJONG TILE SOUTH WIND;So;0;ON;;;;;N;;;;; +1F002;MAHJONG TILE WEST WIND;So;0;ON;;;;;N;;;;; +1F003;MAHJONG TILE NORTH WIND;So;0;ON;;;;;N;;;;; +1F004;MAHJONG TILE RED DRAGON;So;0;ON;;;;;N;;;;; +1F005;MAHJONG TILE GREEN DRAGON;So;0;ON;;;;;N;;;;; +1F006;MAHJONG TILE WHITE DRAGON;So;0;ON;;;;;N;;;;; +1F007;MAHJONG TILE ONE OF CHARACTERS;So;0;ON;;;;;N;;;;; +1F008;MAHJONG TILE TWO OF CHARACTERS;So;0;ON;;;;;N;;;;; +1F009;MAHJONG TILE THREE OF CHARACTERS;So;0;ON;;;;;N;;;;; +1F00A;MAHJONG TILE FOUR OF CHARACTERS;So;0;ON;;;;;N;;;;; +1F00B;MAHJONG TILE FIVE OF CHARACTERS;So;0;ON;;;;;N;;;;; +1F00C;MAHJONG TILE SIX OF CHARACTERS;So;0;ON;;;;;N;;;;; +1F00D;MAHJONG TILE SEVEN OF CHARACTERS;So;0;ON;;;;;N;;;;; +1F00E;MAHJONG TILE EIGHT OF CHARACTERS;So;0;ON;;;;;N;;;;; +1F00F;MAHJONG TILE NINE OF CHARACTERS;So;0;ON;;;;;N;;;;; +1F010;MAHJONG TILE ONE OF BAMBOOS;So;0;ON;;;;;N;;;;; +1F011;MAHJONG TILE TWO OF BAMBOOS;So;0;ON;;;;;N;;;;; +1F012;MAHJONG TILE THREE OF BAMBOOS;So;0;ON;;;;;N;;;;; +1F013;MAHJONG TILE FOUR OF BAMBOOS;So;0;ON;;;;;N;;;;; +1F014;MAHJONG TILE FIVE OF BAMBOOS;So;0;ON;;;;;N;;;;; +1F015;MAHJONG TILE SIX OF BAMBOOS;So;0;ON;;;;;N;;;;; +1F016;MAHJONG TILE SEVEN OF BAMBOOS;So;0;ON;;;;;N;;;;; +1F017;MAHJONG TILE EIGHT OF BAMBOOS;So;0;ON;;;;;N;;;;; +1F018;MAHJONG TILE NINE OF BAMBOOS;So;0;ON;;;;;N;;;;; +1F019;MAHJONG TILE ONE OF CIRCLES;So;0;ON;;;;;N;;;;; +1F01A;MAHJONG TILE TWO OF CIRCLES;So;0;ON;;;;;N;;;;; +1F01B;MAHJONG TILE THREE OF CIRCLES;So;0;ON;;;;;N;;;;; +1F01C;MAHJONG TILE FOUR OF CIRCLES;So;0;ON;;;;;N;;;;; +1F01D;MAHJONG TILE FIVE OF CIRCLES;So;0;ON;;;;;N;;;;; +1F01E;MAHJONG TILE SIX OF CIRCLES;So;0;ON;;;;;N;;;;; +1F01F;MAHJONG TILE SEVEN OF CIRCLES;So;0;ON;;;;;N;;;;; +1F020;MAHJONG TILE EIGHT OF CIRCLES;So;0;ON;;;;;N;;;;; +1F021;MAHJONG TILE NINE OF CIRCLES;So;0;ON;;;;;N;;;;; +1F022;MAHJONG TILE PLUM;So;0;ON;;;;;N;;;;; +1F023;MAHJONG TILE ORCHID;So;0;ON;;;;;N;;;;; +1F024;MAHJONG TILE BAMBOO;So;0;ON;;;;;N;;;;; +1F025;MAHJONG TILE CHRYSANTHEMUM;So;0;ON;;;;;N;;;;; +1F026;MAHJONG TILE SPRING;So;0;ON;;;;;N;;;;; +1F027;MAHJONG TILE SUMMER;So;0;ON;;;;;N;;;;; +1F028;MAHJONG TILE AUTUMN;So;0;ON;;;;;N;;;;; +1F029;MAHJONG TILE WINTER;So;0;ON;;;;;N;;;;; +1F02A;MAHJONG TILE JOKER;So;0;ON;;;;;N;;;;; +1F02B;MAHJONG TILE BACK;So;0;ON;;;;;N;;;;; +1F030;DOMINO TILE HORIZONTAL BACK;So;0;ON;;;;;N;;;;; +1F031;DOMINO TILE HORIZONTAL-00-00;So;0;ON;;;;;N;;;;; +1F032;DOMINO TILE HORIZONTAL-00-01;So;0;ON;;;;;N;;;;; +1F033;DOMINO TILE HORIZONTAL-00-02;So;0;ON;;;;;N;;;;; +1F034;DOMINO TILE HORIZONTAL-00-03;So;0;ON;;;;;N;;;;; +1F035;DOMINO TILE HORIZONTAL-00-04;So;0;ON;;;;;N;;;;; +1F036;DOMINO TILE HORIZONTAL-00-05;So;0;ON;;;;;N;;;;; +1F037;DOMINO TILE HORIZONTAL-00-06;So;0;ON;;;;;N;;;;; +1F038;DOMINO TILE HORIZONTAL-01-00;So;0;ON;;;;;N;;;;; +1F039;DOMINO TILE HORIZONTAL-01-01;So;0;ON;;;;;N;;;;; +1F03A;DOMINO TILE HORIZONTAL-01-02;So;0;ON;;;;;N;;;;; +1F03B;DOMINO TILE HORIZONTAL-01-03;So;0;ON;;;;;N;;;;; +1F03C;DOMINO TILE HORIZONTAL-01-04;So;0;ON;;;;;N;;;;; +1F03D;DOMINO TILE HORIZONTAL-01-05;So;0;ON;;;;;N;;;;; +1F03E;DOMINO TILE HORIZONTAL-01-06;So;0;ON;;;;;N;;;;; +1F03F;DOMINO TILE HORIZONTAL-02-00;So;0;ON;;;;;N;;;;; +1F040;DOMINO TILE HORIZONTAL-02-01;So;0;ON;;;;;N;;;;; +1F041;DOMINO TILE HORIZONTAL-02-02;So;0;ON;;;;;N;;;;; +1F042;DOMINO TILE HORIZONTAL-02-03;So;0;ON;;;;;N;;;;; +1F043;DOMINO TILE HORIZONTAL-02-04;So;0;ON;;;;;N;;;;; +1F044;DOMINO TILE HORIZONTAL-02-05;So;0;ON;;;;;N;;;;; +1F045;DOMINO TILE HORIZONTAL-02-06;So;0;ON;;;;;N;;;;; +1F046;DOMINO TILE HORIZONTAL-03-00;So;0;ON;;;;;N;;;;; +1F047;DOMINO TILE HORIZONTAL-03-01;So;0;ON;;;;;N;;;;; +1F048;DOMINO TILE HORIZONTAL-03-02;So;0;ON;;;;;N;;;;; +1F049;DOMINO TILE HORIZONTAL-03-03;So;0;ON;;;;;N;;;;; +1F04A;DOMINO TILE HORIZONTAL-03-04;So;0;ON;;;;;N;;;;; +1F04B;DOMINO TILE HORIZONTAL-03-05;So;0;ON;;;;;N;;;;; +1F04C;DOMINO TILE HORIZONTAL-03-06;So;0;ON;;;;;N;;;;; +1F04D;DOMINO TILE HORIZONTAL-04-00;So;0;ON;;;;;N;;;;; +1F04E;DOMINO TILE HORIZONTAL-04-01;So;0;ON;;;;;N;;;;; +1F04F;DOMINO TILE HORIZONTAL-04-02;So;0;ON;;;;;N;;;;; +1F050;DOMINO TILE HORIZONTAL-04-03;So;0;ON;;;;;N;;;;; +1F051;DOMINO TILE HORIZONTAL-04-04;So;0;ON;;;;;N;;;;; +1F052;DOMINO TILE HORIZONTAL-04-05;So;0;ON;;;;;N;;;;; +1F053;DOMINO TILE HORIZONTAL-04-06;So;0;ON;;;;;N;;;;; +1F054;DOMINO TILE HORIZONTAL-05-00;So;0;ON;;;;;N;;;;; +1F055;DOMINO TILE HORIZONTAL-05-01;So;0;ON;;;;;N;;;;; +1F056;DOMINO TILE HORIZONTAL-05-02;So;0;ON;;;;;N;;;;; +1F057;DOMINO TILE HORIZONTAL-05-03;So;0;ON;;;;;N;;;;; +1F058;DOMINO TILE HORIZONTAL-05-04;So;0;ON;;;;;N;;;;; +1F059;DOMINO TILE HORIZONTAL-05-05;So;0;ON;;;;;N;;;;; +1F05A;DOMINO TILE HORIZONTAL-05-06;So;0;ON;;;;;N;;;;; +1F05B;DOMINO TILE HORIZONTAL-06-00;So;0;ON;;;;;N;;;;; +1F05C;DOMINO TILE HORIZONTAL-06-01;So;0;ON;;;;;N;;;;; +1F05D;DOMINO TILE HORIZONTAL-06-02;So;0;ON;;;;;N;;;;; +1F05E;DOMINO TILE HORIZONTAL-06-03;So;0;ON;;;;;N;;;;; +1F05F;DOMINO TILE HORIZONTAL-06-04;So;0;ON;;;;;N;;;;; +1F060;DOMINO TILE HORIZONTAL-06-05;So;0;ON;;;;;N;;;;; +1F061;DOMINO TILE HORIZONTAL-06-06;So;0;ON;;;;;N;;;;; +1F062;DOMINO TILE VERTICAL BACK;So;0;ON;;;;;N;;;;; +1F063;DOMINO TILE VERTICAL-00-00;So;0;ON;;;;;N;;;;; +1F064;DOMINO TILE VERTICAL-00-01;So;0;ON;;;;;N;;;;; +1F065;DOMINO TILE VERTICAL-00-02;So;0;ON;;;;;N;;;;; +1F066;DOMINO TILE VERTICAL-00-03;So;0;ON;;;;;N;;;;; +1F067;DOMINO TILE VERTICAL-00-04;So;0;ON;;;;;N;;;;; +1F068;DOMINO TILE VERTICAL-00-05;So;0;ON;;;;;N;;;;; +1F069;DOMINO TILE VERTICAL-00-06;So;0;ON;;;;;N;;;;; +1F06A;DOMINO TILE VERTICAL-01-00;So;0;ON;;;;;N;;;;; +1F06B;DOMINO TILE VERTICAL-01-01;So;0;ON;;;;;N;;;;; +1F06C;DOMINO TILE VERTICAL-01-02;So;0;ON;;;;;N;;;;; +1F06D;DOMINO TILE VERTICAL-01-03;So;0;ON;;;;;N;;;;; +1F06E;DOMINO TILE VERTICAL-01-04;So;0;ON;;;;;N;;;;; +1F06F;DOMINO TILE VERTICAL-01-05;So;0;ON;;;;;N;;;;; +1F070;DOMINO TILE VERTICAL-01-06;So;0;ON;;;;;N;;;;; +1F071;DOMINO TILE VERTICAL-02-00;So;0;ON;;;;;N;;;;; +1F072;DOMINO TILE VERTICAL-02-01;So;0;ON;;;;;N;;;;; +1F073;DOMINO TILE VERTICAL-02-02;So;0;ON;;;;;N;;;;; +1F074;DOMINO TILE VERTICAL-02-03;So;0;ON;;;;;N;;;;; +1F075;DOMINO TILE VERTICAL-02-04;So;0;ON;;;;;N;;;;; +1F076;DOMINO TILE VERTICAL-02-05;So;0;ON;;;;;N;;;;; +1F077;DOMINO TILE VERTICAL-02-06;So;0;ON;;;;;N;;;;; +1F078;DOMINO TILE VERTICAL-03-00;So;0;ON;;;;;N;;;;; +1F079;DOMINO TILE VERTICAL-03-01;So;0;ON;;;;;N;;;;; +1F07A;DOMINO TILE VERTICAL-03-02;So;0;ON;;;;;N;;;;; +1F07B;DOMINO TILE VERTICAL-03-03;So;0;ON;;;;;N;;;;; +1F07C;DOMINO TILE VERTICAL-03-04;So;0;ON;;;;;N;;;;; +1F07D;DOMINO TILE VERTICAL-03-05;So;0;ON;;;;;N;;;;; +1F07E;DOMINO TILE VERTICAL-03-06;So;0;ON;;;;;N;;;;; +1F07F;DOMINO TILE VERTICAL-04-00;So;0;ON;;;;;N;;;;; +1F080;DOMINO TILE VERTICAL-04-01;So;0;ON;;;;;N;;;;; +1F081;DOMINO TILE VERTICAL-04-02;So;0;ON;;;;;N;;;;; +1F082;DOMINO TILE VERTICAL-04-03;So;0;ON;;;;;N;;;;; +1F083;DOMINO TILE VERTICAL-04-04;So;0;ON;;;;;N;;;;; +1F084;DOMINO TILE VERTICAL-04-05;So;0;ON;;;;;N;;;;; +1F085;DOMINO TILE VERTICAL-04-06;So;0;ON;;;;;N;;;;; +1F086;DOMINO TILE VERTICAL-05-00;So;0;ON;;;;;N;;;;; +1F087;DOMINO TILE VERTICAL-05-01;So;0;ON;;;;;N;;;;; +1F088;DOMINO TILE VERTICAL-05-02;So;0;ON;;;;;N;;;;; +1F089;DOMINO TILE VERTICAL-05-03;So;0;ON;;;;;N;;;;; +1F08A;DOMINO TILE VERTICAL-05-04;So;0;ON;;;;;N;;;;; +1F08B;DOMINO TILE VERTICAL-05-05;So;0;ON;;;;;N;;;;; +1F08C;DOMINO TILE VERTICAL-05-06;So;0;ON;;;;;N;;;;; +1F08D;DOMINO TILE VERTICAL-06-00;So;0;ON;;;;;N;;;;; +1F08E;DOMINO TILE VERTICAL-06-01;So;0;ON;;;;;N;;;;; +1F08F;DOMINO TILE VERTICAL-06-02;So;0;ON;;;;;N;;;;; +1F090;DOMINO TILE VERTICAL-06-03;So;0;ON;;;;;N;;;;; +1F091;DOMINO TILE VERTICAL-06-04;So;0;ON;;;;;N;;;;; +1F092;DOMINO TILE VERTICAL-06-05;So;0;ON;;;;;N;;;;; +1F093;DOMINO TILE VERTICAL-06-06;So;0;ON;;;;;N;;;;; +1F0A0;PLAYING CARD BACK;So;0;ON;;;;;N;;;;; +1F0A1;PLAYING CARD ACE OF SPADES;So;0;ON;;;;;N;;;;; +1F0A2;PLAYING CARD TWO OF SPADES;So;0;ON;;;;;N;;;;; +1F0A3;PLAYING CARD THREE OF SPADES;So;0;ON;;;;;N;;;;; +1F0A4;PLAYING CARD FOUR OF SPADES;So;0;ON;;;;;N;;;;; +1F0A5;PLAYING CARD FIVE OF SPADES;So;0;ON;;;;;N;;;;; +1F0A6;PLAYING CARD SIX OF SPADES;So;0;ON;;;;;N;;;;; +1F0A7;PLAYING CARD SEVEN OF SPADES;So;0;ON;;;;;N;;;;; +1F0A8;PLAYING CARD EIGHT OF SPADES;So;0;ON;;;;;N;;;;; +1F0A9;PLAYING CARD NINE OF SPADES;So;0;ON;;;;;N;;;;; +1F0AA;PLAYING CARD TEN OF SPADES;So;0;ON;;;;;N;;;;; +1F0AB;PLAYING CARD JACK OF SPADES;So;0;ON;;;;;N;;;;; +1F0AC;PLAYING CARD KNIGHT OF SPADES;So;0;ON;;;;;N;;;;; +1F0AD;PLAYING CARD QUEEN OF SPADES;So;0;ON;;;;;N;;;;; +1F0AE;PLAYING CARD KING OF SPADES;So;0;ON;;;;;N;;;;; +1F0B1;PLAYING CARD ACE OF HEARTS;So;0;ON;;;;;N;;;;; +1F0B2;PLAYING CARD TWO OF HEARTS;So;0;ON;;;;;N;;;;; +1F0B3;PLAYING CARD THREE OF HEARTS;So;0;ON;;;;;N;;;;; +1F0B4;PLAYING CARD FOUR OF HEARTS;So;0;ON;;;;;N;;;;; +1F0B5;PLAYING CARD FIVE OF HEARTS;So;0;ON;;;;;N;;;;; +1F0B6;PLAYING CARD SIX OF HEARTS;So;0;ON;;;;;N;;;;; +1F0B7;PLAYING CARD SEVEN OF HEARTS;So;0;ON;;;;;N;;;;; +1F0B8;PLAYING CARD EIGHT OF HEARTS;So;0;ON;;;;;N;;;;; +1F0B9;PLAYING CARD NINE OF HEARTS;So;0;ON;;;;;N;;;;; +1F0BA;PLAYING CARD TEN OF HEARTS;So;0;ON;;;;;N;;;;; +1F0BB;PLAYING CARD JACK OF HEARTS;So;0;ON;;;;;N;;;;; +1F0BC;PLAYING CARD KNIGHT OF HEARTS;So;0;ON;;;;;N;;;;; +1F0BD;PLAYING CARD QUEEN OF HEARTS;So;0;ON;;;;;N;;;;; +1F0BE;PLAYING CARD KING OF HEARTS;So;0;ON;;;;;N;;;;; +1F0BF;PLAYING CARD RED JOKER;So;0;ON;;;;;N;;;;; +1F0C1;PLAYING CARD ACE OF DIAMONDS;So;0;ON;;;;;N;;;;; +1F0C2;PLAYING CARD TWO OF DIAMONDS;So;0;ON;;;;;N;;;;; +1F0C3;PLAYING CARD THREE OF DIAMONDS;So;0;ON;;;;;N;;;;; +1F0C4;PLAYING CARD FOUR OF DIAMONDS;So;0;ON;;;;;N;;;;; +1F0C5;PLAYING CARD FIVE OF DIAMONDS;So;0;ON;;;;;N;;;;; +1F0C6;PLAYING CARD SIX OF DIAMONDS;So;0;ON;;;;;N;;;;; +1F0C7;PLAYING CARD SEVEN OF DIAMONDS;So;0;ON;;;;;N;;;;; +1F0C8;PLAYING CARD EIGHT OF DIAMONDS;So;0;ON;;;;;N;;;;; +1F0C9;PLAYING CARD NINE OF DIAMONDS;So;0;ON;;;;;N;;;;; +1F0CA;PLAYING CARD TEN OF DIAMONDS;So;0;ON;;;;;N;;;;; +1F0CB;PLAYING CARD JACK OF DIAMONDS;So;0;ON;;;;;N;;;;; +1F0CC;PLAYING CARD KNIGHT OF DIAMONDS;So;0;ON;;;;;N;;;;; +1F0CD;PLAYING CARD QUEEN OF DIAMONDS;So;0;ON;;;;;N;;;;; +1F0CE;PLAYING CARD KING OF DIAMONDS;So;0;ON;;;;;N;;;;; +1F0CF;PLAYING CARD BLACK JOKER;So;0;ON;;;;;N;;;;; +1F0D1;PLAYING CARD ACE OF CLUBS;So;0;ON;;;;;N;;;;; +1F0D2;PLAYING CARD TWO OF CLUBS;So;0;ON;;;;;N;;;;; +1F0D3;PLAYING CARD THREE OF CLUBS;So;0;ON;;;;;N;;;;; +1F0D4;PLAYING CARD FOUR OF CLUBS;So;0;ON;;;;;N;;;;; +1F0D5;PLAYING CARD FIVE OF CLUBS;So;0;ON;;;;;N;;;;; +1F0D6;PLAYING CARD SIX OF CLUBS;So;0;ON;;;;;N;;;;; +1F0D7;PLAYING CARD SEVEN OF CLUBS;So;0;ON;;;;;N;;;;; +1F0D8;PLAYING CARD EIGHT OF CLUBS;So;0;ON;;;;;N;;;;; +1F0D9;PLAYING CARD NINE OF CLUBS;So;0;ON;;;;;N;;;;; +1F0DA;PLAYING CARD TEN OF CLUBS;So;0;ON;;;;;N;;;;; +1F0DB;PLAYING CARD JACK OF CLUBS;So;0;ON;;;;;N;;;;; +1F0DC;PLAYING CARD KNIGHT OF CLUBS;So;0;ON;;;;;N;;;;; +1F0DD;PLAYING CARD QUEEN OF CLUBS;So;0;ON;;;;;N;;;;; +1F0DE;PLAYING CARD KING OF CLUBS;So;0;ON;;;;;N;;;;; +1F0DF;PLAYING CARD WHITE JOKER;So;0;ON;;;;;N;;;;; +1F0E0;PLAYING CARD FOOL;So;0;ON;;;;;N;;;;; +1F0E1;PLAYING CARD TRUMP-1;So;0;ON;;;;;N;;;;; +1F0E2;PLAYING CARD TRUMP-2;So;0;ON;;;;;N;;;;; +1F0E3;PLAYING CARD TRUMP-3;So;0;ON;;;;;N;;;;; +1F0E4;PLAYING CARD TRUMP-4;So;0;ON;;;;;N;;;;; +1F0E5;PLAYING CARD TRUMP-5;So;0;ON;;;;;N;;;;; +1F0E6;PLAYING CARD TRUMP-6;So;0;ON;;;;;N;;;;; +1F0E7;PLAYING CARD TRUMP-7;So;0;ON;;;;;N;;;;; +1F0E8;PLAYING CARD TRUMP-8;So;0;ON;;;;;N;;;;; +1F0E9;PLAYING CARD TRUMP-9;So;0;ON;;;;;N;;;;; +1F0EA;PLAYING CARD TRUMP-10;So;0;ON;;;;;N;;;;; +1F0EB;PLAYING CARD TRUMP-11;So;0;ON;;;;;N;;;;; +1F0EC;PLAYING CARD TRUMP-12;So;0;ON;;;;;N;;;;; +1F0ED;PLAYING CARD TRUMP-13;So;0;ON;;;;;N;;;;; +1F0EE;PLAYING CARD TRUMP-14;So;0;ON;;;;;N;;;;; +1F0EF;PLAYING CARD TRUMP-15;So;0;ON;;;;;N;;;;; +1F0F0;PLAYING CARD TRUMP-16;So;0;ON;;;;;N;;;;; +1F0F1;PLAYING CARD TRUMP-17;So;0;ON;;;;;N;;;;; +1F0F2;PLAYING CARD TRUMP-18;So;0;ON;;;;;N;;;;; +1F0F3;PLAYING CARD TRUMP-19;So;0;ON;;;;;N;;;;; +1F0F4;PLAYING CARD TRUMP-20;So;0;ON;;;;;N;;;;; +1F0F5;PLAYING CARD TRUMP-21;So;0;ON;;;;;N;;;;; +1F100;DIGIT ZERO FULL STOP;No;0;EN;<compat> 0030 002E;;0;0;N;;;;; +1F101;DIGIT ZERO COMMA;No;0;EN;<compat> 0030 002C;;0;0;N;;;;; +1F102;DIGIT ONE COMMA;No;0;EN;<compat> 0031 002C;;1;1;N;;;;; +1F103;DIGIT TWO COMMA;No;0;EN;<compat> 0032 002C;;2;2;N;;;;; +1F104;DIGIT THREE COMMA;No;0;EN;<compat> 0033 002C;;3;3;N;;;;; +1F105;DIGIT FOUR COMMA;No;0;EN;<compat> 0034 002C;;4;4;N;;;;; +1F106;DIGIT FIVE COMMA;No;0;EN;<compat> 0035 002C;;5;5;N;;;;; +1F107;DIGIT SIX COMMA;No;0;EN;<compat> 0036 002C;;6;6;N;;;;; +1F108;DIGIT SEVEN COMMA;No;0;EN;<compat> 0037 002C;;7;7;N;;;;; +1F109;DIGIT EIGHT COMMA;No;0;EN;<compat> 0038 002C;;8;8;N;;;;; +1F10A;DIGIT NINE COMMA;No;0;EN;<compat> 0039 002C;;9;9;N;;;;; +1F10B;DINGBAT CIRCLED SANS-SERIF DIGIT ZERO;No;0;ON;;;;0;N;;;;; +1F10C;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ZERO;No;0;ON;;;;0;N;;;;; +1F110;PARENTHESIZED LATIN CAPITAL LETTER A;So;0;L;<compat> 0028 0041 0029;;;;N;;;;; +1F111;PARENTHESIZED LATIN CAPITAL LETTER B;So;0;L;<compat> 0028 0042 0029;;;;N;;;;; +1F112;PARENTHESIZED LATIN CAPITAL LETTER C;So;0;L;<compat> 0028 0043 0029;;;;N;;;;; +1F113;PARENTHESIZED LATIN CAPITAL LETTER D;So;0;L;<compat> 0028 0044 0029;;;;N;;;;; +1F114;PARENTHESIZED LATIN CAPITAL LETTER E;So;0;L;<compat> 0028 0045 0029;;;;N;;;;; +1F115;PARENTHESIZED LATIN CAPITAL LETTER F;So;0;L;<compat> 0028 0046 0029;;;;N;;;;; +1F116;PARENTHESIZED LATIN CAPITAL LETTER G;So;0;L;<compat> 0028 0047 0029;;;;N;;;;; +1F117;PARENTHESIZED LATIN CAPITAL LETTER H;So;0;L;<compat> 0028 0048 0029;;;;N;;;;; +1F118;PARENTHESIZED LATIN CAPITAL LETTER I;So;0;L;<compat> 0028 0049 0029;;;;N;;;;; +1F119;PARENTHESIZED LATIN CAPITAL LETTER J;So;0;L;<compat> 0028 004A 0029;;;;N;;;;; +1F11A;PARENTHESIZED LATIN CAPITAL LETTER K;So;0;L;<compat> 0028 004B 0029;;;;N;;;;; +1F11B;PARENTHESIZED LATIN CAPITAL LETTER L;So;0;L;<compat> 0028 004C 0029;;;;N;;;;; +1F11C;PARENTHESIZED LATIN CAPITAL LETTER M;So;0;L;<compat> 0028 004D 0029;;;;N;;;;; +1F11D;PARENTHESIZED LATIN CAPITAL LETTER N;So;0;L;<compat> 0028 004E 0029;;;;N;;;;; +1F11E;PARENTHESIZED LATIN CAPITAL LETTER O;So;0;L;<compat> 0028 004F 0029;;;;N;;;;; +1F11F;PARENTHESIZED LATIN CAPITAL LETTER P;So;0;L;<compat> 0028 0050 0029;;;;N;;;;; +1F120;PARENTHESIZED LATIN CAPITAL LETTER Q;So;0;L;<compat> 0028 0051 0029;;;;N;;;;; +1F121;PARENTHESIZED LATIN CAPITAL LETTER R;So;0;L;<compat> 0028 0052 0029;;;;N;;;;; +1F122;PARENTHESIZED LATIN CAPITAL LETTER S;So;0;L;<compat> 0028 0053 0029;;;;N;;;;; +1F123;PARENTHESIZED LATIN CAPITAL LETTER T;So;0;L;<compat> 0028 0054 0029;;;;N;;;;; +1F124;PARENTHESIZED LATIN CAPITAL LETTER U;So;0;L;<compat> 0028 0055 0029;;;;N;;;;; +1F125;PARENTHESIZED LATIN CAPITAL LETTER V;So;0;L;<compat> 0028 0056 0029;;;;N;;;;; +1F126;PARENTHESIZED LATIN CAPITAL LETTER W;So;0;L;<compat> 0028 0057 0029;;;;N;;;;; +1F127;PARENTHESIZED LATIN CAPITAL LETTER X;So;0;L;<compat> 0028 0058 0029;;;;N;;;;; +1F128;PARENTHESIZED LATIN CAPITAL LETTER Y;So;0;L;<compat> 0028 0059 0029;;;;N;;;;; +1F129;PARENTHESIZED LATIN CAPITAL LETTER Z;So;0;L;<compat> 0028 005A 0029;;;;N;;;;; +1F12A;TORTOISE SHELL BRACKETED LATIN CAPITAL LETTER S;So;0;L;<compat> 3014 0053 3015;;;;N;;;;; +1F12B;CIRCLED ITALIC LATIN CAPITAL LETTER C;So;0;L;<circle> 0043;;;;N;;;;; +1F12C;CIRCLED ITALIC LATIN CAPITAL LETTER R;So;0;L;<circle> 0052;;;;N;;;;; +1F12D;CIRCLED CD;So;0;L;<circle> 0043 0044;;;;N;;;;; +1F12E;CIRCLED WZ;So;0;L;<circle> 0057 005A;;;;N;;;;; +1F130;SQUARED LATIN CAPITAL LETTER A;So;0;L;<square> 0041;;;;N;;;;; +1F131;SQUARED LATIN CAPITAL LETTER B;So;0;L;<square> 0042;;;;N;;;;; +1F132;SQUARED LATIN CAPITAL LETTER C;So;0;L;<square> 0043;;;;N;;;;; +1F133;SQUARED LATIN CAPITAL LETTER D;So;0;L;<square> 0044;;;;N;;;;; +1F134;SQUARED LATIN CAPITAL LETTER E;So;0;L;<square> 0045;;;;N;;;;; +1F135;SQUARED LATIN CAPITAL LETTER F;So;0;L;<square> 0046;;;;N;;;;; +1F136;SQUARED LATIN CAPITAL LETTER G;So;0;L;<square> 0047;;;;N;;;;; +1F137;SQUARED LATIN CAPITAL LETTER H;So;0;L;<square> 0048;;;;N;;;;; +1F138;SQUARED LATIN CAPITAL LETTER I;So;0;L;<square> 0049;;;;N;;;;; +1F139;SQUARED LATIN CAPITAL LETTER J;So;0;L;<square> 004A;;;;N;;;;; +1F13A;SQUARED LATIN CAPITAL LETTER K;So;0;L;<square> 004B;;;;N;;;;; +1F13B;SQUARED LATIN CAPITAL LETTER L;So;0;L;<square> 004C;;;;N;;;;; +1F13C;SQUARED LATIN CAPITAL LETTER M;So;0;L;<square> 004D;;;;N;;;;; +1F13D;SQUARED LATIN CAPITAL LETTER N;So;0;L;<square> 004E;;;;N;;;;; +1F13E;SQUARED LATIN CAPITAL LETTER O;So;0;L;<square> 004F;;;;N;;;;; +1F13F;SQUARED LATIN CAPITAL LETTER P;So;0;L;<square> 0050;;;;N;;;;; +1F140;SQUARED LATIN CAPITAL LETTER Q;So;0;L;<square> 0051;;;;N;;;;; +1F141;SQUARED LATIN CAPITAL LETTER R;So;0;L;<square> 0052;;;;N;;;;; +1F142;SQUARED LATIN CAPITAL LETTER S;So;0;L;<square> 0053;;;;N;;;;; +1F143;SQUARED LATIN CAPITAL LETTER T;So;0;L;<square> 0054;;;;N;;;;; +1F144;SQUARED LATIN CAPITAL LETTER U;So;0;L;<square> 0055;;;;N;;;;; +1F145;SQUARED LATIN CAPITAL LETTER V;So;0;L;<square> 0056;;;;N;;;;; +1F146;SQUARED LATIN CAPITAL LETTER W;So;0;L;<square> 0057;;;;N;;;;; +1F147;SQUARED LATIN CAPITAL LETTER X;So;0;L;<square> 0058;;;;N;;;;; +1F148;SQUARED LATIN CAPITAL LETTER Y;So;0;L;<square> 0059;;;;N;;;;; +1F149;SQUARED LATIN CAPITAL LETTER Z;So;0;L;<square> 005A;;;;N;;;;; +1F14A;SQUARED HV;So;0;L;<square> 0048 0056;;;;N;;;;; +1F14B;SQUARED MV;So;0;L;<square> 004D 0056;;;;N;;;;; +1F14C;SQUARED SD;So;0;L;<square> 0053 0044;;;;N;;;;; +1F14D;SQUARED SS;So;0;L;<square> 0053 0053;;;;N;;;;; +1F14E;SQUARED PPV;So;0;L;<square> 0050 0050 0056;;;;N;;;;; +1F14F;SQUARED WC;So;0;L;<square> 0057 0043;;;;N;;;;; +1F150;NEGATIVE CIRCLED LATIN CAPITAL LETTER A;So;0;L;;;;;N;;;;; +1F151;NEGATIVE CIRCLED LATIN CAPITAL LETTER B;So;0;L;;;;;N;;;;; +1F152;NEGATIVE CIRCLED LATIN CAPITAL LETTER C;So;0;L;;;;;N;;;;; +1F153;NEGATIVE CIRCLED LATIN CAPITAL LETTER D;So;0;L;;;;;N;;;;; +1F154;NEGATIVE CIRCLED LATIN CAPITAL LETTER E;So;0;L;;;;;N;;;;; +1F155;NEGATIVE CIRCLED LATIN CAPITAL LETTER F;So;0;L;;;;;N;;;;; +1F156;NEGATIVE CIRCLED LATIN CAPITAL LETTER G;So;0;L;;;;;N;;;;; +1F157;NEGATIVE CIRCLED LATIN CAPITAL LETTER H;So;0;L;;;;;N;;;;; +1F158;NEGATIVE CIRCLED LATIN CAPITAL LETTER I;So;0;L;;;;;N;;;;; +1F159;NEGATIVE CIRCLED LATIN CAPITAL LETTER J;So;0;L;;;;;N;;;;; +1F15A;NEGATIVE CIRCLED LATIN CAPITAL LETTER K;So;0;L;;;;;N;;;;; +1F15B;NEGATIVE CIRCLED LATIN CAPITAL LETTER L;So;0;L;;;;;N;;;;; +1F15C;NEGATIVE CIRCLED LATIN CAPITAL LETTER M;So;0;L;;;;;N;;;;; +1F15D;NEGATIVE CIRCLED LATIN CAPITAL LETTER N;So;0;L;;;;;N;;;;; +1F15E;NEGATIVE CIRCLED LATIN CAPITAL LETTER O;So;0;L;;;;;N;;;;; +1F15F;NEGATIVE CIRCLED LATIN CAPITAL LETTER P;So;0;L;;;;;N;;;;; +1F160;NEGATIVE CIRCLED LATIN CAPITAL LETTER Q;So;0;L;;;;;N;;;;; +1F161;NEGATIVE CIRCLED LATIN CAPITAL LETTER R;So;0;L;;;;;N;;;;; +1F162;NEGATIVE CIRCLED LATIN CAPITAL LETTER S;So;0;L;;;;;N;;;;; +1F163;NEGATIVE CIRCLED LATIN CAPITAL LETTER T;So;0;L;;;;;N;;;;; +1F164;NEGATIVE CIRCLED LATIN CAPITAL LETTER U;So;0;L;;;;;N;;;;; +1F165;NEGATIVE CIRCLED LATIN CAPITAL LETTER V;So;0;L;;;;;N;;;;; +1F166;NEGATIVE CIRCLED LATIN CAPITAL LETTER W;So;0;L;;;;;N;;;;; +1F167;NEGATIVE CIRCLED LATIN CAPITAL LETTER X;So;0;L;;;;;N;;;;; +1F168;NEGATIVE CIRCLED LATIN CAPITAL LETTER Y;So;0;L;;;;;N;;;;; +1F169;NEGATIVE CIRCLED LATIN CAPITAL LETTER Z;So;0;L;;;;;N;;;;; +1F16A;RAISED MC SIGN;So;0;ON;<super> 004D 0043;;;;N;;;;; +1F16B;RAISED MD SIGN;So;0;ON;<super> 004D 0044;;;;N;;;;; +1F170;NEGATIVE SQUARED LATIN CAPITAL LETTER A;So;0;L;;;;;N;;;;; +1F171;NEGATIVE SQUARED LATIN CAPITAL LETTER B;So;0;L;;;;;N;;;;; +1F172;NEGATIVE SQUARED LATIN CAPITAL LETTER C;So;0;L;;;;;N;;;;; +1F173;NEGATIVE SQUARED LATIN CAPITAL LETTER D;So;0;L;;;;;N;;;;; +1F174;NEGATIVE SQUARED LATIN CAPITAL LETTER E;So;0;L;;;;;N;;;;; +1F175;NEGATIVE SQUARED LATIN CAPITAL LETTER F;So;0;L;;;;;N;;;;; +1F176;NEGATIVE SQUARED LATIN CAPITAL LETTER G;So;0;L;;;;;N;;;;; +1F177;NEGATIVE SQUARED LATIN CAPITAL LETTER H;So;0;L;;;;;N;;;;; +1F178;NEGATIVE SQUARED LATIN CAPITAL LETTER I;So;0;L;;;;;N;;;;; +1F179;NEGATIVE SQUARED LATIN CAPITAL LETTER J;So;0;L;;;;;N;;;;; +1F17A;NEGATIVE SQUARED LATIN CAPITAL LETTER K;So;0;L;;;;;N;;;;; +1F17B;NEGATIVE SQUARED LATIN CAPITAL LETTER L;So;0;L;;;;;N;;;;; +1F17C;NEGATIVE SQUARED LATIN CAPITAL LETTER M;So;0;L;;;;;N;;;;; +1F17D;NEGATIVE SQUARED LATIN CAPITAL LETTER N;So;0;L;;;;;N;;;;; +1F17E;NEGATIVE SQUARED LATIN CAPITAL LETTER O;So;0;L;;;;;N;;;;; +1F17F;NEGATIVE SQUARED LATIN CAPITAL LETTER P;So;0;L;;;;;N;;;;; +1F180;NEGATIVE SQUARED LATIN CAPITAL LETTER Q;So;0;L;;;;;N;;;;; +1F181;NEGATIVE SQUARED LATIN CAPITAL LETTER R;So;0;L;;;;;N;;;;; +1F182;NEGATIVE SQUARED LATIN CAPITAL LETTER S;So;0;L;;;;;N;;;;; +1F183;NEGATIVE SQUARED LATIN CAPITAL LETTER T;So;0;L;;;;;N;;;;; +1F184;NEGATIVE SQUARED LATIN CAPITAL LETTER U;So;0;L;;;;;N;;;;; +1F185;NEGATIVE SQUARED LATIN CAPITAL LETTER V;So;0;L;;;;;N;;;;; +1F186;NEGATIVE SQUARED LATIN CAPITAL LETTER W;So;0;L;;;;;N;;;;; +1F187;NEGATIVE SQUARED LATIN CAPITAL LETTER X;So;0;L;;;;;N;;;;; +1F188;NEGATIVE SQUARED LATIN CAPITAL LETTER Y;So;0;L;;;;;N;;;;; +1F189;NEGATIVE SQUARED LATIN CAPITAL LETTER Z;So;0;L;;;;;N;;;;; +1F18A;CROSSED NEGATIVE SQUARED LATIN CAPITAL LETTER P;So;0;L;;;;;N;;;;; +1F18B;NEGATIVE SQUARED IC;So;0;L;;;;;N;;;;; +1F18C;NEGATIVE SQUARED PA;So;0;L;;;;;N;;;;; +1F18D;NEGATIVE SQUARED SA;So;0;L;;;;;N;;;;; +1F18E;NEGATIVE SQUARED AB;So;0;L;;;;;N;;;;; +1F18F;NEGATIVE SQUARED WC;So;0;L;;;;;N;;;;; +1F190;SQUARE DJ;So;0;L;<square> 0044 004A;;;;N;;;;; +1F191;SQUARED CL;So;0;L;;;;;N;;;;; +1F192;SQUARED COOL;So;0;L;;;;;N;;;;; +1F193;SQUARED FREE;So;0;L;;;;;N;;;;; +1F194;SQUARED ID;So;0;L;;;;;N;;;;; +1F195;SQUARED NEW;So;0;L;;;;;N;;;;; +1F196;SQUARED NG;So;0;L;;;;;N;;;;; +1F197;SQUARED OK;So;0;L;;;;;N;;;;; +1F198;SQUARED SOS;So;0;L;;;;;N;;;;; +1F199;SQUARED UP WITH EXCLAMATION MARK;So;0;L;;;;;N;;;;; +1F19A;SQUARED VS;So;0;L;;;;;N;;;;; +1F19B;SQUARED THREE D;So;0;L;;;;;N;;;;; +1F19C;SQUARED SECOND SCREEN;So;0;L;;;;;N;;;;; +1F19D;SQUARED TWO K;So;0;L;;;;;N;;;;; +1F19E;SQUARED FOUR K;So;0;L;;;;;N;;;;; +1F19F;SQUARED EIGHT K;So;0;L;;;;;N;;;;; +1F1A0;SQUARED FIVE POINT ONE;So;0;L;;;;;N;;;;; +1F1A1;SQUARED SEVEN POINT ONE;So;0;L;;;;;N;;;;; +1F1A2;SQUARED TWENTY-TWO POINT TWO;So;0;L;;;;;N;;;;; +1F1A3;SQUARED SIXTY P;So;0;L;;;;;N;;;;; +1F1A4;SQUARED ONE HUNDRED TWENTY P;So;0;L;;;;;N;;;;; +1F1A5;SQUARED LATIN SMALL LETTER D;So;0;L;;;;;N;;;;; +1F1A6;SQUARED HC;So;0;L;;;;;N;;;;; +1F1A7;SQUARED HDR;So;0;L;;;;;N;;;;; +1F1A8;SQUARED HI-RES;So;0;L;;;;;N;;;;; +1F1A9;SQUARED LOSSLESS;So;0;L;;;;;N;;;;; +1F1AA;SQUARED SHV;So;0;L;;;;;N;;;;; +1F1AB;SQUARED UHD;So;0;L;;;;;N;;;;; +1F1AC;SQUARED VOD;So;0;L;;;;;N;;;;; +1F1E6;REGIONAL INDICATOR SYMBOL LETTER A;So;0;L;;;;;N;;;;; +1F1E7;REGIONAL INDICATOR SYMBOL LETTER B;So;0;L;;;;;N;;;;; +1F1E8;REGIONAL INDICATOR SYMBOL LETTER C;So;0;L;;;;;N;;;;; +1F1E9;REGIONAL INDICATOR SYMBOL LETTER D;So;0;L;;;;;N;;;;; +1F1EA;REGIONAL INDICATOR SYMBOL LETTER E;So;0;L;;;;;N;;;;; +1F1EB;REGIONAL INDICATOR SYMBOL LETTER F;So;0;L;;;;;N;;;;; +1F1EC;REGIONAL INDICATOR SYMBOL LETTER G;So;0;L;;;;;N;;;;; +1F1ED;REGIONAL INDICATOR SYMBOL LETTER H;So;0;L;;;;;N;;;;; +1F1EE;REGIONAL INDICATOR SYMBOL LETTER I;So;0;L;;;;;N;;;;; +1F1EF;REGIONAL INDICATOR SYMBOL LETTER J;So;0;L;;;;;N;;;;; +1F1F0;REGIONAL INDICATOR SYMBOL LETTER K;So;0;L;;;;;N;;;;; +1F1F1;REGIONAL INDICATOR SYMBOL LETTER L;So;0;L;;;;;N;;;;; +1F1F2;REGIONAL INDICATOR SYMBOL LETTER M;So;0;L;;;;;N;;;;; +1F1F3;REGIONAL INDICATOR SYMBOL LETTER N;So;0;L;;;;;N;;;;; +1F1F4;REGIONAL INDICATOR SYMBOL LETTER O;So;0;L;;;;;N;;;;; +1F1F5;REGIONAL INDICATOR SYMBOL LETTER P;So;0;L;;;;;N;;;;; +1F1F6;REGIONAL INDICATOR SYMBOL LETTER Q;So;0;L;;;;;N;;;;; +1F1F7;REGIONAL INDICATOR SYMBOL LETTER R;So;0;L;;;;;N;;;;; +1F1F8;REGIONAL INDICATOR SYMBOL LETTER S;So;0;L;;;;;N;;;;; +1F1F9;REGIONAL INDICATOR SYMBOL LETTER T;So;0;L;;;;;N;;;;; +1F1FA;REGIONAL INDICATOR SYMBOL LETTER U;So;0;L;;;;;N;;;;; +1F1FB;REGIONAL INDICATOR SYMBOL LETTER V;So;0;L;;;;;N;;;;; +1F1FC;REGIONAL INDICATOR SYMBOL LETTER W;So;0;L;;;;;N;;;;; +1F1FD;REGIONAL INDICATOR SYMBOL LETTER X;So;0;L;;;;;N;;;;; +1F1FE;REGIONAL INDICATOR SYMBOL LETTER Y;So;0;L;;;;;N;;;;; +1F1FF;REGIONAL INDICATOR SYMBOL LETTER Z;So;0;L;;;;;N;;;;; +1F200;SQUARE HIRAGANA HOKA;So;0;L;<square> 307B 304B;;;;N;;;;; +1F201;SQUARED KATAKANA KOKO;So;0;L;<square> 30B3 30B3;;;;N;;;;; +1F202;SQUARED KATAKANA SA;So;0;L;<square> 30B5;;;;N;;;;; +1F210;SQUARED CJK UNIFIED IDEOGRAPH-624B;So;0;L;<square> 624B;;;;N;;;;; +1F211;SQUARED CJK UNIFIED IDEOGRAPH-5B57;So;0;L;<square> 5B57;;;;N;;;;; +1F212;SQUARED CJK UNIFIED IDEOGRAPH-53CC;So;0;L;<square> 53CC;;;;N;;;;; +1F213;SQUARED KATAKANA DE;So;0;L;<square> 30C7;;;;N;;;;; +1F214;SQUARED CJK UNIFIED IDEOGRAPH-4E8C;So;0;L;<square> 4E8C;;;;N;;;;; +1F215;SQUARED CJK UNIFIED IDEOGRAPH-591A;So;0;L;<square> 591A;;;;N;;;;; +1F216;SQUARED CJK UNIFIED IDEOGRAPH-89E3;So;0;L;<square> 89E3;;;;N;;;;; +1F217;SQUARED CJK UNIFIED IDEOGRAPH-5929;So;0;L;<square> 5929;;;;N;;;;; +1F218;SQUARED CJK UNIFIED IDEOGRAPH-4EA4;So;0;L;<square> 4EA4;;;;N;;;;; +1F219;SQUARED CJK UNIFIED IDEOGRAPH-6620;So;0;L;<square> 6620;;;;N;;;;; +1F21A;SQUARED CJK UNIFIED IDEOGRAPH-7121;So;0;L;<square> 7121;;;;N;;;;; +1F21B;SQUARED CJK UNIFIED IDEOGRAPH-6599;So;0;L;<square> 6599;;;;N;;;;; +1F21C;SQUARED CJK UNIFIED IDEOGRAPH-524D;So;0;L;<square> 524D;;;;N;;;;; +1F21D;SQUARED CJK UNIFIED IDEOGRAPH-5F8C;So;0;L;<square> 5F8C;;;;N;;;;; +1F21E;SQUARED CJK UNIFIED IDEOGRAPH-518D;So;0;L;<square> 518D;;;;N;;;;; +1F21F;SQUARED CJK UNIFIED IDEOGRAPH-65B0;So;0;L;<square> 65B0;;;;N;;;;; +1F220;SQUARED CJK UNIFIED IDEOGRAPH-521D;So;0;L;<square> 521D;;;;N;;;;; +1F221;SQUARED CJK UNIFIED IDEOGRAPH-7D42;So;0;L;<square> 7D42;;;;N;;;;; +1F222;SQUARED CJK UNIFIED IDEOGRAPH-751F;So;0;L;<square> 751F;;;;N;;;;; +1F223;SQUARED CJK UNIFIED IDEOGRAPH-8CA9;So;0;L;<square> 8CA9;;;;N;;;;; +1F224;SQUARED CJK UNIFIED IDEOGRAPH-58F0;So;0;L;<square> 58F0;;;;N;;;;; +1F225;SQUARED CJK UNIFIED IDEOGRAPH-5439;So;0;L;<square> 5439;;;;N;;;;; +1F226;SQUARED CJK UNIFIED IDEOGRAPH-6F14;So;0;L;<square> 6F14;;;;N;;;;; +1F227;SQUARED CJK UNIFIED IDEOGRAPH-6295;So;0;L;<square> 6295;;;;N;;;;; +1F228;SQUARED CJK UNIFIED IDEOGRAPH-6355;So;0;L;<square> 6355;;;;N;;;;; +1F229;SQUARED CJK UNIFIED IDEOGRAPH-4E00;So;0;L;<square> 4E00;;;;N;;;;; +1F22A;SQUARED CJK UNIFIED IDEOGRAPH-4E09;So;0;L;<square> 4E09;;;;N;;;;; +1F22B;SQUARED CJK UNIFIED IDEOGRAPH-904A;So;0;L;<square> 904A;;;;N;;;;; +1F22C;SQUARED CJK UNIFIED IDEOGRAPH-5DE6;So;0;L;<square> 5DE6;;;;N;;;;; +1F22D;SQUARED CJK UNIFIED IDEOGRAPH-4E2D;So;0;L;<square> 4E2D;;;;N;;;;; +1F22E;SQUARED CJK UNIFIED IDEOGRAPH-53F3;So;0;L;<square> 53F3;;;;N;;;;; +1F22F;SQUARED CJK UNIFIED IDEOGRAPH-6307;So;0;L;<square> 6307;;;;N;;;;; +1F230;SQUARED CJK UNIFIED IDEOGRAPH-8D70;So;0;L;<square> 8D70;;;;N;;;;; +1F231;SQUARED CJK UNIFIED IDEOGRAPH-6253;So;0;L;<square> 6253;;;;N;;;;; +1F232;SQUARED CJK UNIFIED IDEOGRAPH-7981;So;0;L;<square> 7981;;;;N;;;;; +1F233;SQUARED CJK UNIFIED IDEOGRAPH-7A7A;So;0;L;<square> 7A7A;;;;N;;;;; +1F234;SQUARED CJK UNIFIED IDEOGRAPH-5408;So;0;L;<square> 5408;;;;N;;;;; +1F235;SQUARED CJK UNIFIED IDEOGRAPH-6E80;So;0;L;<square> 6E80;;;;N;;;;; +1F236;SQUARED CJK UNIFIED IDEOGRAPH-6709;So;0;L;<square> 6709;;;;N;;;;; +1F237;SQUARED CJK UNIFIED IDEOGRAPH-6708;So;0;L;<square> 6708;;;;N;;;;; +1F238;SQUARED CJK UNIFIED IDEOGRAPH-7533;So;0;L;<square> 7533;;;;N;;;;; +1F239;SQUARED CJK UNIFIED IDEOGRAPH-5272;So;0;L;<square> 5272;;;;N;;;;; +1F23A;SQUARED CJK UNIFIED IDEOGRAPH-55B6;So;0;L;<square> 55B6;;;;N;;;;; +1F23B;SQUARED CJK UNIFIED IDEOGRAPH-914D;So;0;L;<square> 914D;;;;N;;;;; +1F240;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-672C;So;0;L;<compat> 3014 672C 3015;;;;N;;;;; +1F241;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-4E09;So;0;L;<compat> 3014 4E09 3015;;;;N;;;;; +1F242;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-4E8C;So;0;L;<compat> 3014 4E8C 3015;;;;N;;;;; +1F243;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-5B89;So;0;L;<compat> 3014 5B89 3015;;;;N;;;;; +1F244;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-70B9;So;0;L;<compat> 3014 70B9 3015;;;;N;;;;; +1F245;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6253;So;0;L;<compat> 3014 6253 3015;;;;N;;;;; +1F246;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-76D7;So;0;L;<compat> 3014 76D7 3015;;;;N;;;;; +1F247;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-52DD;So;0;L;<compat> 3014 52DD 3015;;;;N;;;;; +1F248;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557;So;0;L;<compat> 3014 6557 3015;;;;N;;;;; +1F250;CIRCLED IDEOGRAPH ADVANTAGE;So;0;L;<circle> 5F97;;;;N;;;;; +1F251;CIRCLED IDEOGRAPH ACCEPT;So;0;L;<circle> 53EF;;;;N;;;;; +1F300;CYCLONE;So;0;ON;;;;;N;;;;; +1F301;FOGGY;So;0;ON;;;;;N;;;;; +1F302;CLOSED UMBRELLA;So;0;ON;;;;;N;;;;; +1F303;NIGHT WITH STARS;So;0;ON;;;;;N;;;;; +1F304;SUNRISE OVER MOUNTAINS;So;0;ON;;;;;N;;;;; +1F305;SUNRISE;So;0;ON;;;;;N;;;;; +1F306;CITYSCAPE AT DUSK;So;0;ON;;;;;N;;;;; +1F307;SUNSET OVER BUILDINGS;So;0;ON;;;;;N;;;;; +1F308;RAINBOW;So;0;ON;;;;;N;;;;; +1F309;BRIDGE AT NIGHT;So;0;ON;;;;;N;;;;; +1F30A;WATER WAVE;So;0;ON;;;;;N;;;;; +1F30B;VOLCANO;So;0;ON;;;;;N;;;;; +1F30C;MILKY WAY;So;0;ON;;;;;N;;;;; +1F30D;EARTH GLOBE EUROPE-AFRICA;So;0;ON;;;;;N;;;;; +1F30E;EARTH GLOBE AMERICAS;So;0;ON;;;;;N;;;;; +1F30F;EARTH GLOBE ASIA-AUSTRALIA;So;0;ON;;;;;N;;;;; +1F310;GLOBE WITH MERIDIANS;So;0;ON;;;;;N;;;;; +1F311;NEW MOON SYMBOL;So;0;ON;;;;;N;;;;; +1F312;WAXING CRESCENT MOON SYMBOL;So;0;ON;;;;;N;;;;; +1F313;FIRST QUARTER MOON SYMBOL;So;0;ON;;;;;N;;;;; +1F314;WAXING GIBBOUS MOON SYMBOL;So;0;ON;;;;;N;;;;; +1F315;FULL MOON SYMBOL;So;0;ON;;;;;N;;;;; +1F316;WANING GIBBOUS MOON SYMBOL;So;0;ON;;;;;N;;;;; +1F317;LAST QUARTER MOON SYMBOL;So;0;ON;;;;;N;;;;; +1F318;WANING CRESCENT MOON SYMBOL;So;0;ON;;;;;N;;;;; +1F319;CRESCENT MOON;So;0;ON;;;;;N;;;;; +1F31A;NEW MOON WITH FACE;So;0;ON;;;;;N;;;;; +1F31B;FIRST QUARTER MOON WITH FACE;So;0;ON;;;;;N;;;;; +1F31C;LAST QUARTER MOON WITH FACE;So;0;ON;;;;;N;;;;; +1F31D;FULL MOON WITH FACE;So;0;ON;;;;;N;;;;; +1F31E;SUN WITH FACE;So;0;ON;;;;;N;;;;; +1F31F;GLOWING STAR;So;0;ON;;;;;N;;;;; +1F320;SHOOTING STAR;So;0;ON;;;;;N;;;;; +1F321;THERMOMETER;So;0;ON;;;;;N;;;;; +1F322;BLACK DROPLET;So;0;ON;;;;;N;;;;; +1F323;WHITE SUN;So;0;ON;;;;;N;;;;; +1F324;WHITE SUN WITH SMALL CLOUD;So;0;ON;;;;;N;;;;; +1F325;WHITE SUN BEHIND CLOUD;So;0;ON;;;;;N;;;;; +1F326;WHITE SUN BEHIND CLOUD WITH RAIN;So;0;ON;;;;;N;;;;; +1F327;CLOUD WITH RAIN;So;0;ON;;;;;N;;;;; +1F328;CLOUD WITH SNOW;So;0;ON;;;;;N;;;;; +1F329;CLOUD WITH LIGHTNING;So;0;ON;;;;;N;;;;; +1F32A;CLOUD WITH TORNADO;So;0;ON;;;;;N;;;;; +1F32B;FOG;So;0;ON;;;;;N;;;;; +1F32C;WIND BLOWING FACE;So;0;ON;;;;;N;;;;; +1F32D;HOT DOG;So;0;ON;;;;;N;;;;; +1F32E;TACO;So;0;ON;;;;;N;;;;; +1F32F;BURRITO;So;0;ON;;;;;N;;;;; +1F330;CHESTNUT;So;0;ON;;;;;N;;;;; +1F331;SEEDLING;So;0;ON;;;;;N;;;;; +1F332;EVERGREEN TREE;So;0;ON;;;;;N;;;;; +1F333;DECIDUOUS TREE;So;0;ON;;;;;N;;;;; +1F334;PALM TREE;So;0;ON;;;;;N;;;;; +1F335;CACTUS;So;0;ON;;;;;N;;;;; +1F336;HOT PEPPER;So;0;ON;;;;;N;;;;; +1F337;TULIP;So;0;ON;;;;;N;;;;; +1F338;CHERRY BLOSSOM;So;0;ON;;;;;N;;;;; +1F339;ROSE;So;0;ON;;;;;N;;;;; +1F33A;HIBISCUS;So;0;ON;;;;;N;;;;; +1F33B;SUNFLOWER;So;0;ON;;;;;N;;;;; +1F33C;BLOSSOM;So;0;ON;;;;;N;;;;; +1F33D;EAR OF MAIZE;So;0;ON;;;;;N;;;;; +1F33E;EAR OF RICE;So;0;ON;;;;;N;;;;; +1F33F;HERB;So;0;ON;;;;;N;;;;; +1F340;FOUR LEAF CLOVER;So;0;ON;;;;;N;;;;; +1F341;MAPLE LEAF;So;0;ON;;;;;N;;;;; +1F342;FALLEN LEAF;So;0;ON;;;;;N;;;;; +1F343;LEAF FLUTTERING IN WIND;So;0;ON;;;;;N;;;;; +1F344;MUSHROOM;So;0;ON;;;;;N;;;;; +1F345;TOMATO;So;0;ON;;;;;N;;;;; +1F346;AUBERGINE;So;0;ON;;;;;N;;;;; +1F347;GRAPES;So;0;ON;;;;;N;;;;; +1F348;MELON;So;0;ON;;;;;N;;;;; +1F349;WATERMELON;So;0;ON;;;;;N;;;;; +1F34A;TANGERINE;So;0;ON;;;;;N;;;;; +1F34B;LEMON;So;0;ON;;;;;N;;;;; +1F34C;BANANA;So;0;ON;;;;;N;;;;; +1F34D;PINEAPPLE;So;0;ON;;;;;N;;;;; +1F34E;RED APPLE;So;0;ON;;;;;N;;;;; +1F34F;GREEN APPLE;So;0;ON;;;;;N;;;;; +1F350;PEAR;So;0;ON;;;;;N;;;;; +1F351;PEACH;So;0;ON;;;;;N;;;;; +1F352;CHERRIES;So;0;ON;;;;;N;;;;; +1F353;STRAWBERRY;So;0;ON;;;;;N;;;;; +1F354;HAMBURGER;So;0;ON;;;;;N;;;;; +1F355;SLICE OF PIZZA;So;0;ON;;;;;N;;;;; +1F356;MEAT ON BONE;So;0;ON;;;;;N;;;;; +1F357;POULTRY LEG;So;0;ON;;;;;N;;;;; +1F358;RICE CRACKER;So;0;ON;;;;;N;;;;; +1F359;RICE BALL;So;0;ON;;;;;N;;;;; +1F35A;COOKED RICE;So;0;ON;;;;;N;;;;; +1F35B;CURRY AND RICE;So;0;ON;;;;;N;;;;; +1F35C;STEAMING BOWL;So;0;ON;;;;;N;;;;; +1F35D;SPAGHETTI;So;0;ON;;;;;N;;;;; +1F35E;BREAD;So;0;ON;;;;;N;;;;; +1F35F;FRENCH FRIES;So;0;ON;;;;;N;;;;; +1F360;ROASTED SWEET POTATO;So;0;ON;;;;;N;;;;; +1F361;DANGO;So;0;ON;;;;;N;;;;; +1F362;ODEN;So;0;ON;;;;;N;;;;; +1F363;SUSHI;So;0;ON;;;;;N;;;;; +1F364;FRIED SHRIMP;So;0;ON;;;;;N;;;;; +1F365;FISH CAKE WITH SWIRL DESIGN;So;0;ON;;;;;N;;;;; +1F366;SOFT ICE CREAM;So;0;ON;;;;;N;;;;; +1F367;SHAVED ICE;So;0;ON;;;;;N;;;;; +1F368;ICE CREAM;So;0;ON;;;;;N;;;;; +1F369;DOUGHNUT;So;0;ON;;;;;N;;;;; +1F36A;COOKIE;So;0;ON;;;;;N;;;;; +1F36B;CHOCOLATE BAR;So;0;ON;;;;;N;;;;; +1F36C;CANDY;So;0;ON;;;;;N;;;;; +1F36D;LOLLIPOP;So;0;ON;;;;;N;;;;; +1F36E;CUSTARD;So;0;ON;;;;;N;;;;; +1F36F;HONEY POT;So;0;ON;;;;;N;;;;; +1F370;SHORTCAKE;So;0;ON;;;;;N;;;;; +1F371;BENTO BOX;So;0;ON;;;;;N;;;;; +1F372;POT OF FOOD;So;0;ON;;;;;N;;;;; +1F373;COOKING;So;0;ON;;;;;N;;;;; +1F374;FORK AND KNIFE;So;0;ON;;;;;N;;;;; +1F375;TEACUP WITHOUT HANDLE;So;0;ON;;;;;N;;;;; +1F376;SAKE BOTTLE AND CUP;So;0;ON;;;;;N;;;;; +1F377;WINE GLASS;So;0;ON;;;;;N;;;;; +1F378;COCKTAIL GLASS;So;0;ON;;;;;N;;;;; +1F379;TROPICAL DRINK;So;0;ON;;;;;N;;;;; +1F37A;BEER MUG;So;0;ON;;;;;N;;;;; +1F37B;CLINKING BEER MUGS;So;0;ON;;;;;N;;;;; +1F37C;BABY BOTTLE;So;0;ON;;;;;N;;;;; +1F37D;FORK AND KNIFE WITH PLATE;So;0;ON;;;;;N;;;;; +1F37E;BOTTLE WITH POPPING CORK;So;0;ON;;;;;N;;;;; +1F37F;POPCORN;So;0;ON;;;;;N;;;;; +1F380;RIBBON;So;0;ON;;;;;N;;;;; +1F381;WRAPPED PRESENT;So;0;ON;;;;;N;;;;; +1F382;BIRTHDAY CAKE;So;0;ON;;;;;N;;;;; +1F383;JACK-O-LANTERN;So;0;ON;;;;;N;;;;; +1F384;CHRISTMAS TREE;So;0;ON;;;;;N;;;;; +1F385;FATHER CHRISTMAS;So;0;ON;;;;;N;;;;; +1F386;FIREWORKS;So;0;ON;;;;;N;;;;; +1F387;FIREWORK SPARKLER;So;0;ON;;;;;N;;;;; +1F388;BALLOON;So;0;ON;;;;;N;;;;; +1F389;PARTY POPPER;So;0;ON;;;;;N;;;;; +1F38A;CONFETTI BALL;So;0;ON;;;;;N;;;;; +1F38B;TANABATA TREE;So;0;ON;;;;;N;;;;; +1F38C;CROSSED FLAGS;So;0;ON;;;;;N;;;;; +1F38D;PINE DECORATION;So;0;ON;;;;;N;;;;; +1F38E;JAPANESE DOLLS;So;0;ON;;;;;N;;;;; +1F38F;CARP STREAMER;So;0;ON;;;;;N;;;;; +1F390;WIND CHIME;So;0;ON;;;;;N;;;;; +1F391;MOON VIEWING CEREMONY;So;0;ON;;;;;N;;;;; +1F392;SCHOOL SATCHEL;So;0;ON;;;;;N;;;;; +1F393;GRADUATION CAP;So;0;ON;;;;;N;;;;; +1F394;HEART WITH TIP ON THE LEFT;So;0;ON;;;;;N;;;;; +1F395;BOUQUET OF FLOWERS;So;0;ON;;;;;N;;;;; +1F396;MILITARY MEDAL;So;0;ON;;;;;N;;;;; +1F397;REMINDER RIBBON;So;0;ON;;;;;N;;;;; +1F398;MUSICAL KEYBOARD WITH JACKS;So;0;ON;;;;;N;;;;; +1F399;STUDIO MICROPHONE;So;0;ON;;;;;N;;;;; +1F39A;LEVEL SLIDER;So;0;ON;;;;;N;;;;; +1F39B;CONTROL KNOBS;So;0;ON;;;;;N;;;;; +1F39C;BEAMED ASCENDING MUSICAL NOTES;So;0;ON;;;;;N;;;;; +1F39D;BEAMED DESCENDING MUSICAL NOTES;So;0;ON;;;;;N;;;;; +1F39E;FILM FRAMES;So;0;ON;;;;;N;;;;; +1F39F;ADMISSION TICKETS;So;0;ON;;;;;N;;;;; +1F3A0;CAROUSEL HORSE;So;0;ON;;;;;N;;;;; +1F3A1;FERRIS WHEEL;So;0;ON;;;;;N;;;;; +1F3A2;ROLLER COASTER;So;0;ON;;;;;N;;;;; +1F3A3;FISHING POLE AND FISH;So;0;ON;;;;;N;;;;; +1F3A4;MICROPHONE;So;0;ON;;;;;N;;;;; +1F3A5;MOVIE CAMERA;So;0;ON;;;;;N;;;;; +1F3A6;CINEMA;So;0;ON;;;;;N;;;;; +1F3A7;HEADPHONE;So;0;ON;;;;;N;;;;; +1F3A8;ARTIST PALETTE;So;0;ON;;;;;N;;;;; +1F3A9;TOP HAT;So;0;ON;;;;;N;;;;; +1F3AA;CIRCUS TENT;So;0;ON;;;;;N;;;;; +1F3AB;TICKET;So;0;ON;;;;;N;;;;; +1F3AC;CLAPPER BOARD;So;0;ON;;;;;N;;;;; +1F3AD;PERFORMING ARTS;So;0;ON;;;;;N;;;;; +1F3AE;VIDEO GAME;So;0;ON;;;;;N;;;;; +1F3AF;DIRECT HIT;So;0;ON;;;;;N;;;;; +1F3B0;SLOT MACHINE;So;0;ON;;;;;N;;;;; +1F3B1;BILLIARDS;So;0;ON;;;;;N;;;;; +1F3B2;GAME DIE;So;0;ON;;;;;N;;;;; +1F3B3;BOWLING;So;0;ON;;;;;N;;;;; +1F3B4;FLOWER PLAYING CARDS;So;0;ON;;;;;N;;;;; +1F3B5;MUSICAL NOTE;So;0;ON;;;;;N;;;;; +1F3B6;MULTIPLE MUSICAL NOTES;So;0;ON;;;;;N;;;;; +1F3B7;SAXOPHONE;So;0;ON;;;;;N;;;;; +1F3B8;GUITAR;So;0;ON;;;;;N;;;;; +1F3B9;MUSICAL KEYBOARD;So;0;ON;;;;;N;;;;; +1F3BA;TRUMPET;So;0;ON;;;;;N;;;;; +1F3BB;VIOLIN;So;0;ON;;;;;N;;;;; +1F3BC;MUSICAL SCORE;So;0;ON;;;;;N;;;;; +1F3BD;RUNNING SHIRT WITH SASH;So;0;ON;;;;;N;;;;; +1F3BE;TENNIS RACQUET AND BALL;So;0;ON;;;;;N;;;;; +1F3BF;SKI AND SKI BOOT;So;0;ON;;;;;N;;;;; +1F3C0;BASKETBALL AND HOOP;So;0;ON;;;;;N;;;;; +1F3C1;CHEQUERED FLAG;So;0;ON;;;;;N;;;;; +1F3C2;SNOWBOARDER;So;0;ON;;;;;N;;;;; +1F3C3;RUNNER;So;0;ON;;;;;N;;;;; +1F3C4;SURFER;So;0;ON;;;;;N;;;;; +1F3C5;SPORTS MEDAL;So;0;ON;;;;;N;;;;; +1F3C6;TROPHY;So;0;ON;;;;;N;;;;; +1F3C7;HORSE RACING;So;0;ON;;;;;N;;;;; +1F3C8;AMERICAN FOOTBALL;So;0;ON;;;;;N;;;;; +1F3C9;RUGBY FOOTBALL;So;0;ON;;;;;N;;;;; +1F3CA;SWIMMER;So;0;ON;;;;;N;;;;; +1F3CB;WEIGHT LIFTER;So;0;ON;;;;;N;;;;; +1F3CC;GOLFER;So;0;ON;;;;;N;;;;; +1F3CD;RACING MOTORCYCLE;So;0;ON;;;;;N;;;;; +1F3CE;RACING CAR;So;0;ON;;;;;N;;;;; +1F3CF;CRICKET BAT AND BALL;So;0;ON;;;;;N;;;;; +1F3D0;VOLLEYBALL;So;0;ON;;;;;N;;;;; +1F3D1;FIELD HOCKEY STICK AND BALL;So;0;ON;;;;;N;;;;; +1F3D2;ICE HOCKEY STICK AND PUCK;So;0;ON;;;;;N;;;;; +1F3D3;TABLE TENNIS PADDLE AND BALL;So;0;ON;;;;;N;;;;; +1F3D4;SNOW CAPPED MOUNTAIN;So;0;ON;;;;;N;;;;; +1F3D5;CAMPING;So;0;ON;;;;;N;;;;; +1F3D6;BEACH WITH UMBRELLA;So;0;ON;;;;;N;;;;; +1F3D7;BUILDING CONSTRUCTION;So;0;ON;;;;;N;;;;; +1F3D8;HOUSE BUILDINGS;So;0;ON;;;;;N;;;;; +1F3D9;CITYSCAPE;So;0;ON;;;;;N;;;;; +1F3DA;DERELICT HOUSE BUILDING;So;0;ON;;;;;N;;;;; +1F3DB;CLASSICAL BUILDING;So;0;ON;;;;;N;;;;; +1F3DC;DESERT;So;0;ON;;;;;N;;;;; +1F3DD;DESERT ISLAND;So;0;ON;;;;;N;;;;; +1F3DE;NATIONAL PARK;So;0;ON;;;;;N;;;;; +1F3DF;STADIUM;So;0;ON;;;;;N;;;;; +1F3E0;HOUSE BUILDING;So;0;ON;;;;;N;;;;; +1F3E1;HOUSE WITH GARDEN;So;0;ON;;;;;N;;;;; +1F3E2;OFFICE BUILDING;So;0;ON;;;;;N;;;;; +1F3E3;JAPANESE POST OFFICE;So;0;ON;;;;;N;;;;; +1F3E4;EUROPEAN POST OFFICE;So;0;ON;;;;;N;;;;; +1F3E5;HOSPITAL;So;0;ON;;;;;N;;;;; +1F3E6;BANK;So;0;ON;;;;;N;;;;; +1F3E7;AUTOMATED TELLER MACHINE;So;0;ON;;;;;N;;;;; +1F3E8;HOTEL;So;0;ON;;;;;N;;;;; +1F3E9;LOVE HOTEL;So;0;ON;;;;;N;;;;; +1F3EA;CONVENIENCE STORE;So;0;ON;;;;;N;;;;; +1F3EB;SCHOOL;So;0;ON;;;;;N;;;;; +1F3EC;DEPARTMENT STORE;So;0;ON;;;;;N;;;;; +1F3ED;FACTORY;So;0;ON;;;;;N;;;;; +1F3EE;IZAKAYA LANTERN;So;0;ON;;;;;N;;;;; +1F3EF;JAPANESE CASTLE;So;0;ON;;;;;N;;;;; +1F3F0;EUROPEAN CASTLE;So;0;ON;;;;;N;;;;; +1F3F1;WHITE PENNANT;So;0;ON;;;;;N;;;;; +1F3F2;BLACK PENNANT;So;0;ON;;;;;N;;;;; +1F3F3;WAVING WHITE FLAG;So;0;ON;;;;;N;;;;; +1F3F4;WAVING BLACK FLAG;So;0;ON;;;;;N;;;;; +1F3F5;ROSETTE;So;0;ON;;;;;N;;;;; +1F3F6;BLACK ROSETTE;So;0;ON;;;;;N;;;;; +1F3F7;LABEL;So;0;ON;;;;;N;;;;; +1F3F8;BADMINTON RACQUET AND SHUTTLECOCK;So;0;ON;;;;;N;;;;; +1F3F9;BOW AND ARROW;So;0;ON;;;;;N;;;;; +1F3FA;AMPHORA;So;0;ON;;;;;N;;;;; +1F3FB;EMOJI MODIFIER FITZPATRICK TYPE-1-2;Sk;0;ON;;;;;N;;;;; +1F3FC;EMOJI MODIFIER FITZPATRICK TYPE-3;Sk;0;ON;;;;;N;;;;; +1F3FD;EMOJI MODIFIER FITZPATRICK TYPE-4;Sk;0;ON;;;;;N;;;;; +1F3FE;EMOJI MODIFIER FITZPATRICK TYPE-5;Sk;0;ON;;;;;N;;;;; +1F3FF;EMOJI MODIFIER FITZPATRICK TYPE-6;Sk;0;ON;;;;;N;;;;; +1F400;RAT;So;0;ON;;;;;N;;;;; +1F401;MOUSE;So;0;ON;;;;;N;;;;; +1F402;OX;So;0;ON;;;;;N;;;;; +1F403;WATER BUFFALO;So;0;ON;;;;;N;;;;; +1F404;COW;So;0;ON;;;;;N;;;;; +1F405;TIGER;So;0;ON;;;;;N;;;;; +1F406;LEOPARD;So;0;ON;;;;;N;;;;; +1F407;RABBIT;So;0;ON;;;;;N;;;;; +1F408;CAT;So;0;ON;;;;;N;;;;; +1F409;DRAGON;So;0;ON;;;;;N;;;;; +1F40A;CROCODILE;So;0;ON;;;;;N;;;;; +1F40B;WHALE;So;0;ON;;;;;N;;;;; +1F40C;SNAIL;So;0;ON;;;;;N;;;;; +1F40D;SNAKE;So;0;ON;;;;;N;;;;; +1F40E;HORSE;So;0;ON;;;;;N;;;;; +1F40F;RAM;So;0;ON;;;;;N;;;;; +1F410;GOAT;So;0;ON;;;;;N;;;;; +1F411;SHEEP;So;0;ON;;;;;N;;;;; +1F412;MONKEY;So;0;ON;;;;;N;;;;; +1F413;ROOSTER;So;0;ON;;;;;N;;;;; +1F414;CHICKEN;So;0;ON;;;;;N;;;;; +1F415;DOG;So;0;ON;;;;;N;;;;; +1F416;PIG;So;0;ON;;;;;N;;;;; +1F417;BOAR;So;0;ON;;;;;N;;;;; +1F418;ELEPHANT;So;0;ON;;;;;N;;;;; +1F419;OCTOPUS;So;0;ON;;;;;N;;;;; +1F41A;SPIRAL SHELL;So;0;ON;;;;;N;;;;; +1F41B;BUG;So;0;ON;;;;;N;;;;; +1F41C;ANT;So;0;ON;;;;;N;;;;; +1F41D;HONEYBEE;So;0;ON;;;;;N;;;;; +1F41E;LADY BEETLE;So;0;ON;;;;;N;;;;; +1F41F;FISH;So;0;ON;;;;;N;;;;; +1F420;TROPICAL FISH;So;0;ON;;;;;N;;;;; +1F421;BLOWFISH;So;0;ON;;;;;N;;;;; +1F422;TURTLE;So;0;ON;;;;;N;;;;; +1F423;HATCHING CHICK;So;0;ON;;;;;N;;;;; +1F424;BABY CHICK;So;0;ON;;;;;N;;;;; +1F425;FRONT-FACING BABY CHICK;So;0;ON;;;;;N;;;;; +1F426;BIRD;So;0;ON;;;;;N;;;;; +1F427;PENGUIN;So;0;ON;;;;;N;;;;; +1F428;KOALA;So;0;ON;;;;;N;;;;; +1F429;POODLE;So;0;ON;;;;;N;;;;; +1F42A;DROMEDARY CAMEL;So;0;ON;;;;;N;;;;; +1F42B;BACTRIAN CAMEL;So;0;ON;;;;;N;;;;; +1F42C;DOLPHIN;So;0;ON;;;;;N;;;;; +1F42D;MOUSE FACE;So;0;ON;;;;;N;;;;; +1F42E;COW FACE;So;0;ON;;;;;N;;;;; +1F42F;TIGER FACE;So;0;ON;;;;;N;;;;; +1F430;RABBIT FACE;So;0;ON;;;;;N;;;;; +1F431;CAT FACE;So;0;ON;;;;;N;;;;; +1F432;DRAGON FACE;So;0;ON;;;;;N;;;;; +1F433;SPOUTING WHALE;So;0;ON;;;;;N;;;;; +1F434;HORSE FACE;So;0;ON;;;;;N;;;;; +1F435;MONKEY FACE;So;0;ON;;;;;N;;;;; +1F436;DOG FACE;So;0;ON;;;;;N;;;;; +1F437;PIG FACE;So;0;ON;;;;;N;;;;; +1F438;FROG FACE;So;0;ON;;;;;N;;;;; +1F439;HAMSTER FACE;So;0;ON;;;;;N;;;;; +1F43A;WOLF FACE;So;0;ON;;;;;N;;;;; +1F43B;BEAR FACE;So;0;ON;;;;;N;;;;; +1F43C;PANDA FACE;So;0;ON;;;;;N;;;;; +1F43D;PIG NOSE;So;0;ON;;;;;N;;;;; +1F43E;PAW PRINTS;So;0;ON;;;;;N;;;;; +1F43F;CHIPMUNK;So;0;ON;;;;;N;;;;; +1F440;EYES;So;0;ON;;;;;N;;;;; +1F441;EYE;So;0;ON;;;;;N;;;;; +1F442;EAR;So;0;ON;;;;;N;;;;; +1F443;NOSE;So;0;ON;;;;;N;;;;; +1F444;MOUTH;So;0;ON;;;;;N;;;;; +1F445;TONGUE;So;0;ON;;;;;N;;;;; +1F446;WHITE UP POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; +1F447;WHITE DOWN POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; +1F448;WHITE LEFT POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; +1F449;WHITE RIGHT POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; +1F44A;FISTED HAND SIGN;So;0;ON;;;;;N;;;;; +1F44B;WAVING HAND SIGN;So;0;ON;;;;;N;;;;; +1F44C;OK HAND SIGN;So;0;ON;;;;;N;;;;; +1F44D;THUMBS UP SIGN;So;0;ON;;;;;N;;;;; +1F44E;THUMBS DOWN SIGN;So;0;ON;;;;;N;;;;; +1F44F;CLAPPING HANDS SIGN;So;0;ON;;;;;N;;;;; +1F450;OPEN HANDS SIGN;So;0;ON;;;;;N;;;;; +1F451;CROWN;So;0;ON;;;;;N;;;;; +1F452;WOMANS HAT;So;0;ON;;;;;N;;;;; +1F453;EYEGLASSES;So;0;ON;;;;;N;;;;; +1F454;NECKTIE;So;0;ON;;;;;N;;;;; +1F455;T-SHIRT;So;0;ON;;;;;N;;;;; +1F456;JEANS;So;0;ON;;;;;N;;;;; +1F457;DRESS;So;0;ON;;;;;N;;;;; +1F458;KIMONO;So;0;ON;;;;;N;;;;; +1F459;BIKINI;So;0;ON;;;;;N;;;;; +1F45A;WOMANS CLOTHES;So;0;ON;;;;;N;;;;; +1F45B;PURSE;So;0;ON;;;;;N;;;;; +1F45C;HANDBAG;So;0;ON;;;;;N;;;;; +1F45D;POUCH;So;0;ON;;;;;N;;;;; +1F45E;MANS SHOE;So;0;ON;;;;;N;;;;; +1F45F;ATHLETIC SHOE;So;0;ON;;;;;N;;;;; +1F460;HIGH-HEELED SHOE;So;0;ON;;;;;N;;;;; +1F461;WOMANS SANDAL;So;0;ON;;;;;N;;;;; +1F462;WOMANS BOOTS;So;0;ON;;;;;N;;;;; +1F463;FOOTPRINTS;So;0;ON;;;;;N;;;;; +1F464;BUST IN SILHOUETTE;So;0;ON;;;;;N;;;;; +1F465;BUSTS IN SILHOUETTE;So;0;ON;;;;;N;;;;; +1F466;BOY;So;0;ON;;;;;N;;;;; +1F467;GIRL;So;0;ON;;;;;N;;;;; +1F468;MAN;So;0;ON;;;;;N;;;;; +1F469;WOMAN;So;0;ON;;;;;N;;;;; +1F46A;FAMILY;So;0;ON;;;;;N;;;;; +1F46B;MAN AND WOMAN HOLDING HANDS;So;0;ON;;;;;N;;;;; +1F46C;TWO MEN HOLDING HANDS;So;0;ON;;;;;N;;;;; +1F46D;TWO WOMEN HOLDING HANDS;So;0;ON;;;;;N;;;;; +1F46E;POLICE OFFICER;So;0;ON;;;;;N;;;;; +1F46F;WOMAN WITH BUNNY EARS;So;0;ON;;;;;N;;;;; +1F470;BRIDE WITH VEIL;So;0;ON;;;;;N;;;;; +1F471;PERSON WITH BLOND HAIR;So;0;ON;;;;;N;;;;; +1F472;MAN WITH GUA PI MAO;So;0;ON;;;;;N;;;;; +1F473;MAN WITH TURBAN;So;0;ON;;;;;N;;;;; +1F474;OLDER MAN;So;0;ON;;;;;N;;;;; +1F475;OLDER WOMAN;So;0;ON;;;;;N;;;;; +1F476;BABY;So;0;ON;;;;;N;;;;; +1F477;CONSTRUCTION WORKER;So;0;ON;;;;;N;;;;; +1F478;PRINCESS;So;0;ON;;;;;N;;;;; +1F479;JAPANESE OGRE;So;0;ON;;;;;N;;;;; +1F47A;JAPANESE GOBLIN;So;0;ON;;;;;N;;;;; +1F47B;GHOST;So;0;ON;;;;;N;;;;; +1F47C;BABY ANGEL;So;0;ON;;;;;N;;;;; +1F47D;EXTRATERRESTRIAL ALIEN;So;0;ON;;;;;N;;;;; +1F47E;ALIEN MONSTER;So;0;ON;;;;;N;;;;; +1F47F;IMP;So;0;ON;;;;;N;;;;; +1F480;SKULL;So;0;ON;;;;;N;;;;; +1F481;INFORMATION DESK PERSON;So;0;ON;;;;;N;;;;; +1F482;GUARDSMAN;So;0;ON;;;;;N;;;;; +1F483;DANCER;So;0;ON;;;;;N;;;;; +1F484;LIPSTICK;So;0;ON;;;;;N;;;;; +1F485;NAIL POLISH;So;0;ON;;;;;N;;;;; +1F486;FACE MASSAGE;So;0;ON;;;;;N;;;;; +1F487;HAIRCUT;So;0;ON;;;;;N;;;;; +1F488;BARBER POLE;So;0;ON;;;;;N;;;;; +1F489;SYRINGE;So;0;ON;;;;;N;;;;; +1F48A;PILL;So;0;ON;;;;;N;;;;; +1F48B;KISS MARK;So;0;ON;;;;;N;;;;; +1F48C;LOVE LETTER;So;0;ON;;;;;N;;;;; +1F48D;RING;So;0;ON;;;;;N;;;;; +1F48E;GEM STONE;So;0;ON;;;;;N;;;;; +1F48F;KISS;So;0;ON;;;;;N;;;;; +1F490;BOUQUET;So;0;ON;;;;;N;;;;; +1F491;COUPLE WITH HEART;So;0;ON;;;;;N;;;;; +1F492;WEDDING;So;0;ON;;;;;N;;;;; +1F493;BEATING HEART;So;0;ON;;;;;N;;;;; +1F494;BROKEN HEART;So;0;ON;;;;;N;;;;; +1F495;TWO HEARTS;So;0;ON;;;;;N;;;;; +1F496;SPARKLING HEART;So;0;ON;;;;;N;;;;; +1F497;GROWING HEART;So;0;ON;;;;;N;;;;; +1F498;HEART WITH ARROW;So;0;ON;;;;;N;;;;; +1F499;BLUE HEART;So;0;ON;;;;;N;;;;; +1F49A;GREEN HEART;So;0;ON;;;;;N;;;;; +1F49B;YELLOW HEART;So;0;ON;;;;;N;;;;; +1F49C;PURPLE HEART;So;0;ON;;;;;N;;;;; +1F49D;HEART WITH RIBBON;So;0;ON;;;;;N;;;;; +1F49E;REVOLVING HEARTS;So;0;ON;;;;;N;;;;; +1F49F;HEART DECORATION;So;0;ON;;;;;N;;;;; +1F4A0;DIAMOND SHAPE WITH A DOT INSIDE;So;0;ON;;;;;N;;;;; +1F4A1;ELECTRIC LIGHT BULB;So;0;ON;;;;;N;;;;; +1F4A2;ANGER SYMBOL;So;0;ON;;;;;N;;;;; +1F4A3;BOMB;So;0;ON;;;;;N;;;;; +1F4A4;SLEEPING SYMBOL;So;0;ON;;;;;N;;;;; +1F4A5;COLLISION SYMBOL;So;0;ON;;;;;N;;;;; +1F4A6;SPLASHING SWEAT SYMBOL;So;0;ON;;;;;N;;;;; +1F4A7;DROPLET;So;0;ON;;;;;N;;;;; +1F4A8;DASH SYMBOL;So;0;ON;;;;;N;;;;; +1F4A9;PILE OF POO;So;0;ON;;;;;N;;;;; +1F4AA;FLEXED BICEPS;So;0;ON;;;;;N;;;;; +1F4AB;DIZZY SYMBOL;So;0;ON;;;;;N;;;;; +1F4AC;SPEECH BALLOON;So;0;ON;;;;;N;;;;; +1F4AD;THOUGHT BALLOON;So;0;ON;;;;;N;;;;; +1F4AE;WHITE FLOWER;So;0;ON;;;;;N;;;;; +1F4AF;HUNDRED POINTS SYMBOL;So;0;ON;;;;;N;;;;; +1F4B0;MONEY BAG;So;0;ON;;;;;N;;;;; +1F4B1;CURRENCY EXCHANGE;So;0;ON;;;;;N;;;;; +1F4B2;HEAVY DOLLAR SIGN;So;0;ON;;;;;N;;;;; +1F4B3;CREDIT CARD;So;0;ON;;;;;N;;;;; +1F4B4;BANKNOTE WITH YEN SIGN;So;0;ON;;;;;N;;;;; +1F4B5;BANKNOTE WITH DOLLAR SIGN;So;0;ON;;;;;N;;;;; +1F4B6;BANKNOTE WITH EURO SIGN;So;0;ON;;;;;N;;;;; +1F4B7;BANKNOTE WITH POUND SIGN;So;0;ON;;;;;N;;;;; +1F4B8;MONEY WITH WINGS;So;0;ON;;;;;N;;;;; +1F4B9;CHART WITH UPWARDS TREND AND YEN SIGN;So;0;ON;;;;;N;;;;; +1F4BA;SEAT;So;0;ON;;;;;N;;;;; +1F4BB;PERSONAL COMPUTER;So;0;ON;;;;;N;;;;; +1F4BC;BRIEFCASE;So;0;ON;;;;;N;;;;; +1F4BD;MINIDISC;So;0;ON;;;;;N;;;;; +1F4BE;FLOPPY DISK;So;0;ON;;;;;N;;;;; +1F4BF;OPTICAL DISC;So;0;ON;;;;;N;;;;; +1F4C0;DVD;So;0;ON;;;;;N;;;;; +1F4C1;FILE FOLDER;So;0;ON;;;;;N;;;;; +1F4C2;OPEN FILE FOLDER;So;0;ON;;;;;N;;;;; +1F4C3;PAGE WITH CURL;So;0;ON;;;;;N;;;;; +1F4C4;PAGE FACING UP;So;0;ON;;;;;N;;;;; +1F4C5;CALENDAR;So;0;ON;;;;;N;;;;; +1F4C6;TEAR-OFF CALENDAR;So;0;ON;;;;;N;;;;; +1F4C7;CARD INDEX;So;0;ON;;;;;N;;;;; +1F4C8;CHART WITH UPWARDS TREND;So;0;ON;;;;;N;;;;; +1F4C9;CHART WITH DOWNWARDS TREND;So;0;ON;;;;;N;;;;; +1F4CA;BAR CHART;So;0;ON;;;;;N;;;;; +1F4CB;CLIPBOARD;So;0;ON;;;;;N;;;;; +1F4CC;PUSHPIN;So;0;ON;;;;;N;;;;; +1F4CD;ROUND PUSHPIN;So;0;ON;;;;;N;;;;; +1F4CE;PAPERCLIP;So;0;ON;;;;;N;;;;; +1F4CF;STRAIGHT RULER;So;0;ON;;;;;N;;;;; +1F4D0;TRIANGULAR RULER;So;0;ON;;;;;N;;;;; +1F4D1;BOOKMARK TABS;So;0;ON;;;;;N;;;;; +1F4D2;LEDGER;So;0;ON;;;;;N;;;;; +1F4D3;NOTEBOOK;So;0;ON;;;;;N;;;;; +1F4D4;NOTEBOOK WITH DECORATIVE COVER;So;0;ON;;;;;N;;;;; +1F4D5;CLOSED BOOK;So;0;ON;;;;;N;;;;; +1F4D6;OPEN BOOK;So;0;ON;;;;;N;;;;; +1F4D7;GREEN BOOK;So;0;ON;;;;;N;;;;; +1F4D8;BLUE BOOK;So;0;ON;;;;;N;;;;; +1F4D9;ORANGE BOOK;So;0;ON;;;;;N;;;;; +1F4DA;BOOKS;So;0;ON;;;;;N;;;;; +1F4DB;NAME BADGE;So;0;ON;;;;;N;;;;; +1F4DC;SCROLL;So;0;ON;;;;;N;;;;; +1F4DD;MEMO;So;0;ON;;;;;N;;;;; +1F4DE;TELEPHONE RECEIVER;So;0;ON;;;;;N;;;;; +1F4DF;PAGER;So;0;ON;;;;;N;;;;; +1F4E0;FAX MACHINE;So;0;ON;;;;;N;;;;; +1F4E1;SATELLITE ANTENNA;So;0;ON;;;;;N;;;;; +1F4E2;PUBLIC ADDRESS LOUDSPEAKER;So;0;ON;;;;;N;;;;; +1F4E3;CHEERING MEGAPHONE;So;0;ON;;;;;N;;;;; +1F4E4;OUTBOX TRAY;So;0;ON;;;;;N;;;;; +1F4E5;INBOX TRAY;So;0;ON;;;;;N;;;;; +1F4E6;PACKAGE;So;0;ON;;;;;N;;;;; +1F4E7;E-MAIL SYMBOL;So;0;ON;;;;;N;;;;; +1F4E8;INCOMING ENVELOPE;So;0;ON;;;;;N;;;;; +1F4E9;ENVELOPE WITH DOWNWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;; +1F4EA;CLOSED MAILBOX WITH LOWERED FLAG;So;0;ON;;;;;N;;;;; +1F4EB;CLOSED MAILBOX WITH RAISED FLAG;So;0;ON;;;;;N;;;;; +1F4EC;OPEN MAILBOX WITH RAISED FLAG;So;0;ON;;;;;N;;;;; +1F4ED;OPEN MAILBOX WITH LOWERED FLAG;So;0;ON;;;;;N;;;;; +1F4EE;POSTBOX;So;0;ON;;;;;N;;;;; +1F4EF;POSTAL HORN;So;0;ON;;;;;N;;;;; +1F4F0;NEWSPAPER;So;0;ON;;;;;N;;;;; +1F4F1;MOBILE PHONE;So;0;ON;;;;;N;;;;; +1F4F2;MOBILE PHONE WITH RIGHTWARDS ARROW AT LEFT;So;0;ON;;;;;N;;;;; +1F4F3;VIBRATION MODE;So;0;ON;;;;;N;;;;; +1F4F4;MOBILE PHONE OFF;So;0;ON;;;;;N;;;;; +1F4F5;NO MOBILE PHONES;So;0;ON;;;;;N;;;;; +1F4F6;ANTENNA WITH BARS;So;0;ON;;;;;N;;;;; +1F4F7;CAMERA;So;0;ON;;;;;N;;;;; +1F4F8;CAMERA WITH FLASH;So;0;ON;;;;;N;;;;; +1F4F9;VIDEO CAMERA;So;0;ON;;;;;N;;;;; +1F4FA;TELEVISION;So;0;ON;;;;;N;;;;; +1F4FB;RADIO;So;0;ON;;;;;N;;;;; +1F4FC;VIDEOCASSETTE;So;0;ON;;;;;N;;;;; +1F4FD;FILM PROJECTOR;So;0;ON;;;;;N;;;;; +1F4FE;PORTABLE STEREO;So;0;ON;;;;;N;;;;; +1F4FF;PRAYER BEADS;So;0;ON;;;;;N;;;;; +1F500;TWISTED RIGHTWARDS ARROWS;So;0;ON;;;;;N;;;;; +1F501;CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS;So;0;ON;;;;;N;;;;; +1F502;CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS WITH CIRCLED ONE OVERLAY;So;0;ON;;;;;N;;;;; +1F503;CLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS;So;0;ON;;;;;N;;;;; +1F504;ANTICLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS;So;0;ON;;;;;N;;;;; +1F505;LOW BRIGHTNESS SYMBOL;So;0;ON;;;;;N;;;;; +1F506;HIGH BRIGHTNESS SYMBOL;So;0;ON;;;;;N;;;;; +1F507;SPEAKER WITH CANCELLATION STROKE;So;0;ON;;;;;N;;;;; +1F508;SPEAKER;So;0;ON;;;;;N;;;;; +1F509;SPEAKER WITH ONE SOUND WAVE;So;0;ON;;;;;N;;;;; +1F50A;SPEAKER WITH THREE SOUND WAVES;So;0;ON;;;;;N;;;;; +1F50B;BATTERY;So;0;ON;;;;;N;;;;; +1F50C;ELECTRIC PLUG;So;0;ON;;;;;N;;;;; +1F50D;LEFT-POINTING MAGNIFYING GLASS;So;0;ON;;;;;N;;;;; +1F50E;RIGHT-POINTING MAGNIFYING GLASS;So;0;ON;;;;;N;;;;; +1F50F;LOCK WITH INK PEN;So;0;ON;;;;;N;;;;; +1F510;CLOSED LOCK WITH KEY;So;0;ON;;;;;N;;;;; +1F511;KEY;So;0;ON;;;;;N;;;;; +1F512;LOCK;So;0;ON;;;;;N;;;;; +1F513;OPEN LOCK;So;0;ON;;;;;N;;;;; +1F514;BELL;So;0;ON;;;;;N;;;;; +1F515;BELL WITH CANCELLATION STROKE;So;0;ON;;;;;N;;;;; +1F516;BOOKMARK;So;0;ON;;;;;N;;;;; +1F517;LINK SYMBOL;So;0;ON;;;;;N;;;;; +1F518;RADIO BUTTON;So;0;ON;;;;;N;;;;; +1F519;BACK WITH LEFTWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;; +1F51A;END WITH LEFTWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;; +1F51B;ON WITH EXCLAMATION MARK WITH LEFT RIGHT ARROW ABOVE;So;0;ON;;;;;N;;;;; +1F51C;SOON WITH RIGHTWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;; +1F51D;TOP WITH UPWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;; +1F51E;NO ONE UNDER EIGHTEEN SYMBOL;So;0;ON;;;;;N;;;;; +1F51F;KEYCAP TEN;So;0;ON;;;;;N;;;;; +1F520;INPUT SYMBOL FOR LATIN CAPITAL LETTERS;So;0;ON;;;;;N;;;;; +1F521;INPUT SYMBOL FOR LATIN SMALL LETTERS;So;0;ON;;;;;N;;;;; +1F522;INPUT SYMBOL FOR NUMBERS;So;0;ON;;;;;N;;;;; +1F523;INPUT SYMBOL FOR SYMBOLS;So;0;ON;;;;;N;;;;; +1F524;INPUT SYMBOL FOR LATIN LETTERS;So;0;ON;;;;;N;;;;; +1F525;FIRE;So;0;ON;;;;;N;;;;; +1F526;ELECTRIC TORCH;So;0;ON;;;;;N;;;;; +1F527;WRENCH;So;0;ON;;;;;N;;;;; +1F528;HAMMER;So;0;ON;;;;;N;;;;; +1F529;NUT AND BOLT;So;0;ON;;;;;N;;;;; +1F52A;HOCHO;So;0;ON;;;;;N;;;;; +1F52B;PISTOL;So;0;ON;;;;;N;;;;; +1F52C;MICROSCOPE;So;0;ON;;;;;N;;;;; +1F52D;TELESCOPE;So;0;ON;;;;;N;;;;; +1F52E;CRYSTAL BALL;So;0;ON;;;;;N;;;;; +1F52F;SIX POINTED STAR WITH MIDDLE DOT;So;0;ON;;;;;N;;;;; +1F530;JAPANESE SYMBOL FOR BEGINNER;So;0;ON;;;;;N;;;;; +1F531;TRIDENT EMBLEM;So;0;ON;;;;;N;;;;; +1F532;BLACK SQUARE BUTTON;So;0;ON;;;;;N;;;;; +1F533;WHITE SQUARE BUTTON;So;0;ON;;;;;N;;;;; +1F534;LARGE RED CIRCLE;So;0;ON;;;;;N;;;;; +1F535;LARGE BLUE CIRCLE;So;0;ON;;;;;N;;;;; +1F536;LARGE ORANGE DIAMOND;So;0;ON;;;;;N;;;;; +1F537;LARGE BLUE DIAMOND;So;0;ON;;;;;N;;;;; +1F538;SMALL ORANGE DIAMOND;So;0;ON;;;;;N;;;;; +1F539;SMALL BLUE DIAMOND;So;0;ON;;;;;N;;;;; +1F53A;UP-POINTING RED TRIANGLE;So;0;ON;;;;;N;;;;; +1F53B;DOWN-POINTING RED TRIANGLE;So;0;ON;;;;;N;;;;; +1F53C;UP-POINTING SMALL RED TRIANGLE;So;0;ON;;;;;N;;;;; +1F53D;DOWN-POINTING SMALL RED TRIANGLE;So;0;ON;;;;;N;;;;; +1F53E;LOWER RIGHT SHADOWED WHITE CIRCLE;So;0;ON;;;;;N;;;;; +1F53F;UPPER RIGHT SHADOWED WHITE CIRCLE;So;0;ON;;;;;N;;;;; +1F540;CIRCLED CROSS POMMEE;So;0;ON;;;;;N;;;;; +1F541;CROSS POMMEE WITH HALF-CIRCLE BELOW;So;0;ON;;;;;N;;;;; +1F542;CROSS POMMEE;So;0;ON;;;;;N;;;;; +1F543;NOTCHED LEFT SEMICIRCLE WITH THREE DOTS;So;0;ON;;;;;N;;;;; +1F544;NOTCHED RIGHT SEMICIRCLE WITH THREE DOTS;So;0;ON;;;;;N;;;;; +1F545;SYMBOL FOR MARKS CHAPTER;So;0;ON;;;;;N;;;;; +1F546;WHITE LATIN CROSS;So;0;ON;;;;;N;;;;; +1F547;HEAVY LATIN CROSS;So;0;ON;;;;;N;;;;; +1F548;CELTIC CROSS;So;0;ON;;;;;N;;;;; +1F549;OM SYMBOL;So;0;ON;;;;;N;;;;; +1F54A;DOVE OF PEACE;So;0;ON;;;;;N;;;;; +1F54B;KAABA;So;0;ON;;;;;N;;;;; +1F54C;MOSQUE;So;0;ON;;;;;N;;;;; +1F54D;SYNAGOGUE;So;0;ON;;;;;N;;;;; +1F54E;MENORAH WITH NINE BRANCHES;So;0;ON;;;;;N;;;;; +1F54F;BOWL OF HYGIEIA;So;0;ON;;;;;N;;;;; +1F550;CLOCK FACE ONE OCLOCK;So;0;ON;;;;;N;;;;; +1F551;CLOCK FACE TWO OCLOCK;So;0;ON;;;;;N;;;;; +1F552;CLOCK FACE THREE OCLOCK;So;0;ON;;;;;N;;;;; +1F553;CLOCK FACE FOUR OCLOCK;So;0;ON;;;;;N;;;;; +1F554;CLOCK FACE FIVE OCLOCK;So;0;ON;;;;;N;;;;; +1F555;CLOCK FACE SIX OCLOCK;So;0;ON;;;;;N;;;;; +1F556;CLOCK FACE SEVEN OCLOCK;So;0;ON;;;;;N;;;;; +1F557;CLOCK FACE EIGHT OCLOCK;So;0;ON;;;;;N;;;;; +1F558;CLOCK FACE NINE OCLOCK;So;0;ON;;;;;N;;;;; +1F559;CLOCK FACE TEN OCLOCK;So;0;ON;;;;;N;;;;; +1F55A;CLOCK FACE ELEVEN OCLOCK;So;0;ON;;;;;N;;;;; +1F55B;CLOCK FACE TWELVE OCLOCK;So;0;ON;;;;;N;;;;; +1F55C;CLOCK FACE ONE-THIRTY;So;0;ON;;;;;N;;;;; +1F55D;CLOCK FACE TWO-THIRTY;So;0;ON;;;;;N;;;;; +1F55E;CLOCK FACE THREE-THIRTY;So;0;ON;;;;;N;;;;; +1F55F;CLOCK FACE FOUR-THIRTY;So;0;ON;;;;;N;;;;; +1F560;CLOCK FACE FIVE-THIRTY;So;0;ON;;;;;N;;;;; +1F561;CLOCK FACE SIX-THIRTY;So;0;ON;;;;;N;;;;; +1F562;CLOCK FACE SEVEN-THIRTY;So;0;ON;;;;;N;;;;; +1F563;CLOCK FACE EIGHT-THIRTY;So;0;ON;;;;;N;;;;; +1F564;CLOCK FACE NINE-THIRTY;So;0;ON;;;;;N;;;;; +1F565;CLOCK FACE TEN-THIRTY;So;0;ON;;;;;N;;;;; +1F566;CLOCK FACE ELEVEN-THIRTY;So;0;ON;;;;;N;;;;; +1F567;CLOCK FACE TWELVE-THIRTY;So;0;ON;;;;;N;;;;; +1F568;RIGHT SPEAKER;So;0;ON;;;;;N;;;;; +1F569;RIGHT SPEAKER WITH ONE SOUND WAVE;So;0;ON;;;;;N;;;;; +1F56A;RIGHT SPEAKER WITH THREE SOUND WAVES;So;0;ON;;;;;N;;;;; +1F56B;BULLHORN;So;0;ON;;;;;N;;;;; +1F56C;BULLHORN WITH SOUND WAVES;So;0;ON;;;;;N;;;;; +1F56D;RINGING BELL;So;0;ON;;;;;N;;;;; +1F56E;BOOK;So;0;ON;;;;;N;;;;; +1F56F;CANDLE;So;0;ON;;;;;N;;;;; +1F570;MANTELPIECE CLOCK;So;0;ON;;;;;N;;;;; +1F571;BLACK SKULL AND CROSSBONES;So;0;ON;;;;;N;;;;; +1F572;NO PIRACY;So;0;ON;;;;;N;;;;; +1F573;HOLE;So;0;ON;;;;;N;;;;; +1F574;MAN IN BUSINESS SUIT LEVITATING;So;0;ON;;;;;N;;;;; +1F575;SLEUTH OR SPY;So;0;ON;;;;;N;;;;; +1F576;DARK SUNGLASSES;So;0;ON;;;;;N;;;;; +1F577;SPIDER;So;0;ON;;;;;N;;;;; +1F578;SPIDER WEB;So;0;ON;;;;;N;;;;; +1F579;JOYSTICK;So;0;ON;;;;;N;;;;; +1F57A;MAN DANCING;So;0;ON;;;;;N;;;;; +1F57B;LEFT HAND TELEPHONE RECEIVER;So;0;ON;;;;;N;;;;; +1F57C;TELEPHONE RECEIVER WITH PAGE;So;0;ON;;;;;N;;;;; +1F57D;RIGHT HAND TELEPHONE RECEIVER;So;0;ON;;;;;N;;;;; +1F57E;WHITE TOUCHTONE TELEPHONE;So;0;ON;;;;;N;;;;; +1F57F;BLACK TOUCHTONE TELEPHONE;So;0;ON;;;;;N;;;;; +1F580;TELEPHONE ON TOP OF MODEM;So;0;ON;;;;;N;;;;; +1F581;CLAMSHELL MOBILE PHONE;So;0;ON;;;;;N;;;;; +1F582;BACK OF ENVELOPE;So;0;ON;;;;;N;;;;; +1F583;STAMPED ENVELOPE;So;0;ON;;;;;N;;;;; +1F584;ENVELOPE WITH LIGHTNING;So;0;ON;;;;;N;;;;; +1F585;FLYING ENVELOPE;So;0;ON;;;;;N;;;;; +1F586;PEN OVER STAMPED ENVELOPE;So;0;ON;;;;;N;;;;; +1F587;LINKED PAPERCLIPS;So;0;ON;;;;;N;;;;; +1F588;BLACK PUSHPIN;So;0;ON;;;;;N;;;;; +1F589;LOWER LEFT PENCIL;So;0;ON;;;;;N;;;;; +1F58A;LOWER LEFT BALLPOINT PEN;So;0;ON;;;;;N;;;;; +1F58B;LOWER LEFT FOUNTAIN PEN;So;0;ON;;;;;N;;;;; +1F58C;LOWER LEFT PAINTBRUSH;So;0;ON;;;;;N;;;;; +1F58D;LOWER LEFT CRAYON;So;0;ON;;;;;N;;;;; +1F58E;LEFT WRITING HAND;So;0;ON;;;;;N;;;;; +1F58F;TURNED OK HAND SIGN;So;0;ON;;;;;N;;;;; +1F590;RAISED HAND WITH FINGERS SPLAYED;So;0;ON;;;;;N;;;;; +1F591;REVERSED RAISED HAND WITH FINGERS SPLAYED;So;0;ON;;;;;N;;;;; +1F592;REVERSED THUMBS UP SIGN;So;0;ON;;;;;N;;;;; +1F593;REVERSED THUMBS DOWN SIGN;So;0;ON;;;;;N;;;;; +1F594;REVERSED VICTORY HAND;So;0;ON;;;;;N;;;;; +1F595;REVERSED HAND WITH MIDDLE FINGER EXTENDED;So;0;ON;;;;;N;;;;; +1F596;RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS;So;0;ON;;;;;N;;;;; +1F597;WHITE DOWN POINTING LEFT HAND INDEX;So;0;ON;;;;;N;;;;; +1F598;SIDEWAYS WHITE LEFT POINTING INDEX;So;0;ON;;;;;N;;;;; +1F599;SIDEWAYS WHITE RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;; +1F59A;SIDEWAYS BLACK LEFT POINTING INDEX;So;0;ON;;;;;N;;;;; +1F59B;SIDEWAYS BLACK RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;; +1F59C;BLACK LEFT POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; +1F59D;BLACK RIGHT POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; +1F59E;SIDEWAYS WHITE UP POINTING INDEX;So;0;ON;;;;;N;;;;; +1F59F;SIDEWAYS WHITE DOWN POINTING INDEX;So;0;ON;;;;;N;;;;; +1F5A0;SIDEWAYS BLACK UP POINTING INDEX;So;0;ON;;;;;N;;;;; +1F5A1;SIDEWAYS BLACK DOWN POINTING INDEX;So;0;ON;;;;;N;;;;; +1F5A2;BLACK UP POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; +1F5A3;BLACK DOWN POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;; +1F5A4;BLACK HEART;So;0;ON;;;;;N;;;;; +1F5A5;DESKTOP COMPUTER;So;0;ON;;;;;N;;;;; +1F5A6;KEYBOARD AND MOUSE;So;0;ON;;;;;N;;;;; +1F5A7;THREE NETWORKED COMPUTERS;So;0;ON;;;;;N;;;;; +1F5A8;PRINTER;So;0;ON;;;;;N;;;;; +1F5A9;POCKET CALCULATOR;So;0;ON;;;;;N;;;;; +1F5AA;BLACK HARD SHELL FLOPPY DISK;So;0;ON;;;;;N;;;;; +1F5AB;WHITE HARD SHELL FLOPPY DISK;So;0;ON;;;;;N;;;;; +1F5AC;SOFT SHELL FLOPPY DISK;So;0;ON;;;;;N;;;;; +1F5AD;TAPE CARTRIDGE;So;0;ON;;;;;N;;;;; +1F5AE;WIRED KEYBOARD;So;0;ON;;;;;N;;;;; +1F5AF;ONE BUTTON MOUSE;So;0;ON;;;;;N;;;;; +1F5B0;TWO BUTTON MOUSE;So;0;ON;;;;;N;;;;; +1F5B1;THREE BUTTON MOUSE;So;0;ON;;;;;N;;;;; +1F5B2;TRACKBALL;So;0;ON;;;;;N;;;;; +1F5B3;OLD PERSONAL COMPUTER;So;0;ON;;;;;N;;;;; +1F5B4;HARD DISK;So;0;ON;;;;;N;;;;; +1F5B5;SCREEN;So;0;ON;;;;;N;;;;; +1F5B6;PRINTER ICON;So;0;ON;;;;;N;;;;; +1F5B7;FAX ICON;So;0;ON;;;;;N;;;;; +1F5B8;OPTICAL DISC ICON;So;0;ON;;;;;N;;;;; +1F5B9;DOCUMENT WITH TEXT;So;0;ON;;;;;N;;;;; +1F5BA;DOCUMENT WITH TEXT AND PICTURE;So;0;ON;;;;;N;;;;; +1F5BB;DOCUMENT WITH PICTURE;So;0;ON;;;;;N;;;;; +1F5BC;FRAME WITH PICTURE;So;0;ON;;;;;N;;;;; +1F5BD;FRAME WITH TILES;So;0;ON;;;;;N;;;;; +1F5BE;FRAME WITH AN X;So;0;ON;;;;;N;;;;; +1F5BF;BLACK FOLDER;So;0;ON;;;;;N;;;;; +1F5C0;FOLDER;So;0;ON;;;;;N;;;;; +1F5C1;OPEN FOLDER;So;0;ON;;;;;N;;;;; +1F5C2;CARD INDEX DIVIDERS;So;0;ON;;;;;N;;;;; +1F5C3;CARD FILE BOX;So;0;ON;;;;;N;;;;; +1F5C4;FILE CABINET;So;0;ON;;;;;N;;;;; +1F5C5;EMPTY NOTE;So;0;ON;;;;;N;;;;; +1F5C6;EMPTY NOTE PAGE;So;0;ON;;;;;N;;;;; +1F5C7;EMPTY NOTE PAD;So;0;ON;;;;;N;;;;; +1F5C8;NOTE;So;0;ON;;;;;N;;;;; +1F5C9;NOTE PAGE;So;0;ON;;;;;N;;;;; +1F5CA;NOTE PAD;So;0;ON;;;;;N;;;;; +1F5CB;EMPTY DOCUMENT;So;0;ON;;;;;N;;;;; +1F5CC;EMPTY PAGE;So;0;ON;;;;;N;;;;; +1F5CD;EMPTY PAGES;So;0;ON;;;;;N;;;;; +1F5CE;DOCUMENT;So;0;ON;;;;;N;;;;; +1F5CF;PAGE;So;0;ON;;;;;N;;;;; +1F5D0;PAGES;So;0;ON;;;;;N;;;;; +1F5D1;WASTEBASKET;So;0;ON;;;;;N;;;;; +1F5D2;SPIRAL NOTE PAD;So;0;ON;;;;;N;;;;; +1F5D3;SPIRAL CALENDAR PAD;So;0;ON;;;;;N;;;;; +1F5D4;DESKTOP WINDOW;So;0;ON;;;;;N;;;;; +1F5D5;MINIMIZE;So;0;ON;;;;;N;;;;; +1F5D6;MAXIMIZE;So;0;ON;;;;;N;;;;; +1F5D7;OVERLAP;So;0;ON;;;;;N;;;;; +1F5D8;CLOCKWISE RIGHT AND LEFT SEMICIRCLE ARROWS;So;0;ON;;;;;N;;;;; +1F5D9;CANCELLATION X;So;0;ON;;;;;N;;;;; +1F5DA;INCREASE FONT SIZE SYMBOL;So;0;ON;;;;;N;;;;; +1F5DB;DECREASE FONT SIZE SYMBOL;So;0;ON;;;;;N;;;;; +1F5DC;COMPRESSION;So;0;ON;;;;;N;;;;; +1F5DD;OLD KEY;So;0;ON;;;;;N;;;;; +1F5DE;ROLLED-UP NEWSPAPER;So;0;ON;;;;;N;;;;; +1F5DF;PAGE WITH CIRCLED TEXT;So;0;ON;;;;;N;;;;; +1F5E0;STOCK CHART;So;0;ON;;;;;N;;;;; +1F5E1;DAGGER KNIFE;So;0;ON;;;;;N;;;;; +1F5E2;LIPS;So;0;ON;;;;;N;;;;; +1F5E3;SPEAKING HEAD IN SILHOUETTE;So;0;ON;;;;;N;;;;; +1F5E4;THREE RAYS ABOVE;So;0;ON;;;;;N;;;;; +1F5E5;THREE RAYS BELOW;So;0;ON;;;;;N;;;;; +1F5E6;THREE RAYS LEFT;So;0;ON;;;;;N;;;;; +1F5E7;THREE RAYS RIGHT;So;0;ON;;;;;N;;;;; +1F5E8;LEFT SPEECH BUBBLE;So;0;ON;;;;;N;;;;; +1F5E9;RIGHT SPEECH BUBBLE;So;0;ON;;;;;N;;;;; +1F5EA;TWO SPEECH BUBBLES;So;0;ON;;;;;N;;;;; +1F5EB;THREE SPEECH BUBBLES;So;0;ON;;;;;N;;;;; +1F5EC;LEFT THOUGHT BUBBLE;So;0;ON;;;;;N;;;;; +1F5ED;RIGHT THOUGHT BUBBLE;So;0;ON;;;;;N;;;;; +1F5EE;LEFT ANGER BUBBLE;So;0;ON;;;;;N;;;;; +1F5EF;RIGHT ANGER BUBBLE;So;0;ON;;;;;N;;;;; +1F5F0;MOOD BUBBLE;So;0;ON;;;;;N;;;;; +1F5F1;LIGHTNING MOOD BUBBLE;So;0;ON;;;;;N;;;;; +1F5F2;LIGHTNING MOOD;So;0;ON;;;;;N;;;;; +1F5F3;BALLOT BOX WITH BALLOT;So;0;ON;;;;;N;;;;; +1F5F4;BALLOT SCRIPT X;So;0;ON;;;;;N;;;;; +1F5F5;BALLOT BOX WITH SCRIPT X;So;0;ON;;;;;N;;;;; +1F5F6;BALLOT BOLD SCRIPT X;So;0;ON;;;;;N;;;;; +1F5F7;BALLOT BOX WITH BOLD SCRIPT X;So;0;ON;;;;;N;;;;; +1F5F8;LIGHT CHECK MARK;So;0;ON;;;;;N;;;;; +1F5F9;BALLOT BOX WITH BOLD CHECK;So;0;ON;;;;;N;;;;; +1F5FA;WORLD MAP;So;0;ON;;;;;N;;;;; +1F5FB;MOUNT FUJI;So;0;ON;;;;;N;;;;; +1F5FC;TOKYO TOWER;So;0;ON;;;;;N;;;;; +1F5FD;STATUE OF LIBERTY;So;0;ON;;;;;N;;;;; +1F5FE;SILHOUETTE OF JAPAN;So;0;ON;;;;;N;;;;; +1F5FF;MOYAI;So;0;ON;;;;;N;;;;; +1F600;GRINNING FACE;So;0;ON;;;;;N;;;;; +1F601;GRINNING FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;; +1F602;FACE WITH TEARS OF JOY;So;0;ON;;;;;N;;;;; +1F603;SMILING FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;; +1F604;SMILING FACE WITH OPEN MOUTH AND SMILING EYES;So;0;ON;;;;;N;;;;; +1F605;SMILING FACE WITH OPEN MOUTH AND COLD SWEAT;So;0;ON;;;;;N;;;;; +1F606;SMILING FACE WITH OPEN MOUTH AND TIGHTLY-CLOSED EYES;So;0;ON;;;;;N;;;;; +1F607;SMILING FACE WITH HALO;So;0;ON;;;;;N;;;;; +1F608;SMILING FACE WITH HORNS;So;0;ON;;;;;N;;;;; +1F609;WINKING FACE;So;0;ON;;;;;N;;;;; +1F60A;SMILING FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;; +1F60B;FACE SAVOURING DELICIOUS FOOD;So;0;ON;;;;;N;;;;; +1F60C;RELIEVED FACE;So;0;ON;;;;;N;;;;; +1F60D;SMILING FACE WITH HEART-SHAPED EYES;So;0;ON;;;;;N;;;;; +1F60E;SMILING FACE WITH SUNGLASSES;So;0;ON;;;;;N;;;;; +1F60F;SMIRKING FACE;So;0;ON;;;;;N;;;;; +1F610;NEUTRAL FACE;So;0;ON;;;;;N;;;;; +1F611;EXPRESSIONLESS FACE;So;0;ON;;;;;N;;;;; +1F612;UNAMUSED FACE;So;0;ON;;;;;N;;;;; +1F613;FACE WITH COLD SWEAT;So;0;ON;;;;;N;;;;; +1F614;PENSIVE FACE;So;0;ON;;;;;N;;;;; +1F615;CONFUSED FACE;So;0;ON;;;;;N;;;;; +1F616;CONFOUNDED FACE;So;0;ON;;;;;N;;;;; +1F617;KISSING FACE;So;0;ON;;;;;N;;;;; +1F618;FACE THROWING A KISS;So;0;ON;;;;;N;;;;; +1F619;KISSING FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;; +1F61A;KISSING FACE WITH CLOSED EYES;So;0;ON;;;;;N;;;;; +1F61B;FACE WITH STUCK-OUT TONGUE;So;0;ON;;;;;N;;;;; +1F61C;FACE WITH STUCK-OUT TONGUE AND WINKING EYE;So;0;ON;;;;;N;;;;; +1F61D;FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES;So;0;ON;;;;;N;;;;; +1F61E;DISAPPOINTED FACE;So;0;ON;;;;;N;;;;; +1F61F;WORRIED FACE;So;0;ON;;;;;N;;;;; +1F620;ANGRY FACE;So;0;ON;;;;;N;;;;; +1F621;POUTING FACE;So;0;ON;;;;;N;;;;; +1F622;CRYING FACE;So;0;ON;;;;;N;;;;; +1F623;PERSEVERING FACE;So;0;ON;;;;;N;;;;; +1F624;FACE WITH LOOK OF TRIUMPH;So;0;ON;;;;;N;;;;; +1F625;DISAPPOINTED BUT RELIEVED FACE;So;0;ON;;;;;N;;;;; +1F626;FROWNING FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;; +1F627;ANGUISHED FACE;So;0;ON;;;;;N;;;;; +1F628;FEARFUL FACE;So;0;ON;;;;;N;;;;; +1F629;WEARY FACE;So;0;ON;;;;;N;;;;; +1F62A;SLEEPY FACE;So;0;ON;;;;;N;;;;; +1F62B;TIRED FACE;So;0;ON;;;;;N;;;;; +1F62C;GRIMACING FACE;So;0;ON;;;;;N;;;;; +1F62D;LOUDLY CRYING FACE;So;0;ON;;;;;N;;;;; +1F62E;FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;; +1F62F;HUSHED FACE;So;0;ON;;;;;N;;;;; +1F630;FACE WITH OPEN MOUTH AND COLD SWEAT;So;0;ON;;;;;N;;;;; +1F631;FACE SCREAMING IN FEAR;So;0;ON;;;;;N;;;;; +1F632;ASTONISHED FACE;So;0;ON;;;;;N;;;;; +1F633;FLUSHED FACE;So;0;ON;;;;;N;;;;; +1F634;SLEEPING FACE;So;0;ON;;;;;N;;;;; +1F635;DIZZY FACE;So;0;ON;;;;;N;;;;; +1F636;FACE WITHOUT MOUTH;So;0;ON;;;;;N;;;;; +1F637;FACE WITH MEDICAL MASK;So;0;ON;;;;;N;;;;; +1F638;GRINNING CAT FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;; +1F639;CAT FACE WITH TEARS OF JOY;So;0;ON;;;;;N;;;;; +1F63A;SMILING CAT FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;; +1F63B;SMILING CAT FACE WITH HEART-SHAPED EYES;So;0;ON;;;;;N;;;;; +1F63C;CAT FACE WITH WRY SMILE;So;0;ON;;;;;N;;;;; +1F63D;KISSING CAT FACE WITH CLOSED EYES;So;0;ON;;;;;N;;;;; +1F63E;POUTING CAT FACE;So;0;ON;;;;;N;;;;; +1F63F;CRYING CAT FACE;So;0;ON;;;;;N;;;;; +1F640;WEARY CAT FACE;So;0;ON;;;;;N;;;;; +1F641;SLIGHTLY FROWNING FACE;So;0;ON;;;;;N;;;;; +1F642;SLIGHTLY SMILING FACE;So;0;ON;;;;;N;;;;; +1F643;UPSIDE-DOWN FACE;So;0;ON;;;;;N;;;;; +1F644;FACE WITH ROLLING EYES;So;0;ON;;;;;N;;;;; +1F645;FACE WITH NO GOOD GESTURE;So;0;ON;;;;;N;;;;; +1F646;FACE WITH OK GESTURE;So;0;ON;;;;;N;;;;; +1F647;PERSON BOWING DEEPLY;So;0;ON;;;;;N;;;;; +1F648;SEE-NO-EVIL MONKEY;So;0;ON;;;;;N;;;;; +1F649;HEAR-NO-EVIL MONKEY;So;0;ON;;;;;N;;;;; +1F64A;SPEAK-NO-EVIL MONKEY;So;0;ON;;;;;N;;;;; +1F64B;HAPPY PERSON RAISING ONE HAND;So;0;ON;;;;;N;;;;; +1F64C;PERSON RAISING BOTH HANDS IN CELEBRATION;So;0;ON;;;;;N;;;;; +1F64D;PERSON FROWNING;So;0;ON;;;;;N;;;;; +1F64E;PERSON WITH POUTING FACE;So;0;ON;;;;;N;;;;; +1F64F;PERSON WITH FOLDED HANDS;So;0;ON;;;;;N;;;;; +1F650;NORTH WEST POINTING LEAF;So;0;ON;;;;;N;;;;; +1F651;SOUTH WEST POINTING LEAF;So;0;ON;;;;;N;;;;; +1F652;NORTH EAST POINTING LEAF;So;0;ON;;;;;N;;;;; +1F653;SOUTH EAST POINTING LEAF;So;0;ON;;;;;N;;;;; +1F654;TURNED NORTH WEST POINTING LEAF;So;0;ON;;;;;N;;;;; +1F655;TURNED SOUTH WEST POINTING LEAF;So;0;ON;;;;;N;;;;; +1F656;TURNED NORTH EAST POINTING LEAF;So;0;ON;;;;;N;;;;; +1F657;TURNED SOUTH EAST POINTING LEAF;So;0;ON;;;;;N;;;;; +1F658;NORTH WEST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; +1F659;SOUTH WEST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; +1F65A;NORTH EAST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; +1F65B;SOUTH EAST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; +1F65C;HEAVY NORTH WEST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; +1F65D;HEAVY SOUTH WEST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; +1F65E;HEAVY NORTH EAST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; +1F65F;HEAVY SOUTH EAST POINTING VINE LEAF;So;0;ON;;;;;N;;;;; +1F660;NORTH WEST POINTING BUD;So;0;ON;;;;;N;;;;; +1F661;SOUTH WEST POINTING BUD;So;0;ON;;;;;N;;;;; +1F662;NORTH EAST POINTING BUD;So;0;ON;;;;;N;;;;; +1F663;SOUTH EAST POINTING BUD;So;0;ON;;;;;N;;;;; +1F664;HEAVY NORTH WEST POINTING BUD;So;0;ON;;;;;N;;;;; +1F665;HEAVY SOUTH WEST POINTING BUD;So;0;ON;;;;;N;;;;; +1F666;HEAVY NORTH EAST POINTING BUD;So;0;ON;;;;;N;;;;; +1F667;HEAVY SOUTH EAST POINTING BUD;So;0;ON;;;;;N;;;;; +1F668;HOLLOW QUILT SQUARE ORNAMENT;So;0;ON;;;;;N;;;;; +1F669;HOLLOW QUILT SQUARE ORNAMENT IN BLACK SQUARE;So;0;ON;;;;;N;;;;; +1F66A;SOLID QUILT SQUARE ORNAMENT;So;0;ON;;;;;N;;;;; +1F66B;SOLID QUILT SQUARE ORNAMENT IN BLACK SQUARE;So;0;ON;;;;;N;;;;; +1F66C;LEFTWARDS ROCKET;So;0;ON;;;;;N;;;;; +1F66D;UPWARDS ROCKET;So;0;ON;;;;;N;;;;; +1F66E;RIGHTWARDS ROCKET;So;0;ON;;;;;N;;;;; +1F66F;DOWNWARDS ROCKET;So;0;ON;;;;;N;;;;; +1F670;SCRIPT LIGATURE ET ORNAMENT;So;0;ON;;;;;N;;;;; +1F671;HEAVY SCRIPT LIGATURE ET ORNAMENT;So;0;ON;;;;;N;;;;; +1F672;LIGATURE OPEN ET ORNAMENT;So;0;ON;;;;;N;;;;; +1F673;HEAVY LIGATURE OPEN ET ORNAMENT;So;0;ON;;;;;N;;;;; +1F674;HEAVY AMPERSAND ORNAMENT;So;0;ON;;;;;N;;;;; +1F675;SWASH AMPERSAND ORNAMENT;So;0;ON;;;;;N;;;;; +1F676;SANS-SERIF HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +1F677;SANS-SERIF HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +1F678;SANS-SERIF HEAVY LOW DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +1F679;HEAVY INTERROBANG ORNAMENT;So;0;ON;;;;;N;;;;; +1F67A;SANS-SERIF INTERROBANG ORNAMENT;So;0;ON;;;;;N;;;;; +1F67B;HEAVY SANS-SERIF INTERROBANG ORNAMENT;So;0;ON;;;;;N;;;;; +1F67C;VERY HEAVY SOLIDUS;So;0;ON;;;;;N;;;;; +1F67D;VERY HEAVY REVERSE SOLIDUS;So;0;ON;;;;;N;;;;; +1F67E;CHECKER BOARD;So;0;ON;;;;;N;;;;; +1F67F;REVERSE CHECKER BOARD;So;0;ON;;;;;N;;;;; +1F680;ROCKET;So;0;ON;;;;;N;;;;; +1F681;HELICOPTER;So;0;ON;;;;;N;;;;; +1F682;STEAM LOCOMOTIVE;So;0;ON;;;;;N;;;;; +1F683;RAILWAY CAR;So;0;ON;;;;;N;;;;; +1F684;HIGH-SPEED TRAIN;So;0;ON;;;;;N;;;;; +1F685;HIGH-SPEED TRAIN WITH BULLET NOSE;So;0;ON;;;;;N;;;;; +1F686;TRAIN;So;0;ON;;;;;N;;;;; +1F687;METRO;So;0;ON;;;;;N;;;;; +1F688;LIGHT RAIL;So;0;ON;;;;;N;;;;; +1F689;STATION;So;0;ON;;;;;N;;;;; +1F68A;TRAM;So;0;ON;;;;;N;;;;; +1F68B;TRAM CAR;So;0;ON;;;;;N;;;;; +1F68C;BUS;So;0;ON;;;;;N;;;;; +1F68D;ONCOMING BUS;So;0;ON;;;;;N;;;;; +1F68E;TROLLEYBUS;So;0;ON;;;;;N;;;;; +1F68F;BUS STOP;So;0;ON;;;;;N;;;;; +1F690;MINIBUS;So;0;ON;;;;;N;;;;; +1F691;AMBULANCE;So;0;ON;;;;;N;;;;; +1F692;FIRE ENGINE;So;0;ON;;;;;N;;;;; +1F693;POLICE CAR;So;0;ON;;;;;N;;;;; +1F694;ONCOMING POLICE CAR;So;0;ON;;;;;N;;;;; +1F695;TAXI;So;0;ON;;;;;N;;;;; +1F696;ONCOMING TAXI;So;0;ON;;;;;N;;;;; +1F697;AUTOMOBILE;So;0;ON;;;;;N;;;;; +1F698;ONCOMING AUTOMOBILE;So;0;ON;;;;;N;;;;; +1F699;RECREATIONAL VEHICLE;So;0;ON;;;;;N;;;;; +1F69A;DELIVERY TRUCK;So;0;ON;;;;;N;;;;; +1F69B;ARTICULATED LORRY;So;0;ON;;;;;N;;;;; +1F69C;TRACTOR;So;0;ON;;;;;N;;;;; +1F69D;MONORAIL;So;0;ON;;;;;N;;;;; +1F69E;MOUNTAIN RAILWAY;So;0;ON;;;;;N;;;;; +1F69F;SUSPENSION RAILWAY;So;0;ON;;;;;N;;;;; +1F6A0;MOUNTAIN CABLEWAY;So;0;ON;;;;;N;;;;; +1F6A1;AERIAL TRAMWAY;So;0;ON;;;;;N;;;;; +1F6A2;SHIP;So;0;ON;;;;;N;;;;; +1F6A3;ROWBOAT;So;0;ON;;;;;N;;;;; +1F6A4;SPEEDBOAT;So;0;ON;;;;;N;;;;; +1F6A5;HORIZONTAL TRAFFIC LIGHT;So;0;ON;;;;;N;;;;; +1F6A6;VERTICAL TRAFFIC LIGHT;So;0;ON;;;;;N;;;;; +1F6A7;CONSTRUCTION SIGN;So;0;ON;;;;;N;;;;; +1F6A8;POLICE CARS REVOLVING LIGHT;So;0;ON;;;;;N;;;;; +1F6A9;TRIANGULAR FLAG ON POST;So;0;ON;;;;;N;;;;; +1F6AA;DOOR;So;0;ON;;;;;N;;;;; +1F6AB;NO ENTRY SIGN;So;0;ON;;;;;N;;;;; +1F6AC;SMOKING SYMBOL;So;0;ON;;;;;N;;;;; +1F6AD;NO SMOKING SYMBOL;So;0;ON;;;;;N;;;;; +1F6AE;PUT LITTER IN ITS PLACE SYMBOL;So;0;ON;;;;;N;;;;; +1F6AF;DO NOT LITTER SYMBOL;So;0;ON;;;;;N;;;;; +1F6B0;POTABLE WATER SYMBOL;So;0;ON;;;;;N;;;;; +1F6B1;NON-POTABLE WATER SYMBOL;So;0;ON;;;;;N;;;;; +1F6B2;BICYCLE;So;0;ON;;;;;N;;;;; +1F6B3;NO BICYCLES;So;0;ON;;;;;N;;;;; +1F6B4;BICYCLIST;So;0;ON;;;;;N;;;;; +1F6B5;MOUNTAIN BICYCLIST;So;0;ON;;;;;N;;;;; +1F6B6;PEDESTRIAN;So;0;ON;;;;;N;;;;; +1F6B7;NO PEDESTRIANS;So;0;ON;;;;;N;;;;; +1F6B8;CHILDREN CROSSING;So;0;ON;;;;;N;;;;; +1F6B9;MENS SYMBOL;So;0;ON;;;;;N;;;;; +1F6BA;WOMENS SYMBOL;So;0;ON;;;;;N;;;;; +1F6BB;RESTROOM;So;0;ON;;;;;N;;;;; +1F6BC;BABY SYMBOL;So;0;ON;;;;;N;;;;; +1F6BD;TOILET;So;0;ON;;;;;N;;;;; +1F6BE;WATER CLOSET;So;0;ON;;;;;N;;;;; +1F6BF;SHOWER;So;0;ON;;;;;N;;;;; +1F6C0;BATH;So;0;ON;;;;;N;;;;; +1F6C1;BATHTUB;So;0;ON;;;;;N;;;;; +1F6C2;PASSPORT CONTROL;So;0;ON;;;;;N;;;;; +1F6C3;CUSTOMS;So;0;ON;;;;;N;;;;; +1F6C4;BAGGAGE CLAIM;So;0;ON;;;;;N;;;;; +1F6C5;LEFT LUGGAGE;So;0;ON;;;;;N;;;;; +1F6C6;TRIANGLE WITH ROUNDED CORNERS;So;0;ON;;;;;N;;;;; +1F6C7;PROHIBITED SIGN;So;0;ON;;;;;N;;;;; +1F6C8;CIRCLED INFORMATION SOURCE;So;0;ON;;;;;N;;;;; +1F6C9;BOYS SYMBOL;So;0;ON;;;;;N;;;;; +1F6CA;GIRLS SYMBOL;So;0;ON;;;;;N;;;;; +1F6CB;COUCH AND LAMP;So;0;ON;;;;;N;;;;; +1F6CC;SLEEPING ACCOMMODATION;So;0;ON;;;;;N;;;;; +1F6CD;SHOPPING BAGS;So;0;ON;;;;;N;;;;; +1F6CE;BELLHOP BELL;So;0;ON;;;;;N;;;;; +1F6CF;BED;So;0;ON;;;;;N;;;;; +1F6D0;PLACE OF WORSHIP;So;0;ON;;;;;N;;;;; +1F6D1;OCTAGONAL SIGN;So;0;ON;;;;;N;;;;; +1F6D2;SHOPPING TROLLEY;So;0;ON;;;;;N;;;;; +1F6E0;HAMMER AND WRENCH;So;0;ON;;;;;N;;;;; +1F6E1;SHIELD;So;0;ON;;;;;N;;;;; +1F6E2;OIL DRUM;So;0;ON;;;;;N;;;;; +1F6E3;MOTORWAY;So;0;ON;;;;;N;;;;; +1F6E4;RAILWAY TRACK;So;0;ON;;;;;N;;;;; +1F6E5;MOTOR BOAT;So;0;ON;;;;;N;;;;; +1F6E6;UP-POINTING MILITARY AIRPLANE;So;0;ON;;;;;N;;;;; +1F6E7;UP-POINTING AIRPLANE;So;0;ON;;;;;N;;;;; +1F6E8;UP-POINTING SMALL AIRPLANE;So;0;ON;;;;;N;;;;; +1F6E9;SMALL AIRPLANE;So;0;ON;;;;;N;;;;; +1F6EA;NORTHEAST-POINTING AIRPLANE;So;0;ON;;;;;N;;;;; +1F6EB;AIRPLANE DEPARTURE;So;0;ON;;;;;N;;;;; +1F6EC;AIRPLANE ARRIVING;So;0;ON;;;;;N;;;;; +1F6F0;SATELLITE;So;0;ON;;;;;N;;;;; +1F6F1;ONCOMING FIRE ENGINE;So;0;ON;;;;;N;;;;; +1F6F2;DIESEL LOCOMOTIVE;So;0;ON;;;;;N;;;;; +1F6F3;PASSENGER SHIP;So;0;ON;;;;;N;;;;; +1F6F4;SCOOTER;So;0;ON;;;;;N;;;;; +1F6F5;MOTOR SCOOTER;So;0;ON;;;;;N;;;;; +1F6F6;CANOE;So;0;ON;;;;;N;;;;; +1F700;ALCHEMICAL SYMBOL FOR QUINTESSENCE;So;0;ON;;;;;N;;;;; +1F701;ALCHEMICAL SYMBOL FOR AIR;So;0;ON;;;;;N;;;;; +1F702;ALCHEMICAL SYMBOL FOR FIRE;So;0;ON;;;;;N;;;;; +1F703;ALCHEMICAL SYMBOL FOR EARTH;So;0;ON;;;;;N;;;;; +1F704;ALCHEMICAL SYMBOL FOR WATER;So;0;ON;;;;;N;;;;; +1F705;ALCHEMICAL SYMBOL FOR AQUAFORTIS;So;0;ON;;;;;N;;;;; +1F706;ALCHEMICAL SYMBOL FOR AQUA REGIA;So;0;ON;;;;;N;;;;; +1F707;ALCHEMICAL SYMBOL FOR AQUA REGIA-2;So;0;ON;;;;;N;;;;; +1F708;ALCHEMICAL SYMBOL FOR AQUA VITAE;So;0;ON;;;;;N;;;;; +1F709;ALCHEMICAL SYMBOL FOR AQUA VITAE-2;So;0;ON;;;;;N;;;;; +1F70A;ALCHEMICAL SYMBOL FOR VINEGAR;So;0;ON;;;;;N;;;;; +1F70B;ALCHEMICAL SYMBOL FOR VINEGAR-2;So;0;ON;;;;;N;;;;; +1F70C;ALCHEMICAL SYMBOL FOR VINEGAR-3;So;0;ON;;;;;N;;;;; +1F70D;ALCHEMICAL SYMBOL FOR SULFUR;So;0;ON;;;;;N;;;;; +1F70E;ALCHEMICAL SYMBOL FOR PHILOSOPHERS SULFUR;So;0;ON;;;;;N;;;;; +1F70F;ALCHEMICAL SYMBOL FOR BLACK SULFUR;So;0;ON;;;;;N;;;;; +1F710;ALCHEMICAL SYMBOL FOR MERCURY SUBLIMATE;So;0;ON;;;;;N;;;;; +1F711;ALCHEMICAL SYMBOL FOR MERCURY SUBLIMATE-2;So;0;ON;;;;;N;;;;; +1F712;ALCHEMICAL SYMBOL FOR MERCURY SUBLIMATE-3;So;0;ON;;;;;N;;;;; +1F713;ALCHEMICAL SYMBOL FOR CINNABAR;So;0;ON;;;;;N;;;;; +1F714;ALCHEMICAL SYMBOL FOR SALT;So;0;ON;;;;;N;;;;; +1F715;ALCHEMICAL SYMBOL FOR NITRE;So;0;ON;;;;;N;;;;; +1F716;ALCHEMICAL SYMBOL FOR VITRIOL;So;0;ON;;;;;N;;;;; +1F717;ALCHEMICAL SYMBOL FOR VITRIOL-2;So;0;ON;;;;;N;;;;; +1F718;ALCHEMICAL SYMBOL FOR ROCK SALT;So;0;ON;;;;;N;;;;; +1F719;ALCHEMICAL SYMBOL FOR ROCK SALT-2;So;0;ON;;;;;N;;;;; +1F71A;ALCHEMICAL SYMBOL FOR GOLD;So;0;ON;;;;;N;;;;; +1F71B;ALCHEMICAL SYMBOL FOR SILVER;So;0;ON;;;;;N;;;;; +1F71C;ALCHEMICAL SYMBOL FOR IRON ORE;So;0;ON;;;;;N;;;;; +1F71D;ALCHEMICAL SYMBOL FOR IRON ORE-2;So;0;ON;;;;;N;;;;; +1F71E;ALCHEMICAL SYMBOL FOR CROCUS OF IRON;So;0;ON;;;;;N;;;;; +1F71F;ALCHEMICAL SYMBOL FOR REGULUS OF IRON;So;0;ON;;;;;N;;;;; +1F720;ALCHEMICAL SYMBOL FOR COPPER ORE;So;0;ON;;;;;N;;;;; +1F721;ALCHEMICAL SYMBOL FOR IRON-COPPER ORE;So;0;ON;;;;;N;;;;; +1F722;ALCHEMICAL SYMBOL FOR SUBLIMATE OF COPPER;So;0;ON;;;;;N;;;;; +1F723;ALCHEMICAL SYMBOL FOR CROCUS OF COPPER;So;0;ON;;;;;N;;;;; +1F724;ALCHEMICAL SYMBOL FOR CROCUS OF COPPER-2;So;0;ON;;;;;N;;;;; +1F725;ALCHEMICAL SYMBOL FOR COPPER ANTIMONIATE;So;0;ON;;;;;N;;;;; +1F726;ALCHEMICAL SYMBOL FOR SALT OF COPPER ANTIMONIATE;So;0;ON;;;;;N;;;;; +1F727;ALCHEMICAL SYMBOL FOR SUBLIMATE OF SALT OF COPPER;So;0;ON;;;;;N;;;;; +1F728;ALCHEMICAL SYMBOL FOR VERDIGRIS;So;0;ON;;;;;N;;;;; +1F729;ALCHEMICAL SYMBOL FOR TIN ORE;So;0;ON;;;;;N;;;;; +1F72A;ALCHEMICAL SYMBOL FOR LEAD ORE;So;0;ON;;;;;N;;;;; +1F72B;ALCHEMICAL SYMBOL FOR ANTIMONY ORE;So;0;ON;;;;;N;;;;; +1F72C;ALCHEMICAL SYMBOL FOR SUBLIMATE OF ANTIMONY;So;0;ON;;;;;N;;;;; +1F72D;ALCHEMICAL SYMBOL FOR SALT OF ANTIMONY;So;0;ON;;;;;N;;;;; +1F72E;ALCHEMICAL SYMBOL FOR SUBLIMATE OF SALT OF ANTIMONY;So;0;ON;;;;;N;;;;; +1F72F;ALCHEMICAL SYMBOL FOR VINEGAR OF ANTIMONY;So;0;ON;;;;;N;;;;; +1F730;ALCHEMICAL SYMBOL FOR REGULUS OF ANTIMONY;So;0;ON;;;;;N;;;;; +1F731;ALCHEMICAL SYMBOL FOR REGULUS OF ANTIMONY-2;So;0;ON;;;;;N;;;;; +1F732;ALCHEMICAL SYMBOL FOR REGULUS;So;0;ON;;;;;N;;;;; +1F733;ALCHEMICAL SYMBOL FOR REGULUS-2;So;0;ON;;;;;N;;;;; +1F734;ALCHEMICAL SYMBOL FOR REGULUS-3;So;0;ON;;;;;N;;;;; +1F735;ALCHEMICAL SYMBOL FOR REGULUS-4;So;0;ON;;;;;N;;;;; +1F736;ALCHEMICAL SYMBOL FOR ALKALI;So;0;ON;;;;;N;;;;; +1F737;ALCHEMICAL SYMBOL FOR ALKALI-2;So;0;ON;;;;;N;;;;; +1F738;ALCHEMICAL SYMBOL FOR MARCASITE;So;0;ON;;;;;N;;;;; +1F739;ALCHEMICAL SYMBOL FOR SAL-AMMONIAC;So;0;ON;;;;;N;;;;; +1F73A;ALCHEMICAL SYMBOL FOR ARSENIC;So;0;ON;;;;;N;;;;; +1F73B;ALCHEMICAL SYMBOL FOR REALGAR;So;0;ON;;;;;N;;;;; +1F73C;ALCHEMICAL SYMBOL FOR REALGAR-2;So;0;ON;;;;;N;;;;; +1F73D;ALCHEMICAL SYMBOL FOR AURIPIGMENT;So;0;ON;;;;;N;;;;; +1F73E;ALCHEMICAL SYMBOL FOR BISMUTH ORE;So;0;ON;;;;;N;;;;; +1F73F;ALCHEMICAL SYMBOL FOR TARTAR;So;0;ON;;;;;N;;;;; +1F740;ALCHEMICAL SYMBOL FOR TARTAR-2;So;0;ON;;;;;N;;;;; +1F741;ALCHEMICAL SYMBOL FOR QUICK LIME;So;0;ON;;;;;N;;;;; +1F742;ALCHEMICAL SYMBOL FOR BORAX;So;0;ON;;;;;N;;;;; +1F743;ALCHEMICAL SYMBOL FOR BORAX-2;So;0;ON;;;;;N;;;;; +1F744;ALCHEMICAL SYMBOL FOR BORAX-3;So;0;ON;;;;;N;;;;; +1F745;ALCHEMICAL SYMBOL FOR ALUM;So;0;ON;;;;;N;;;;; +1F746;ALCHEMICAL SYMBOL FOR OIL;So;0;ON;;;;;N;;;;; +1F747;ALCHEMICAL SYMBOL FOR SPIRIT;So;0;ON;;;;;N;;;;; +1F748;ALCHEMICAL SYMBOL FOR TINCTURE;So;0;ON;;;;;N;;;;; +1F749;ALCHEMICAL SYMBOL FOR GUM;So;0;ON;;;;;N;;;;; +1F74A;ALCHEMICAL SYMBOL FOR WAX;So;0;ON;;;;;N;;;;; +1F74B;ALCHEMICAL SYMBOL FOR POWDER;So;0;ON;;;;;N;;;;; +1F74C;ALCHEMICAL SYMBOL FOR CALX;So;0;ON;;;;;N;;;;; +1F74D;ALCHEMICAL SYMBOL FOR TUTTY;So;0;ON;;;;;N;;;;; +1F74E;ALCHEMICAL SYMBOL FOR CAPUT MORTUUM;So;0;ON;;;;;N;;;;; +1F74F;ALCHEMICAL SYMBOL FOR SCEPTER OF JOVE;So;0;ON;;;;;N;;;;; +1F750;ALCHEMICAL SYMBOL FOR CADUCEUS;So;0;ON;;;;;N;;;;; +1F751;ALCHEMICAL SYMBOL FOR TRIDENT;So;0;ON;;;;;N;;;;; +1F752;ALCHEMICAL SYMBOL FOR STARRED TRIDENT;So;0;ON;;;;;N;;;;; +1F753;ALCHEMICAL SYMBOL FOR LODESTONE;So;0;ON;;;;;N;;;;; +1F754;ALCHEMICAL SYMBOL FOR SOAP;So;0;ON;;;;;N;;;;; +1F755;ALCHEMICAL SYMBOL FOR URINE;So;0;ON;;;;;N;;;;; +1F756;ALCHEMICAL SYMBOL FOR HORSE DUNG;So;0;ON;;;;;N;;;;; +1F757;ALCHEMICAL SYMBOL FOR ASHES;So;0;ON;;;;;N;;;;; +1F758;ALCHEMICAL SYMBOL FOR POT ASHES;So;0;ON;;;;;N;;;;; +1F759;ALCHEMICAL SYMBOL FOR BRICK;So;0;ON;;;;;N;;;;; +1F75A;ALCHEMICAL SYMBOL FOR POWDERED BRICK;So;0;ON;;;;;N;;;;; +1F75B;ALCHEMICAL SYMBOL FOR AMALGAM;So;0;ON;;;;;N;;;;; +1F75C;ALCHEMICAL SYMBOL FOR STRATUM SUPER STRATUM;So;0;ON;;;;;N;;;;; +1F75D;ALCHEMICAL SYMBOL FOR STRATUM SUPER STRATUM-2;So;0;ON;;;;;N;;;;; +1F75E;ALCHEMICAL SYMBOL FOR SUBLIMATION;So;0;ON;;;;;N;;;;; +1F75F;ALCHEMICAL SYMBOL FOR PRECIPITATE;So;0;ON;;;;;N;;;;; +1F760;ALCHEMICAL SYMBOL FOR DISTILL;So;0;ON;;;;;N;;;;; +1F761;ALCHEMICAL SYMBOL FOR DISSOLVE;So;0;ON;;;;;N;;;;; +1F762;ALCHEMICAL SYMBOL FOR DISSOLVE-2;So;0;ON;;;;;N;;;;; +1F763;ALCHEMICAL SYMBOL FOR PURIFY;So;0;ON;;;;;N;;;;; +1F764;ALCHEMICAL SYMBOL FOR PUTREFACTION;So;0;ON;;;;;N;;;;; +1F765;ALCHEMICAL SYMBOL FOR CRUCIBLE;So;0;ON;;;;;N;;;;; +1F766;ALCHEMICAL SYMBOL FOR CRUCIBLE-2;So;0;ON;;;;;N;;;;; +1F767;ALCHEMICAL SYMBOL FOR CRUCIBLE-3;So;0;ON;;;;;N;;;;; +1F768;ALCHEMICAL SYMBOL FOR CRUCIBLE-4;So;0;ON;;;;;N;;;;; +1F769;ALCHEMICAL SYMBOL FOR CRUCIBLE-5;So;0;ON;;;;;N;;;;; +1F76A;ALCHEMICAL SYMBOL FOR ALEMBIC;So;0;ON;;;;;N;;;;; +1F76B;ALCHEMICAL SYMBOL FOR BATH OF MARY;So;0;ON;;;;;N;;;;; +1F76C;ALCHEMICAL SYMBOL FOR BATH OF VAPOURS;So;0;ON;;;;;N;;;;; +1F76D;ALCHEMICAL SYMBOL FOR RETORT;So;0;ON;;;;;N;;;;; +1F76E;ALCHEMICAL SYMBOL FOR HOUR;So;0;ON;;;;;N;;;;; +1F76F;ALCHEMICAL SYMBOL FOR NIGHT;So;0;ON;;;;;N;;;;; +1F770;ALCHEMICAL SYMBOL FOR DAY-NIGHT;So;0;ON;;;;;N;;;;; +1F771;ALCHEMICAL SYMBOL FOR MONTH;So;0;ON;;;;;N;;;;; +1F772;ALCHEMICAL SYMBOL FOR HALF DRAM;So;0;ON;;;;;N;;;;; +1F773;ALCHEMICAL SYMBOL FOR HALF OUNCE;So;0;ON;;;;;N;;;;; +1F780;BLACK LEFT-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;; +1F781;BLACK UP-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;; +1F782;BLACK RIGHT-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;; +1F783;BLACK DOWN-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;; +1F784;BLACK SLIGHTLY SMALL CIRCLE;So;0;ON;;;;;N;;;;; +1F785;MEDIUM BOLD WHITE CIRCLE;So;0;ON;;;;;N;;;;; +1F786;BOLD WHITE CIRCLE;So;0;ON;;;;;N;;;;; +1F787;HEAVY WHITE CIRCLE;So;0;ON;;;;;N;;;;; +1F788;VERY HEAVY WHITE CIRCLE;So;0;ON;;;;;N;;;;; +1F789;EXTREMELY HEAVY WHITE CIRCLE;So;0;ON;;;;;N;;;;; +1F78A;WHITE CIRCLE CONTAINING BLACK SMALL CIRCLE;So;0;ON;;;;;N;;;;; +1F78B;ROUND TARGET;So;0;ON;;;;;N;;;;; +1F78C;BLACK TINY SQUARE;So;0;ON;;;;;N;;;;; +1F78D;BLACK SLIGHTLY SMALL SQUARE;So;0;ON;;;;;N;;;;; +1F78E;LIGHT WHITE SQUARE;So;0;ON;;;;;N;;;;; +1F78F;MEDIUM WHITE SQUARE;So;0;ON;;;;;N;;;;; +1F790;BOLD WHITE SQUARE;So;0;ON;;;;;N;;;;; +1F791;HEAVY WHITE SQUARE;So;0;ON;;;;;N;;;;; +1F792;VERY HEAVY WHITE SQUARE;So;0;ON;;;;;N;;;;; +1F793;EXTREMELY HEAVY WHITE SQUARE;So;0;ON;;;;;N;;;;; +1F794;WHITE SQUARE CONTAINING BLACK VERY SMALL SQUARE;So;0;ON;;;;;N;;;;; +1F795;WHITE SQUARE CONTAINING BLACK MEDIUM SQUARE;So;0;ON;;;;;N;;;;; +1F796;SQUARE TARGET;So;0;ON;;;;;N;;;;; +1F797;BLACK TINY DIAMOND;So;0;ON;;;;;N;;;;; +1F798;BLACK VERY SMALL DIAMOND;So;0;ON;;;;;N;;;;; +1F799;BLACK MEDIUM SMALL DIAMOND;So;0;ON;;;;;N;;;;; +1F79A;WHITE DIAMOND CONTAINING BLACK VERY SMALL DIAMOND;So;0;ON;;;;;N;;;;; +1F79B;WHITE DIAMOND CONTAINING BLACK MEDIUM DIAMOND;So;0;ON;;;;;N;;;;; +1F79C;DIAMOND TARGET;So;0;ON;;;;;N;;;;; +1F79D;BLACK TINY LOZENGE;So;0;ON;;;;;N;;;;; +1F79E;BLACK VERY SMALL LOZENGE;So;0;ON;;;;;N;;;;; +1F79F;BLACK MEDIUM SMALL LOZENGE;So;0;ON;;;;;N;;;;; +1F7A0;WHITE LOZENGE CONTAINING BLACK SMALL LOZENGE;So;0;ON;;;;;N;;;;; +1F7A1;THIN GREEK CROSS;So;0;ON;;;;;N;;;;; +1F7A2;LIGHT GREEK CROSS;So;0;ON;;;;;N;;;;; +1F7A3;MEDIUM GREEK CROSS;So;0;ON;;;;;N;;;;; +1F7A4;BOLD GREEK CROSS;So;0;ON;;;;;N;;;;; +1F7A5;VERY BOLD GREEK CROSS;So;0;ON;;;;;N;;;;; +1F7A6;VERY HEAVY GREEK CROSS;So;0;ON;;;;;N;;;;; +1F7A7;EXTREMELY HEAVY GREEK CROSS;So;0;ON;;;;;N;;;;; +1F7A8;THIN SALTIRE;So;0;ON;;;;;N;;;;; +1F7A9;LIGHT SALTIRE;So;0;ON;;;;;N;;;;; +1F7AA;MEDIUM SALTIRE;So;0;ON;;;;;N;;;;; +1F7AB;BOLD SALTIRE;So;0;ON;;;;;N;;;;; +1F7AC;HEAVY SALTIRE;So;0;ON;;;;;N;;;;; +1F7AD;VERY HEAVY SALTIRE;So;0;ON;;;;;N;;;;; +1F7AE;EXTREMELY HEAVY SALTIRE;So;0;ON;;;;;N;;;;; +1F7AF;LIGHT FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7B0;MEDIUM FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7B1;BOLD FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7B2;HEAVY FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7B3;VERY HEAVY FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7B4;EXTREMELY HEAVY FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7B5;LIGHT SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7B6;MEDIUM SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7B7;BOLD SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7B8;HEAVY SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7B9;VERY HEAVY SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7BA;EXTREMELY HEAVY SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7BB;LIGHT EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7BC;MEDIUM EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7BD;BOLD EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7BE;HEAVY EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7BF;VERY HEAVY EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +1F7C0;LIGHT THREE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7C1;MEDIUM THREE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7C2;THREE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7C3;MEDIUM THREE POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; +1F7C4;LIGHT FOUR POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7C5;MEDIUM FOUR POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7C6;FOUR POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7C7;MEDIUM FOUR POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; +1F7C8;REVERSE LIGHT FOUR POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; +1F7C9;LIGHT FIVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7CA;HEAVY FIVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7CB;MEDIUM SIX POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7CC;HEAVY SIX POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7CD;SIX POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; +1F7CE;MEDIUM EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7CF;HEAVY EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7D0;VERY HEAVY EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7D1;HEAVY EIGHT POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; +1F7D2;LIGHT TWELVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7D3;HEAVY TWELVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +1F7D4;HEAVY TWELVE POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; +1F800;LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F801;UPWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F802;RIGHTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F803;DOWNWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F804;LEFTWARDS ARROW WITH MEDIUM TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F805;UPWARDS ARROW WITH MEDIUM TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F806;RIGHTWARDS ARROW WITH MEDIUM TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F807;DOWNWARDS ARROW WITH MEDIUM TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F808;LEFTWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F809;UPWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F80A;RIGHTWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F80B;DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F810;LEFTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F811;UPWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F812;RIGHTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F813;DOWNWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F814;LEFTWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F815;UPWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F816;RIGHTWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F817;DOWNWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F818;HEAVY LEFTWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F819;HEAVY UPWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F81A;HEAVY RIGHTWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F81B;HEAVY DOWNWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F81C;HEAVY LEFTWARDS ARROW WITH LARGE EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F81D;HEAVY UPWARDS ARROW WITH LARGE EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F81E;HEAVY RIGHTWARDS ARROW WITH LARGE EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F81F;HEAVY DOWNWARDS ARROW WITH LARGE EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; +1F820;LEFTWARDS TRIANGLE-HEADED ARROW WITH NARROW SHAFT;So;0;ON;;;;;N;;;;; +1F821;UPWARDS TRIANGLE-HEADED ARROW WITH NARROW SHAFT;So;0;ON;;;;;N;;;;; +1F822;RIGHTWARDS TRIANGLE-HEADED ARROW WITH NARROW SHAFT;So;0;ON;;;;;N;;;;; +1F823;DOWNWARDS TRIANGLE-HEADED ARROW WITH NARROW SHAFT;So;0;ON;;;;;N;;;;; +1F824;LEFTWARDS TRIANGLE-HEADED ARROW WITH MEDIUM SHAFT;So;0;ON;;;;;N;;;;; +1F825;UPWARDS TRIANGLE-HEADED ARROW WITH MEDIUM SHAFT;So;0;ON;;;;;N;;;;; +1F826;RIGHTWARDS TRIANGLE-HEADED ARROW WITH MEDIUM SHAFT;So;0;ON;;;;;N;;;;; +1F827;DOWNWARDS TRIANGLE-HEADED ARROW WITH MEDIUM SHAFT;So;0;ON;;;;;N;;;;; +1F828;LEFTWARDS TRIANGLE-HEADED ARROW WITH BOLD SHAFT;So;0;ON;;;;;N;;;;; +1F829;UPWARDS TRIANGLE-HEADED ARROW WITH BOLD SHAFT;So;0;ON;;;;;N;;;;; +1F82A;RIGHTWARDS TRIANGLE-HEADED ARROW WITH BOLD SHAFT;So;0;ON;;;;;N;;;;; +1F82B;DOWNWARDS TRIANGLE-HEADED ARROW WITH BOLD SHAFT;So;0;ON;;;;;N;;;;; +1F82C;LEFTWARDS TRIANGLE-HEADED ARROW WITH HEAVY SHAFT;So;0;ON;;;;;N;;;;; +1F82D;UPWARDS TRIANGLE-HEADED ARROW WITH HEAVY SHAFT;So;0;ON;;;;;N;;;;; +1F82E;RIGHTWARDS TRIANGLE-HEADED ARROW WITH HEAVY SHAFT;So;0;ON;;;;;N;;;;; +1F82F;DOWNWARDS TRIANGLE-HEADED ARROW WITH HEAVY SHAFT;So;0;ON;;;;;N;;;;; +1F830;LEFTWARDS TRIANGLE-HEADED ARROW WITH VERY HEAVY SHAFT;So;0;ON;;;;;N;;;;; +1F831;UPWARDS TRIANGLE-HEADED ARROW WITH VERY HEAVY SHAFT;So;0;ON;;;;;N;;;;; +1F832;RIGHTWARDS TRIANGLE-HEADED ARROW WITH VERY HEAVY SHAFT;So;0;ON;;;;;N;;;;; +1F833;DOWNWARDS TRIANGLE-HEADED ARROW WITH VERY HEAVY SHAFT;So;0;ON;;;;;N;;;;; +1F834;LEFTWARDS FINGER-POST ARROW;So;0;ON;;;;;N;;;;; +1F835;UPWARDS FINGER-POST ARROW;So;0;ON;;;;;N;;;;; +1F836;RIGHTWARDS FINGER-POST ARROW;So;0;ON;;;;;N;;;;; +1F837;DOWNWARDS FINGER-POST ARROW;So;0;ON;;;;;N;;;;; +1F838;LEFTWARDS SQUARED ARROW;So;0;ON;;;;;N;;;;; +1F839;UPWARDS SQUARED ARROW;So;0;ON;;;;;N;;;;; +1F83A;RIGHTWARDS SQUARED ARROW;So;0;ON;;;;;N;;;;; +1F83B;DOWNWARDS SQUARED ARROW;So;0;ON;;;;;N;;;;; +1F83C;LEFTWARDS COMPRESSED ARROW;So;0;ON;;;;;N;;;;; +1F83D;UPWARDS COMPRESSED ARROW;So;0;ON;;;;;N;;;;; +1F83E;RIGHTWARDS COMPRESSED ARROW;So;0;ON;;;;;N;;;;; +1F83F;DOWNWARDS COMPRESSED ARROW;So;0;ON;;;;;N;;;;; +1F840;LEFTWARDS HEAVY COMPRESSED ARROW;So;0;ON;;;;;N;;;;; +1F841;UPWARDS HEAVY COMPRESSED ARROW;So;0;ON;;;;;N;;;;; +1F842;RIGHTWARDS HEAVY COMPRESSED ARROW;So;0;ON;;;;;N;;;;; +1F843;DOWNWARDS HEAVY COMPRESSED ARROW;So;0;ON;;;;;N;;;;; +1F844;LEFTWARDS HEAVY ARROW;So;0;ON;;;;;N;;;;; +1F845;UPWARDS HEAVY ARROW;So;0;ON;;;;;N;;;;; +1F846;RIGHTWARDS HEAVY ARROW;So;0;ON;;;;;N;;;;; +1F847;DOWNWARDS HEAVY ARROW;So;0;ON;;;;;N;;;;; +1F850;LEFTWARDS SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; +1F851;UPWARDS SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; +1F852;RIGHTWARDS SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; +1F853;DOWNWARDS SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; +1F854;NORTH WEST SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; +1F855;NORTH EAST SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; +1F856;SOUTH EAST SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; +1F857;SOUTH WEST SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; +1F858;LEFT RIGHT SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; +1F859;UP DOWN SANS-SERIF ARROW;So;0;ON;;;;;N;;;;; +1F860;WIDE-HEADED LEFTWARDS LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; +1F861;WIDE-HEADED UPWARDS LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; +1F862;WIDE-HEADED RIGHTWARDS LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; +1F863;WIDE-HEADED DOWNWARDS LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; +1F864;WIDE-HEADED NORTH WEST LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; +1F865;WIDE-HEADED NORTH EAST LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; +1F866;WIDE-HEADED SOUTH EAST LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; +1F867;WIDE-HEADED SOUTH WEST LIGHT BARB ARROW;So;0;ON;;;;;N;;;;; +1F868;WIDE-HEADED LEFTWARDS BARB ARROW;So;0;ON;;;;;N;;;;; +1F869;WIDE-HEADED UPWARDS BARB ARROW;So;0;ON;;;;;N;;;;; +1F86A;WIDE-HEADED RIGHTWARDS BARB ARROW;So;0;ON;;;;;N;;;;; +1F86B;WIDE-HEADED DOWNWARDS BARB ARROW;So;0;ON;;;;;N;;;;; +1F86C;WIDE-HEADED NORTH WEST BARB ARROW;So;0;ON;;;;;N;;;;; +1F86D;WIDE-HEADED NORTH EAST BARB ARROW;So;0;ON;;;;;N;;;;; +1F86E;WIDE-HEADED SOUTH EAST BARB ARROW;So;0;ON;;;;;N;;;;; +1F86F;WIDE-HEADED SOUTH WEST BARB ARROW;So;0;ON;;;;;N;;;;; +1F870;WIDE-HEADED LEFTWARDS MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; +1F871;WIDE-HEADED UPWARDS MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; +1F872;WIDE-HEADED RIGHTWARDS MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; +1F873;WIDE-HEADED DOWNWARDS MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; +1F874;WIDE-HEADED NORTH WEST MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; +1F875;WIDE-HEADED NORTH EAST MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; +1F876;WIDE-HEADED SOUTH EAST MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; +1F877;WIDE-HEADED SOUTH WEST MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;; +1F878;WIDE-HEADED LEFTWARDS HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F879;WIDE-HEADED UPWARDS HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F87A;WIDE-HEADED RIGHTWARDS HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F87B;WIDE-HEADED DOWNWARDS HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F87C;WIDE-HEADED NORTH WEST HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F87D;WIDE-HEADED NORTH EAST HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F87E;WIDE-HEADED SOUTH EAST HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F87F;WIDE-HEADED SOUTH WEST HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F880;WIDE-HEADED LEFTWARDS VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F881;WIDE-HEADED UPWARDS VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F882;WIDE-HEADED RIGHTWARDS VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F883;WIDE-HEADED DOWNWARDS VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F884;WIDE-HEADED NORTH WEST VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F885;WIDE-HEADED NORTH EAST VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F886;WIDE-HEADED SOUTH EAST VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F887;WIDE-HEADED SOUTH WEST VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;; +1F890;LEFTWARDS TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F891;UPWARDS TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F892;RIGHTWARDS TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F893;DOWNWARDS TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F894;LEFTWARDS WHITE ARROW WITHIN TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F895;UPWARDS WHITE ARROW WITHIN TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F896;RIGHTWARDS WHITE ARROW WITHIN TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F897;DOWNWARDS WHITE ARROW WITHIN TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;; +1F898;LEFTWARDS ARROW WITH NOTCHED TAIL;So;0;ON;;;;;N;;;;; +1F899;UPWARDS ARROW WITH NOTCHED TAIL;So;0;ON;;;;;N;;;;; +1F89A;RIGHTWARDS ARROW WITH NOTCHED TAIL;So;0;ON;;;;;N;;;;; +1F89B;DOWNWARDS ARROW WITH NOTCHED TAIL;So;0;ON;;;;;N;;;;; +1F89C;HEAVY ARROW SHAFT WIDTH ONE;So;0;ON;;;;;N;;;;; +1F89D;HEAVY ARROW SHAFT WIDTH TWO THIRDS;So;0;ON;;;;;N;;;;; +1F89E;HEAVY ARROW SHAFT WIDTH ONE HALF;So;0;ON;;;;;N;;;;; +1F89F;HEAVY ARROW SHAFT WIDTH ONE THIRD;So;0;ON;;;;;N;;;;; +1F8A0;LEFTWARDS BOTTOM-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; +1F8A1;RIGHTWARDS BOTTOM SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; +1F8A2;LEFTWARDS TOP SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; +1F8A3;RIGHTWARDS TOP SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; +1F8A4;LEFTWARDS LEFT-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; +1F8A5;RIGHTWARDS RIGHT-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; +1F8A6;LEFTWARDS RIGHT-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; +1F8A7;RIGHTWARDS LEFT-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;; +1F8A8;LEFTWARDS BACK-TILTED SHADOWED WHITE ARROW;So;0;ON;;;;;N;;;;; +1F8A9;RIGHTWARDS BACK-TILTED SHADOWED WHITE ARROW;So;0;ON;;;;;N;;;;; +1F8AA;LEFTWARDS FRONT-TILTED SHADOWED WHITE ARROW;So;0;ON;;;;;N;;;;; +1F8AB;RIGHTWARDS FRONT-TILTED SHADOWED WHITE ARROW;So;0;ON;;;;;N;;;;; +1F8AC;WHITE ARROW SHAFT WIDTH ONE;So;0;ON;;;;;N;;;;; +1F8AD;WHITE ARROW SHAFT WIDTH TWO THIRDS;So;0;ON;;;;;N;;;;; +1F910;ZIPPER-MOUTH FACE;So;0;ON;;;;;N;;;;; +1F911;MONEY-MOUTH FACE;So;0;ON;;;;;N;;;;; +1F912;FACE WITH THERMOMETER;So;0;ON;;;;;N;;;;; +1F913;NERD FACE;So;0;ON;;;;;N;;;;; +1F914;THINKING FACE;So;0;ON;;;;;N;;;;; +1F915;FACE WITH HEAD-BANDAGE;So;0;ON;;;;;N;;;;; +1F916;ROBOT FACE;So;0;ON;;;;;N;;;;; +1F917;HUGGING FACE;So;0;ON;;;;;N;;;;; +1F918;SIGN OF THE HORNS;So;0;ON;;;;;N;;;;; +1F919;CALL ME HAND;So;0;ON;;;;;N;;;;; +1F91A;RAISED BACK OF HAND;So;0;ON;;;;;N;;;;; +1F91B;LEFT-FACING FIST;So;0;ON;;;;;N;;;;; +1F91C;RIGHT-FACING FIST;So;0;ON;;;;;N;;;;; +1F91D;HANDSHAKE;So;0;ON;;;;;N;;;;; +1F91E;HAND WITH INDEX AND MIDDLE FINGERS CROSSED;So;0;ON;;;;;N;;;;; +1F920;FACE WITH COWBOY HAT;So;0;ON;;;;;N;;;;; +1F921;CLOWN FACE;So;0;ON;;;;;N;;;;; +1F922;NAUSEATED FACE;So;0;ON;;;;;N;;;;; +1F923;ROLLING ON THE FLOOR LAUGHING;So;0;ON;;;;;N;;;;; +1F924;DROOLING FACE;So;0;ON;;;;;N;;;;; +1F925;LYING FACE;So;0;ON;;;;;N;;;;; +1F926;FACE PALM;So;0;ON;;;;;N;;;;; +1F927;SNEEZING FACE;So;0;ON;;;;;N;;;;; +1F930;PREGNANT WOMAN;So;0;ON;;;;;N;;;;; +1F933;SELFIE;So;0;ON;;;;;N;;;;; +1F934;PRINCE;So;0;ON;;;;;N;;;;; +1F935;MAN IN TUXEDO;So;0;ON;;;;;N;;;;; +1F936;MOTHER CHRISTMAS;So;0;ON;;;;;N;;;;; +1F937;SHRUG;So;0;ON;;;;;N;;;;; +1F938;PERSON DOING CARTWHEEL;So;0;ON;;;;;N;;;;; +1F939;JUGGLING;So;0;ON;;;;;N;;;;; +1F93A;FENCER;So;0;ON;;;;;N;;;;; +1F93B;MODERN PENTATHLON;So;0;ON;;;;;N;;;;; +1F93C;WRESTLERS;So;0;ON;;;;;N;;;;; +1F93D;WATER POLO;So;0;ON;;;;;N;;;;; +1F93E;HANDBALL;So;0;ON;;;;;N;;;;; +1F940;WILTED FLOWER;So;0;ON;;;;;N;;;;; +1F941;DRUM WITH DRUMSTICKS;So;0;ON;;;;;N;;;;; +1F942;CLINKING GLASSES;So;0;ON;;;;;N;;;;; +1F943;TUMBLER GLASS;So;0;ON;;;;;N;;;;; +1F944;SPOON;So;0;ON;;;;;N;;;;; +1F945;GOAL NET;So;0;ON;;;;;N;;;;; +1F946;RIFLE;So;0;ON;;;;;N;;;;; +1F947;FIRST PLACE MEDAL;So;0;ON;;;;;N;;;;; +1F948;SECOND PLACE MEDAL;So;0;ON;;;;;N;;;;; +1F949;THIRD PLACE MEDAL;So;0;ON;;;;;N;;;;; +1F94A;BOXING GLOVE;So;0;ON;;;;;N;;;;; +1F94B;MARTIAL ARTS UNIFORM;So;0;ON;;;;;N;;;;; +1F950;CROISSANT;So;0;ON;;;;;N;;;;; +1F951;AVOCADO;So;0;ON;;;;;N;;;;; +1F952;CUCUMBER;So;0;ON;;;;;N;;;;; +1F953;BACON;So;0;ON;;;;;N;;;;; +1F954;POTATO;So;0;ON;;;;;N;;;;; +1F955;CARROT;So;0;ON;;;;;N;;;;; +1F956;BAGUETTE BREAD;So;0;ON;;;;;N;;;;; +1F957;GREEN SALAD;So;0;ON;;;;;N;;;;; +1F958;SHALLOW PAN OF FOOD;So;0;ON;;;;;N;;;;; +1F959;STUFFED FLATBREAD;So;0;ON;;;;;N;;;;; +1F95A;EGG;So;0;ON;;;;;N;;;;; +1F95B;GLASS OF MILK;So;0;ON;;;;;N;;;;; +1F95C;PEANUTS;So;0;ON;;;;;N;;;;; +1F95D;KIWIFRUIT;So;0;ON;;;;;N;;;;; +1F95E;PANCAKES;So;0;ON;;;;;N;;;;; +1F980;CRAB;So;0;ON;;;;;N;;;;; +1F981;LION FACE;So;0;ON;;;;;N;;;;; +1F982;SCORPION;So;0;ON;;;;;N;;;;; +1F983;TURKEY;So;0;ON;;;;;N;;;;; +1F984;UNICORN FACE;So;0;ON;;;;;N;;;;; +1F985;EAGLE;So;0;ON;;;;;N;;;;; +1F986;DUCK;So;0;ON;;;;;N;;;;; +1F987;BAT;So;0;ON;;;;;N;;;;; +1F988;SHARK;So;0;ON;;;;;N;;;;; +1F989;OWL;So;0;ON;;;;;N;;;;; +1F98A;FOX FACE;So;0;ON;;;;;N;;;;; +1F98B;BUTTERFLY;So;0;ON;;;;;N;;;;; +1F98C;DEER;So;0;ON;;;;;N;;;;; +1F98D;GORILLA;So;0;ON;;;;;N;;;;; +1F98E;LIZARD;So;0;ON;;;;;N;;;;; +1F98F;RHINOCEROS;So;0;ON;;;;;N;;;;; +1F990;SHRIMP;So;0;ON;;;;;N;;;;; +1F991;SQUID;So;0;ON;;;;;N;;;;; +1F9C0;CHEESE WEDGE;So;0;ON;;;;;N;;;;; +20000;<CJK Ideograph Extension B, First>;Lo;0;L;;;;;N;;;;; +2A6D6;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;; +2A700;<CJK Ideograph Extension C, First>;Lo;0;L;;;;;N;;;;; +2B734;<CJK Ideograph Extension C, Last>;Lo;0;L;;;;;N;;;;; +2B740;<CJK Ideograph Extension D, First>;Lo;0;L;;;;;N;;;;; +2B81D;<CJK Ideograph Extension D, Last>;Lo;0;L;;;;;N;;;;; +2B820;<CJK Ideograph Extension E, First>;Lo;0;L;;;;;N;;;;; +2CEA1;<CJK Ideograph Extension E, Last>;Lo;0;L;;;;;N;;;;; +2F800;CJK COMPATIBILITY IDEOGRAPH-2F800;Lo;0;L;4E3D;;;;N;;;;; +2F801;CJK COMPATIBILITY IDEOGRAPH-2F801;Lo;0;L;4E38;;;;N;;;;; +2F802;CJK COMPATIBILITY IDEOGRAPH-2F802;Lo;0;L;4E41;;;;N;;;;; +2F803;CJK COMPATIBILITY IDEOGRAPH-2F803;Lo;0;L;20122;;;;N;;;;; +2F804;CJK COMPATIBILITY IDEOGRAPH-2F804;Lo;0;L;4F60;;;;N;;;;; +2F805;CJK COMPATIBILITY IDEOGRAPH-2F805;Lo;0;L;4FAE;;;;N;;;;; +2F806;CJK COMPATIBILITY IDEOGRAPH-2F806;Lo;0;L;4FBB;;;;N;;;;; +2F807;CJK COMPATIBILITY IDEOGRAPH-2F807;Lo;0;L;5002;;;;N;;;;; +2F808;CJK COMPATIBILITY IDEOGRAPH-2F808;Lo;0;L;507A;;;;N;;;;; +2F809;CJK COMPATIBILITY IDEOGRAPH-2F809;Lo;0;L;5099;;;;N;;;;; +2F80A;CJK COMPATIBILITY IDEOGRAPH-2F80A;Lo;0;L;50E7;;;;N;;;;; +2F80B;CJK COMPATIBILITY IDEOGRAPH-2F80B;Lo;0;L;50CF;;;;N;;;;; +2F80C;CJK COMPATIBILITY IDEOGRAPH-2F80C;Lo;0;L;349E;;;;N;;;;; +2F80D;CJK COMPATIBILITY IDEOGRAPH-2F80D;Lo;0;L;2063A;;;;N;;;;; +2F80E;CJK COMPATIBILITY IDEOGRAPH-2F80E;Lo;0;L;514D;;;;N;;;;; +2F80F;CJK COMPATIBILITY IDEOGRAPH-2F80F;Lo;0;L;5154;;;;N;;;;; +2F810;CJK COMPATIBILITY IDEOGRAPH-2F810;Lo;0;L;5164;;;;N;;;;; +2F811;CJK COMPATIBILITY IDEOGRAPH-2F811;Lo;0;L;5177;;;;N;;;;; +2F812;CJK COMPATIBILITY IDEOGRAPH-2F812;Lo;0;L;2051C;;;;N;;;;; +2F813;CJK COMPATIBILITY IDEOGRAPH-2F813;Lo;0;L;34B9;;;;N;;;;; +2F814;CJK COMPATIBILITY IDEOGRAPH-2F814;Lo;0;L;5167;;;;N;;;;; +2F815;CJK COMPATIBILITY IDEOGRAPH-2F815;Lo;0;L;518D;;;;N;;;;; +2F816;CJK COMPATIBILITY IDEOGRAPH-2F816;Lo;0;L;2054B;;;;N;;;;; +2F817;CJK COMPATIBILITY IDEOGRAPH-2F817;Lo;0;L;5197;;;;N;;;;; +2F818;CJK COMPATIBILITY IDEOGRAPH-2F818;Lo;0;L;51A4;;;;N;;;;; +2F819;CJK COMPATIBILITY IDEOGRAPH-2F819;Lo;0;L;4ECC;;;;N;;;;; +2F81A;CJK COMPATIBILITY IDEOGRAPH-2F81A;Lo;0;L;51AC;;;;N;;;;; +2F81B;CJK COMPATIBILITY IDEOGRAPH-2F81B;Lo;0;L;51B5;;;;N;;;;; +2F81C;CJK COMPATIBILITY IDEOGRAPH-2F81C;Lo;0;L;291DF;;;;N;;;;; +2F81D;CJK COMPATIBILITY IDEOGRAPH-2F81D;Lo;0;L;51F5;;;;N;;;;; +2F81E;CJK COMPATIBILITY IDEOGRAPH-2F81E;Lo;0;L;5203;;;;N;;;;; +2F81F;CJK COMPATIBILITY IDEOGRAPH-2F81F;Lo;0;L;34DF;;;;N;;;;; +2F820;CJK COMPATIBILITY IDEOGRAPH-2F820;Lo;0;L;523B;;;;N;;;;; +2F821;CJK COMPATIBILITY IDEOGRAPH-2F821;Lo;0;L;5246;;;;N;;;;; +2F822;CJK COMPATIBILITY IDEOGRAPH-2F822;Lo;0;L;5272;;;;N;;;;; +2F823;CJK COMPATIBILITY IDEOGRAPH-2F823;Lo;0;L;5277;;;;N;;;;; +2F824;CJK COMPATIBILITY IDEOGRAPH-2F824;Lo;0;L;3515;;;;N;;;;; +2F825;CJK COMPATIBILITY IDEOGRAPH-2F825;Lo;0;L;52C7;;;;N;;;;; +2F826;CJK COMPATIBILITY IDEOGRAPH-2F826;Lo;0;L;52C9;;;;N;;;;; +2F827;CJK COMPATIBILITY IDEOGRAPH-2F827;Lo;0;L;52E4;;;;N;;;;; +2F828;CJK COMPATIBILITY IDEOGRAPH-2F828;Lo;0;L;52FA;;;;N;;;;; +2F829;CJK COMPATIBILITY IDEOGRAPH-2F829;Lo;0;L;5305;;;;N;;;;; +2F82A;CJK COMPATIBILITY IDEOGRAPH-2F82A;Lo;0;L;5306;;;;N;;;;; +2F82B;CJK COMPATIBILITY IDEOGRAPH-2F82B;Lo;0;L;5317;;;;N;;;;; +2F82C;CJK COMPATIBILITY IDEOGRAPH-2F82C;Lo;0;L;5349;;;;N;;;;; +2F82D;CJK COMPATIBILITY IDEOGRAPH-2F82D;Lo;0;L;5351;;;;N;;;;; +2F82E;CJK COMPATIBILITY IDEOGRAPH-2F82E;Lo;0;L;535A;;;;N;;;;; +2F82F;CJK COMPATIBILITY IDEOGRAPH-2F82F;Lo;0;L;5373;;;;N;;;;; +2F830;CJK COMPATIBILITY IDEOGRAPH-2F830;Lo;0;L;537D;;;;N;;;;; +2F831;CJK COMPATIBILITY IDEOGRAPH-2F831;Lo;0;L;537F;;;;N;;;;; +2F832;CJK COMPATIBILITY IDEOGRAPH-2F832;Lo;0;L;537F;;;;N;;;;; +2F833;CJK COMPATIBILITY IDEOGRAPH-2F833;Lo;0;L;537F;;;;N;;;;; +2F834;CJK COMPATIBILITY IDEOGRAPH-2F834;Lo;0;L;20A2C;;;;N;;;;; +2F835;CJK COMPATIBILITY IDEOGRAPH-2F835;Lo;0;L;7070;;;;N;;;;; +2F836;CJK COMPATIBILITY IDEOGRAPH-2F836;Lo;0;L;53CA;;;;N;;;;; +2F837;CJK COMPATIBILITY IDEOGRAPH-2F837;Lo;0;L;53DF;;;;N;;;;; +2F838;CJK COMPATIBILITY IDEOGRAPH-2F838;Lo;0;L;20B63;;;;N;;;;; +2F839;CJK COMPATIBILITY IDEOGRAPH-2F839;Lo;0;L;53EB;;;;N;;;;; +2F83A;CJK COMPATIBILITY IDEOGRAPH-2F83A;Lo;0;L;53F1;;;;N;;;;; +2F83B;CJK COMPATIBILITY IDEOGRAPH-2F83B;Lo;0;L;5406;;;;N;;;;; +2F83C;CJK COMPATIBILITY IDEOGRAPH-2F83C;Lo;0;L;549E;;;;N;;;;; +2F83D;CJK COMPATIBILITY IDEOGRAPH-2F83D;Lo;0;L;5438;;;;N;;;;; +2F83E;CJK COMPATIBILITY IDEOGRAPH-2F83E;Lo;0;L;5448;;;;N;;;;; +2F83F;CJK COMPATIBILITY IDEOGRAPH-2F83F;Lo;0;L;5468;;;;N;;;;; +2F840;CJK COMPATIBILITY IDEOGRAPH-2F840;Lo;0;L;54A2;;;;N;;;;; +2F841;CJK COMPATIBILITY IDEOGRAPH-2F841;Lo;0;L;54F6;;;;N;;;;; +2F842;CJK COMPATIBILITY IDEOGRAPH-2F842;Lo;0;L;5510;;;;N;;;;; +2F843;CJK COMPATIBILITY IDEOGRAPH-2F843;Lo;0;L;5553;;;;N;;;;; +2F844;CJK COMPATIBILITY IDEOGRAPH-2F844;Lo;0;L;5563;;;;N;;;;; +2F845;CJK COMPATIBILITY IDEOGRAPH-2F845;Lo;0;L;5584;;;;N;;;;; +2F846;CJK COMPATIBILITY IDEOGRAPH-2F846;Lo;0;L;5584;;;;N;;;;; +2F847;CJK COMPATIBILITY IDEOGRAPH-2F847;Lo;0;L;5599;;;;N;;;;; +2F848;CJK COMPATIBILITY IDEOGRAPH-2F848;Lo;0;L;55AB;;;;N;;;;; +2F849;CJK COMPATIBILITY IDEOGRAPH-2F849;Lo;0;L;55B3;;;;N;;;;; +2F84A;CJK COMPATIBILITY IDEOGRAPH-2F84A;Lo;0;L;55C2;;;;N;;;;; +2F84B;CJK COMPATIBILITY IDEOGRAPH-2F84B;Lo;0;L;5716;;;;N;;;;; +2F84C;CJK COMPATIBILITY IDEOGRAPH-2F84C;Lo;0;L;5606;;;;N;;;;; +2F84D;CJK COMPATIBILITY IDEOGRAPH-2F84D;Lo;0;L;5717;;;;N;;;;; +2F84E;CJK COMPATIBILITY IDEOGRAPH-2F84E;Lo;0;L;5651;;;;N;;;;; +2F84F;CJK COMPATIBILITY IDEOGRAPH-2F84F;Lo;0;L;5674;;;;N;;;;; +2F850;CJK COMPATIBILITY IDEOGRAPH-2F850;Lo;0;L;5207;;;;N;;;;; +2F851;CJK COMPATIBILITY IDEOGRAPH-2F851;Lo;0;L;58EE;;;;N;;;;; +2F852;CJK COMPATIBILITY IDEOGRAPH-2F852;Lo;0;L;57CE;;;;N;;;;; +2F853;CJK COMPATIBILITY IDEOGRAPH-2F853;Lo;0;L;57F4;;;;N;;;;; +2F854;CJK COMPATIBILITY IDEOGRAPH-2F854;Lo;0;L;580D;;;;N;;;;; +2F855;CJK COMPATIBILITY IDEOGRAPH-2F855;Lo;0;L;578B;;;;N;;;;; +2F856;CJK COMPATIBILITY IDEOGRAPH-2F856;Lo;0;L;5832;;;;N;;;;; +2F857;CJK COMPATIBILITY IDEOGRAPH-2F857;Lo;0;L;5831;;;;N;;;;; +2F858;CJK COMPATIBILITY IDEOGRAPH-2F858;Lo;0;L;58AC;;;;N;;;;; +2F859;CJK COMPATIBILITY IDEOGRAPH-2F859;Lo;0;L;214E4;;;;N;;;;; +2F85A;CJK COMPATIBILITY IDEOGRAPH-2F85A;Lo;0;L;58F2;;;;N;;;;; +2F85B;CJK COMPATIBILITY IDEOGRAPH-2F85B;Lo;0;L;58F7;;;;N;;;;; +2F85C;CJK COMPATIBILITY IDEOGRAPH-2F85C;Lo;0;L;5906;;;;N;;;;; +2F85D;CJK COMPATIBILITY IDEOGRAPH-2F85D;Lo;0;L;591A;;;;N;;;;; +2F85E;CJK COMPATIBILITY IDEOGRAPH-2F85E;Lo;0;L;5922;;;;N;;;;; +2F85F;CJK COMPATIBILITY IDEOGRAPH-2F85F;Lo;0;L;5962;;;;N;;;;; +2F860;CJK COMPATIBILITY IDEOGRAPH-2F860;Lo;0;L;216A8;;;;N;;;;; +2F861;CJK COMPATIBILITY IDEOGRAPH-2F861;Lo;0;L;216EA;;;;N;;;;; +2F862;CJK COMPATIBILITY IDEOGRAPH-2F862;Lo;0;L;59EC;;;;N;;;;; +2F863;CJK COMPATIBILITY IDEOGRAPH-2F863;Lo;0;L;5A1B;;;;N;;;;; +2F864;CJK COMPATIBILITY IDEOGRAPH-2F864;Lo;0;L;5A27;;;;N;;;;; +2F865;CJK COMPATIBILITY IDEOGRAPH-2F865;Lo;0;L;59D8;;;;N;;;;; +2F866;CJK COMPATIBILITY IDEOGRAPH-2F866;Lo;0;L;5A66;;;;N;;;;; +2F867;CJK COMPATIBILITY IDEOGRAPH-2F867;Lo;0;L;36EE;;;;N;;;;; +2F868;CJK COMPATIBILITY IDEOGRAPH-2F868;Lo;0;L;36FC;;;;N;;;;; +2F869;CJK COMPATIBILITY IDEOGRAPH-2F869;Lo;0;L;5B08;;;;N;;;;; +2F86A;CJK COMPATIBILITY IDEOGRAPH-2F86A;Lo;0;L;5B3E;;;;N;;;;; +2F86B;CJK COMPATIBILITY IDEOGRAPH-2F86B;Lo;0;L;5B3E;;;;N;;;;; +2F86C;CJK COMPATIBILITY IDEOGRAPH-2F86C;Lo;0;L;219C8;;;;N;;;;; +2F86D;CJK COMPATIBILITY IDEOGRAPH-2F86D;Lo;0;L;5BC3;;;;N;;;;; +2F86E;CJK COMPATIBILITY IDEOGRAPH-2F86E;Lo;0;L;5BD8;;;;N;;;;; +2F86F;CJK COMPATIBILITY IDEOGRAPH-2F86F;Lo;0;L;5BE7;;;;N;;;;; +2F870;CJK COMPATIBILITY IDEOGRAPH-2F870;Lo;0;L;5BF3;;;;N;;;;; +2F871;CJK COMPATIBILITY IDEOGRAPH-2F871;Lo;0;L;21B18;;;;N;;;;; +2F872;CJK COMPATIBILITY IDEOGRAPH-2F872;Lo;0;L;5BFF;;;;N;;;;; +2F873;CJK COMPATIBILITY IDEOGRAPH-2F873;Lo;0;L;5C06;;;;N;;;;; +2F874;CJK COMPATIBILITY IDEOGRAPH-2F874;Lo;0;L;5F53;;;;N;;;;; +2F875;CJK COMPATIBILITY IDEOGRAPH-2F875;Lo;0;L;5C22;;;;N;;;;; +2F876;CJK COMPATIBILITY IDEOGRAPH-2F876;Lo;0;L;3781;;;;N;;;;; +2F877;CJK COMPATIBILITY IDEOGRAPH-2F877;Lo;0;L;5C60;;;;N;;;;; +2F878;CJK COMPATIBILITY IDEOGRAPH-2F878;Lo;0;L;5C6E;;;;N;;;;; +2F879;CJK COMPATIBILITY IDEOGRAPH-2F879;Lo;0;L;5CC0;;;;N;;;;; +2F87A;CJK COMPATIBILITY IDEOGRAPH-2F87A;Lo;0;L;5C8D;;;;N;;;;; +2F87B;CJK COMPATIBILITY IDEOGRAPH-2F87B;Lo;0;L;21DE4;;;;N;;;;; +2F87C;CJK COMPATIBILITY IDEOGRAPH-2F87C;Lo;0;L;5D43;;;;N;;;;; +2F87D;CJK COMPATIBILITY IDEOGRAPH-2F87D;Lo;0;L;21DE6;;;;N;;;;; +2F87E;CJK COMPATIBILITY IDEOGRAPH-2F87E;Lo;0;L;5D6E;;;;N;;;;; +2F87F;CJK COMPATIBILITY IDEOGRAPH-2F87F;Lo;0;L;5D6B;;;;N;;;;; +2F880;CJK COMPATIBILITY IDEOGRAPH-2F880;Lo;0;L;5D7C;;;;N;;;;; +2F881;CJK COMPATIBILITY IDEOGRAPH-2F881;Lo;0;L;5DE1;;;;N;;;;; +2F882;CJK COMPATIBILITY IDEOGRAPH-2F882;Lo;0;L;5DE2;;;;N;;;;; +2F883;CJK COMPATIBILITY IDEOGRAPH-2F883;Lo;0;L;382F;;;;N;;;;; +2F884;CJK COMPATIBILITY IDEOGRAPH-2F884;Lo;0;L;5DFD;;;;N;;;;; +2F885;CJK COMPATIBILITY IDEOGRAPH-2F885;Lo;0;L;5E28;;;;N;;;;; +2F886;CJK COMPATIBILITY IDEOGRAPH-2F886;Lo;0;L;5E3D;;;;N;;;;; +2F887;CJK COMPATIBILITY IDEOGRAPH-2F887;Lo;0;L;5E69;;;;N;;;;; +2F888;CJK COMPATIBILITY IDEOGRAPH-2F888;Lo;0;L;3862;;;;N;;;;; +2F889;CJK COMPATIBILITY IDEOGRAPH-2F889;Lo;0;L;22183;;;;N;;;;; +2F88A;CJK COMPATIBILITY IDEOGRAPH-2F88A;Lo;0;L;387C;;;;N;;;;; +2F88B;CJK COMPATIBILITY IDEOGRAPH-2F88B;Lo;0;L;5EB0;;;;N;;;;; +2F88C;CJK COMPATIBILITY IDEOGRAPH-2F88C;Lo;0;L;5EB3;;;;N;;;;; +2F88D;CJK COMPATIBILITY IDEOGRAPH-2F88D;Lo;0;L;5EB6;;;;N;;;;; +2F88E;CJK COMPATIBILITY IDEOGRAPH-2F88E;Lo;0;L;5ECA;;;;N;;;;; +2F88F;CJK COMPATIBILITY IDEOGRAPH-2F88F;Lo;0;L;2A392;;;;N;;;;; +2F890;CJK COMPATIBILITY IDEOGRAPH-2F890;Lo;0;L;5EFE;;;9;N;;;;; +2F891;CJK COMPATIBILITY IDEOGRAPH-2F891;Lo;0;L;22331;;;;N;;;;; +2F892;CJK COMPATIBILITY IDEOGRAPH-2F892;Lo;0;L;22331;;;;N;;;;; +2F893;CJK COMPATIBILITY IDEOGRAPH-2F893;Lo;0;L;8201;;;;N;;;;; +2F894;CJK COMPATIBILITY IDEOGRAPH-2F894;Lo;0;L;5F22;;;;N;;;;; +2F895;CJK COMPATIBILITY IDEOGRAPH-2F895;Lo;0;L;5F22;;;;N;;;;; +2F896;CJK COMPATIBILITY IDEOGRAPH-2F896;Lo;0;L;38C7;;;;N;;;;; +2F897;CJK COMPATIBILITY IDEOGRAPH-2F897;Lo;0;L;232B8;;;;N;;;;; +2F898;CJK COMPATIBILITY IDEOGRAPH-2F898;Lo;0;L;261DA;;;;N;;;;; +2F899;CJK COMPATIBILITY IDEOGRAPH-2F899;Lo;0;L;5F62;;;;N;;;;; +2F89A;CJK COMPATIBILITY IDEOGRAPH-2F89A;Lo;0;L;5F6B;;;;N;;;;; +2F89B;CJK COMPATIBILITY IDEOGRAPH-2F89B;Lo;0;L;38E3;;;;N;;;;; +2F89C;CJK COMPATIBILITY IDEOGRAPH-2F89C;Lo;0;L;5F9A;;;;N;;;;; +2F89D;CJK COMPATIBILITY IDEOGRAPH-2F89D;Lo;0;L;5FCD;;;;N;;;;; +2F89E;CJK COMPATIBILITY IDEOGRAPH-2F89E;Lo;0;L;5FD7;;;;N;;;;; +2F89F;CJK COMPATIBILITY IDEOGRAPH-2F89F;Lo;0;L;5FF9;;;;N;;;;; +2F8A0;CJK COMPATIBILITY IDEOGRAPH-2F8A0;Lo;0;L;6081;;;;N;;;;; +2F8A1;CJK COMPATIBILITY IDEOGRAPH-2F8A1;Lo;0;L;393A;;;;N;;;;; +2F8A2;CJK COMPATIBILITY IDEOGRAPH-2F8A2;Lo;0;L;391C;;;;N;;;;; +2F8A3;CJK COMPATIBILITY IDEOGRAPH-2F8A3;Lo;0;L;6094;;;;N;;;;; +2F8A4;CJK COMPATIBILITY IDEOGRAPH-2F8A4;Lo;0;L;226D4;;;;N;;;;; +2F8A5;CJK COMPATIBILITY IDEOGRAPH-2F8A5;Lo;0;L;60C7;;;;N;;;;; +2F8A6;CJK COMPATIBILITY IDEOGRAPH-2F8A6;Lo;0;L;6148;;;;N;;;;; +2F8A7;CJK COMPATIBILITY IDEOGRAPH-2F8A7;Lo;0;L;614C;;;;N;;;;; +2F8A8;CJK COMPATIBILITY IDEOGRAPH-2F8A8;Lo;0;L;614E;;;;N;;;;; +2F8A9;CJK COMPATIBILITY IDEOGRAPH-2F8A9;Lo;0;L;614C;;;;N;;;;; +2F8AA;CJK COMPATIBILITY IDEOGRAPH-2F8AA;Lo;0;L;617A;;;;N;;;;; +2F8AB;CJK COMPATIBILITY IDEOGRAPH-2F8AB;Lo;0;L;618E;;;;N;;;;; +2F8AC;CJK COMPATIBILITY IDEOGRAPH-2F8AC;Lo;0;L;61B2;;;;N;;;;; +2F8AD;CJK COMPATIBILITY IDEOGRAPH-2F8AD;Lo;0;L;61A4;;;;N;;;;; +2F8AE;CJK COMPATIBILITY IDEOGRAPH-2F8AE;Lo;0;L;61AF;;;;N;;;;; +2F8AF;CJK COMPATIBILITY IDEOGRAPH-2F8AF;Lo;0;L;61DE;;;;N;;;;; +2F8B0;CJK COMPATIBILITY IDEOGRAPH-2F8B0;Lo;0;L;61F2;;;;N;;;;; +2F8B1;CJK COMPATIBILITY IDEOGRAPH-2F8B1;Lo;0;L;61F6;;;;N;;;;; +2F8B2;CJK COMPATIBILITY IDEOGRAPH-2F8B2;Lo;0;L;6210;;;;N;;;;; +2F8B3;CJK COMPATIBILITY IDEOGRAPH-2F8B3;Lo;0;L;621B;;;;N;;;;; +2F8B4;CJK COMPATIBILITY IDEOGRAPH-2F8B4;Lo;0;L;625D;;;;N;;;;; +2F8B5;CJK COMPATIBILITY IDEOGRAPH-2F8B5;Lo;0;L;62B1;;;;N;;;;; +2F8B6;CJK COMPATIBILITY IDEOGRAPH-2F8B6;Lo;0;L;62D4;;;;N;;;;; +2F8B7;CJK COMPATIBILITY IDEOGRAPH-2F8B7;Lo;0;L;6350;;;;N;;;;; +2F8B8;CJK COMPATIBILITY IDEOGRAPH-2F8B8;Lo;0;L;22B0C;;;;N;;;;; +2F8B9;CJK COMPATIBILITY IDEOGRAPH-2F8B9;Lo;0;L;633D;;;;N;;;;; +2F8BA;CJK COMPATIBILITY IDEOGRAPH-2F8BA;Lo;0;L;62FC;;;;N;;;;; +2F8BB;CJK COMPATIBILITY IDEOGRAPH-2F8BB;Lo;0;L;6368;;;;N;;;;; +2F8BC;CJK COMPATIBILITY IDEOGRAPH-2F8BC;Lo;0;L;6383;;;;N;;;;; +2F8BD;CJK COMPATIBILITY IDEOGRAPH-2F8BD;Lo;0;L;63E4;;;;N;;;;; +2F8BE;CJK COMPATIBILITY IDEOGRAPH-2F8BE;Lo;0;L;22BF1;;;;N;;;;; +2F8BF;CJK COMPATIBILITY IDEOGRAPH-2F8BF;Lo;0;L;6422;;;;N;;;;; +2F8C0;CJK COMPATIBILITY IDEOGRAPH-2F8C0;Lo;0;L;63C5;;;;N;;;;; +2F8C1;CJK COMPATIBILITY IDEOGRAPH-2F8C1;Lo;0;L;63A9;;;;N;;;;; +2F8C2;CJK COMPATIBILITY IDEOGRAPH-2F8C2;Lo;0;L;3A2E;;;;N;;;;; +2F8C3;CJK COMPATIBILITY IDEOGRAPH-2F8C3;Lo;0;L;6469;;;;N;;;;; +2F8C4;CJK COMPATIBILITY IDEOGRAPH-2F8C4;Lo;0;L;647E;;;;N;;;;; +2F8C5;CJK COMPATIBILITY IDEOGRAPH-2F8C5;Lo;0;L;649D;;;;N;;;;; +2F8C6;CJK COMPATIBILITY IDEOGRAPH-2F8C6;Lo;0;L;6477;;;;N;;;;; +2F8C7;CJK COMPATIBILITY IDEOGRAPH-2F8C7;Lo;0;L;3A6C;;;;N;;;;; +2F8C8;CJK COMPATIBILITY IDEOGRAPH-2F8C8;Lo;0;L;654F;;;;N;;;;; +2F8C9;CJK COMPATIBILITY IDEOGRAPH-2F8C9;Lo;0;L;656C;;;;N;;;;; +2F8CA;CJK COMPATIBILITY IDEOGRAPH-2F8CA;Lo;0;L;2300A;;;;N;;;;; +2F8CB;CJK COMPATIBILITY IDEOGRAPH-2F8CB;Lo;0;L;65E3;;;;N;;;;; +2F8CC;CJK COMPATIBILITY IDEOGRAPH-2F8CC;Lo;0;L;66F8;;;;N;;;;; +2F8CD;CJK COMPATIBILITY IDEOGRAPH-2F8CD;Lo;0;L;6649;;;;N;;;;; +2F8CE;CJK COMPATIBILITY IDEOGRAPH-2F8CE;Lo;0;L;3B19;;;;N;;;;; +2F8CF;CJK COMPATIBILITY IDEOGRAPH-2F8CF;Lo;0;L;6691;;;;N;;;;; +2F8D0;CJK COMPATIBILITY IDEOGRAPH-2F8D0;Lo;0;L;3B08;;;;N;;;;; +2F8D1;CJK COMPATIBILITY IDEOGRAPH-2F8D1;Lo;0;L;3AE4;;;;N;;;;; +2F8D2;CJK COMPATIBILITY IDEOGRAPH-2F8D2;Lo;0;L;5192;;;;N;;;;; +2F8D3;CJK COMPATIBILITY IDEOGRAPH-2F8D3;Lo;0;L;5195;;;;N;;;;; +2F8D4;CJK COMPATIBILITY IDEOGRAPH-2F8D4;Lo;0;L;6700;;;;N;;;;; +2F8D5;CJK COMPATIBILITY IDEOGRAPH-2F8D5;Lo;0;L;669C;;;;N;;;;; +2F8D6;CJK COMPATIBILITY IDEOGRAPH-2F8D6;Lo;0;L;80AD;;;;N;;;;; +2F8D7;CJK COMPATIBILITY IDEOGRAPH-2F8D7;Lo;0;L;43D9;;;;N;;;;; +2F8D8;CJK COMPATIBILITY IDEOGRAPH-2F8D8;Lo;0;L;6717;;;;N;;;;; +2F8D9;CJK COMPATIBILITY IDEOGRAPH-2F8D9;Lo;0;L;671B;;;;N;;;;; +2F8DA;CJK COMPATIBILITY IDEOGRAPH-2F8DA;Lo;0;L;6721;;;;N;;;;; +2F8DB;CJK COMPATIBILITY IDEOGRAPH-2F8DB;Lo;0;L;675E;;;;N;;;;; +2F8DC;CJK COMPATIBILITY IDEOGRAPH-2F8DC;Lo;0;L;6753;;;;N;;;;; +2F8DD;CJK COMPATIBILITY IDEOGRAPH-2F8DD;Lo;0;L;233C3;;;;N;;;;; +2F8DE;CJK COMPATIBILITY IDEOGRAPH-2F8DE;Lo;0;L;3B49;;;;N;;;;; +2F8DF;CJK COMPATIBILITY IDEOGRAPH-2F8DF;Lo;0;L;67FA;;;;N;;;;; +2F8E0;CJK COMPATIBILITY IDEOGRAPH-2F8E0;Lo;0;L;6785;;;;N;;;;; +2F8E1;CJK COMPATIBILITY IDEOGRAPH-2F8E1;Lo;0;L;6852;;;;N;;;;; +2F8E2;CJK COMPATIBILITY IDEOGRAPH-2F8E2;Lo;0;L;6885;;;;N;;;;; +2F8E3;CJK COMPATIBILITY IDEOGRAPH-2F8E3;Lo;0;L;2346D;;;;N;;;;; +2F8E4;CJK COMPATIBILITY IDEOGRAPH-2F8E4;Lo;0;L;688E;;;;N;;;;; +2F8E5;CJK COMPATIBILITY IDEOGRAPH-2F8E5;Lo;0;L;681F;;;;N;;;;; +2F8E6;CJK COMPATIBILITY IDEOGRAPH-2F8E6;Lo;0;L;6914;;;;N;;;;; +2F8E7;CJK COMPATIBILITY IDEOGRAPH-2F8E7;Lo;0;L;3B9D;;;;N;;;;; +2F8E8;CJK COMPATIBILITY IDEOGRAPH-2F8E8;Lo;0;L;6942;;;;N;;;;; +2F8E9;CJK COMPATIBILITY IDEOGRAPH-2F8E9;Lo;0;L;69A3;;;;N;;;;; +2F8EA;CJK COMPATIBILITY IDEOGRAPH-2F8EA;Lo;0;L;69EA;;;;N;;;;; +2F8EB;CJK COMPATIBILITY IDEOGRAPH-2F8EB;Lo;0;L;6AA8;;;;N;;;;; +2F8EC;CJK COMPATIBILITY IDEOGRAPH-2F8EC;Lo;0;L;236A3;;;;N;;;;; +2F8ED;CJK COMPATIBILITY IDEOGRAPH-2F8ED;Lo;0;L;6ADB;;;;N;;;;; +2F8EE;CJK COMPATIBILITY IDEOGRAPH-2F8EE;Lo;0;L;3C18;;;;N;;;;; +2F8EF;CJK COMPATIBILITY IDEOGRAPH-2F8EF;Lo;0;L;6B21;;;;N;;;;; +2F8F0;CJK COMPATIBILITY IDEOGRAPH-2F8F0;Lo;0;L;238A7;;;;N;;;;; +2F8F1;CJK COMPATIBILITY IDEOGRAPH-2F8F1;Lo;0;L;6B54;;;;N;;;;; +2F8F2;CJK COMPATIBILITY IDEOGRAPH-2F8F2;Lo;0;L;3C4E;;;;N;;;;; +2F8F3;CJK COMPATIBILITY IDEOGRAPH-2F8F3;Lo;0;L;6B72;;;;N;;;;; +2F8F4;CJK COMPATIBILITY IDEOGRAPH-2F8F4;Lo;0;L;6B9F;;;;N;;;;; +2F8F5;CJK COMPATIBILITY IDEOGRAPH-2F8F5;Lo;0;L;6BBA;;;;N;;;;; +2F8F6;CJK COMPATIBILITY IDEOGRAPH-2F8F6;Lo;0;L;6BBB;;;;N;;;;; +2F8F7;CJK COMPATIBILITY IDEOGRAPH-2F8F7;Lo;0;L;23A8D;;;;N;;;;; +2F8F8;CJK COMPATIBILITY IDEOGRAPH-2F8F8;Lo;0;L;21D0B;;;;N;;;;; +2F8F9;CJK COMPATIBILITY IDEOGRAPH-2F8F9;Lo;0;L;23AFA;;;;N;;;;; +2F8FA;CJK COMPATIBILITY IDEOGRAPH-2F8FA;Lo;0;L;6C4E;;;;N;;;;; +2F8FB;CJK COMPATIBILITY IDEOGRAPH-2F8FB;Lo;0;L;23CBC;;;;N;;;;; +2F8FC;CJK COMPATIBILITY IDEOGRAPH-2F8FC;Lo;0;L;6CBF;;;;N;;;;; +2F8FD;CJK COMPATIBILITY IDEOGRAPH-2F8FD;Lo;0;L;6CCD;;;;N;;;;; +2F8FE;CJK COMPATIBILITY IDEOGRAPH-2F8FE;Lo;0;L;6C67;;;;N;;;;; +2F8FF;CJK COMPATIBILITY IDEOGRAPH-2F8FF;Lo;0;L;6D16;;;;N;;;;; +2F900;CJK COMPATIBILITY IDEOGRAPH-2F900;Lo;0;L;6D3E;;;;N;;;;; +2F901;CJK COMPATIBILITY IDEOGRAPH-2F901;Lo;0;L;6D77;;;;N;;;;; +2F902;CJK COMPATIBILITY IDEOGRAPH-2F902;Lo;0;L;6D41;;;;N;;;;; +2F903;CJK COMPATIBILITY IDEOGRAPH-2F903;Lo;0;L;6D69;;;;N;;;;; +2F904;CJK COMPATIBILITY IDEOGRAPH-2F904;Lo;0;L;6D78;;;;N;;;;; +2F905;CJK COMPATIBILITY IDEOGRAPH-2F905;Lo;0;L;6D85;;;;N;;;;; +2F906;CJK COMPATIBILITY IDEOGRAPH-2F906;Lo;0;L;23D1E;;;;N;;;;; +2F907;CJK COMPATIBILITY IDEOGRAPH-2F907;Lo;0;L;6D34;;;;N;;;;; +2F908;CJK COMPATIBILITY IDEOGRAPH-2F908;Lo;0;L;6E2F;;;;N;;;;; +2F909;CJK COMPATIBILITY IDEOGRAPH-2F909;Lo;0;L;6E6E;;;;N;;;;; +2F90A;CJK COMPATIBILITY IDEOGRAPH-2F90A;Lo;0;L;3D33;;;;N;;;;; +2F90B;CJK COMPATIBILITY IDEOGRAPH-2F90B;Lo;0;L;6ECB;;;;N;;;;; +2F90C;CJK COMPATIBILITY IDEOGRAPH-2F90C;Lo;0;L;6EC7;;;;N;;;;; +2F90D;CJK COMPATIBILITY IDEOGRAPH-2F90D;Lo;0;L;23ED1;;;;N;;;;; +2F90E;CJK COMPATIBILITY IDEOGRAPH-2F90E;Lo;0;L;6DF9;;;;N;;;;; +2F90F;CJK COMPATIBILITY IDEOGRAPH-2F90F;Lo;0;L;6F6E;;;;N;;;;; +2F910;CJK COMPATIBILITY IDEOGRAPH-2F910;Lo;0;L;23F5E;;;;N;;;;; +2F911;CJK COMPATIBILITY IDEOGRAPH-2F911;Lo;0;L;23F8E;;;;N;;;;; +2F912;CJK COMPATIBILITY IDEOGRAPH-2F912;Lo;0;L;6FC6;;;;N;;;;; +2F913;CJK COMPATIBILITY IDEOGRAPH-2F913;Lo;0;L;7039;;;;N;;;;; +2F914;CJK COMPATIBILITY IDEOGRAPH-2F914;Lo;0;L;701E;;;;N;;;;; +2F915;CJK COMPATIBILITY IDEOGRAPH-2F915;Lo;0;L;701B;;;;N;;;;; +2F916;CJK COMPATIBILITY IDEOGRAPH-2F916;Lo;0;L;3D96;;;;N;;;;; +2F917;CJK COMPATIBILITY IDEOGRAPH-2F917;Lo;0;L;704A;;;;N;;;;; +2F918;CJK COMPATIBILITY IDEOGRAPH-2F918;Lo;0;L;707D;;;;N;;;;; +2F919;CJK COMPATIBILITY IDEOGRAPH-2F919;Lo;0;L;7077;;;;N;;;;; +2F91A;CJK COMPATIBILITY IDEOGRAPH-2F91A;Lo;0;L;70AD;;;;N;;;;; +2F91B;CJK COMPATIBILITY IDEOGRAPH-2F91B;Lo;0;L;20525;;;;N;;;;; +2F91C;CJK COMPATIBILITY IDEOGRAPH-2F91C;Lo;0;L;7145;;;;N;;;;; +2F91D;CJK COMPATIBILITY IDEOGRAPH-2F91D;Lo;0;L;24263;;;;N;;;;; +2F91E;CJK COMPATIBILITY IDEOGRAPH-2F91E;Lo;0;L;719C;;;;N;;;;; +2F91F;CJK COMPATIBILITY IDEOGRAPH-2F91F;Lo;0;L;243AB;;;;N;;;;; +2F920;CJK COMPATIBILITY IDEOGRAPH-2F920;Lo;0;L;7228;;;;N;;;;; +2F921;CJK COMPATIBILITY IDEOGRAPH-2F921;Lo;0;L;7235;;;;N;;;;; +2F922;CJK COMPATIBILITY IDEOGRAPH-2F922;Lo;0;L;7250;;;;N;;;;; +2F923;CJK COMPATIBILITY IDEOGRAPH-2F923;Lo;0;L;24608;;;;N;;;;; +2F924;CJK COMPATIBILITY IDEOGRAPH-2F924;Lo;0;L;7280;;;;N;;;;; +2F925;CJK COMPATIBILITY IDEOGRAPH-2F925;Lo;0;L;7295;;;;N;;;;; +2F926;CJK COMPATIBILITY IDEOGRAPH-2F926;Lo;0;L;24735;;;;N;;;;; +2F927;CJK COMPATIBILITY IDEOGRAPH-2F927;Lo;0;L;24814;;;;N;;;;; +2F928;CJK COMPATIBILITY IDEOGRAPH-2F928;Lo;0;L;737A;;;;N;;;;; +2F929;CJK COMPATIBILITY IDEOGRAPH-2F929;Lo;0;L;738B;;;;N;;;;; +2F92A;CJK COMPATIBILITY IDEOGRAPH-2F92A;Lo;0;L;3EAC;;;;N;;;;; +2F92B;CJK COMPATIBILITY IDEOGRAPH-2F92B;Lo;0;L;73A5;;;;N;;;;; +2F92C;CJK COMPATIBILITY IDEOGRAPH-2F92C;Lo;0;L;3EB8;;;;N;;;;; +2F92D;CJK COMPATIBILITY IDEOGRAPH-2F92D;Lo;0;L;3EB8;;;;N;;;;; +2F92E;CJK COMPATIBILITY IDEOGRAPH-2F92E;Lo;0;L;7447;;;;N;;;;; +2F92F;CJK COMPATIBILITY IDEOGRAPH-2F92F;Lo;0;L;745C;;;;N;;;;; +2F930;CJK COMPATIBILITY IDEOGRAPH-2F930;Lo;0;L;7471;;;;N;;;;; +2F931;CJK COMPATIBILITY IDEOGRAPH-2F931;Lo;0;L;7485;;;;N;;;;; +2F932;CJK COMPATIBILITY IDEOGRAPH-2F932;Lo;0;L;74CA;;;;N;;;;; +2F933;CJK COMPATIBILITY IDEOGRAPH-2F933;Lo;0;L;3F1B;;;;N;;;;; +2F934;CJK COMPATIBILITY IDEOGRAPH-2F934;Lo;0;L;7524;;;;N;;;;; +2F935;CJK COMPATIBILITY IDEOGRAPH-2F935;Lo;0;L;24C36;;;;N;;;;; +2F936;CJK COMPATIBILITY IDEOGRAPH-2F936;Lo;0;L;753E;;;;N;;;;; +2F937;CJK COMPATIBILITY IDEOGRAPH-2F937;Lo;0;L;24C92;;;;N;;;;; +2F938;CJK COMPATIBILITY IDEOGRAPH-2F938;Lo;0;L;7570;;;;N;;;;; +2F939;CJK COMPATIBILITY IDEOGRAPH-2F939;Lo;0;L;2219F;;;;N;;;;; +2F93A;CJK COMPATIBILITY IDEOGRAPH-2F93A;Lo;0;L;7610;;;;N;;;;; +2F93B;CJK COMPATIBILITY IDEOGRAPH-2F93B;Lo;0;L;24FA1;;;;N;;;;; +2F93C;CJK COMPATIBILITY IDEOGRAPH-2F93C;Lo;0;L;24FB8;;;;N;;;;; +2F93D;CJK COMPATIBILITY IDEOGRAPH-2F93D;Lo;0;L;25044;;;;N;;;;; +2F93E;CJK COMPATIBILITY IDEOGRAPH-2F93E;Lo;0;L;3FFC;;;;N;;;;; +2F93F;CJK COMPATIBILITY IDEOGRAPH-2F93F;Lo;0;L;4008;;;;N;;;;; +2F940;CJK COMPATIBILITY IDEOGRAPH-2F940;Lo;0;L;76F4;;;;N;;;;; +2F941;CJK COMPATIBILITY IDEOGRAPH-2F941;Lo;0;L;250F3;;;;N;;;;; +2F942;CJK COMPATIBILITY IDEOGRAPH-2F942;Lo;0;L;250F2;;;;N;;;;; +2F943;CJK COMPATIBILITY IDEOGRAPH-2F943;Lo;0;L;25119;;;;N;;;;; +2F944;CJK COMPATIBILITY IDEOGRAPH-2F944;Lo;0;L;25133;;;;N;;;;; +2F945;CJK COMPATIBILITY IDEOGRAPH-2F945;Lo;0;L;771E;;;;N;;;;; +2F946;CJK COMPATIBILITY IDEOGRAPH-2F946;Lo;0;L;771F;;;;N;;;;; +2F947;CJK COMPATIBILITY IDEOGRAPH-2F947;Lo;0;L;771F;;;;N;;;;; +2F948;CJK COMPATIBILITY IDEOGRAPH-2F948;Lo;0;L;774A;;;;N;;;;; +2F949;CJK COMPATIBILITY IDEOGRAPH-2F949;Lo;0;L;4039;;;;N;;;;; +2F94A;CJK COMPATIBILITY IDEOGRAPH-2F94A;Lo;0;L;778B;;;;N;;;;; +2F94B;CJK COMPATIBILITY IDEOGRAPH-2F94B;Lo;0;L;4046;;;;N;;;;; +2F94C;CJK COMPATIBILITY IDEOGRAPH-2F94C;Lo;0;L;4096;;;;N;;;;; +2F94D;CJK COMPATIBILITY IDEOGRAPH-2F94D;Lo;0;L;2541D;;;;N;;;;; +2F94E;CJK COMPATIBILITY IDEOGRAPH-2F94E;Lo;0;L;784E;;;;N;;;;; +2F94F;CJK COMPATIBILITY IDEOGRAPH-2F94F;Lo;0;L;788C;;;;N;;;;; +2F950;CJK COMPATIBILITY IDEOGRAPH-2F950;Lo;0;L;78CC;;;;N;;;;; +2F951;CJK COMPATIBILITY IDEOGRAPH-2F951;Lo;0;L;40E3;;;;N;;;;; +2F952;CJK COMPATIBILITY IDEOGRAPH-2F952;Lo;0;L;25626;;;;N;;;;; +2F953;CJK COMPATIBILITY IDEOGRAPH-2F953;Lo;0;L;7956;;;;N;;;;; +2F954;CJK COMPATIBILITY IDEOGRAPH-2F954;Lo;0;L;2569A;;;;N;;;;; +2F955;CJK COMPATIBILITY IDEOGRAPH-2F955;Lo;0;L;256C5;;;;N;;;;; +2F956;CJK COMPATIBILITY IDEOGRAPH-2F956;Lo;0;L;798F;;;;N;;;;; +2F957;CJK COMPATIBILITY IDEOGRAPH-2F957;Lo;0;L;79EB;;;;N;;;;; +2F958;CJK COMPATIBILITY IDEOGRAPH-2F958;Lo;0;L;412F;;;;N;;;;; +2F959;CJK COMPATIBILITY IDEOGRAPH-2F959;Lo;0;L;7A40;;;;N;;;;; +2F95A;CJK COMPATIBILITY IDEOGRAPH-2F95A;Lo;0;L;7A4A;;;;N;;;;; +2F95B;CJK COMPATIBILITY IDEOGRAPH-2F95B;Lo;0;L;7A4F;;;;N;;;;; +2F95C;CJK COMPATIBILITY IDEOGRAPH-2F95C;Lo;0;L;2597C;;;;N;;;;; +2F95D;CJK COMPATIBILITY IDEOGRAPH-2F95D;Lo;0;L;25AA7;;;;N;;;;; +2F95E;CJK COMPATIBILITY IDEOGRAPH-2F95E;Lo;0;L;25AA7;;;;N;;;;; +2F95F;CJK COMPATIBILITY IDEOGRAPH-2F95F;Lo;0;L;7AEE;;;;N;;;;; +2F960;CJK COMPATIBILITY IDEOGRAPH-2F960;Lo;0;L;4202;;;;N;;;;; +2F961;CJK COMPATIBILITY IDEOGRAPH-2F961;Lo;0;L;25BAB;;;;N;;;;; +2F962;CJK COMPATIBILITY IDEOGRAPH-2F962;Lo;0;L;7BC6;;;;N;;;;; +2F963;CJK COMPATIBILITY IDEOGRAPH-2F963;Lo;0;L;7BC9;;;;N;;;;; +2F964;CJK COMPATIBILITY IDEOGRAPH-2F964;Lo;0;L;4227;;;;N;;;;; +2F965;CJK COMPATIBILITY IDEOGRAPH-2F965;Lo;0;L;25C80;;;;N;;;;; +2F966;CJK COMPATIBILITY IDEOGRAPH-2F966;Lo;0;L;7CD2;;;;N;;;;; +2F967;CJK COMPATIBILITY IDEOGRAPH-2F967;Lo;0;L;42A0;;;;N;;;;; +2F968;CJK COMPATIBILITY IDEOGRAPH-2F968;Lo;0;L;7CE8;;;;N;;;;; +2F969;CJK COMPATIBILITY IDEOGRAPH-2F969;Lo;0;L;7CE3;;;;N;;;;; +2F96A;CJK COMPATIBILITY IDEOGRAPH-2F96A;Lo;0;L;7D00;;;;N;;;;; +2F96B;CJK COMPATIBILITY IDEOGRAPH-2F96B;Lo;0;L;25F86;;;;N;;;;; +2F96C;CJK COMPATIBILITY IDEOGRAPH-2F96C;Lo;0;L;7D63;;;;N;;;;; +2F96D;CJK COMPATIBILITY IDEOGRAPH-2F96D;Lo;0;L;4301;;;;N;;;;; +2F96E;CJK COMPATIBILITY IDEOGRAPH-2F96E;Lo;0;L;7DC7;;;;N;;;;; +2F96F;CJK COMPATIBILITY IDEOGRAPH-2F96F;Lo;0;L;7E02;;;;N;;;;; +2F970;CJK COMPATIBILITY IDEOGRAPH-2F970;Lo;0;L;7E45;;;;N;;;;; +2F971;CJK COMPATIBILITY IDEOGRAPH-2F971;Lo;0;L;4334;;;;N;;;;; +2F972;CJK COMPATIBILITY IDEOGRAPH-2F972;Lo;0;L;26228;;;;N;;;;; +2F973;CJK COMPATIBILITY IDEOGRAPH-2F973;Lo;0;L;26247;;;;N;;;;; +2F974;CJK COMPATIBILITY IDEOGRAPH-2F974;Lo;0;L;4359;;;;N;;;;; +2F975;CJK COMPATIBILITY IDEOGRAPH-2F975;Lo;0;L;262D9;;;;N;;;;; +2F976;CJK COMPATIBILITY IDEOGRAPH-2F976;Lo;0;L;7F7A;;;;N;;;;; +2F977;CJK COMPATIBILITY IDEOGRAPH-2F977;Lo;0;L;2633E;;;;N;;;;; +2F978;CJK COMPATIBILITY IDEOGRAPH-2F978;Lo;0;L;7F95;;;;N;;;;; +2F979;CJK COMPATIBILITY IDEOGRAPH-2F979;Lo;0;L;7FFA;;;;N;;;;; +2F97A;CJK COMPATIBILITY IDEOGRAPH-2F97A;Lo;0;L;8005;;;;N;;;;; +2F97B;CJK COMPATIBILITY IDEOGRAPH-2F97B;Lo;0;L;264DA;;;;N;;;;; +2F97C;CJK COMPATIBILITY IDEOGRAPH-2F97C;Lo;0;L;26523;;;;N;;;;; +2F97D;CJK COMPATIBILITY IDEOGRAPH-2F97D;Lo;0;L;8060;;;;N;;;;; +2F97E;CJK COMPATIBILITY IDEOGRAPH-2F97E;Lo;0;L;265A8;;;;N;;;;; +2F97F;CJK COMPATIBILITY IDEOGRAPH-2F97F;Lo;0;L;8070;;;;N;;;;; +2F980;CJK COMPATIBILITY IDEOGRAPH-2F980;Lo;0;L;2335F;;;;N;;;;; +2F981;CJK COMPATIBILITY IDEOGRAPH-2F981;Lo;0;L;43D5;;;;N;;;;; +2F982;CJK COMPATIBILITY IDEOGRAPH-2F982;Lo;0;L;80B2;;;;N;;;;; +2F983;CJK COMPATIBILITY IDEOGRAPH-2F983;Lo;0;L;8103;;;;N;;;;; +2F984;CJK COMPATIBILITY IDEOGRAPH-2F984;Lo;0;L;440B;;;;N;;;;; +2F985;CJK COMPATIBILITY IDEOGRAPH-2F985;Lo;0;L;813E;;;;N;;;;; +2F986;CJK COMPATIBILITY IDEOGRAPH-2F986;Lo;0;L;5AB5;;;;N;;;;; +2F987;CJK COMPATIBILITY IDEOGRAPH-2F987;Lo;0;L;267A7;;;;N;;;;; +2F988;CJK COMPATIBILITY IDEOGRAPH-2F988;Lo;0;L;267B5;;;;N;;;;; +2F989;CJK COMPATIBILITY IDEOGRAPH-2F989;Lo;0;L;23393;;;;N;;;;; +2F98A;CJK COMPATIBILITY IDEOGRAPH-2F98A;Lo;0;L;2339C;;;;N;;;;; +2F98B;CJK COMPATIBILITY IDEOGRAPH-2F98B;Lo;0;L;8201;;;;N;;;;; +2F98C;CJK COMPATIBILITY IDEOGRAPH-2F98C;Lo;0;L;8204;;;;N;;;;; +2F98D;CJK COMPATIBILITY IDEOGRAPH-2F98D;Lo;0;L;8F9E;;;;N;;;;; +2F98E;CJK COMPATIBILITY IDEOGRAPH-2F98E;Lo;0;L;446B;;;;N;;;;; +2F98F;CJK COMPATIBILITY IDEOGRAPH-2F98F;Lo;0;L;8291;;;;N;;;;; +2F990;CJK COMPATIBILITY IDEOGRAPH-2F990;Lo;0;L;828B;;;;N;;;;; +2F991;CJK COMPATIBILITY IDEOGRAPH-2F991;Lo;0;L;829D;;;;N;;;;; +2F992;CJK COMPATIBILITY IDEOGRAPH-2F992;Lo;0;L;52B3;;;;N;;;;; +2F993;CJK COMPATIBILITY IDEOGRAPH-2F993;Lo;0;L;82B1;;;;N;;;;; +2F994;CJK COMPATIBILITY IDEOGRAPH-2F994;Lo;0;L;82B3;;;;N;;;;; +2F995;CJK COMPATIBILITY IDEOGRAPH-2F995;Lo;0;L;82BD;;;;N;;;;; +2F996;CJK COMPATIBILITY IDEOGRAPH-2F996;Lo;0;L;82E6;;;;N;;;;; +2F997;CJK COMPATIBILITY IDEOGRAPH-2F997;Lo;0;L;26B3C;;;;N;;;;; +2F998;CJK COMPATIBILITY IDEOGRAPH-2F998;Lo;0;L;82E5;;;;N;;;;; +2F999;CJK COMPATIBILITY IDEOGRAPH-2F999;Lo;0;L;831D;;;;N;;;;; +2F99A;CJK COMPATIBILITY IDEOGRAPH-2F99A;Lo;0;L;8363;;;;N;;;;; +2F99B;CJK COMPATIBILITY IDEOGRAPH-2F99B;Lo;0;L;83AD;;;;N;;;;; +2F99C;CJK COMPATIBILITY IDEOGRAPH-2F99C;Lo;0;L;8323;;;;N;;;;; +2F99D;CJK COMPATIBILITY IDEOGRAPH-2F99D;Lo;0;L;83BD;;;;N;;;;; +2F99E;CJK COMPATIBILITY IDEOGRAPH-2F99E;Lo;0;L;83E7;;;;N;;;;; +2F99F;CJK COMPATIBILITY IDEOGRAPH-2F99F;Lo;0;L;8457;;;;N;;;;; +2F9A0;CJK COMPATIBILITY IDEOGRAPH-2F9A0;Lo;0;L;8353;;;;N;;;;; +2F9A1;CJK COMPATIBILITY IDEOGRAPH-2F9A1;Lo;0;L;83CA;;;;N;;;;; +2F9A2;CJK COMPATIBILITY IDEOGRAPH-2F9A2;Lo;0;L;83CC;;;;N;;;;; +2F9A3;CJK COMPATIBILITY IDEOGRAPH-2F9A3;Lo;0;L;83DC;;;;N;;;;; +2F9A4;CJK COMPATIBILITY IDEOGRAPH-2F9A4;Lo;0;L;26C36;;;;N;;;;; +2F9A5;CJK COMPATIBILITY IDEOGRAPH-2F9A5;Lo;0;L;26D6B;;;;N;;;;; +2F9A6;CJK COMPATIBILITY IDEOGRAPH-2F9A6;Lo;0;L;26CD5;;;;N;;;;; +2F9A7;CJK COMPATIBILITY IDEOGRAPH-2F9A7;Lo;0;L;452B;;;;N;;;;; +2F9A8;CJK COMPATIBILITY IDEOGRAPH-2F9A8;Lo;0;L;84F1;;;;N;;;;; +2F9A9;CJK COMPATIBILITY IDEOGRAPH-2F9A9;Lo;0;L;84F3;;;;N;;;;; +2F9AA;CJK COMPATIBILITY IDEOGRAPH-2F9AA;Lo;0;L;8516;;;;N;;;;; +2F9AB;CJK COMPATIBILITY IDEOGRAPH-2F9AB;Lo;0;L;273CA;;;;N;;;;; +2F9AC;CJK COMPATIBILITY IDEOGRAPH-2F9AC;Lo;0;L;8564;;;;N;;;;; +2F9AD;CJK COMPATIBILITY IDEOGRAPH-2F9AD;Lo;0;L;26F2C;;;;N;;;;; +2F9AE;CJK COMPATIBILITY IDEOGRAPH-2F9AE;Lo;0;L;455D;;;;N;;;;; +2F9AF;CJK COMPATIBILITY IDEOGRAPH-2F9AF;Lo;0;L;4561;;;;N;;;;; +2F9B0;CJK COMPATIBILITY IDEOGRAPH-2F9B0;Lo;0;L;26FB1;;;;N;;;;; +2F9B1;CJK COMPATIBILITY IDEOGRAPH-2F9B1;Lo;0;L;270D2;;;;N;;;;; +2F9B2;CJK COMPATIBILITY IDEOGRAPH-2F9B2;Lo;0;L;456B;;;;N;;;;; +2F9B3;CJK COMPATIBILITY IDEOGRAPH-2F9B3;Lo;0;L;8650;;;;N;;;;; +2F9B4;CJK COMPATIBILITY IDEOGRAPH-2F9B4;Lo;0;L;865C;;;;N;;;;; +2F9B5;CJK COMPATIBILITY IDEOGRAPH-2F9B5;Lo;0;L;8667;;;;N;;;;; +2F9B6;CJK COMPATIBILITY IDEOGRAPH-2F9B6;Lo;0;L;8669;;;;N;;;;; +2F9B7;CJK COMPATIBILITY IDEOGRAPH-2F9B7;Lo;0;L;86A9;;;;N;;;;; +2F9B8;CJK COMPATIBILITY IDEOGRAPH-2F9B8;Lo;0;L;8688;;;;N;;;;; +2F9B9;CJK COMPATIBILITY IDEOGRAPH-2F9B9;Lo;0;L;870E;;;;N;;;;; +2F9BA;CJK COMPATIBILITY IDEOGRAPH-2F9BA;Lo;0;L;86E2;;;;N;;;;; +2F9BB;CJK COMPATIBILITY IDEOGRAPH-2F9BB;Lo;0;L;8779;;;;N;;;;; +2F9BC;CJK COMPATIBILITY IDEOGRAPH-2F9BC;Lo;0;L;8728;;;;N;;;;; +2F9BD;CJK COMPATIBILITY IDEOGRAPH-2F9BD;Lo;0;L;876B;;;;N;;;;; +2F9BE;CJK COMPATIBILITY IDEOGRAPH-2F9BE;Lo;0;L;8786;;;;N;;;;; +2F9BF;CJK COMPATIBILITY IDEOGRAPH-2F9BF;Lo;0;L;45D7;;;;N;;;;; +2F9C0;CJK COMPATIBILITY IDEOGRAPH-2F9C0;Lo;0;L;87E1;;;;N;;;;; +2F9C1;CJK COMPATIBILITY IDEOGRAPH-2F9C1;Lo;0;L;8801;;;;N;;;;; +2F9C2;CJK COMPATIBILITY IDEOGRAPH-2F9C2;Lo;0;L;45F9;;;;N;;;;; +2F9C3;CJK COMPATIBILITY IDEOGRAPH-2F9C3;Lo;0;L;8860;;;;N;;;;; +2F9C4;CJK COMPATIBILITY IDEOGRAPH-2F9C4;Lo;0;L;8863;;;;N;;;;; +2F9C5;CJK COMPATIBILITY IDEOGRAPH-2F9C5;Lo;0;L;27667;;;;N;;;;; +2F9C6;CJK COMPATIBILITY IDEOGRAPH-2F9C6;Lo;0;L;88D7;;;;N;;;;; +2F9C7;CJK COMPATIBILITY IDEOGRAPH-2F9C7;Lo;0;L;88DE;;;;N;;;;; +2F9C8;CJK COMPATIBILITY IDEOGRAPH-2F9C8;Lo;0;L;4635;;;;N;;;;; +2F9C9;CJK COMPATIBILITY IDEOGRAPH-2F9C9;Lo;0;L;88FA;;;;N;;;;; +2F9CA;CJK COMPATIBILITY IDEOGRAPH-2F9CA;Lo;0;L;34BB;;;;N;;;;; +2F9CB;CJK COMPATIBILITY IDEOGRAPH-2F9CB;Lo;0;L;278AE;;;;N;;;;; +2F9CC;CJK COMPATIBILITY IDEOGRAPH-2F9CC;Lo;0;L;27966;;;;N;;;;; +2F9CD;CJK COMPATIBILITY IDEOGRAPH-2F9CD;Lo;0;L;46BE;;;;N;;;;; +2F9CE;CJK COMPATIBILITY IDEOGRAPH-2F9CE;Lo;0;L;46C7;;;;N;;;;; +2F9CF;CJK COMPATIBILITY IDEOGRAPH-2F9CF;Lo;0;L;8AA0;;;;N;;;;; +2F9D0;CJK COMPATIBILITY IDEOGRAPH-2F9D0;Lo;0;L;8AED;;;;N;;;;; +2F9D1;CJK COMPATIBILITY IDEOGRAPH-2F9D1;Lo;0;L;8B8A;;;;N;;;;; +2F9D2;CJK COMPATIBILITY IDEOGRAPH-2F9D2;Lo;0;L;8C55;;;;N;;;;; +2F9D3;CJK COMPATIBILITY IDEOGRAPH-2F9D3;Lo;0;L;27CA8;;;;N;;;;; +2F9D4;CJK COMPATIBILITY IDEOGRAPH-2F9D4;Lo;0;L;8CAB;;;;N;;;;; +2F9D5;CJK COMPATIBILITY IDEOGRAPH-2F9D5;Lo;0;L;8CC1;;;;N;;;;; +2F9D6;CJK COMPATIBILITY IDEOGRAPH-2F9D6;Lo;0;L;8D1B;;;;N;;;;; +2F9D7;CJK COMPATIBILITY IDEOGRAPH-2F9D7;Lo;0;L;8D77;;;;N;;;;; +2F9D8;CJK COMPATIBILITY IDEOGRAPH-2F9D8;Lo;0;L;27F2F;;;;N;;;;; +2F9D9;CJK COMPATIBILITY IDEOGRAPH-2F9D9;Lo;0;L;20804;;;;N;;;;; +2F9DA;CJK COMPATIBILITY IDEOGRAPH-2F9DA;Lo;0;L;8DCB;;;;N;;;;; +2F9DB;CJK COMPATIBILITY IDEOGRAPH-2F9DB;Lo;0;L;8DBC;;;;N;;;;; +2F9DC;CJK COMPATIBILITY IDEOGRAPH-2F9DC;Lo;0;L;8DF0;;;;N;;;;; +2F9DD;CJK COMPATIBILITY IDEOGRAPH-2F9DD;Lo;0;L;208DE;;;;N;;;;; +2F9DE;CJK COMPATIBILITY IDEOGRAPH-2F9DE;Lo;0;L;8ED4;;;;N;;;;; +2F9DF;CJK COMPATIBILITY IDEOGRAPH-2F9DF;Lo;0;L;8F38;;;;N;;;;; +2F9E0;CJK COMPATIBILITY IDEOGRAPH-2F9E0;Lo;0;L;285D2;;;;N;;;;; +2F9E1;CJK COMPATIBILITY IDEOGRAPH-2F9E1;Lo;0;L;285ED;;;;N;;;;; +2F9E2;CJK COMPATIBILITY IDEOGRAPH-2F9E2;Lo;0;L;9094;;;;N;;;;; +2F9E3;CJK COMPATIBILITY IDEOGRAPH-2F9E3;Lo;0;L;90F1;;;;N;;;;; +2F9E4;CJK COMPATIBILITY IDEOGRAPH-2F9E4;Lo;0;L;9111;;;;N;;;;; +2F9E5;CJK COMPATIBILITY IDEOGRAPH-2F9E5;Lo;0;L;2872E;;;;N;;;;; +2F9E6;CJK COMPATIBILITY IDEOGRAPH-2F9E6;Lo;0;L;911B;;;;N;;;;; +2F9E7;CJK COMPATIBILITY IDEOGRAPH-2F9E7;Lo;0;L;9238;;;;N;;;;; +2F9E8;CJK COMPATIBILITY IDEOGRAPH-2F9E8;Lo;0;L;92D7;;;;N;;;;; +2F9E9;CJK COMPATIBILITY IDEOGRAPH-2F9E9;Lo;0;L;92D8;;;;N;;;;; +2F9EA;CJK COMPATIBILITY IDEOGRAPH-2F9EA;Lo;0;L;927C;;;;N;;;;; +2F9EB;CJK COMPATIBILITY IDEOGRAPH-2F9EB;Lo;0;L;93F9;;;;N;;;;; +2F9EC;CJK COMPATIBILITY IDEOGRAPH-2F9EC;Lo;0;L;9415;;;;N;;;;; +2F9ED;CJK COMPATIBILITY IDEOGRAPH-2F9ED;Lo;0;L;28BFA;;;;N;;;;; +2F9EE;CJK COMPATIBILITY IDEOGRAPH-2F9EE;Lo;0;L;958B;;;;N;;;;; +2F9EF;CJK COMPATIBILITY IDEOGRAPH-2F9EF;Lo;0;L;4995;;;;N;;;;; +2F9F0;CJK COMPATIBILITY IDEOGRAPH-2F9F0;Lo;0;L;95B7;;;;N;;;;; +2F9F1;CJK COMPATIBILITY IDEOGRAPH-2F9F1;Lo;0;L;28D77;;;;N;;;;; +2F9F2;CJK COMPATIBILITY IDEOGRAPH-2F9F2;Lo;0;L;49E6;;;;N;;;;; +2F9F3;CJK COMPATIBILITY IDEOGRAPH-2F9F3;Lo;0;L;96C3;;;;N;;;;; +2F9F4;CJK COMPATIBILITY IDEOGRAPH-2F9F4;Lo;0;L;5DB2;;;;N;;;;; +2F9F5;CJK COMPATIBILITY IDEOGRAPH-2F9F5;Lo;0;L;9723;;;;N;;;;; +2F9F6;CJK COMPATIBILITY IDEOGRAPH-2F9F6;Lo;0;L;29145;;;;N;;;;; +2F9F7;CJK COMPATIBILITY IDEOGRAPH-2F9F7;Lo;0;L;2921A;;;;N;;;;; +2F9F8;CJK COMPATIBILITY IDEOGRAPH-2F9F8;Lo;0;L;4A6E;;;;N;;;;; +2F9F9;CJK COMPATIBILITY IDEOGRAPH-2F9F9;Lo;0;L;4A76;;;;N;;;;; +2F9FA;CJK COMPATIBILITY IDEOGRAPH-2F9FA;Lo;0;L;97E0;;;;N;;;;; +2F9FB;CJK COMPATIBILITY IDEOGRAPH-2F9FB;Lo;0;L;2940A;;;;N;;;;; +2F9FC;CJK COMPATIBILITY IDEOGRAPH-2F9FC;Lo;0;L;4AB2;;;;N;;;;; +2F9FD;CJK COMPATIBILITY IDEOGRAPH-2F9FD;Lo;0;L;29496;;;;N;;;;; +2F9FE;CJK COMPATIBILITY IDEOGRAPH-2F9FE;Lo;0;L;980B;;;;N;;;;; +2F9FF;CJK COMPATIBILITY IDEOGRAPH-2F9FF;Lo;0;L;980B;;;;N;;;;; +2FA00;CJK COMPATIBILITY IDEOGRAPH-2FA00;Lo;0;L;9829;;;;N;;;;; +2FA01;CJK COMPATIBILITY IDEOGRAPH-2FA01;Lo;0;L;295B6;;;;N;;;;; +2FA02;CJK COMPATIBILITY IDEOGRAPH-2FA02;Lo;0;L;98E2;;;;N;;;;; +2FA03;CJK COMPATIBILITY IDEOGRAPH-2FA03;Lo;0;L;4B33;;;;N;;;;; +2FA04;CJK COMPATIBILITY IDEOGRAPH-2FA04;Lo;0;L;9929;;;;N;;;;; +2FA05;CJK COMPATIBILITY IDEOGRAPH-2FA05;Lo;0;L;99A7;;;;N;;;;; +2FA06;CJK COMPATIBILITY IDEOGRAPH-2FA06;Lo;0;L;99C2;;;;N;;;;; +2FA07;CJK COMPATIBILITY IDEOGRAPH-2FA07;Lo;0;L;99FE;;;;N;;;;; +2FA08;CJK COMPATIBILITY IDEOGRAPH-2FA08;Lo;0;L;4BCE;;;;N;;;;; +2FA09;CJK COMPATIBILITY IDEOGRAPH-2FA09;Lo;0;L;29B30;;;;N;;;;; +2FA0A;CJK COMPATIBILITY IDEOGRAPH-2FA0A;Lo;0;L;9B12;;;;N;;;;; +2FA0B;CJK COMPATIBILITY IDEOGRAPH-2FA0B;Lo;0;L;9C40;;;;N;;;;; +2FA0C;CJK COMPATIBILITY IDEOGRAPH-2FA0C;Lo;0;L;9CFD;;;;N;;;;; +2FA0D;CJK COMPATIBILITY IDEOGRAPH-2FA0D;Lo;0;L;4CCE;;;;N;;;;; +2FA0E;CJK COMPATIBILITY IDEOGRAPH-2FA0E;Lo;0;L;4CED;;;;N;;;;; +2FA0F;CJK COMPATIBILITY IDEOGRAPH-2FA0F;Lo;0;L;9D67;;;;N;;;;; +2FA10;CJK COMPATIBILITY IDEOGRAPH-2FA10;Lo;0;L;2A0CE;;;;N;;;;; +2FA11;CJK COMPATIBILITY IDEOGRAPH-2FA11;Lo;0;L;4CF8;;;;N;;;;; +2FA12;CJK COMPATIBILITY IDEOGRAPH-2FA12;Lo;0;L;2A105;;;;N;;;;; +2FA13;CJK COMPATIBILITY IDEOGRAPH-2FA13;Lo;0;L;2A20E;;;;N;;;;; +2FA14;CJK COMPATIBILITY IDEOGRAPH-2FA14;Lo;0;L;2A291;;;;N;;;;; +2FA15;CJK COMPATIBILITY IDEOGRAPH-2FA15;Lo;0;L;9EBB;;;;N;;;;; +2FA16;CJK COMPATIBILITY IDEOGRAPH-2FA16;Lo;0;L;4D56;;;;N;;;;; +2FA17;CJK COMPATIBILITY IDEOGRAPH-2FA17;Lo;0;L;9EF9;;;;N;;;;; +2FA18;CJK COMPATIBILITY IDEOGRAPH-2FA18;Lo;0;L;9EFE;;;;N;;;;; +2FA19;CJK COMPATIBILITY IDEOGRAPH-2FA19;Lo;0;L;9F05;;;;N;;;;; +2FA1A;CJK COMPATIBILITY IDEOGRAPH-2FA1A;Lo;0;L;9F0F;;;;N;;;;; +2FA1B;CJK COMPATIBILITY IDEOGRAPH-2FA1B;Lo;0;L;9F16;;;;N;;;;; +2FA1C;CJK COMPATIBILITY IDEOGRAPH-2FA1C;Lo;0;L;9F3B;;;;N;;;;; +2FA1D;CJK COMPATIBILITY IDEOGRAPH-2FA1D;Lo;0;L;2A600;;;;N;;;;; +E0001;LANGUAGE TAG;Cf;0;BN;;;;;N;;;;; +E0020;TAG SPACE;Cf;0;BN;;;;;N;;;;; +E0021;TAG EXCLAMATION MARK;Cf;0;BN;;;;;N;;;;; +E0022;TAG QUOTATION MARK;Cf;0;BN;;;;;N;;;;; +E0023;TAG NUMBER SIGN;Cf;0;BN;;;;;N;;;;; +E0024;TAG DOLLAR SIGN;Cf;0;BN;;;;;N;;;;; +E0025;TAG PERCENT SIGN;Cf;0;BN;;;;;N;;;;; +E0026;TAG AMPERSAND;Cf;0;BN;;;;;N;;;;; +E0027;TAG APOSTROPHE;Cf;0;BN;;;;;N;;;;; +E0028;TAG LEFT PARENTHESIS;Cf;0;BN;;;;;N;;;;; +E0029;TAG RIGHT PARENTHESIS;Cf;0;BN;;;;;N;;;;; +E002A;TAG ASTERISK;Cf;0;BN;;;;;N;;;;; +E002B;TAG PLUS SIGN;Cf;0;BN;;;;;N;;;;; +E002C;TAG COMMA;Cf;0;BN;;;;;N;;;;; +E002D;TAG HYPHEN-MINUS;Cf;0;BN;;;;;N;;;;; +E002E;TAG FULL STOP;Cf;0;BN;;;;;N;;;;; +E002F;TAG SOLIDUS;Cf;0;BN;;;;;N;;;;; +E0030;TAG DIGIT ZERO;Cf;0;BN;;;;;N;;;;; +E0031;TAG DIGIT ONE;Cf;0;BN;;;;;N;;;;; +E0032;TAG DIGIT TWO;Cf;0;BN;;;;;N;;;;; +E0033;TAG DIGIT THREE;Cf;0;BN;;;;;N;;;;; +E0034;TAG DIGIT FOUR;Cf;0;BN;;;;;N;;;;; +E0035;TAG DIGIT FIVE;Cf;0;BN;;;;;N;;;;; +E0036;TAG DIGIT SIX;Cf;0;BN;;;;;N;;;;; +E0037;TAG DIGIT SEVEN;Cf;0;BN;;;;;N;;;;; +E0038;TAG DIGIT EIGHT;Cf;0;BN;;;;;N;;;;; +E0039;TAG DIGIT NINE;Cf;0;BN;;;;;N;;;;; +E003A;TAG COLON;Cf;0;BN;;;;;N;;;;; +E003B;TAG SEMICOLON;Cf;0;BN;;;;;N;;;;; +E003C;TAG LESS-THAN SIGN;Cf;0;BN;;;;;N;;;;; +E003D;TAG EQUALS SIGN;Cf;0;BN;;;;;N;;;;; +E003E;TAG GREATER-THAN SIGN;Cf;0;BN;;;;;N;;;;; +E003F;TAG QUESTION MARK;Cf;0;BN;;;;;N;;;;; +E0040;TAG COMMERCIAL AT;Cf;0;BN;;;;;N;;;;; +E0041;TAG LATIN CAPITAL LETTER A;Cf;0;BN;;;;;N;;;;; +E0042;TAG LATIN CAPITAL LETTER B;Cf;0;BN;;;;;N;;;;; +E0043;TAG LATIN CAPITAL LETTER C;Cf;0;BN;;;;;N;;;;; +E0044;TAG LATIN CAPITAL LETTER D;Cf;0;BN;;;;;N;;;;; +E0045;TAG LATIN CAPITAL LETTER E;Cf;0;BN;;;;;N;;;;; +E0046;TAG LATIN CAPITAL LETTER F;Cf;0;BN;;;;;N;;;;; +E0047;TAG LATIN CAPITAL LETTER G;Cf;0;BN;;;;;N;;;;; +E0048;TAG LATIN CAPITAL LETTER H;Cf;0;BN;;;;;N;;;;; +E0049;TAG LATIN CAPITAL LETTER I;Cf;0;BN;;;;;N;;;;; +E004A;TAG LATIN CAPITAL LETTER J;Cf;0;BN;;;;;N;;;;; +E004B;TAG LATIN CAPITAL LETTER K;Cf;0;BN;;;;;N;;;;; +E004C;TAG LATIN CAPITAL LETTER L;Cf;0;BN;;;;;N;;;;; +E004D;TAG LATIN CAPITAL LETTER M;Cf;0;BN;;;;;N;;;;; +E004E;TAG LATIN CAPITAL LETTER N;Cf;0;BN;;;;;N;;;;; +E004F;TAG LATIN CAPITAL LETTER O;Cf;0;BN;;;;;N;;;;; +E0050;TAG LATIN CAPITAL LETTER P;Cf;0;BN;;;;;N;;;;; +E0051;TAG LATIN CAPITAL LETTER Q;Cf;0;BN;;;;;N;;;;; +E0052;TAG LATIN CAPITAL LETTER R;Cf;0;BN;;;;;N;;;;; +E0053;TAG LATIN CAPITAL LETTER S;Cf;0;BN;;;;;N;;;;; +E0054;TAG LATIN CAPITAL LETTER T;Cf;0;BN;;;;;N;;;;; +E0055;TAG LATIN CAPITAL LETTER U;Cf;0;BN;;;;;N;;;;; +E0056;TAG LATIN CAPITAL LETTER V;Cf;0;BN;;;;;N;;;;; +E0057;TAG LATIN CAPITAL LETTER W;Cf;0;BN;;;;;N;;;;; +E0058;TAG LATIN CAPITAL LETTER X;Cf;0;BN;;;;;N;;;;; +E0059;TAG LATIN CAPITAL LETTER Y;Cf;0;BN;;;;;N;;;;; +E005A;TAG LATIN CAPITAL LETTER Z;Cf;0;BN;;;;;N;;;;; +E005B;TAG LEFT SQUARE BRACKET;Cf;0;BN;;;;;N;;;;; +E005C;TAG REVERSE SOLIDUS;Cf;0;BN;;;;;N;;;;; +E005D;TAG RIGHT SQUARE BRACKET;Cf;0;BN;;;;;N;;;;; +E005E;TAG CIRCUMFLEX ACCENT;Cf;0;BN;;;;;N;;;;; +E005F;TAG LOW LINE;Cf;0;BN;;;;;N;;;;; +E0060;TAG GRAVE ACCENT;Cf;0;BN;;;;;N;;;;; +E0061;TAG LATIN SMALL LETTER A;Cf;0;BN;;;;;N;;;;; +E0062;TAG LATIN SMALL LETTER B;Cf;0;BN;;;;;N;;;;; +E0063;TAG LATIN SMALL LETTER C;Cf;0;BN;;;;;N;;;;; +E0064;TAG LATIN SMALL LETTER D;Cf;0;BN;;;;;N;;;;; +E0065;TAG LATIN SMALL LETTER E;Cf;0;BN;;;;;N;;;;; +E0066;TAG LATIN SMALL LETTER F;Cf;0;BN;;;;;N;;;;; +E0067;TAG LATIN SMALL LETTER G;Cf;0;BN;;;;;N;;;;; +E0068;TAG LATIN SMALL LETTER H;Cf;0;BN;;;;;N;;;;; +E0069;TAG LATIN SMALL LETTER I;Cf;0;BN;;;;;N;;;;; +E006A;TAG LATIN SMALL LETTER J;Cf;0;BN;;;;;N;;;;; +E006B;TAG LATIN SMALL LETTER K;Cf;0;BN;;;;;N;;;;; +E006C;TAG LATIN SMALL LETTER L;Cf;0;BN;;;;;N;;;;; +E006D;TAG LATIN SMALL LETTER M;Cf;0;BN;;;;;N;;;;; +E006E;TAG LATIN SMALL LETTER N;Cf;0;BN;;;;;N;;;;; +E006F;TAG LATIN SMALL LETTER O;Cf;0;BN;;;;;N;;;;; +E0070;TAG LATIN SMALL LETTER P;Cf;0;BN;;;;;N;;;;; +E0071;TAG LATIN SMALL LETTER Q;Cf;0;BN;;;;;N;;;;; +E0072;TAG LATIN SMALL LETTER R;Cf;0;BN;;;;;N;;;;; +E0073;TAG LATIN SMALL LETTER S;Cf;0;BN;;;;;N;;;;; +E0074;TAG LATIN SMALL LETTER T;Cf;0;BN;;;;;N;;;;; +E0075;TAG LATIN SMALL LETTER U;Cf;0;BN;;;;;N;;;;; +E0076;TAG LATIN SMALL LETTER V;Cf;0;BN;;;;;N;;;;; +E0077;TAG LATIN SMALL LETTER W;Cf;0;BN;;;;;N;;;;; +E0078;TAG LATIN SMALL LETTER X;Cf;0;BN;;;;;N;;;;; +E0079;TAG LATIN SMALL LETTER Y;Cf;0;BN;;;;;N;;;;; +E007A;TAG LATIN SMALL LETTER Z;Cf;0;BN;;;;;N;;;;; +E007B;TAG LEFT CURLY BRACKET;Cf;0;BN;;;;;N;;;;; +E007C;TAG VERTICAL LINE;Cf;0;BN;;;;;N;;;;; +E007D;TAG RIGHT CURLY BRACKET;Cf;0;BN;;;;;N;;;;; +E007E;TAG TILDE;Cf;0;BN;;;;;N;;;;; +E007F;CANCEL TAG;Cf;0;BN;;;;;N;;;;; +E0100;VARIATION SELECTOR-17;Mn;0;NSM;;;;;N;;;;; +E0101;VARIATION SELECTOR-18;Mn;0;NSM;;;;;N;;;;; +E0102;VARIATION SELECTOR-19;Mn;0;NSM;;;;;N;;;;; +E0103;VARIATION SELECTOR-20;Mn;0;NSM;;;;;N;;;;; +E0104;VARIATION SELECTOR-21;Mn;0;NSM;;;;;N;;;;; +E0105;VARIATION SELECTOR-22;Mn;0;NSM;;;;;N;;;;; +E0106;VARIATION SELECTOR-23;Mn;0;NSM;;;;;N;;;;; +E0107;VARIATION SELECTOR-24;Mn;0;NSM;;;;;N;;;;; +E0108;VARIATION SELECTOR-25;Mn;0;NSM;;;;;N;;;;; +E0109;VARIATION SELECTOR-26;Mn;0;NSM;;;;;N;;;;; +E010A;VARIATION SELECTOR-27;Mn;0;NSM;;;;;N;;;;; +E010B;VARIATION SELECTOR-28;Mn;0;NSM;;;;;N;;;;; +E010C;VARIATION SELECTOR-29;Mn;0;NSM;;;;;N;;;;; +E010D;VARIATION SELECTOR-30;Mn;0;NSM;;;;;N;;;;; +E010E;VARIATION SELECTOR-31;Mn;0;NSM;;;;;N;;;;; +E010F;VARIATION SELECTOR-32;Mn;0;NSM;;;;;N;;;;; +E0110;VARIATION SELECTOR-33;Mn;0;NSM;;;;;N;;;;; +E0111;VARIATION SELECTOR-34;Mn;0;NSM;;;;;N;;;;; +E0112;VARIATION SELECTOR-35;Mn;0;NSM;;;;;N;;;;; +E0113;VARIATION SELECTOR-36;Mn;0;NSM;;;;;N;;;;; +E0114;VARIATION SELECTOR-37;Mn;0;NSM;;;;;N;;;;; +E0115;VARIATION SELECTOR-38;Mn;0;NSM;;;;;N;;;;; +E0116;VARIATION SELECTOR-39;Mn;0;NSM;;;;;N;;;;; +E0117;VARIATION SELECTOR-40;Mn;0;NSM;;;;;N;;;;; +E0118;VARIATION SELECTOR-41;Mn;0;NSM;;;;;N;;;;; +E0119;VARIATION SELECTOR-42;Mn;0;NSM;;;;;N;;;;; +E011A;VARIATION SELECTOR-43;Mn;0;NSM;;;;;N;;;;; +E011B;VARIATION SELECTOR-44;Mn;0;NSM;;;;;N;;;;; +E011C;VARIATION SELECTOR-45;Mn;0;NSM;;;;;N;;;;; +E011D;VARIATION SELECTOR-46;Mn;0;NSM;;;;;N;;;;; +E011E;VARIATION SELECTOR-47;Mn;0;NSM;;;;;N;;;;; +E011F;VARIATION SELECTOR-48;Mn;0;NSM;;;;;N;;;;; +E0120;VARIATION SELECTOR-49;Mn;0;NSM;;;;;N;;;;; +E0121;VARIATION SELECTOR-50;Mn;0;NSM;;;;;N;;;;; +E0122;VARIATION SELECTOR-51;Mn;0;NSM;;;;;N;;;;; +E0123;VARIATION SELECTOR-52;Mn;0;NSM;;;;;N;;;;; +E0124;VARIATION SELECTOR-53;Mn;0;NSM;;;;;N;;;;; +E0125;VARIATION SELECTOR-54;Mn;0;NSM;;;;;N;;;;; +E0126;VARIATION SELECTOR-55;Mn;0;NSM;;;;;N;;;;; +E0127;VARIATION SELECTOR-56;Mn;0;NSM;;;;;N;;;;; +E0128;VARIATION SELECTOR-57;Mn;0;NSM;;;;;N;;;;; +E0129;VARIATION SELECTOR-58;Mn;0;NSM;;;;;N;;;;; +E012A;VARIATION SELECTOR-59;Mn;0;NSM;;;;;N;;;;; +E012B;VARIATION SELECTOR-60;Mn;0;NSM;;;;;N;;;;; +E012C;VARIATION SELECTOR-61;Mn;0;NSM;;;;;N;;;;; +E012D;VARIATION SELECTOR-62;Mn;0;NSM;;;;;N;;;;; +E012E;VARIATION SELECTOR-63;Mn;0;NSM;;;;;N;;;;; +E012F;VARIATION SELECTOR-64;Mn;0;NSM;;;;;N;;;;; +E0130;VARIATION SELECTOR-65;Mn;0;NSM;;;;;N;;;;; +E0131;VARIATION SELECTOR-66;Mn;0;NSM;;;;;N;;;;; +E0132;VARIATION SELECTOR-67;Mn;0;NSM;;;;;N;;;;; +E0133;VARIATION SELECTOR-68;Mn;0;NSM;;;;;N;;;;; +E0134;VARIATION SELECTOR-69;Mn;0;NSM;;;;;N;;;;; +E0135;VARIATION SELECTOR-70;Mn;0;NSM;;;;;N;;;;; +E0136;VARIATION SELECTOR-71;Mn;0;NSM;;;;;N;;;;; +E0137;VARIATION SELECTOR-72;Mn;0;NSM;;;;;N;;;;; +E0138;VARIATION SELECTOR-73;Mn;0;NSM;;;;;N;;;;; +E0139;VARIATION SELECTOR-74;Mn;0;NSM;;;;;N;;;;; +E013A;VARIATION SELECTOR-75;Mn;0;NSM;;;;;N;;;;; +E013B;VARIATION SELECTOR-76;Mn;0;NSM;;;;;N;;;;; +E013C;VARIATION SELECTOR-77;Mn;0;NSM;;;;;N;;;;; +E013D;VARIATION SELECTOR-78;Mn;0;NSM;;;;;N;;;;; +E013E;VARIATION SELECTOR-79;Mn;0;NSM;;;;;N;;;;; +E013F;VARIATION SELECTOR-80;Mn;0;NSM;;;;;N;;;;; +E0140;VARIATION SELECTOR-81;Mn;0;NSM;;;;;N;;;;; +E0141;VARIATION SELECTOR-82;Mn;0;NSM;;;;;N;;;;; +E0142;VARIATION SELECTOR-83;Mn;0;NSM;;;;;N;;;;; +E0143;VARIATION SELECTOR-84;Mn;0;NSM;;;;;N;;;;; +E0144;VARIATION SELECTOR-85;Mn;0;NSM;;;;;N;;;;; +E0145;VARIATION SELECTOR-86;Mn;0;NSM;;;;;N;;;;; +E0146;VARIATION SELECTOR-87;Mn;0;NSM;;;;;N;;;;; +E0147;VARIATION SELECTOR-88;Mn;0;NSM;;;;;N;;;;; +E0148;VARIATION SELECTOR-89;Mn;0;NSM;;;;;N;;;;; +E0149;VARIATION SELECTOR-90;Mn;0;NSM;;;;;N;;;;; +E014A;VARIATION SELECTOR-91;Mn;0;NSM;;;;;N;;;;; +E014B;VARIATION SELECTOR-92;Mn;0;NSM;;;;;N;;;;; +E014C;VARIATION SELECTOR-93;Mn;0;NSM;;;;;N;;;;; +E014D;VARIATION SELECTOR-94;Mn;0;NSM;;;;;N;;;;; +E014E;VARIATION SELECTOR-95;Mn;0;NSM;;;;;N;;;;; +E014F;VARIATION SELECTOR-96;Mn;0;NSM;;;;;N;;;;; +E0150;VARIATION SELECTOR-97;Mn;0;NSM;;;;;N;;;;; +E0151;VARIATION SELECTOR-98;Mn;0;NSM;;;;;N;;;;; +E0152;VARIATION SELECTOR-99;Mn;0;NSM;;;;;N;;;;; +E0153;VARIATION SELECTOR-100;Mn;0;NSM;;;;;N;;;;; +E0154;VARIATION SELECTOR-101;Mn;0;NSM;;;;;N;;;;; +E0155;VARIATION SELECTOR-102;Mn;0;NSM;;;;;N;;;;; +E0156;VARIATION SELECTOR-103;Mn;0;NSM;;;;;N;;;;; +E0157;VARIATION SELECTOR-104;Mn;0;NSM;;;;;N;;;;; +E0158;VARIATION SELECTOR-105;Mn;0;NSM;;;;;N;;;;; +E0159;VARIATION SELECTOR-106;Mn;0;NSM;;;;;N;;;;; +E015A;VARIATION SELECTOR-107;Mn;0;NSM;;;;;N;;;;; +E015B;VARIATION SELECTOR-108;Mn;0;NSM;;;;;N;;;;; +E015C;VARIATION SELECTOR-109;Mn;0;NSM;;;;;N;;;;; +E015D;VARIATION SELECTOR-110;Mn;0;NSM;;;;;N;;;;; +E015E;VARIATION SELECTOR-111;Mn;0;NSM;;;;;N;;;;; +E015F;VARIATION SELECTOR-112;Mn;0;NSM;;;;;N;;;;; +E0160;VARIATION SELECTOR-113;Mn;0;NSM;;;;;N;;;;; +E0161;VARIATION SELECTOR-114;Mn;0;NSM;;;;;N;;;;; +E0162;VARIATION SELECTOR-115;Mn;0;NSM;;;;;N;;;;; +E0163;VARIATION SELECTOR-116;Mn;0;NSM;;;;;N;;;;; +E0164;VARIATION SELECTOR-117;Mn;0;NSM;;;;;N;;;;; +E0165;VARIATION SELECTOR-118;Mn;0;NSM;;;;;N;;;;; +E0166;VARIATION SELECTOR-119;Mn;0;NSM;;;;;N;;;;; +E0167;VARIATION SELECTOR-120;Mn;0;NSM;;;;;N;;;;; +E0168;VARIATION SELECTOR-121;Mn;0;NSM;;;;;N;;;;; +E0169;VARIATION SELECTOR-122;Mn;0;NSM;;;;;N;;;;; +E016A;VARIATION SELECTOR-123;Mn;0;NSM;;;;;N;;;;; +E016B;VARIATION SELECTOR-124;Mn;0;NSM;;;;;N;;;;; +E016C;VARIATION SELECTOR-125;Mn;0;NSM;;;;;N;;;;; +E016D;VARIATION SELECTOR-126;Mn;0;NSM;;;;;N;;;;; +E016E;VARIATION SELECTOR-127;Mn;0;NSM;;;;;N;;;;; +E016F;VARIATION SELECTOR-128;Mn;0;NSM;;;;;N;;;;; +E0170;VARIATION SELECTOR-129;Mn;0;NSM;;;;;N;;;;; +E0171;VARIATION SELECTOR-130;Mn;0;NSM;;;;;N;;;;; +E0172;VARIATION SELECTOR-131;Mn;0;NSM;;;;;N;;;;; +E0173;VARIATION SELECTOR-132;Mn;0;NSM;;;;;N;;;;; +E0174;VARIATION SELECTOR-133;Mn;0;NSM;;;;;N;;;;; +E0175;VARIATION SELECTOR-134;Mn;0;NSM;;;;;N;;;;; +E0176;VARIATION SELECTOR-135;Mn;0;NSM;;;;;N;;;;; +E0177;VARIATION SELECTOR-136;Mn;0;NSM;;;;;N;;;;; +E0178;VARIATION SELECTOR-137;Mn;0;NSM;;;;;N;;;;; +E0179;VARIATION SELECTOR-138;Mn;0;NSM;;;;;N;;;;; +E017A;VARIATION SELECTOR-139;Mn;0;NSM;;;;;N;;;;; +E017B;VARIATION SELECTOR-140;Mn;0;NSM;;;;;N;;;;; +E017C;VARIATION SELECTOR-141;Mn;0;NSM;;;;;N;;;;; +E017D;VARIATION SELECTOR-142;Mn;0;NSM;;;;;N;;;;; +E017E;VARIATION SELECTOR-143;Mn;0;NSM;;;;;N;;;;; +E017F;VARIATION SELECTOR-144;Mn;0;NSM;;;;;N;;;;; +E0180;VARIATION SELECTOR-145;Mn;0;NSM;;;;;N;;;;; +E0181;VARIATION SELECTOR-146;Mn;0;NSM;;;;;N;;;;; +E0182;VARIATION SELECTOR-147;Mn;0;NSM;;;;;N;;;;; +E0183;VARIATION SELECTOR-148;Mn;0;NSM;;;;;N;;;;; +E0184;VARIATION SELECTOR-149;Mn;0;NSM;;;;;N;;;;; +E0185;VARIATION SELECTOR-150;Mn;0;NSM;;;;;N;;;;; +E0186;VARIATION SELECTOR-151;Mn;0;NSM;;;;;N;;;;; +E0187;VARIATION SELECTOR-152;Mn;0;NSM;;;;;N;;;;; +E0188;VARIATION SELECTOR-153;Mn;0;NSM;;;;;N;;;;; +E0189;VARIATION SELECTOR-154;Mn;0;NSM;;;;;N;;;;; +E018A;VARIATION SELECTOR-155;Mn;0;NSM;;;;;N;;;;; +E018B;VARIATION SELECTOR-156;Mn;0;NSM;;;;;N;;;;; +E018C;VARIATION SELECTOR-157;Mn;0;NSM;;;;;N;;;;; +E018D;VARIATION SELECTOR-158;Mn;0;NSM;;;;;N;;;;; +E018E;VARIATION SELECTOR-159;Mn;0;NSM;;;;;N;;;;; +E018F;VARIATION SELECTOR-160;Mn;0;NSM;;;;;N;;;;; +E0190;VARIATION SELECTOR-161;Mn;0;NSM;;;;;N;;;;; +E0191;VARIATION SELECTOR-162;Mn;0;NSM;;;;;N;;;;; +E0192;VARIATION SELECTOR-163;Mn;0;NSM;;;;;N;;;;; +E0193;VARIATION SELECTOR-164;Mn;0;NSM;;;;;N;;;;; +E0194;VARIATION SELECTOR-165;Mn;0;NSM;;;;;N;;;;; +E0195;VARIATION SELECTOR-166;Mn;0;NSM;;;;;N;;;;; +E0196;VARIATION SELECTOR-167;Mn;0;NSM;;;;;N;;;;; +E0197;VARIATION SELECTOR-168;Mn;0;NSM;;;;;N;;;;; +E0198;VARIATION SELECTOR-169;Mn;0;NSM;;;;;N;;;;; +E0199;VARIATION SELECTOR-170;Mn;0;NSM;;;;;N;;;;; +E019A;VARIATION SELECTOR-171;Mn;0;NSM;;;;;N;;;;; +E019B;VARIATION SELECTOR-172;Mn;0;NSM;;;;;N;;;;; +E019C;VARIATION SELECTOR-173;Mn;0;NSM;;;;;N;;;;; +E019D;VARIATION SELECTOR-174;Mn;0;NSM;;;;;N;;;;; +E019E;VARIATION SELECTOR-175;Mn;0;NSM;;;;;N;;;;; +E019F;VARIATION SELECTOR-176;Mn;0;NSM;;;;;N;;;;; +E01A0;VARIATION SELECTOR-177;Mn;0;NSM;;;;;N;;;;; +E01A1;VARIATION SELECTOR-178;Mn;0;NSM;;;;;N;;;;; +E01A2;VARIATION SELECTOR-179;Mn;0;NSM;;;;;N;;;;; +E01A3;VARIATION SELECTOR-180;Mn;0;NSM;;;;;N;;;;; +E01A4;VARIATION SELECTOR-181;Mn;0;NSM;;;;;N;;;;; +E01A5;VARIATION SELECTOR-182;Mn;0;NSM;;;;;N;;;;; +E01A6;VARIATION SELECTOR-183;Mn;0;NSM;;;;;N;;;;; +E01A7;VARIATION SELECTOR-184;Mn;0;NSM;;;;;N;;;;; +E01A8;VARIATION SELECTOR-185;Mn;0;NSM;;;;;N;;;;; +E01A9;VARIATION SELECTOR-186;Mn;0;NSM;;;;;N;;;;; +E01AA;VARIATION SELECTOR-187;Mn;0;NSM;;;;;N;;;;; +E01AB;VARIATION SELECTOR-188;Mn;0;NSM;;;;;N;;;;; +E01AC;VARIATION SELECTOR-189;Mn;0;NSM;;;;;N;;;;; +E01AD;VARIATION SELECTOR-190;Mn;0;NSM;;;;;N;;;;; +E01AE;VARIATION SELECTOR-191;Mn;0;NSM;;;;;N;;;;; +E01AF;VARIATION SELECTOR-192;Mn;0;NSM;;;;;N;;;;; +E01B0;VARIATION SELECTOR-193;Mn;0;NSM;;;;;N;;;;; +E01B1;VARIATION SELECTOR-194;Mn;0;NSM;;;;;N;;;;; +E01B2;VARIATION SELECTOR-195;Mn;0;NSM;;;;;N;;;;; +E01B3;VARIATION SELECTOR-196;Mn;0;NSM;;;;;N;;;;; +E01B4;VARIATION SELECTOR-197;Mn;0;NSM;;;;;N;;;;; +E01B5;VARIATION SELECTOR-198;Mn;0;NSM;;;;;N;;;;; +E01B6;VARIATION SELECTOR-199;Mn;0;NSM;;;;;N;;;;; +E01B7;VARIATION SELECTOR-200;Mn;0;NSM;;;;;N;;;;; +E01B8;VARIATION SELECTOR-201;Mn;0;NSM;;;;;N;;;;; +E01B9;VARIATION SELECTOR-202;Mn;0;NSM;;;;;N;;;;; +E01BA;VARIATION SELECTOR-203;Mn;0;NSM;;;;;N;;;;; +E01BB;VARIATION SELECTOR-204;Mn;0;NSM;;;;;N;;;;; +E01BC;VARIATION SELECTOR-205;Mn;0;NSM;;;;;N;;;;; +E01BD;VARIATION SELECTOR-206;Mn;0;NSM;;;;;N;;;;; +E01BE;VARIATION SELECTOR-207;Mn;0;NSM;;;;;N;;;;; +E01BF;VARIATION SELECTOR-208;Mn;0;NSM;;;;;N;;;;; +E01C0;VARIATION SELECTOR-209;Mn;0;NSM;;;;;N;;;;; +E01C1;VARIATION SELECTOR-210;Mn;0;NSM;;;;;N;;;;; +E01C2;VARIATION SELECTOR-211;Mn;0;NSM;;;;;N;;;;; +E01C3;VARIATION SELECTOR-212;Mn;0;NSM;;;;;N;;;;; +E01C4;VARIATION SELECTOR-213;Mn;0;NSM;;;;;N;;;;; +E01C5;VARIATION SELECTOR-214;Mn;0;NSM;;;;;N;;;;; +E01C6;VARIATION SELECTOR-215;Mn;0;NSM;;;;;N;;;;; +E01C7;VARIATION SELECTOR-216;Mn;0;NSM;;;;;N;;;;; +E01C8;VARIATION SELECTOR-217;Mn;0;NSM;;;;;N;;;;; +E01C9;VARIATION SELECTOR-218;Mn;0;NSM;;;;;N;;;;; +E01CA;VARIATION SELECTOR-219;Mn;0;NSM;;;;;N;;;;; +E01CB;VARIATION SELECTOR-220;Mn;0;NSM;;;;;N;;;;; +E01CC;VARIATION SELECTOR-221;Mn;0;NSM;;;;;N;;;;; +E01CD;VARIATION SELECTOR-222;Mn;0;NSM;;;;;N;;;;; +E01CE;VARIATION SELECTOR-223;Mn;0;NSM;;;;;N;;;;; +E01CF;VARIATION SELECTOR-224;Mn;0;NSM;;;;;N;;;;; +E01D0;VARIATION SELECTOR-225;Mn;0;NSM;;;;;N;;;;; +E01D1;VARIATION SELECTOR-226;Mn;0;NSM;;;;;N;;;;; +E01D2;VARIATION SELECTOR-227;Mn;0;NSM;;;;;N;;;;; +E01D3;VARIATION SELECTOR-228;Mn;0;NSM;;;;;N;;;;; +E01D4;VARIATION SELECTOR-229;Mn;0;NSM;;;;;N;;;;; +E01D5;VARIATION SELECTOR-230;Mn;0;NSM;;;;;N;;;;; +E01D6;VARIATION SELECTOR-231;Mn;0;NSM;;;;;N;;;;; +E01D7;VARIATION SELECTOR-232;Mn;0;NSM;;;;;N;;;;; +E01D8;VARIATION SELECTOR-233;Mn;0;NSM;;;;;N;;;;; +E01D9;VARIATION SELECTOR-234;Mn;0;NSM;;;;;N;;;;; +E01DA;VARIATION SELECTOR-235;Mn;0;NSM;;;;;N;;;;; +E01DB;VARIATION SELECTOR-236;Mn;0;NSM;;;;;N;;;;; +E01DC;VARIATION SELECTOR-237;Mn;0;NSM;;;;;N;;;;; +E01DD;VARIATION SELECTOR-238;Mn;0;NSM;;;;;N;;;;; +E01DE;VARIATION SELECTOR-239;Mn;0;NSM;;;;;N;;;;; +E01DF;VARIATION SELECTOR-240;Mn;0;NSM;;;;;N;;;;; +E01E0;VARIATION SELECTOR-241;Mn;0;NSM;;;;;N;;;;; +E01E1;VARIATION SELECTOR-242;Mn;0;NSM;;;;;N;;;;; +E01E2;VARIATION SELECTOR-243;Mn;0;NSM;;;;;N;;;;; +E01E3;VARIATION SELECTOR-244;Mn;0;NSM;;;;;N;;;;; +E01E4;VARIATION SELECTOR-245;Mn;0;NSM;;;;;N;;;;; +E01E5;VARIATION SELECTOR-246;Mn;0;NSM;;;;;N;;;;; +E01E6;VARIATION SELECTOR-247;Mn;0;NSM;;;;;N;;;;; +E01E7;VARIATION SELECTOR-248;Mn;0;NSM;;;;;N;;;;; +E01E8;VARIATION SELECTOR-249;Mn;0;NSM;;;;;N;;;;; +E01E9;VARIATION SELECTOR-250;Mn;0;NSM;;;;;N;;;;; +E01EA;VARIATION SELECTOR-251;Mn;0;NSM;;;;;N;;;;; +E01EB;VARIATION SELECTOR-252;Mn;0;NSM;;;;;N;;;;; +E01EC;VARIATION SELECTOR-253;Mn;0;NSM;;;;;N;;;;; +E01ED;VARIATION SELECTOR-254;Mn;0;NSM;;;;;N;;;;; +E01EE;VARIATION SELECTOR-255;Mn;0;NSM;;;;;N;;;;; +E01EF;VARIATION SELECTOR-256;Mn;0;NSM;;;;;N;;;;; +F0000;<Plane 15 Private Use, First>;Co;0;L;;;;;N;;;;; +FFFFD;<Plane 15 Private Use, Last>;Co;0;L;;;;;N;;;;; +100000;<Plane 16 Private Use, First>;Co;0;L;;;;;N;;;;; +10FFFD;<Plane 16 Private Use, Last>;Co;0;L;;;;;N;;;;; diff --git a/src/lib/aqueue.c b/src/lib/aqueue.c new file mode 100644 index 0000000..c770bb7 --- /dev/null +++ b/src/lib/aqueue.c @@ -0,0 +1,124 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "aqueue.h" + +struct aqueue *aqueue_init(struct array *array) +{ + struct aqueue *aqueue; + + aqueue = i_new(struct aqueue, 1); + aqueue->arr = array; + aqueue->area_size = buffer_get_writable_size(aqueue->arr->buffer) / + aqueue->arr->element_size; + i_assert(aqueue->area_size > 0); + return aqueue; +} + +void aqueue_deinit(struct aqueue **_aqueue) +{ + struct aqueue *aqueue = *_aqueue; + + *_aqueue = NULL; + i_free(aqueue); +} + +static void aqueue_grow(struct aqueue *aqueue) +{ + unsigned int orig_area_size, count; + + i_assert(aqueue->full && aqueue->head == aqueue->tail); + + orig_area_size = aqueue->area_size; + (void)array_append_space_i(aqueue->arr); + aqueue->area_size = buffer_get_writable_size(aqueue->arr->buffer) / + aqueue->arr->element_size; + i_assert(orig_area_size < aqueue->area_size); + + count = I_MIN(aqueue->area_size - orig_area_size, aqueue->head); + array_copy(aqueue->arr, orig_area_size, aqueue->arr, 0, count); + if (count < aqueue->area_size - orig_area_size) + aqueue->head = orig_area_size + count; + else { + array_copy(aqueue->arr, 0, aqueue->arr, count, + aqueue->head - count); + aqueue->head -= count; + } + + i_assert(aqueue->head != aqueue->tail); + aqueue->full = FALSE; +} + +void aqueue_append(struct aqueue *aqueue, const void *data) +{ + if (aqueue->full) { + aqueue_grow(aqueue); + i_assert(!aqueue->full); + } + + array_idx_set_i(aqueue->arr, aqueue->head, data); + aqueue->head = (aqueue->head + 1) % aqueue->area_size; + aqueue->full = aqueue->head == aqueue->tail; +} + +void aqueue_delete(struct aqueue *aqueue, unsigned int n) +{ + unsigned int idx, count = aqueue_count(aqueue); + + i_assert(n < count); + + aqueue->full = FALSE; + if (n == 0) { + /* optimized deletion from tail */ + aqueue->tail = (aqueue->tail + 1) % aqueue->area_size; + return; + } + if (n == count-1) { + /* optimized deletion from head */ + aqueue->head = (aqueue->head + aqueue->area_size - 1) % + aqueue->area_size; + return; + } + + idx = aqueue_idx(aqueue, n); + if ((n < count/2 || idx > aqueue->head) && idx > aqueue->tail) { + /* move tail forward. + ..tail##idx##head.. or ##head..tail##idx## */ + array_copy(aqueue->arr, aqueue->tail + 1, + aqueue->arr, aqueue->tail, + idx - aqueue->tail); + aqueue->tail++; + i_assert(aqueue->tail < aqueue->area_size); + } else { + /* move head backward. + ..tail##idx##head.. or ##idx##head..tail## */ + i_assert(idx < aqueue->head); + array_copy(aqueue->arr, idx, + aqueue->arr, idx + 1, + aqueue->head - idx); + aqueue->head = (aqueue->head + aqueue->area_size - 1) % + aqueue->area_size; + } + i_assert(aqueue->head < aqueue->area_size && + aqueue->head != aqueue->tail); +} + +void aqueue_delete_tail(struct aqueue *aqueue) +{ + aqueue_delete(aqueue, 0); +} + +void aqueue_clear(struct aqueue *aqueue) +{ + aqueue->head = aqueue->tail = 0; + aqueue->full = FALSE; +} + +unsigned int aqueue_count(const struct aqueue *aqueue) +{ + unsigned int area_size = aqueue->area_size; + + return aqueue->full ? area_size : + (area_size - aqueue->tail + aqueue->head) % area_size; +} diff --git a/src/lib/aqueue.h b/src/lib/aqueue.h new file mode 100644 index 0000000..fffe310 --- /dev/null +++ b/src/lib/aqueue.h @@ -0,0 +1,41 @@ +#ifndef AQUEUE_H +#define AQUEUE_H + +/* Dynamically growing queue. Use the array directly to access the data, + for example: + + count = queue_count(queue); + for (i = 0; i < count; i++) { + data = array[queue_idx(i)]; + } +*/ + +struct aqueue { + struct array *arr; + unsigned int head, tail, area_size; + bool full; +}; + +struct aqueue *aqueue_init(struct array *array); +void aqueue_deinit(struct aqueue **aqueue); + +/* Append item to head */ +void aqueue_append(struct aqueue *aqueue, const void *data); +/* Delete last item from tail */ +void aqueue_delete_tail(struct aqueue *aqueue); +/* Remove item from n'th position */ +void aqueue_delete(struct aqueue *aqueue, unsigned int n); +/* Clear the entire aqueue */ +void aqueue_clear(struct aqueue *aqueue); + +/* Returns the number of items in aqueue. */ +unsigned int aqueue_count(const struct aqueue *aqueue) ATTR_PURE; + +/* Returns array index of n'th element in aqueue. */ +static inline unsigned int ATTR_PURE +aqueue_idx(const struct aqueue *aqueue, unsigned int n) +{ + return (aqueue->tail + n) % aqueue->area_size; +} + +#endif diff --git a/src/lib/array-decl.h b/src/lib/array-decl.h new file mode 100644 index 0000000..6201df9 --- /dev/null +++ b/src/lib/array-decl.h @@ -0,0 +1,26 @@ +#ifndef ARRAY_DECL_H +#define ARRAY_DECL_H + +#define ARRAY(array_type) union { struct array arr; array_type const *const *v; array_type **v_modifiable; } +#define ARRAY_INIT { { NULL, 0 } } + +#define ARRAY_DEFINE_TYPE(name, array_type) \ + union array ## __ ## name { struct array arr; array_type const *const *v; array_type **v_modifiable; } +#define ARRAY_TYPE(name) \ + union array ## __ ## name + +struct array { + buffer_t *buffer; + size_t element_size; +}; + +ARRAY_DEFINE_TYPE(string, char *); +ARRAY_DEFINE_TYPE(const_string, const char *); +ARRAY_DEFINE_TYPE(uint8_t, uint8_t); +ARRAY_DEFINE_TYPE(uint16_t, uint16_t); +ARRAY_DEFINE_TYPE(uint32_t, uint32_t); +ARRAY_DEFINE_TYPE(uint64_t, uint64_t); +ARRAY_DEFINE_TYPE(uint, unsigned int); +ARRAY_DEFINE_TYPE(void_array, void *); + +#endif diff --git a/src/lib/array.c b/src/lib/array.c new file mode 100644 index 0000000..44b91a5 --- /dev/null +++ b/src/lib/array.c @@ -0,0 +1,166 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" + + +void * +array_idx_modifiable_i(const struct array *array, unsigned int idx) +{ + i_assert(idx < array->buffer->used / array->element_size); + return PTR_OFFSET(array->buffer->data, idx * array->element_size); +} + +void *array_idx_get_space_i(struct array *array, unsigned int idx) +{ + return buffer_get_space_unsafe(array->buffer, idx * array->element_size, + array->element_size); +} + +void array_idx_set_i(struct array *array, unsigned int idx, const void *data) +{ + buffer_write(array->buffer, idx * array->element_size, + data, array->element_size); +} + +void array_idx_clear_i(struct array *array, unsigned int idx) +{ + buffer_write_zero(array->buffer, idx * array->element_size, + array->element_size); +} + +void *array_insert_space_i(struct array *array, unsigned int idx) +{ + void *data; + size_t pos; + + pos = idx * array->element_size; + buffer_copy(array->buffer, pos + array->element_size, + array->buffer, pos, SIZE_MAX); + + data = buffer_get_space_unsafe(array->buffer, pos, array->element_size); + memset(data, 0, array->element_size); + return data; +} + +bool array_cmp_i(const struct array *array1, const struct array *array2) +{ + if (!array_is_created_i(array1) || array1->buffer->used == 0) + return !array_is_created_i(array2) || array2->buffer->used == 0; + + if (!array_is_created_i(array2)) + return FALSE; + + return buffer_cmp(array1->buffer, array2->buffer); +} + +bool array_equal_fn_i(const struct array *array1, const struct array *array2, + int (*cmp)(const void *, const void*)) +{ + unsigned int count1, count2, i; + size_t size; + + if (!array_is_created_i(array1) || array1->buffer->used == 0) + return !array_is_created_i(array2) || array2->buffer->used == 0; + + if (!array_is_created_i(array2)) + return FALSE; + + count1 = array_count_i(array1); count2 = array_count_i(array2); + if (count1 != count2) + return FALSE; + + size = array1->element_size; + i_assert(size == array2->element_size); + + for (i = 0; i < count1; i++) { + if (cmp(CONST_PTR_OFFSET(array1->buffer->data, i * size), + CONST_PTR_OFFSET(array2->buffer->data, i * size)) != 0) + return FALSE; + } + return TRUE; +} + +bool array_equal_fn_ctx_i(const struct array *array1, const struct array *array2, + int (*cmp)(const void *, const void *, const void *), + const void *context) +{ + unsigned int count1, count2, i; + size_t size; + + if (!array_is_created_i(array1) || array1->buffer->used == 0) + return !array_is_created_i(array2) || array2->buffer->used == 0; + + if (!array_is_created_i(array2)) + return FALSE; + + count1 = array_count_i(array1); count2 = array_count_i(array2); + if (count1 != count2) + return FALSE; + + size = array1->element_size; + i_assert(size == array2->element_size); + + for (i = 0; i < count1; i++) { + if (cmp(CONST_PTR_OFFSET(array1->buffer->data, i * size), + CONST_PTR_OFFSET(array2->buffer->data, i * size), context) != 0) + return FALSE; + } + return TRUE; +} + +void array_reverse_i(struct array *array) +{ + const size_t element_size = array->element_size; + unsigned int i, count = array_count_i(array); + size_t size; + void *data, *tmp; + + data = buffer_get_modifiable_data(array->buffer, &size); + tmp = t_buffer_get(array->element_size); + for (i = 0; i+1 < count; i++, count--) { + memcpy(tmp, PTR_OFFSET(data, i * element_size), element_size); + memcpy(PTR_OFFSET(data, i * element_size), + PTR_OFFSET(data, (count-1) * element_size), + element_size); + memcpy(PTR_OFFSET(data, (count-1) * element_size), tmp, + element_size); + } +} + +void array_sort_i(struct array *array, int (*cmp)(const void *, const void *)) +{ + unsigned int count; + + count = array_count_i(array); + if (count == 0) + return; + qsort(buffer_get_modifiable_data(array->buffer, NULL), + count, array->element_size, cmp); +} + +void *array_bsearch_i(struct array *array, const void *key, + int (*cmp)(const void *, const void *)) +{ + unsigned int count; + + count = array_count_i(array); + return bsearch(key, array->buffer->data, + count, array->element_size, cmp); +} + +const void *array_lsearch_i(const struct array *array, const void *key, + int (*cmp)(const void *, const void *)) +{ + const void * const data = array->buffer->data; + const size_t s = array->element_size; + unsigned int idx; + + for (idx = 0; idx < array_count_i(array); idx++) { + if (cmp(key, CONST_PTR_OFFSET(data, idx * s)) == 0) { + return PTR_OFFSET(data, idx * s); + } + } + + return NULL; +} diff --git a/src/lib/array.h b/src/lib/array.h new file mode 100644 index 0000000..9349e8a --- /dev/null +++ b/src/lib/array.h @@ -0,0 +1,420 @@ +#ifndef ARRAY_H +#define ARRAY_H + +/* Array is a buffer accessible using fixed size elements. As long as the + compiler provides a typeof() operator, the array provides type safety. If + a wrong type is tried to be added to the array, or if the array's contents + are tried to be used using a wrong type, the compiler will give a warning. + + Example usage: + + struct foo { + ARRAY(struct bar) bars; + ... + }; + + i_array_init(&foo->bars, 10); + + struct bar *bar = array_idx(&foo->bars, 5); + struct baz *baz = array_idx(&foo->bars, 5); // compiler warning + + If you want to pass an array as a parameter to a function, you'll need to + create a type for the array using ARRAY_DEFINE_TYPE() and use the type in + the parameter using ARRAY_TYPE(). Any arrays that you want to be passing + around, such as structure members as in the above example, must also be + defined using ARRAY_TYPE() too, rather than ARRAY(). + + Example: + + ARRAY_DEFINE_TYPE(foo, struct foo); + void do_foo(ARRAY_TYPE(foo) *foos) { + struct foo *foo = array_idx(foos, 0); + } + struct foo_manager { + ARRAY_TYPE(foo) foos; // pedantically, ARRAY(struct foo) is a different type + }; + // ... + do_foo(&my_foo_manager->foos); // No compiler warning about mismatched types + +*/ +#include "array-decl.h" +#include "buffer.h" + +#define p_array_init(array, pool, init_count) \ + array_create(array, pool, sizeof(**(array)->v), init_count) +#define i_array_init(array, init_count) \ + p_array_init(array, default_pool, init_count) +#define t_array_init(array, init_count) \ + p_array_init(array, pool_datastack_create(), init_count) + +#ifdef HAVE_TYPEOF +# define ARRAY_TYPE_CAST_CONST(array) \ + (typeof(*(array)->v)) +# define ARRAY_TYPE_CAST_MODIFIABLE(array) \ + (typeof(*(array)->v_modifiable)) +# define ARRAY_TYPE_CHECK(array, data) \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ + **(array)->v_modifiable, *(data)) +# define ARRAY_TYPES_CHECK(array1, array2) \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ + **(array1)->v_modifiable, **(array2)->v_modifiable) + +#else +# define ARRAY_TYPE_CAST_CONST(array) +# define ARRAY_TYPE_CAST_MODIFIABLE(array) +# define ARRAY_TYPE_CHECK(array, data) 0 +# define ARRAY_TYPES_CHECK(array1, array2) 0 +#endif + +/* Usage: + ARRAY(struct foo) foo_arr; + struct foo *foo; + + array_foreach(&foo_arr, foo) { + .. + } + + Note that deleting an element while iterating will cause the iteration to + skip over the next element. So deleting a single element and breaking out + of the loop is fine, but continuing the loop is likely a bug. Use + array_foreach_reverse() instead when deleting multiple elements. +*/ +#define array_foreach(array, elem) \ + for (const void *elem ## __foreach_end = \ + (const char *)(elem = *(array)->v) + (array)->arr.buffer->used; \ + elem != elem ## __foreach_end; (elem)++) +#define array_foreach_modifiable(array, elem) \ + for (const void *elem ## _end = \ + (const char *)(elem = ARRAY_TYPE_CAST_MODIFIABLE(array) \ + buffer_get_modifiable_data((array)->arr.buffer, NULL)) + \ + (array)->arr.buffer->used; \ + elem != elem ## _end; (elem)++) + +/* Iterate the array in reverse order. */ +#define array_foreach_reverse(array, elem) \ + for (elem = CONST_PTR_OFFSET(*(array)->v, (array)->arr.buffer->used); \ + (const char *)(elem--) > (const char *)*(array)->v; ) +#define array_foreach_reverse_modifiable(array, elem) \ + for (elem = ARRAY_TYPE_CAST_MODIFIABLE(array) \ + ((char *)buffer_get_modifiable_data((array)->arr.buffer, NULL) + \ + (array)->arr.buffer->used); \ + (const char *)(elem--) > (const char *)*(array)->v; ) + +/* Usage: + ARRAY(struct foo *) foo_ptrs_arr; + struct foo *foo; + + array_foreach_elem(&foo_ptrs_arr, foo) { + .. + } */ +#define array_foreach_elem(array, elem) \ + for (const void *_foreach_end = \ + CONST_PTR_OFFSET(*(array)->v, (array)->arr.buffer->used), \ + *_foreach_ptr = CONST_PTR_OFFSET(*(array)->v, ARRAY_TYPE_CHECK(array, &elem) + \ + COMPILE_ERROR_IF_TRUE(sizeof(elem) > sizeof(void *))) \ + ; \ + (_foreach_ptr != _foreach_end && \ + (memcpy(&elem, _foreach_ptr, sizeof(elem)), TRUE)) \ + ; \ + _foreach_ptr = CONST_PTR_OFFSET(_foreach_ptr, sizeof(elem))) + + +#define array_ptr_to_idx(array, elem) \ + ((elem) - (array)->v[0]) +/* Return index of iterated element inside array_foreach() or + array_foreach_modifiable() loop. Note that this doesn't work inside + array_foreach_elem() loop. */ +#define array_foreach_idx(array, elem) \ + array_ptr_to_idx(array, elem) + +static inline void +array_create_from_buffer_i(struct array *array, buffer_t *buffer, + size_t element_size) +{ + array->buffer = buffer; + array->element_size = element_size; +} +#define array_create_from_buffer(array, buffer, element_size) \ + array_create_from_buffer_i(&(array)->arr, buffer, element_size) + +static inline void +array_create_i(struct array *array, pool_t pool, + size_t element_size, unsigned int init_count) +{ + buffer_t *buffer; + + buffer = buffer_create_dynamic_max(pool, init_count * element_size, + SIZE_MAX / element_size < UINT_MAX ? SIZE_MAX : + UINT_MAX * element_size); + array_create_from_buffer_i(array, buffer, element_size); +} +#define array_create(array, pool, element_size, init_count) \ + array_create_i(&(array)->arr, pool, element_size, init_count) + +static inline void +array_free_i(struct array *array) +{ + buffer_free(&array->buffer); +} +#define array_free(array) \ + array_free_i(&(array)->arr) + +static inline void * ATTR_WARN_UNUSED_RESULT +array_free_without_data_i(struct array *array) +{ + return buffer_free_without_data(&array->buffer); +} +#define array_free_without_data(array) \ + ARRAY_TYPE_CAST_MODIFIABLE(array)array_free_without_data_i(&(array)->arr) + +static inline bool +array_is_created_i(const struct array *array) +{ + return array->buffer != NULL; +} +#define array_is_created(array) \ + array_is_created_i(&(array)->arr) + +static inline pool_t ATTR_PURE +array_get_pool_i(struct array *array) +{ + return buffer_get_pool(array->buffer); +} +#define array_get_pool(array) \ + array_get_pool_i(&(array)->arr) + +static inline void +array_clear_i(struct array *array) +{ + buffer_set_used_size(array->buffer, 0); +} +#define array_clear(array) \ + array_clear_i(&(array)->arr) + +static inline unsigned int ATTR_PURE +array_count_i(const struct array *array) +{ + return array->buffer->used / array->element_size; +} +#define array_count(array) \ + array_count_i(&(array)->arr) +/* No need for the real count if all we're doing is comparing against 0 */ +#define array_is_empty(array) \ + ((array)->arr.buffer->used == 0) +#define array_not_empty(array) \ + ((array)->arr.buffer->used > 0) + +static inline void +array_append_i(struct array *array, const void *data, unsigned int count) +{ + buffer_append(array->buffer, data, count * array->element_size); +} + +#define array_append(array, data, count) \ + TYPE_CHECKS(void, ARRAY_TYPE_CHECK(array, data), \ + array_append_i(&(array)->arr, data, count)) + +static inline void +array_append_array_i(struct array *dest_array, const struct array *src_array) +{ + i_assert(dest_array->element_size == src_array->element_size); + buffer_append_buf(dest_array->buffer, src_array->buffer, 0, SIZE_MAX); +} +#define array_append_array(dest_array, src_array) \ + TYPE_CHECKS(void, ARRAY_TYPES_CHECK(dest_array, src_array), \ + array_append_array_i(&(dest_array)->arr, &(src_array)->arr)) + +static inline void +array_insert_i(struct array *array, unsigned int idx, + const void *data, unsigned int count) +{ + buffer_insert(array->buffer, idx * array->element_size, + data, count * array->element_size); +} + +#define array_insert(array, idx, data, count) \ + TYPE_CHECKS(void, ARRAY_TYPE_CHECK(array, data), \ + array_insert_i(&(array)->arr, idx, data, count)) + +static inline void +array_delete_i(struct array *array, unsigned int idx, unsigned int count) +{ + buffer_delete(array->buffer, idx * array->element_size, + count * array->element_size); +} +#define array_delete(array, idx, count) \ + array_delete_i(&(array)->arr, idx, count) + +static inline const void * +array_get_i(const struct array *array, unsigned int *count_r) +{ + *count_r = array_count_i(array); + return array->buffer->data; +} +#define array_get(array, count) \ + ARRAY_TYPE_CAST_CONST(array)array_get_i(&(array)->arr, count) + +/* Re: i_assert() vs. pure: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51971#c1 */ +static inline const void * ATTR_PURE +array_idx_i(const struct array *array, unsigned int idx) +{ + i_assert(idx < array->buffer->used / array->element_size); + return CONST_PTR_OFFSET(array->buffer->data, idx * array->element_size); +} + +#define array_front(array) array_idx(array, 0) +#define array_front_modifiable(array) array_idx_modifiable(array, 0) +#define array_back(array) array_idx(array, array_count(array)-1) +#define array_back_modifiable(array) array_idx_modifiable(array, array_count(array)-1) +#define array_pop_back(array) array_delete(array, array_count(array)-1, 1); +#define array_push_back(array, item) array_append(array, (item), 1) +#define array_pop_front(array) array_delete(array, 0, 1) +#define array_push_front(array, item) array_insert(array, 0, (item), 1) + +#define array_idx(array, idx) \ + ARRAY_TYPE_CAST_CONST(array)array_idx_i(&(array)->arr, idx) +/* Using *array_idx() will fail if the compiler doesn't support typeof(). + The same can be done with array_idx_elem() for arrays that have pointers. */ +#ifdef HAVE_TYPEOF +# define array_idx_elem(array, idx) \ + (TRUE ? *array_idx(array, idx) : \ + COMPILE_ERROR_IF_TRUE(sizeof(**(array)->v) != sizeof(void *))) +#else +# define array_idx_elem(array, idx) \ + (*(void **)array_idx_i(&(array)->arr, idx)) +#endif + +static inline void * +array_get_modifiable_i(struct array *array, unsigned int *count_r) +{ + *count_r = array_count_i(array); + return buffer_get_modifiable_data(array->buffer, NULL); +} +#define array_get_modifiable(array, count) \ + ARRAY_TYPE_CAST_MODIFIABLE(array) \ + array_get_modifiable_i(&(array)->arr, count) + +void * +array_idx_modifiable_i(const struct array *array, unsigned int idx) ATTR_PURE; +#define array_idx_modifiable(array, idx) \ + ARRAY_TYPE_CAST_MODIFIABLE(array) \ + array_idx_modifiable_i(&(array)->arr, idx) + +void *array_idx_get_space_i(struct array *array, unsigned int idx); +#define array_idx_get_space(array, idx) \ + ARRAY_TYPE_CAST_MODIFIABLE(array) \ + array_idx_get_space_i(&(array)->arr, idx) + +void array_idx_set_i(struct array *array, unsigned int idx, const void *data); +#define array_idx_set(array, idx, data) \ + TYPE_CHECKS(void, ARRAY_TYPE_CHECK(array, data), \ + array_idx_set_i(&(array)->arr, idx, data)) + +void array_idx_clear_i(struct array *array, unsigned int idx); +#define array_idx_clear(array, idx) \ + array_idx_clear_i(&(array)->arr, idx) + +static inline void * +array_append_space_i(struct array *array) +{ + void *data; + + data = buffer_append_space_unsafe(array->buffer, array->element_size); + memset(data, 0, array->element_size); + return data; +} +#define array_append_space(array) \ + ARRAY_TYPE_CAST_MODIFIABLE(array)array_append_space_i(&(array)->arr) +#define array_append_zero(array) \ + (void)array_append_space_i(&(array)->arr) + +void *array_insert_space_i(struct array *array, unsigned int idx); +#define array_insert_space(array, idx) \ + ARRAY_TYPE_CAST_MODIFIABLE(array) \ + array_insert_space_i(&(array)->arr, idx) + +static inline void +array_copy(struct array *dest, unsigned int dest_idx, + const struct array *src, unsigned int src_idx, unsigned int count) +{ + i_assert(dest->element_size == src->element_size); + + buffer_copy(dest->buffer, dest_idx * dest->element_size, + src->buffer, src_idx * src->element_size, + count * dest->element_size); +} + +bool array_cmp_i(const struct array *array1, + const struct array *array2) ATTR_PURE; +#define array_cmp(array1, array2) \ + array_cmp_i(&(array1)->arr, &(array2)->arr) + +/* Test equality via a comparator */ +bool array_equal_fn_i(const struct array *array1, + const struct array *array2, + int (*cmp)(const void*, const void *)) ATTR_PURE; +#define array_equal_fn(array1, array2, cmp) \ + TYPE_CHECKS(bool, \ + ARRAY_TYPES_CHECK(array1, array2) || \ + CALLBACK_TYPECHECK(cmp, int (*)(typeof(*(array1)->v), \ + typeof(*(array2)->v))), \ + array_equal_fn_i(&(array1)->arr, &(array2)->arr, \ + (int (*)(const void *, const void *))cmp)) +bool array_equal_fn_ctx_i(const struct array *array1, + const struct array *array2, + int (*cmp)(const void*, const void *, const void *), + const void *context) ATTR_PURE; +/* Same, but with a context pointer. + context can't be void* as ``const typeof(context)'' won't compile, + so ``const typeof(*context)*'' is required instead, and that requires a + complete type. */ +#define array_equal_fn_ctx(array1, array2, cmp, ctx) \ + TYPE_CHECKS(bool, \ + ARRAY_TYPES_CHECK(array1, array2) || \ + CALLBACK_TYPECHECK(cmp, int (*)(typeof(*(array1)->v), \ + typeof(*(array2)->v), \ + const typeof(*ctx)*)), \ + array_equal_fn_ctx_i(&(array1)->arr, &(array2)->arr, \ + (int (*)(const void *, const void *, const void *))cmp, ctx)) + +void array_reverse_i(struct array *array); +#define array_reverse(array) \ + array_reverse_i(&(array)->arr) + +void array_sort_i(struct array *array, int (*cmp)(const void *, const void *)); +#define array_sort(array, cmp) \ + TYPE_CHECKS(void, \ + CALLBACK_TYPECHECK(cmp, int (*)(typeof(*(array)->v), \ + typeof(*(array)->v))), \ + array_sort_i(&(array)->arr, (int (*)(const void *, const void *))cmp)) + +void *array_bsearch_i(struct array *array, const void *key, + int (*cmp)(const void *, const void *)); +#define array_bsearch(array, key, cmp) \ + TYPE_CHECKS(void *, \ + CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*key) *), \ + typeof(*(array)->v))), \ + ARRAY_TYPE_CAST_MODIFIABLE(array)array_bsearch_i(&(array)->arr, \ + (const void *)key, (int (*)(const void *, const void *))cmp)) + +/* Returns pointer to first element for which cmp(key,elem)==0, or NULL */ +const void *array_lsearch_i(const struct array *array, const void *key, + int (*cmp)(const void *, const void *)); +static inline void *array_lsearch_modifiable_i(struct array *array, const void *key, + int (*cmp)(const void *, const void *)) +{ + return (void *)array_lsearch_i(array, key, cmp); +} +#define ARRAY_LSEARCH_CALL(modifiable, array, key, cmp) \ + TYPE_CHECKS(void *, \ + CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*key) *), \ + typeof(*(array)->v))), \ + array_lsearch##modifiable##i( \ + &(array)->arr, (const void *)key, \ + (int (*)(const void *, const void *))cmp)) +#define array_lsearch(array, key, cmp) \ + ARRAY_TYPE_CAST_CONST(array)ARRAY_LSEARCH_CALL(_, array, key, cmp) +#define array_lsearch_modifiable(array, key, cmp) \ + ARRAY_TYPE_CAST_MODIFIABLE(array)ARRAY_LSEARCH_CALL(_modifiable_, array, key, cmp) + +#endif diff --git a/src/lib/askpass.c b/src/lib/askpass.c new file mode 100644 index 0000000..bb9c128 --- /dev/null +++ b/src/lib/askpass.c @@ -0,0 +1,72 @@ +/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "str.h" +#include "askpass.h" + +#include <stdio.h> +#include <termios.h> +#include <fcntl.h> +#include <unistd.h> + +static void askpass_str(const char *prompt, buffer_t *pass) +{ + struct termios old_tio, tio; + bool tty, restore_tio = FALSE; + char ch; + int fd; + + tty = isatty(STDIN_FILENO) != 0; + if (tty) { + fputs(prompt, stderr); + fflush(stderr); + + fd = open("/dev/tty", O_RDONLY); + if (fd < 0) + i_fatal("open(/dev/tty) failed: %m"); + + /* turn off echo */ + if (tcgetattr(fd, &old_tio) == 0) { + restore_tio = TRUE; + tio = old_tio; + tio.c_lflag &= ENUM_NEGATE(ECHO | ECHONL); + (void)tcsetattr(fd, TCSAFLUSH, &tio); + } + } else { + /* read it from stdin without showing a prompt */ + fd = STDIN_FILENO; + } + + /* read the password */ + while (read(fd, &ch, 1) > 0) { + if (ch == '\n' || ch == '\r') + break; + buffer_append_c(pass, ch); + } + + if (tty) { + if (restore_tio) + (void)tcsetattr(fd, TCSAFLUSH, &old_tio); + + fputs("\n", stderr); fflush(stderr); + i_close_fd(&fd); + } +} + +void askpass(const char *prompt, char *buf, size_t buf_size) +{ + buffer_t str; + + buffer_create_from_data(&str, buf, buf_size); + askpass_str(prompt, &str); + buffer_append_c(&str, '\0'); +} + +const char *t_askpass(const char *prompt) +{ + string_t *str = t_str_new(32); + + askpass_str(prompt, str); + return str_c(str); +} diff --git a/src/lib/askpass.h b/src/lib/askpass.h new file mode 100644 index 0000000..59ceb75 --- /dev/null +++ b/src/lib/askpass.h @@ -0,0 +1,7 @@ +#ifndef ASKPASS_H +#define ASKPASS_H + +void askpass(const char *prompt, char *buf, size_t buf_size); +const char *t_askpass(const char *prompt); + +#endif diff --git a/src/lib/backtrace-string.c b/src/lib/backtrace-string.c new file mode 100644 index 0000000..2ac90ec --- /dev/null +++ b/src/lib/backtrace-string.c @@ -0,0 +1,156 @@ +/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "backtrace-string.h" + +#define MAX_STACK_SIZE 30 +#define BACKTRACE_SKIP_PREFIX "backtrace_" + +#if defined(HAVE_LIBUNWIND) + +#include <libunwind.h> + +static int backtrace_append_unwind(string_t *str) +{ + size_t str_orig_size = str_len(str); + char proc_name[256]; + int ret; + unsigned int fp = 0; + unw_cursor_t c; + unw_context_t ctx; + unw_proc_info_t pip; + bool success = FALSE; + + if ((ret = unw_getcontext(&ctx)) != 0) { + str_printfa(str, "unw_getcontext() failed: %d", ret); + return -1; + } + if ((ret = unw_init_local(&c, &ctx)) != 0) { + str_printfa(str, "unw_init_local() failed: %d", ret); + return -1; + } + + do { + str_printfa(str, "#%d ", fp); + if ((ret = unw_get_proc_info(&c, &pip)) != 0) { + str_printfa(str, "[unw_get_proc_info_failed(): %d]", ret); + } else if (pip.start_ip == 0 || pip.end_ip == 0) { + str_append(str, "[no start/end information]"); + } else if ((ret = unw_get_proc_name(&c, proc_name, sizeof(proc_name), 0)) != 0 && + ret != UNW_ENOMEM) { + str_printfa(str, "[unw_get_proc_name() failed: %d]", ret); + } else if (!success && str_begins(proc_name, BACKTRACE_SKIP_PREFIX)) { + str_truncate(str, str_orig_size); + continue; + } else { + str_append_max(str, proc_name, sizeof(proc_name)); + str_printfa(str, "[0x%08zx]", pip.start_ip); + success = TRUE; + } + str_append(str, " -> "); + fp++; + } while ((ret = unw_step(&c)) > 0); + + /* remove ' -> ' */ + if (str->used > 4) + str_truncate(str, str->used - 4); + return ret == 0 && success ? 0 : -1; +} +#endif + +#if defined(HAVE_BACKTRACE_SYMBOLS) && defined(HAVE_EXECINFO_H) +/* Linux */ +#include <execinfo.h> + +static int backtrace_append_libc(string_t *str) +{ + size_t str_orig_size = str_len(str); + void *stack[MAX_STACK_SIZE]; + char **strings; + int ret, i; + + ret = backtrace(stack, N_ELEMENTS(stack)); + if (ret <= 0) + return -1; + + strings = backtrace_symbols(stack, ret); + for (i = 0; i < ret; i++) { + if (str_len(str) > str_orig_size) + str_append(str, " -> "); + + if (strings == NULL) { + /* out of memory case */ + str_printfa(str, "0x%p", stack[i]); + } else if (str_len(str) != str_orig_size || + !str_begins(strings[i], BACKTRACE_SKIP_PREFIX)) + str_append(str, strings[i]); + } + free(strings); + return 0; +} +#elif defined(HAVE_WALKCONTEXT) && defined(HAVE_UCONTEXT_H) +/* Solaris */ +#include <ucontext.h> + +struct walk_context { + string_t *str; + unsigned int pos; +}; + +static int walk_callback(uintptr_t ptr, int signo ATTR_UNUSED, + void *context) +{ + struct walk_context *ctx = context; + + if (ctx->pos > 0) + str_append(ctx->str, " -> "); + str_printfa(ctx->str, "0x%p", (void *)ptr); + ctx->pos++; + return 0; +} + +static int backtrace_append_libc(string_t *str) +{ + ucontext_t uc; + struct walk_context ctx; + + if (getcontext(&uc) < 0) + return -1; + + ctx.str = str; + ctx.pos = 0; + walkcontext(&uc, walk_callback, &ctx); + return 0; +} +#else +static int backtrace_append_libc(string_t *str ATTR_UNUSED) +{ + return -1; +} +#endif + +int backtrace_append(string_t *str) +{ +#if defined(HAVE_LIBUNWIND) + size_t orig_len = str_len(str); + if (backtrace_append_unwind(str) == 0) + return 0; + /* failed to get useful backtrace. libc's own method is likely + better. */ + str_truncate(str, orig_len); +#endif + return backtrace_append_libc(str); +} + +int backtrace_get(const char **backtrace_r) +{ + string_t *str; + + str = t_str_new(512); + if (backtrace_append(str) < 0) + return -1; + + *backtrace_r = str_c(str); + return 0; +} diff --git a/src/lib/backtrace-string.h b/src/lib/backtrace-string.h new file mode 100644 index 0000000..ef448d8 --- /dev/null +++ b/src/lib/backtrace-string.h @@ -0,0 +1,8 @@ +#ifndef BACKTRACE_STRING_H +#define BACKTRACE_STRING_H + +/* Returns 0 if ok, -1 if failure. */ +int backtrace_append(string_t *str); +int backtrace_get(const char **backtrace_r); + +#endif diff --git a/src/lib/base32.c b/src/lib/base32.c new file mode 100644 index 0000000..89ae978 --- /dev/null +++ b/src/lib/base32.c @@ -0,0 +1,324 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "base32.h" +#include "buffer.h" + +static const char b32enc[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + +static const char b32hexenc[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUV"; + +static const unsigned char b32dec[256] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 40-47 */ + 0xff, 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 48-55 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */ + 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* 64-71 */ + 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, /* 72-79 */ + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* 80-87 */ + 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, /* 88-95 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96-103 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 104-111 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112-119 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */ + + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char b32hexdec[256] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 40-47 */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 48-55 */ + 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */ + 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, /* 64-71 */ + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, /* 72-79 */ + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0xff, /* 80-87 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 88-95 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96-103 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 104-111 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112-119 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */ + + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static void +base32_encode_with_alphabet(const char *alph, + bool pad, const void *src, size_t src_size, buffer_t *dest) +{ + const unsigned char *src_c = src; + unsigned char tmp[8], endb; + size_t src_pos; + + /* [5 3][2 5 1][4 4][1 5 2][3 5] + (5)(3 2)(5)(1 4)(4 1)(5)(2 3)(5) + */ + + /* encode main part */ + for (src_pos = 0; src_pos + 4 < src_size; src_pos += 5) { + tmp[0] = alph[src_c[src_pos] >> 3]; + tmp[1] = alph[((src_c[src_pos] & 0x07) << 2) | + ((src_c[src_pos+1] >> 6) & 0x03)]; + tmp[2] = alph[((src_c[src_pos+1] >> 1) & 0x1f)]; + tmp[3] = alph[((src_c[src_pos+1] & 0x01) << 4) | + (src_c[src_pos+2] >> 4)]; + tmp[4] = alph[((src_c[src_pos+2] & 0x0f) << 1) | + (src_c[src_pos+3] >> 7)]; + tmp[5] = alph[((src_c[src_pos+3] >> 2) & 0x1f)]; + tmp[6] = alph[((src_c[src_pos+3] & 0x03) << 3) | + (src_c[src_pos+4] >> 5)]; + tmp[7] = alph[src_c[src_pos+4] & 0x1f]; + buffer_append(dest, tmp, 8); + } + + /* encode last < 5 bytes if any */ + if (src_pos < src_size) { + tmp[0] = alph[src_c[src_pos] >> 3]; + switch (src_size - src_pos) { + case 1: + tmp[1] = alph[((src_c[src_pos] & 0x07) << 2)]; + endb = 2; + break; + case 2: + tmp[1] = alph[((src_c[src_pos] & 0x07) << 2) | + ((src_c[src_pos+1] >> 6) & 0x03)]; + tmp[2] = alph[((src_c[src_pos+1] >> 1) & 0x1f)]; + tmp[3] = alph[((src_c[src_pos+1] & 0x01) << 4)]; + endb = 4; + break; + case 3: + tmp[1] = alph[((src_c[src_pos] & 0x07) << 2) | + ((src_c[src_pos+1] >> 6) & 0x03)]; + tmp[2] = alph[((src_c[src_pos+1] >> 1) & 0x1f)]; + tmp[3] = alph[((src_c[src_pos+1] & 0x01) << 4) | + (src_c[src_pos+2] >> 4)]; + tmp[4] = alph[((src_c[src_pos+2] & 0x0f) << 1)]; + endb = 5; + break; + case 4: + tmp[1] = alph[((src_c[src_pos] & 0x07) << 2) | + ((src_c[src_pos+1] >> 6) & 0x03)]; + tmp[2] = alph[((src_c[src_pos+1] >> 1) & 0x1f)]; + tmp[3] = alph[((src_c[src_pos+1] & 0x01) << 4) | + (src_c[src_pos+2] >> 4)]; + tmp[4] = alph[((src_c[src_pos+2] & 0x0f) << 1) | + (src_c[src_pos+3] >> 7)]; + tmp[5] = alph[((src_c[src_pos+3] >> 2) & 0x1f)]; + tmp[6] = alph[((src_c[src_pos+3] & 0x03) << 3)]; + endb = 7; + break; + default: + i_unreached(); + } + + /* add padding if required */ + if (pad) { + memset(&tmp[endb], '=', sizeof(tmp)-endb); + buffer_append(dest, tmp, 8); + } else { + buffer_append(dest, tmp, endb); + } + } +} + +void base32_encode(bool pad, const void *src, size_t src_size, + buffer_t *dest) +{ + base32_encode_with_alphabet(b32enc, pad, src, src_size, dest); +} + +void base32hex_encode(bool pad, const void *src, size_t src_size, + buffer_t *dest) +{ + base32_encode_with_alphabet(b32hexenc, pad, src, src_size, dest); +} + +#define IS_EMPTY(c) \ + ((c) == '\n' || (c) == '\r' || (c) == ' ' || (c) == '\t') + +static int +base32_decode_with_alphabet(const unsigned char *alph, + const void *src, size_t src_size, size_t *src_pos_r, + buffer_t *dest) +{ + const unsigned char *src_c = src; + size_t block_pos, src_pos; + unsigned char output[5], ipos, opos; + int ret = 1; + + /* (5)(3 2)(5)(1 4)(4 1)(5)(2 3)(5) + [5 3][2 5 1][4 4][1 5 2][3 5] + */ + ipos = opos = 0; + block_pos = 0; + for (src_pos = 0; src_pos < src_size; src_pos++) { + unsigned char input = alph[src_c[src_pos]]; + + if (input == 0xff) { + if (unlikely(!IS_EMPTY(src_c[src_pos]))) + break; + continue; + } + + ipos++; + switch (ipos) { + case 1: + output[0] = input << 3; + opos = 0; + break; + case 2: + output[0] |= input >> 2; + output[1] = (input & 0x03) << 6; + opos = 1; + break; + case 3: + output[1] |= input << 1; + opos = 1; + break; + case 4: + output[1] |= input >> 4; + output[2] = (input & 0x0f) << 4; + opos = 2; + break; + case 5: + output[2] |= input >> 1; + output[3] = (input & 0x01) << 7; + opos = 3; + break; + case 6: + output[3] |= input << 2; + opos = 3; + break; + case 7: + output[3] |= input >> 3; + output[4] = ((input & 0x07) << 5); + opos = 4; + break; + case 8: + output[4] |= input; + buffer_append(dest, output, 5); + ipos = 0; + opos = 0; + block_pos = src_pos; + break; + default: + i_unreached(); + } + } + + if (ipos > 0) { + for (; src_pos < src_size; src_pos++) { + if (src_c[src_pos] != '=') { + if (unlikely(!IS_EMPTY(src_c[src_pos]))) { + ret = -1; + break; + } + continue; + } + if (++ipos >= 8) { + buffer_append(dest, output, opos); + ipos = 0; + ret = 0; + src_pos++; + break; + } + } + } + + if (src_pos_r != NULL) { + if (ipos == 0) { + for (; src_pos < src_size; src_pos++) { + if (!IS_EMPTY(src_c[src_pos])) + break; + } + + *src_pos_r = src_pos; + } else { + *src_pos_r = block_pos; + } + } + return ret; +} + +int base32_decode(const void *src, size_t src_size, + size_t *src_pos_r, buffer_t *dest) +{ + return base32_decode_with_alphabet + (b32dec, src, src_size, src_pos_r, dest); +} +int base32hex_decode(const void *src, size_t src_size, + size_t *src_pos_r, buffer_t *dest) +{ + return base32_decode_with_alphabet + (b32hexdec, src, src_size, src_pos_r, dest); +} + +buffer_t *t_base32_decode_str(const char *str) +{ + buffer_t *buf; + size_t len = strlen(str); + + buf = t_buffer_create(MAX_BASE32_DECODED_SIZE(len)); + (void)base32_decode(str, len, NULL, buf); + return buf; +} + +buffer_t *t_base32hex_decode_str(const char *str) +{ + buffer_t *buf; + size_t len = strlen(str); + + buf = t_buffer_create(MAX_BASE32_DECODED_SIZE(len)); + (void)base32hex_decode(str, len, NULL, buf); + return buf; +} + +bool base32_is_valid_char(char c) +{ + return b32dec[(uint8_t)c] != 0xff; +} + +bool base32hex_is_valid_char(char c) +{ + return b32hexdec[(uint8_t)c] != 0xff; +} diff --git a/src/lib/base32.h b/src/lib/base32.h new file mode 100644 index 0000000..6a7b52a --- /dev/null +++ b/src/lib/base32.h @@ -0,0 +1,47 @@ +#ifndef BASE32_H +#define BASE32_H + +/* Translates binary data into base32 (RFC 4648, Section 6). The src must not + point to dest buffer. The pad argument determines whether output is padded + with '='. + */ +void base32_encode(bool pad, const void *src, size_t src_size, + buffer_t *dest); + +/* Translates binary data into base32hex (RFC 4648, Section 7). The src must + not point to dest buffer. The pad argument determines whether output is + padded with '='. + */ +void base32hex_encode(bool pad, const void *src, size_t src_size, + buffer_t *dest); + +/* Translates base32/base32hex data into binary and appends it to dest buffer. + dest may point to same buffer as src. Returns 1 if all ok, 0 if end of + base32 data found, -1 if data is invalid. + + Any whitespace characters are ignored. + + This function may be called multiple times for parsing the same stream. + If src_pos is non-NULL, it's updated to first non-translated character in + src. */ +int base32_decode(const void *src, size_t src_size, + size_t *src_pos_r, buffer_t *dest) ATTR_NULL(4); +int base32hex_decode(const void *src, size_t src_size, + size_t *src_pos_r, buffer_t *dest) ATTR_NULL(4); + +/* Decode given string to a buffer allocated from data stack. */ +buffer_t *t_base32_decode_str(const char *str); +buffer_t *t_base32hex_decode_str(const char *str); + +/* Returns TRUE if c is a valid base32 encoding character (excluding '=') */ +bool base32_is_valid_char(char c); +bool base32hex_is_valid_char(char c); + +/* max. buffer size required for base32_encode()/base32hex_encode() */ +#define MAX_BASE32_ENCODED_SIZE(size) \ + ((size) / 5 * 8 + 8) +/* max. buffer size required for base32_decode()/base32hex_decode() */ +#define MAX_BASE32_DECODED_SIZE(size) \ + ((size) / 8 * 5 + 5) + +#endif diff --git a/src/lib/base64.c b/src/lib/base64.c new file mode 100644 index 0000000..5e4f96a --- /dev/null +++ b/src/lib/base64.c @@ -0,0 +1,968 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "base64.h" +#include "buffer.h" + +/* + * Low-level Base64 encoder + */ + +uoff_t base64_get_full_encoded_size(struct base64_encoder *enc, uoff_t src_size) +{ + bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF); + bool no_padding = HAS_ALL_BITS(enc->flags, + BASE64_ENCODE_FLAG_NO_PADDING); + uoff_t out_size; + uoff_t newlines; + + if (src_size == 0) + return 0; + + /* calculate size of encoded data */ + out_size = MAX_BASE64_ENCODED_SIZE(src_size); + if (no_padding) { + switch (src_size % 3) { + case 0: + break; + case 1: + i_assert(out_size > 2); + out_size -= 2; + break; + case 2: + i_assert(out_size > 1); + out_size -= 1; + break; + } + } + + if (out_size > enc->max_line_len) { + /* newline between each full line */ + i_assert(enc->max_line_len > 0); + newlines = (out_size / enc->max_line_len) - 1; + /* an extra newline to separate the partial last line from the + previous full line */ + if ((out_size % enc->max_line_len) != 0) + newlines++; + + /* update size with added newlines */ + out_size += newlines * (crlf ? 2 : 1); + } + + return out_size; +} + +static size_t +base64_encode_get_out_size(struct base64_encoder *enc, size_t src_size) +{ + size_t res_size = enc->w_buf_len; + + i_assert(enc->w_buf_len <= sizeof(enc->w_buf)); + + if (src_size == 0) + return res_size; + + /* Handle sub-position */ + switch (enc->sub_pos) { + case 0: + break; + case 1: + res_size++; + src_size--; + if (src_size == 0) + return res_size; + /* fall through */ + case 2: + res_size += 2; + src_size--; + break; + default: + i_unreached(); + } + + /* We're now at a 3-byte boundary */ + if (src_size == 0) + return res_size; + + /* Calculate size we can append to the output from remaining input */ + res_size += ((src_size) / 3) * 4; + switch (src_size % 3) { + case 0: + break; + case 1: + res_size += 1; + break; + case 2: + res_size += 2; + break; + } + return res_size; +} + +size_t base64_encode_get_size(struct base64_encoder *enc, size_t src_size) +{ + bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF); + size_t out_size = base64_encode_get_out_size(enc, src_size); + + if (src_size == 0) { + /* last block */ + switch (enc->sub_pos) { + case 0: + break; + case 1: + out_size += 3; + break; + case 2: + out_size += 2; + break; + default: + i_unreached(); + } + } + + if (enc->max_line_len < SIZE_MAX) { + size_t line_part, lines; + + /* Calculate how many line endings must be added */ + i_assert(enc->max_line_len > 0); + lines = out_size / enc->max_line_len; + line_part = out_size % enc->max_line_len; + if (enc->cur_line_len > (enc->max_line_len - line_part)) + lines++; + + out_size += lines * (crlf ? 2 : 1); + } + + if (enc->pending_lf) + out_size++; + + return out_size; +} + +size_t base64_encode_get_full_space(struct base64_encoder *enc, + size_t dst_space) +{ + bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF); + bool no_padding = HAS_ALL_BITS(enc->flags, + BASE64_ENCODE_FLAG_NO_PADDING); + size_t src_space = 0; + + i_assert(enc->w_buf_len <= sizeof(enc->w_buf)); + + if (enc->max_line_len < SIZE_MAX) { + size_t max_line_space, lines, nl_space; + + /* Calculate how many line endings must be added if all space + were used. */ + i_assert(enc->max_line_len < SIZE_MAX-2); + max_line_space = enc->max_line_len + (crlf ? 2 : 1); + lines = dst_space / max_line_space; + + /* Calculate how much space is used by newline characters and + subtract this from the available space. */ + nl_space = lines * (crlf ? 2 : 1); + if (dst_space <= nl_space) + return 0; + dst_space -= nl_space; + } + + if (dst_space <= enc->w_buf_len) + return 0; + dst_space -= enc->w_buf_len; + + if (enc->pending_lf) + dst_space--; + if (dst_space == 0) + return 0; + + /* Handle sub-position */ + switch (enc->sub_pos) { + case 0: + break; + case 1: + dst_space--; + src_space++; + /* fall through */ + case 2: + if (dst_space < 2) + return src_space; + dst_space -= 2; + src_space++; + break; + default: + i_unreached(); + } + + if (dst_space == 0) + return src_space; + + src_space += dst_space / 4 * 3; + if (no_padding) { + switch (dst_space % 4) { + case 0: + case 1: + break; + case 2: + src_space += 1; + break; + case 3: + src_space += 2; + break; + } + } + + return src_space; +} + +static void +base64_encode_more_data(struct base64_encoder *enc, + const unsigned char *src_c, size_t src_size, + size_t *src_pos_r, size_t dst_avail, buffer_t *dest) +{ + const struct base64_scheme *b64 = enc->b64; + const char *b64enc = b64->encmap; + size_t res_size; + unsigned char *start, *ptr, *end; + size_t src_pos; + + i_assert(!enc->pending_lf); + + /* determine how much we can write in destination buffer */ + if (dst_avail == 0) { + *src_pos_r = 0; + return; + } + + /* pre-allocate space in the destination buffer */ + res_size = base64_encode_get_out_size(enc, src_size); + if (res_size > dst_avail) + res_size = dst_avail; + + start = buffer_append_space_unsafe(dest, res_size); + end = start + res_size; + ptr = start; + + /* write bytes not written in previous call */ + i_assert(enc->w_buf_len <= sizeof(enc->w_buf)); + if (enc->w_buf_len > res_size) { + memcpy(ptr, enc->w_buf, res_size); + ptr += res_size; + enc->w_buf_len -= res_size; + memmove(enc->w_buf, enc->w_buf + res_size, enc->w_buf_len); + } else if (enc->w_buf_len > 0) { + memcpy(ptr, enc->w_buf, enc->w_buf_len); + ptr += enc->w_buf_len; + enc->w_buf_len = 0; + } + if (ptr == end) { + *src_pos_r = 0; + return; + } + i_assert(enc->w_buf_len == 0); + i_assert(src_size != 0); + + /* Handle sub-position */ + src_pos = 0; + switch (enc->sub_pos) { + case 0: + break; + case 1: + i_assert(ptr < end); + ptr[0] = b64enc[enc->buf | (src_c[src_pos] >> 4)]; + ptr++; + enc->buf = (src_c[src_pos] & 0x0f) << 2; + src_pos++; + if (src_pos == src_size || ptr == end) { + enc->sub_pos = 2; + *src_pos_r = src_pos; + return; + } + /* fall through */ + case 2: + ptr[0] = b64enc[enc->buf | ((src_c[src_pos] & 0xc0) >> 6)]; + enc->w_buf[0] = b64enc[src_c[src_pos] & 0x3f]; + ptr++; + src_pos++; + if (ptr < end) { + ptr[0] = enc->w_buf[0]; + ptr++; + enc->w_buf_len = 0; + } else { + enc->sub_pos = 0; + enc->w_buf_len = 1; + *src_pos_r = src_pos; + return; + } + break; + default: + i_unreached(); + } + enc->sub_pos = 0; + + /* We're now at a 3-byte boundary */ + if (src_pos == src_size) { + i_assert(ptr == end); + *src_pos_r = src_pos; + return; + } + + /* Convert the bulk */ + for (; src_size - src_pos > 2 && &ptr[3] < end; + src_pos += 3, ptr += 4) { + ptr[0] = b64enc[src_c[src_pos] >> 2]; + ptr[1] = b64enc[((src_c[src_pos] & 0x03) << 4) | + (src_c[src_pos+1] >> 4)]; + ptr[2] = b64enc[((src_c[src_pos+1] & 0x0f) << 2) | + ((src_c[src_pos+2] & 0xc0) >> 6)]; + ptr[3] = b64enc[src_c[src_pos+2] & 0x3f]; + } + + /* Convert the bytes beyond the last 3-byte boundary and update state + for next call */ + switch (src_size - src_pos) { + case 0: + enc->sub_pos = 0; + enc->buf = 0; + break; + case 1: + enc->sub_pos = 1; + enc->w_buf[0] = b64enc[src_c[src_pos] >> 2]; + enc->w_buf_len = 1; + enc->buf = (src_c[src_pos] & 0x03) << 4; + src_pos++; + break; + case 2: + enc->sub_pos = 2; + enc->w_buf[0] = b64enc[src_c[src_pos] >> 2]; + enc->w_buf[1] = b64enc[((src_c[src_pos] & 0x03) << 4) | + (src_c[src_pos+1] >> 4)]; + enc->w_buf_len = 2; + enc->buf = (src_c[src_pos+1] & 0x0f) << 2; + src_pos += 2; + break; + default: + /* hit the end of the destination buffer */ + enc->sub_pos = 0; + enc->w_buf[0] = b64enc[src_c[src_pos] >> 2]; + enc->w_buf[1] = b64enc[((src_c[src_pos] & 0x03) << 4) | + (src_c[src_pos+1] >> 4)]; + enc->w_buf[2] = b64enc[((src_c[src_pos+1] & 0x0f) << 2) | + ((src_c[src_pos+2] & 0xc0) >> 6)]; + enc->w_buf[3] = b64enc[src_c[src_pos+2] & 0x3f]; + enc->w_buf_len = 4; + enc->buf = 0; + src_pos += 3; + } + + /* fill the remaining allocated space */ + i_assert(ptr <= end); + res_size = end - ptr; + i_assert(enc->w_buf_len <= sizeof(enc->w_buf)); + if (enc->w_buf_len > res_size) { + memcpy(ptr, enc->w_buf, res_size); + ptr += res_size; + enc->w_buf_len -= res_size; + memmove(enc->w_buf, enc->w_buf + res_size, enc->w_buf_len); + } else if (enc->w_buf_len > 0) { + memcpy(ptr, enc->w_buf, enc->w_buf_len); + ptr += enc->w_buf_len; + enc->w_buf_len = 0; + } + + i_assert(ptr == end); + *src_pos_r = src_pos; +} + +bool base64_encode_more(struct base64_encoder *enc, + const void *src, size_t src_size, size_t *src_pos_r, + buffer_t *dest) +{ + bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF); + const unsigned char *src_c, *src_p; + size_t src_pos, src_left; + + i_assert(!enc->finishing); + i_assert(!enc->finished); + + src_p = src_c = src; + src_left = src_size; + while (src_left > 0) { + size_t dst_avail, dst_pos, line_avail, written; + + /* determine how much we can write in destination buffer */ + dst_avail = buffer_get_avail_size(dest); + if (dst_avail == 0) + break; + + /* Emit pending newline immediately */ + if (enc->pending_lf) { + i_assert(crlf); + buffer_append_c(dest, '\n'); + enc->pending_lf = FALSE; + dst_avail--; + if (dst_avail == 0) + break; + } + + i_assert(enc->max_line_len > 0); + i_assert(enc->cur_line_len <= enc->max_line_len); + line_avail = I_MIN(enc->max_line_len - enc->cur_line_len, + dst_avail); + + if (line_avail > 0) { + dst_pos = dest->used; + base64_encode_more_data(enc, src_p, src_left, &src_pos, + line_avail, dest); + i_assert(src_pos <= src_left); + src_p += src_pos; + src_left -= src_pos; + i_assert(dest->used >= dst_pos); + written = dest->used - dst_pos; + + i_assert(written <= line_avail); + i_assert(written <= enc->max_line_len); + i_assert(enc->cur_line_len <= + (enc->max_line_len - written)); + enc->cur_line_len += written; + dst_avail -= written; + } + + if (dst_avail == 0) + break; + + if (src_left > 0 && enc->cur_line_len == enc->max_line_len) { + if (crlf) { + if (dst_avail >= 2) { + /* emit the full CRLF sequence */ + buffer_append(dest, "\r\n", 2); + } else { + /* emit CR */ + buffer_append_c(dest, '\r'); + /* remember the LF */ + enc->pending_lf = TRUE; + } + } else { + buffer_append_c(dest, '\n'); + } + enc->cur_line_len = 0; + } + } + + i_assert(src_p >= src_c); + src_pos = (src_p - src_c); + if (src_pos_r != NULL) + *src_pos_r = src_pos; + return (src_pos == src_size); +} + +bool base64_encode_finish(struct base64_encoder *enc, buffer_t *dest) +{ + const struct base64_scheme *b64 = enc->b64; + const char *b64enc = b64->encmap; + bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF); + bool padding = HAS_NO_BITS(enc->flags, BASE64_ENCODE_FLAG_NO_PADDING); + unsigned char *ptr, *end; + size_t dst_avail, line_avail, write_full, write; + unsigned int w_buf_pos = 0; + + i_assert(!enc->finished); + enc->finishing = TRUE; + + dst_avail = 0; + if (dest != NULL) + dst_avail = buffer_get_avail_size(dest); + + if (enc->w_buf_len > 0 || enc->pending_lf) { + if (dst_avail == 0) + return FALSE; + i_assert(enc->w_buf_len <= sizeof(enc->w_buf)); + } + + i_assert(enc->max_line_len > 0); + i_assert(enc->cur_line_len <= enc->max_line_len); + line_avail = enc->max_line_len - enc->cur_line_len; + + switch (enc->sub_pos) { + case 0: + break; + case 1: + i_assert(enc->w_buf_len <= (sizeof(enc->w_buf) - 3)); + enc->w_buf[enc->w_buf_len] = b64enc[enc->buf]; + enc->w_buf_len++; + if (padding) { + enc->w_buf[enc->w_buf_len + 0] = '='; + enc->w_buf[enc->w_buf_len + 1] = '='; + enc->w_buf_len += 2; + } + break; + case 2: + i_assert(enc->w_buf_len <= (sizeof(enc->w_buf) - 2)); + enc->w_buf[enc->w_buf_len] = b64enc[enc->buf]; + enc->w_buf_len++; + if (padding) { + enc->w_buf[enc->w_buf_len + 0] = '='; + enc->w_buf_len++; + } + break; + default: + i_unreached(); + } + enc->sub_pos = 0; + + write_full = write = enc->w_buf_len; + if (enc->pending_lf) + write_full++; + if (enc->max_line_len < SIZE_MAX && line_avail < write) { + unsigned int lines; + + lines = I_MAX((write - line_avail) / enc->max_line_len, 1); + write_full += lines * (crlf ? 2 : 1); + } else { + line_avail = write; + } + + if (write_full == 0) { + enc->finished = TRUE; + return TRUE; + } + + i_assert(dest != NULL); + if (write_full > dst_avail) + write_full = dst_avail; + + ptr = buffer_append_space_unsafe(dest, write_full); + end = ptr + write_full; + if (enc->pending_lf) { + ptr[0] = '\n'; + dst_avail--; + ptr++; + enc->pending_lf = FALSE; + } + if (line_avail > dst_avail) + line_avail = dst_avail; + if (line_avail > 0) { + memcpy(ptr, enc->w_buf, line_avail); + ptr += line_avail; + w_buf_pos += line_avail; + } + while (ptr < end && w_buf_pos < enc->w_buf_len) { + enc->cur_line_len = 0; + if (crlf) { + ptr[0] = '\r'; + ptr++; + if (ptr == end) { + enc->pending_lf = TRUE; + break; + } + } + ptr[0] = '\n'; + ptr++; + if (ptr == end) + break; + + write = I_MIN(enc->w_buf_len - w_buf_pos, enc->max_line_len); + write = I_MIN(write, (size_t)(end - ptr)); + memcpy(ptr, &enc->w_buf[w_buf_pos], write); + ptr += write; + w_buf_pos += write; + enc->cur_line_len += write; + i_assert(ptr <= end); + } + i_assert(ptr == end); + if (w_buf_pos < enc->w_buf_len) { + enc->w_buf_len -= w_buf_pos; + memmove(enc->w_buf, enc->w_buf + w_buf_pos, enc->w_buf_len); + return FALSE; + } + if (enc->pending_lf) + return FALSE; + enc->finished = TRUE; + return TRUE; +} + +/* + * Low-level Base64 decoder + */ + +#define IS_EMPTY(c) \ + ((c) == '\n' || (c) == '\r' || (c) == ' ' || (c) == '\t') + +static inline void +base64_skip_whitespace(struct base64_decoder *dec, const unsigned char *src_c, + size_t src_size, size_t *src_pos) +{ + if (HAS_ALL_BITS(dec->flags, BASE64_DECODE_FLAG_NO_WHITESPACE)) + return; + + /* skip any whitespace in the padding */ + while ((*src_pos) < src_size && IS_EMPTY(src_c[(*src_pos)])) + (*src_pos)++; +} + +int base64_decode_more(struct base64_decoder *dec, + const void *src, size_t src_size, size_t *src_pos_r, + buffer_t *dest) +{ + const struct base64_scheme *b64 = dec->b64; + const unsigned char *src_c = src; + bool expect_boundary = HAS_ALL_BITS( + dec->flags, BASE64_DECODE_FLAG_EXPECT_BOUNDARY); + bool no_whitespace = HAS_ALL_BITS( + dec->flags, BASE64_DECODE_FLAG_NO_WHITESPACE); + bool no_padding = HAS_ALL_BITS( + dec->flags, BASE64_DECODE_FLAG_NO_PADDING); + size_t src_pos, dst_avail; + int ret = 1; + + i_assert(!dec->finished); + i_assert(!dec->failed); + + if (dec->seen_boundary) { + /* already seen the boundary/end of base64 data */ + if (src_pos_r != NULL) + *src_pos_r = 0; + dec->failed = TRUE; + return -1; + } + + src_pos = 0; + if (dec->seen_end) { + /* skip any whitespace at the end */ + base64_skip_whitespace(dec, src_c, src_size, &src_pos); + if (src_pos_r != NULL) + *src_pos_r = src_pos; + if (src_pos < src_size) { + if (!expect_boundary) { + dec->failed = TRUE; + return -1; + } + dec->seen_boundary = TRUE; + return 0; + } + if (no_whitespace) { + dec->seen_boundary = TRUE; + return 0; + } + /* more whitespace may follow */ + return 1; + } + + if (src_size == 0) { + if (src_pos_r != NULL) + *src_pos_r = 0; + return 1; + } + + dst_avail = buffer_get_avail_size(dest); + if (dst_avail == 0) { + i_assert(src_pos_r != NULL); + *src_pos_r = 0; + return 1; + } + + for (; !dec->seen_padding && src_pos < src_size; src_pos++) { + unsigned char in = src_c[src_pos]; + unsigned char dm = b64->decmap[in]; + + if (dm == 0xff) { + if (no_whitespace) { + ret = -1; + break; + } + if (unlikely(!IS_EMPTY(in))) { + ret = -1; + break; + } + continue; + } + + if (dst_avail == 0) { + i_assert(src_pos_r != NULL); + *src_pos_r = src_pos; + return 1; + } + + switch (dec->sub_pos) { + case 0: + dec->buf = dm; + dec->sub_pos++; + break; + case 1: + dec->buf = (dec->buf << 2) | (dm >> 4); + buffer_append_c(dest, dec->buf); + dst_avail--; + dec->buf = dm; + dec->sub_pos++; + break; + case 2: + dec->buf = ((dec->buf << 4) & 0xff) | (dm >> 2); + buffer_append_c(dest, dec->buf); + dst_avail--; + dec->buf = dm; + dec->sub_pos++; + break; + case 3: + dec->buf = ((dec->buf << 6) & 0xc0) | dm; + buffer_append_c(dest, dec->buf); + dst_avail--; + dec->buf = 0; + dec->sub_pos = 0; + break; + default: + i_unreached(); + } + } + + if (dec->seen_padding) { + /* skip any whitespace in or after the padding */ + base64_skip_whitespace(dec, src_c, src_size, &src_pos); + if (src_pos == src_size) { + if (src_pos_r != NULL) + *src_pos_r = src_pos; + return 1; + } + } + + if (dec->seen_padding || ret < 0) { + /* try to parse the end (padding) of the base64 input */ + i_assert(src_pos < src_size); + + if (no_padding) { + /* no padding allowed */ + i_assert(!dec->seen_padding); + ret = -1; + } else { + switch (dec->sub_pos) { + case 0: + case 1: + /* no padding expected */ + ret = -1; + break; + case 2: + if (unlikely(src_c[src_pos] != '=')) { + /* invalid character */ + ret = -1; + break; + } + dec->seen_padding = TRUE; + dec->sub_pos++; + src_pos++; + if (src_pos == src_size) { + ret = 1; + break; + } + /* skip any whitespace in the padding */ + base64_skip_whitespace(dec, src_c, src_size, + &src_pos); + if (src_pos == src_size) { + ret = 1; + break; + } + /* fall through */ + case 3: + if (unlikely(src_c[src_pos] != '=')) { + /* invalid character */ + ret = -1; + break; + } + dec->seen_padding = TRUE; + dec->seen_end = TRUE; + dec->sub_pos = 0; + src_pos++; + /* skip any trailing whitespace */ + base64_skip_whitespace(dec, src_c, src_size, + &src_pos); + if (src_pos < src_size) { + ret = -1; + break; + } + if (no_whitespace) { + dec->seen_boundary = TRUE; + ret = 0; + } else { + /* more whitespace may follow */ + ret = 1; + } + break; + } + } + } + + if (ret < 0) { + if (!expect_boundary) { + dec->failed = TRUE; + } else { + dec->seen_boundary = TRUE; + ret = 0; + } + } + if (src_pos_r != NULL) + *src_pos_r = src_pos; + return ret; +} + +int base64_decode_finish(struct base64_decoder *dec) +{ + i_assert(!dec->finished); + dec->finished = TRUE; + + if (dec->failed) + return -1; + + if (HAS_ALL_BITS(dec->flags, + BASE64_DECODE_FLAG_NO_PADDING)) { + i_assert(!dec->seen_padding); + return 0; + } + if (HAS_ALL_BITS(dec->flags, + BASE64_DECODE_FLAG_IGNORE_PADDING)) + return 0; + return (dec->sub_pos == 0 ? 0 : -1); +} + +/* + * Generic Base64 API + */ + +buffer_t *t_base64_scheme_encode(const struct base64_scheme *b64, + enum base64_encode_flags flags, + size_t max_line_len, + const void *src, size_t src_size) +{ + buffer_t *buf; + + buf = t_buffer_create(MAX_BASE64_ENCODED_SIZE(src_size)); + base64_scheme_encode(b64, flags, max_line_len, src, src_size, buf); + return buf; +} + + +int base64_scheme_decode(const struct base64_scheme *b64, + enum base64_decode_flags flags, + const void *src, size_t src_size, buffer_t *dest) +{ + struct base64_decoder dec; + int ret; + + base64_decode_init(&dec, b64, flags); + ret = base64_decode_more(&dec, src, src_size, NULL, dest); + if (ret >= 0) + ret = base64_decode_finish(&dec); + + return ret; +} + +buffer_t *t_base64_scheme_decode(const struct base64_scheme *b64, + enum base64_decode_flags flags, + const void *src, size_t src_size) +{ + buffer_t *buf; + + buf = t_buffer_create(MAX_BASE64_DECODED_SIZE(src_size)); + (void)base64_scheme_decode(b64, flags, src, src_size, buf); + return buf; +} + +/* + * "base64" encoding scheme (RFC 4648, Section 4) + */ + +struct base64_scheme base64_scheme = { + .encmap = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', + }, + .decmap = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */ + 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, /* 40-47 */ + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, /* 48-55 */ + 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */ + 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* 64-71 */ + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /* 72-79 */ + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* 80-87 */ + 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, /* 88-95 */ + 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /* 96-103 */ + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, /* 104-111 */ + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, /* 112-119 */ + 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */ + + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, +}; + +/* + * "base64url" encoding scheme (RFC 4648, Section 5) + */ + +struct base64_scheme base64url_scheme = { + .encmap = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '-', '_', + }, + .decmap = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, /* 40-47 */ + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, /* 48-55 */ + 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */ + 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* 64-71 */ + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /* 72-79 */ + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* 80-87 */ + 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0x3f, /* 88-95 */ + 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /* 96-103 */ + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, /* 104-111 */ + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, /* 112-119 */ + 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */ + + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, +}; diff --git a/src/lib/base64.h b/src/lib/base64.h new file mode 100644 index 0000000..ec6ac17 --- /dev/null +++ b/src/lib/base64.h @@ -0,0 +1,403 @@ +#ifndef BASE64_H +#define BASE64_H + +/* + * Common Base64 + */ + +/* max. buffer size required for base64_encode() */ +#define MAX_BASE64_ENCODED_SIZE(size) \ + ((((size) + 2) / 3) * 4) +/* max. buffer size required for base64_decode() */ +#define MAX_BASE64_DECODED_SIZE(size) \ + (((size) + 3) / 4 * 3) + +struct base64_scheme { + const char encmap[64]; + const unsigned char decmap[256]; +}; + +/* + * Low-level Base64 encoder + */ + +enum base64_encode_flags { + /* Use CRLF instead of the default LF as line ending. */ + BASE64_ENCODE_FLAG_CRLF = BIT(0), + /* Encode no padding at the end of the data. */ + BASE64_ENCODE_FLAG_NO_PADDING = BIT(1), +}; + +struct base64_encoder { + const struct base64_scheme *b64; + enum base64_encode_flags flags; + size_t max_line_len; + + /* state */ + unsigned int sub_pos; + unsigned char buf; + size_t cur_line_len; + + unsigned char w_buf[10]; + unsigned int w_buf_len; + + bool pending_lf:1; + bool finishing:1; + bool finished:1; +}; + +/* Returns TRUE when base64_encode_finish() was called on this encoder. */ +static inline bool +base64_encode_is_finished(struct base64_encoder *enc) +{ + return enc->finished; +} + +/* Initialize the Base64 encoder. The b64 parameter is the definition of the + particular Base64 encoding scheme that is used. + */ +static inline void +base64_encode_init(struct base64_encoder *enc, + const struct base64_scheme *b64, + enum base64_encode_flags flags, + size_t max_line_len) +{ + i_zero(enc); + enc->b64 = b64; + enc->flags = flags; + enc->max_line_len = (max_line_len == 0 ? SIZE_MAX : max_line_len); +} + +/* Reset the Base64 encoder to its initial state. */ +static inline void +base64_encode_reset(struct base64_encoder *enc) +{ + const struct base64_scheme *b64 = enc->b64; + enum base64_encode_flags flags = enc->flags; + size_t max_line_len = enc->max_line_len; + + base64_encode_init(enc, b64, flags, max_line_len); +} + +/* Translate the size of the full encoder input to the size of the encoder + output. + */ +uoff_t base64_get_full_encoded_size(struct base64_encoder *enc, + uoff_t src_size); +/* Translate the size of the next input to the size of the output once encoded. + This yields the amount of data appended to the dest buffer by + base64_encode_more() with the indicated src_size. */ +size_t base64_encode_get_size(struct base64_encoder *enc, size_t src_size); + +/* Translate the space in the destination buffer to the number of bytes that can + be encoded at most to complete the full base64 encoding, including padding + and newlines if configured. */ +size_t base64_encode_get_full_space(struct base64_encoder *enc, + size_t dst_space); + +/* Translates binary data into some form of Base64. The src must not point to + dest buffer. Returns TRUE when all the provided data is encoded. Returns + FALSE when the space in the provided buffer is insufficient. The return value + may be ignored. If src_pos_r is non-NULL, it's updated to first + non-translated character in src. + */ +bool ATTR_NOWARN_UNUSED_RESULT +base64_encode_more(struct base64_encoder *enc, const void *src, size_t src_size, + size_t *src_pos_r, buffer_t *dest) ATTR_NULL(4); + +/* Finishes Base64 encoding. Returns TRUE when all the provided data is encoded. + Returns FALSE when the space in the provided buffer is insufficient. The + return value may be ignored. + */ +bool ATTR_NOWARN_UNUSED_RESULT +base64_encode_finish(struct base64_encoder *enc, buffer_t *dest) ATTR_NULL(2); + +/* + * Low-level Base64 decoder + */ + +enum base64_decode_flags { + /* Decode input until a boundary is reached. This boundary is a + non-Base64 input sequence that would normally trigger a decode error; + e.g., Base64 data followed by a ':'. With this flag, it is possible + to decode such a Base64 prefix. The base64_decode_finish() function + will still check that the Base64 data ends properly (padding). */ + BASE64_DECODE_FLAG_EXPECT_BOUNDARY = BIT(0), + /* Prohibit whitespace in the input. */ + BASE64_DECODE_FLAG_NO_WHITESPACE = BIT(1), + /* Require absence of padding at the end of the input. */ + BASE64_DECODE_FLAG_NO_PADDING = BIT(2), + /* Ignore padding at the end of the input. This flag is ignored when + BASE64_DECODE_FLAG_NO_PADDING is also set. If both of these flags are + absent, padding is required (the default). */ + BASE64_DECODE_FLAG_IGNORE_PADDING = BIT(3), +}; + +struct base64_decoder { + const struct base64_scheme *b64; + enum base64_decode_flags flags; + + /* state */ + unsigned int sub_pos; + unsigned char buf; + + bool seen_padding:1; + bool seen_end:1; + bool seen_boundary:1; + bool finished:1; + bool failed:1; +}; + +/* Returns TRUE when base64_decode_finish() was called on this decoder. */ +static inline bool +base64_decode_is_finished(struct base64_decoder *dec) +{ + return dec->finished; +} + +/* Initialize the Base64 decoder. The b64 parameter is the definition of the + particular Base64 encoding scheme that is expected. + */ +static inline void +base64_decode_init(struct base64_decoder *dec, + const struct base64_scheme *b64, + enum base64_decode_flags flags) +{ + i_zero(dec); + dec->b64 = b64; + dec->flags = flags; +} + +/* Reset the Base64 decoder to its initial state. */ +static inline void +base64_decode_reset(struct base64_decoder *dec) +{ + const struct base64_scheme *b64 = dec->b64; + enum base64_decode_flags flags = dec->flags; + + base64_decode_init(dec, b64, flags); +} + +/* Translates some form of Base64 data into binary and appends it to dest + buffer. dest may point to same buffer as src. Returns 1 if all ok, 0 if end + of base64 data found, -1 if data is invalid. + + By default, any CR, LF characters are ignored, as well as any whitespace. + This can be overridden using the BASE64_DECODE_FLAG_NO_WHITESPACE flag. + + If src_pos is non-NULL, it's updated to first non-translated character in + src. + */ +int base64_decode_more(struct base64_decoder *dec, + const void *src, size_t src_size, size_t *src_pos_r, + buffer_t *dest) ATTR_NULL(4); +/* Finishes Base64 decoding. This function checks whether the encoded data ends + in the proper padding. Returns 0 if all ok, and -1 if data is invalid. + */ +int base64_decode_finish(struct base64_decoder *dec); + +/* + * Generic Base64 API + */ + +/* Translates binary data into some variant of Base64. The src must not point to + dest buffer. + + The b64 parameter is the definition of the particular Base 64 encoding scheme + that is used. See below for specific functions. + */ +static inline void +base64_scheme_encode(const struct base64_scheme *b64, + enum base64_encode_flags flags, size_t max_line_len, + const void *src, size_t src_size, buffer_t *dest) +{ + struct base64_encoder enc; + + base64_encode_init(&enc, b64, flags, max_line_len); + base64_encode_more(&enc, src, src_size, NULL, dest); + base64_encode_finish(&enc, dest); +} + +buffer_t *t_base64_scheme_encode(const struct base64_scheme *b64, + enum base64_encode_flags flags, + size_t max_line_len, + const void *src, size_t src_size); + +static inline buffer_t * +t_base64_scheme_encode_str(const struct base64_scheme *b64, + enum base64_encode_flags flags, size_t max_line_len, + const char *src) +{ + return t_base64_scheme_encode(b64, flags, max_line_len, + src, strlen(src)); +} + +/* Translates some variant of Base64 data into binary and appends it to dest + buffer. dest may point to same buffer as src. Returns 1 if all ok, 0 if end + of Base64 data found, -1 if data is invalid. + + The b64 parameter is the definition of the particular Base 64 encoding scheme + that is expected. See below for specific functions. + + Any CR, LF characters are ignored, as well as whitespace at beginning or + end of line. + */ +int base64_scheme_decode(const struct base64_scheme *b64, + enum base64_decode_flags flags, + const void *src, size_t src_size, buffer_t *dest); + +/* Decode given data to a buffer allocated from data stack. + + The b64 parameter is the definition of the particular Base 64 encoding scheme + that is expected. See below for specific functions. + */ +buffer_t *t_base64_scheme_decode(const struct base64_scheme *b64, + enum base64_decode_flags flags, + const void *src, size_t src_size); +/* Decode given string to a buffer allocated from data stack. + + The b64 parameter is the definition of the particular Base 64 encoding scheme + that is expected. See below for specific functions. + */ +static inline buffer_t * +t_base64_scheme_decode_str(const struct base64_scheme *b64, + enum base64_decode_flags flags, const char *str) +{ + return t_base64_scheme_decode(b64, flags, str, strlen(str)); +} + +/* Returns TRUE if c is a valid encoding character (excluding '=') for the + provided base64 mapping table */ +static inline bool +base64_scheme_is_valid_char(const struct base64_scheme *b64, char c) +{ + return b64->decmap[(uint8_t)c] != 0xff; +} + +/* + * "base64" encoding scheme (RFC 4648, Section 4) + */ + +extern struct base64_scheme base64_scheme; + +/* Translates binary data into base64. See base64_scheme_encode(). */ +static inline void +base64_encode(const void *src, size_t src_size, buffer_t *dest) +{ + base64_scheme_encode(&base64_scheme, 0, 0, src, src_size, dest); +} + +static inline buffer_t * +t_base64_encode(enum base64_encode_flags flags, size_t max_line_len, + const void *src, size_t src_size) +{ + return t_base64_scheme_encode(&base64_scheme, flags, max_line_len, + src, src_size); +} + +static inline buffer_t * +t_base64_encode_str(enum base64_encode_flags flags, size_t max_line_len, + const char *src) +{ + return t_base64_scheme_encode(&base64_scheme, flags, max_line_len, + src, strlen(src)); +} + +/* Translates base64 data into binary and appends it to dest buffer. See + base64_scheme_decode(). + + The src_pos_r parameter is deprecated and MUST be NULL. + */ +static inline int +base64_decode(const void *src, size_t src_size, size_t *src_pos_r ATTR_UNUSED, + buffer_t *dest) ATTR_NULL(3) +{ + // NOTE: src_pos_r is deprecated here; to be removed in v2.4 */ + i_assert(src_pos_r == NULL); + + return base64_scheme_decode(&base64_scheme, 0, src, src_size, dest); +} + +/* Decode given data to a buffer allocated from data stack. */ +static inline buffer_t * +t_base64_decode(enum base64_decode_flags flags, + const void *src, size_t src_size) +{ + return t_base64_scheme_decode(&base64_scheme, flags, src, src_size); +} + +/* Decode given string to a buffer allocated from data stack. */ +static inline buffer_t *t_base64_decode_str(const char *str) +{ + return t_base64_scheme_decode_str(&base64_scheme, 0, str); +} + +/* Returns TRUE if c is a valid base64 encoding character (excluding '=') */ +static inline bool base64_is_valid_char(char c) +{ + return base64_scheme_is_valid_char(&base64_scheme, c); +} + +/* + * "base64url" encoding scheme (RFC 4648, Section 5) + */ + +extern struct base64_scheme base64url_scheme; + +/* Translates binary data into base64url. See base64_scheme_encode(). */ +static inline void +base64url_encode(enum base64_encode_flags flags, size_t max_line_len, + const void *src, size_t src_size, buffer_t *dest) +{ + base64_scheme_encode(&base64url_scheme, flags, max_line_len, + src, src_size, dest); +} + +static inline buffer_t * +t_base64url_encode(enum base64_encode_flags flags, size_t max_line_len, + const void *src, size_t src_size) +{ + return t_base64_scheme_encode(&base64url_scheme, flags, max_line_len, + src, src_size); +} + +static inline buffer_t * +t_base64url_encode_str(enum base64_encode_flags flags, size_t max_line_len, + const char *src) +{ + return t_base64_scheme_encode(&base64url_scheme, flags, max_line_len, + src, strlen(src)); +} + +/* Translates base64url data into binary and appends it to dest buffer. See + base64_scheme_decode(). */ +static inline int +base64url_decode(enum base64_decode_flags flags, + const void *src, size_t src_size, buffer_t *dest) +{ + return base64_scheme_decode(&base64url_scheme, flags, + src, src_size, dest); +} + +/* Decode given data to a buffer allocated from data stack. */ +static inline buffer_t * +t_base64url_decode(enum base64_decode_flags flags, + const void *src, size_t src_size) +{ + return t_base64_scheme_decode(&base64url_scheme, flags, src, src_size); +} + +/* Decode given string to a buffer allocated from data stack. */ +static inline buffer_t * +t_base64url_decode_str(enum base64_decode_flags flags, const char *str) +{ + return t_base64_scheme_decode_str(&base64url_scheme, flags, str); +} + +/* Returns TRUE if c is a valid base64url encoding character (excluding '=') */ +static inline bool base64url_is_valid_char(char c) +{ + return base64_scheme_is_valid_char(&base64url_scheme, c); +} + +#endif diff --git a/src/lib/bits.c b/src/lib/bits.c new file mode 100644 index 0000000..6366cf2 --- /dev/null +++ b/src/lib/bits.c @@ -0,0 +1,36 @@ +/* Copyright (c) 2001-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" + +/* + * We could use bits_required64() unconditionally, but that's unnecessary + * and way more heavy weight on 32-bit systems. + */ +#ifdef _LP64 +#define BITS_REQUIRED(x) bits_required64(x) +#else +#define BITS_REQUIRED(x) bits_required32(x) +#endif + +size_t nearest_power(size_t num) +{ + i_assert(num <= ((size_t)1 << (CHAR_BIT*sizeof(size_t) - 1))); + + if (num == 0) + return 1; + + return 1UL << BITS_REQUIRED(num - 1); +} + +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +/* Lucky you, it's all inline intrinsics */ +#else +unsigned int bits_required8(uint8_t num) +{ + int ret = 0; + if (num > 0xf) { ret += 4; num >>= 4; } + if (num > 0x3) { ret += 2; num >>= 2; } + num &= ~(num>>1); /* 3->2, else unchanged */ + return ret + num; +} +#endif diff --git a/src/lib/bits.h b/src/lib/bits.h new file mode 100644 index 0000000..2e84755 --- /dev/null +++ b/src/lib/bits.h @@ -0,0 +1,184 @@ +#ifndef BITS_H +#define BITS_H + +#define UINT64_SUM_OVERFLOWS(a, b) \ + (a > (uint64_t)-1 - b) + +#define BIT(n) (1u << (n)) + +/* These expressions make it easy to ensure that bit test expressions + are boolean in order to satisfy the in-house -Wstrict-bool. */ +/* ((val & bits) == 0) is very common */ +#define HAS_NO_BITS(val,bits) (((val) & (bits)) == 0) +/* ((val & bits) != 0) is even more common */ +/* Note - illogical behaviour if bits==0, fixing that requires potential + multiple evaluation, but it's a corner case that should never occur. */ +#define HAS_ANY_BITS(val,bits) (((val) & (bits)) != 0) +/* ((val & bits) == bits) is uncommon */ +#define HAS_ALL_BITS(val,bits) ((~(val) & (bits)) == 0) + +/* negation implemented without using the subtraction operator + ~(x - 1) = 1 + ~x these are equivalent by -(-x) == ~(~(x)) == x */ +#define UNSIGNED_MINUS(x) (1 + ~(x)) + +/* Returns x, such that x is the smallest power of 2 >= num. */ +size_t nearest_power(size_t num) ATTR_CONST; + +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + +/* Returns TRUE if 2^x=num, i.e. if num has only a single bit set to 1. */ +static inline bool ATTR_CONST +bits_is_power_of_two(uint64_t num) +{ + return __builtin_popcountll(num) == 1; +} + +static inline unsigned int ATTR_CONST +bits_required32(uint32_t num) +{ + return num == 0 ? 0 : 32 - __builtin_clz(num); +} +static inline unsigned int ATTR_CONST +bits_required8(uint8_t num) { return bits_required32(num); } + +static inline unsigned int ATTR_CONST +bits_required16(uint16_t num) { return bits_required32(num); } + +static inline unsigned int ATTR_CONST +bits_required64(uint64_t num) +{ + return num == 0 ? 0 : 64 - __builtin_clzll(num); +} + +#else + +/* Returns TRUE if 2^x=num, i.e. if num has only a single bit set to 1. */ +static inline bool ATTR_CONST +bits_is_power_of_two(uint64_t num) +{ + return num != 0 && (num & (num + ~0ULL)) == 0; +} + +unsigned int bits_required8(uint8_t num) ATTR_CONST; + +static inline +unsigned int bits_required16(uint16_t num) +{ + return (num <= 0xff) ? bits_required8(num) + : 8 + bits_required8(num >> 8); +} +static inline +unsigned int bits_required32(uint32_t num) +{ + return (num <= 0xffff) ? bits_required16(num) + : 16 + bits_required16(num >> 16); +} +static inline +unsigned int bits_required64(uint64_t num) +{ + return (num <= 0xffffffff) ? bits_required32(num) + : 32 + bits_required32(num >> 32); +} + +#endif + +static inline uint64_t ATTR_NO_SANITIZE_INTEGER + ATTR_NO_SANITIZE_IMPLICIT_CONVERSION +bits_rotl64(uint64_t num, unsigned int count) +{ + const unsigned int mask = CHAR_BIT*sizeof(num) - 1; + count &= mask; + return (num << count) | (num >> (UNSIGNED_MINUS(count) & mask)); +} + +static inline uint32_t ATTR_NO_SANITIZE_INTEGER + ATTR_NO_SANITIZE_IMPLICIT_CONVERSION +bits_rotl32(uint32_t num, unsigned int count) +{ + const unsigned int mask = CHAR_BIT*sizeof(num) - 1; + count &= mask; + return (num << count) | (num >> (UNSIGNED_MINUS(count) & mask)); +} + +static inline uint64_t ATTR_NO_SANITIZE_INTEGER + ATTR_NO_SANITIZE_IMPLICIT_CONVERSION +bits_rotr64(uint64_t num, unsigned int count) +{ + const unsigned int mask = CHAR_BIT*sizeof(num) - 1; + count &= mask; + return (num >> count) | (num << (UNSIGNED_MINUS(count) & mask)); +} + +static inline uint32_t ATTR_NO_SANITIZE_INTEGER + ATTR_NO_SANITIZE_IMPLICIT_CONVERSION +bits_rotr32(uint32_t num, unsigned int count) +{ + const unsigned int mask = CHAR_BIT*sizeof(num) - 1; + count &= mask; + return (num >> count) | (num << (UNSIGNED_MINUS(count) & mask)); +} + +/* These functions look too big to be inline, but in almost all expected + uses, 'fracbits' will be a compile-time constant, and most of the + expressions will simplify greatly. +*/ + +/* Perform a piecewise-linear approximation to a log2, with fracbits "fractional" bits. + Best explained with examples: + With 2 fractional bits splitting each power of 2 into 4 bands: + 00, 01, 10, 11 -> 00, 01, 10, 11 (small corner cases) + 100, 101, 110, 111 -> 100, 101, 110, 111 ([4-8) split into 4 bands) + 1000, 1001, 1010, 1011 -> 1000, 1000, 1001, 1001 ([8-15) split ... + 1100, 1101, 1110, 1111 -> 1010, 1010, 1011, 1011 ... into 4 bands) + [16..31) -> 11bb + [32..63) -> 100bb + [64..127) -> 101bb + [128..255) -> 110bb + e.g. 236 = 11101100 -> ((8-2)<<2 == 11000) + (111.....>> 5 == 111) - 100 == 11011 + */ +static inline unsigned int ATTR_CONST +bits_fraclog(unsigned int val, unsigned int fracbits) +{ + unsigned bits = bits_required32(val); + if (bits <= fracbits + 1) + return val; + + unsigned int bandnum = bits - fracbits; + unsigned int bandstart = bandnum << fracbits; + unsigned int fracoffsbad = val >> (bandnum - 1); /* has leading 1 still */ + unsigned int bucket = bandstart + fracoffsbad - BIT(fracbits); + return bucket; +} +static inline unsigned int ATTR_CONST +bits_fraclog_bucket_start(unsigned int bucket, unsigned int fracbits) +{ + unsigned int bandnum = bucket >> fracbits; + if (bandnum <= 1) + return bucket; + if (fracbits == 0) + return BIT(bucket - 1); + unsigned int fracoffs = bucket & (BIT(fracbits)-1); + unsigned int fracoffs1 = BIT(fracbits) + fracoffs; + unsigned int bandstart = fracoffs1 << (bandnum - 1); + return bandstart; +} +static inline unsigned int ATTR_CONST ATTR_NO_SANITIZE_INTEGER + ATTR_NO_SANITIZE_IMPLICIT_CONVERSION +bits_fraclog_bucket_end(unsigned int bucket, unsigned int fracbits) +{ + unsigned int bandnum = bucket >> fracbits; + if (bandnum <= 1) + return bucket; + if (fracbits == 0) + return BIT(bucket - 1) * 2 - 1; + unsigned int fracoffs = bucket & (BIT(fracbits)-1); + unsigned int nextfracoffs1 = 1 + BIT(fracbits) + fracoffs; + unsigned int nextbandstart = nextfracoffs1 << (bandnum - 1); + return nextbandstart - 1; +} +/* UNSAFE: multiple use of parameter (but expecting a constant in reality). + But a macro as it's most likely to be used to declare an array size. +*/ +#define BITS_FRACLOG_BUCKETS(bits) ((33u - (bits)) << (bits)) + +#endif diff --git a/src/lib/bsearch-insert-pos.c b/src/lib/bsearch-insert-pos.c new file mode 100644 index 0000000..dc92f47 --- /dev/null +++ b/src/lib/bsearch-insert-pos.c @@ -0,0 +1,48 @@ +/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "bsearch-insert-pos.h" + +#undef bsearch_insert_pos +bool bsearch_insert_pos(const void *key, const void *base, unsigned int nmemb, + size_t size, int (*cmp)(const void *, const void *), + unsigned int *idx_r) +{ + const void *p; + unsigned int idx, left_idx, right_idx; + int ret; + + i_assert(nmemb < INT_MAX); + + idx = 0; left_idx = 0; right_idx = nmemb; + while (left_idx < right_idx) { + idx = (left_idx + right_idx) / 2; + + p = CONST_PTR_OFFSET(base, idx * size); + ret = cmp(key, p); + if (ret > 0) + left_idx = idx+1; + else if (ret < 0) + right_idx = idx; + else { + *idx_r = idx; + return TRUE; + } + } + + if (left_idx > idx) + idx++; + + *idx_r = idx; + return FALSE; +} + +bool array_bsearch_insert_pos_i(const struct array *array, const void *key, + int (*cmp)(const void *, const void *), + unsigned int *idx_r) +{ + return bsearch_insert_pos(key, array->buffer->data, + array_count_i(array), + array->element_size, cmp, idx_r); +} diff --git a/src/lib/bsearch-insert-pos.h b/src/lib/bsearch-insert-pos.h new file mode 100644 index 0000000..320182b --- /dev/null +++ b/src/lib/bsearch-insert-pos.h @@ -0,0 +1,52 @@ +#ifndef BSEARCH_INSERT_POS_H +#define BSEARCH_INSERT_POS_H + +/* Binary search template - getdata must be the name of a pure function + or a function-like macro that takes the two obvious parameters. */ +#define BINARY_NUMERIC_SEARCH(getdata, data, count, value, idx_r) \ + unsigned int idx, left_idx, right_idx; \ + \ + i_assert((count) < INT_MAX); \ + left_idx = 0; right_idx = (count); \ + while (left_idx < right_idx) { \ + idx = (left_idx + right_idx) / 2; \ + \ + if (getdata(data, idx) < (value)) \ + left_idx = idx+1; \ + else if (getdata(data, idx) > (value))\ + right_idx = idx; \ + else { \ + *(idx_r) = idx; \ + return TRUE; \ + } \ + } \ + return FALSE + +#define BINARY_SEARCH_ARRAY_GET(array, index) ((array)[(index)]) +#define BINARY_NUMBER_SEARCH(data, count, value, idx_r) \ + BINARY_NUMERIC_SEARCH(BINARY_SEARCH_ARRAY_GET, data, count, value, idx_r); + +/* If key is found, returns TRUE and sets idx_r to the position where the key + was found. If key isn't found, returns FALSE and sets idx_r to the position + where the key should be inserted. */ +bool ATTR_NOWARN_UNUSED_RESULT +bsearch_insert_pos(const void *key, const void *base, unsigned int nmemb, + size_t size, int (*cmp)(const void *, const void *), + unsigned int *idx_r); +#define bsearch_insert_pos(key, base, nmemb, size, cmp, idx_r) \ + bsearch_insert_pos(key, base, nmemb, size - \ + CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*key) *), \ + typeof(const typeof(*base) *))), \ + (int (*)(const void *, const void *))cmp, idx_r) + +bool ATTR_NOWARN_UNUSED_RESULT +array_bsearch_insert_pos_i(const struct array *array, const void *key, + int (*cmp)(const void *, const void *), + unsigned int *idx_r); +#define array_bsearch_insert_pos(array, key, cmp, idx_r) \ + array_bsearch_insert_pos_i(&(array)->arr - \ + CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*key) *), \ + typeof(*(array)->v))), \ + (const void *)key, (int (*)(const void *, const void *))cmp, idx_r) + +#endif diff --git a/src/lib/buffer-istream.c b/src/lib/buffer-istream.c new file mode 100644 index 0000000..3898fd0 --- /dev/null +++ b/src/lib/buffer-istream.c @@ -0,0 +1,49 @@ +/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "eacces-error.h" +#include "istream.h" + +enum buffer_append_result +buffer_append_full_istream(buffer_t *buf, struct istream *is, size_t max_read_size, + const char **error_r) +{ + const unsigned char *data; + size_t size; + ssize_t ret; + + while ((ret = i_stream_read_more(is, &data, &size)) > 0) { + if (max_read_size == 0) + return BUFFER_APPEND_READ_MAX_SIZE; + size = I_MIN(max_read_size, size); + buffer_append(buf, data, size); + i_stream_skip(is, size); + max_read_size -= size; + } + + if (ret == 0) + return BUFFER_APPEND_READ_MORE; + + i_assert(is->eof); + + if (is->stream_errno != 0) { + *error_r = i_stream_get_error(is); + return BUFFER_APPEND_READ_ERROR; + } + return BUFFER_APPEND_OK; +} + +enum buffer_append_result +buffer_append_full_file(buffer_t *buf, const char *file, size_t max_read_size, + const char **error_r) +{ + struct istream *is = i_stream_create_file(file, IO_BLOCK_SIZE); + enum buffer_append_result res = + buffer_append_full_istream(buf, is, max_read_size, error_r); + if (is->stream_errno == EACCES) + *error_r = eacces_error_get("open", file); + i_stream_unref(&is); + i_assert(res != BUFFER_APPEND_READ_MORE); + return res; +} diff --git a/src/lib/buffer.c b/src/lib/buffer.c new file mode 100644 index 0000000..64255b5 --- /dev/null +++ b/src/lib/buffer.c @@ -0,0 +1,495 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +/* @UNSAFE: whole file */ + +#include "lib.h" +#include "buffer.h" + +struct real_buffer { + union { + struct buffer buf; + struct { + /* public: */ + const void *r_buffer; + size_t used; + /* private: */ + unsigned char *w_buffer; + size_t dirty, alloc, writable_size, max_size; + + pool_t pool; + + bool alloced:1; + bool dynamic:1; + }; + }; +}; +typedef int buffer_check_sizes[COMPILE_ERROR_IF_TRUE(sizeof(struct real_buffer) > sizeof(buffer_t)) ?1:1]; + +static void buffer_alloc(struct real_buffer *buf, size_t size) +{ + i_assert(buf->w_buffer == NULL || buf->alloced); + + if (size == buf->alloc) + return; + + i_assert(size > buf->alloc); + + if (buf->w_buffer == NULL) + buf->w_buffer = p_malloc(buf->pool, size); + else + buf->w_buffer = p_realloc(buf->pool, buf->w_buffer, buf->alloc, size); + buf->alloc = size; + buf->writable_size = size-1; /* -1 for str_c() NUL */ + + buf->r_buffer = buf->w_buffer; + buf->alloced = TRUE; +} + +static inline void +buffer_check_limits(struct real_buffer *buf, size_t pos, size_t data_size) +{ + size_t new_size; + + if (unlikely(buf->max_size - pos < data_size)) + i_panic("Buffer write out of range (%zu + %zu)", pos, data_size); + + new_size = pos + data_size; + + if (new_size > buf->used && buf->used < buf->dirty) { + /* clear used..dirty area */ + size_t max = I_MIN(I_MIN(buf->alloc, buf->dirty), new_size); + + memset(buf->w_buffer + buf->used, 0, max - buf->used); + } + + /* Use buf->writable_size instead of buf->alloc to always keep +1 byte + available in case str_c() is called for this buffer. This is mainly + for cases where the buffer is allocated from data stack, and str_c() + is called in a separate stack frame. */ + if (new_size > buf->writable_size) { + if (unlikely(!buf->dynamic)) { + i_panic("Buffer full (%zu > %zu, pool %s)", + pos + data_size, buf->alloc, + buf->pool == NULL ? "<none>" : + pool_get_name(buf->pool)); + } + + size_t new_alloc_size = + pool_get_exp_grown_size(buf->pool, buf->alloc, + new_size + 1); + if (new_alloc_size > buf->max_size) { + /* limit to max_size, but do include +1 for + str_c() NUL */ + new_alloc_size = buf->max_size + 1; + } + buffer_alloc(buf, new_alloc_size); + } +#if 0 + else if (new_size > buf->used && buf->alloced && + !buf->pool->alloconly_pool && !buf->pool->datastack_pool) { + void *new_buf; + + /* buffer's size increased: move the buffer's memory elsewhere. + this should help catch bugs where old pointers are tried to + be used to access the buffer's memory */ + new_buf = p_malloc(buf->pool, buf->alloc); + memcpy(new_buf, buf->w_buffer, buf->alloc); + p_free(buf->pool, buf->w_buffer); + + buf->w_buffer = new_buf; + buf->r_buffer = new_buf; + } +#endif + + if (new_size > buf->used) + buf->used = new_size; + i_assert(buf->used <= buf->alloc); + i_assert(buf->w_buffer != NULL); +} + +static inline void +buffer_check_append_limits(struct real_buffer *buf, size_t data_size) +{ + /* Fast path: See if data to be appended fits into allocated buffer. + If it does, we don't even need to memset() the dirty buffer since + it's going to be filled with the newly appended data. */ + if (buf->writable_size - buf->used < data_size) + buffer_check_limits(buf, buf->used, data_size); + else + buf->used += data_size; +} + +#undef buffer_create_from_data +void buffer_create_from_data(buffer_t *buffer, void *data, size_t size) +{ + struct real_buffer *buf; + + i_assert(sizeof(*buffer) >= sizeof(struct real_buffer)); + + buf = container_of(buffer, struct real_buffer, buf); + i_zero(buf); + buf->alloc = buf->writable_size = buf->max_size = size; + buf->r_buffer = buf->w_buffer = data; + /* clear the whole memory area. unnecessary usually, but if the + buffer is used by e.g. str_c() it tries to access uninitialized + memory */ + memset(data, 0, size); +} + +#undef buffer_create_from_const_data +void buffer_create_from_const_data(buffer_t *buffer, + const void *data, size_t size) +{ + struct real_buffer *buf; + + i_assert(sizeof(*buffer) >= sizeof(struct real_buffer)); + + buf = container_of(buffer, struct real_buffer, buf); + i_zero(buf); + + buf->used = buf->alloc = buf->writable_size = buf->max_size = size; + buf->r_buffer = data; + i_assert(buf->w_buffer == NULL); +} + +buffer_t *buffer_create_dynamic(pool_t pool, size_t init_size) +{ + return buffer_create_dynamic_max(pool, init_size, SIZE_MAX); +} + +buffer_t *buffer_create_dynamic_max(pool_t pool, size_t init_size, + size_t max_size) +{ + struct real_buffer *buf; + +#ifdef DEBUG + /* we increment this by 1 later on, so if it's SIZE_MAX + it turns into 0 and hides a potential bug. + + Too scary to use in production for now, though. This + can change in future. */ + i_assert(init_size < SIZE_MAX); +#endif + + buf = p_new(pool, struct real_buffer, 1); + buf->pool = pool; + buf->dynamic = TRUE; + buf->max_size = max_size; + /* buffer_alloc() reserves +1 for str_c() NIL, so add +1 here to + init_size so we can actually write that much to the buffer without + realloc */ + buffer_alloc(buf, init_size+1); + return &buf->buf; +} + +void buffer_free(buffer_t **_buf) +{ + if (*_buf == NULL) + return; + struct real_buffer *buf = container_of(*_buf, struct real_buffer, buf); + + *_buf = NULL; + if (buf->alloced) + p_free(buf->pool, buf->w_buffer); + if (buf->pool != NULL) + p_free(buf->pool, buf); +} + +void *buffer_free_without_data(buffer_t **_buf) +{ + struct real_buffer *buf = container_of(*_buf, struct real_buffer, buf); + void *data; + + *_buf = NULL; + + data = buf->w_buffer; + p_free(buf->pool, buf); + return data; +} + +pool_t buffer_get_pool(const buffer_t *_buf) +{ + const struct real_buffer *buf = + container_of(_buf, const struct real_buffer, buf); + + return buf->pool; +} + +void buffer_write(buffer_t *_buf, size_t pos, + const void *data, size_t data_size) +{ + struct real_buffer *buf = container_of(_buf, struct real_buffer, buf); + + buffer_check_limits(buf, pos, data_size); + if (data_size > 0) + memcpy(buf->w_buffer + pos, data, data_size); +} + +void buffer_append(buffer_t *_buf, const void *data, size_t data_size) +{ + struct real_buffer *buf = container_of(_buf, struct real_buffer, buf); + + if (data_size > 0) { + size_t pos = buf->used; + buffer_check_append_limits(buf, data_size); + memcpy(buf->w_buffer + pos, data, data_size); + } +} + +void buffer_append_c(buffer_t *_buf, unsigned char chr) +{ + struct real_buffer *buf = container_of(_buf, struct real_buffer, buf); + size_t pos = buf->used; + + buffer_check_append_limits(buf, 1); + buf->w_buffer[pos] = chr; +} + +void buffer_insert(buffer_t *_buf, size_t pos, + const void *data, size_t data_size) +{ + struct real_buffer *buf = container_of(_buf, struct real_buffer, buf); + + if (pos >= buf->used) + buffer_write(_buf, pos, data, data_size); + else { + buffer_copy(_buf, pos + data_size, _buf, pos, SIZE_MAX); + memcpy(buf->w_buffer + pos, data, data_size); + } +} + +void buffer_delete(buffer_t *_buf, size_t pos, size_t size) +{ + struct real_buffer *buf = container_of(_buf, struct real_buffer, buf); + size_t end_size; + + if (pos >= buf->used) + return; + end_size = buf->used - pos; + + if (size < end_size) { + /* delete from between */ + end_size -= size; + memmove(buf->w_buffer + pos, + buf->w_buffer + pos + size, end_size); + } else { + /* delete the rest of the buffer */ + end_size = 0; + } + + buffer_set_used_size(_buf, pos + end_size); +} + +void buffer_replace(buffer_t *_buf, size_t pos, size_t size, + const void *data, size_t data_size) +{ + struct real_buffer *buf = container_of(_buf, struct real_buffer, buf); + size_t end_size; + + if (pos >= buf->used) { + buffer_write(_buf, pos, data, data_size); + return; + } + end_size = buf->used - pos; + + if (size < end_size) { + end_size -= size; + if (data_size == 0) { + /* delete from between */ + memmove(buf->w_buffer + pos, + buf->w_buffer + pos + size, end_size); + } else { + /* insert */ + buffer_copy(_buf, pos + data_size, _buf, pos + size, + SIZE_MAX); + memcpy(buf->w_buffer + pos, data, data_size); + } + } else { + /* overwrite the end */ + end_size = 0; + buffer_write(_buf, pos, data, data_size); + } + + buffer_set_used_size(_buf, pos + data_size + end_size); +} + + +void buffer_write_zero(buffer_t *_buf, size_t pos, size_t data_size) +{ + struct real_buffer *buf = container_of(_buf, struct real_buffer, buf); + + buffer_check_limits(buf, pos, data_size); + memset(buf->w_buffer + pos, 0, data_size); +} + +void buffer_append_zero(buffer_t *_buf, size_t data_size) +{ + struct real_buffer *buf = container_of(_buf, struct real_buffer, buf); + + /* NOTE: When appending it's enough to check that the limits are + valid, because the data is already guaranteed to be zero-filled. */ + buffer_check_limits(buf, buf->used, data_size); +} + +void buffer_insert_zero(buffer_t *_buf, size_t pos, size_t data_size) +{ + struct real_buffer *buf = container_of(_buf, struct real_buffer, buf); + + if (pos >= buf->used) + buffer_write_zero(_buf, pos, data_size); + else { + buffer_copy(_buf, pos + data_size, _buf, pos, SIZE_MAX); + memset(buf->w_buffer + pos, 0, data_size); + } +} + +void buffer_copy(buffer_t *_dest, size_t dest_pos, + const buffer_t *_src, size_t src_pos, size_t copy_size) +{ + struct real_buffer *dest = container_of(_dest, struct real_buffer, buf); + const struct real_buffer *src = + container_of(_src, const struct real_buffer, buf); + size_t max_size; + + i_assert(src_pos <= src->used); + + max_size = src->used - src_pos; + if (copy_size > max_size) + copy_size = max_size; + + buffer_check_limits(dest, dest_pos, copy_size); + i_assert(src->r_buffer != NULL); + + if (src == dest) { + memmove(dest->w_buffer + dest_pos, + CONST_PTR_OFFSET(src->r_buffer, src_pos), copy_size); + } else { + memcpy(dest->w_buffer + dest_pos, + CONST_PTR_OFFSET(src->r_buffer, src_pos), copy_size); + } +} + +void buffer_append_buf(buffer_t *dest, const buffer_t *src, + size_t src_pos, size_t copy_size) +{ + buffer_copy(dest, dest->used, src, src_pos, copy_size); +} + +void *buffer_get_space_unsafe(buffer_t *_buf, size_t pos, size_t size) +{ + struct real_buffer *buf = container_of(_buf, struct real_buffer, buf); + + buffer_check_limits(buf, pos, size); + return buf->w_buffer + pos; +} + +void *buffer_append_space_unsafe(buffer_t *buf, size_t size) +{ + /* NOTE: can't use buffer_check_append_limits() here because it doesn't + guarantee that the buffer is zero-filled. */ + return buffer_get_space_unsafe(buf, buf->used, size); +} + +void *buffer_get_modifiable_data(const buffer_t *_buf, size_t *used_size_r) +{ + const struct real_buffer *buf = + container_of(_buf, const struct real_buffer, buf); + + if (used_size_r != NULL) + *used_size_r = buf->used; + i_assert(buf->used == 0 || buf->w_buffer != NULL); + return buf->w_buffer; +} + +void buffer_set_used_size(buffer_t *_buf, size_t used_size) +{ + struct real_buffer *buf = container_of(_buf, struct real_buffer, buf); + + i_assert(used_size <= buf->alloc); + + if (buf->used > buf->dirty) + buf->dirty = buf->used; + + buf->used = used_size; +} + +size_t buffer_get_size(const buffer_t *_buf) +{ + const struct real_buffer *buf = + container_of(_buf, const struct real_buffer, buf); + + return buf->alloc; +} + +size_t buffer_get_writable_size(const buffer_t *_buf) +{ + const struct real_buffer *buf = + container_of(_buf, const struct real_buffer, buf); + + /* Use buf->writable_size instead of buf->alloc to reserve +1 for + str_c() NUL in buffer_check_limits(). Otherwise the caller might + increase the buffer's alloc size unnecessarily when it just wants + to access the entire buffer. */ + return buf->writable_size; +} + +size_t buffer_get_avail_size(const buffer_t *_buf) +{ + const struct real_buffer *buf = + container_of(_buf, const struct real_buffer, buf); + + i_assert(buf->alloc >= buf->used); + return ((buf->dynamic ? SIZE_MAX : buf->alloc) - buf->used); +} + +bool buffer_cmp(const buffer_t *buf1, const buffer_t *buf2) +{ + if (buf1->used != buf2->used) + return FALSE; + if (buf1->used == 0) + return TRUE; + + return memcmp(buf1->data, buf2->data, buf1->used) == 0; +} + +void buffer_verify_pool(buffer_t *_buf) +{ + const struct real_buffer *buf = + container_of(_buf, struct real_buffer, buf); + void *ret; + + if (buf->pool != NULL && buf->pool->datastack_pool && buf->alloc > 0) { + /* this doesn't really do anything except verify the + stack frame */ + ret = p_realloc(buf->pool, buf->w_buffer, + buf->alloc, buf->alloc); + i_assert(ret == buf->w_buffer); + } +} + +void ATTR_NO_SANITIZE_IMPLICIT_CONVERSION + ATTR_NO_SANITIZE_INTEGER +buffer_truncate_rshift_bits(buffer_t *buf, size_t bits) +{ + /* no-op if it's shorten than bits in any case.. */ + if (buf->used * 8 < bits) return; + + if (bits > 0) { + /* truncate it to closest byte boundary */ + size_t bytes = ((bits + 7) & ~(size_t)7) / 8; + /* remaining bits */ + bits = bits % 8; + buffer_set_used_size(buf, I_MIN(bytes, buf->used)); + unsigned char *ptr = buffer_get_modifiable_data(buf, &bytes); + /* right shift over byte array */ + if (bits > 0) { + for(size_t i=bytes-1;i>0;i--) + ptr[i] = (ptr[i]>>(8-bits)) + + ((ptr[i-1]&(0xff>>(bits)))<<bits); + ptr[0] = ptr[0]>>(8-bits); + } + } else { + buffer_set_used_size(buf, 0); + } +} + diff --git a/src/lib/buffer.h b/src/lib/buffer.h new file mode 100644 index 0000000..902ec88 --- /dev/null +++ b/src/lib/buffer.h @@ -0,0 +1,199 @@ +#ifndef BUFFER_H +#define BUFFER_H + +struct buffer { + union { + struct { + const void *data; + const size_t used; + }; + void *priv[9]; + }; +}; + +/* WARNING: Be careful with functions that return pointers to data. + With dynamic buffers they are valid only as long as buffer is not + realloc()ed. You shouldn't rely on it being valid if you have modified + buffer in any way. */ + +/* Create a modifiable buffer from given data. Writes past this size will + i_panic(). */ +void buffer_create_from_data(buffer_t *buffer, void *data, size_t size); +/* Create a non-modifiable buffer from given data. */ +void buffer_create_from_const_data(buffer_t *buffer, + const void *data, size_t size); +#define buffer_create_from_data(b,d,s) \ + TYPE_CHECKS(void, \ + COMPILE_ERROR_IF_TRUE(__builtin_object_size((d),1) < ((s)>0?(s):1)), \ + buffer_create_from_data((b), (d), (s))) +#define buffer_create_from_const_data(b,d,s) \ + TYPE_CHECKS(void, \ + COMPILE_ERROR_IF_TRUE(__builtin_object_size((d),1) < ((s)>0?(s):1)), \ + buffer_create_from_const_data((b), (d), (s))) + +/* Creates a dynamically growing buffer. Whenever write would exceed the + current size it's grown. */ +buffer_t *buffer_create_dynamic(pool_t pool, size_t init_size); +/* Create a dynamically growing buffer with a maximum size. Writes past the + maximum size will i_panic(). Internally allow it to grow max_size+1 so + str_c() NUL can be used. */ +buffer_t *buffer_create_dynamic_max(pool_t pool, size_t init_size, + size_t max_size); + +#define t_buffer_create(init_size) \ + buffer_create_dynamic(pool_datastack_create(), (init_size)) + +/* Free the memory used by buffer. Not needed if the memory is free'd + directly from the memory pool. */ +void buffer_free(buffer_t **buf); +/* Free the memory used by buffer structure, but return the buffer data + unfree'd. */ +void *buffer_free_without_data(buffer_t **buf); + +/* Returns the pool buffer was created with. */ +pool_t buffer_get_pool(const buffer_t *buf) ATTR_PURE; + +/* Write data to buffer at specified position. If pos is beyond the buffer's + current size, it is zero-filled up to that point (even if data_size==0). */ +void buffer_write(buffer_t *buf, size_t pos, + const void *data, size_t data_size); +/* Append data to buffer. */ +void buffer_append(buffer_t *buf, const void *data, size_t data_size); +/* Append character to buffer. */ +void buffer_append_c(buffer_t *buf, unsigned char chr); + +/* Insert the provided data into the buffer at position pos. If pos points past + the current buffer size, the gap is zero-filled. */ +void buffer_insert(buffer_t *buf, size_t pos, + const void *data, size_t data_size); +/* Delete data with the indicated size from the buffer at position pos. The + deleted block may cross the current buffer size boundary, which is ignored. + */ +void buffer_delete(buffer_t *buf, size_t pos, size_t size); +/* Replace the data in the buffer with the indicated size at position pos with + the provided data. This is a more optimized version of + buffer_delete(buf, pos, size); buffer_insert(buf, pos, data, data_size); */ +void buffer_replace(buffer_t *buf, size_t pos, size_t size, + const void *data, size_t data_size); + +/* Fill buffer with zero bytes. */ +void buffer_write_zero(buffer_t *buf, size_t pos, size_t data_size); +void buffer_append_zero(buffer_t *buf, size_t data_size); +void buffer_insert_zero(buffer_t *buf, size_t pos, size_t data_size); + +/* Copy data from buffer to another. The buffers may be same in which case + it's internal copying, possibly with overlapping positions (ie. memmove() + like functionality). copy_size may be set to SIZE_MAX to copy the rest of + the used data in buffer. */ +void buffer_copy(buffer_t *dest, size_t dest_pos, + const buffer_t *src, size_t src_pos, size_t copy_size); +/* Append data to buffer from another. copy_size may be set to SIZE_MAX to + copy the rest of the used data in buffer. */ +void buffer_append_buf(buffer_t *dest, const buffer_t *src, + size_t src_pos, size_t copy_size); + +/* Returns pointer to specified position in buffer. WARNING: The returned + address may become invalid if you add more data to buffer. */ +void *buffer_get_space_unsafe(buffer_t *buf, size_t pos, size_t size); +/* Increase the buffer usage by given size, and return a pointer to beginning + of it. */ +void *buffer_append_space_unsafe(buffer_t *buf, size_t size); + +/* Like buffer_get_data(), but don't return it as const. Returns NULL if the + buffer is non-modifiable. WARNING: The returned address may become invalid + if you add more data to buffer. */ +void *buffer_get_modifiable_data(const buffer_t *buf, size_t *used_size_r) + ATTR_NULL(2); + +/* Set the "used size" of buffer, ie. 0 would set the buffer empty. + Must not be used to grow buffer. The data after the buffer's new size will + be effectively lost, because e.g. buffer_get_space_unsafe() will zero out + the contents. */ +void buffer_set_used_size(buffer_t *buf, size_t used_size); + +/* Returns the current buffer size. */ +size_t buffer_get_size(const buffer_t *buf) ATTR_PURE; +/* Returns how many bytes we can write to buffer without increasing its size. + With dynamic buffers this is buffer_get_size()-1, because the extra 1 byte + is reserved for str_c()'s NUL. */ +size_t buffer_get_writable_size(const buffer_t *buf) ATTR_PURE; +/* Returns the maximum number of bytes we can append to the buffer. If the + buffer is dynamic, this is always near SIZE_MAX. */ +size_t buffer_get_avail_size(const buffer_t *buf) ATTR_PURE; + +/* Returns TRUE if buffer contents are identical. */ +bool buffer_cmp(const buffer_t *buf1, const buffer_t *buf2); + +/* Returns pointer to beginning of buffer data. Current used size of buffer is + stored in used_size if it's non-NULL. */ +static inline const void * ATTR_NULL(2) +buffer_get_data(const buffer_t *buf, size_t *used_size_r) +{ + if (used_size_r != NULL) + *used_size_r = buf->used; + return buf->data; +} + +/* Returns the current used buffer size. */ +static inline size_t ATTR_PURE +buffer_get_used_size(const buffer_t *buf) +{ + return buf->used; +} + +/* Crash if buffer was allocated from data stack and stack frame has changed. + This can be used as an assert-like check to verify that it's valid to + increase the buffer size here, instead of crashing only randomly when the + buffer needs to be increased. */ +void buffer_verify_pool(buffer_t *buf); + +/* This will truncate your byte buffer to contain at most + given number of bits. + + 1 bits: 01 00000001 + 2 bits: 03 00000011 + 3 bits: 07 00000111 + 4 bits: 0f 00001111 + 5 bits: 1f 00011111 + 6 bits: 3f 00111111 + 7 bits: 7f 01111111 + 8 bits: ff 11111111 + 9 bits: 01ff 0000000111111111 +10 bits: 03ff 0000001111111111 +11 bits: 07ff 0000011111111111 +12 bits: 0fff 0000111111111111 +13 bits: 1fff 0001111111111111 +14 bits: 3fff 0011111111111111 +15 bits: 7fff 0111111111111111 +16 bits: ffff 1111111111111111 + + and so forth + +*/ +void buffer_truncate_rshift_bits(buffer_t *buf, size_t bits); + +enum buffer_append_result { + /* Stream reached EOF successfully */ + BUFFER_APPEND_OK = 0, + /* Error was encountered */ + BUFFER_APPEND_READ_ERROR = -1, + /* Stream is non-blocking, call again later */ + BUFFER_APPEND_READ_MORE = -2, + /* Stream was consumed up to max_read_size */ + BUFFER_APPEND_READ_MAX_SIZE = -3, +}; + +/* Attempt to fully read a stream. Since this can be a network stream, it + can return BUFFER_APPEND_READ_MORE, which means you need to call this + function again. It is caller's responsibility to keep track of + max_read_size in case more reading is needed. */ +enum buffer_append_result +buffer_append_full_istream(buffer_t *buf, struct istream *is, size_t max_read_size, + const char **error_r); + +/* Attempt to fully read a file. BUFFER_APPEND_READ_MORE is never returned. */ +enum buffer_append_result +buffer_append_full_file(buffer_t *buf, const char *file, size_t max_read_size, + const char **error_r); + +#endif diff --git a/src/lib/byteorder.h b/src/lib/byteorder.h new file mode 100644 index 0000000..e7912bd --- /dev/null +++ b/src/lib/byteorder.h @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2016-2017 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> + * + * 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 BYTEORDER_H +#define BYTEORDER_H + +/* + * These prototypes exist to catch bugs in the code generating macros below. + */ +/* return byte swapped input */ +static inline uint64_t i_bswap_64(uint64_t in); +static inline uint32_t i_bswap_32(uint32_t in); +static inline uint16_t i_bswap_16(uint16_t in); +static inline uint8_t i_bswap_8(uint8_t in); + +/* load an unaligned cpu native endian number from memory */ +static inline uint64_t cpu64_to_cpu_unaligned(const void *in); +static inline uint32_t cpu32_to_cpu_unaligned(const void *in); +static inline uint16_t cpu16_to_cpu_unaligned(const void *in); +static inline uint8_t cpu8_to_cpu_unaligned(const void *in); + +/* load an unaligned big endian number from memory */ +static inline uint64_t be64_to_cpu_unaligned(const void *in); +static inline uint32_t be32_to_cpu_unaligned(const void *in); +static inline uint16_t be16_to_cpu_unaligned(const void *in); +static inline uint8_t be8_to_cpu_unaligned(const void *in); + +/* load an unaligned little endian number from memory */ +static inline uint64_t le64_to_cpu_unaligned(const void *in); +static inline uint32_t le32_to_cpu_unaligned(const void *in); +static inline uint16_t le16_to_cpu_unaligned(const void *in); +static inline uint8_t le8_to_cpu_unaligned(const void *in); + +/* store into memory a cpu native endian number as a big endian number */ +static inline void cpu64_to_be_unaligned(uint64_t in, void *out); +static inline void cpu32_to_be_unaligned(uint32_t in, void *out); +static inline void cpu16_to_be_unaligned(uint16_t in, void *out); +static inline void cpu8_to_be_unaligned(uint8_t in, void *out); + +/* store into memory a cpu native endian number as a little endian number */ +static inline void cpu64_to_le_unaligned(uint64_t in, void *out); +static inline void cpu32_to_le_unaligned(uint32_t in, void *out); +static inline void cpu16_to_le_unaligned(uint16_t in, void *out); +static inline void cpu8_to_le_unaligned(uint8_t in, void *out); + +/* convert a big endian input into cpu native endian */ +static inline uint64_t be64_to_cpu(uint64_t in); +static inline uint32_t be32_to_cpu(uint32_t in); +static inline uint16_t be16_to_cpu(uint16_t in); +static inline uint8_t be8_to_cpu(uint8_t in); + +/* convert a cpu native endian input into big endian */ +static inline uint64_t cpu64_to_be(uint64_t in); +static inline uint32_t cpu32_to_be(uint32_t in); +static inline uint16_t cpu16_to_be(uint16_t in); +static inline uint8_t cpu8_to_be(uint8_t in); + +/* convert a little endian input into cpu native endian */ +static inline uint64_t le64_to_cpu(uint64_t in); +static inline uint32_t le32_to_cpu(uint32_t in); +static inline uint16_t le16_to_cpu(uint16_t in); +static inline uint8_t le8_to_cpu(uint8_t in); + +/* convert a cpu native endian input into little endian */ +static inline uint64_t cpu64_to_le(uint64_t in); +static inline uint32_t cpu32_to_le(uint32_t in); +static inline uint16_t cpu16_to_le(uint16_t in); +static inline uint8_t cpu8_to_le(uint8_t in); + +/* + * byte swapping + */ +static inline uint64_t i_bswap_64(uint64_t in) +{ + return ((in & 0xff00000000000000ULL) >> 56) | + ((in & 0x00ff000000000000ULL) >> 40) | + ((in & 0x0000ff0000000000ULL) >> 24) | + ((in & 0x000000ff00000000ULL) >> 8) | + ((in & 0x00000000ff000000ULL) << 8) | + ((in & 0x0000000000ff0000ULL) << 24) | + ((in & 0x000000000000ff00ULL) << 40) | + ((in & 0x00000000000000ffULL) << 56); +} + +static inline uint32_t i_bswap_32(uint32_t in) +{ + return ((in & 0xff000000) >> 24) | + ((in & 0x00ff0000) >> 8) | + ((in & 0x0000ff00) << 8) | + ((in & 0x000000ff) << 24); +} + +static inline uint16_t i_bswap_16(uint16_t in) +{ + return ((in & 0xff00) >> 8) | + ((in & 0x00ff) << 8); +} + +static inline uint8_t i_bswap_8(uint8_t in) +{ + return (in & 0xff); +} + +/* + * unaligned big-endian integer + */ +static inline uint64_t be64_to_cpu_unaligned(const void *in) +{ + const uint8_t *p = (const uint8_t *) in; + + return (((uint64_t) p[0] << 56) | + ((uint64_t) p[1] << 48) | + ((uint64_t) p[2] << 40) | + ((uint64_t) p[3] << 32) | + ((uint64_t) p[4] << 24) | + ((uint64_t) p[5] << 16) | + ((uint64_t) p[6] << 8) | + ((uint64_t) p[7])); +} + +static inline void cpu64_to_be_unaligned(uint64_t in, void *out) +{ + uint8_t *p = (uint8_t *) out; + + p[0] = (in >> 56) & 0xff; + p[1] = (in >> 48) & 0xff; + p[2] = (in >> 40) & 0xff; + p[3] = (in >> 32) & 0xff; + p[4] = (in >> 24) & 0xff; + p[5] = (in >> 16) & 0xff; + p[6] = (in >> 8) & 0xff; + p[7] = in & 0xff; +} + +static inline uint32_t be32_to_cpu_unaligned(const void *in) +{ + const uint8_t *p = (const uint8_t *) in; + + return (((uint32_t) p[0] << 24) | + ((uint32_t) p[1] << 16) | + ((uint32_t) p[2] << 8) | + ((uint32_t) p[3])); +} + +static inline void cpu32_to_be_unaligned(uint32_t in, void *out) +{ + uint8_t *p = (uint8_t *) out; + + p[0] = (in >> 24) & 0xff; + p[1] = (in >> 16) & 0xff; + p[2] = (in >> 8) & 0xff; + p[3] = in & 0xff; +} + +static inline uint16_t be16_to_cpu_unaligned(const void *in) +{ + const uint8_t *p = (const uint8_t *) in; + + return (((uint16_t) p[0] << 8) | + ((uint16_t) p[1])); +} + +static inline void cpu16_to_be_unaligned(uint16_t in, void *out) +{ + uint8_t *p = (uint8_t *) out; + + p[0] = (in >> 8) & 0xff; + p[1] = in & 0xff; +} + +static inline uint8_t be8_to_cpu_unaligned(const void *in) +{ + return *((const uint8_t *) in); +} + +static inline void cpu8_to_be_unaligned(uint8_t in, void *out) +{ + uint8_t *p = (uint8_t *) out; + + *p = in; +} + +/* + * unaligned little-endian & cpu-endian integers + */ +#define __GEN(size, bswap) \ +static inline uint##size##_t le##size##_to_cpu_unaligned(const void *in)\ +{ \ + uint##size##_t x = be##size##_to_cpu_unaligned(in); \ + /* we read a LE int as BE, so we always have to byte swap */ \ + return i_bswap_##size(x); \ +} \ +static inline void cpu##size##_to_le_unaligned(uint##size##_t in, \ + void *out) \ +{ \ + /* we'll be writing in BE, so we always have to byte swap */ \ + cpu##size##_to_be_unaligned(i_bswap_##size(in), out); \ +} \ +static inline uint##size##_t cpu##size##_to_cpu_unaligned(const void *in)\ +{ \ + uint##size##_t x = be##size##_to_cpu_unaligned(in); \ + return bswap; \ +} + +#ifdef WORDS_BIGENDIAN +#define GEN(size) __GEN(size, x) +#else +#define GEN(size) __GEN(size, i_bswap_##size(x)) +#endif + +GEN(64) +GEN(32) +GEN(16) +GEN(8) + +#undef __GEN +#undef GEN + +/* + * byte ordering + */ +#define ___GEN(from, size, to, bswap) \ +static inline uint##size##_t from##size##_to_##to(uint##size##_t x) \ +{ \ + return bswap; \ +} + +#ifdef WORDS_BIGENDIAN +#define __GEN(from, size, to, be, le) ___GEN(from, size, to, be) +#else +#define __GEN(from, size, to, be, le) ___GEN(from, size, to, le) +#endif + +#define GEN(size) \ + __GEN(be, size, cpu, x, i_bswap_##size(x)) \ + __GEN(cpu, size, be, x, i_bswap_##size(x)) \ + __GEN(le, size, cpu, i_bswap_##size(x), x) \ + __GEN(cpu, size, le, i_bswap_##size(x), x) + +GEN(64) +GEN(32) +GEN(16) +GEN(8) + +#undef ___GEN +#undef __GEN +#undef GEN + +#endif diff --git a/src/lib/child-wait.c b/src/lib/child-wait.c new file mode 100644 index 0000000..024d81d --- /dev/null +++ b/src/lib/child-wait.c @@ -0,0 +1,139 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "lib-signals.h" +#include "hash.h" +#include "child-wait.h" + +#include <sys/wait.h> + +struct child_wait { + unsigned int pid_count; + + child_wait_callback_t *callback; + void *context; +}; + +static int child_wait_refcount = 0; + +/* pid_t => wait */ +static HASH_TABLE(void *, struct child_wait *) child_pids; + +static void +sigchld_handler(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED); + +#undef child_wait_new_with_pid +struct child_wait * +child_wait_new_with_pid(pid_t pid, child_wait_callback_t *callback, + void *context) +{ + struct child_wait *wait; + + wait = i_new(struct child_wait, 1); + wait->callback = callback; + wait->context = context; + + if (pid != (pid_t)-1) + child_wait_add_pid(wait, pid); + return wait; +} + +void child_wait_free(struct child_wait **_wait) +{ + struct child_wait *wait = *_wait; + struct hash_iterate_context *iter; + void *key; + struct child_wait *value; + + *_wait = NULL; + + if (wait->pid_count > 0) { + /* this should be rare, so iterating hash is fast enough */ + iter = hash_table_iterate_init(child_pids); + while (hash_table_iterate(iter, child_pids, &key, &value)) { + if (value == wait) { + hash_table_remove(child_pids, key); + if (--wait->pid_count == 0) + break; + } + } + hash_table_iterate_deinit(&iter); + } + + i_free(wait); +} + +void child_wait_add_pid(struct child_wait *wait, pid_t pid) +{ + wait->pid_count++; + hash_table_insert(child_pids, POINTER_CAST(pid), wait); + + lib_signals_set_expected(SIGCHLD, TRUE, sigchld_handler, NULL); +} + +void child_wait_remove_pid(struct child_wait *wait, pid_t pid) +{ + wait->pid_count--; + hash_table_remove(child_pids, POINTER_CAST(pid)); + + if (hash_table_count(child_pids) == 0) + lib_signals_set_expected(SIGCHLD, FALSE, sigchld_handler, NULL); +} + +static void +sigchld_handler(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) +{ + struct child_wait_status status; + + while ((status.pid = waitpid(-1, &status.status, WNOHANG)) > 0) { + status.wait = hash_table_lookup(child_pids, + POINTER_CAST(status.pid)); + if (status.wait != NULL) { + child_wait_remove_pid(status.wait, status.pid); + status.wait->callback(&status, status.wait->context); + } + } + + if (status.pid == -1 && errno != EINTR && errno != ECHILD) + i_error("waitpid() failed: %m"); +} + +void child_wait_switch_ioloop(void) +{ + lib_signals_switch_ioloop(SIGCHLD, sigchld_handler, NULL); +} + +void child_wait_init(void) +{ + if (child_wait_refcount++ > 0) { + child_wait_switch_ioloop(); + return; + } + + hash_table_create_direct(&child_pids, default_pool, 0); + + lib_signals_set_handler(SIGCHLD, LIBSIG_FLAGS_SAFE, + sigchld_handler, NULL); +} + +void child_wait_deinit(void) +{ + struct hash_iterate_context *iter; + void *key; + struct child_wait *value; + + i_assert(child_wait_refcount > 0); + if (--child_wait_refcount > 0) { + child_wait_switch_ioloop(); + return; + } + + lib_signals_unset_handler(SIGCHLD, sigchld_handler, NULL); + + iter = hash_table_iterate_init(child_pids); + while (hash_table_iterate(iter, child_pids, &key, &value)) + i_free(value); + hash_table_iterate_deinit(&iter); + + hash_table_destroy(&child_pids); +} diff --git a/src/lib/child-wait.h b/src/lib/child-wait.h new file mode 100644 index 0000000..c495638 --- /dev/null +++ b/src/lib/child-wait.h @@ -0,0 +1,34 @@ +#ifndef CHILD_WAIT_H +#define CHILD_WAIT_H + +struct child_wait_status { + struct child_wait *wait; + + pid_t pid; + int status; +}; + +typedef void child_wait_callback_t(const struct child_wait_status *status, + void *context); + +struct child_wait * +child_wait_new_with_pid(pid_t pid, child_wait_callback_t *callback, + void *context) ATTR_NULL(3); +#define child_wait_new_with_pid(pid, callback, context) \ + child_wait_new_with_pid(pid - \ + CALLBACK_TYPECHECK(callback, void (*)( \ + const struct child_wait_status *status, typeof(context))), \ + (child_wait_callback_t *)callback, context) +#define child_wait_new(callback, context) \ + child_wait_new_with_pid((pid_t)-1, callback, context) +void child_wait_free(struct child_wait **wait); + +void child_wait_add_pid(struct child_wait *wait, pid_t pid); +void child_wait_remove_pid(struct child_wait *wait, pid_t pid); + +void child_wait_switch_ioloop(void); + +void child_wait_init(void); +void child_wait_deinit(void); + +#endif diff --git a/src/lib/compat.c b/src/lib/compat.c new file mode 100644 index 0000000..524ef37 --- /dev/null +++ b/src/lib/compat.c @@ -0,0 +1,268 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "config.h" +#undef HAVE_CONFIG_H + +/* Linux needs the _XOPEN_SOURCE define, but others don't. It needs to be + defined before unistd.h, so we need the above config.h include hack.. */ +#ifdef PREAD_WRAPPERS +# define _XOPEN_SOURCE 500 /* Linux */ +#endif + +#define IN_COMPAT_C +#include "lib.h" + +#include <stdio.h> +#include <ctype.h> +#include <unistd.h> +#include <syslog.h> +#include <time.h> +#include <sys/time.h> + +#ifndef INADDR_NONE +# define INADDR_NONE INADDR_BROADCAST +#endif + +#if !defined (HAVE_STRCASECMP) && !defined (HAVE_STRICMP) +int i_my_strcasecmp(const char *s1, const char *s2) +{ + while (*s1 != '\0' && i_toupper(*s1) == i_toupper(*s2)) { + s1++; s2++; + } + + return i_toupper(*s1) - i_toupper(*s2); +} + +int i_my_strncasecmp(const char *s1, const char *s2, size_t max_chars) +{ + while (max_chars > 1 && *s1 != '\0' && + i_toupper(*s1) == i_toupper(*s2)) { + s1++; s2++; max_chars--; + } + + return i_toupper(*s1) - i_toupper(*s2); +} +#endif + +#ifndef HAVE_INET_ATON +int i_my_inet_aton(const char *cp, struct in_addr *inp) +{ + in_addr_t addr; + + addr = inet_addr(cp); + if (addr == INADDR_NONE) + return 0; + + inp->s_addr = addr; + return 1; +} +#endif + +#ifndef HAVE_VSYSLOG +void i_my_vsyslog(int priority, const char *format, va_list args) +{ + T_BEGIN { + syslog(priority, "%s", t_strdup_vprintf(format, args)); + } T_END; +} +#endif + +#ifndef HAVE_GETPAGESIZE +int i_my_getpagesize(void) +{ +#ifdef _SC_PAGESIZE + return sysconf(_SC_PAGESIZE); +#else +# ifdef __GNUC__ +# warning Guessing page size to be 4096 +# endif + return 4096; +#endif +} +#endif + +#ifndef HAVE_WRITEV +ssize_t i_my_writev(int fd, const struct iovec *iov, int iov_len) +{ + size_t written; + ssize_t ret; + int i; + + written = 0; + for (i = 0; i < iov_len; i++, iov++) { + ret = write(fd, iov->iov_base, iov->iov_len); + if (ret < 0) + return -1; + + written += ret; + if ((size_t)ret != iov->iov_len) + break; + } + + if (written > SSIZE_T_MAX) { + errno = ERANGE; + return -1; + } + + return (ssize_t)written; +} +#endif + +#if !defined(HAVE_PREAD) || defined(PREAD_BROKEN) +ssize_t i_my_pread(int fd, void *buf, size_t count, off_t offset) +{ + ssize_t ret; + off_t old_offset; + + old_offset = lseek(fd, 0, SEEK_CUR); + if (old_offset == -1) + return -1; + + if (lseek(fd, offset, SEEK_SET) < 0) + return -1; + + ret = read(fd, buf, count); + if (ret < 0) + return -1; + + if (lseek(fd, old_offset, SEEK_SET) < 0) + return -1; + return ret; +} + +ssize_t i_my_pwrite(int fd, const void *buf, size_t count, off_t offset) +{ + ssize_t ret; + off_t old_offset; + + old_offset = lseek(fd, 0, SEEK_CUR); + if (old_offset == -1) + return -1; + + if (lseek(fd, offset, SEEK_SET) < 0) + return -1; + + ret = write(fd, buf, count); + if (ret < 0) + return -1; + + if (lseek(fd, old_offset, SEEK_SET) < 0) + return -1; + return ret; +} +#elif defined(PREAD_WRAPPERS) + +ssize_t i_my_pread(int fd, void *buf, size_t count, off_t offset) +{ + ssize_t ret; + + ret = pread(fd, buf, count, offset); + return ret; +} + +ssize_t i_my_pwrite(int fd, const void *buf, size_t count, off_t offset) +{ + return pwrite(fd, buf, count, offset); +} +#endif + +#ifndef HAVE_SETEUID +int i_my_seteuid(uid_t euid) +{ +#ifdef HAVE_SETREUID + /* HP-UX at least doesn't have seteuid() but has setreuid() */ + return setreuid(-1, euid); +#else +# error Missing seteuid functionality +#endif +} +#endif + +#ifndef HAVE_SETEGID +int i_my_setegid(gid_t egid) +{ +#ifdef HAVE_SETRESGID + /* HP-UX at least doesn't have setegid() but has setresgid() */ + return setresgid(-1, egid, -1); +#else +# error Missing setegid functionality +#endif +} +#endif + +#ifndef HAVE_LIBGEN_H +char *i_my_basename(char *path) +{ + char *p; + + /* note that this isn't POSIX-compliant basename() replacement. + too much trouble without any gain. */ + p = strrchr(path, '/'); + return p == NULL ? path : p + 1; +} +#endif + +#ifdef HAVE_OLD_VSNPRINTF +#undef vsnprintf +int i_my_vsnprintf(char *str, size_t size, const char *format, va_list ap) +{ + size_t tmp_size; + char *tmp; + int ret; + + /* On overflow HP-UX returns -1, IRIX and Tru64 return size-1. */ + ret = vsnprintf(str, size, format, ap); + if (ret >= 0 && (size_t)ret+1 != size) + return ret; + + /* see if data stack has enough available space for it */ + tmp_size = t_get_bytes_available(); + if (tmp_size > size) { + tmp = t_buffer_get(tmp_size); + ret = vsnprintf(tmp, tmp_size, format, ap); + if (ret >= 0 && (size_t)ret+1 != tmp_size) { + if (size > 0) { + memcpy(str, tmp, size-1); + str[size-1] = '\0'; + } + return ret; + } + } else { + tmp_size = size; + } + + /* try to allocate enough memory to get it to fit. */ + do { + tmp_size = nearest_power(tmp_size+1); + tmp = i_malloc(tmp_size); + ret = vsnprintf(tmp, tmp_size, format, ap); + if (ret >= 0 && (size_t)ret+1 != tmp_size) { + if (size > 0) { + memcpy(str, tmp, size-1); + str[size-1] = '\0'; + } + i_free(tmp); + return ret; + } + i_free(tmp); + } while (tmp_size < 1024*1024); + + i_panic("my_vsnprintf(): Output string too big"); +} +#endif + +#ifndef HAVE_CLOCK_GETTIME +int i_my_clock_gettime(int clk_id, struct timespec *tp) +{ + struct timeval tv; + + i_assert(clk_id == CLOCK_REALTIME); + + /* fallback to using microseconds */ + if (gettimeofday(&tv, NULL) < 0) + return -1; + tp->tv_sec = tv.tv_sec; + tp->tv_nsec = tv.tv_usec * 1000; + return 0; +} +#endif diff --git a/src/lib/compat.h b/src/lib/compat.h new file mode 100644 index 0000000..ce3127d --- /dev/null +++ b/src/lib/compat.h @@ -0,0 +1,323 @@ +#ifndef COMPAT_H +#define COMPAT_H + +/* _ILP32 and _LP64 are common but not universal, make sure that exactly one + of them is defined. */ +#if !defined(_ILP32) && \ + (SIZEOF_INT == 4) && (SIZEOF_LONG == 4) && (SIZEOF_VOID_P == 4) +# define _ILP32 +#endif +#if !defined(_LP64) && \ + (SIZEOF_INT == 4) && (SIZEOF_LONG == 8) && (SIZEOF_VOID_P == 8) +# define _LP64 +#endif +#if defined(_ILP32) && defined(_LP64) +# error "Cannot have both _ILP32 and _LP64 defined" +#elif !defined(_ILP32) && !defined(_LP64) +# error "Must have one of _ILP32 and _LP64 defined" +#endif + +/* well, this is obviously wrong since it assumes it's 64bit, but older + GCCs don't define it and we really want it. */ +#ifndef LLONG_MAX +# define LLONG_MAX 9223372036854775807LL +#endif + +#if ((__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)) && \ + defined(HAVE_TYPEOF)) && !defined(__cplusplus) +# define HAVE_TYPE_CHECKS +#endif + +/* We really want NULL to be a pointer, since we have various type-checks + that may result in compiler warnings/errors if it's not. Do this only when + type checking is used - it's not otherwise needed and causes compiling + problems with e.g. Sun C compiler. */ +#ifdef HAVE_TYPE_CHECKS +# undef NULL +# define NULL ((void *)0) +#endif + +#ifndef __has_extension + #define __has_extension(x) 0 // Compatibility with non-clang compilers. +#endif + +#if (defined(__GNUC__) && __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) || \ + (defined(__clang__) && (__has_extension(attribute_deprecated_with_message))) +# define HAVE_ATTR_DEPRECATED +int rand(void) __attribute__((deprecated("Do not use rand, use i_rand"))); +int rand_r(unsigned int*) __attribute__((deprecated("Do not use rand_r, use i_rand"))); +#endif + +#ifndef __cplusplus +#ifdef HAVE__BOOL +typedef _Bool bool; +#else +typedef int bool; +#endif +#endif + +#if defined (HAVE_UOFF_T) +/* native support */ +#elif defined (UOFF_T_INT) +typedef unsigned int uoff_t; +#elif defined (UOFF_T_LONG) +typedef unsigned long uoff_t; +#elif defined (UOFF_T_LONG_LONG) +typedef unsigned long long uoff_t; +#else +# error uoff_t size not set +#endif + +#ifndef HAVE_UINTMAX_T +# if SIZEOF_LONG_LONG > 0 +typedef unsigned long long uintmax_t; +# else +typedef unsigned long uintmax_t; +# endif +#endif + +#ifndef HAVE_UINT_FAST32_T +# if SIZEOF_INT >= 4 +typedef unsigned int uint_fast32_t; +# else +typedef unsigned long uint_fast32_t; +# endif +#endif + +#ifndef HAVE_SOCKLEN_T +typedef int socklen_t; +#endif + +/* WORDS_BIGENDIAN needs to be undefined if not enabled */ +#if defined(WORDS_BIGENDIAN) && WORDS_BIGENDIAN == 0 +# undef WORDS_BIGENDIAN +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +# include <sys/sysmacros.h> +# ifdef HAVE_SYS_MKDEV_H +# include <sys/mkdev.h> /* UnixWare */ +# endif +# define CMP_DEV_T(a, b) (major(a) == major(b) && minor(a) == minor(b)) +#elif !defined (DEV_T_STRUCT) +# define CMP_DEV_T(a, b) ((a) == (b)) +#else +# error I do not know how to compare dev_t +#endif + +#ifdef HAVE_STAT_XTIM +# define HAVE_ST_NSECS +# define ST_ATIME_NSEC(st) ((unsigned long)(st).st_atim.tv_nsec) +# define ST_MTIME_NSEC(st) ((unsigned long)(st).st_mtim.tv_nsec) +# define ST_CTIME_NSEC(st) ((unsigned long)(st).st_ctim.tv_nsec) +#elif defined (HAVE_STAT_XTIMESPEC) +# define HAVE_ST_NSECS +# define ST_ATIME_NSEC(st) ((unsigned long)(st).st_atimespec.tv_nsec) +# define ST_MTIME_NSEC(st) ((unsigned long)(st).st_mtimespec.tv_nsec) +# define ST_CTIME_NSEC(st) ((unsigned long)(st).st_ctimespec.tv_nsec) +#else +# define ST_ATIME_NSEC(st) 0UL +# define ST_MTIME_NSEC(st) 0UL +# define ST_CTIME_NSEC(st) 0UL +#endif + +#ifdef HAVE_ST_NSECS +/* TRUE if a nanosecond timestamp from struct stat matches another nanosecond. + If nanoseconds aren't supported in struct stat, returns always TRUE (useful + with NFS if some hosts support nanoseconds and others don't). */ +# define ST_NTIMES_EQUAL(ns1, ns2) ((ns1) == (ns2)) +#else +# define ST_NTIMES_EQUAL(ns1, ns2) TRUE +#endif + +#define CMP_ST_MTIME(st1, st2) \ + ((st1)->st_mtime == (st2)->st_mtime && \ + ST_NTIMES_EQUAL(ST_MTIME_NSEC(*(st1)), ST_MTIME_NSEC(*(st2)))) +#define CMP_ST_CTIME(st1, st2) \ + ((st1)->st_ctime == (st2)->st_ctime && \ + ST_NTIMES_EQUAL(ST_CTIME_NSEC(*(st1)), ST_CTIME_NSEC(*(st2)))) + +/* strcasecmp(), strncasecmp() */ +#ifndef HAVE_STRCASECMP +# ifdef HAVE_STRICMP +# define strcasecmp stricmp +# define strncasecmp strnicmp +# else +# define strcasecmp i_my_strcasecmp +# define strncasecmp i_my_strncasecmp +int i_my_strcasecmp(const char *s1, const char *s2); +int i_my_strncasecmp(const char *s1, const char *s2, size_t max_chars); +# endif +#endif + +#ifndef HAVE_INET_ATON +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# define inet_aton i_my_inet_aton +int i_my_inet_aton(const char *cp, struct in_addr *inp); +#endif + +#ifndef HAVE_VSYSLOG +# define vsyslog i_my_vsyslog +void i_my_vsyslog(int priority, const char *format, va_list args); +#endif + +#ifndef HAVE_GETPAGESIZE +# define getpagesize i_my_getpagesize +int i_my_getpagesize(void); +#endif + +#ifndef HAVE_FDATASYNC +# define fdatasync fsync +#endif + +struct const_iovec { + const void *iov_base; + size_t iov_len; +}; + +#ifndef HAVE_STRUCT_IOVEC +struct iovec { + void *iov_base; + size_t iov_len; +}; +#endif + +/* IOV_MAX should be in limits.h nowadays. Linux still (2005) requires + defining _XOPEN_SOURCE to get that value. UIO_MAXIOV works with it though, + so use it instead. 16 is the lowest acceptable value for all OSes. */ +#ifndef IOV_MAX +# include <sys/uio.h> +# ifdef UIO_MAXIOV +# define IOV_MAX UIO_MAXIOV +# else +# define IOV_MAX 16 +# endif +#endif + +#ifndef HAVE_WRITEV +# define writev i_my_writev +struct iovec; +ssize_t i_my_writev(int fd, const struct iovec *iov, int iov_len); +#endif + +#if !defined(HAVE_PREAD) || defined(PREAD_WRAPPERS) || defined(PREAD_BROKEN) +# ifndef IN_COMPAT_C +# define pread i_my_pread +# define pwrite i_my_pwrite +# endif +ssize_t i_my_pread(int fd, void *buf, size_t count, off_t offset); +ssize_t i_my_pwrite(int fd, const void *buf, size_t count, off_t offset); +#endif + +#ifndef HAVE_SETEUID +# define seteuid i_my_seteuid +int i_my_seteuid(uid_t euid); +#endif + +#ifndef HAVE_SETEGID +# define setegid i_my_setegid +int i_my_setegid(gid_t egid); +#endif + +#ifndef HAVE_LIBGEN_H +# define basename i_my_basename +char *i_my_basename(char *path); +#endif + +#ifdef HAVE_OLD_VSNPRINTF +# include <stdio.h> +# define vsnprintf i_my_vsnprintf +int i_my_vsnprintf(char *str, size_t size, const char *format, va_list ap); +#endif + +#ifndef HAVE_CLOCK_GETTIME +# include <time.h> +# undef CLOCK_REALTIME +# define CLOCK_REALTIME 1 +# define clock_gettime i_my_clock_gettime +int i_my_clock_gettime(int clk_id, struct timespec *tp); +#endif + +/* ctype.h isn't safe with signed chars, + use our own instead if really needed */ +#define i_toupper(x) ((char) toupper((int) (unsigned char) (x))) +#define i_tolower(x) ((char) tolower((int) (unsigned char) (x))) +#define i_isalnum(x) (isalnum((int) (unsigned char) (x)) != 0) +#define i_isalpha(x) (isalpha((int) (unsigned char) (x)) != 0) +#define i_isascii(x) (isascii((int) (unsigned char) (x)) != 0) +#define i_isblank(x) (isblank((int) (unsigned char) (x)) != 0) +#define i_iscntrl(x) (iscntrl((int) (unsigned char) (x)) != 0) +#define i_isdigit(x) (isdigit((int) (unsigned char) (x)) != 0) +#define i_isgraph(x) (isgraph((int) (unsigned char) (x)) != 0) +#define i_islower(x) (islower((int) (unsigned char) (x)) != 0) +#define i_isprint(x) (isprint((int) (unsigned char) (x)) != 0) +#define i_ispunct(x) (ispunct((int) (unsigned char) (x)) != 0) +#define i_isspace(x) (isspace((int) (unsigned char) (x)) != 0) +#define i_isupper(x) (isupper((int) (unsigned char) (x)) != 0) +#define i_isxdigit(x) (isxdigit((int) (unsigned char) (x)) != 0) + +#ifndef EOVERFLOW +# define EOVERFLOW ERANGE +#endif + +#ifdef EDQUOT +# define ENOSPACE(errno) ((errno) == ENOSPC || (errno) == EDQUOT) +# define ENOQUOTA(errno) ((errno) == EDQUOT) +#else +/* probably all modern OSes have EDQUOT, but just in case one doesn't assume + that ENOSPC is the same as "over quota". */ +# define ENOSPACE(errno) ((errno) == ENOSPC) +# define ENOQUOTA(errno) ((errno) == ENOSPC) +#endif + +/* EPERM is returned sometimes if device doesn't support such modification */ +#ifdef EROFS +# define ENOACCESS(errno) \ + ((errno) == EACCES || (errno) == EROFS || (errno) == EPERM) +#else +# define ENOACCESS(errno) ((errno) == EACCES || (errno) == EPERM) +#endif + +#define ENOTFOUND(errno) \ + ((errno) == ENOENT || (errno) == ENOTDIR || \ + (errno) == ELOOP || (errno) == ENAMETOOLONG) + +#define ECANTLINK(errno) \ + ((errno) == EXDEV || (errno) == EMLINK || (errno) == EPERM) + +/* Returns TRUE if unlink() failed because it attempted to delete a directory */ +#define UNLINK_EISDIR(errno) \ + ((errno) == EPERM || /* POSIX */ \ + (errno) == EISDIR) /* Linux */ + +/* EBUSY is given by some NFS implementations */ +#define EDESTDIREXISTS(errno) \ + ((errno) == EEXIST || (errno) == ENOTEMPTY || (errno) == EBUSY) + +/* fstat() returns ENOENT instead of ESTALE with some Linux versions */ +#define ESTALE_FSTAT(errno) \ + ((errno) == ESTALE || (errno) == ENOENT) + +#if !defined(_POSIX_SYNCHRONIZED_IO) && \ + defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060) +/* OS X Snow Leopard has fdatasync(), but no prototype for it. */ +int fdatasync(int); +#endif + +/* Try to keep IO operations at least this size */ +#ifndef IO_BLOCK_SIZE +# define IO_BLOCK_SIZE 8192 +#endif +/* Default size for data blocks transferred over the network */ +#ifndef NET_BLOCK_SIZE +# define NET_BLOCK_SIZE (128*1024) +#endif + +#if !defined(PIPE_BUF) && defined(_POSIX_PIPE_BUF) +# define PIPE_BUF (8 * _POSIX_PIPE_BUF) /* for HURD */ +#endif + +#endif diff --git a/src/lib/connection.c b/src/lib/connection.c new file mode 100644 index 0000000..f154f9e --- /dev/null +++ b/src/lib/connection.c @@ -0,0 +1,962 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "ioloop.h" +#include "istream.h" +#include "istream-unix.h" +#include "ostream.h" +#include "ostream-unix.h" +#include "iostream.h" +#include "net.h" +#include "strescape.h" +#include "llist.h" +#include "time-util.h" +#include "connection.h" + +#include <unistd.h> +#include <libgen.h> + +static void connection_handshake_ready(struct connection *conn) +{ + conn->handshake_received = TRUE; + if (conn->v.handshake_ready != NULL) + conn->v.handshake_ready(conn); +} + +static void connection_closed(struct connection *conn, + enum connection_disconnect_reason reason) +{ + conn->disconnect_reason = reason; + conn->v.destroy(conn); +} + +static void connection_idle_timeout(struct connection *conn) +{ + connection_closed(conn, CONNECTION_DISCONNECT_IDLE_TIMEOUT); +} + +static void connection_connect_timeout(struct connection *conn) +{ + connection_closed(conn, CONNECTION_DISCONNECT_CONNECT_TIMEOUT); +} + +void connection_input_default(struct connection *conn) +{ + const char *line; + struct istream *input; + struct ostream *output; + int ret = 0; + + if (!conn->handshake_received && + conn->v.handshake != NULL) { + if ((ret = conn->v.handshake(conn)) < 0) { + connection_closed( + conn, CONNECTION_DISCONNECT_HANDSHAKE_FAILED); + return; + } else if (ret == 0) { + return; + } else { + connection_handshake_ready(conn); + } + } + + switch (connection_input_read(conn)) { + case -1: + return; + case 0: /* allow calling this function for buffered input */ + case 1: + break; + default: + i_unreached(); + } + + input = conn->input; + output = conn->output; + i_stream_ref(input); + if (output != NULL) { + o_stream_ref(output); + o_stream_cork(output); + } + while (!input->closed && (line = i_stream_next_line(input)) != NULL) { + T_BEGIN { + if (!conn->handshake_received && + conn->v.handshake_line != NULL) { + ret = conn->v.handshake_line(conn, line); + if (ret > 0) + connection_handshake_ready(conn); + else if (ret == 0) + /* continue reading */ + ret = 1; + else + conn->disconnect_reason = + CONNECTION_DISCONNECT_HANDSHAKE_FAILED; + } else { + ret = conn->v.input_line(conn, line); + } + } T_END; + if (ret <= 0) + break; + } + if (output != NULL) { + o_stream_uncork(output); + o_stream_unref(&output); + } + if (ret < 0 && !input->closed) { + enum connection_disconnect_reason reason = + conn->disconnect_reason; + if (reason == CONNECTION_DISCONNECT_NOT) + reason = CONNECTION_DISCONNECT_DEINIT; + connection_closed(conn, reason); + } + i_stream_unref(&input); +} + +int connection_verify_version(struct connection *conn, + const char *service_name, + unsigned int major_version, + unsigned int minor_version) +{ + i_assert(!conn->version_received); + + if (strcmp(service_name, conn->list->set.service_name_in) != 0) { + e_error(conn->event, "Connected to wrong socket type. " + "We want '%s', but received '%s'", + conn->list->set.service_name_in, service_name); + return -1; + } + + if (major_version != conn->list->set.major_version) { + e_error(conn->event, "Socket supports major version %u, " + "but we support only %u (mixed old and new binaries?)", + major_version, conn->list->set.major_version); + return -1; + } + + conn->minor_version = minor_version; + conn->version_received = TRUE; + return 0; +} + +int connection_handshake_args_default(struct connection *conn, + const char *const *args) +{ + unsigned int major_version, minor_version; + + if (conn->version_received) + return 1; + + /* VERSION <tab> service_name <tab> major version <tab> minor version */ + if (str_array_length(args) != 4 || + strcmp(args[0], "VERSION") != 0 || + str_to_uint(args[2], &major_version) < 0 || + str_to_uint(args[3], &minor_version) < 0) { + e_error(conn->event, "didn't reply with a valid VERSION line: %s", + t_strarray_join(args, "\t")); + return -1; + } + + if (connection_verify_version(conn, args[1], + major_version, minor_version) < 0) + return -1; + return 1; +} + +int connection_input_line_default(struct connection *conn, const char *line) +{ + const char *const *args; + + args = t_strsplit_tabescaped(line); + if (args[0] == NULL && !conn->list->set.allow_empty_args_input) { + e_error(conn->event, "Unexpectedly received empty line"); + return -1; + } + + if (!conn->handshake_received && + (conn->v.handshake_args != connection_handshake_args_default || + conn->list->set.major_version != 0)) { + int ret; + if ((ret = conn->v.handshake_args(conn, args)) == 0) + ret = 1; /* continue reading */ + else if (ret > 0) + connection_handshake_ready(conn); + else { + conn->disconnect_reason = + CONNECTION_DISCONNECT_HANDSHAKE_FAILED; + } + return ret; + } else if (!conn->handshake_received) { + /* we don't do handshakes */ + connection_handshake_ready(conn); + } + + /* version must be handled though, by something */ + i_assert(conn->version_received); + + return conn->v.input_args(conn, args); +} + +void connection_input_halt(struct connection *conn) +{ + io_remove(&conn->io); + timeout_remove(&conn->to); +} + +static void +connection_input_resume_full(struct connection *conn, bool set_io_pending) +{ + i_assert(!conn->disconnected); + + if (conn->io != NULL) { + /* do nothing */ + } else if (conn->input != NULL) { + conn->io = io_add_istream_to(conn->ioloop, conn->input, + *conn->v.input, conn); + if (set_io_pending) + io_set_pending(conn->io); + } else if (conn->fd_in != -1) { + conn->io = io_add_to(conn->ioloop, conn->fd_in, IO_READ, + *conn->v.input, conn); + if (set_io_pending) + io_set_pending(conn->io); + } + if (conn->input_idle_timeout_secs != 0 && conn->to == NULL) { + conn->to = timeout_add_to(conn->ioloop, + conn->input_idle_timeout_secs*1000, + *conn->v.idle_timeout, conn); + } +} + +void connection_input_resume(struct connection *conn) +{ + connection_input_resume_full(conn, TRUE); +} + +static void connection_update_property_label(struct connection *conn) +{ + const char *label; + + if (conn->remote_ip.family == 0) { + if (conn->remote_uid == (uid_t)-1) + label = NULL; + else if (conn->remote_pid != (pid_t)-1) { + label = t_strdup_printf("pid=%ld,uid=%ld", + (long)conn->remote_pid, + (long)conn->remote_uid); + } else { + label = t_strdup_printf("uid=%ld", + (long)conn->remote_uid); + } + } else if (conn->remote_ip.family == AF_INET6) { + label = t_strdup_printf("[%s]:%u", + net_ip2addr(&conn->remote_ip), + conn->remote_port); + } else { + label = t_strdup_printf("%s:%u", + net_ip2addr(&conn->remote_ip), + conn->remote_port); + } + + i_assert(label != NULL || conn->property_label == NULL); + if (conn->property_label != NULL && + strcmp(conn->property_label, label) != 0) { + e_debug(conn->event, "Updated peer address to %s", label); + } + + i_free(conn->property_label); + conn->property_label = i_strdup(label); +} + +static void connection_update_label(struct connection *conn) +{ + bool unix_socket = conn->unix_socket || + (conn->remote_ip.family == 0 && conn->remote_uid != (uid_t)-1); + string_t *label; + + label = t_str_new(64); + if (conn->base_name != NULL) + str_append(label, conn->base_name); + if (conn->property_label != NULL) { + if (str_len(label) == 0) + str_append(label, conn->property_label); + else { + str_append(label, " ("); + str_append(label, conn->property_label); + str_append(label, ")"); + } + } + if (str_len(label) == 0) { + if (conn->fd_in >= 0 && + (conn->fd_in == conn->fd_out || conn->fd_out < 0)) + str_printfa(label, "fd=%d", conn->fd_in); + else if (conn->fd_in < 0 && conn->fd_out >= 0) + str_printfa(label, "fd=%d", conn->fd_out); + else if (conn->fd_in >= 0 && conn->fd_out >= 0) { + str_printfa(label, "fd_in=%d,fd_out=%d", + conn->fd_in, conn->fd_out); + } + } + if (unix_socket && str_len(label) > 0) + str_insert(label, 0, "unix:"); + if (conn->list->set.log_connection_id) { + if (str_len(label) > 0) + str_append_c(label, ' '); + str_printfa(label, "[%u]", conn->id); + } + + i_free(conn->label); + conn->label = i_strdup(str_c(label)); +} + +static const char * +connection_create_stream_name(struct connection *conn, int fd) +{ + string_t *name; + + name = t_str_new(64); + str_append(name, "(conn"); + if (conn->unix_socket || + (conn->remote_ip.family == 0 && conn->remote_uid != (uid_t)-1)) + str_append(name, ":unix"); + if (conn->base_name != NULL) { + str_append_c(name, ':'); + str_append(name, conn->base_name); + } else if (conn->property_label != NULL) { + str_append_c(name, ':'); + str_append(name, conn->property_label); + } else if (fd >= 0) { + str_printfa(name, ":fd=%d", fd); + } + if (conn->list->set.log_connection_id) { + if (str_len(name) == 5) + str_append_c(name, ':'); + else + str_append_c(name, ','); + str_printfa(name, "id=%u", conn->id); + } + str_append_c(name, ')'); + + return str_c(name); +} + +static void connection_update_stream_names(struct connection *conn) +{ + if (conn->input != NULL) { + i_stream_set_name( + conn->input, + connection_create_stream_name(conn, conn->fd_in)); + } + if (conn->output != NULL) { + o_stream_set_name( + conn->output, + connection_create_stream_name(conn, conn->fd_out)); + } +} + +void connection_update_event(struct connection *conn) +{ + string_t *prefix; + + prefix = t_str_new(64); + str_append(prefix, "conn"); + if (strlen(conn->label) > 0) { + str_append_c(prefix, ' '); + str_append(prefix, conn->label); + } + str_append(prefix, ": "); + event_set_append_log_prefix(conn->event, str_c(prefix)); + + if (conn->local_ip.family > 0) { + event_add_str(conn->event, conn->list->set.client ? + "source_ip" : "local_ip", + net_ip2addr(&conn->local_ip)); + } + + if (conn->remote_ip.family > 0) { + event_add_str(conn->event, conn->list->set.client ? + "dest_ip" : "remote_ip", + net_ip2addr(&conn->remote_ip)); + } + if (conn->remote_port > 0) { + event_add_int(conn->event, conn->list->set.client ? + "dest_port" : "remote_port", + conn->remote_port); + } + + if (conn->remote_pid != (pid_t)-1) + event_add_int(conn->event, "remote_pid", conn->remote_pid); + if (conn->remote_uid != (uid_t)-1) + event_add_int(conn->event, "remote_uid", conn->remote_uid); +} + +void connection_update_properties(struct connection *conn) +{ + int fd = (conn->fd_in < 0 ? conn->fd_out : conn->fd_in); + struct net_unix_cred cred; + + if (conn->remote_ip.family != 0) { + /* remote IP was already set */ + } else if (conn->unix_peer_checked) { + /* already checked */ + } else if (fd < 0) { + /* not connected yet - wait */ + } else { + if (net_getpeername(fd, &conn->remote_ip, + &conn->remote_port) == 0) { + /* either TCP or UNIX socket connection */ + errno = 0; + } + + if (conn->remote_ip.family != 0) { + /* TCP connection */ + i_assert(conn->remote_port != 0); + } else if (errno == ENOTSOCK) { + /* getpeername() already found out this can't be a UNIX + socket connection */ + } else if (net_getunixcred(fd, &cred) == 0) { + conn->remote_pid = cred.pid; + conn->remote_uid = cred.uid; + } + conn->unix_peer_checked = TRUE; + } + + connection_update_property_label(conn); + connection_update_label(conn); + connection_update_stream_names(conn); + connection_update_event(conn); + + conn->name = (conn->base_name != NULL ? + conn->base_name : conn->property_label); +} + +static void connection_init_streams(struct connection *conn) +{ + const struct connection_settings *set = &conn->list->set; + + i_assert(conn->io == NULL); + i_assert(conn->input == NULL); + i_assert(conn->output == NULL); + i_assert(conn->to == NULL); + + conn->handshake_received = FALSE; + conn->version_received = set->major_version == 0; + + if (set->input_max_size != 0) { + if (conn->unix_socket) + conn->input = i_stream_create_unix(conn->fd_in, + set->input_max_size); + else + conn->input = i_stream_create_fd(conn->fd_in, + set->input_max_size); + i_stream_switch_ioloop_to(conn->input, conn->ioloop); + } + if (set->output_max_size != 0) { + if (conn->unix_socket) + conn->output = o_stream_create_unix(conn->fd_out, + set->output_max_size); + else + conn->output = o_stream_create_fd(conn->fd_out, + set->output_max_size); + o_stream_set_no_error_handling(conn->output, TRUE); + o_stream_set_finish_via_child(conn->output, FALSE); + o_stream_switch_ioloop_to(conn->output, conn->ioloop); + } + connection_update_stream_names(conn); + + conn->disconnected = FALSE; + i_assert(conn->to == NULL); + connection_input_resume_full(conn, FALSE); + i_assert(conn->to != NULL || conn->input_idle_timeout_secs == 0); + if (set->major_version != 0 && !set->dont_send_version) { + e_debug(conn->event, "Sending version handshake"); + o_stream_nsend_str(conn->output, t_strdup_printf( + "VERSION\t%s\t%u\t%u\n", set->service_name_out, + set->major_version, set->minor_version)); + } +} + +void connection_streams_changed(struct connection *conn) +{ + const struct connection_settings *set = &conn->list->set; + + if (set->input_max_size != 0 && conn->io != NULL) { + connection_input_halt(conn); + connection_input_resume(conn); + } +} + +static void connection_client_connected(struct connection *conn, bool success) +{ + i_assert(conn->list->set.client); + + connection_update_properties(conn); + + conn->connect_finished = ioloop_timeval; + + struct event_passthrough *e = event_create_passthrough(conn->event)-> + set_name("server_connection_connected"); + if (success) { + e_debug(e->event(), "Client connected (fd=%d)", + conn->fd_in); + } else { + e_debug(e->event(), "Client connection failed (fd=%d)", + conn->fd_in); + } + + if (success) + connection_init_streams(conn); + if (conn->v.client_connected != NULL) + conn->v.client_connected(conn, success); + if (!success) { + connection_closed(conn, CONNECTION_DISCONNECT_CONN_CLOSED); + } +} + +static void +connection_init_full(struct connection_list *list, struct connection *conn, + const char *name, int fd_in, int fd_out) +{ + if (conn->id == 0) { + if (list->id_counter == 0) + list->id_counter++; + conn->id = list->id_counter++; + } + + conn->ioloop = current_ioloop; + conn->fd_in = fd_in; + conn->fd_out = fd_out; + conn->disconnected = TRUE; + conn->remote_uid = (uid_t)-1; + conn->remote_pid = (pid_t)-1; + + i_free(conn->base_name); + conn->base_name = i_strdup(name); + + if (list->set.input_idle_timeout_secs != 0 && + conn->input_idle_timeout_secs == 0) { + conn->input_idle_timeout_secs = + list->set.input_idle_timeout_secs; + } + + if (conn->event == NULL) + conn->event = event_create(conn->event_parent); + if (list->set.debug) + event_set_forced_debug(conn->event, TRUE); + + if (conn->list != NULL) { + i_assert(conn->list == list); + } else { + conn->list = list; + DLLIST_PREPEND(&list->connections, conn); + list->connections_count++; + } + + connection_update_properties(conn); + connection_set_default_handlers(conn); +} + +void connection_init(struct connection_list *list, struct connection *conn, + const char *name) +{ + connection_init_full(list, conn, name, -1, -1); +} + +void connection_init_server(struct connection_list *list, + struct connection *conn, const char *name, + int fd_in, int fd_out) +{ + i_assert(!list->set.client); + + connection_init_full(list, conn, name, fd_in, fd_out); + + struct event_passthrough *e = event_create_passthrough(conn->event)-> + set_name("client_connection_connected"); + /* fd_out differs from fd_in only for stdin/stdout. Keep the logging + output nice and clean by logging only the fd_in. If it's 0, it'll + also be obvious that fd_out=1. */ + e_debug(e->event(), "Server accepted connection (fd=%d)", fd_in); + + connection_init_streams(conn); + if (conn->v.init != NULL) + conn->v.init(conn); +} + +void connection_init_server_ip(struct connection_list *list, + struct connection *conn, const char *name, + int fd_in, int fd_out, + const struct ip_addr *remote_ip, + in_port_t remote_port) +{ + if (remote_ip != NULL && remote_ip->family != 0) + conn->remote_ip = *remote_ip; + if (remote_port != 0) + conn->remote_port = remote_port; + + connection_init_server(list, conn, name, fd_in, fd_out); +} + +void connection_init_client_fd(struct connection_list *list, + struct connection *conn, const char *name, + int fd_in, int fd_out) +{ + i_assert(list->set.client); + + connection_init_full(list, conn, name, fd_in, fd_out); + + struct event_passthrough *e = event_create_passthrough(conn->event)-> + set_name("server_connection_connected"); + /* fd_out differs from fd_in only for stdin/stdout. Keep the logging + output nice and clean by logging only the fd_in. If it's 0, it'll + also be obvious that fd_out=1. */ + e_debug(e->event(), "Client connected (fd=%d)", fd_in); + + if (conn->v.init != NULL) + conn->v.init(conn); + connection_client_connected(conn, TRUE); +} + +void connection_init_client_ip_from(struct connection_list *list, + struct connection *conn, + const char *hostname, + const struct ip_addr *ip, in_port_t port, + const struct ip_addr *my_ip) +{ + const char *name = NULL; + + if (hostname != NULL) + name = t_strdup_printf("%s:%u", hostname, port); + + i_assert(list->set.client); + + conn->remote_ip = *ip; + conn->remote_port = port; + + if (my_ip != NULL) + conn->local_ip = *my_ip; + else + i_zero(&conn->local_ip); + + connection_init(list, conn, name); + if (hostname != NULL) + event_add_str(conn->event, "dest_host", hostname); + connection_update_event(conn); + + if (conn->v.init != NULL) + conn->v.init(conn); +} + +void connection_init_client_ip(struct connection_list *list, + struct connection *conn, const char *hostname, + const struct ip_addr *ip, in_port_t port) +{ + connection_init_client_ip_from(list, conn, hostname, ip, port, NULL); +} + +void connection_init_client_unix(struct connection_list *list, + struct connection *conn, const char *path) +{ + i_assert(list->set.client); + + conn->unix_socket = TRUE; + + connection_init(list, conn, path); + event_add_str(conn->event, "socket_path", path); + + if (conn->v.init != NULL) + conn->v.init(conn); +} + +void connection_init_from_streams(struct connection_list *list, + struct connection *conn, const char *name, + struct istream *input, struct ostream *output) +{ + connection_init_full(list, conn, name, + i_stream_get_fd(input), o_stream_get_fd(output)); + + i_assert(conn->fd_in >= 0); + i_assert(conn->fd_out >= 0); + i_assert(conn->io == NULL); + i_assert(conn->input == NULL); + i_assert(conn->output == NULL); + i_assert(conn->to == NULL); + + conn->input = input; + i_stream_ref(conn->input); + + conn->output = output; + o_stream_ref(conn->output); + o_stream_set_no_error_handling(conn->output, TRUE); + + connection_update_stream_names(conn); + + conn->disconnected = FALSE; + connection_input_resume_full(conn, FALSE); + + if (conn->v.client_connected != NULL) + conn->v.client_connected(conn, TRUE); +} + +static void connection_socket_connected(struct connection *conn) +{ + io_remove(&conn->io); + timeout_remove(&conn->to); + + errno = net_geterror(conn->fd_in); + connection_client_connected(conn, errno == 0); +} + +int connection_client_connect(struct connection *conn) +{ + const struct connection_settings *set = &conn->list->set; + int fd; + + i_assert(conn->list->set.client); + i_assert(conn->fd_in == -1); + + e_debug(conn->event, "Connecting"); + + if (conn->remote_port != 0) { + fd = net_connect_ip(&conn->remote_ip, conn->remote_port, + (conn->local_ip.family != 0 ? + &conn->local_ip : NULL)); + } else if (conn->list->set.unix_client_connect_msecs == 0) { + fd = net_connect_unix(conn->base_name); + } else { + fd = net_connect_unix_with_retries( + conn->base_name, + conn->list->set.unix_client_connect_msecs); + } + if (fd == -1) + return -1; + conn->fd_in = conn->fd_out = fd; + conn->connect_started = ioloop_timeval; + conn->disconnected = FALSE; + + if (conn->remote_port != 0 || + conn->list->set.delayed_unix_client_connected_callback) { + connection_update_properties(conn); + conn->io = io_add_to(conn->ioloop, conn->fd_out, IO_WRITE, + connection_socket_connected, conn); + e_debug(conn->event, + "Waiting for connect (fd=%d) to finish for max %u msecs", + fd, set->client_connect_timeout_msecs); + if (set->client_connect_timeout_msecs != 0) { + conn->to = timeout_add_to(conn->ioloop, + set->client_connect_timeout_msecs, + *conn->v.connect_timeout, conn); + } + } else { + connection_client_connected(conn, TRUE); + } + return 0; +} + +static void connection_update_counters(struct connection *conn) +{ + if (conn->input != NULL) + event_add_int(conn->event, "bytes_in", conn->input->v_offset); + if (conn->output != NULL) + event_add_int(conn->event, "bytes_out", conn->output->offset); +} + +void connection_disconnect(struct connection *conn) +{ + if (conn->disconnected) + return; + connection_update_counters(conn); + /* client connects to a Server, and Server gets connection from Client + */ + const char *ename = conn->list->set.client ? + "server_connection_disconnected" : + "client_connection_disconnected"; + + struct event_passthrough *e = event_create_passthrough(conn->event)-> + set_name(ename)-> + add_str("reason", connection_disconnect_reason(conn)); + e_debug(e->event(), "Disconnected: %s (fd=%d)", + connection_disconnect_reason(conn), conn->fd_in); + + conn->last_input = 0; + i_zero(&conn->last_input_tv); + timeout_remove(&conn->to); + io_remove(&conn->io); + i_stream_close(conn->input); + i_stream_destroy(&conn->input); + o_stream_close(conn->output); + o_stream_destroy(&conn->output); + fd_close_maybe_stdio(&conn->fd_in, &conn->fd_out); + conn->disconnected = TRUE; +} + +void connection_deinit(struct connection *conn) +{ + i_assert(conn->list->connections_count > 0); + + conn->list->connections_count--; + DLLIST_REMOVE(&conn->list->connections, conn); + + connection_disconnect(conn); + i_free(conn->base_name); + i_free(conn->label); + i_free(conn->property_label); + event_unref(&conn->event); + conn->list = NULL; +} + +int connection_input_read(struct connection *conn) +{ + conn->last_input = ioloop_time; + conn->last_input_tv = ioloop_timeval; + if (conn->to != NULL) + timeout_reset(conn->to); + + switch (i_stream_read(conn->input)) { + case -2: + /* buffer full */ + switch (conn->list->set.input_full_behavior) { + case CONNECTION_BEHAVIOR_DESTROY: + connection_closed(conn, + CONNECTION_DISCONNECT_BUFFER_FULL); + return -1; + case CONNECTION_BEHAVIOR_ALLOW: + return -2; + } + i_unreached(); + case -1: + /* disconnected */ + connection_closed(conn, CONNECTION_DISCONNECT_CONN_CLOSED); + return -1; + case 0: + /* nothing new read */ + return 0; + default: + /* something was read */ + return 1; + } +} + +const char *connection_disconnect_reason(struct connection *conn) +{ + switch (conn->disconnect_reason) { + case CONNECTION_DISCONNECT_DEINIT: + return "Deinitializing"; + case CONNECTION_DISCONNECT_CONNECT_TIMEOUT: { + unsigned int msecs = + conn->list->set.client_connect_timeout_msecs; + return t_strdup_printf("connect() timed out in %u.%03u secs", + msecs/1000, msecs%1000); + } + case CONNECTION_DISCONNECT_IDLE_TIMEOUT: + return "Idle timeout"; + case CONNECTION_DISCONNECT_CONN_CLOSED: + if (conn->input == NULL) + return t_strdup_printf("connect() failed: %m"); + /* fall through */ + case CONNECTION_DISCONNECT_NOT: + case CONNECTION_DISCONNECT_BUFFER_FULL: + return io_stream_get_disconnect_reason(conn->input, conn->output); + case CONNECTION_DISCONNECT_HANDSHAKE_FAILED: + return "Handshake failed"; + } + i_unreached(); +} + +const char *connection_input_timeout_reason(struct connection *conn) +{ + if (conn->last_input_tv.tv_sec != 0) { + int diff = timeval_diff_msecs(&ioloop_timeval, + &conn->last_input_tv); + return t_strdup_printf("No input for %u.%03u secs", + diff/1000, diff%1000); + } else if (conn->connect_finished.tv_sec != 0) { + int diff = timeval_diff_msecs(&ioloop_timeval, + &conn->connect_finished); + return t_strdup_printf( + "No input since connected %u.%03u secs ago", + diff/1000, diff%1000); + } else { + int diff = timeval_diff_msecs(&ioloop_timeval, + &conn->connect_started); + return t_strdup_printf("connect() timed out after %u.%03u secs", + diff/1000, diff%1000); + } +} + +void connection_set_handlers(struct connection *conn, + const struct connection_vfuncs *vfuncs) +{ + connection_input_halt(conn); + i_assert(vfuncs->destroy != NULL); + conn->v = *vfuncs; + if (conn->v.input == NULL) + conn->v.input = connection_input_default; + if (conn->v.input_line == NULL) + conn->v.input_line = connection_input_line_default; + if (conn->v.handshake_args == NULL) + conn->v.handshake_args = connection_handshake_args_default; + if (conn->v.idle_timeout == NULL) + conn->v.idle_timeout = connection_idle_timeout; + if (conn->v.connect_timeout == NULL) + conn->v.connect_timeout = connection_connect_timeout; + if (!conn->disconnected) + connection_input_resume_full(conn, FALSE); +} + +void connection_set_default_handlers(struct connection *conn) +{ + connection_set_handlers(conn, &conn->list->v); +} + +void connection_switch_ioloop_to(struct connection *conn, + struct ioloop *ioloop) +{ + conn->ioloop = ioloop; + if (conn->io != NULL) + conn->io = io_loop_move_io_to(ioloop, &conn->io); + if (conn->to != NULL) + conn->to = io_loop_move_timeout_to(ioloop, &conn->to); + if (conn->input != NULL) + i_stream_switch_ioloop_to(conn->input, ioloop); + if (conn->output != NULL) + o_stream_switch_ioloop_to(conn->output, ioloop); +} + +void connection_switch_ioloop(struct connection *conn) +{ + connection_switch_ioloop_to(conn, current_ioloop); +} + +struct connection_list * +connection_list_init(const struct connection_settings *set, + const struct connection_vfuncs *vfuncs) +{ + struct connection_list *list; + + i_assert(vfuncs->input != NULL || + set->input_full_behavior != CONNECTION_BEHAVIOR_ALLOW); + i_assert(set->major_version == 0 || + (set->service_name_in != NULL && + set->service_name_out != NULL && + set->output_max_size != 0)); + + list = i_new(struct connection_list, 1); + list->set = *set; + list->v = *vfuncs; + + return list; +} + +void connection_list_deinit(struct connection_list **_list) +{ + struct connection_list *list = *_list; + struct connection *conn; + + *_list = NULL; + + while (list->connections != NULL) { + conn = list->connections; + connection_closed(conn, CONNECTION_DISCONNECT_DEINIT); + i_assert(conn != list->connections); + } + i_free(list); +} diff --git a/src/lib/connection.h b/src/lib/connection.h new file mode 100644 index 0000000..612c540 --- /dev/null +++ b/src/lib/connection.h @@ -0,0 +1,260 @@ +#ifndef CONNECTION_H +#define CONNECTION_H + +#include "net.h" + +struct ioloop; +struct connection; + +enum connection_behavior { + CONNECTION_BEHAVIOR_DESTROY = 0, + CONNECTION_BEHAVIOR_ALLOW +}; + +enum connection_disconnect_reason { + /* not disconnected yet */ + CONNECTION_DISCONNECT_NOT = 0, + /* normal requested disconnection */ + CONNECTION_DISCONNECT_DEINIT, + /* input buffer full */ + CONNECTION_DISCONNECT_BUFFER_FULL, + /* connection got disconnected */ + CONNECTION_DISCONNECT_CONN_CLOSED, + /* connect() timed out */ + CONNECTION_DISCONNECT_CONNECT_TIMEOUT, + /* remote didn't send input */ + CONNECTION_DISCONNECT_IDLE_TIMEOUT, + /* handshake failed */ + CONNECTION_DISCONNECT_HANDSHAKE_FAILED, +}; + +struct connection_vfuncs { + void (*init)(struct connection *conn); + void (*destroy)(struct connection *conn); + /* For UNIX socket clients this gets called immediately (unless + delayed_unix_client_connected_callback=TRUE) with success=TRUE, + for IP connections it gets called later: + + If connect() fails, sets success=FALSE and errno. Streams aren't + initialized in that situation either. destroy() is called after + the callback. */ + void (*client_connected)(struct connection *conn, bool success); + + /* implement one of the input*() methods. + They return 1 = ok, continue. 0 = ok, but stop processing more + lines, -1 = error, disconnect the client. */ + void (*input)(struct connection *conn); + int (*input_line)(struct connection *conn, const char *line); + int (*input_args)(struct connection *conn, const char *const *args); + + /* handshake functions. Defaults to version checking. + must return 1 when handshake is completed, otherwise return 0. + return -1 to indicate error and disconnect client. + + if you implement this, remember to call connection_verify_version + yourself, otherwise you end up with assert crash. + + these will not be called if you implement `input` virtual function. + */ + int (*handshake)(struct connection *conn); + int (*handshake_line)(struct connection *conn, const char *line); + int (*handshake_args)(struct connection *conn, const char *const *args); + + /* Called when the connection handshake is ready. */ + void (*handshake_ready)(struct connection *conn); + + /* Called when input_idle_timeout_secs is reached, defaults to disconnect */ + void (*idle_timeout)(struct connection *conn); + /* Called when client_connect_timeout_msecs is reached, defaults to disconnect */ + void (*connect_timeout)(struct connection *conn); +}; + +struct connection_settings { + const char *service_name_in; + const char *service_name_out; + unsigned int major_version, minor_version; + + unsigned int client_connect_timeout_msecs; + unsigned int input_idle_timeout_secs; + + /* These need to be non-zero for corresponding stream to + be created. */ + size_t input_max_size; + size_t output_max_size; + enum connection_behavior input_full_behavior; + + /* Set to TRUE if this is a client */ + bool client; + + /* Set to TRUE if version should not be sent */ + bool dont_send_version; + /* By default when only input_args() is used, or when + connection_input_line_default() is used, empty lines aren't allowed + since it would result in additional args[0] == NULL check. Setting + this to TRUE passes it through instead of logging an error. */ + bool allow_empty_args_input; + /* Don't call client_connected() immediately on + connection_client_connect() with UNIX sockets. This is mainly + to make the functionality identical with inet sockets, which may + simplify the calling code. */ + bool delayed_unix_client_connected_callback; + /* Put the connection id in the log prefix */ + bool log_connection_id; + /* If connect() to UNIX socket fails with EAGAIN, retry for this many + milliseconds before giving up (0 = try once) */ + unsigned int unix_client_connect_msecs; + /* Turn on debug logging */ + bool debug; +}; + +struct connection { + struct connection *prev, *next; + struct connection_list *list; + + /* The name for the connection provided by the application. This is + usually a host name or a unix socket path. This may be NULL if the + application provides none. */ + char *base_name; + /* The name of the connection determined by the connection API. It is + equal to base_name when that is available and otherwise it is + composed from the connection properties; e.g., "ip:port". */ + const char *name; + + char *label; + char *property_label; + unsigned int id; + + int fd_in, fd_out; + struct ioloop *ioloop; + struct io *io; + struct istream *input; + struct ostream *output; + + unsigned int input_idle_timeout_secs; + struct timeout *to; + time_t last_input; + struct timeval last_input_tv; + struct timeval connect_started; + struct timeval connect_finished; + + /* set to parent event before calling init */ + struct event *event_parent; + struct event *event; + + /* connection properties */ + struct ip_addr local_ip, remote_ip; + in_port_t remote_port; + pid_t remote_pid; + uid_t remote_uid; + + /* received minor version */ + unsigned int minor_version; + + /* handlers */ + struct connection_vfuncs v; + + enum connection_disconnect_reason disconnect_reason; + + bool version_received:1; + bool handshake_received:1; + bool unix_socket:1; + bool unix_peer_checked:1; + bool disconnected:1; +}; + +struct connection_list { + struct connection *connections; + unsigned int connections_count; + + unsigned int id_counter; + + struct connection_settings set; + struct connection_vfuncs v; +}; + +void connection_init(struct connection_list *list, struct connection *conn, + const char *name) ATTR_NULL(3); +void connection_init_server(struct connection_list *list, + struct connection *conn, const char *name, + int fd_in, int fd_out) ATTR_NULL(3); +void connection_init_server_ip(struct connection_list *list, + struct connection *conn, const char *name, + int fd_in, int fd_out, + const struct ip_addr *remote_ip, + in_port_t remote_port) ATTR_NULL(3, 6); +void connection_init_client_ip(struct connection_list *list, + struct connection *conn, const char *hostname, + const struct ip_addr *ip, in_port_t port) + ATTR_NULL(3); +void connection_init_client_ip_from(struct connection_list *list, + struct connection *conn, + const char *hostname, + const struct ip_addr *ip, in_port_t port, + const struct ip_addr *my_ip) ATTR_NULL(3,6); +void connection_init_client_unix(struct connection_list *list, + struct connection *conn, const char *path); +void connection_init_client_fd(struct connection_list *list, + struct connection *conn, const char *name, + int fd_int, int fd_out) ATTR_NULL(3); +void connection_init_from_streams(struct connection_list *list, + struct connection *conn, const char *name, + struct istream *input, + struct ostream *output) ATTR_NULL(3); + +int connection_client_connect(struct connection *conn); + +/* Disconnects a connection */ +void connection_disconnect(struct connection *conn); + +/* Deinitializes a connection, calls disconnect */ +void connection_deinit(struct connection *conn); + +void connection_input_halt(struct connection *conn); +/* Resume connection handling. If a new IO was added, it's marked as having + pending input. */ +void connection_input_resume(struct connection *conn); + +/* Update event fields and log prefix based on connection properties. */ +void connection_update_event(struct connection *conn); + +/* Update connection properties and labels */ +void connection_update_properties(struct connection *conn); + +/* This needs to be called if the input/output streams are changed */ +void connection_streams_changed(struct connection *conn); + +/* Returns -1 = disconnected, 0 = nothing new, 1 = something new. + If input_full_behavior is ALLOW, may return also -2 = buffer full. */ +int connection_input_read(struct connection *conn); +/* Verify that VERSION input matches what we expect. */ +int connection_verify_version(struct connection *conn, + const char *service_name, + unsigned int major_version, + unsigned int minor_version); + +int connection_handshake_args_default(struct connection *conn, + const char *const *args); + +/* Returns human-readable reason for why connection was disconnected. */ +const char *connection_disconnect_reason(struct connection *conn); +/* Returns human-readable reason for why connection timed out, + e.g. "No input for 10.023 secs". */ +const char *connection_input_timeout_reason(struct connection *conn); + +void connection_switch_ioloop_to(struct connection *conn, + struct ioloop *ioloop); +void connection_switch_ioloop(struct connection *conn); + +struct connection_list * +connection_list_init(const struct connection_settings *set, + const struct connection_vfuncs *vfuncs); +void connection_list_deinit(struct connection_list **list); + +void connection_input_default(struct connection *conn); +int connection_input_line_default(struct connection *conn, const char *line); + +/* Change handlers, calls connection_input_halt and connection_input_resume */ +void connection_set_handlers(struct connection *conn, const struct connection_vfuncs *vfuncs); +void connection_set_default_handlers(struct connection *conn); + +#endif diff --git a/src/lib/cpu-limit.c b/src/lib/cpu-limit.c new file mode 100644 index 0000000..754717a --- /dev/null +++ b/src/lib/cpu-limit.c @@ -0,0 +1,200 @@ +/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "lib-signals.h" +#include "time-util.h" +#include "cpu-limit.h" + +#include <sys/time.h> +#include <sys/resource.h> + +struct cpu_limit { + struct cpu_limit *parent; + + enum cpu_limit_type type; + unsigned int cpu_limit_secs; + struct rusage initial_usage; + + bool limit_reached; +}; + +static struct cpu_limit *cpu_limit; +static struct rlimit orig_limit, last_set_rlimit; +static volatile sig_atomic_t xcpu_signal_counter; +static sig_atomic_t checked_signal_counter; +static unsigned int rlim_cur_adjust_secs; + +static void +cpu_limit_handler(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) +{ + xcpu_signal_counter++; +} + +static unsigned int +cpu_limit_get_usage_msecs_with(struct cpu_limit *climit, + enum cpu_limit_type type, + const struct rusage *rusage) +{ + struct timeval cpu_usage = { 0, 0 }; + int usage_diff; + + if ((type & CPU_LIMIT_TYPE_USER) != 0) + timeval_add(&cpu_usage, &rusage->ru_utime); + if ((type & CPU_LIMIT_TYPE_SYSTEM) != 0) + timeval_add(&cpu_usage, &rusage->ru_stime); + + struct timeval initial_total = { 0, 0 }; + if ((type & CPU_LIMIT_TYPE_USER) != 0) + timeval_add(&initial_total, &climit->initial_usage.ru_utime); + if ((type & CPU_LIMIT_TYPE_SYSTEM) != 0) + timeval_add(&initial_total, &climit->initial_usage.ru_stime); + usage_diff = timeval_diff_msecs(&cpu_usage, &initial_total); + i_assert(usage_diff >= 0); + + return (unsigned int)usage_diff; +} + +unsigned int +cpu_limit_get_usage_msecs(struct cpu_limit *climit, enum cpu_limit_type type) +{ + struct rusage rusage; + + /* Query cpu usage so far */ + if (getrusage(RUSAGE_SELF, &rusage) < 0) + i_fatal("getrusage() failed: %m"); + + return cpu_limit_get_usage_msecs_with(climit, type, &rusage); +} + +static bool +cpu_limit_update_recursive(struct cpu_limit *climit, + const struct rusage *rusage, + unsigned int *max_wait_secs) +{ + if (climit == NULL) + return FALSE; + if (cpu_limit_update_recursive(climit->parent, rusage, max_wait_secs)) { + /* parent's limit reached */ + climit->limit_reached = TRUE; + return TRUE; + } + unsigned int secs_used = + cpu_limit_get_usage_msecs_with(climit, climit->type, rusage)/1000; + if (secs_used >= climit->cpu_limit_secs) { + climit->limit_reached = TRUE; + return TRUE; + } + unsigned int secs_left = climit->cpu_limit_secs - secs_used; + if (*max_wait_secs > secs_left) + *max_wait_secs = secs_left; + return FALSE; +} + +static void cpu_limit_update_rlimit(void) +{ + struct rusage rusage; + struct rlimit rlimit; + unsigned int max_wait_secs = UINT_MAX; + + if (getrusage(RUSAGE_SELF, &rusage) < 0) + i_fatal("getrusage() failed: %m"); + + (void)cpu_limit_update_recursive(cpu_limit, &rusage, &max_wait_secs); + if (max_wait_secs == UINT_MAX) { + /* All the limits have reached now. Restore the original + limits. */ + rlimit = orig_limit; + } else { + struct timeval tv_limit = rusage.ru_utime; + timeval_add(&tv_limit, &rusage.ru_stime); + + i_zero(&rlimit); + /* Add +1 second to round up. */ + rlimit.rlim_cur = tv_limit.tv_sec + + max_wait_secs + 1 + rlim_cur_adjust_secs; + rlimit.rlim_max = orig_limit.rlim_max; + } + if (last_set_rlimit.rlim_cur != rlimit.rlim_cur) { + last_set_rlimit = rlimit; + if (setrlimit(RLIMIT_CPU, &rlimit) < 0) + i_fatal("setrlimit() failed: %m"); + } +} + +struct cpu_limit * +cpu_limit_init(unsigned int cpu_limit_secs, enum cpu_limit_type type) +{ + struct cpu_limit *climit; + struct rusage rusage; + + i_assert(cpu_limit_secs > 0); + i_assert(type != 0); + + climit = i_new(struct cpu_limit, 1); + climit->parent = cpu_limit; + climit->type = type; + climit->cpu_limit_secs = cpu_limit_secs; + + /* Query current limit */ + if (climit->parent == NULL) { + if (getrlimit(RLIMIT_CPU, &orig_limit) < 0) + i_fatal("getrlimit() failed: %m"); + } + + /* Query cpu usage so far */ + if (getrusage(RUSAGE_SELF, &rusage) < 0) + i_fatal("getrusage() failed: %m"); + climit->initial_usage = rusage; + + if (climit->parent == NULL) { + lib_signals_set_handler(SIGXCPU, LIBSIG_FLAG_RESTART, + cpu_limit_handler, NULL); + } + + cpu_limit = climit; + cpu_limit_update_rlimit(); + return climit; +} + +void cpu_limit_deinit(struct cpu_limit **_climit) +{ + struct cpu_limit *climit = *_climit; + + *_climit = NULL; + if (climit == NULL) + return; + + i_assert(climit == cpu_limit); + + cpu_limit = climit->parent; + cpu_limit_update_rlimit(); + if (climit->parent == NULL) + lib_signals_unset_handler(SIGXCPU, cpu_limit_handler, NULL); + i_free(climit); +} + +bool cpu_limit_exceeded(struct cpu_limit *climit) +{ + static struct timeval tv_last = { 0, 0 }; + struct timeval tv_now; + + if (checked_signal_counter != xcpu_signal_counter) { + i_gettimeofday(&tv_now); + if (tv_last.tv_sec != 0 && + timeval_diff_msecs(&tv_now, &tv_last) < 1000) { + /* Additional sanity check: We're getting here more + rapidly than once per second. This isn't expected + to happen, but at least in theory it could happen + because rlim_cur isn't clearly calculated from just + the user+system CPU usage. So in case rlim_cur is + too low and keeps firing XCPU signal, try to + increase rlim_cur by 1 second. Eventually it should + become large enough. */ + rlim_cur_adjust_secs++; + } + + checked_signal_counter = xcpu_signal_counter; + cpu_limit_update_rlimit(); + } + return climit->limit_reached; +} diff --git a/src/lib/cpu-limit.h b/src/lib/cpu-limit.h new file mode 100644 index 0000000..b6e45ae --- /dev/null +++ b/src/lib/cpu-limit.h @@ -0,0 +1,67 @@ +#ifndef CPU_LIMIT +#define CPU_LIMIT + +struct cpu_limit; + +enum cpu_limit_type { + CPU_LIMIT_TYPE_USER = BIT(0), + CPU_LIMIT_TYPE_SYSTEM = BIT(1), +}; +#define CPU_LIMIT_TYPE_ALL (CPU_LIMIT_TYPE_USER | CPU_LIMIT_TYPE_SYSTEM) + +/* Start tracking CPU usage. This internally uses setrlimit(RLIMIT_CPU) to + trigger SIGXCPU to avoid constantly calling getrlimit() to check if the CPU + usage has reached a limit. Once all limits created by this API are released, + the original CPU resource limits are restored (if any). + + CPU time limits can be nested, i.e. they are never independent. The outer + limits contain the bounded maximum limit for the inner limits. For example + the code execution flow might be: + - Set 30s CPU limit (outer limit) + - Use up 5s of CPU + - Set 40s CPU limit (inner limit) + - Infinite loop + The inner loop's limit won't even be reached here. After the inner loops + runs for 25 seconds, the outer loop's 30s limit is reached. This causes + both the inner and the other limit's cpu_limit_exceeded() to return TRUE. + It's expected that the inner execution stops and returns back to the outer + execution, which notices that the outer execution has also reached the limit. + + Another example where the inner limit is reached: + - Set 30s CPU limit (outer limit) + - Use up 5s of CPU + - Set 10s CPU limit (inner limit) + - Infinite loop + Here the inner 10s limit is reached, and the inner execution stops. The + outer execution could still run for another 15 seconds. + + Example usage: + + bool limit_reached = FALSE; + limit = cpu_limit_init(5, CPU_LIMIT_TYPE_ALL); + while (long_operation_iterate_once()) { + if (cpu_limit_exceeded(limit)) { + limit_reached = TRUE; // operation took >=5 secs + break; + } + } + cpu_limit_deinit(&limit); +*/ +struct cpu_limit * +cpu_limit_init(unsigned int cpu_limit_secs, enum cpu_limit_type type); +void cpu_limit_deinit(struct cpu_limit **_climit); + +/* Returns TRUE if the CPU limit has been exceeded for this limit or any of its + parents. */ +bool cpu_limit_exceeded(struct cpu_limit *climit); + +unsigned int cpu_limit_get_usage_msecs(struct cpu_limit *climit, + enum cpu_limit_type type); + +static inline unsigned int +cpu_limit_get_usage_secs(struct cpu_limit *climit, enum cpu_limit_type type) +{ + return cpu_limit_get_usage_msecs(climit, type) / 1000; +} + +#endif diff --git a/src/lib/crc32.c b/src/lib/crc32.c new file mode 100644 index 0000000..576c689 --- /dev/null +++ b/src/lib/crc32.c @@ -0,0 +1,91 @@ +/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "crc32.h" + +static uint32_t crc32tab[256] = { + 0x00000000, + 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, + 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, + 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, + 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, + 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, + 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, + 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, + 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, + 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, + 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, + 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, + 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, + 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, + 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, + 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, + 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, + 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, + 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, + 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, + 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, + 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, + 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, + 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, + 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, + 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, + 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, + 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, + 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, + 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, + 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, + 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, + 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, + 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, + 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, + 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, + 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, + 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +uint32_t crc32_data(const void *data, size_t size) +{ + return crc32_data_more(0, data, size); +} + +uint32_t crc32_data_more(uint32_t crc, const void *data, size_t size) +{ + const uint8_t *p = data, *end = p + size; + + crc ^= 0xffffffff; + for (; p != end; p++) + crc = (crc >> 8) ^ crc32tab[((crc ^ *p) & 0xff)]; + crc ^= 0xffffffff; + return crc; +} + +uint32_t crc32_str(const char *str) +{ + return crc32_str_more(0, str); +} + +uint32_t crc32_str_more(uint32_t crc, const char *str) +{ + const uint8_t *p = (const uint8_t *)str; + + crc ^= 0xffffffff; + for (; *p != '\0'; p++) + crc = (crc >> 8) ^ crc32tab[((crc ^ *p) & 0xff)]; + crc ^= 0xffffffff; + return crc; +} diff --git a/src/lib/crc32.h b/src/lib/crc32.h new file mode 100644 index 0000000..7892212 --- /dev/null +++ b/src/lib/crc32.h @@ -0,0 +1,10 @@ +#ifndef CRC32_H +#define CRC32_H + +uint32_t crc32_data(const void *data, size_t size) ATTR_PURE; +uint32_t crc32_str(const char *str) ATTR_PURE; + +uint32_t crc32_data_more(uint32_t crc, const void *data, size_t size) ATTR_PURE; +uint32_t crc32_str_more(uint32_t crc, const char *str) ATTR_PURE; + +#endif diff --git a/src/lib/data-stack.c b/src/lib/data-stack.c new file mode 100644 index 0000000..acbedc9 --- /dev/null +++ b/src/lib/data-stack.c @@ -0,0 +1,777 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +/* @UNSAFE: whole file */ + +#include "lib.h" +#include "backtrace-string.h" +#include "str.h" +#include "data-stack.h" + + +/* Initial stack size - this should be kept in a size that doesn't exceed + in a normal use to avoid extra malloc()ing. */ +#ifdef DEBUG +# define INITIAL_STACK_SIZE (1024*10) +#else +# define INITIAL_STACK_SIZE (1024*32) +#endif + +#ifdef DEBUG +# define CLEAR_CHR 0xD5 /* D5 is mnemonic for "Data 5tack" */ +# define SENTRY_COUNT (4*8) +# define BLOCK_CANARY ((void *)0xBADBADD5BADBADD5) /* contains 'D5' */ +# define ALLOC_SIZE(size) (MEM_ALIGN(sizeof(size_t)) + MEM_ALIGN(size + SENTRY_COUNT)) +#else +# define CLEAR_CHR 0 +# define BLOCK_CANARY NULL +# define block_canary_check(block) do { ; } while(0) +# define ALLOC_SIZE(size) MEM_ALIGN(size) +#endif + +struct stack_block { + struct stack_block *prev, *next; + + size_t size, left; +#ifdef DEBUG + /* The lowest value that "left" has been in this block since it was + last popped. This is used to keep track which parts of the block + needs to be cleared if DEBUG is used. */ + size_t left_lowwater; +#endif + /* NULL or a poison value, just in case something accesses + the memory in front of an allocated area */ + void *canary; + unsigned char data[FLEXIBLE_ARRAY_MEMBER]; +}; + +#define SIZEOF_MEMBLOCK MEM_ALIGN(sizeof(struct stack_block)) + +#define STACK_BLOCK_DATA(block) \ + (block->data + (SIZEOF_MEMBLOCK - sizeof(struct stack_block))) + +struct stack_frame { + struct stack_frame *prev; + + struct stack_block *block; + /* Each frame initializes this to current_block->left, i.e. how much + free space is left in the block. So the frame's start position in + the block is (block.size - block_space_left) */ + size_t block_space_left; + size_t last_alloc_size; + const char *marker; +#ifdef DEBUG + /* Fairly arbitrary profiling data */ + unsigned long long alloc_bytes; + unsigned int alloc_count; +#endif +}; + +#ifdef STATIC_CHECKER +struct data_stack_frame { + unsigned int id; +}; +#endif + +unsigned int data_stack_frame_id = 0; + +static bool data_stack_initialized = FALSE; +static data_stack_frame_t root_frame_id; + +static struct stack_frame *current_frame; + +/* The latest block currently used for allocation. current_block->next is + always NULL. */ +static struct stack_block *current_block; +/* The largest block that data stack has allocated so far, which was already + freed. This can prevent rapid malloc()+free()ing when data stack is grown + and shrunk constantly. */ +static struct stack_block *unused_block = NULL; + +static struct event *event_datastack = NULL; +static bool event_datastack_deinitialized = FALSE; + +static struct stack_block *last_buffer_block; +static size_t last_buffer_size; +static bool outofmem = FALSE; + +static union { + struct stack_block block; + unsigned char data[512]; +} outofmem_area; + +static struct stack_block *mem_block_alloc(size_t min_size); + +static inline +unsigned char *data_stack_after_last_alloc(struct stack_block *block) +{ + return STACK_BLOCK_DATA(block) + (block->size - block->left); +} + +static void data_stack_last_buffer_reset(bool preserve_data ATTR_UNUSED) +{ + if (last_buffer_block != NULL) { +#ifdef DEBUG + unsigned char *last_alloc_end, *p, *pend; + + /* We assume that this function gets called before + current_block changes. */ + i_assert(last_buffer_block == current_block); + + last_alloc_end = data_stack_after_last_alloc(current_block); + p = last_alloc_end + MEM_ALIGN(sizeof(size_t)) + last_buffer_size; + pend = last_alloc_end + ALLOC_SIZE(last_buffer_size); +#endif + /* reset t_buffer_get() mark - not really needed but makes it + easier to notice if t_malloc()/t_push()/t_pop() is called + between t_buffer_get() and t_buffer_alloc(). + do this before we get to i_panic() to avoid recursive + panics. */ + last_buffer_block = NULL; + +#ifdef DEBUG + /* NOTE: If the below panic triggers, it may also be due to an + internal bug in data-stack (since this is rather complex). While + debugging whether that is the case, it's a good idea to change the + i_panic() to abort(). Otherwise the i_panic() changes the + data-stack's internal state and complicates debugging. */ + while (p < pend) + if (*p++ != CLEAR_CHR) + i_panic("t_buffer_get(): buffer overflow"); + + if (!preserve_data) { + p = last_alloc_end; + memset(p, CLEAR_CHR, SENTRY_COUNT); + } +#endif + } +} + +data_stack_frame_t t_push(const char *marker) +{ + struct stack_frame *frame; + + i_assert(marker != NULL); + + if (unlikely(!data_stack_initialized)) { + /* kludgy, but allow this before initialization */ + data_stack_init(); + return t_push(marker); + } + + /* allocate new block */ + frame = t_buffer_get(sizeof(*frame)); + frame->prev = current_frame; + current_frame = frame; + + /* mark our current position */ + current_frame->block = current_block; + current_frame->block_space_left = current_block->left; + current_frame->last_alloc_size = 0; + current_frame->marker = marker; +#ifdef DEBUG + current_frame->alloc_bytes = 0; + current_frame->alloc_count = 0; +#endif + + t_buffer_alloc(sizeof(*frame)); + +#ifndef STATIC_CHECKER + return data_stack_frame_id++; +#else + struct data_stack_frame *ds_frame = i_new(struct data_stack_frame, 1); + ds_frame->id = data_stack_frame_id++; + return ds_frame; +#endif +} + +data_stack_frame_t t_push_named(const char *format, ...) +{ + data_stack_frame_t ret = t_push(format); +#ifdef DEBUG + va_list args; + va_start(args, format); + current_frame->marker = p_strdup_vprintf(unsafe_data_stack_pool, format, args); + va_end(args); +#else + (void)format; /* unused in non-DEBUG builds */ +#endif + + return ret; +} + +#ifdef DEBUG +static void block_canary_check(struct stack_block *block) +{ + if (block->canary != BLOCK_CANARY) { + /* Make sure i_panic() won't try to allocate from the + same block by falling back onto our emergency block. */ + current_block = &outofmem_area.block; + i_panic("Corrupted data stack canary"); + } +} +#endif + +static void free_blocks(struct stack_block *block) +{ + struct stack_block *next; + + /* free all the blocks, except if any of them is bigger than + unused_block, replace it */ + while (block != NULL) { + block_canary_check(block); + next = block->next; + +#ifdef DEBUG + memset(STACK_BLOCK_DATA(block), CLEAR_CHR, block->size); +#endif + + if (block == &outofmem_area.block) + ; + else if (unused_block == NULL || + block->size > unused_block->size) { + free(unused_block); + unused_block = block; + } else { + free(block); + } + + block = next; + } +} + +#ifdef DEBUG +static void t_pop_verify(void) +{ + struct stack_block *block; + unsigned char *p; + size_t pos, max_pos, used_size; + + block = current_frame->block; + pos = block->size - current_frame->block_space_left; + while (block != NULL) { + block_canary_check(block); + used_size = block->size - block->left; + p = STACK_BLOCK_DATA(block); + while (pos < used_size) { + size_t requested_size = *(size_t *)(p + pos); + if (used_size - pos < requested_size) + i_panic("data stack[%s]: saved alloc size broken", + current_frame->marker); + max_pos = pos + ALLOC_SIZE(requested_size); + pos += MEM_ALIGN(sizeof(size_t)) + requested_size; + + for (; pos < max_pos; pos++) { + if (p[pos] != CLEAR_CHR) + i_panic("data stack[%s]: buffer overflow", + current_frame->marker); + } + } + + /* if we had used t_buffer_get(), the rest of the buffer + may not contain CLEAR_CHRs. but we've already checked all + the allocations, so there's no need to check them anyway. */ + block = block->next; + pos = 0; + } +} +#endif + +void t_pop_last_unsafe(void) +{ + size_t block_space_left; + + if (unlikely(current_frame == NULL)) + i_panic("t_pop() called with empty stack"); + + data_stack_last_buffer_reset(FALSE); +#ifdef DEBUG + t_pop_verify(); +#endif + + /* Usually the block doesn't change. If it doesn't, the next pointer + must also be NULL. */ + if (current_block != current_frame->block) { + current_block = current_frame->block; + if (current_block->next != NULL) { + /* free unused blocks */ + free_blocks(current_block->next); + current_block->next = NULL; + } + } + block_canary_check(current_block); + + /* current_frame points inside the stack frame that will be freed. + make sure it's not accessed after it's already freed/cleaned. */ + block_space_left = current_frame->block_space_left; + current_frame = current_frame->prev; + +#ifdef DEBUG + size_t start_pos, end_pos; + + start_pos = current_block->size - block_space_left; + end_pos = current_block->size - current_block->left_lowwater; + i_assert(end_pos >= start_pos); + memset(STACK_BLOCK_DATA(current_block) + start_pos, CLEAR_CHR, + end_pos - start_pos); + current_block->left_lowwater = block_space_left; +#endif + + current_block->left = block_space_left; + + data_stack_frame_id--; +} + +bool t_pop(data_stack_frame_t *id) +{ + t_pop_last_unsafe(); +#ifndef STATIC_CHECKER + if (unlikely(data_stack_frame_id != *id)) + return FALSE; + *id = 0; +#else + unsigned int frame_id = (*id)->id; + i_free_and_null(*id); + + if (unlikely(data_stack_frame_id != frame_id)) + return FALSE; +#endif + return TRUE; +} + +bool t_pop_pass_str(data_stack_frame_t *id, const char **str) +{ + if (str == NULL || !data_stack_frame_contains(id, *str)) + return t_pop(id); + + /* FIXME: The string could be memmove()d to the beginning of the + data stack frame and the previous frame's size extended past it. + This would avoid the malloc. It's a bit complicated though. */ + char *tmp_str = i_strdup(*str); + bool ret = t_pop(id); + *str = t_strdup(tmp_str); + i_free(tmp_str); + return ret; +} + +static void mem_block_reset(struct stack_block *block) +{ + block->prev = NULL; + block->next = NULL; + block->left = block->size; +#ifdef DEBUG + block->left_lowwater = block->size; +#endif +} + +static struct stack_block *mem_block_alloc(size_t min_size) +{ + struct stack_block *block; + size_t prev_size, alloc_size; + + prev_size = current_block == NULL ? 0 : current_block->size; + /* Use INITIAL_STACK_SIZE without growing it to nearest power. */ + alloc_size = prev_size == 0 ? min_size : + nearest_power(MALLOC_ADD(prev_size, min_size)); + + /* nearest_power() returns 2^n values, so alloc_size can't be + anywhere close to SIZE_MAX */ + block = malloc(SIZEOF_MEMBLOCK + alloc_size); + if (unlikely(block == NULL)) { + if (outofmem) { + if (min_size > outofmem_area.block.left) + abort(); + return &outofmem_area.block; + } + outofmem = TRUE; + i_panic("data stack: Out of memory when allocating %zu bytes", + alloc_size + SIZEOF_MEMBLOCK); + } + block->size = alloc_size; + block->canary = BLOCK_CANARY; + mem_block_reset(block); +#ifdef DEBUG + memset(STACK_BLOCK_DATA(block), CLEAR_CHR, alloc_size); +#endif + return block; +} + +static void data_stack_send_grow_event(size_t last_alloc_size) +{ + if (event_datastack_deinitialized) { + /* already in the deinitialization code - + don't send more events */ + return; + } + if (event_datastack == NULL) + event_datastack = event_create(NULL); + event_set_name(event_datastack, "data_stack_grow"); + event_add_int(event_datastack, "alloc_size", data_stack_get_alloc_size()); + event_add_int(event_datastack, "used_size", data_stack_get_used_size()); + event_add_int(event_datastack, "last_alloc_size", last_alloc_size); + event_add_int(event_datastack, "last_block_size", current_block->size); +#ifdef DEBUG + event_add_int(event_datastack, "frame_alloc_bytes", + current_frame->alloc_bytes); + event_add_int(event_datastack, "frame_alloc_count", + current_frame->alloc_count); +#endif + event_add_str(event_datastack, "frame_marker", current_frame->marker); + + /* It's possible that the data stack gets grown and shrunk rapidly. + Try to avoid doing expensive work if the event isn't even used for + anything. Note that at this point all the event fields must be + set already that might potentially be used by the filters. */ + if (!event_want_debug(event_datastack)) + return; + + /* Getting backtrace is potentially inefficient, so do it after + checking if the event is wanted. Note that this prevents using the + backtrace field in event field comparisons. */ + const char *backtrace; + if (backtrace_get(&backtrace) == 0) + event_add_str(event_datastack, "backtrace", backtrace); + + string_t *str = t_str_new(128); + str_printfa(str, "total_used=%zu, total_alloc=%zu, last_alloc_size=%zu", + data_stack_get_used_size(), + data_stack_get_alloc_size(), + last_alloc_size); +#ifdef DEBUG + str_printfa(str, ", frame_bytes=%llu, frame_alloc_count=%u", + current_frame->alloc_bytes, current_frame->alloc_count); +#endif + e_debug(event_datastack, "Growing data stack by %zu for '%s' (%s)", + current_block->size, current_frame->marker, + str_c(str)); +} + +static void *t_malloc_real(size_t size, bool permanent) +{ + void *ret; + size_t alloc_size; + bool warn = FALSE; +#ifdef DEBUG + int old_errno = errno; +#endif + + if (unlikely(size == 0 || size > SSIZE_T_MAX)) + i_panic("Trying to allocate %zu bytes", size); + + if (unlikely(!data_stack_initialized)) { + /* kludgy, but allow this before initialization */ + data_stack_init(); + } + block_canary_check(current_block); + + /* allocate only aligned amount of memory so alignment comes + always properly */ + alloc_size = ALLOC_SIZE(size); +#ifdef DEBUG + if(permanent) { + current_frame->alloc_bytes += alloc_size; + current_frame->alloc_count++; + } +#endif + data_stack_last_buffer_reset(TRUE); + + if (permanent) { + /* used for t_try_realloc() */ + current_frame->last_alloc_size = alloc_size; + } + + if (current_block->left < alloc_size) { + struct stack_block *block; + + /* current block is full, see if we can use the unused_block */ + if (unused_block != NULL && unused_block->size >= alloc_size) { + block = unused_block; + unused_block = NULL; + mem_block_reset(block); + } else { + /* current block is full, allocate a new one */ + block = mem_block_alloc(alloc_size); + warn = TRUE; + } + + /* The newly allocated block will replace the current_block, + i.e. current_block always points to the last element in + the linked list. */ + block->prev = current_block; + current_block->next = block; + current_block = block; + } + + /* enough space in current block, use it */ + ret = data_stack_after_last_alloc(current_block); + +#ifdef DEBUG + if (current_block->left - alloc_size < current_block->left_lowwater) + current_block->left_lowwater = current_block->left - alloc_size; +#endif + if (permanent) + current_block->left -= alloc_size; + + if (warn) T_BEGIN { + /* sending event can cause errno changes. */ +#ifdef DEBUG + i_assert(errno == old_errno); +#else + int old_errno = errno; +#endif + /* warn after allocation, so if e_debug() wants to + allocate more memory we don't go to infinite loop */ + data_stack_send_grow_event(alloc_size); + /* reset errno back to what it was */ + errno = old_errno; + } T_END; +#ifdef DEBUG + memcpy(ret, &size, sizeof(size)); + ret = PTR_OFFSET(ret, MEM_ALIGN(sizeof(size))); + /* make sure the sentry contains CLEAR_CHRs. it might not if we + had used t_buffer_get(). */ + memset(PTR_OFFSET(ret, size), CLEAR_CHR, + MEM_ALIGN(size + SENTRY_COUNT) - size); + + /* we rely on errno not changing. it shouldn't. */ + i_assert(errno == old_errno); +#endif + return ret; +} + +void *t_malloc_no0(size_t size) +{ + return t_malloc_real(size, TRUE); +} + +void *t_malloc0(size_t size) +{ + void *mem; + + mem = t_malloc_real(size, TRUE); + memset(mem, 0, size); + return mem; +} + +bool ATTR_NO_SANITIZE_INTEGER +t_try_realloc(void *mem, size_t size) +{ + size_t debug_adjust = 0, last_alloc_size; + unsigned char *after_last_alloc; + + if (unlikely(size == 0 || size > SSIZE_T_MAX)) + i_panic("Trying to allocate %zu bytes", size); + block_canary_check(current_block); + data_stack_last_buffer_reset(TRUE); + + last_alloc_size = current_frame->last_alloc_size; + + /* see if we're trying to grow the memory we allocated last */ + after_last_alloc = data_stack_after_last_alloc(current_block); +#ifdef DEBUG + debug_adjust = MEM_ALIGN(sizeof(size_t)); +#endif + if (after_last_alloc - last_alloc_size + debug_adjust == mem) { + /* yeah, see if we have space to grow */ + size_t new_alloc_size, alloc_growth; + + new_alloc_size = ALLOC_SIZE(size); + alloc_growth = (new_alloc_size - last_alloc_size); +#ifdef DEBUG + size_t old_raw_size; /* sorry, non-C99 users - add braces if you need them */ + old_raw_size = *(size_t *)PTR_OFFSET(mem, -(ptrdiff_t)MEM_ALIGN(sizeof(size_t))); + i_assert(ALLOC_SIZE(old_raw_size) == last_alloc_size); + /* Only check one byte for over-run, that catches most + offenders who are likely to use t_try_realloc() */ + i_assert(((unsigned char*)mem)[old_raw_size] == CLEAR_CHR); +#endif + + if (current_block->left >= alloc_growth) { + /* just shrink the available size */ + current_block->left -= alloc_growth; + current_frame->last_alloc_size = new_alloc_size; +#ifdef DEBUG + if (current_block->left < current_block->left_lowwater) + current_block->left_lowwater = current_block->left; + /* All reallocs are permanent by definition + However, they don't count as a new allocation */ + current_frame->alloc_bytes += alloc_growth; + *(size_t *)PTR_OFFSET(mem, -(ptrdiff_t)MEM_ALIGN(sizeof(size_t))) = size; + memset(PTR_OFFSET(mem, size), CLEAR_CHR, + new_alloc_size - size - MEM_ALIGN(sizeof(size_t))); +#endif + return TRUE; + } + } + + return FALSE; +} + +size_t t_get_bytes_available(void) +{ + block_canary_check(current_block); +#ifndef DEBUG + const unsigned int min_extra = 0; +#else + const unsigned int min_extra = SENTRY_COUNT + MEM_ALIGN(sizeof(size_t)); + if (current_block->left < min_extra) + return 0; +#endif + size_t size = current_block->left - min_extra; + i_assert(ALLOC_SIZE(size) == current_block->left); + return size; +} + +void *t_buffer_get(size_t size) +{ + void *ret; + + ret = t_malloc_real(size, FALSE); + + last_buffer_size = size; + last_buffer_block = current_block; + return ret; +} + +void *t_buffer_reget(void *buffer, size_t size) +{ + size_t old_size; + void *new_buffer; + + old_size = last_buffer_size; + if (size <= old_size) + return buffer; + + new_buffer = t_buffer_get(size); + if (new_buffer != buffer) + memcpy(new_buffer, buffer, old_size); + + return new_buffer; +} + +void t_buffer_alloc(size_t size) +{ + i_assert(last_buffer_block != NULL); + i_assert(last_buffer_size >= size); + i_assert(current_block->left >= size); + + /* we've already reserved the space, now we just mark it used */ + (void)t_malloc_real(size, TRUE); +} + +void t_buffer_alloc_last_full(void) +{ + if (last_buffer_block != NULL) + (void)t_malloc_real(last_buffer_size, TRUE); +} + +bool data_stack_frame_contains(data_stack_frame_t *id, const void *_ptr) +{ + const unsigned char *block_data, *ptr = _ptr; + const struct stack_block *block; + unsigned int wanted_frame_id; + size_t block_start_pos, block_used; + + /* first handle the fast path - NULL can never be within the frame */ + if (ptr == NULL) + return FALSE; + +#ifndef STATIC_CHECKER + wanted_frame_id = *id; +#else + wanted_frame_id = (*id)->id; +#endif + /* Too much effort to support more than the latest frame. + It's the only thing that is currently needed anyway. */ + i_assert(wanted_frame_id+1 == data_stack_frame_id); + block = current_frame->block; + i_assert(block != NULL); + + /* See if it's in the frame's first block. Only the data after + block_start_pos belong to this frame. */ + block_data = STACK_BLOCK_DATA(block); + block_start_pos = block->size - current_frame->block_space_left; + block_used = block->size - block->left; + if (ptr >= block_data + block_start_pos && + ptr <= block_data + block_used) + return TRUE; + + /* See if it's in the other blocks. All the data in them belong to + this frame. */ + for (block = block->next; block != NULL; block = block->next) { + block_data = STACK_BLOCK_DATA(block); + block_used = block->size - block->left; + if (ptr >= block_data && ptr < block_data + block_used) + return TRUE; + } + return FALSE; +} + +size_t data_stack_get_alloc_size(void) +{ + struct stack_block *block; + size_t size = 0; + + i_assert(current_block->next == NULL); + + for (block = current_block; block != NULL; block = block->prev) + size += block->size; + return size; +} + +size_t data_stack_get_used_size(void) +{ + struct stack_block *block; + size_t size = 0; + + i_assert(current_block->next == NULL); + + for (block = current_block; block != NULL; block = block->prev) + size += block->size - block->left; + return size; +} + +void data_stack_free_unused(void) +{ + free(unused_block); + unused_block = NULL; +} + +void data_stack_init(void) +{ + if (data_stack_initialized) { + /* already initialized (we did auto-initialization in + t_malloc/t_push) */ + return; + } + data_stack_initialized = TRUE; + data_stack_frame_id = 1; + + outofmem_area.block.size = outofmem_area.block.left = + sizeof(outofmem_area) - sizeof(outofmem_area.block); + outofmem_area.block.canary = BLOCK_CANARY; + + current_block = mem_block_alloc(INITIAL_STACK_SIZE); + current_frame = NULL; + + last_buffer_block = NULL; + last_buffer_size = 0; + + root_frame_id = t_push("data_stack_init"); +} + +void data_stack_deinit_event(void) +{ + event_unref(&event_datastack); + event_datastack_deinitialized = TRUE; +} + +void data_stack_deinit(void) +{ + if (!t_pop(&root_frame_id) || + current_frame != NULL) + i_panic("Missing t_pop() call"); + + free(current_block); + current_block = NULL; + data_stack_free_unused(); +} diff --git a/src/lib/data-stack.h b/src/lib/data-stack.h new file mode 100644 index 0000000..ba2b566 --- /dev/null +++ b/src/lib/data-stack.h @@ -0,0 +1,165 @@ +#ifndef DATA_STACK_H +#define DATA_STACK_H + +/* Data stack makes it very easy to implement functions returning dynamic data + without having to worry much about memory management like freeing the + result or having large enough buffers for the result. + + t_ prefix was chosen to describe functions allocating memory from data + stack. "t" meaning temporary. + + Advantages over control stack and alloca(): + - Functions can return a value allocated from data stack + - We can portably specify how much data we want to allocate at runtime + + Advantages over malloc(): + - FAST, most of the time allocating memory means only updating a couple of + pointers and integers. Freeing the memory all at once also is a fast + operation. + - No need to free() each allocation resulting in prettier code + - No memory leaks + - No memory fragmentation + + Disadvantages: + - Allocating memory inside loops can accidentally allocate a lot of memory + if the loops are long and you forgot to place t_push() and t_pop() there. + - t_malloc()ed data could be accidentally stored into permanent location + and accessed after it's already been freed. const'ing the return values + helps for most uses though (see the t_malloc() description). + - Debugging invalid memory usage may be difficult using existing tools, + although compiling with DEBUG enabled helps finding simple buffer + overflows. +*/ + +#ifndef STATIC_CHECKER +typedef unsigned int data_stack_frame_t; +#else +typedef struct data_stack_frame *data_stack_frame_t; +#endif + +extern unsigned int data_stack_frame_id; + +/* All t_..() allocations between t_push*() and t_pop() are freed after t_pop() + is called. Returns the current stack frame number, which can be used + to detect missing t_pop() calls: + + x = t_push(marker); .. if (!t_pop(x)) abort(); + + In DEBUG mode, t_push_named() makes a temporary allocation for the name, + but is safe to call in a loop as it performs the allocation within its own + frame. However, you should always prefer to use T_BEGIN { ... } T_END below. +*/ +data_stack_frame_t t_push(const char *marker) ATTR_HOT; +data_stack_frame_t t_push_named(const char *format, ...) ATTR_HOT ATTR_FORMAT(1, 2); +/* Returns TRUE on success, FALSE if t_pop() call was leaked. The caller + should panic. */ +bool t_pop(data_stack_frame_t *id) ATTR_HOT; +/* Same as t_pop(), but move str out of the stack frame if it is inside. + This can be used to easily move e.g. error strings outside stack frames. */ +bool t_pop_pass_str(data_stack_frame_t *id, const char **str); + +/* Pop the last data stack frame. This shouldn't be called outside test code. */ +void t_pop_last_unsafe(void); + +/* Usage: T_BEGIN { code } T_END */ +#define T_STRING(x) #x +#define T_XSTRING(x) T_STRING(x) /* expand and then stringify */ +#define T_BEGIN \ + STMT_START { \ + data_stack_frame_t _data_stack_cur_id = t_push(__FILE__ ":" T_XSTRING(__LINE__)); +#define T_END \ + STMT_START { \ + if (unlikely(!t_pop(&_data_stack_cur_id))) \ + i_panic("Leaked t_pop() call"); \ + } STMT_END; \ + } STMT_END + +/* Usage: + const char *error; + T_BEGIN { + ... + if (ret < 0) + error = t_strdup_printf("foo() failed: %m"); + } T_END_PASS_STR_IF(ret < 0, &error); + // error is still valid +*/ +#define T_END_PASS_STR_IF(pass_condition, str) \ + STMT_START { \ + if (unlikely(!t_pop_pass_str(&_data_stack_cur_id, (pass_condition) ? (str) : NULL))) \ + i_panic("Leaked t_pop() call"); \ + } STMT_END; \ + } STMT_END +/* + Usage: + const char *result; + T_BEGIN { + ... + result = t_strdup_printf(...); + } T_END_PASS_STR(&result); + // result is still valid +*/ +#define T_END_PASS_STR(str) \ + T_END_PASS_STR_IF(TRUE, str) + +/* WARNING: Be careful when using these functions, it's too easy to + accidentally save the returned value somewhere permanently. + + You probably should never use these functions directly, rather + create functions that return 'const xxx*' types and use t_malloc() + internally in them. This is a lot safer, since usually compiler + warns if you try to place them in xxx*. See strfuncs.c for examples. + + t_malloc() calls never fail. If there's not enough memory left, + i_panic() will be called. */ +void *t_malloc_no0(size_t size) ATTR_MALLOC ATTR_RETURNS_NONNULL; +void *t_malloc0(size_t size) ATTR_MALLOC ATTR_RETURNS_NONNULL; + +/* Try growing allocated memory. Returns TRUE if successful. Works only + for last allocated memory in current stack frame. */ +bool t_try_realloc(void *mem, size_t size); + +/* Returns the number of bytes available in data stack without allocating + more memory. */ +size_t t_get_bytes_available(void) ATTR_PURE; + +#define t_new(type, count) \ + ((type *) t_malloc0(MALLOC_MULTIPLY((unsigned int)sizeof(type), (count))) + \ + COMPILE_ERROR_IF_TRUE(sizeof(type) > UINT_MAX)) + +/* Returns pointer to a temporary buffer you can use. The buffer will be + invalid as soon as next t_malloc() is called! + + If you wish to grow the buffer, you must give the full wanted size + in the size parameter. If return value doesn't point to the same value + as last time, you need to memcpy() data from the old buffer to the + new one (or do some other trickery). See t_buffer_reget(). */ +void *t_buffer_get(size_t size) ATTR_RETURNS_NONNULL; + +/* Grow the buffer, memcpy()ing the memory to new location if needed. */ +void *t_buffer_reget(void *buffer, size_t size) ATTR_RETURNS_NONNULL; + +/* Make the last t_buffer_get()ed buffer permanent. Note that size MUST be + less or equal than the size you gave with last t_buffer_get() or the + result will be undefined. */ +void t_buffer_alloc(size_t size); +/* Allocate the last t_buffer_get()ed data entirely. */ +void t_buffer_alloc_last_full(void); + +/* Returns TRUE if ptr is allocated within the given data stack frame. + Currently this assert-crashes if the data stack frame isn't the latest. */ +bool data_stack_frame_contains(data_stack_frame_t *id, const void *ptr); + +/* Returns the number of bytes malloc()ated for data stack. */ +size_t data_stack_get_alloc_size(void); +/* Returns the number of bytes currently used in data stack. */ +size_t data_stack_get_used_size(void); + +/* Free all the memory that is currently unused (i.e. reserved for growing + data stack quickly). */ +void data_stack_free_unused(void); + +void data_stack_init(void); +void data_stack_deinit_event(void); +void data_stack_deinit(void); + +#endif diff --git a/src/lib/eacces-error.c b/src/lib/eacces-error.c new file mode 100644 index 0000000..954ea78 --- /dev/null +++ b/src/lib/eacces-error.c @@ -0,0 +1,310 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "path-util.h" +#include "ipwd.h" +#include "restrict-access.h" +#include "eacces-error.h" + +#include <sys/stat.h> +#include <unistd.h> + +static bool is_in_group(gid_t gid) +{ + const gid_t *gids; + unsigned int i, count; + + if (getegid() == gid) + return TRUE; + + gids = restrict_get_groups_list(&count); + for (i = 0; i < count; i++) { + if (gids[i] == gid) + return TRUE; + } + return FALSE; +} + +static void write_eacces_error(string_t *errmsg, const char *path, int mode) +{ + char c; + + switch (mode) { + case R_OK: + c = 'r'; + break; + case W_OK: + c = 'w'; + break; + case X_OK: + c = 'x'; + break; + default: + i_unreached(); + } + str_printfa(errmsg, " missing +%c perm: %s", c, path); +} + +static int +test_manual_access(const char *path, int access_mode, bool write_eacces, + string_t *errmsg) +{ + const struct group *group; + bool user_not_in_group = FALSE; + struct stat st; + int mode; + + if (stat(path, &st) < 0) { + str_printfa(errmsg, " stat(%s) failed: %m", path); + return -1; + } + + switch (access_mode) { + case R_OK: + mode = 04; + break; + case W_OK: + mode = 02; + break; + case X_OK: + mode = 01; + break; + default: + i_unreached(); + } + + if (st.st_uid == geteuid()) + st.st_mode = (st.st_mode & 0700) >> 6; + else if (is_in_group(st.st_gid)) + st.st_mode = (st.st_mode & 0070) >> 3; + else { + if ((((st.st_mode & 0070) >> 3) & mode) != 0) + user_not_in_group = TRUE; + st.st_mode = (st.st_mode & 0007); + } + + if ((st.st_mode & mode) != 0) + return 0; + + if (write_eacces) + write_eacces_error(errmsg, path, access_mode); + if (user_not_in_group) { + /* group would have had enough permissions, + but we don't belong to the group */ + str_printfa(errmsg, ", we're not in group %s", + dec2str(st.st_gid)); + group = getgrgid(st.st_gid); + if (group != NULL) + str_printfa(errmsg, "(%s)", group->gr_name); + } + errno = EACCES; + return -1; +} + +static int test_access(const char *path, int access_mode, string_t *errmsg) +{ + struct stat st; + + if (getuid() == geteuid()) { + if (access(path, access_mode) == 0) + return 0; + if (errno == EACCES) { + write_eacces_error(errmsg, path, access_mode); + if (test_manual_access(path, access_mode, + FALSE, errmsg) == 0) { + str_append(errmsg, ", UNIX perms appear ok " + "(ACL/MAC wrong?)"); + } + errno = EACCES; + } else { + str_printfa(errmsg, ", access(%s, %d) failed: %m", + path, access_mode); + } + return -1; + } + + /* access() uses real uid, not effective uid. + we'll have to do these checks manually. */ + switch (access_mode) { + case X_OK: + if (stat(t_strconcat(path, "/test", NULL), &st) == 0) + return 0; + if (errno == ENOENT || errno == ENOTDIR) + return 0; + if (errno == EACCES) + write_eacces_error(errmsg, path, access_mode); + else + str_printfa(errmsg, ", stat(%s/test) failed: %m", path); + return -1; + case R_OK: + case W_OK: + break; + default: + i_unreached(); + } + + return test_manual_access(path, access_mode, TRUE, errmsg); +} + +static const char * +eacces_error_get_full(const char *func, const char *path, bool creating) +{ + const char *prev_path, *dir = NULL, *p; + const char *pw_name = NULL, *gr_name = NULL; + struct passwd pw; + struct group group; + string_t *errmsg; + struct stat st; + int orig_errno, ret, missing_mode = 0; + + orig_errno = errno; + errmsg = t_str_new(256); + str_printfa(errmsg, "%s(%s)", func, path); + if (*path != '/') { + const char *error; + if (t_get_working_dir(&dir, &error) < 0) { + i_error("eacces_error_get_full: %s", error); + str_printfa(errmsg, " in an unknown directory"); + } else { + str_printfa(errmsg, " in directory %s", dir); + path = t_strconcat(dir, "/", path, NULL); + } + } + str_printfa(errmsg, " failed: Permission denied (euid=%s", + dec2str(geteuid())); + + switch (i_getpwuid(geteuid(), &pw)) { + case -1: + str_append(errmsg, "(<getpwuid() error>)"); + break; + case 0: + str_append(errmsg, "(<unknown>)"); + break; + default: + pw_name = t_strdup(pw.pw_name); + str_printfa(errmsg, "(%s)", pw_name); + break; + } + + str_printfa(errmsg, " egid=%s", dec2str(getegid())); + switch (i_getgrgid(getegid(), &group)) { + case -1: + str_append(errmsg, "(<getgrgid() error>)"); + break; + case 0: + str_append(errmsg, "(<unknown>)"); + break; + default: + gr_name = t_strdup(group.gr_name); + str_printfa(errmsg, "(%s)", gr_name); + break; + } + + prev_path = path; ret = -1; + while (strcmp(prev_path, "/") != 0) { + if ((p = strrchr(prev_path, '/')) == NULL) + break; + + dir = t_strdup_until(prev_path, p); + ret = stat(dir, &st); + if (ret == 0) + break; + if (errno == EACCES && strcmp(dir, "/") != 0) { + /* see if we have access to parent directory */ + } else if (errno == ENOENT && creating && + strcmp(dir, "/") != 0) { + /* probably mkdir_parents() failed here, find the first + parent directory we couldn't create */ + } else { + /* some other error, can't handle it */ + str_printfa(errmsg, " stat(%s) failed: %m", dir); + break; + } + prev_path = dir; + } + + if (ret == 0) { + /* dir is the first parent directory we can stat() */ + if (test_access(dir, X_OK, errmsg) < 0) { + if (errno == EACCES) + missing_mode = 1; + } else if (creating && test_access(dir, W_OK, errmsg) < 0) { + if (errno == EACCES) + missing_mode = 2; + } else if (prev_path == path && + test_access(path, R_OK, errmsg) < 0) { + } else if (!creating && test_access(path, W_OK, errmsg) < 0) { + /* this produces a wrong error if the operation didn't + actually need write permissions, but we don't know + it here.. */ + if (errno == EACCES) + missing_mode = 4; + } else { + str_append(errmsg, " UNIX perms appear ok " + "(ACL/MAC wrong?)"); + } + } + if (ret < 0) + ; + else if (st.st_uid != geteuid()) { + if (pw_name != NULL && i_getpwuid(st.st_uid, &pw) > 0 && + strcmp(pw.pw_name, pw_name) == 0) { + str_printfa(errmsg, ", conflicting dir uid=%s(%s)", + dec2str(st.st_uid), pw_name); + } else { + str_printfa(errmsg, ", dir owned by %s:%s mode=0%o", + dec2str(st.st_uid), dec2str(st.st_gid), + (unsigned int)(st.st_mode & 0777)); + } + } else if (missing_mode != 0 && + (((st.st_mode & 0700) >> 6) & missing_mode) == 0) { + str_append(errmsg, ", dir owner missing perms"); + } + if (ret == 0 && gr_name != NULL && st.st_gid != getegid()) { + if (i_getgrgid(st.st_gid, &group) > 0 && + strcmp(group.gr_name, gr_name) == 0) { + str_printfa(errmsg, ", conflicting dir gid=%s(%s)", + dec2str(st.st_gid), gr_name); + } + } + str_append_c(errmsg, ')'); + errno = orig_errno; + return str_c(errmsg); +} + +const char *eacces_error_get(const char *func, const char *path) +{ + return eacces_error_get_full(func, path, FALSE); +} + +const char *eacces_error_get_creating(const char *func, const char *path) +{ + return eacces_error_get_full(func, path, TRUE); +} + +const char *eperm_error_get_chgrp(const char *func, const char *path, + gid_t gid, const char *gid_origin) +{ + string_t *errmsg; + const struct group *group; + int orig_errno = errno; + + errmsg = t_str_new(256); + + str_printfa(errmsg, "%s(%s, group=%s", func, path, dec2str(gid)); + group = getgrgid(gid); + if (group != NULL) + str_printfa(errmsg, "(%s)", group->gr_name); + + str_printfa(errmsg, ") failed: Operation not permitted (egid=%s", + dec2str(getegid())); + group = getgrgid(getegid()); + if (group != NULL) + str_printfa(errmsg, "(%s)", group->gr_name); + if (gid_origin != NULL) + str_printfa(errmsg, ", group based on %s", gid_origin); + str_append(errmsg, " - see http://wiki2.dovecot.org/Errors/ChgrpNoPerm)"); + errno = orig_errno; + return str_c(errmsg); +} diff --git a/src/lib/eacces-error.h b/src/lib/eacces-error.h new file mode 100644 index 0000000..a15b406 --- /dev/null +++ b/src/lib/eacces-error.h @@ -0,0 +1,14 @@ +#ifndef EACCES_ERROR_H +#define EACCES_ERROR_H + +/* Return a user-friendly error message for EACCES failures. */ +const char *eacces_error_get(const char *func, const char *path); +const char *eacces_error_get_creating(const char *func, const char *path); +/* Return a user-friendly error message for fchown() or chown() EPERM + failures when only the group is being changed. gid_origin specifies why + exactly this group is being used. */ +const char *eperm_error_get_chgrp(const char *func, const char *path, + gid_t gid, const char *gid_origin) + ATTR_NULL(4); + +#endif diff --git a/src/lib/env-util.c b/src/lib/env-util.c new file mode 100644 index 0000000..490e4f6 --- /dev/null +++ b/src/lib/env-util.c @@ -0,0 +1,140 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "env-util.h" + +#ifdef __APPLE__ +# include <crt_externs.h> +#endif + +struct env_backup { + pool_t pool; + const char **strings; +}; + +void env_put(const char *name, const char *value) +{ + i_assert(strchr(name, '=') == NULL); + + if (setenv(name, value, 1) != 0) + i_fatal("setenv(%s, %s) failed: %m", name, value); +} + +void env_put_array(const char *const *envs) +{ + for (unsigned int i = 0; envs[i] != NULL; i++) { + const char *value = strchr(envs[i], '='); + i_assert(value != NULL); + T_BEGIN { + const char *name = t_strdup_until(envs[i], value++); + env_put(name, value); + } T_END; + } +} + +void env_remove(const char *name) +{ + if (unsetenv(name) < 0) + i_fatal("unsetenv(%s) failed: %m", name); +} + +void env_clean(void) +{ +#ifdef HAVE_CLEARENV + if (clearenv() < 0) + i_fatal("clearenv() failed"); +#else + char ***environ_p = env_get_environ_p(); + + /* Try to clear the environment. + + a) environ = NULL crashes on OS X. + b) *environ = NULL doesn't work on FreeBSD 7.0. + c) environ = emptyenv doesn't work on Haiku OS + d) environ = calloc() should work everywhere + */ + *environ_p = calloc(1, sizeof(**environ_p)); +#endif +} + +static void env_clean_except_real(const char *const preserve_envs[]) +{ + ARRAY_TYPE(const_string) copy; + const char *value, *const *envp; + unsigned int i, count; + + t_array_init(©, 16); + for (i = 0; preserve_envs[i] != NULL; i++) { + const char *key = preserve_envs[i]; + + value = getenv(key); + if (value != NULL) { + key = t_strdup(key); + value = t_strdup(value); + array_push_back(©, &key); + array_push_back(©, &value); + } + } + + /* Note that if the original environment was set with env_put(), the + environment strings will be invalid after env_clean(). That's why + we t_strdup() them above. */ + env_clean(); + + envp = array_get(©, &count); + for (i = 0; i < count; i += 2) + env_put(envp[i], envp[i+1]); +} + +void env_clean_except(const char *const preserve_envs[]) +{ + T_BEGIN { + env_clean_except_real(preserve_envs); + } T_END; +} + +struct env_backup *env_backup_save(void) +{ + char **environ = *env_get_environ_p(); + struct env_backup *env; + unsigned int i, count; + pool_t pool; + + i_assert(environ != NULL); + + for (count = 0; environ[count] != NULL; count++) ; + + pool = pool_alloconly_create("saved environment", 4096); + env = p_new(pool, struct env_backup, 1); + env->pool = pool; + env->strings = p_new(pool, const char *, count + 1); + for (i = 0; i < count; i++) + env->strings[i] = p_strdup(pool, environ[i]); + return env; +} + +void env_backup_restore(struct env_backup *env) +{ + env_clean(); + env_put_array(env->strings); +} + +void env_backup_free(struct env_backup **_env) +{ + struct env_backup *env = *_env; + + *_env = NULL; + pool_unref(&env->pool); +} + +char ***env_get_environ_p(void) +{ +#ifdef __APPLE__ + return _NSGetEnviron(); +#else + extern char **environ; + + return &environ; +#endif +} diff --git a/src/lib/env-util.h b/src/lib/env-util.h new file mode 100644 index 0000000..e5b87de --- /dev/null +++ b/src/lib/env-util.h @@ -0,0 +1,30 @@ +#ifndef ENV_UTIL_H +#define ENV_UTIL_H + +/* Add a new environment variable or replace an existing one. + Wrapper to setenv(). Note that setenv() often doesn't free memory used by + replaced environment, so don't keep repeatedly changing values in + environment. */ +void env_put(const char *name, const char *value); +/* env_put() NULL-terminated array of name=value strings */ +void env_put_array(const char *const *envs); +/* Remove a single environment. */ +void env_remove(const char *name); +/* Clear all environment variables. */ +void env_clean(void); +/* Clear all environment variables except what's listed in preserve_envs[] */ +void env_clean_except(const char *const preserve_envs[]); + +/* Save a copy of the current environment. */ +struct env_backup *env_backup_save(void); +/* Clear the current environment and restore the backup. */ +void env_backup_restore(struct env_backup *env); +/* Free the memory used by environment backup. */ +void env_backup_free(struct env_backup **env); + +/* Returns the value of "&environ". This is more portable than using it + directly. */ +char ***env_get_environ_p(void); + + +#endif diff --git a/src/lib/event-filter-lexer.c b/src/lib/event-filter-lexer.c new file mode 100644 index 0000000..ba5dc39 --- /dev/null +++ b/src/lib/event-filter-lexer.c @@ -0,0 +1,2297 @@ +#line 2 "event-filter-lexer.c" + +#line 4 "event-filter-lexer.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define event_filter_parser__create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer event_filter_parser__create_buffer +#endif + +#ifdef yy_delete_buffer +#define event_filter_parser__delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer event_filter_parser__delete_buffer +#endif + +#ifdef yy_scan_buffer +#define event_filter_parser__scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer event_filter_parser__scan_buffer +#endif + +#ifdef yy_scan_string +#define event_filter_parser__scan_string_ALREADY_DEFINED +#else +#define yy_scan_string event_filter_parser__scan_string +#endif + +#ifdef yy_scan_bytes +#define event_filter_parser__scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes event_filter_parser__scan_bytes +#endif + +#ifdef yy_init_buffer +#define event_filter_parser__init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer event_filter_parser__init_buffer +#endif + +#ifdef yy_flush_buffer +#define event_filter_parser__flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer event_filter_parser__flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define event_filter_parser__load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state event_filter_parser__load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define event_filter_parser__switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer event_filter_parser__switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define event_filter_parser_push_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state event_filter_parser_push_buffer_state +#endif + +#ifdef yypop_buffer_state +#define event_filter_parser_pop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state event_filter_parser_pop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define event_filter_parser_ensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack event_filter_parser_ensure_buffer_stack +#endif + +#ifdef yylex +#define event_filter_parser_lex_ALREADY_DEFINED +#else +#define yylex event_filter_parser_lex +#endif + +#ifdef yyrestart +#define event_filter_parser_restart_ALREADY_DEFINED +#else +#define yyrestart event_filter_parser_restart +#endif + +#ifdef yylex_init +#define event_filter_parser_lex_init_ALREADY_DEFINED +#else +#define yylex_init event_filter_parser_lex_init +#endif + +#ifdef yylex_init_extra +#define event_filter_parser_lex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra event_filter_parser_lex_init_extra +#endif + +#ifdef yylex_destroy +#define event_filter_parser_lex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy event_filter_parser_lex_destroy +#endif + +#ifdef yyget_debug +#define event_filter_parser_get_debug_ALREADY_DEFINED +#else +#define yyget_debug event_filter_parser_get_debug +#endif + +#ifdef yyset_debug +#define event_filter_parser_set_debug_ALREADY_DEFINED +#else +#define yyset_debug event_filter_parser_set_debug +#endif + +#ifdef yyget_extra +#define event_filter_parser_get_extra_ALREADY_DEFINED +#else +#define yyget_extra event_filter_parser_get_extra +#endif + +#ifdef yyset_extra +#define event_filter_parser_set_extra_ALREADY_DEFINED +#else +#define yyset_extra event_filter_parser_set_extra +#endif + +#ifdef yyget_in +#define event_filter_parser_get_in_ALREADY_DEFINED +#else +#define yyget_in event_filter_parser_get_in +#endif + +#ifdef yyset_in +#define event_filter_parser_set_in_ALREADY_DEFINED +#else +#define yyset_in event_filter_parser_set_in +#endif + +#ifdef yyget_out +#define event_filter_parser_get_out_ALREADY_DEFINED +#else +#define yyget_out event_filter_parser_get_out +#endif + +#ifdef yyset_out +#define event_filter_parser_set_out_ALREADY_DEFINED +#else +#define yyset_out event_filter_parser_set_out +#endif + +#ifdef yyget_leng +#define event_filter_parser_get_leng_ALREADY_DEFINED +#else +#define yyget_leng event_filter_parser_get_leng +#endif + +#ifdef yyget_text +#define event_filter_parser_get_text_ALREADY_DEFINED +#else +#define yyget_text event_filter_parser_get_text +#endif + +#ifdef yyget_lineno +#define event_filter_parser_get_lineno_ALREADY_DEFINED +#else +#define yyget_lineno event_filter_parser_get_lineno +#endif + +#ifdef yyset_lineno +#define event_filter_parser_set_lineno_ALREADY_DEFINED +#else +#define yyset_lineno event_filter_parser_set_lineno +#endif + +#ifdef yyget_column +#define event_filter_parser_get_column_ALREADY_DEFINED +#else +#define yyget_column event_filter_parser_get_column +#endif + +#ifdef yyset_column +#define event_filter_parser_set_column_ALREADY_DEFINED +#else +#define yyset_column event_filter_parser_set_column +#endif + +#ifdef yywrap +#define event_filter_parser_wrap_ALREADY_DEFINED +#else +#define yywrap event_filter_parser_wrap +#endif + +#ifdef yyget_lval +#define event_filter_parser_get_lval_ALREADY_DEFINED +#else +#define yyget_lval event_filter_parser_get_lval +#endif + +#ifdef yyset_lval +#define event_filter_parser_set_lval_ALREADY_DEFINED +#else +#define yyset_lval event_filter_parser_set_lval +#endif + +#ifdef yyalloc +#define event_filter_parser_alloc_ALREADY_DEFINED +#else +#define yyalloc event_filter_parser_alloc +#endif + +#ifdef yyrealloc +#define event_filter_parser_realloc_ALREADY_DEFINED +#else +#define yyrealloc event_filter_parser_realloc +#endif + +#ifdef yyfree +#define event_filter_parser_free_ALREADY_DEFINED +#else +#define yyfree event_filter_parser_free +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define event_filter_parser_wrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 14 +#define YY_END_OF_BUFFER 15 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[29] = + { 0, + 0, 0, 0, 0, 15, 13, 12, 12, 1, 10, + 11, 11, 11, 11, 3, 2, 14, 11, 11, 11, + 8, 3, 6, 5, 4, 7, 9, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 4, 1, 1, 1, 1, 1, 5, + 5, 6, 1, 1, 6, 6, 1, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 1, 5, + 5, 5, 6, 1, 7, 6, 6, 8, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 9, 10, 6, + 6, 11, 6, 12, 6, 6, 6, 6, 6, 6, + 1, 13, 1, 1, 6, 1, 7, 6, 6, 8, + + 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, + 10, 6, 6, 11, 6, 12, 6, 6, 6, 6, + 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[14] = + { 0, + 1, 1, 2, 3, 1, 4, 4, 4, 4, 4, + 4, 4, 3 + } ; + +static const flex_int16_t yy_base[33] = + { 0, + 0, 0, 10, 20, 21, 56, 56, 56, 56, 56, + 0, 11, 9, 7, 0, 56, 30, 0, 9, 4, + 0, 0, 56, 56, 56, 0, 0, 56, 43, 11, + 47, 51 + } ; + +static const flex_int16_t yy_def[33] = + { 0, + 28, 1, 29, 29, 28, 28, 28, 28, 28, 28, + 30, 30, 30, 30, 31, 28, 32, 30, 30, 30, + 30, 31, 28, 28, 28, 30, 30, 0, 28, 28, + 28, 28 + } ; + +static const flex_int16_t yy_nxt[70] = + { 0, + 6, 7, 8, 9, 10, 11, 12, 11, 13, 14, + 11, 11, 6, 16, 18, 27, 26, 21, 20, 19, + 28, 28, 17, 16, 28, 28, 28, 28, 28, 28, + 28, 28, 17, 24, 28, 28, 28, 28, 28, 28, + 28, 28, 25, 15, 15, 15, 15, 22, 22, 28, + 22, 23, 28, 23, 23, 5, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28 + } ; + +static const flex_int16_t yy_chk[70] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3, 30, 20, 19, 14, 13, 12, + 5, 0, 3, 4, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 17, 0, 0, 0, 0, 0, 0, + 0, 0, 17, 29, 29, 29, 29, 31, 31, 0, + 31, 32, 0, 32, 32, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "event-filter-lexer.l" +/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ +#define YY_NO_INPUT 1 +#line 13 "event-filter-lexer.l" +#include "lib.h" +#include "str.h" +#include "event-filter-private.h" +#include "event-filter-parser.h" + +#define YY_FATAL_ERROR(msg) { i_fatal("event filter parsing: %s", (msg)); } + +/* mimic renaming done by bison's api.prefix %define */ +#define YYSTYPE EVENT_FILTER_PARSER_STYPE + +#define YY_INPUT(buf, result, max_size) \ + result = event_filter_parser_input_proc(buf, max_size, yyscanner) +static size_t event_filter_parser_input_proc(char *buf, size_t size, yyscan_t scanner); + +#pragma GCC diagnostic push + +/* ignore strict bool warnings in generated code */ +#ifdef HAVE_STRICT_BOOL +# pragma GCC diagnostic ignored "-Wstrict-bool" +#endif +/* ignore sign comparison errors (buggy flex) */ +#pragma GCC diagnostic ignored "-Wsign-compare" +/* ignore unused functions */ +#pragma GCC diagnostic ignored "-Wunused-function" +/* ignore unused parameters */ +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#line 695 "event-filter-lexer.c" + +#line 697 "event-filter-lexer.c" + +#define INITIAL 0 +#define string 1 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( yyscan_t yyscanner ); +#else +static int input ( yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yylval = yylval_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 44 "event-filter-lexer.l" + +#line 46 "event-filter-lexer.l" + string_t *str_buf = NULL; + +#line 975 "event-filter-lexer.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 29 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_current_state != 28 ); + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 48 "event-filter-lexer.l" +{ + BEGIN(string); + + str_buf = t_str_new(128); + } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 53 "event-filter-lexer.l" +{ + yylval->str = str_c(str_buf); + BEGIN(INITIAL); + return STRING; + } + YY_BREAK +/* Note: these have to match the event_filter_append_escaped() behavior */ +case 3: +/* rule 3 can match eol */ +YY_RULE_SETUP +#line 59 "event-filter-lexer.l" +{ str_append(str_buf, yytext); } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 60 "event-filter-lexer.l" +{ str_append_c(str_buf, '\\'); } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 61 "event-filter-lexer.l" +{ str_append_c(str_buf, '"'); } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 62 "event-filter-lexer.l" +{ str_append(str_buf, yytext); } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 64 "event-filter-lexer.l" +{ return AND; } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 65 "event-filter-lexer.l" +{ return OR; } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 66 "event-filter-lexer.l" +{ return NOT; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 67 "event-filter-lexer.l" +{ return *yytext; } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 68 "event-filter-lexer.l" +{ yylval->str = t_strdup(yytext); return TOKEN; } + YY_BREAK +case 12: +/* rule 12 can match eol */ +YY_RULE_SETUP +#line 69 "event-filter-lexer.l" +{ /* ignore */ } + YY_BREAK +case 13: +YY_RULE_SETUP +#line 70 "event-filter-lexer.l" +{ + /* + * We simply return the char to the + * and let the grammar error out + * with a syntax error. + * + * Note: The cast is significant + * since utf-8 bytes >=128 will + * otherwise result in sign + * extension and a negative int + * getting returned on some + * platforms (e.g., x86) which in + * turn confuses the parser. E.g., + * if: + * *yytext = '\x80' + * we get: + * *yytext -> -128 + * (int) *yytext -> -128 + * which is wrong. With the + * unsigned char cast, we get: + * (u.c.) *yytext -> 128 + * (int)(u.c.) *yytext -> 128 + * which is correct. + */ + return (unsigned char) *yytext; + } + YY_BREAK +case 14: +YY_RULE_SETUP +#line 96 "event-filter-lexer.l" +ECHO; + YY_BREAK +#line 1134 "event-filter-lexer.c" +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(string): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap( yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin , yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 29 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 29 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 28); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( yyscanner ) ) + return 0; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +int yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void yyset_column (int _column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = _column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int _bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +#define YYTABLES_NAME "yytables" + +#line 96 "event-filter-lexer.l" + + +#pragma GCC diagnostic pop + +void *yyalloc(size_t bytes, void* yyscanner ATTR_UNUSED) +{ + void *ptr = calloc(1, bytes); + if (ptr == NULL) + i_fatal_status(FATAL_OUTOFMEM, "calloc(1, %zu): Out of memory", + bytes); + return ptr; +} + +void *yyrealloc (void *ptr, size_t bytes, void *yyscanner ATTR_UNUSED) +{ + void *nptr = realloc(ptr, bytes); + if (nptr == NULL) + i_fatal_status(FATAL_OUTOFMEM, "realloc(ptr, %zu): Out of memory", + bytes); + return nptr; +} + +void yyfree(void *ptr, void *yyscanner ATTR_UNUSED) +{ + if (ptr == NULL) + return; + free(ptr); +} + +static size_t event_filter_parser_input_proc(char *buf, size_t size, yyscan_t scanner) +{ + struct event_filter_parser_state *state; + size_t num_bytes; + + state = event_filter_parser_get_extra(scanner); + + if (state->len == state->pos) + return 0; + + i_assert(state->len > state->pos); + + num_bytes = I_MIN(state->len - state->pos, size); + memcpy(buf, state->input + state->pos, num_bytes); + state->pos += num_bytes; + + return num_bytes; +} + diff --git a/src/lib/event-filter-lexer.l b/src/lib/event-filter-lexer.l new file mode 100644 index 0000000..8df4ddc --- /dev/null +++ b/src/lib/event-filter-lexer.l @@ -0,0 +1,141 @@ +/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ + +%option nounput +%option noinput +%option noyywrap +%option noyyalloc noyyrealloc noyyfree +%option reentrant +%option bison-bridge +%option never-interactive +%option prefix="event_filter_parser_" + +%{ +#include "lib.h" +#include "str.h" +#include "event-filter-private.h" +#include "event-filter-parser.h" + +#define YY_FATAL_ERROR(msg) { i_fatal("event filter parsing: %s", (msg)); } + +/* mimic renaming done by bison's api.prefix %define */ +#define YYSTYPE EVENT_FILTER_PARSER_STYPE + +#define YY_INPUT(buf, result, max_size) \ + result = event_filter_parser_input_proc(buf, max_size, yyscanner) +static size_t event_filter_parser_input_proc(char *buf, size_t size, yyscan_t scanner); + +#pragma GCC diagnostic push + +/* ignore strict bool warnings in generated code */ +#ifdef HAVE_STRICT_BOOL +# pragma GCC diagnostic ignored "-Wstrict-bool" +#endif +/* ignore sign comparison errors (buggy flex) */ +#pragma GCC diagnostic ignored "-Wsign-compare" +/* ignore unused functions */ +#pragma GCC diagnostic ignored "-Wunused-function" +/* ignore unused parameters */ +#pragma GCC diagnostic ignored "-Wunused-parameter" + +%} + +%x string + +%% + string_t *str_buf = NULL; + +\" { + BEGIN(string); + + str_buf = t_str_new(128); + } +<string>\" { + yylval->str = str_c(str_buf); + BEGIN(INITIAL); + return STRING; + } + /* Note: these have to match the event_filter_append_escaped() behavior */ +<string>[^\\"]+ { str_append(str_buf, yytext); } +<string>\\\\ { str_append_c(str_buf, '\\'); } +<string>\\\" { str_append_c(str_buf, '"'); } +<string>\\. { str_append(str_buf, yytext); } + +[Aa][Nn][Dd] { return AND; } +[Oo][Rr] { return OR; } +[Nn][Oo][Tt] { return NOT; } +[<>=()] { return *yytext; } +[A-Za-z0-9:.*?_-]+ { yylval->str = t_strdup(yytext); return TOKEN; } +[ \t\n\r] { /* ignore */ } +. { + /* + * We simply return the char to the + * and let the grammar error out + * with a syntax error. + * + * Note: The cast is significant + * since utf-8 bytes >=128 will + * otherwise result in sign + * extension and a negative int + * getting returned on some + * platforms (e.g., x86) which in + * turn confuses the parser. E.g., + * if: + * *yytext = '\x80' + * we get: + * *yytext -> -128 + * (int) *yytext -> -128 + * which is wrong. With the + * unsigned char cast, we get: + * (u.c.) *yytext -> 128 + * (int)(u.c.) *yytext -> 128 + * which is correct. + */ + return (unsigned char) *yytext; + } +%% + +#pragma GCC diagnostic pop + +void *yyalloc(size_t bytes, void* yyscanner ATTR_UNUSED) +{ + void *ptr = calloc(1, bytes); + if (ptr == NULL) + i_fatal_status(FATAL_OUTOFMEM, "calloc(1, %zu): Out of memory", + bytes); + return ptr; +} + +void *yyrealloc (void *ptr, size_t bytes, void *yyscanner ATTR_UNUSED) +{ + void *nptr = realloc(ptr, bytes); + if (nptr == NULL) + i_fatal_status(FATAL_OUTOFMEM, "realloc(ptr, %zu): Out of memory", + bytes); + return nptr; +} + +void yyfree(void *ptr, void *yyscanner ATTR_UNUSED) +{ + if (ptr == NULL) + return; + free(ptr); +} + +static size_t event_filter_parser_input_proc(char *buf, size_t size, yyscan_t scanner) +{ + struct event_filter_parser_state *state; + size_t num_bytes; + + state = event_filter_parser_get_extra(scanner); + + if (state->len == state->pos) + return 0; + + i_assert(state->len > state->pos); + + num_bytes = I_MIN(state->len - state->pos, size); + memcpy(buf, state->input + state->pos, num_bytes); + state->pos += num_bytes; + + return num_bytes; +} diff --git a/src/lib/event-filter-parser.c b/src/lib/event-filter-parser.c new file mode 100644 index 0000000..9689617 --- /dev/null +++ b/src/lib/event-filter-parser.c @@ -0,0 +1,1852 @@ +/* A Bison parser, made by GNU Bison 3.7.5. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output, and Bison version. */ +#define YYBISON 30705 + +/* Bison version string. */ +#define YYBISON_VERSION "3.7.5" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + +/* Substitute the type names. */ +#define YYSTYPE EVENT_FILTER_PARSER_STYPE +/* Substitute the variable and function names. */ +#define yyparse event_filter_parser_parse +#define yylex event_filter_parser_lex +#define yyerror event_filter_parser_error +#define yydebug event_filter_parser_debug +#define yynerrs event_filter_parser_nerrs + +/* First part of user prologue. */ +#line 11 "event-filter-parser.y" + +#include "lib.h" +#include "wildcard-match.h" +#include "lib-event-private.h" +#include "event-filter-private.h" + +#define scanner state->scanner + +#define YYERROR_VERBOSE + +extern int event_filter_parser_lex(void *, void *); + +void event_filter_parser_error(void *scan, const char *e) +{ + struct event_filter_parser_state *state = scan; + + state->error = t_strdup_printf("event filter: %s", e); +} + +static struct event_filter_node *key_value(struct event_filter_parser_state *state, + const char *a, const char *b, + enum event_filter_node_op op) +{ + struct event_filter_node *node; + enum event_filter_node_type type; + + if (strcmp(a, "event") == 0) + type = EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD; + else if (strcmp(a, "category") == 0) + type = EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY; + else if (strcmp(a, "source_location") == 0) + type = EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION; + else + type = EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD; + + /* only fields support comparators other than EQ */ + if ((type != EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD) && + (op != EVENT_FILTER_OP_CMP_EQ)) { + state->error = "Only fields support inequality comparisons"; + return NULL; + } + + node = p_new(state->pool, struct event_filter_node, 1); + node->type = type; + node->op = op; + + switch (type) { + case EVENT_FILTER_NODE_TYPE_LOGIC: + i_unreached(); + case EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD: + node->str = p_strdup(state->pool, b); + if (wildcard_is_literal(node->str)) + node->type = EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT; + break; + case EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION: { + const char *colon = strrchr(b, ':'); + const char *file; + uintmax_t line; + + /* split "filename:line-number", but also handle "filename" */ + if (colon != NULL) { + if (str_to_uintmax(colon + 1, &line) < 0) { + file = p_strdup(state->pool, b); + line = 0; + } else { + file = p_strdup_until(state->pool, b, colon); + } + } else { + file = p_strdup(state->pool, b); + line = 0; + } + + node->str = file; + node->intmax = line; + break; + } + case EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY: + if (!event_filter_category_to_log_type(b, &node->category.log_type)) { + node->category.name = p_strdup(state->pool, b); + node->category.ptr = event_category_find_registered(b); + } + break; + case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD: + node->field.key = p_strdup(state->pool, a); + node->field.value.str = p_strdup(state->pool, b); + + /* Filter currently supports only comparing strings + and numbers. */ + if (str_to_intmax(b, &node->field.value.intmax) < 0) { + /* not a number - no problem + Either we have a string, or a number with wildcards */ + node->field.value.intmax = INT_MIN; + } + + if (wildcard_is_literal(node->field.value.str)) + node->type = EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT; + break; + case EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT: + case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT: + i_unreached(); + } + + return node; +} + +static struct event_filter_node *logic(struct event_filter_parser_state *state, + struct event_filter_node *a, + struct event_filter_node *b, + enum event_filter_node_op op) +{ + struct event_filter_node *node; + + node = p_new(state->pool, struct event_filter_node, 1); + node->type = EVENT_FILTER_NODE_TYPE_LOGIC; + node->op = op; + node->children[0] = a; + node->children[1] = b; + + return node; +} + +/* ignore strict bool warnings in generated code */ +#ifdef HAVE_STRICT_BOOL +# pragma GCC diagnostic ignored "-Wstrict-bool" +#endif + + +#line 205 "event-filter-parser.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast<Type> (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +#include "event-filter-parser.h" +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_TOKEN = 3, /* TOKEN */ + YYSYMBOL_STRING = 4, /* STRING */ + YYSYMBOL_AND = 5, /* AND */ + YYSYMBOL_OR = 6, /* OR */ + YYSYMBOL_NOT = 7, /* NOT */ + YYSYMBOL_8_ = 8, /* '(' */ + YYSYMBOL_9_ = 9, /* ')' */ + YYSYMBOL_10_ = 10, /* '=' */ + YYSYMBOL_11_ = 11, /* '>' */ + YYSYMBOL_12_ = 12, /* '<' */ + YYSYMBOL_YYACCEPT = 13, /* $accept */ + YYSYMBOL_filter = 14, /* filter */ + YYSYMBOL_expr = 15, /* expr */ + YYSYMBOL_key_value = 16, /* key_value */ + YYSYMBOL_key = 17, /* key */ + YYSYMBOL_value = 18, /* value */ + YYSYMBOL_op = 19 /* op */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + <limits.h> and (if available) <stdint.h> are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include <limits.h> /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stdint.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + <https://h20195.www2.hpe.com/V2/getpdf.aspx/4AA4-7673ENW.pdf>. */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if 1 + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* 1 */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined EVENT_FILTER_PARSER_STYPE_IS_TRIVIAL && EVENT_FILTER_PARSER_STYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 11 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 24 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 13 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 7 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 21 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 29 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 262 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 8, 9, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 12, 10, 11, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7 +}; + +#if EVENT_FILTER_PARSER_DEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint8 yyrline[] = +{ + 0, 156, 156, 157, 160, 161, 162, 163, 164, 167, + 178, 179, 182, 183, 184, 185, 186, 189, 190, 191, + 192, 193 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if 1 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "TOKEN", "STRING", + "AND", "OR", "NOT", "'('", "')'", "'='", "'>'", "'<'", "$accept", + "filter", "expr", "key_value", "key", "value", "op", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 40, 41, + 61, 62, 60 +}; +#endif + +#define YYPACT_NINF (-6) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + -1, -6, -6, -1, -1, 4, 13, -6, 12, -6, + 11, -6, -1, -1, -6, -5, -2, 8, -6, -6, + -6, -6, -6, -6, -6, -6, -6, -6, -6 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 3, 10, 11, 0, 0, 0, 2, 8, 0, 6, + 0, 1, 0, 0, 17, 18, 19, 0, 7, 4, + 5, 20, 21, 12, 13, 14, 15, 16, 9 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -6, -6, -3, -6, -6, -6, -6 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + 0, 5, 6, 7, 8, 28, 17 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 yytable[] = +{ + 9, 10, 1, 2, 11, 21, 3, 4, 22, 19, + 20, 23, 24, 25, 26, 27, 12, 13, 12, 13, + 18, 0, 14, 15, 16 +}; + +static const yytype_int8 yycheck[] = +{ + 3, 4, 3, 4, 0, 10, 7, 8, 10, 12, + 13, 3, 4, 5, 6, 7, 5, 6, 5, 6, + 9, -1, 10, 11, 12 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 3, 4, 7, 8, 14, 15, 16, 17, 15, + 15, 0, 5, 6, 10, 11, 12, 19, 9, 15, + 15, 10, 10, 3, 4, 5, 6, 7, 18 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 13, 14, 14, 15, 15, 15, 15, 15, 16, + 17, 17, 18, 18, 18, 18, 18, 19, 19, 19, + 19, 19 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 0, 3, 3, 2, 3, 1, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = EVENT_FILTER_PARSER_EMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == EVENT_FILTER_PARSER_EMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (state, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use EVENT_FILTER_PARSER_error or EVENT_FILTER_PARSER_UNDEF. */ +#define YYERRCODE EVENT_FILTER_PARSER_UNDEF + + +/* Enable debugging if requested. */ +#if EVENT_FILTER_PARSER_DEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +# ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value, state); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, struct event_filter_parser_state *state) +{ + FILE *yyoutput = yyo; + YY_USE (yyoutput); + YY_USE (state); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yykind < YYNTOKENS) + YYPRINT (yyo, yytoknum[yykind], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, struct event_filter_parser_state *state) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep, state); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule, struct event_filter_parser_state *state) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)], state); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule, state); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !EVENT_FILTER_PARSER_DEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !EVENT_FILTER_PARSER_DEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +/* Context of a parse error. */ +typedef struct +{ + yy_state_t *yyssp; + yysymbol_kind_t yytoken; +} yypcontext_t; + +/* Put in YYARG at most YYARGN of the expected tokens given the + current YYCTX, and return the number of tokens stored in YYARG. If + YYARG is null, return the number of expected tokens (guaranteed to + be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. + Return 0 if there are more than YYARGN expected tokens, yet fill + YYARG up to YYARGN. */ +static int +yypcontext_expected_tokens (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + int yyn = yypact[+*yyctx->yyssp]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (!yyarg) + ++yycount; + else if (yycount == yyargn) + return 0; + else + yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); + } + } + if (yyarg && yycount == 0 && 0 < yyargn) + yyarg[0] = YYSYMBOL_YYEMPTY; + return yycount; +} + + + + +#ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +#endif + +#ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +#endif + +#ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +#endif + + +static int +yy_syntax_error_arguments (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yyctx->yytoken != YYSYMBOL_YYEMPTY) + { + int yyn; + if (yyarg) + yyarg[yycount] = yyctx->yytoken; + ++yycount; + yyn = yypcontext_expected_tokens (yyctx, + yyarg ? yyarg + 1 : yyarg, yyargn - 1); + if (yyn == YYENOMEM) + return YYENOMEM; + else + yycount += yyn; + } + return yycount; +} + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + const yypcontext_t *yyctx) +{ + enum { YYARGS_MAX = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + yysymbol_kind_t yyarg[YYARGS_MAX]; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* Actual size of YYARG. */ + int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); + if (yycount == YYENOMEM) + return YYENOMEM; + + switch (yycount) + { +#define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +#undef YYCASE_ + } + + /* Compute error message size. Don't count the "%s"s, but reserve + room for the terminator. */ + yysize = yystrlen (yyformat) - 2 * yycount + 1; + { + int yyi; + for (yyi = 0; yyi < yycount; ++yyi) + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyarg[yyi]]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return YYENOMEM; + } + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return -1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yytname[yyarg[yyi++]]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep, struct event_filter_parser_state *state) +{ + YY_USE (yyvaluep); + YY_USE (state); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (struct event_filter_parser_state *state) +{ +/* Lookahead token kind. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + + /* Number of syntax errors so far. */ + int yynerrs = 0; + + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = EVENT_FILTER_PARSER_EMPTY; /* Cause a token to be read. */ + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == EVENT_FILTER_PARSER_EMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (&yylval, scanner); + } + + if (yychar <= EVENT_FILTER_PARSER_EOF) + { + yychar = EVENT_FILTER_PARSER_EOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == EVENT_FILTER_PARSER_error) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = EVENT_FILTER_PARSER_UNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = EVENT_FILTER_PARSER_EMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: /* filter: expr */ +#line 156 "event-filter-parser.y" + { state->output = (yyvsp[0].node); } +#line 1501 "event-filter-parser.c" + break; + + case 3: /* filter: %empty */ +#line 157 "event-filter-parser.y" + { state->output = NULL; } +#line 1507 "event-filter-parser.c" + break; + + case 4: /* expr: expr AND expr */ +#line 160 "event-filter-parser.y" + { (yyval.node) = logic(state, (yyvsp[-2].node), (yyvsp[0].node), EVENT_FILTER_OP_AND); } +#line 1513 "event-filter-parser.c" + break; + + case 5: /* expr: expr OR expr */ +#line 161 "event-filter-parser.y" + { (yyval.node) = logic(state, (yyvsp[-2].node), (yyvsp[0].node), EVENT_FILTER_OP_OR); } +#line 1519 "event-filter-parser.c" + break; + + case 6: /* expr: NOT expr */ +#line 162 "event-filter-parser.y" + { (yyval.node) = logic(state, (yyvsp[0].node), NULL, EVENT_FILTER_OP_NOT); } +#line 1525 "event-filter-parser.c" + break; + + case 7: /* expr: '(' expr ')' */ +#line 163 "event-filter-parser.y" + { (yyval.node) = (yyvsp[-1].node); } +#line 1531 "event-filter-parser.c" + break; + + case 8: /* expr: key_value */ +#line 164 "event-filter-parser.y" + { (yyval.node) = (yyvsp[0].node); } +#line 1537 "event-filter-parser.c" + break; + + case 9: /* key_value: key op value */ +#line 167 "event-filter-parser.y" + { + (yyval.node) = key_value(state, (yyvsp[-2].str), (yyvsp[0].str), (yyvsp[-1].op)); + if ((yyval.node) == NULL) { + yyerror(state, state->error); + /* avoid compiler warning about yynerrs being set, but not used */ + (void)yynerrs; + YYERROR; + } + } +#line 1551 "event-filter-parser.c" + break; + + case 10: /* key: TOKEN */ +#line 178 "event-filter-parser.y" + { (yyval.str) = (yyvsp[0].str); } +#line 1557 "event-filter-parser.c" + break; + + case 11: /* key: STRING */ +#line 179 "event-filter-parser.y" + { (yyval.str) = (yyvsp[0].str); } +#line 1563 "event-filter-parser.c" + break; + + case 12: /* value: TOKEN */ +#line 182 "event-filter-parser.y" + { (yyval.str) = (yyvsp[0].str); } +#line 1569 "event-filter-parser.c" + break; + + case 13: /* value: STRING */ +#line 183 "event-filter-parser.y" + { (yyval.str) = (yyvsp[0].str); } +#line 1575 "event-filter-parser.c" + break; + + case 14: /* value: AND */ +#line 184 "event-filter-parser.y" + { (yyval.str) = "and"; } +#line 1581 "event-filter-parser.c" + break; + + case 15: /* value: OR */ +#line 185 "event-filter-parser.y" + { (yyval.str) = "or"; } +#line 1587 "event-filter-parser.c" + break; + + case 16: /* value: NOT */ +#line 186 "event-filter-parser.y" + { (yyval.str) = "not"; } +#line 1593 "event-filter-parser.c" + break; + + case 17: /* op: '=' */ +#line 189 "event-filter-parser.y" + { (yyval.op) = EVENT_FILTER_OP_CMP_EQ; } +#line 1599 "event-filter-parser.c" + break; + + case 18: /* op: '>' */ +#line 190 "event-filter-parser.y" + { (yyval.op) = EVENT_FILTER_OP_CMP_GT; } +#line 1605 "event-filter-parser.c" + break; + + case 19: /* op: '<' */ +#line 191 "event-filter-parser.y" + { (yyval.op) = EVENT_FILTER_OP_CMP_LT; } +#line 1611 "event-filter-parser.c" + break; + + case 20: /* op: '>' '=' */ +#line 192 "event-filter-parser.y" + { (yyval.op) = EVENT_FILTER_OP_CMP_GE; } +#line 1617 "event-filter-parser.c" + break; + + case 21: /* op: '<' '=' */ +#line 193 "event-filter-parser.y" + { (yyval.op) = EVENT_FILTER_OP_CMP_LE; } +#line 1623 "event-filter-parser.c" + break; + + +#line 1627 "event-filter-parser.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == EVENT_FILTER_PARSER_EMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + { + yypcontext_t yyctx + = {yyssp, yytoken}; + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == -1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (yymsg) + { + yysyntax_error_status + = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + yymsgp = yymsg; + } + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = YYENOMEM; + } + } + yyerror (state, yymsgp); + if (yysyntax_error_status == YYENOMEM) + goto yyexhaustedlab; + } + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= EVENT_FILTER_PARSER_EOF) + { + /* Return failure if at end of input. */ + if (yychar == EVENT_FILTER_PARSER_EOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, state); + yychar = EVENT_FILTER_PARSER_EMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp, state); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if 1 +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (state, YY_("memory exhausted")); + yyresult = 2; + goto yyreturn; +#endif + + +/*-------------------------------------------------------. +| yyreturn -- parsing is finished, clean up and return. | +`-------------------------------------------------------*/ +yyreturn: + if (yychar != EVENT_FILTER_PARSER_EMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, state); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, state); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + return yyresult; +} + +#line 195 "event-filter-parser.y" + diff --git a/src/lib/event-filter-parser.h b/src/lib/event-filter-parser.h new file mode 100644 index 0000000..4df919d --- /dev/null +++ b/src/lib/event-filter-parser.h @@ -0,0 +1,96 @@ +/* A Bison parser, made by GNU Bison 3.7.5. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY_EVENT_FILTER_PARSER_EVENT_FILTER_PARSER_H_INCLUDED +# define YY_EVENT_FILTER_PARSER_EVENT_FILTER_PARSER_H_INCLUDED +/* Debug traces. */ +#ifndef EVENT_FILTER_PARSER_DEBUG +# if defined YYDEBUG +#if YYDEBUG +# define EVENT_FILTER_PARSER_DEBUG 1 +# else +# define EVENT_FILTER_PARSER_DEBUG 0 +# endif +# else /* ! defined YYDEBUG */ +# define EVENT_FILTER_PARSER_DEBUG 0 +# endif /* ! defined YYDEBUG */ +#endif /* ! defined EVENT_FILTER_PARSER_DEBUG */ +#if EVENT_FILTER_PARSER_DEBUG +extern int event_filter_parser_debug; +#endif + +/* Token kinds. */ +#ifndef EVENT_FILTER_PARSER_TOKENTYPE +# define EVENT_FILTER_PARSER_TOKENTYPE + enum event_filter_parser_tokentype + { + EVENT_FILTER_PARSER_EMPTY = -2, + EVENT_FILTER_PARSER_EOF = 0, /* "end of file" */ + EVENT_FILTER_PARSER_error = 256, /* error */ + EVENT_FILTER_PARSER_UNDEF = 257, /* "invalid token" */ + TOKEN = 258, /* TOKEN */ + STRING = 259, /* STRING */ + AND = 260, /* AND */ + OR = 261, /* OR */ + NOT = 262 /* NOT */ + }; + typedef enum event_filter_parser_tokentype event_filter_parser_token_kind_t; +#endif + +/* Value type. */ +#if ! defined EVENT_FILTER_PARSER_STYPE && ! defined EVENT_FILTER_PARSER_STYPE_IS_DECLARED +union EVENT_FILTER_PARSER_STYPE +{ +#line 139 "event-filter-parser.y" + + const char *str; + enum event_filter_node_op op; + struct event_filter_node *node; + +#line 85 "event-filter-parser.h" + +}; +typedef union EVENT_FILTER_PARSER_STYPE EVENT_FILTER_PARSER_STYPE; +# define EVENT_FILTER_PARSER_STYPE_IS_TRIVIAL 1 +# define EVENT_FILTER_PARSER_STYPE_IS_DECLARED 1 +#endif + + + +int event_filter_parser_parse (struct event_filter_parser_state *state); + +#endif /* !YY_EVENT_FILTER_PARSER_EVENT_FILTER_PARSER_H_INCLUDED */ diff --git a/src/lib/event-filter-parser.y b/src/lib/event-filter-parser.y new file mode 100644 index 0000000..2b75a49 --- /dev/null +++ b/src/lib/event-filter-parser.y @@ -0,0 +1,195 @@ +/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ + +%define api.pure +%define api.prefix {event_filter_parser_} +%define parse.error verbose +%lex-param {void *scanner} +%parse-param {struct event_filter_parser_state *state} + +%defines + +%{ +#include "lib.h" +#include "wildcard-match.h" +#include "lib-event-private.h" +#include "event-filter-private.h" + +#define scanner state->scanner + +#define YYERROR_VERBOSE + +extern int event_filter_parser_lex(void *, void *); + +void event_filter_parser_error(void *scan, const char *e) +{ + struct event_filter_parser_state *state = scan; + + state->error = t_strdup_printf("event filter: %s", e); +} + +static struct event_filter_node *key_value(struct event_filter_parser_state *state, + const char *a, const char *b, + enum event_filter_node_op op) +{ + struct event_filter_node *node; + enum event_filter_node_type type; + + if (strcmp(a, "event") == 0) + type = EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD; + else if (strcmp(a, "category") == 0) + type = EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY; + else if (strcmp(a, "source_location") == 0) + type = EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION; + else + type = EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD; + + /* only fields support comparators other than EQ */ + if ((type != EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD) && + (op != EVENT_FILTER_OP_CMP_EQ)) { + state->error = "Only fields support inequality comparisons"; + return NULL; + } + + node = p_new(state->pool, struct event_filter_node, 1); + node->type = type; + node->op = op; + + switch (type) { + case EVENT_FILTER_NODE_TYPE_LOGIC: + i_unreached(); + case EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD: + node->str = p_strdup(state->pool, b); + if (wildcard_is_literal(node->str)) + node->type = EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT; + break; + case EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION: { + const char *colon = strrchr(b, ':'); + const char *file; + uintmax_t line; + + /* split "filename:line-number", but also handle "filename" */ + if (colon != NULL) { + if (str_to_uintmax(colon + 1, &line) < 0) { + file = p_strdup(state->pool, b); + line = 0; + } else { + file = p_strdup_until(state->pool, b, colon); + } + } else { + file = p_strdup(state->pool, b); + line = 0; + } + + node->str = file; + node->intmax = line; + break; + } + case EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY: + if (!event_filter_category_to_log_type(b, &node->category.log_type)) { + node->category.name = p_strdup(state->pool, b); + node->category.ptr = event_category_find_registered(b); + } + break; + case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD: + node->field.key = p_strdup(state->pool, a); + node->field.value.str = p_strdup(state->pool, b); + + /* Filter currently supports only comparing strings + and numbers. */ + if (str_to_intmax(b, &node->field.value.intmax) < 0) { + /* not a number - no problem + Either we have a string, or a number with wildcards */ + node->field.value.intmax = INT_MIN; + } + + if (wildcard_is_literal(node->field.value.str)) + node->type = EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT; + break; + case EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT: + case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT: + i_unreached(); + } + + return node; +} + +static struct event_filter_node *logic(struct event_filter_parser_state *state, + struct event_filter_node *a, + struct event_filter_node *b, + enum event_filter_node_op op) +{ + struct event_filter_node *node; + + node = p_new(state->pool, struct event_filter_node, 1); + node->type = EVENT_FILTER_NODE_TYPE_LOGIC; + node->op = op; + node->children[0] = a; + node->children[1] = b; + + return node; +} + +/* ignore strict bool warnings in generated code */ +#ifdef HAVE_STRICT_BOOL +# pragma GCC diagnostic ignored "-Wstrict-bool" +#endif + +%} + +%union { + const char *str; + enum event_filter_node_op op; + struct event_filter_node *node; +}; + +%token <str> TOKEN STRING +%token AND OR NOT + +%type <str> key value +%type <op> op +%type <node> expr key_value + +%left AND OR +%right NOT + +%% +filter : expr { state->output = $1; } + | %empty { state->output = NULL; } + ; + +expr : expr AND expr { $$ = logic(state, $1, $3, EVENT_FILTER_OP_AND); } + | expr OR expr { $$ = logic(state, $1, $3, EVENT_FILTER_OP_OR); } + | NOT expr { $$ = logic(state, $2, NULL, EVENT_FILTER_OP_NOT); } + | '(' expr ')' { $$ = $2; } + | key_value { $$ = $1; } + ; + +key_value : key op value { + $$ = key_value(state, $1, $3, $2); + if ($$ == NULL) { + yyerror(state, state->error); + /* avoid compiler warning about yynerrs being set, but not used */ + (void)yynerrs; + YYERROR; + } + } + ; + +key : TOKEN { $$ = $1; } + | STRING { $$ = $1; } + ; + +value : TOKEN { $$ = $1; } + | STRING { $$ = $1; } + | AND { $$ = "and"; } + | OR { $$ = "or"; } + | NOT { $$ = "not"; } + ; + +op : '=' { $$ = EVENT_FILTER_OP_CMP_EQ; } + | '>' { $$ = EVENT_FILTER_OP_CMP_GT; } + | '<' { $$ = EVENT_FILTER_OP_CMP_LT; } + | '>' '=' { $$ = EVENT_FILTER_OP_CMP_GE; } + | '<' '=' { $$ = EVENT_FILTER_OP_CMP_LE; } + ; +%% diff --git a/src/lib/event-filter-private.h b/src/lib/event-filter-private.h new file mode 100644 index 0000000..5cd47bc --- /dev/null +++ b/src/lib/event-filter-private.h @@ -0,0 +1,121 @@ +#ifndef EVENT_FILTER_PRIVATE_H +#define EVENT_FILTER_PRIVATE_H + +#include "event-filter.h" + +enum event_filter_node_op { + /* leaf nodes */ + EVENT_FILTER_OP_CMP_EQ = 1, + EVENT_FILTER_OP_CMP_GT, + EVENT_FILTER_OP_CMP_LT, + EVENT_FILTER_OP_CMP_GE, + EVENT_FILTER_OP_CMP_LE, + + /* internal nodes */ + EVENT_FILTER_OP_AND, + EVENT_FILTER_OP_OR, + EVENT_FILTER_OP_NOT, +}; + +struct event_filter { + struct event_filter *prev, *next; + + pool_t pool; + int refcount; + ARRAY(struct event_filter_query_internal) queries; + + bool fragment; + bool named_queries_only; +}; + +enum event_filter_node_type { + /* internal nodes */ + EVENT_FILTER_NODE_TYPE_LOGIC = 1, /* children */ + + /* leaf nodes */ + EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT, /* str */ + EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD, /* str */ + EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION, /* str + int */ + EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY, /* cat */ + EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT, /* field */ + EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD, /* field */ +}; + +enum event_filter_log_type { + EVENT_FILTER_LOG_TYPE_DEBUG = BIT(0), + EVENT_FILTER_LOG_TYPE_INFO = BIT(1), + EVENT_FILTER_LOG_TYPE_WARNING = BIT(2), + EVENT_FILTER_LOG_TYPE_ERROR = BIT(3), + EVENT_FILTER_LOG_TYPE_FATAL = BIT(4), + EVENT_FILTER_LOG_TYPE_PANIC = BIT(5), + + EVENT_FILTER_LOG_TYPE_ALL = 0xff, +}; + +struct event_filter_node { + enum event_filter_node_type type; + enum event_filter_node_op op; + + /* internal node */ + struct event_filter_node *children[2]; + + /* leaf node */ + const char *str; + uintmax_t intmax; + struct { + /* + * We may be dealing with one of three situations: + * + * 1) the category is a special "log type" category + * 2) the category is a "normal" category which is: + * a) registered + * b) not registered + * + * A "log type" category is always stored here as the + * log_type enum value with the name and ptr members being + * NULL. + * + * A regular category always has a name. Additionally, if + * it is registered, the category pointer is non-NULL. + */ + enum event_filter_log_type log_type; + const char *name; + struct event_category *ptr; + } category; + struct event_field field; +}; + +bool event_filter_category_to_log_type(const char *name, + enum event_filter_log_type *log_type_r); + +/* lexer & parser state */ +struct event_filter_parser_state { + void *scanner; + const char *input; + size_t len; + size_t pos; + + pool_t pool; + struct event_filter_node *output; + const char *error; + bool has_event_name:1; +}; + +int event_filter_parser_lex_init(void **scanner); +int event_filter_parser_lex_destroy(void *yyscanner); +int event_filter_parser_parse(struct event_filter_parser_state *state); +void event_filter_parser_set_extra(void *user, void *yyscanner); +void event_filter_parser_error(void *scan, const char *e); + +/* the following are exposed to allow for unit testing */ +bool +event_filter_query_match_eval(struct event_filter_node *node, + struct event *event, const char *source_filename, + unsigned int source_linenum, + enum event_filter_log_type log_type); +const char * +event_filter_category_from_log_type(enum event_filter_log_type log_type); +struct event_filter_node * +event_filter_get_expr_for_testing(struct event_filter *filter, unsigned int *count_r); + +#endif diff --git a/src/lib/event-filter.c b/src/lib/event-filter.c new file mode 100644 index 0000000..fe146e3 --- /dev/null +++ b/src/lib/event-filter.c @@ -0,0 +1,855 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "llist.h" +#include "str.h" +#include "strescape.h" +#include "wildcard-match.h" +#include "lib-event-private.h" +#include "event-filter.h" +#include "event-filter-private.h" + +/* Note: this has to match the regexp behavior in the event filter lexer file */ +#define event_filter_append_escaped(dst, str) \ + str_append_escaped((dst), (str), strlen(str)) + +enum event_filter_code { + EVENT_FILTER_CODE_NAME = 'n', + EVENT_FILTER_CODE_SOURCE = 's', + EVENT_FILTER_CODE_CATEGORY = 'c', + EVENT_FILTER_CODE_FIELD = 'f', +}; + +/* map <log type> to <event filter log type & name> */ +static const struct log_type_map { + enum event_filter_log_type log_type; + const char *name; +} event_filter_log_type_map[] = { + [LOG_TYPE_DEBUG] = { EVENT_FILTER_LOG_TYPE_DEBUG, "debug" }, + [LOG_TYPE_INFO] = { EVENT_FILTER_LOG_TYPE_INFO, "info" }, + [LOG_TYPE_WARNING] = { EVENT_FILTER_LOG_TYPE_WARNING, "warning" }, + [LOG_TYPE_ERROR] = { EVENT_FILTER_LOG_TYPE_ERROR, "error" }, + [LOG_TYPE_FATAL] = { EVENT_FILTER_LOG_TYPE_FATAL, "fatal" }, + [LOG_TYPE_PANIC] = { EVENT_FILTER_LOG_TYPE_PANIC, "panic" }, +}; + +struct event_filter_query_internal { + struct event_filter_node *expr; + void *context; +}; + +static struct event_filter *event_filters = NULL; + +static struct event_filter *event_filter_create_real(pool_t pool, bool fragment) +{ + struct event_filter *filter; + + filter = p_new(pool, struct event_filter, 1); + filter->pool = pool; + filter->refcount = 1; + filter->named_queries_only = TRUE; + filter->fragment = fragment; + p_array_init(&filter->queries, pool, 4); + if (!fragment) + DLLIST_PREPEND(&event_filters, filter); + return filter; +} + +struct event_filter *event_filter_create(void) +{ + return event_filter_create_real(pool_alloconly_create("event filter", 2048), FALSE); +} + +struct event_filter *event_filter_create_fragment(pool_t pool) +{ + return event_filter_create_real(pool, TRUE); +} + +void event_filter_ref(struct event_filter *filter) +{ + i_assert(filter->refcount > 0); + filter->refcount++; +} + +void event_filter_unref(struct event_filter **_filter) +{ + struct event_filter *filter = *_filter; + + if (filter == NULL) + return; + i_assert(filter->refcount > 0); + + *_filter = NULL; + if (--filter->refcount > 0) + return; + + if (!filter->fragment) { + DLLIST_REMOVE(&event_filters, filter); + + /* fragments' pools are freed by the consumer */ + pool_unref(&filter->pool); + } +} + +/* + * Look for an existing query with the same context pointer and return it. + * + * If not found, allocate a new internal query and return it. + */ +static struct event_filter_query_internal * +event_filter_get_or_alloc_internal_query(struct event_filter *filter, + void *context) +{ + struct event_filter_query_internal *query; + + array_foreach_modifiable(&filter->queries, query) { + if (query->context == context) + return query; + } + + /* no matching context, allocate a new query */ + query = array_append_space(&filter->queries); + query->context = context; + query->expr = NULL; + + return query; +} + +static void add_node(pool_t pool, struct event_filter_node **root, + struct event_filter_node *new, + enum event_filter_node_op op) +{ + struct event_filter_node *parent; + + i_assert((op == EVENT_FILTER_OP_AND) || (op == EVENT_FILTER_OP_OR)); + + if (*root == NULL) { + *root = new; + return; + } + + parent = p_new(pool, struct event_filter_node, 1); + parent->type = EVENT_FILTER_NODE_TYPE_LOGIC; + parent->op = op; + parent->children[0] = *root; + parent->children[1] = new; + + *root = parent; +} + +static bool filter_node_requires_event_name(struct event_filter_node *node) +{ + switch (node->op) { + case EVENT_FILTER_OP_NOT: + return filter_node_requires_event_name(node->children[0]); + case EVENT_FILTER_OP_AND: + return filter_node_requires_event_name(node->children[0]) || + filter_node_requires_event_name(node->children[1]); + case EVENT_FILTER_OP_OR: + return filter_node_requires_event_name(node->children[0]) && + filter_node_requires_event_name(node->children[1]); + default: + return node->type == EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD || + node->type == EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT; + } +} + +int event_filter_parse(const char *str, struct event_filter *filter, + const char **error_r) +{ + struct event_filter_query_internal *int_query; + struct event_filter_parser_state state; + int ret; + + i_zero(&state); + state.input = str; + state.len = strlen(str); + state.pos = 0; + state.pool = filter->pool; + + event_filter_parser_lex_init(&state.scanner); + event_filter_parser_set_extra(&state, state.scanner); + + ret = event_filter_parser_parse(&state); + + event_filter_parser_lex_destroy(state.scanner); + + if ((ret == 0) && (state.output != NULL)) { + /* success - non-NULL expression */ + i_assert(state.error == NULL); + + int_query = event_filter_get_or_alloc_internal_query(filter, NULL); + + add_node(filter->pool, &int_query->expr, state.output, + EVENT_FILTER_OP_OR); + + filter->named_queries_only = filter->named_queries_only && + filter_node_requires_event_name(state.output); + } else if (ret != 0) { + /* error */ + i_assert(state.error != NULL); + + *error_r = state.error; + } + + /* + * Note that success with a NULL expression output is possible, but + * turns into a no-op. + */ + + return (ret != 0) ? -1 : 0; +} + +bool event_filter_category_to_log_type(const char *name, + enum event_filter_log_type *log_type_r) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(event_filter_log_type_map); i++) { + if (strcmp(name, event_filter_log_type_map[i].name) == 0) { + *log_type_r = event_filter_log_type_map[i].log_type; + return TRUE; + } + } + return FALSE; +} + +const char * +event_filter_category_from_log_type(enum event_filter_log_type log_type) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(event_filter_log_type_map); i++) { + if (event_filter_log_type_map[i].log_type == log_type) + return event_filter_log_type_map[i].name; + } + i_unreached(); +} + +static struct event_filter_node * +clone_expr(pool_t pool, struct event_filter_node *old) +{ + struct event_filter_node *new; + + if (old == NULL) + return NULL; + + new = p_new(pool, struct event_filter_node, 1); + new->type = old->type; + new->op = old->op; + new->children[0] = clone_expr(pool, old->children[0]); + new->children[1] = clone_expr(pool, old->children[1]); + new->str = p_strdup(pool, old->str); + new->intmax = old->intmax; + new->category.log_type = old->category.log_type; + new->category.name = p_strdup(pool, old->category.name); + new->category.ptr = old->category.ptr; + new->field.key = p_strdup(pool, old->field.key); + new->field.value_type = old->field.value_type; + new->field.value.str = p_strdup(pool, old->field.value.str); + new->field.value.intmax = old->field.value.intmax; + new->field.value.timeval = old->field.value.timeval; + + return new; +} + +static void +event_filter_merge_with_context_internal(struct event_filter *dest, + const struct event_filter *src, + void *new_context, bool with_context) +{ + const struct event_filter_query_internal *int_query; + + array_foreach(&src->queries, int_query) T_BEGIN { + void *context = with_context ? new_context : int_query->context; + struct event_filter_query_internal *new; + + new = event_filter_get_or_alloc_internal_query(dest, context); + + add_node(dest->pool, &new->expr, + clone_expr(dest->pool, int_query->expr), + EVENT_FILTER_OP_OR); + } T_END; +} + +bool event_filter_remove_queries_with_context(struct event_filter *filter, + void *context) +{ + const struct event_filter_query_internal *int_query; + unsigned int idx; + + array_foreach(&filter->queries, int_query) { + if (int_query->context == context) { + idx = array_foreach_idx(&filter->queries, int_query); + array_delete(&filter->queries, idx, 1); + return TRUE; + } + } + return FALSE; +} + +void event_filter_merge(struct event_filter *dest, + const struct event_filter *src) +{ + event_filter_merge_with_context_internal(dest, src, NULL, FALSE); +} + +void event_filter_merge_with_context(struct event_filter *dest, + const struct event_filter *src, + void *new_context) +{ + event_filter_merge_with_context_internal(dest, src, new_context, TRUE); +} + +static const char * +event_filter_export_query_expr_op(enum event_filter_node_op op) +{ + switch (op) { + case EVENT_FILTER_OP_AND: + case EVENT_FILTER_OP_OR: + case EVENT_FILTER_OP_NOT: + i_unreached(); + case EVENT_FILTER_OP_CMP_EQ: + return "="; + case EVENT_FILTER_OP_CMP_GT: + return ">"; + case EVENT_FILTER_OP_CMP_LT: + return "<"; + case EVENT_FILTER_OP_CMP_GE: + return ">="; + case EVENT_FILTER_OP_CMP_LE: + return "<="; + } + + i_unreached(); +} + +static void +event_filter_export_query_expr(const struct event_filter_query_internal *query, + struct event_filter_node *node, + string_t *dest) +{ + switch (node->type) { + case EVENT_FILTER_NODE_TYPE_LOGIC: + str_append_c(dest, '('); + switch (node->op) { + case EVENT_FILTER_OP_AND: + event_filter_export_query_expr(query, node->children[0], dest); + str_append(dest, " AND "); + event_filter_export_query_expr(query, node->children[1], dest); + break; + case EVENT_FILTER_OP_OR: + event_filter_export_query_expr(query, node->children[0], dest); + str_append(dest, " OR "); + event_filter_export_query_expr(query, node->children[1], dest); + break; + case EVENT_FILTER_OP_NOT: + str_append(dest, "NOT "); + event_filter_export_query_expr(query, node->children[0], dest); + break; + case EVENT_FILTER_OP_CMP_EQ: + case EVENT_FILTER_OP_CMP_GT: + case EVENT_FILTER_OP_CMP_LT: + case EVENT_FILTER_OP_CMP_GE: + case EVENT_FILTER_OP_CMP_LE: + i_unreached(); + } + str_append_c(dest, ')'); + break; + case EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT: + case EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD: + str_append(dest, "event"); + str_append(dest, event_filter_export_query_expr_op(node->op)); + str_append_c(dest, '"'); + event_filter_append_escaped(dest, node->str); + str_append_c(dest, '"'); + break; + case EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION: + str_append(dest, "source_location"); + str_append(dest, event_filter_export_query_expr_op(node->op)); + str_append_c(dest, '"'); + event_filter_append_escaped(dest, node->str); + if (node->intmax != 0) + str_printfa(dest, ":%ju", node->intmax); + str_append_c(dest, '"'); + break; + case EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY: + str_append(dest, "category"); + str_append(dest, event_filter_export_query_expr_op(node->op)); + if (node->category.name != NULL) { + str_append_c(dest, '"'); + event_filter_append_escaped(dest, node->category.name); + str_append_c(dest, '"'); + } else + str_append(dest, event_filter_category_from_log_type(node->category.log_type)); + break; + case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT: + case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD: + str_append_c(dest, '"'); + event_filter_append_escaped(dest, node->field.key); + str_append_c(dest, '"'); + str_append(dest, event_filter_export_query_expr_op(node->op)); + str_append_c(dest, '"'); + event_filter_append_escaped(dest, node->field.value.str); + str_append_c(dest, '"'); + break; + } +} + +static void +event_filter_export_query(const struct event_filter_query_internal *query, + string_t *dest) +{ + str_append_c(dest, '('); + event_filter_export_query_expr(query, query->expr, dest); + str_append_c(dest, ')'); +} + +void event_filter_export(struct event_filter *filter, string_t *dest) +{ + const struct event_filter_query_internal *query; + bool first = TRUE; + + array_foreach(&filter->queries, query) { + if (!first) + str_append(dest, " OR "); + first = FALSE; + event_filter_export_query(query, dest); + } +} + +struct event_filter_node * +event_filter_get_expr_for_testing(struct event_filter *filter, + unsigned int *count_r) +{ + const struct event_filter_query_internal *queries; + + queries = array_get(&filter->queries, count_r); + + return (*count_r == 0) ? NULL : queries[0].expr; +} + +static bool +event_category_match(const struct event_category *category, + const struct event_category *wanted_category) +{ + for (; category != NULL; category = category->parent) { + if (category->internal == wanted_category->internal) + return TRUE; + } + return FALSE; +} + +static bool +event_has_category_nonrecursive(struct event *event, + struct event_category *wanted_category) +{ + struct event_category *cat; + + if (array_is_created(&event->categories)) { + array_foreach_elem(&event->categories, cat) { + if (event_category_match(cat, wanted_category)) + return TRUE; + } + } + return FALSE; +} + +static bool +event_has_category(struct event *event, struct event_filter_node *node, + enum event_filter_log_type log_type) +{ + struct event_category *wanted_category = node->category.ptr; + + /* category is a log type */ + if (node->category.name == NULL) + return (node->category.log_type & log_type) != 0; + + /* category not registered, therefore the event cannot have it */ + if (wanted_category == NULL) + return FALSE; + + while (event != NULL) { + if (event_has_category_nonrecursive(event, wanted_category)) + return TRUE; + /* try also the parent events */ + event = event_get_parent(event); + } + /* check also the global event and its parents */ + event = event_get_global(); + while (event != NULL) { + if (event_has_category_nonrecursive(event, wanted_category)) + return TRUE; + event = event_get_parent(event); + } + return FALSE; +} + +static bool +event_match_strlist_recursive(struct event *event, + const struct event_field *wanted_field, + bool use_strcmp, bool *seen) +{ + const char *wanted_value = wanted_field->value.str; + const struct event_field *field; + const char *value; + bool match; + + if (event == NULL) + return FALSE; + + field = event_find_field_nonrecursive(event, wanted_field->key); + if (field != NULL) { + i_assert(field->value_type == EVENT_FIELD_VALUE_TYPE_STRLIST); + array_foreach_elem(&field->value.strlist, value) { + *seen = TRUE; + match = use_strcmp ? strcmp(value, wanted_value) == 0 : + wildcard_match_icase(value, wanted_value); + if (match) + return TRUE; + } + } + return event_match_strlist_recursive(event->parent, wanted_field, + use_strcmp, seen); +} + +static bool +event_match_strlist(struct event *event, const struct event_field *wanted_field, + bool use_strcmp) +{ + bool seen = FALSE; + + if (event_match_strlist_recursive(event, wanted_field, + use_strcmp, &seen)) + return TRUE; + if (event_match_strlist_recursive(event_get_global(), + wanted_field, use_strcmp, &seen)) + return TRUE; + if (wanted_field->value.str[0] == '\0' && !seen) { + /* strlist="" matches nonexistent strlist */ + return TRUE; + } + return FALSE; + +} + +static bool +event_match_field(struct event *event, const struct event_field *wanted_field, + enum event_filter_node_op op, bool use_strcmp) +{ + const struct event_field *field; + + /* wanted_field has the value in all available formats */ + field = event_find_field_recursive(event, wanted_field->key); + if (field == NULL) { + /* field="" matches nonexistent field */ + return wanted_field->value.str[0] == '\0'; + } + + switch (field->value_type) { + case EVENT_FIELD_VALUE_TYPE_STR: + if (op != EVENT_FILTER_OP_CMP_EQ) { + /* we only support string equality comparisons */ + return FALSE; + } + if (field->value.str[0] == '\0') { + /* field was removed, but it matches field="" filter */ + return wanted_field->value.str[0] == '\0'; + } + if (use_strcmp) + return strcasecmp(field->value.str, wanted_field->value.str) == 0; + else + return wildcard_match_icase(field->value.str, wanted_field->value.str); + case EVENT_FIELD_VALUE_TYPE_INTMAX: + if (wanted_field->value.intmax > INT_MIN) { + /* compare against an integer */ + switch (op) { + case EVENT_FILTER_OP_CMP_EQ: + return field->value.intmax == wanted_field->value.intmax; + case EVENT_FILTER_OP_CMP_GT: + return field->value.intmax > wanted_field->value.intmax; + case EVENT_FILTER_OP_CMP_LT: + return field->value.intmax < wanted_field->value.intmax; + case EVENT_FILTER_OP_CMP_GE: + return field->value.intmax >= wanted_field->value.intmax; + case EVENT_FILTER_OP_CMP_LE: + return field->value.intmax <= wanted_field->value.intmax; + case EVENT_FILTER_OP_AND: + case EVENT_FILTER_OP_OR: + case EVENT_FILTER_OP_NOT: + i_unreached(); + } + i_unreached(); + } else { + /* compare against an "integer" with wildcards */ + if (op != EVENT_FILTER_OP_CMP_EQ) { + /* we only support string equality comparisons */ + return FALSE; + } + char tmp[MAX_INT_STRLEN]; + i_snprintf(tmp, sizeof(tmp), "%jd", field->value.intmax); + if (use_strcmp) + return strcasecmp(field->value.str, wanted_field->value.str) == 0; + else + return wildcard_match_icase(tmp, wanted_field->value.str); + } + case EVENT_FIELD_VALUE_TYPE_TIMEVAL: + /* there's no point to support matching exact timestamps */ + return FALSE; + case EVENT_FIELD_VALUE_TYPE_STRLIST: + /* check if the value is (or is not) on the list, + only string matching makes sense here. */ + if (op != EVENT_FILTER_OP_CMP_EQ) + return FALSE; + return event_match_strlist(event, wanted_field, use_strcmp); + } + i_unreached(); +} + +static bool +event_filter_query_match_cmp(struct event_filter_node *node, + struct event *event, const char *source_filename, + unsigned int source_linenum, + enum event_filter_log_type log_type) +{ + i_assert((node->op == EVENT_FILTER_OP_CMP_EQ) || + (node->op == EVENT_FILTER_OP_CMP_GT) || + (node->op == EVENT_FILTER_OP_CMP_LT) || + (node->op == EVENT_FILTER_OP_CMP_GE) || + (node->op == EVENT_FILTER_OP_CMP_LE)); + + switch (node->type) { + case EVENT_FILTER_NODE_TYPE_LOGIC: + i_unreached(); + case EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT: + return (event->sending_name != NULL) && + strcmp(event->sending_name, node->str) == 0; + case EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD: + return (event->sending_name != NULL) && + wildcard_match(event->sending_name, node->str); + case EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION: + return !((source_linenum != node->intmax && + node->intmax != 0) || + source_filename == NULL || + strcmp(event->source_filename, node->str) != 0); + case EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY: + return event_has_category(event, node, log_type); + case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT: + return event_match_field(event, &node->field, node->op, + TRUE); + case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD: + return event_match_field(event, &node->field, node->op, + FALSE); + } + + i_unreached(); +} + +bool +event_filter_query_match_eval(struct event_filter_node *node, + struct event *event, const char *source_filename, + unsigned int source_linenum, + enum event_filter_log_type log_type) +{ + switch (node->op) { + case EVENT_FILTER_OP_CMP_EQ: + case EVENT_FILTER_OP_CMP_GT: + case EVENT_FILTER_OP_CMP_LT: + case EVENT_FILTER_OP_CMP_GE: + case EVENT_FILTER_OP_CMP_LE: + return event_filter_query_match_cmp(node, event, source_filename, + source_linenum, log_type); + case EVENT_FILTER_OP_AND: + return event_filter_query_match_eval(node->children[0], event, + source_filename, source_linenum, + log_type) && + event_filter_query_match_eval(node->children[1], event, + source_filename, source_linenum, + log_type); + case EVENT_FILTER_OP_OR: + return event_filter_query_match_eval(node->children[0], event, + source_filename, source_linenum, + log_type) || + event_filter_query_match_eval(node->children[1], event, + source_filename, source_linenum, + log_type); + case EVENT_FILTER_OP_NOT: + return !event_filter_query_match_eval(node->children[0], event, + source_filename, source_linenum, + log_type); + } + + i_unreached(); +} + +static bool +event_filter_query_match(const struct event_filter_query_internal *query, + struct event *event, const char *source_filename, + unsigned int source_linenum, + const struct failure_context *ctx) +{ + enum event_filter_log_type log_type; + + i_assert(ctx->type < N_ELEMENTS(event_filter_log_type_map)); + log_type = event_filter_log_type_map[ctx->type].log_type; + + return event_filter_query_match_eval(query->expr, event, source_filename, + source_linenum, log_type); +} + +static bool +event_filter_match_fastpath(struct event_filter *filter, struct event *event) +{ + if (filter->named_queries_only && event->sending_name == NULL) { + /* No debug logging is enabled. Only named events may be wanted + for stats. This event doesn't have a name, so we don't need + to check any further. */ + return FALSE; + } + return TRUE; +} + +bool event_filter_match(struct event_filter *filter, struct event *event, + const struct failure_context *ctx) +{ + if (filter == NULL) + return FALSE; + return event_filter_match_source(filter, event, event->source_filename, + event->source_linenum, ctx); +} + +bool event_filter_match_source(struct event_filter *filter, struct event *event, + const char *source_filename, + unsigned int source_linenum, + const struct failure_context *ctx) +{ + const struct event_filter_query_internal *query; + + i_assert(!filter->fragment); + + if (!event_filter_match_fastpath(filter, event)) + return FALSE; + + array_foreach(&filter->queries, query) { + if (event_filter_query_match(query, event, source_filename, + source_linenum, ctx)) + return TRUE; + } + return FALSE; +} + +struct event_filter_match_iter { + struct event_filter *filter; + struct event *event; + const struct failure_context *failure_ctx; + unsigned int idx; +}; + +struct event_filter_match_iter * +event_filter_match_iter_init(struct event_filter *filter, struct event *event, + const struct failure_context *ctx) +{ + struct event_filter_match_iter *iter; + + i_assert(!filter->fragment); + + iter = i_new(struct event_filter_match_iter, 1); + iter->filter = filter; + iter->event = event; + iter->failure_ctx = ctx; + if (!event_filter_match_fastpath(filter, event)) + iter->idx = UINT_MAX; + return iter; +} + +void *event_filter_match_iter_next(struct event_filter_match_iter *iter) +{ + const struct event_filter_query_internal *queries; + unsigned int count; + + queries = array_get(&iter->filter->queries, &count); + while (iter->idx < count) { + const struct event_filter_query_internal *query = + &queries[iter->idx]; + + iter->idx++; + if (query->context != NULL && + event_filter_query_match(query, iter->event, + iter->event->source_filename, + iter->event->source_linenum, + iter->failure_ctx)) + return query->context; + } + return NULL; +} + +void event_filter_match_iter_deinit(struct event_filter_match_iter **_iter) +{ + struct event_filter_match_iter *iter = *_iter; + + *_iter = NULL; + i_free(iter); +} + +static void +event_filter_query_update_category(struct event_filter_query_internal *query, + struct event_filter_node *node, + struct event_category *category, + bool add) +{ + if (node == NULL) + return; + + switch (node->type) { + case EVENT_FILTER_NODE_TYPE_LOGIC: + event_filter_query_update_category(query, node->children[0], category, add); + event_filter_query_update_category(query, node->children[1], category, add); + break; + case EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT: + case EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD: + case EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION: + case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT: + case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD: + break; + case EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY: + if (node->category.name == NULL) + break; /* log type */ + + if (add) { + if (node->category.ptr != NULL) + break; + + if (strcmp(node->category.name, category->name) == 0) + node->category.ptr = category; + } else { + if (node->category.ptr == category) + node->category.ptr = NULL; + } + break; + } +} + +static void event_filter_category_registered(struct event_category *category) +{ + const bool add = category->internal != NULL; + struct event_filter_query_internal *query; + struct event_filter *filter; + + for (filter = event_filters; filter != NULL; filter = filter->next) { + array_foreach_modifiable(&filter->queries, query) { + event_filter_query_update_category(query, query->expr, + category, add); + } + } +} + +void event_filter_init(void) +{ + event_category_register_callback(event_filter_category_registered); +} + +void event_filter_deinit(void) +{ + event_category_unregister_callback(event_filter_category_registered); +} diff --git a/src/lib/event-filter.h b/src/lib/event-filter.h new file mode 100644 index 0000000..0a55e05 --- /dev/null +++ b/src/lib/event-filter.h @@ -0,0 +1,64 @@ +#ifndef EVENT_FILTER_H +#define EVENT_FILTER_H + +struct event; + +struct event_filter_field { + const char *key; + const char *value; +}; + +struct event_filter *event_filter_create(void); +struct event_filter *event_filter_create_fragment(pool_t pool); +void event_filter_ref(struct event_filter *filter); +void event_filter_unref(struct event_filter **filter); + +/* Add queries from source filter to destination filter. */ +void event_filter_merge(struct event_filter *dest, + const struct event_filter *src); +/* Add queries from source filter to destination filter, but with supplied + context overriding whatever context source queries had. */ +void event_filter_merge_with_context(struct event_filter *dest, + const struct event_filter *src, + void *new_context); + +/* Remove query with given context from filter. + Returns TRUE if query was removed, otherwise FALSE. */ +bool event_filter_remove_queries_with_context(struct event_filter *filter, + void *context); + +/* Export the filter into a string. The context pointers aren't exported. */ +void event_filter_export(struct event_filter *filter, string_t *dest); +/* Add queries to the filter from the given string. The string is expected to + be generated by event_filter_export(). Returns TRUE on success, FALSE on + invalid string. */ +#define event_filter_import(filter, str, error_r) \ + (event_filter_parse((str), (filter), (error_r)) == 0) + +/* Parse a string-ified query, filling the passed in filter */ +int event_filter_parse(const char *str, struct event_filter *filter, + const char **error_r); + +/* Returns TRUE if the event matches the event filter. */ +bool event_filter_match(struct event_filter *filter, struct event *event, + const struct failure_context *ctx); +/* Same as event_filter_match(), but use the given source filename:linenum + instead of taking it from the event. */ +bool event_filter_match_source(struct event_filter *filter, struct event *event, + const char *source_filename, + unsigned int source_linenum, + const struct failure_context *ctx); + +/* Iterate through all queries with non-NULL context that match the event. */ +struct event_filter_match_iter * +event_filter_match_iter_init(struct event_filter *filter, struct event *event, + const struct failure_context *ctx); +/* Return context for the query that matched, or NULL when there are no more + matches. Note: This skips over any queries that have NULL context. */ +void *event_filter_match_iter_next(struct event_filter_match_iter *iter); +void event_filter_match_iter_deinit(struct event_filter_match_iter **iter); + +void event_filter_init(void); +void event_filter_deinit(void); + +#endif diff --git a/src/lib/event-log.c b/src/lib/event-log.c new file mode 100644 index 0000000..f8f2f79 --- /dev/null +++ b/src/lib/event-log.c @@ -0,0 +1,461 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "event-filter.h" +#include "lib-event-private.h" + +unsigned int event_filter_replace_counter = 1; + +static struct event_filter *global_debug_log_filter = NULL; +static struct event_filter *global_debug_send_filter = NULL; +static struct event_filter *global_core_log_filter = NULL; + +#undef e_error +void e_error(struct event *event, + const char *source_filename, unsigned int source_linenum, + const char *fmt, ...) +{ + struct event_log_params params = { + .log_type = LOG_TYPE_ERROR, + .source_filename = source_filename, + .source_linenum = source_linenum, + }; + va_list args; + + va_start(args, fmt); + T_BEGIN { + event_logv(event, ¶ms, fmt, args); + } T_END; + va_end(args); +} + +#undef e_warning +void e_warning(struct event *event, + const char *source_filename, unsigned int source_linenum, + const char *fmt, ...) +{ + struct event_log_params params = { + .log_type = LOG_TYPE_WARNING, + .source_filename = source_filename, + .source_linenum = source_linenum, + }; + va_list args; + + va_start(args, fmt); + T_BEGIN { + event_logv(event, ¶ms, fmt, args); + } T_END; + va_end(args); +} + +#undef e_info +void e_info(struct event *event, + const char *source_filename, unsigned int source_linenum, + const char *fmt, ...) +{ + struct event_log_params params = { + .log_type = LOG_TYPE_INFO, + .source_filename = source_filename, + .source_linenum = source_linenum, + }; + va_list args; + + va_start(args, fmt); + T_BEGIN { + event_logv(event, ¶ms, fmt, args); + } T_END; + va_end(args); +} + +#undef e_debug +void e_debug(struct event *event, + const char *source_filename, unsigned int source_linenum, + const char *fmt, ...) +{ + struct event_log_params params = { + .log_type = LOG_TYPE_DEBUG, + .source_filename = source_filename, + .source_linenum = source_linenum, + }; + va_list args; + + va_start(args, fmt); + T_BEGIN { + event_logv(event, ¶ms, fmt, args); + } T_END; + va_end(args); +} + +#undef e_log +void e_log(struct event *event, enum log_type level, + const char *source_filename, unsigned int source_linenum, + const char *fmt, ...) +{ + struct event_log_params params = { + .log_type = level, + .source_filename = source_filename, + .source_linenum = source_linenum, + }; + va_list args; + + va_start(args, fmt); + T_BEGIN { + event_logv(event, ¶ms, fmt, args); + } T_END; + va_end(args); +} + +struct event_get_log_message_context { + const struct event_log_params *params; + + string_t *log_prefix; + const char *message; + unsigned int type_pos; + + bool replace_prefix:1; + bool str_out_done:1; +}; + +static inline void ATTR_FORMAT(2, 0) +event_get_log_message_str_out(struct event_get_log_message_context *glmctx, + const char *fmt, va_list args) +{ + const struct event_log_params *params = glmctx->params; + string_t *str_out = params->base_str_out; + + /* The message is appended once in full, rather than incremental during + the recursion. */ + + if (glmctx->str_out_done || str_out == NULL) + return; + + /* append the current log prefix to the string buffer */ + if (params->base_str_prefix != NULL && !glmctx->replace_prefix) + str_append(str_out, params->base_str_prefix); + str_append_str(str_out, glmctx->log_prefix); + + if (glmctx->message != NULL) { + /* a child event already constructed a message */ + str_append(str_out, glmctx->message); + } else { + va_list args_copy; + + /* construct message from format and arguments */ + VA_COPY(args_copy, args); + str_vprintfa(str_out, fmt, args_copy); + va_end(args_copy); + } + + /* finished with the string buffer */ + glmctx->str_out_done = TRUE; +} + +static bool ATTR_FORMAT(4, 0) +event_get_log_message(struct event *event, + struct event_get_log_message_context *glmctx, + unsigned int prefixes_dropped, + const char *fmt, va_list args) +{ + const struct event_log_params *params = glmctx->params; + const char *prefix = event->log_prefix; + bool ret = FALSE; + + /* Reached the base event? */ + if (event == params->base_event) { + /* Append the message to the provided string buffer. */ + event_get_log_message_str_out(glmctx, fmt, args); + /* Insert the base send prefix */ + if (params->base_send_prefix != NULL) { + str_insert(glmctx->log_prefix, 0, + params->base_send_prefix); + ret = TRUE; + } + } + + /* Call the message amendment callback for this event if there is one. + */ + if (event->log_message_callback != NULL) { + const char *in_message; + + /* construct the log message composed by children and arguments + */ + if (glmctx->message == NULL) { + str_vprintfa(glmctx->log_prefix, fmt, args); + in_message = str_c(glmctx->log_prefix); + } else if (str_len(glmctx->log_prefix) == 0) { + in_message = glmctx->message; + } else { + str_append(glmctx->log_prefix, glmctx->message); + in_message = str_c(glmctx->log_prefix); + } + + /* reformat the log message */ + glmctx->message = event->log_message_callback( + event->log_message_callback_context, + glmctx->params->log_type, in_message); + + /* continue with a cleared prefix buffer (as prefix is now part + of *message_r). */ + str_truncate(glmctx->log_prefix, 0); + ret = TRUE; + } + + if (event->log_prefix_callback != NULL) { + prefix = event->log_prefix_callback( + event->log_prefix_callback_context); + } + if (event->log_prefix_replace) { + /* this event replaces all parent log prefixes */ + glmctx->replace_prefix = TRUE; + glmctx->type_pos = (prefix == NULL ? 0 : strlen(prefix)); + event_get_log_message_str_out(glmctx, fmt, args); + } + if (prefix != NULL) { + if (event->log_prefix_replace || prefixes_dropped == 0) { + str_insert(glmctx->log_prefix, 0, prefix); + ret = TRUE; + } else if (prefixes_dropped > 0) { + prefixes_dropped--; + } + } + if (event->parent == NULL) { + event_get_log_message_str_out(glmctx, fmt, args); + if (params->base_event == NULL && + params->base_send_prefix != NULL && + !glmctx->replace_prefix) { + str_insert(glmctx->log_prefix, 0, + params->base_send_prefix); + ret = TRUE; + } + } else if (!event->log_prefix_replace && + (!params->no_send || !glmctx->str_out_done)) { + prefixes_dropped += event->log_prefixes_dropped; + if (event_get_log_message(event->parent, glmctx, + prefixes_dropped, fmt, args)) + ret = TRUE; + } + return ret; +} + +void event_log(struct event *event, const struct event_log_params *params, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + event_logv(event, params, fmt, args); + va_end(args); +} + +#undef event_want_log_level +bool event_want_log_level(struct event *event, enum log_type level, + const char *source_filename, + unsigned int source_linenum) +{ + struct failure_context ctx = { .type = LOG_TYPE_DEBUG }; + + if (level >= event->min_log_level) { + /* Always log when level is at least this high */ + return TRUE; + } + + if (event->debug_level_checked_filter_counter == event_filter_replace_counter) { + /* Log filters haven't changed since we last checked this, so + we can rely on the last cached value. FIXME: this doesn't + work correctly if event changes and the change affects + whether the filters would match. */ + return event->sending_debug_log; + } + event->debug_level_checked_filter_counter = + event_filter_replace_counter; + + if (event->forced_debug) { + /* Debugging is forced for this event (and its children) */ + event->sending_debug_log = TRUE; + } else if (global_debug_log_filter != NULL && + event_filter_match_source(global_debug_log_filter, event, + source_filename, source_linenum, &ctx)) { + /* log_debug filter matched */ + event->sending_debug_log = TRUE; + } else if (global_core_log_filter != NULL && + event_filter_match_source(global_core_log_filter, event, + source_filename, source_linenum, &ctx)) { + /* log_core_filter matched */ + event->sending_debug_log = TRUE; + } else { + event->sending_debug_log = FALSE; + } + return event->sending_debug_log; +} + +#undef event_want_level +bool event_want_level(struct event *event, enum log_type level, + const char *source_filename, + unsigned int source_linenum) +{ + if (event_want_log_level(event, level, source_filename, source_linenum)) + return TRUE; + + /* see if debug send filtering matches */ + if (global_debug_send_filter != NULL) { + struct failure_context ctx = { .type = LOG_TYPE_DEBUG }; + + if (event_filter_match_source(global_debug_send_filter, event, + source_filename, source_linenum, + &ctx)) + return TRUE; + } + return FALSE; +} + +static void ATTR_FORMAT(3, 0) +event_logv_params(struct event *event, const struct event_log_params *params, + const char *fmt, va_list args) +{ + struct event_get_log_message_context glmctx; + + struct failure_context ctx = { + .type = params->log_type, + }; + bool abort_after_event = FALSE; + + i_assert(!params->no_send || params->base_str_out != NULL); + + if (global_core_log_filter != NULL && + event_filter_match_source(global_core_log_filter, event, + event->source_filename, + event->source_linenum, &ctx)) + abort_after_event = TRUE; + + i_zero(&glmctx); + glmctx.params = params; + glmctx.log_prefix = t_str_new(64); + if (!event_get_log_message(event, &glmctx, 0, fmt, args)) { + /* keep log prefix as it is */ + if (params->base_str_out != NULL && !glmctx.str_out_done) { + va_list args_copy; + + VA_COPY(args_copy, args); + str_vprintfa(params->base_str_out, fmt, args_copy); + va_end(args_copy); + } + if (!params->no_send) + event_vsend(event, &ctx, fmt, args); + } else if (params->no_send) { + /* don't send the event */ + } else if (glmctx.replace_prefix) { + /* event overrides the log prefix (even if it's "") */ + ctx.log_prefix = str_c(glmctx.log_prefix); + ctx.log_prefix_type_pos = glmctx.type_pos; + if (glmctx.message != NULL) + event_send(event, &ctx, "%s", glmctx.message); + else + event_vsend(event, &ctx, fmt, args); + } else { + /* append to log prefix, but don't fully replace it */ + if (glmctx.message != NULL) + str_append(glmctx.log_prefix, glmctx.message); + else + str_vprintfa(glmctx.log_prefix, fmt, args); + event_send(event, &ctx, "%s", str_c(glmctx.log_prefix)); + } + if (abort_after_event) + abort(); +} + +void event_logv(struct event *event, const struct event_log_params *params, + const char *fmt, va_list args) +{ + const char *orig_source_filename = event->source_filename; + unsigned int orig_source_linenum = event->source_linenum; + int old_errno = errno; + + if (params->source_filename != NULL) { + event_set_source(event, params->source_filename, + params->source_linenum, TRUE); + } + + (void)event_want_log_level(event, params->log_type, + event->source_filename, + event->source_linenum); + + event_ref(event); + event_logv_params(event, params, fmt, args); + event_set_source(event, orig_source_filename, + orig_source_linenum, TRUE); + event_unref(&event); + errno = old_errno; +} + +struct event *event_set_forced_debug(struct event *event, bool force) +{ + if (force) + event->forced_debug = TRUE; + event_recalculate_debug_level(event); + return event; +} + +struct event *event_unset_forced_debug(struct event *event) +{ + event->forced_debug = FALSE; + event_recalculate_debug_level(event); + return event; +} + +void event_set_global_debug_log_filter(struct event_filter *filter) +{ + event_unset_global_debug_log_filter(); + global_debug_log_filter = filter; + event_filter_ref(global_debug_log_filter); + event_filter_replace_counter++; +} + +struct event_filter *event_get_global_debug_log_filter(void) +{ + return global_debug_log_filter; +} + +void event_unset_global_debug_log_filter(void) +{ + event_filter_unref(&global_debug_log_filter); + event_filter_replace_counter++; +} + +void event_set_global_debug_send_filter(struct event_filter *filter) +{ + event_unset_global_debug_send_filter(); + global_debug_send_filter = filter; + event_filter_ref(global_debug_send_filter); + event_filter_replace_counter++; +} + +struct event_filter *event_get_global_debug_send_filter(void) +{ + return global_debug_send_filter; +} + +void event_unset_global_debug_send_filter(void) +{ + event_filter_unref(&global_debug_send_filter); + event_filter_replace_counter++; +} + +void event_set_global_core_log_filter(struct event_filter *filter) +{ + event_unset_global_core_log_filter(); + global_core_log_filter = filter; + event_filter_ref(global_core_log_filter); + event_filter_replace_counter++; +} + +struct event_filter *event_get_global_core_log_filter(void) +{ + return global_core_log_filter; +} + +void event_unset_global_core_log_filter(void) +{ + event_filter_unref(&global_core_log_filter); + event_filter_replace_counter++; +} diff --git a/src/lib/event-log.h b/src/lib/event-log.h new file mode 100644 index 0000000..e5b7eb8 --- /dev/null +++ b/src/lib/event-log.h @@ -0,0 +1,151 @@ +#ifndef EVENT_LOG_H +#define EVENT_LOG_H + +struct event_filter; + +#include "lib-event.h" + +struct event_log_params { + enum log_type log_type; + const char *source_filename; + unsigned int source_linenum; + + /* Base event used as a reference for base_* parameters (see below) */ + struct event *base_event; + + /* Append the event message to base_str_out in addition to emitting the + event as normal. The message appended to the string buffer includes + prefixes and message callback modifications by parent events up until + the base_event. The event is otherwise sent as normal with the full + prefixes and all modifications up to the root event (unless + no_send=TRUE). This is primarily useful to mimic (part of) event + logging in parallel logs that are visible to users. */ + string_t *base_str_out; + + /* Prefix inserted at the base_event for the sent log message. */ + const char *base_send_prefix; + /* Prefix inserted at the base_event for the log message appended to the + string buffer. */ + const char *base_str_prefix; + + /* Don't actually send the event; only append to the provided string + buffer (base_str_out must not be NULL). */ + bool no_send:1; +}; + +/* Increased every time global event filters have changed. */ +extern unsigned int event_filter_replace_counter; + +void e_error(struct event *event, + const char *source_filename, unsigned int source_linenum, + const char *fmt, ...) ATTR_FORMAT(4, 5); +#define e_error(_event, ...) STMT_START { \ + struct event *_tmp_event = (_event); \ + if (event_want_level(_tmp_event, LOG_TYPE_ERROR)) \ + e_error(_tmp_event, __FILE__, __LINE__, __VA_ARGS__); \ + else \ + event_send_abort(_tmp_event); \ + } STMT_END +void e_warning(struct event *event, + const char *source_filename, unsigned int source_linenum, + const char *fmt, ...) ATTR_FORMAT(4, 5); +#define e_warning(_event, ...) STMT_START { \ + struct event *_tmp_event = (_event); \ + if (event_want_level(_tmp_event, LOG_TYPE_WARNING)) \ + e_warning(_tmp_event, __FILE__, __LINE__, __VA_ARGS__); \ + else \ + event_send_abort(_tmp_event); \ + } STMT_END +void e_info(struct event *event, + const char *source_filename, unsigned int source_linenum, + const char *fmt, ...) ATTR_FORMAT(4, 5); +#define e_info(_event, ...) STMT_START { \ + struct event *_tmp_event = (_event); \ + if (event_want_level(_tmp_event, LOG_TYPE_INFO)) \ + e_info(_tmp_event, __FILE__, __LINE__, __VA_ARGS__); \ + else \ + event_send_abort(_tmp_event); \ + } STMT_END +void e_debug(struct event *event, + const char *source_filename, unsigned int source_linenum, + const char *fmt, ...) ATTR_FORMAT(4, 5); +#define e_debug(_event, ...) STMT_START { \ + struct event *_tmp_event = (_event); \ + if (event_want_debug(_tmp_event)) \ + e_debug(_tmp_event, __FILE__, __LINE__, __VA_ARGS__); \ + else \ + event_send_abort(_tmp_event); \ + } STMT_END + +void e_log(struct event *event, enum log_type level, + const char *source_filename, unsigned int source_linenum, + const char *fmt, ...) ATTR_FORMAT(5, 6); +#define e_log(_event, level, ...) STMT_START { \ + struct event *_tmp_event = (_event); \ + if (event_want_level(_tmp_event, level)) \ + e_log(_tmp_event, level, __FILE__, __LINE__, __VA_ARGS__); \ + else \ + event_send_abort(_tmp_event); \ + } STMT_END + +/* Returns TRUE if event should be logged. Typically event_want_debug_log() + could be used in deciding whether to build an expensive debug log message + (e.g. requires extra disk IO). Note that if this is used, the actual + event being sent won't be matched against event filters because it's never + called. The result of the check is cached in the event, so repeated calls + are efficient. */ +bool event_want_log_level(struct event *event, enum log_type level, + const char *source_filename, + unsigned int source_linenum); +#define event_want_log_level(_event, level) event_want_log_level((_event), (level), __FILE__, __LINE__) +#define event_want_debug_log(_event) event_want_log_level((_event), LOG_TYPE_DEBUG) + +/* Returns TRUE if event should be processed (for logging or sending to stats). + The logging is checked with event_want_log_level() with the same caching + behavior. */ +bool event_want_level(struct event *event, enum log_type level, + const char *source_filename, + unsigned int source_linenum); +#define event_want_level(_event, level) event_want_level((_event), (level), __FILE__, __LINE__) +#define event_want_debug(_event) event_want_level((_event), LOG_TYPE_DEBUG) + +void event_log(struct event *event, const struct event_log_params *params, + const char *fmt, ...) + ATTR_FORMAT(3, 4); +void event_logv(struct event *event, const struct event_log_params *params, + const char *fmt, va_list args) + ATTR_FORMAT(3, 0); + +/* If debugging is forced, the global debug log filter is ignored. Changing + this applies only to this event and any child event that is created + afterwards. It doesn't apply to existing child events (mainly for + performance reasons). + + Note that event_set_forced_debug(event, FALSE) is a no-op. To disable + forced-debug, use event_unset_forced_debug(event). */ +struct event *event_set_forced_debug(struct event *event, bool force); +/* Set the forced-debug to FALSE */ +struct event *event_unset_forced_debug(struct event *event); +/* Set the global filter to logging debug events. */ +void event_set_global_debug_log_filter(struct event_filter *filter); +/* Return the current global debug log event filter. */ +struct event_filter *event_get_global_debug_log_filter(void); +/* Unset global debug log filter, if one exists. */ +void event_unset_global_debug_log_filter(void); + +/* Set the global filter to sending debug events. The debug events are also + sent if they match the global debug log filter. */ +void event_set_global_debug_send_filter(struct event_filter *filter); +/* Return the current global debug send event filter. */ +struct event_filter *event_get_global_debug_send_filter(void); +/* Unset global debug send filter, if one exists. */ +void event_unset_global_debug_send_filter(void); + +/* Set/replace the global core filter, which abort()s on matching events. */ +void event_set_global_core_log_filter(struct event_filter *filter); +/* Return the current global core filter. */ +struct event_filter *event_get_global_core_log_filter(void); +/* Unset the global core filter, if one exists. */ +void event_unset_global_core_log_filter(void); + +#endif diff --git a/src/lib/execv-const.c b/src/lib/execv-const.c new file mode 100644 index 0000000..d9bda82 --- /dev/null +++ b/src/lib/execv-const.c @@ -0,0 +1,33 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "execv-const.h" + +#include <unistd.h> + +static char **argv_drop_const(const char *const argv[]) +{ + char **ret; + unsigned int i, count; + + for (count = 0; argv[count] != NULL; count++) ; + + ret = t_new(char *, count + 1); + for (i = 0; i < count; i++) + ret[i] = t_strdup_noconst(argv[i]); + return ret; +} + +void execv_const(const char *path, const char *const argv[]) +{ + (void)execv(path, argv_drop_const(argv)); + i_fatal_status(errno == ENOMEM ? FATAL_OUTOFMEM : FATAL_EXEC, + "execv(%s) failed: %m", path); +} + +void execvp_const(const char *file, const char *const argv[]) +{ + (void)execvp(file, argv_drop_const(argv)); + i_fatal_status(errno == ENOMEM ? FATAL_OUTOFMEM : FATAL_EXEC, + "execvp(%s) failed: %m", file); +} diff --git a/src/lib/execv-const.h b/src/lib/execv-const.h new file mode 100644 index 0000000..f112cec --- /dev/null +++ b/src/lib/execv-const.h @@ -0,0 +1,9 @@ +#ifndef EXECV_CONST_H +#define EXECV_CONST_H + +/* Just like execv() and execvp(), except argv points to const strings. + Also if calling execv*() fails, these functions call i_fatal(). */ +void execv_const(const char *path, const char *const argv[]) ATTR_NORETURN; +void execvp_const(const char *file, const char *const argv[]) ATTR_NORETURN; + +#endif diff --git a/src/lib/failures-private.h b/src/lib/failures-private.h new file mode 100644 index 0000000..864247c --- /dev/null +++ b/src/lib/failures-private.h @@ -0,0 +1,26 @@ +#ifndef FAILURES_PRIVATE_H +#define FAILURES_PRIVATE_H + +typedef int +failure_write_to_file_t(enum log_type type, string_t *data, size_t prefix_len); +typedef string_t * +failure_format_str_t(const struct failure_context *ctx, size_t *prefix_len_r, + const char *format, va_list args); +typedef void failure_on_handler_failure_t(const struct failure_context *ctx); +typedef void failure_post_handler_t(const struct failure_context *ctx); + +struct failure_handler_vfuncs { + failure_write_to_file_t *write; + failure_format_str_t *format; + failure_on_handler_failure_t *on_handler_failure; + failure_post_handler_t *post_handler; +}; + +struct failure_handler_config { + int fatal_err_reset; + struct failure_handler_vfuncs *v; +}; + +extern struct failure_handler_config failure_handler; + +#endif diff --git a/src/lib/failures.c b/src/lib/failures.c new file mode 100644 index 0000000..083e3b8 --- /dev/null +++ b/src/lib/failures.c @@ -0,0 +1,998 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "str.h" +#include "hostpid.h" +#include "net.h" +#include "process-title.h" +#include "lib-signals.h" +#include "backtrace-string.h" +#include "printf-format-fix.h" +#include "write-full.h" +#include "time-util.h" +#include "failures-private.h" + +#include <unistd.h> +#include <fcntl.h> +#include <syslog.h> +#include <time.h> +#include <poll.h> + +#define LOG_TYPE_FLAG_PREFIX_LEN 0x40 +#define LOG_TYPE_FLAG_DISABLE_LOG_PREFIX 0x80 + +const char *failure_log_type_prefixes[LOG_TYPE_COUNT] = { + "Debug: ", + "Info: ", + "Warning: ", + "Error: ", + "Fatal: ", + "Panic: " +}; + +const char *failure_log_type_names[LOG_TYPE_COUNT] = { + "debug", "info", "warning", "error", "fatal", "panic" +}; + +static int log_fd_write(int fd, const unsigned char *data, size_t len); + +static void error_handler_real(const struct failure_context *ctx, + const char *format, va_list args); + +/* Initialize working defaults */ +static failure_callback_t *fatal_handler ATTR_NORETURN = + default_fatal_handler; +static failure_callback_t *error_handler = default_error_handler; +static failure_callback_t *info_handler = default_error_handler; +static failure_callback_t *debug_handler = default_error_handler; +static void (*failure_exit_callback)(int *) = NULL; + +static struct failure_context failure_ctx_debug = { .type = LOG_TYPE_DEBUG }; +static struct failure_context failure_ctx_info = { .type = LOG_TYPE_INFO }; +static struct failure_context failure_ctx_warning = { .type = LOG_TYPE_WARNING }; +static struct failure_context failure_ctx_error = { .type = LOG_TYPE_ERROR }; + +static int log_fd = STDERR_FILENO, log_info_fd = STDERR_FILENO, + log_debug_fd = STDERR_FILENO; +static char *log_prefix = NULL; +static char *log_stamp_format = NULL, *log_stamp_format_suffix = NULL; +static bool failure_ignore_errors = FALSE, log_prefix_sent = FALSE; +static bool coredump_on_error = FALSE; +static void log_timestamp_add(const struct failure_context *ctx, string_t *str); +static void log_prefix_add(const struct failure_context *ctx, string_t *str); +static int i_failure_send_option_forced(const char *key, const char *value); +static int internal_send_split(string_t *full_str, size_t prefix_len); + +static string_t * ATTR_FORMAT(3, 0) default_format(const struct failure_context *ctx, + size_t *prefix_len_r ATTR_UNUSED, + const char *format, + va_list args) +{ + string_t *str = t_str_new(256); + log_timestamp_add(ctx, str); + log_prefix_add(ctx, str); + + /* make sure there's no %n in there and fix %m */ + str_vprintfa(str, printf_format_fix(format), args); + return str; +} + +static int default_write(enum log_type type, string_t *data, size_t prefix_len ATTR_UNUSED) +{ + int fd; + + switch (type) { + case LOG_TYPE_DEBUG: + fd = log_debug_fd; + break; + case LOG_TYPE_INFO: + fd = log_info_fd; + break; + default: + fd = log_fd; + break; + } + str_append_c(data, '\n'); + return log_fd_write(fd, str_data(data), str_len(data)); +} + +static void default_on_handler_failure(const struct failure_context *ctx) +{ + const char *log_type = "info"; + switch (ctx->type) { + case LOG_TYPE_DEBUG: + log_type = "debug"; + /* fall through */ + case LOG_TYPE_INFO: + /* we failed to log to info/debug log, try to log the + write error to error log - maybe that'll work. */ + i_fatal_status(FATAL_LOGWRITE, "write() failed to %s log: %m", + log_type); + break; + default: + failure_exit(FATAL_LOGWRITE); + break; + } +} + +static void default_post_handler(const struct failure_context *ctx) +{ + if (ctx->type == LOG_TYPE_ERROR && coredump_on_error) + abort(); +} + +static string_t * ATTR_FORMAT(3, 0) syslog_format(const struct failure_context *ctx, + size_t *prefix_len_r ATTR_UNUSED, + const char *format, + va_list args) +{ + string_t *str = t_str_new(128); + if (ctx->type == LOG_TYPE_INFO) { + if (ctx->log_prefix != NULL) + str_append(str, ctx->log_prefix); + else if (log_prefix != NULL) + str_append(str, log_prefix); + } else { + log_prefix_add(ctx, str); + } + str_vprintfa(str, format, args); + return str; +} + +static int syslog_write(enum log_type type, string_t *data, size_t prefix_len ATTR_UNUSED) +{ + int level = LOG_ERR; + + switch (type) { + case LOG_TYPE_DEBUG: + level = LOG_DEBUG; + break; + case LOG_TYPE_INFO: + level = LOG_INFO; + break; + case LOG_TYPE_WARNING: + level = LOG_WARNING; + break; + case LOG_TYPE_ERROR: + level = LOG_ERR; + break; + case LOG_TYPE_FATAL: + case LOG_TYPE_PANIC: + level = LOG_CRIT; + break; + case LOG_TYPE_COUNT: + case LOG_TYPE_OPTION: + i_unreached(); + } + syslog(level, "%s", str_c(data)); + return 0; +} + +static void syslog_on_handler_failure(const struct failure_context *ctx ATTR_UNUSED) +{ + failure_exit(FATAL_LOGERROR); +} + +static void syslog_post_handler(const struct failure_context *ctx ATTR_UNUSED) +{ +} + +static string_t * ATTR_FORMAT(3, 0) internal_format(const struct failure_context *ctx, + size_t *prefix_len_r, + const char *format, + va_list args) +{ + string_t *str; + unsigned char log_type = ctx->type + 1; + + if (ctx->log_prefix != NULL) { + log_type |= LOG_TYPE_FLAG_DISABLE_LOG_PREFIX; + if (ctx->log_prefix_type_pos != 0) + log_type |= LOG_TYPE_FLAG_PREFIX_LEN; + } else if (!log_prefix_sent && log_prefix != NULL) { + if (i_failure_send_option_forced("prefix", log_prefix) < 0) { + /* Failed to write log prefix. The log message writing + would likely fail as well, but don't even try since + the log prefix would be wrong. */ + return NULL; + } + log_prefix_sent = TRUE; + } + + str = t_str_new(128); + str_printfa(str, "\001%c%s ", log_type, my_pid); + if ((log_type & LOG_TYPE_FLAG_PREFIX_LEN) != 0) + str_printfa(str, "%u ", ctx->log_prefix_type_pos); + if (ctx->log_prefix != NULL) + str_append(str, ctx->log_prefix); + *prefix_len_r = str_len(str); + + str_vprintfa(str, format, args); + return str; +} + +static int internal_write(enum log_type type ATTR_UNUSED, string_t *data, size_t prefix_len) +{ + if (str_len(data)+1 <= PIPE_BUF) { + str_append_c(data, '\n'); + return log_fd_write(STDERR_FILENO, + str_data(data), str_len(data)); + } + return internal_send_split(data, prefix_len); +} + +static void internal_on_handler_failure(const struct failure_context *ctx ATTR_UNUSED) +{ + failure_exit(FATAL_LOGERROR); +} + +static void internal_post_handler(const struct failure_context *ctx ATTR_UNUSED) +{ +} + +static struct failure_handler_vfuncs default_handler_vfuncs = { + .write = &default_write, + .format = &default_format, + .on_handler_failure = &default_on_handler_failure, + .post_handler = &default_post_handler +}; + +static struct failure_handler_vfuncs syslog_handler_vfuncs = { + .write = &syslog_write, + .format = &syslog_format, + .on_handler_failure = &syslog_on_handler_failure, + .post_handler = &syslog_post_handler +}; + +static struct failure_handler_vfuncs internal_handler_vfuncs = { + .write = &internal_write, + .format = &internal_format, + .on_handler_failure = &internal_on_handler_failure, + .post_handler = &internal_post_handler +}; + +struct failure_handler_config failure_handler = { .fatal_err_reset = FATAL_LOGWRITE, + .v = &default_handler_vfuncs }; + +static int common_handler(const struct failure_context *ctx, + const char *format, va_list args) +{ + static int recursed = 0; + int ret; + size_t prefix_len = 0; + + if (recursed >= 2) { + /* we're being called from some signal handler or we ran + out of memory */ + return -1; + } + recursed++; + + T_BEGIN { + string_t *str = failure_handler.v->format(ctx, &prefix_len, format, args); + ret = str == NULL ? -1 : + failure_handler.v->write(ctx->type, str, prefix_len); + } T_END; + + if (ret < 0 && failure_ignore_errors) + ret = 0; + + recursed--; + return ret; +} + +static void error_handler_real(const struct failure_context *ctx, + const char *format, va_list args) +{ + if (common_handler(ctx, format, args) < 0) + failure_handler.v->on_handler_failure(ctx); + failure_handler.v->post_handler(ctx); +} + +static void ATTR_FORMAT(2, 0) +i_internal_error_handler(const struct failure_context *ctx, + const char *format, va_list args); + +/* kludgy .. we want to trust log_stamp_format with -Wformat-nonliteral */ +static const char * +get_log_stamp_format(const char *format_arg, unsigned int timestamp_usecs) + ATTR_FORMAT_ARG(1); + +static const char *get_log_stamp_format(const char *format_arg ATTR_UNUSED, + unsigned int timestamp_usecs) +{ + if (log_stamp_format_suffix == NULL) + return log_stamp_format; + return t_strdup_printf("%s%06u%s", log_stamp_format, + timestamp_usecs, log_stamp_format_suffix); +} + +void failure_exit(int status) +{ + static bool recursed = FALSE; + + if (failure_exit_callback != NULL && !recursed) { + recursed = TRUE; + failure_exit_callback(&status); + } + lib_exit(status); +} + +static void log_timestamp_add(const struct failure_context *ctx, string_t *str) +{ + const struct tm *tm = ctx->timestamp; + char buf[256]; + struct timeval now; + + if (log_stamp_format != NULL) { + if (tm == NULL) { + i_gettimeofday(&now); + tm = localtime(&now.tv_sec); + } else { + now.tv_usec = ctx->timestamp_usecs; + } + + if (strftime(buf, sizeof(buf), + get_log_stamp_format("unused", now.tv_usec), tm) > 0) + str_append(str, buf); + } +} + +static void log_prefix_add(const struct failure_context *ctx, string_t *str) +{ + if (ctx->log_prefix == NULL) { + /* use global log prefix */ + if (log_prefix != NULL) + str_append(str, log_prefix); + str_append(str, failure_log_type_prefixes[ctx->type]); + } else if (ctx->log_prefix_type_pos == 0) { + str_append(str, ctx->log_prefix); + str_append(str, failure_log_type_prefixes[ctx->type]); + } else { + i_assert(ctx->log_prefix_type_pos <= strlen(ctx->log_prefix)); + str_append_data(str, ctx->log_prefix, ctx->log_prefix_type_pos); + str_append(str, failure_log_type_prefixes[ctx->type]); + str_append(str, ctx->log_prefix + ctx->log_prefix_type_pos); + } +} + +static void fd_wait_writable(int fd) +{ + struct pollfd pfd = { + .fd = fd, + .events = POLLOUT | POLLERR | POLLHUP | POLLNVAL, + }; + + /* Use poll() instead of ioloop, because we don't want to recurse back + to log writing in case something fails. */ + if (poll(&pfd, 1, -1) < 0 && errno != EINTR) { + /* Unexpected error. We're already blocking on log writes, + so we can't log it. */ + abort(); + } +} + +static int log_fd_write(int fd, const unsigned char *data, size_t len) +{ + ssize_t ret; + unsigned int prev_signal_term_counter = signal_term_counter; + unsigned int terminal_eintr_count = 0; + const char *old_title = NULL; + bool failed = FALSE, process_title_changed = FALSE; + + while (!failed && + (ret = write(fd, data, len)) != (ssize_t)len) { + if (ret > 0) { + /* some was written, continue.. */ + data += ret; + len -= ret; + continue; + } + if (ret == 0) { + /* out of disk space? */ + errno = ENOSPC; + failed = TRUE; + break; + } + switch (errno) { + case EAGAIN: { + /* Log fd is nonblocking - wait until we can write more. + Indicate in process title that the process is waiting + because it's waiting on the log. + + Remember that the log fd is shared across processes, + which also means the log fd flags are shared. So if + one process changes the O_NONBLOCK flag for a log fd, + all the processes see the change. To avoid problems, + we'll wait using poll() instead of changing the + O_NONBLOCK flag. */ + if (!process_title_changed) { + const char *title; + + process_title_changed = TRUE; + old_title = t_strdup(process_title_get()); + if (old_title == NULL) + title = "[blocking on log write]"; + else + title = t_strdup_printf("%s - [blocking on log write]", + old_title); + process_title_set(title); + } + fd_wait_writable(fd); + break; + } + case EINTR: + if (prev_signal_term_counter == signal_term_counter) { + /* non-terminal signal. ignore. */ + } else if (terminal_eintr_count++ == 0) { + /* we'd rather not die in the middle of + writing to log. try again once more */ + } else { + /* received two terminal signals. + someone wants us dead. */ + failed = TRUE; + break; + } + break; + default: + failed = TRUE; + break; + } + prev_signal_term_counter = signal_term_counter; + } + if (process_title_changed) + process_title_set(old_title); + return failed ? -1 : 0; +} + +static void ATTR_NORETURN +default_fatal_finish(enum log_type type, int status) +{ + const char *backtrace; + static int recursed = 0; + + recursed++; + if ((type == LOG_TYPE_PANIC || status == FATAL_OUTOFMEM) && + recursed == 1) { + if (backtrace_get(&backtrace) == 0) + i_error("Raw backtrace: %s", backtrace); + } + recursed--; + + if (type == LOG_TYPE_PANIC || getenv("CORE_ERROR") != NULL || + (status == FATAL_OUTOFMEM && getenv("CORE_OUTOFMEM") != NULL)) + abort(); + else + failure_exit(status); +} + +static void ATTR_NORETURN fatal_handler_real(const struct failure_context *ctx, + const char *format, va_list args) +{ + int status = ctx->exit_status; + if (common_handler(ctx, format, args) < 0 && + status == FATAL_DEFAULT) + status = failure_handler.fatal_err_reset; + default_fatal_finish(ctx->type, status); +} + +void default_fatal_handler(const struct failure_context *ctx, + const char *format, va_list args) +{ + failure_handler.v = &default_handler_vfuncs; + failure_handler.fatal_err_reset = FATAL_LOGWRITE; + fatal_handler_real(ctx, format, args); +} + +void default_error_handler(const struct failure_context *ctx, + const char *format, va_list args) +{ + failure_handler.v = &default_handler_vfuncs; + failure_handler.fatal_err_reset = FATAL_LOGWRITE; + error_handler_real(ctx, format, args); +} + +void i_log_type(const struct failure_context *ctx, const char *format, ...) +{ + va_list args; + + va_start(args, format); + i_log_typev(ctx, format, args); + va_end(args); +} + +void i_log_typev(const struct failure_context *ctx, const char *format, + va_list args) +{ + switch (ctx->type) { + case LOG_TYPE_DEBUG: + debug_handler(ctx, format, args); + break; + case LOG_TYPE_INFO: + info_handler(ctx, format, args); + break; + default: + error_handler(ctx, format, args); + } +} + +void i_panic(const char *format, ...) +{ + struct failure_context ctx; + va_list args; + + lib_set_clean_exit(TRUE); + i_zero(&ctx); + ctx.type = LOG_TYPE_PANIC; + + va_start(args, format); + fatal_handler(&ctx, format, args); + i_unreached(); + /*va_end(args);*/ +} + +void i_fatal(const char *format, ...) +{ + struct failure_context ctx; + va_list args; + + lib_set_clean_exit(TRUE); + i_zero(&ctx); + ctx.type = LOG_TYPE_FATAL; + ctx.exit_status = FATAL_DEFAULT; + + va_start(args, format); + fatal_handler(&ctx, format, args); + i_unreached(); + /*va_end(args);*/ +} + +void i_fatal_status(int status, const char *format, ...) +{ + struct failure_context ctx; + va_list args; + + lib_set_clean_exit(TRUE); + i_zero(&ctx); + ctx.type = LOG_TYPE_FATAL; + ctx.exit_status = status; + + va_start(args, format); + fatal_handler(&ctx, format, args); + i_unreached(); + /*va_end(args);*/ +} + +void i_error(const char *format, ...) +{ + int old_errno = errno; + va_list args; + + va_start(args, format); + error_handler(&failure_ctx_error, format, args); + va_end(args); + + errno = old_errno; +} + +void i_warning(const char *format, ...) +{ + int old_errno = errno; + va_list args; + + va_start(args, format); + error_handler(&failure_ctx_warning, format, args); + va_end(args); + + errno = old_errno; +} + +void i_info(const char *format, ...) +{ + int old_errno = errno; + va_list args; + + va_start(args, format); + info_handler(&failure_ctx_info, format, args); + va_end(args); + + errno = old_errno; +} + +void i_debug(const char *format, ...) +{ + int old_errno = errno; + va_list args; + + va_start(args, format); + debug_handler(&failure_ctx_debug, format, args); + va_end(args); + + errno = old_errno; +} + +void i_set_fatal_handler(failure_callback_t *callback ATTR_NORETURN) +{ + fatal_handler = callback; +} + +void i_set_error_handler(failure_callback_t *callback) +{ + coredump_on_error = getenv("CORE_ERROR") != NULL; + error_handler = callback; +} + +void i_set_info_handler(failure_callback_t *callback) +{ + info_handler = callback; +} + +void i_set_debug_handler(failure_callback_t *callback) +{ + debug_handler = callback; +} + +void i_get_failure_handlers(failure_callback_t **fatal_callback_r, + failure_callback_t **error_callback_r, + failure_callback_t **info_callback_r, + failure_callback_t **debug_callback_r) +{ + *fatal_callback_r = fatal_handler; + *error_callback_r = error_handler; + *info_callback_r = info_handler; + *debug_callback_r = debug_handler; +} + +void i_syslog_fatal_handler(const struct failure_context *ctx, + const char *format, va_list args) +{ + failure_handler.v = &syslog_handler_vfuncs; + failure_handler.fatal_err_reset = FATAL_LOGERROR; + fatal_handler_real(ctx, format, args); +} + +void i_syslog_error_handler(const struct failure_context *ctx, + const char *format, va_list args) +{ + failure_handler.v = &syslog_handler_vfuncs; + failure_handler.fatal_err_reset = FATAL_LOGERROR; + error_handler_real(ctx, format, args); +} + +void i_set_failure_syslog(const char *ident, int options, int facility) +{ + openlog(ident, options, facility); + + i_set_fatal_handler(i_syslog_fatal_handler); + i_set_error_handler(i_syslog_error_handler); + i_set_info_handler(i_syslog_error_handler); + i_set_debug_handler(i_syslog_error_handler); +} + +static void open_log_file(int *fd, const char *path) +{ + const char *str; + + if (*fd != STDERR_FILENO) { + if (close(*fd) < 0) { + str = t_strdup_printf("close(%d) failed: %m\n", *fd); + (void)write_full(STDERR_FILENO, str, strlen(str)); + } + } + + if (path == NULL || strcmp(path, "/dev/stderr") == 0) + *fd = STDERR_FILENO; + else { + *fd = open(path, O_CREAT | O_APPEND | O_WRONLY, 0600); + if (*fd == -1) { + *fd = STDERR_FILENO; + str = t_strdup_printf("Can't open log file %s: %m\n", + path); + (void)write_full(STDERR_FILENO, str, strlen(str)); + if (fd == &log_fd) + failure_exit(FATAL_LOGOPEN); + else + i_fatal_status(FATAL_LOGOPEN, "%s", str); + } + fd_close_on_exec(*fd, TRUE); + } +} + +void i_set_failure_file(const char *path, const char *prefix) +{ + i_set_failure_prefix("%s", prefix); + + if (log_info_fd != STDERR_FILENO && log_info_fd != log_fd) { + if (close(log_info_fd) < 0) + i_error("close(%d) failed: %m", log_info_fd); + } + + if (log_debug_fd != STDERR_FILENO && log_debug_fd != log_info_fd && + log_debug_fd != log_fd) { + if (close(log_debug_fd) < 0) + i_error("close(%d) failed: %m", log_debug_fd); + } + + open_log_file(&log_fd, path); + /* if info/debug logs are elsewhere, i_set_info/debug_file() + overrides these later. */ + log_info_fd = log_fd; + log_debug_fd = log_fd; + + i_set_fatal_handler(default_fatal_handler); + i_set_error_handler(default_error_handler); + i_set_info_handler(default_error_handler); + i_set_debug_handler(default_error_handler); +} + +static int i_failure_send_option_forced(const char *key, const char *value) +{ + const char *str; + + str = t_strdup_printf("\001%c%s %s=%s\n", LOG_TYPE_OPTION+1, + my_pid, key, value); + return log_fd_write(STDERR_FILENO, (const unsigned char *)str, + strlen(str)); +} + +static void i_failure_send_option(const char *key, const char *value) +{ + if (error_handler == i_internal_error_handler) + (void)i_failure_send_option_forced(key, value); +} + +void i_set_failure_prefix(const char *prefix_fmt, ...) +{ + va_list args; + + va_start(args, prefix_fmt); + i_free(log_prefix); + log_prefix = i_strdup_vprintf(prefix_fmt, args); + va_end(args); + + log_prefix_sent = FALSE; +} + +void i_unset_failure_prefix(void) +{ + i_free(log_prefix); + log_prefix = i_strdup(""); + log_prefix_sent = FALSE; +} + +const char *i_get_failure_prefix(void) +{ + return log_prefix != NULL ? log_prefix : ""; +} + +static int internal_send_split(string_t *full_str, size_t prefix_len) +{ + /* This function splits the log line into PIPE_BUF sized blocks, so + the log process doesn't see partial lines. The log prefix is + repeated for each sent line. However, if the log prefix is + excessively long, we're still going to send the log lines even + if they are longer than PIPE_BUF. LINE_MIN_TEXT_LEN controls the + minimum number of bytes we're going to send of the actual log line + regardless of the log prefix length. (Alternative solution could be + to just forcibly split the line to PIPE_BUF length blocks without + repeating the log prefix for subsequent lines.) */ +#define LINE_MIN_TEXT_LEN 128 +#if LINE_MIN_TEXT_LEN >= PIPE_BUF +# error LINE_MIN_TEXT_LEN too large +#endif + string_t *str; + size_t max_text_len, pos = prefix_len; + + str = t_str_new(PIPE_BUF); + str_append_data(str, str_data(full_str), prefix_len); + if (prefix_len < PIPE_BUF) { + max_text_len = I_MAX(PIPE_BUF - prefix_len - 1, + LINE_MIN_TEXT_LEN); + } else { + max_text_len = LINE_MIN_TEXT_LEN; + } + + while (pos < str_len(full_str)) { + str_truncate(str, prefix_len); + str_append_max(str, str_c(full_str) + pos, max_text_len); + str_append_c(str, '\n'); + if (log_fd_write(STDERR_FILENO, + str_data(str), str_len(str)) < 0) + return -1; + pos += max_text_len; + } + return 0; +} + + +static bool line_parse_prefix(const char *line, enum log_type *log_type_r, + bool *replace_prefix_r, bool *have_prefix_len_r) +{ + if (*line != 1) + return FALSE; + + unsigned char log_type = (line[1] & 0x3f); + if (log_type == '\0') { + i_warning("Broken log line follows (type=NUL)"); + return FALSE; + } + log_type--; + + if (log_type > LOG_TYPE_OPTION) { + i_warning("Broken log line follows (type=%d)", log_type); + return FALSE; + } + *log_type_r = log_type; + *replace_prefix_r = (line[1] & LOG_TYPE_FLAG_DISABLE_LOG_PREFIX) != 0; + *have_prefix_len_r = (line[1] & LOG_TYPE_FLAG_PREFIX_LEN) != 0; + return TRUE; +} + +void i_failure_parse_line(const char *line, struct failure_line *failure) +{ + bool have_prefix_len = FALSE; + + i_zero(failure); + if (!line_parse_prefix(line, &failure->log_type, + &failure->disable_log_prefix, + &have_prefix_len)) { + failure->log_type = LOG_TYPE_ERROR; + failure->text = line; + return; + } + + line += 2; + failure->text = line; + while (*line >= '0' && *line <= '9') { + failure->pid = failure->pid*10 + (*line - '0'); + line++; + } + if (*line != ' ') { + /* some old protocol? */ + failure->pid = 0; + return; + } + line++; + + if (have_prefix_len) { + if (str_parse_uint(line, &failure->log_prefix_len, &line) < 0 || + line[0] != ' ') { + /* unexpected, but ignore */ + } else { + line++; + if (failure->log_prefix_len > strlen(line)) { + /* invalid */ + failure->log_prefix_len = 0; + } + } + } + failure->text = line; +} + +static void ATTR_NORETURN ATTR_FORMAT(2, 0) +i_internal_fatal_handler(const struct failure_context *ctx, + const char *format, va_list args) +{ + failure_handler.v = &internal_handler_vfuncs; + failure_handler.fatal_err_reset = FATAL_LOGERROR; + fatal_handler_real(ctx, format, args); + + +} + +static void +i_internal_error_handler(const struct failure_context *ctx, + const char *format, va_list args) +{ + failure_handler.v = &internal_handler_vfuncs; + failure_handler.fatal_err_reset = FATAL_LOGERROR; + error_handler_real(ctx, format, args); +} + +void i_set_failure_internal(void) +{ + fd_set_nonblock(STDERR_FILENO, TRUE); + i_set_fatal_handler(i_internal_fatal_handler); + i_set_error_handler(i_internal_error_handler); + i_set_info_handler(i_internal_error_handler); + i_set_debug_handler(i_internal_error_handler); +} + +bool i_failure_handler_is_internal(failure_callback_t *const callback) +{ + return callback == i_internal_fatal_handler || + callback == i_internal_error_handler; +} + +void i_set_failure_ignore_errors(bool ignore) +{ + failure_ignore_errors = ignore; +} + +void i_set_info_file(const char *path) +{ + if (log_info_fd == log_fd) + log_info_fd = STDERR_FILENO; + + open_log_file(&log_info_fd, path); + info_handler = default_error_handler; + /* write debug-level messages to the info_log_path, + until i_set_debug_file() was called */ + log_debug_fd = log_info_fd; + i_set_debug_handler(default_error_handler); +} + +void i_set_debug_file(const char *path) +{ + if (log_debug_fd == log_fd || log_debug_fd == log_info_fd) + log_debug_fd = STDERR_FILENO; + + open_log_file(&log_debug_fd, path); + debug_handler = default_error_handler; +} + +void i_set_failure_timestamp_format(const char *fmt) +{ + const char *p; + + i_free(log_stamp_format); + i_free_and_null(log_stamp_format_suffix); + + p = strstr(fmt, "%{usecs}"); + if (p == NULL) + log_stamp_format = i_strdup(fmt); + else { + log_stamp_format = i_strdup_until(fmt, p); + log_stamp_format_suffix = i_strdup(p + 8); + } +} + +void i_set_failure_send_ip(const struct ip_addr *ip) +{ + i_failure_send_option("ip", net_ip2addr(ip)); +} + +void i_set_failure_send_prefix(const char *prefix) +{ + i_failure_send_option("prefix", prefix); +} + +void i_set_failure_exit_callback(void (*callback)(int *status)) +{ + failure_exit_callback = callback; +} + +void failures_deinit(void) +{ + if (log_debug_fd == log_info_fd || log_debug_fd == log_fd) + log_debug_fd = STDERR_FILENO; + + if (log_info_fd == log_fd) + log_info_fd = STDERR_FILENO; + + if (log_fd != STDERR_FILENO) { + i_close_fd(&log_fd); + log_fd = STDERR_FILENO; + } + + if (log_info_fd != STDERR_FILENO) { + i_close_fd(&log_info_fd); + log_info_fd = STDERR_FILENO; + } + + if (log_debug_fd != STDERR_FILENO) { + i_close_fd(&log_debug_fd); + log_debug_fd = STDERR_FILENO; + } + + i_free_and_null(log_prefix); + i_free_and_null(log_stamp_format); + i_free_and_null(log_stamp_format_suffix); +} + +#undef i_unreached +void i_unreached(const char *source_filename, int source_linenum) +{ + i_panic("file %s: line %d: unreached", source_filename, source_linenum); +} diff --git a/src/lib/failures.h b/src/lib/failures.h new file mode 100644 index 0000000..afb4c6f --- /dev/null +++ b/src/lib/failures.h @@ -0,0 +1,157 @@ +#ifndef FAILURES_H +#define FAILURES_H + +struct ip_addr; + +/* Default exit status codes that we could use. */ +enum fatal_exit_status { + FATAL_LOGOPEN = 80, /* Can't open log file */ + FATAL_LOGWRITE = 81, /* Can't write to log file */ + FATAL_LOGERROR = 82, /* Internal logging error */ + FATAL_OUTOFMEM = 83, /* Out of memory */ + FATAL_EXEC = 84, /* exec() failed */ + + FATAL_DEFAULT = 89 +}; + +enum log_type { + LOG_TYPE_DEBUG, + LOG_TYPE_INFO, + LOG_TYPE_WARNING, + LOG_TYPE_ERROR, + LOG_TYPE_FATAL, + LOG_TYPE_PANIC, + + LOG_TYPE_COUNT, + /* special case */ + LOG_TYPE_OPTION +}; + +struct failure_line { + pid_t pid; + enum log_type log_type; + /* If non-zero, the first log_prefix_len bytes in text indicate + the log prefix. This implies disable_log_prefix=TRUE. */ + unsigned int log_prefix_len; + /* Disable the global log prefix. */ + bool disable_log_prefix; + const char *text; +}; + +struct failure_context { + enum log_type type; + int exit_status; /* for LOG_TYPE_FATAL */ + const struct tm *timestamp; /* NULL = use time() + localtime() */ + unsigned int timestamp_usecs; + const char *log_prefix; /* override the default log prefix */ + /* If non-0, insert the log type text (e.g. "Info: ") at this position + in the log_prefix instead of appending it. */ + unsigned int log_prefix_type_pos; +}; + +#define DEFAULT_FAILURE_STAMP_FORMAT "%b %d %H:%M:%S " + +typedef void failure_callback_t(const struct failure_context *ctx, + const char *format, va_list args); + +extern const char *failure_log_type_prefixes[]; +extern const char *failure_log_type_names[]; + +void i_log_type(const struct failure_context *ctx, const char *format, ...) + ATTR_FORMAT(2, 3); +void i_log_typev(const struct failure_context *ctx, const char *format, + va_list args) ATTR_FORMAT(2, 0); + +void i_panic(const char *format, ...) ATTR_FORMAT(1, 2) ATTR_NORETURN ATTR_COLD; +void i_unreached(const char *source_filename, int source_linenum) + ATTR_NORETURN ATTR_COLD; +#define i_unreached() \ + i_unreached(__FILE__, __LINE__) +void i_fatal(const char *format, ...) ATTR_FORMAT(1, 2) ATTR_NORETURN ATTR_COLD; +void i_error(const char *format, ...) ATTR_FORMAT(1, 2) ATTR_COLD; +void i_warning(const char *format, ...) ATTR_FORMAT(1, 2); +void i_info(const char *format, ...) ATTR_FORMAT(1, 2); +void i_debug(const char *format, ...) ATTR_FORMAT(1, 2); + +void i_fatal_status(int status, const char *format, ...) + ATTR_FORMAT(2, 3) ATTR_NORETURN ATTR_COLD; + +/* Change failure handlers. */ +#ifndef __cplusplus +void i_set_fatal_handler(failure_callback_t *callback ATTR_NORETURN); +#else +/* Older g++ doesn't like attributes in parameters */ +void i_set_fatal_handler(failure_callback_t *callback); +#endif +void i_set_error_handler(failure_callback_t *callback); +void i_set_info_handler(failure_callback_t *callback); +void i_set_debug_handler(failure_callback_t *callback); +void i_get_failure_handlers(failure_callback_t **fatal_callback_r, + failure_callback_t **error_callback_r, + failure_callback_t **info_callback_r, + failure_callback_t **debug_callback_r); + +/* Send failures to file. */ +void default_fatal_handler(const struct failure_context *ctx, + const char *format, va_list args) + ATTR_NORETURN ATTR_FORMAT(2, 0); +void default_error_handler(const struct failure_context *ctx, + const char *format, va_list args) + ATTR_FORMAT(2, 0); + +/* Send failures to syslog() */ +void i_syslog_fatal_handler(const struct failure_context *ctx, + const char *format, va_list args) + ATTR_NORETURN ATTR_FORMAT(2, 0); +void i_syslog_error_handler(const struct failure_context *ctx, + const char *format, va_list args) + ATTR_FORMAT(2, 0); + +/* Open syslog and set failure/info/debug handlers to use it. */ +void i_set_failure_syslog(const char *ident, int options, int facility); + +/* Send failures to specified log file instead of stderr. */ +void i_set_failure_file(const char *path, const char *prefix); + +/* Send errors to stderr using internal error protocol. */ +void i_set_failure_internal(void); +/* Returns TRUE if the given callback handler was set via + i_set_failure_internal(). */ +bool i_failure_handler_is_internal(failure_callback_t *const callback); +/* If writing to log fails, ignore it instead of existing with + FATAL_LOGWRITE or FATAL_LOGERROR. */ +void i_set_failure_ignore_errors(bool ignore); + +/* Send informational messages to specified log file. i_set_failure_*() + functions modify the info file too, so call this function after them. */ +void i_set_info_file(const char *path); + +/* Send debug-level message to the given log file. The i_set_info_file() + function modifies also the debug log file, so call this function after it. */ +void i_set_debug_file(const char *path); + +/* Set the failure prefix. */ +void i_set_failure_prefix(const char *prefix_fmt, ...) ATTR_FORMAT(1, 2); +/* Set prefix to "". */ +void i_unset_failure_prefix(void); +/* Returns the current failure prefix (never NULL). */ +const char *i_get_failure_prefix(void); +/* Prefix failures with a timestamp. fmt is in strftime() format. */ +void i_set_failure_timestamp_format(const char *fmt); +/* When logging with internal error protocol, update the process's current + IP address / log prefix by sending it to log process. This is mainly used to + improve the error message if the process crashes. */ +void i_set_failure_send_ip(const struct ip_addr *ip); +void i_set_failure_send_prefix(const char *prefix); + +/* Call the callback before exit()ing. The callback may update the status. */ +void i_set_failure_exit_callback(void (*callback)(int *status)); +/* Call the exit callback and exit() */ +void failure_exit(int status) ATTR_NORETURN ATTR_COLD; + +/* Parse a line logged using internal failure handler */ +void i_failure_parse_line(const char *line, struct failure_line *failure); + +void failures_deinit(void); + +#endif diff --git a/src/lib/fd-util.c b/src/lib/fd-util.c new file mode 100644 index 0000000..01315bc --- /dev/null +++ b/src/lib/fd-util.c @@ -0,0 +1,166 @@ +/* Copyright (c) 1999-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "net.h" + +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/un.h> + +void fd_close_on_exec(int fd, bool set) +{ + int flags; + + flags = fcntl(fd, F_GETFD, 0); + if (flags < 0) + i_fatal("fcntl(F_GETFD, %d) failed: %m", fd); + + flags = set ? (flags | FD_CLOEXEC) : (flags & ~FD_CLOEXEC); + if (fcntl(fd, F_SETFD, flags) < 0) + i_fatal("fcntl(F_SETFD, %d) failed: %m", fd); +} + +void fd_debug_verify_leaks(int first_fd, int last_fd) +{ + struct ip_addr addr, raddr; + in_port_t port, rport; + struct stat st; + int old_errno; + bool leaks = FALSE; + + for (int fd = first_fd; fd <= last_fd; ++fd) { + if (fcntl(fd, F_GETFD, 0) == -1 && errno == EBADF) + continue; + + old_errno = errno; + + if (net_getsockname(fd, &addr, &port) == 0) { + if (addr.family == AF_UNIX) { + struct sockaddr_un sa; + + socklen_t socklen = sizeof(sa); + + if (getsockname(fd, (void *)&sa, + &socklen) < 0) + sa.sun_path[0] = '\0'; + + i_error("Leaked UNIX socket fd %d: %s", + fd, sa.sun_path); + leaks = TRUE; + continue; + } + + if (net_getpeername(fd, &raddr, &rport) < 0) { + i_zero(&raddr); + rport = 0; + } + i_error("Leaked socket fd %d: %s:%u -> %s:%u", + fd, net_ip2addr(&addr), port, + net_ip2addr(&raddr), rport); + leaks = TRUE; + continue; + } + + if (fstat(fd, &st) == 0) { +#ifdef __APPLE__ + /* OSX workaround: gettimeofday() calls shm_open() + internally and the fd won't get closed on exec. + We'll just skip all ino/dev=0 files and hope they + weren't anything else. */ + if (st.st_ino == 0 && st.st_dev == 0) + continue; +#endif +#ifdef HAVE_SYS_SYSMACROS_H + i_error("Leaked file fd %d: dev %s.%s inode %s", + fd, dec2str(major(st.st_dev)), + dec2str(minor(st.st_dev)), dec2str(st.st_ino)); + leaks = TRUE; + continue; +#else + i_error("Leaked file fd %d: dev %s inode %s", + fd, dec2str(st.st_dev), + dec2str(st.st_ino)); + leaks = TRUE; + continue; +#endif + } + + i_error("Leaked unknown fd %d (errno = %s)", + fd, strerror(old_errno)); + leaks = TRUE; + continue; + } + if (leaks) + i_fatal("fd leak found"); +} + +void fd_set_nonblock(int fd, bool nonblock) +{ + int flags; + + i_assert(fd > -1); + + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) + i_fatal("fcntl(%d, F_GETFL) failed: %m", fd); + + if (nonblock) + flags |= O_NONBLOCK; + else + flags &= ENUM_NEGATE(O_NONBLOCK); + + if (fcntl(fd, F_SETFL, flags) < 0) + i_fatal("fcntl(%d, F_SETFL) failed: %m", fd); +} + +void fd_close_maybe_stdio(int *fd_in, int *fd_out) +{ + int *fdp[2] = { fd_in, fd_out }; + + if (*fd_in == *fd_out) + *fd_in = -1; + + for (unsigned int i = 0; i < N_ELEMENTS(fdp); i++) { + if (*fdp[i] == -1) + ; + else if (*fdp[i] > 1) + i_close_fd(fdp[i]); + else if (dup2(dev_null_fd, *fdp[i]) == *fdp[i]) + *fdp[i] = -1; + else + i_fatal("dup2(/dev/null, %d) failed: %m", *fdp[i]); + } +} + +#undef i_close_fd_path +void i_close_fd_path(int *fd, const char *path, const char *arg, + const char *func, const char *file, int line) +{ + int saved_errno; + + if (*fd == -1) + return; + + if (unlikely(*fd <= 0)) { + i_panic("%s: close(%s%s%s) @ %s:%d attempted with fd=%d", + func, arg, + (path == NULL) ? "" : " = ", + (path == NULL) ? "" : path, + file, line, *fd); + } + + saved_errno = errno; + /* Ignore ECONNRESET because we don't really care about it here, + as we are closing the socket down in any case. There might be + unsent data but nothing we can do about that. */ + if (unlikely(close(*fd) < 0 && errno != ECONNRESET)) + i_error("%s: close(%s%s%s) @ %s:%d failed (fd=%d): %m", + func, arg, + (path == NULL) ? "" : " = ", + (path == NULL) ? "" : path, + file, line, *fd); + errno = saved_errno; + + *fd = -1; +} diff --git a/src/lib/fd-util.h b/src/lib/fd-util.h new file mode 100644 index 0000000..54bdd63 --- /dev/null +++ b/src/lib/fd-util.h @@ -0,0 +1,26 @@ +#ifndef FD_UTIL_H +#define FD_UTIL_H + +/* Change close-on-exec flag of fd. */ +void fd_close_on_exec(int fd, bool set); + +/* Verify that fds in given range don't exist. */ +void fd_debug_verify_leaks(int first_fd, int last_fd); + +/* Set file descriptor to blocking/nonblocking state */ +void fd_set_nonblock(int fd, bool nonblock); + +/* Close fd_in and fd_out, unless they're already -1. They can point to the + same fd, in which case they're closed only once. If they point to stdin + or stdout, they're replaced with /dev/null. */ +void fd_close_maybe_stdio(int *fd_in, int *fd_out); + +/* Close the fd and set it to -1. This assert-crashes if fd == 0, and is a + no-op if fd == -1. Normally fd == 0 would happen only if an uninitialized + fd is attempted to be closed, which is a bug. */ +void i_close_fd_path(int *fd, const char *path, const char *arg, + const char *func, const char *file, int line); +#define i_close_fd_path(fd, path) i_close_fd_path((fd), (path), #fd, __func__, __FILE__, __LINE__) +#define i_close_fd(fd) i_close_fd_path((fd), NULL) + +#endif diff --git a/src/lib/fdatasync-path.c b/src/lib/fdatasync-path.c new file mode 100644 index 0000000..769f483 --- /dev/null +++ b/src/lib/fdatasync-path.c @@ -0,0 +1,31 @@ +/* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "fdatasync-path.h" + +#include <fcntl.h> +#include <unistd.h> + +int fdatasync_path(const char *path) +{ + int fd, ret = 0; + + /* Directories need to be opened as read-only. + fsync() doesn't appear to care about it. */ + fd = open(path, O_RDONLY); + if (fd == -1) + return -1; + if (fdatasync(fd) < 0) { + /* Some OSes/FSes don't allow fsyncing directories. Silently + ignore the problem. */ + if (errno == EBADF) { + /* e.g. NetBSD */ + } else if (errno == EINVAL) { + /* e.g. Linux+CIFS */ + } else { + ret = -1; + } + } + i_close_fd(&fd); + return ret; +} diff --git a/src/lib/fdatasync-path.h b/src/lib/fdatasync-path.h new file mode 100644 index 0000000..1da6e6e --- /dev/null +++ b/src/lib/fdatasync-path.h @@ -0,0 +1,7 @@ +#ifndef FDATASYNC_PATH_H +#define FDATASYNC_PATH_H + +/* Open and fdatasync() the path. Works for files and directories. */ +int fdatasync_path(const char *path); + +#endif diff --git a/src/lib/fdpass.c b/src/lib/fdpass.c new file mode 100644 index 0000000..bd54443 --- /dev/null +++ b/src/lib/fdpass.c @@ -0,0 +1,212 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +/* + fdpass.c - File descriptor passing between processes via UNIX sockets + + This isn't fully portable, but pretty much all UNIXes nowadays should + support this. If you're having runtime problems with fd_read(), check the + end of fd_read() and play with the if condition. If you're having problems + with fd_send(), try defining BUGGY_CMSG_MACROS. + + If this file doesn't compile at all, you should check if this is supported + in your system at all. It may require some extra #define to enable it. + If not, you're pretty much out of luck. Cygwin didn't last I checked. +*/ + +#define _XPG4_2 + +#if defined(irix) || defined (__irix__) || defined(sgi) || defined (__sgi__) +# define _XOPEN_SOURCE 4 /* for IRIX */ +#endif + +#if !defined(_AIX) && !defined(_XOPEN_SOURCE_EXTENDED) +# define _XOPEN_SOURCE_EXTENDED /* for Tru64, breaks AIX */ +#endif + +#ifdef HAVE_CONFIG_H +# include "lib.h" +#else +# define i_assert(x) +#endif + +#include <string.h> +#include <limits.h> +#include <sys/types.h> + +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/uio.h> + +#include "fdpass.h" + +#ifndef HAVE_CONFIG_H +struct const_iovec { + const void *iov_base; + size_t iov_len; +}; +#endif + +/* RFC 2292 defines CMSG_*() macros, but some operating systems don't have them + so we'll define our own if they don't exist. + + CMSG_LEN(data) is used to calculate size of sizeof(struct cmsghdr) + + sizeof(data) and padding between them. + + CMSG_SPACE(data) also calculates the padding needed after the data, in case + multiple objects are sent. + + cmsghdr contains cmsg_len field and two integers. cmsg_len is sometimes + defined as sockaddr_t and sometimes size_t, so it can be either 32bit or + 64bit. This padding is added by compiler in sizeof(struct cmsghdr). + + Padding required by CMSG_DATA() can vary. Usually it wants size_t or 32bit. + With Solaris it's in _CMSG_DATA_ALIGNMENT (32bit), we assume others want + size_t. + + We don't really need CMSG_SPACE() to be exactly correct, because currently + we send only one object at a time. But anyway I'm trying to keep that + correct in case it's sometimes needed.. +*/ + +#ifdef BUGGY_CMSG_MACROS +/* Some OSes have broken CMSG macros in 64bit systems. The macros use 64bit + alignment while kernel uses 32bit alignment. */ +# undef CMSG_SPACE +# undef CMSG_LEN +# undef CMSG_DATA +# define CMSG_DATA(cmsg) ((char *)((cmsg) + 1)) +# define _CMSG_DATA_ALIGNMENT 4 +# define _CMSG_HDR_ALIGNMENT 4 +#endif + +#ifndef CMSG_SPACE +# define MY_ALIGN(len, align) \ + (((len) + align - 1) & ~(align - 1)) + +/* Alignment between cmsghdr and data */ +# ifndef _CMSG_DATA_ALIGNMENT +# define _CMSG_DATA_ALIGNMENT sizeof(size_t) +# endif +/* Alignment between data and next cmsghdr */ +# ifndef _CMSG_HDR_ALIGNMENT +# define _CMSG_HDR_ALIGNMENT sizeof(size_t) +# endif + +# define CMSG_SPACE(len) \ + (MY_ALIGN(sizeof(struct cmsghdr), _CMSG_DATA_ALIGNMENT) + \ + MY_ALIGN(len, _CMSG_HDR_ALIGNMENT)) +# define CMSG_LEN(len) \ + (MY_ALIGN(sizeof(struct cmsghdr), _CMSG_DATA_ALIGNMENT) + (len)) +#endif + +#ifdef SCM_RIGHTS + +ssize_t fd_send(int handle, int send_fd, const void *data, size_t size) +{ + struct msghdr msg; + struct const_iovec iov; + struct cmsghdr *cmsg; + char buf[CMSG_SPACE(sizeof(int))]; + + /* at least one byte is required to be sent with fd passing */ + i_assert(size > 0 && size < INT_MAX); + + memset(&msg, 0, sizeof(struct msghdr)); + + iov.iov_base = data; + iov.iov_len = size; + + msg.msg_iov = (void *)&iov; + msg.msg_iovlen = 1; + + if (send_fd != -1) { + /* set the control and controllen before CMSG_FIRSTHDR(). */ + memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(send_fd)); + + /* set the real length we want to use. Do it after all is + set just in case CMSG macros required the extra padding + in the end. */ + msg.msg_controllen = cmsg->cmsg_len; + } + + return sendmsg(handle, &msg, 0); +} + +#ifdef LINUX20 +/* Linux 2.0.x doesn't set any cmsg fields. Note that this might make some + attacks possible so don't do it unless you really have to. */ +# define CHECK_CMSG(cmsg) ((cmsg) != NULL) +#else +# define CHECK_CMSG(cmsg) \ + ((cmsg) != NULL && \ + (size_t)(cmsg)->cmsg_len >= (size_t)CMSG_LEN(sizeof(int)) && \ + (cmsg)->cmsg_level == SOL_SOCKET && (cmsg)->cmsg_type == SCM_RIGHTS) +#endif + +ssize_t fd_read(int handle, void *data, size_t size, int *fd) +{ + struct msghdr msg; + struct iovec iov; + struct cmsghdr *cmsg; + ssize_t ret; + char buf[CMSG_SPACE(sizeof(int))]; + + i_assert(size > 0 && size < INT_MAX); + + memset(&msg, 0, sizeof (struct msghdr)); + + iov.iov_base = data; + iov.iov_len = size; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + + ret = recvmsg(handle, &msg, 0); + if (ret <= 0) { + *fd = -1; + return ret; + } + + /* at least one byte transferred - we should have the fd now. + do extra checks to make sure it really is an fd that is being + transferred to avoid potential DoS conditions. some systems don't + set all these values correctly however so CHECK_CMSG() is somewhat + system dependent */ + cmsg = CMSG_FIRSTHDR(&msg); + if (!CHECK_CMSG(cmsg)) + *fd = -1; + else + memcpy(fd, CMSG_DATA(cmsg), sizeof(*fd)); + return ret; +} + +#else +# ifdef __GNUC__ +# warning SCM_RIGHTS not supported, privilege separation not possible +# endif +ssize_t fd_send(int handle ATTR_UNUSED, int send_fd ATTR_UNUSED, + const void *data ATTR_UNUSED, size_t size ATTR_UNUSED) +{ + errno = ENOSYS; + return -1; +} + +ssize_t fd_read(int handle ATTR_UNUSED, void *data ATTR_UNUSED, + size_t size ATTR_UNUSED, int *fd ATTR_UNUSED) +{ + errno = ENOSYS; + return -1; +} +#endif diff --git a/src/lib/fdpass.h b/src/lib/fdpass.h new file mode 100644 index 0000000..c5d42af --- /dev/null +++ b/src/lib/fdpass.h @@ -0,0 +1,15 @@ +#ifndef FDPASS_H +#define FDPASS_H + +/* Send data and send_fd (unless it's -1) via sendmsg(). Returns number of + bytes sent, or -1 on error. If at least 1 byte was sent, the send_fd was + also sent. */ +ssize_t fd_send(int handle, int send_fd, const void *data, size_t size); + +/* Receive data and fd via recvmsg(). Returns number of bytes read, 0 on + disconnection, or -1 on error. If at least 1 byte was read, the fd is also + returned (if it had been sent). If there was no fd received, it's set to + -1. See test-istream-unix.c for different test cases. */ +ssize_t fd_read(int handle, void *data, size_t size, int *fd_r); + +#endif diff --git a/src/lib/file-cache.c b/src/lib/file-cache.c new file mode 100644 index 0000000..008021e --- /dev/null +++ b/src/lib/file-cache.c @@ -0,0 +1,336 @@ +/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "mmap-util.h" +#include "file-cache.h" + +#include <sys/stat.h> + +struct file_cache { + int fd; + char *path; + buffer_t *page_bitmask; + + void *mmap_base; + size_t mmap_length; + size_t read_highwater; +}; + +struct file_cache *file_cache_new(int fd) +{ + return file_cache_new_path(fd, ""); +} + +struct file_cache *file_cache_new_path(int fd, const char *path) +{ + struct file_cache *cache; + + cache = i_new(struct file_cache, 1); + cache->fd = fd; + cache->path = i_strdup(path); + cache->page_bitmask = buffer_create_dynamic(default_pool, 128); + return cache; +} + +void file_cache_free(struct file_cache **_cache) +{ + struct file_cache *cache = *_cache; + + *_cache = NULL; + + if (cache->mmap_base != NULL) { + if (munmap_anon(cache->mmap_base, cache->mmap_length) < 0) + i_error("munmap_anon(%s) failed: %m", cache->path); + } + buffer_free(&cache->page_bitmask); + i_free(cache->path); + i_free(cache); +} + +void file_cache_set_fd(struct file_cache *cache, int fd) +{ + cache->fd = fd; + file_cache_invalidate(cache, 0, cache->mmap_length); +} + +int file_cache_set_size(struct file_cache *cache, uoff_t size) +{ + size_t page_size = mmap_get_page_size(); + uoff_t diff; + void *new_base; + + i_assert(page_size > 0); + + diff = size % page_size; + if (diff != 0) + size += page_size - diff; + + i_assert((size % page_size) == 0); + if (size <= cache->mmap_length) + return 0; + + if (size > SIZE_MAX) { + i_error("file_cache_set_size(%s, %"PRIuUOFF_T"): size too large", + cache->path, size); + return -1; + } + + /* grow mmaping */ + if (cache->mmap_base == NULL) { + cache->mmap_base = mmap_anon(size); + if (cache->mmap_base == MAP_FAILED) { + i_error("mmap_anon(%s, %"PRIuUOFF_T") failed: %m", + cache->path, size); + cache->mmap_base = NULL; + cache->mmap_length = 0; + return -1; + } + } else { + new_base = mremap_anon(cache->mmap_base, cache->mmap_length, + size, MREMAP_MAYMOVE); + if (new_base == MAP_FAILED) { + i_error("mremap_anon(%s, %"PRIuUOFF_T") failed: %m", + cache->path, size); + return -1; + } + + cache->mmap_base = new_base; + } + cache->mmap_length = size; + return 0; +} + +ssize_t file_cache_read(struct file_cache *cache, uoff_t offset, size_t size) +{ + size_t page_size = mmap_get_page_size(); + size_t poffset, psize, dest_offset, dest_size; + unsigned char *bits, *dest; + ssize_t ret; + + i_assert(page_size > 0); + + if (size > SSIZE_T_MAX) { + /* make sure our calculations won't overflow. most likely + we'll be reading less data, but allow it anyway so caller + doesn't have to deal with any extra checks. */ + size = SSIZE_T_MAX; + } + if (offset >= UOFF_T_MAX - size) + size = UOFF_T_MAX - offset; + + if (offset + size > cache->mmap_length && + offset + size - cache->mmap_length > 1024*1024) { + /* growing more than a megabyte, make sure that the + file is large enough so we don't allocate memory + more than needed */ + struct stat st; + + if (fstat(cache->fd, &st) < 0) { + if (errno != ESTALE) + i_error("fstat(%s) failed: %m", cache->path); + return -1; + } + + if (offset + size > (uoff_t)st.st_size) { + if (offset >= (uoff_t)st.st_size) + return 0; + size = (uoff_t)st.st_size - offset; + } + } + + if (file_cache_set_size(cache, offset + size) < 0) + return -1; + + poffset = offset / page_size; + psize = (offset + size + page_size-1) / page_size - poffset; + i_assert(psize > 0); + + bits = buffer_get_space_unsafe(cache->page_bitmask, 0, + (poffset + psize + CHAR_BIT - 1) / + CHAR_BIT); + + dest_offset = poffset * page_size; + dest = PTR_OFFSET(cache->mmap_base, dest_offset); + dest_size = page_size; + + while (psize > 0) { + if ((bits[poffset / CHAR_BIT] & (1 << (poffset % CHAR_BIT))) != 0) { + /* page is already in cache */ + dest_offset += page_size; + if (dest_offset <= cache->read_highwater) { + psize--; poffset++; + dest += page_size; + continue; + } + + /* this is the last partially cached block. + use the caching only if we don't want to + read past read_highwater */ + if (offset + size <= cache->read_highwater) { + i_assert(psize == 1); + break; + } + + /* mark the block noncached again and + read it */ + bits[poffset / CHAR_BIT] &= + ~(1 << (poffset % CHAR_BIT)); + dest_offset -= page_size; + } + + ret = pread(cache->fd, dest, dest_size, dest_offset); + if (ret <= 0) { + if (ret < 0) + return -1; + + /* EOF. mark the last block as cached even if it + isn't completely. read_highwater tells us how far + we've actually made. */ + if (dest_offset == cache->read_highwater) { + i_assert(poffset == + cache->read_highwater / page_size); + bits[poffset / CHAR_BIT] |= + 1 << (poffset % CHAR_BIT); + } + return dest_offset <= offset ? 0 : + dest_offset - offset < size ? + dest_offset - offset : size; + } + + dest += ret; + dest_offset += ret; + + if (cache->read_highwater < dest_offset) { + unsigned int high_poffset = + cache->read_highwater / page_size; + + /* read_highwater needs to be updated. if we didn't + just read that block, we can't trust anymore that + we have it cached */ + bits[high_poffset / CHAR_BIT] &= + ~(1 << (high_poffset % CHAR_BIT)); + cache->read_highwater = dest_offset; + } + + if ((size_t)ret != dest_size) { + /* partial read - probably EOF but make sure. */ + dest_size -= ret; + continue; + } + + bits[poffset / CHAR_BIT] |= 1 << (poffset % CHAR_BIT); + dest_size = page_size; + psize--; poffset++; + } + + return size; +} + +const void *file_cache_get_map(struct file_cache *cache, size_t *size_r) +{ + *size_r = cache->read_highwater; + return cache->mmap_base; +} + +void file_cache_write(struct file_cache *cache, const void *data, size_t size, + uoff_t offset) +{ + size_t page_size = mmap_get_page_size(); + unsigned char *bits; + unsigned int first_page, last_page; + + i_assert(page_size > 0); + i_assert(UOFF_T_MAX - offset > size); + + if (file_cache_set_size(cache, offset + size) < 0) { + /* couldn't grow mapping. just make sure the written memory + area is invalidated then. */ + file_cache_invalidate(cache, offset, size); + return; + } + + memcpy(PTR_OFFSET(cache->mmap_base, offset), data, size); + + if (cache->read_highwater < offset + size) { + unsigned int page = cache->read_highwater / page_size; + + bits = buffer_get_space_unsafe(cache->page_bitmask, + page / CHAR_BIT, 1); + *bits &= ~(1 << (page % CHAR_BIT)); + cache->read_highwater = offset + size; + } + + /* mark fully written pages cached */ + if (size >= page_size) { + first_page = offset / page_size; + last_page = (offset + size) / page_size; + if ((offset % page_size) != 0) + first_page++; + + bits = buffer_get_space_unsafe(cache->page_bitmask, 0, + last_page / CHAR_BIT + 1); + for (; first_page < last_page; first_page++) { + bits[first_page / CHAR_BIT] |= + 1 << (first_page % CHAR_BIT); + } + } +} + +void file_cache_invalidate(struct file_cache *cache, uoff_t offset, uoff_t size) +{ + size_t page_size = mmap_get_page_size(); + unsigned char *bits, mask; + unsigned int i; + + if (offset >= cache->read_highwater || size == 0) + return; + + i_assert(page_size > 0); + + if (size > cache->read_highwater - offset) { + /* ignore anything after read highwater */ + size = cache->read_highwater - offset; + } + if (size >= cache->read_highwater) { + /* we're invalidating everything up to read highwater. + drop the highwater position. */ + cache->read_highwater = offset & ~(page_size-1); + } + + size = (offset + size + page_size-1) / page_size; + offset /= page_size; + i_assert(size > offset); + size -= offset; + + if (size != 1) { + /* tell operating system that we don't need the memory anymore + and it may free it. don't bother to do it for single pages, + there's a good chance that they get re-read back + immediately. */ + (void)madvise(PTR_OFFSET(cache->mmap_base, offset * page_size), + size * page_size, MADV_DONTNEED); + } + + bits = buffer_get_space_unsafe(cache->page_bitmask, offset / CHAR_BIT, + 1 + (size + CHAR_BIT - 1) / CHAR_BIT); + + /* set the first byte */ + for (i = offset % CHAR_BIT, mask = 0; i < CHAR_BIT && size > 0; i++) { + mask |= 1 << i; + size--; + } + *bits++ &= ~mask; + + /* set the middle bytes */ + memset(bits, 0, size / CHAR_BIT); + bits += size / CHAR_BIT; + size %= CHAR_BIT; + + /* set the last byte */ + if (size > 0) { + for (i = 0, mask = 0; i < size; i++) + mask |= 1 << i; + *bits &= ~mask; + } +} diff --git a/src/lib/file-cache.h b/src/lib/file-cache.h new file mode 100644 index 0000000..afffbd4 --- /dev/null +++ b/src/lib/file-cache.h @@ -0,0 +1,37 @@ +#ifndef FILE_CACHE_H +#define FILE_CACHE_H + +/* Create a new file cache. It works very much like file-backed mmap()ed + memory, but it works more nicely with remote filesystems (no SIGBUS). */ +struct file_cache *file_cache_new(int fd); +struct file_cache *file_cache_new_path(int fd, const char *path); +/* Destroy the cache and set cache pointer to NULL. */ +void file_cache_free(struct file_cache **cache); + +/* Change cached file descriptor. Invalidates the whole cache. */ +void file_cache_set_fd(struct file_cache *cache, int fd); + +/* Change the memory allocated for the cache. This can be used to immediately + set the maximum size so there's no need to grow the memory area with + possibly slow copying. */ +int file_cache_set_size(struct file_cache *cache, uoff_t size); + +/* Read data from file, returns how many bytes was actually read or -1 if + error occurred. */ +ssize_t file_cache_read(struct file_cache *cache, uoff_t offset, size_t size); + +/* Returns pointer to beginning of cached file. Only parts of the returned + memory that are valid are the ones that have been file_cache_read(). + Note that the pointer may become invalid after calling file_cache_read(). */ +const void *file_cache_get_map(struct file_cache *cache, size_t *size_r); + +/* Update cached memory area. Mark fully written pages as cached. */ +void file_cache_write(struct file_cache *cache, const void *data, size_t size, + uoff_t offset); + +/* Invalidate cached memory area. It will be read again next time it's tried + to be accessed. */ +void file_cache_invalidate(struct file_cache *cache, + uoff_t offset, uoff_t size); + +#endif diff --git a/src/lib/file-copy.c b/src/lib/file-copy.c new file mode 100644 index 0000000..c9b8e3e --- /dev/null +++ b/src/lib/file-copy.c @@ -0,0 +1,125 @@ +/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream.h" +#include "ostream.h" +#include "file-copy.h" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> + +static int file_copy_to_tmp(const char *srcpath, const char *tmppath, + bool try_hardlink) +{ + struct istream *input; + struct ostream *output; + struct stat st; + mode_t old_umask; + int fd_in, fd_out; + int ret = -1; + + if (try_hardlink) { + /* see if hardlinking works */ + if (link(srcpath, tmppath) == 0) + return 1; + if (errno == EEXIST) { + if (i_unlink_if_exists(tmppath) < 0) + return -1; + if (link(srcpath, tmppath) == 0) + return 1; + } + if (errno == ENOENT) + return 0; + if (!ECANTLINK(errno)) { + i_error("link(%s, %s) failed: %m", srcpath, tmppath); + return -1; + } + + /* fallback to manual copying */ + } + + fd_in = open(srcpath, O_RDONLY); + if (fd_in == -1) { + if (errno == ENOENT) + return 0; + i_error("open(%s) failed: %m", srcpath); + return -1; + } + + if (fstat(fd_in, &st) < 0) { + i_error("fstat(%s) failed: %m", srcpath); + i_close_fd(&fd_in); + return -1; + } + + old_umask = umask(0); + fd_out = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode); + umask(old_umask); + if (fd_out == -1) { + i_error("open(%s, O_CREAT) failed: %m", tmppath); + i_close_fd(&fd_in); + return -1; + } + + /* try to change the group, don't really care if it fails */ + if (fchown(fd_out, (uid_t)-1, st.st_gid) < 0 && errno != EPERM) + i_error("fchown(%s) failed: %m", tmppath); + + input = i_stream_create_fd(fd_in, IO_BLOCK_SIZE); + output = o_stream_create_fd_file(fd_out, 0, FALSE); + + switch (o_stream_send_istream(output, input)) { + case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: + ret = 0; + break; + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: + i_unreached(); + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: + i_error("read(%s) failed: %s", srcpath, + i_stream_get_error(input)); + break; + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: + i_error("write(%s) failed: %s", tmppath, + o_stream_get_error(output)); + break; + } + + i_stream_destroy(&input); + o_stream_destroy(&output); + + if (close(fd_in) < 0) { + i_error("close(%s) failed: %m", srcpath); + ret = -1; + } + if (close(fd_out) < 0) { + i_error("close(%s) failed: %m", tmppath); + ret = -1; + } + return ret < 0 ? -1 : 1; +} + +int file_copy(const char *srcpath, const char *destpath, bool try_hardlink) +{ + int ret; + + T_BEGIN { + const char *tmppath; + + tmppath = t_strconcat(destpath, ".tmp", NULL); + + ret = file_copy_to_tmp(srcpath, tmppath, try_hardlink); + if (ret > 0) { + if (rename(tmppath, destpath) < 0) { + i_error("rename(%s, %s) failed: %m", + tmppath, destpath); + ret = -1; + } + } + if (ret < 0) + i_unlink(tmppath); + } T_END; + return ret; +} diff --git a/src/lib/file-copy.h b/src/lib/file-copy.h new file mode 100644 index 0000000..f70b941 --- /dev/null +++ b/src/lib/file-copy.h @@ -0,0 +1,12 @@ +#ifndef FILE_COPY_H +#define FILE_COPY_H + +/* Copy file atomically. First try hardlinking, then fallback to creating + a temporary file (destpath.tmp) and rename()ing it over srcpath. + If the destination file already exists, it may or may not be overwritten, + so that shouldn't be relied on. + + Returns -1 = error, 0 = source file not found, 1 = ok */ +int file_copy(const char *srcpath, const char *destpath, bool try_hardlink); + +#endif diff --git a/src/lib/file-create-locked.c b/src/lib/file-create-locked.c new file mode 100644 index 0000000..47fe7f9 --- /dev/null +++ b/src/lib/file-create-locked.c @@ -0,0 +1,193 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "safe-mkstemp.h" +#include "mkdir-parents.h" +#include "file-lock.h" +#include "file-create-locked.h" + +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> + +/* Try mkdir() + lock creation multiple times. This allows the lock file + creation to work even while the directory is simultaneously being + rmdir()ed. */ +#define MAX_MKDIR_COUNT 10 +#define MAX_RETRY_COUNT 1000 + +static int +try_lock_existing(int fd, const char *path, + const struct file_create_settings *set, + struct file_lock **lock_r, const char **error_r) +{ + struct file_lock_settings lock_set = set->lock_settings; + struct stat st1, st2; + int ret; + + lock_set.unlink_on_free = FALSE; + lock_set.close_on_free = FALSE; + + if (fstat(fd, &st1) < 0) { + *error_r = t_strdup_printf("fstat(%s) failed: %m", path); + return -1; + } + if (file_wait_lock(fd, path, F_WRLCK, &lock_set, set->lock_timeout_secs, + lock_r, error_r) <= 0) + return -1; + if (stat(path, &st2) == 0) { + ret = st1.st_ino == st2.st_ino && + CMP_DEV_T(st1.st_dev, st2.st_dev) ? 1 : 0; + } else if (errno == ENOENT) { + ret = 0; + } else { + *error_r = t_strdup_printf("stat(%s) failed: %m", path); + ret = -1; + } + if (ret <= 0) { + /* the fd is closed next - no need to unlock */ + file_lock_free(lock_r); + } else { + file_lock_set_unlink_on_free( + *lock_r, set->lock_settings.unlink_on_free); + file_lock_set_close_on_free( + *lock_r, set->lock_settings.close_on_free); + } + return ret; +} + +static int +try_mkdir(const char *path, const struct file_create_settings *set, + const char **error_r) +{ + uid_t uid = set->mkdir_uid != 0 ? set->mkdir_uid : (uid_t)-1; + gid_t gid = set->mkdir_gid != 0 ? set->mkdir_gid : (gid_t)-1; + const char *p = strrchr(path, '/'); + if (p == NULL) + return 0; + + const char *dir = t_strdup_until(path, p); + int ret; + if (uid != (uid_t)-1) + ret = mkdir_parents_chown(dir, set->mkdir_mode, uid, gid); + else { + ret = mkdir_parents_chgrp(dir, set->mkdir_mode, + gid, set->gid_origin); + } + if (ret < 0 && errno != EEXIST) { + *error_r = t_strdup_printf("mkdir_parents(%s) failed: %m", dir); + return -1; + } + return 1; +} + +static int +try_create_new(const char *path, const struct file_create_settings *set, + int *fd_r, struct file_lock **lock_r, const char **error_r) +{ + string_t *temp_path = t_str_new(128); + int fd, orig_errno, ret = 1; + int mode = set->mode != 0 ? set->mode : 0600; + uid_t uid = set->uid != 0 ? set->uid : (uid_t)-1; + uid_t gid = set->gid != 0 ? set->gid : (gid_t)-1; + + str_append(temp_path, path); + for (unsigned int i = 0; ret > 0; i++) { + if (uid != (uid_t)-1) + fd = safe_mkstemp(temp_path, mode, uid, gid); + else + fd = safe_mkstemp_group(temp_path, mode, gid, set->gid_origin); + if (fd != -1 || errno != ENOENT || set->mkdir_mode == 0 || + i >= MAX_MKDIR_COUNT) + break; + + int orig_errno = errno; + if ((ret = try_mkdir(path, set, error_r)) < 0) + return -1; + errno = orig_errno; + } + if (fd == -1) { + *error_r = t_strdup_printf("safe_mkstemp(%s) failed: %m", path); + return -1; + } + + struct file_lock_settings lock_set = set->lock_settings; + lock_set.unlink_on_free = FALSE; + lock_set.close_on_free = FALSE; + + ret = -1; + if (file_try_lock(fd, str_c(temp_path), F_WRLCK, &lock_set, + lock_r, error_r) <= 0) { + } else if (link(str_c(temp_path), path) < 0) { + if (errno == EEXIST) { + /* just created by somebody else */ + ret = 0; + } else if (errno == ENOENT) { + /* nobody should be deleting the temp file unless the + entire directory is deleted. */ + *error_r = t_strdup_printf( + "Temporary file %s was unexpectedly deleted", + str_c(temp_path)); + } else { + *error_r = t_strdup_printf("link(%s, %s) failed: %m", + str_c(temp_path), path); + } + file_lock_free(lock_r); + } else { + file_lock_set_path(*lock_r, path); + file_lock_set_unlink_on_free( + *lock_r, set->lock_settings.unlink_on_free); + file_lock_set_close_on_free( + *lock_r, set->lock_settings.close_on_free); + i_unlink_if_exists(str_c(temp_path)); + *fd_r = fd; + return 1; + } + orig_errno = errno; + i_close_fd(&fd); + i_unlink_if_exists(str_c(temp_path)); + errno = orig_errno; + return ret; +} + +int file_create_locked(const char *path, const struct file_create_settings *set, + struct file_lock **lock_r, bool *created_r, + const char **error_r) +{ + unsigned int i; + int fd, ret; + + for (i = 0; i < MAX_RETRY_COUNT; i++) { + fd = open(path, O_RDWR); + if (fd != -1) { + ret = try_lock_existing(fd, path, set, lock_r, error_r); + if (ret > 0) { + /* successfully locked an existing file */ + *created_r = FALSE; + return fd; + } + i_close_fd(&fd); + if (ret < 0) + return -1; + } else if (errno != ENOENT) { + *error_r = t_strdup_printf("open(%s) failed: %m", path); + return -1; + } else { + /* try to create the file */ + ret = try_create_new(path, set, &fd, lock_r, error_r); + if (ret < 0) + return -1; + if (ret > 0) { + /* successfully created a new locked file */ + *created_r = TRUE; + return fd; + } + /* the file was just created - try again opening and + locking it */ + } + } + *error_r = t_strdup_printf("Creating a locked file %s keeps failing", path); + errno = EINVAL; + return -1; +} diff --git a/src/lib/file-create-locked.h b/src/lib/file-create-locked.h new file mode 100644 index 0000000..b88e3c8 --- /dev/null +++ b/src/lib/file-create-locked.h @@ -0,0 +1,47 @@ +#ifndef FILE_CREATE_LOCKED_H +#define FILE_CREATE_LOCKED_H + +#include "file-lock.h" + +struct file_create_settings { + /* 0 = try locking without waiting */ + unsigned int lock_timeout_secs; + + struct file_lock_settings lock_settings; + + /* 0 = 0600 */ + int mode; + /* 0 = default */ + uid_t uid; + /* 0 = default */ + gid_t gid; + const char *gid_origin; + + /* If parent directory doesn't exist, mkdir() it with this mode. + 0 = don't mkdir(). The parent directories are assumed to be + potentially rmdir() simultaneously, so the mkdir()+locking may be + attempted multiple times. */ + int mkdir_mode; + /* 0 = default */ + uid_t mkdir_uid; + /* 0 = default */ + gid_t mkdir_gid; + const char *mkdir_gid_origin; +}; + +/* Either open an existing file and lock it, or create the file locked. + The creation is done by creating a temp file and link()ing it to path. + If link() fails, opening is retried again. Returns fd on success, + -1 on error. errno is preserved for the last failed syscall, so most + importantly ENOENT could mean that the directory doesn't exist and EAGAIN + means locking timed out. + + If this function is used to create lock files, file_lock_set_unlink_on_free() + should be used for the resulting lock. It attempts to avoid unlinking the + file if there are already other processes using the lock. That can help to + avoid "Creating a locked file ... keeps failing" errors */ +int file_create_locked(const char *path, const struct file_create_settings *set, + struct file_lock **lock_r, bool *created_r, + const char **error_r); + +#endif diff --git a/src/lib/file-dotlock.c b/src/lib/file-dotlock.c new file mode 100644 index 0000000..ad14ef0 --- /dev/null +++ b/src/lib/file-dotlock.c @@ -0,0 +1,925 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "str.h" +#include "hex-binary.h" +#include "hostpid.h" +#include "file-lock.h" +#include "eacces-error.h" +#include "write-full.h" +#include "safe-mkstemp.h" +#include "nfs-workarounds.h" +#include "file-dotlock.h" +#include "sleep.h" + +#include <stdio.h> +#include <signal.h> +#include <time.h> +#include <utime.h> +#include <sys/stat.h> + +#define DEFAULT_LOCK_SUFFIX ".lock" + +/* 0.1 .. 0.2msec */ +#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)i_rand() % 100000) +/* Maximum 3 second wait between dotlock checks */ +#define LOCK_MAX_WAIT_USECS (1000000 * 3) + +/* If the dotlock is newer than this, don't verify that the PID it contains + is valid (since it most likely is). */ +#define STALE_PID_CHECK_SECS 2 + +/* Maximum difference between current time and create file's ctime before + logging a warning. Should be less than a second in normal operation. */ +#define MAX_TIME_DIFF 30 +/* NFS may return a cached mtime in stat(). A later non-cached stat() may + return a slightly different mtime. Allow the difference to be this much + and still consider it to be the same mtime. */ +#define FILE_DOTLOCK_MAX_STAT_MTIME_DIFF 1 + +struct dotlock { + struct dotlock_settings settings; + + dev_t dev; + ino_t ino; + time_t mtime; + + char *path; + char *lock_path; + int fd; + + time_t lock_time; +}; + +struct file_change_info { + dev_t dev; + ino_t ino; + off_t size; + time_t ctime, mtime; +}; + +struct lock_info { + const struct dotlock_settings *set; + const char *path, *lock_path, *temp_path; + int fd; + + struct file_change_info lock_info; + struct file_change_info file_info; + + time_t last_pid_check; + time_t last_change; + unsigned int wait_usecs; + + bool have_pid:1; + bool pid_read:1; + bool use_io_notify:1; + bool lock_stated:1; +}; + +static struct dotlock * +file_dotlock_alloc(const struct dotlock_settings *settings, const char *path) +{ + struct dotlock *dotlock; + + dotlock = i_new(struct dotlock, 1); + dotlock->settings = *settings; + if (dotlock->settings.lock_suffix == NULL) + dotlock->settings.lock_suffix = DEFAULT_LOCK_SUFFIX; + dotlock->path = i_strdup(path); + dotlock->fd = -1; + + return dotlock; +} + +static pid_t read_local_pid(const char *lock_path) +{ + char buf[512], *host; + int fd; + ssize_t ret; + pid_t pid; + + fd = open(lock_path, O_RDONLY); + if (fd == -1) + return -1; /* ignore the actual error */ + + /* read line */ + ret = read(fd, buf, sizeof(buf)-1); + i_close_fd(&fd); + if (ret <= 0) + return -1; + + /* fix the string */ + if (buf[ret-1] == '\n') + ret--; + buf[ret] = '\0'; + + /* it should contain pid:host */ + host = strchr(buf, ':'); + if (host == NULL) + return -1; + *host++ = '\0'; + + /* host must be ours */ + if (strcmp(host, my_hostname) != 0) + return -1; + + if (str_to_pid(buf, &pid) < 0) + return -1; + if (pid <= 0) + return -1; + return pid; +} + +static bool +update_change_info(const struct stat *st, struct file_change_info *change, + time_t *last_change_r, time_t now, bool check_ctime) +{ + /* ctime is checked only if we're not doing NFS attribute cache + flushes. it changes them. */ + if (change->ino != st->st_ino || !CMP_DEV_T(change->dev, st->st_dev) || + (change->ctime != st->st_ctime && check_ctime) || + change->mtime != st->st_mtime || change->size != st->st_size) { + time_t change_time = now; + + if (change->ctime == 0) { + /* First check, set last_change to file's change time. + Use mtime instead if it's higher, but only if it's + not higher than current time, because the mtime + can also be used for keeping metadata. */ + change_time = st->st_mtime <= now && + (st->st_mtime > st->st_ctime || !check_ctime) ? + st->st_mtime : st->st_ctime; + } + if (*last_change_r < change_time) + *last_change_r = change_time; + change->ino = st->st_ino; + change->dev = st->st_dev; + change->ctime = st->st_ctime; + change->mtime = st->st_mtime; + change->size = st->st_size; + return TRUE; + } + return FALSE; +} + +static int update_lock_info(time_t now, struct lock_info *lock_info, + bool *changed_r) +{ + struct stat st; + + /* don't waste time flushing attribute cache the first time we're here. + if it's stale we'll get back here soon. */ + if (lock_info->set->nfs_flush && lock_info->lock_stated) { + nfs_flush_file_handle_cache(lock_info->lock_path); + nfs_flush_attr_cache_unlocked(lock_info->lock_path); + } + + lock_info->lock_stated = TRUE; + if (nfs_safe_lstat(lock_info->lock_path, &st) < 0) { + if (errno != ENOENT) { + i_error("lstat(%s) failed: %m", lock_info->lock_path); + return -1; + } + return 1; + } + + *changed_r = update_change_info(&st, &lock_info->lock_info, + &lock_info->last_change, now, + !lock_info->set->nfs_flush); + return 0; +} + +static int dotlock_override(struct lock_info *lock_info) +{ + if (i_unlink_if_exists(lock_info->lock_path) < 0) + return -1; + + /* make sure we sleep for a while after overriding the lock file. + otherwise another process might try to override it at the same time + and unlink our newly created dotlock. */ + if (lock_info->use_io_notify) + i_sleep_usecs(LOCK_RANDOM_USLEEP_TIME); + return 0; +} + +static int check_lock(time_t now, struct lock_info *lock_info) +{ + time_t stale_timeout = lock_info->set->stale_timeout; + pid_t pid = -1; + bool changed; + int ret; + + if ((ret = update_lock_info(now, lock_info, &changed)) != 0) + return ret; + if (changed || !lock_info->pid_read) { + /* either our first check or someone else got the lock file. + if the dotlock was created only a couple of seconds ago, + don't bother to read its PID. */ + if (lock_info->lock_info.mtime >= now - STALE_PID_CHECK_SECS) + lock_info->pid_read = FALSE; + else { + pid = read_local_pid(lock_info->lock_path); + lock_info->pid_read = TRUE; + } + lock_info->have_pid = pid != -1; + } else if (!lock_info->have_pid) { + /* no pid checking */ + } else { + if (lock_info->last_pid_check == now) { + /* we just checked the pid */ + return 0; + } + + /* re-read the pid. even if all times and inodes are the same, + the PID in the file might have changed if lock files were + rapidly being recreated. */ + pid = read_local_pid(lock_info->lock_path); + lock_info->have_pid = pid != -1; + } + + if (lock_info->have_pid) { + /* we've local PID. Check if it exists. */ + if (kill(pid, 0) == 0 || errno != ESRCH) { + if (pid != getpid()) { + /* process exists, don't override */ + return 0; + } + /* it's us. either we're locking it again, or it's a + stale lock file with same pid than us. either way, + recreate it.. */ + } + + /* doesn't exist - now check again if the dotlock was just + deleted or replaced */ + if ((ret = update_lock_info(now, lock_info, &changed)) != 0) + return ret; + + if (!changed) { + /* still there, go ahead and override it */ + return dotlock_override(lock_info); + } + return 1; + } + + if (stale_timeout == 0) { + /* no change checking */ + return 0; + } + + if (now > lock_info->last_change + stale_timeout) { + struct stat st; + + /* possibly stale lock file. check also the timestamp of the + file we're protecting. */ + if (lock_info->set->nfs_flush) { + nfs_flush_file_handle_cache(lock_info->path); + nfs_flush_attr_cache_maybe_locked(lock_info->path); + } + if (nfs_safe_stat(lock_info->path, &st) < 0) { + if (errno == ENOENT) { + /* file doesn't exist. treat it as if + it hasn't changed */ + } else { + i_error("stat(%s) failed: %m", lock_info->path); + return -1; + } + } else { + (void)update_change_info(&st, &lock_info->file_info, + &lock_info->last_change, now, + !lock_info->set->nfs_flush); + } + } + + if (now > lock_info->last_change + stale_timeout) { + /* no changes for a while, assume stale lock */ + return dotlock_override(lock_info); + } + + return 0; +} + +static int file_write_pid(int fd, const char *path, bool nfs_flush) +{ + const char *str; + + /* write our pid and host, if possible */ + str = t_strdup_printf("%s:%s", my_pid, my_hostname); + if (write_full(fd, str, strlen(str)) < 0 || + (nfs_flush && fdatasync(fd) < 0)) { + /* failed, leave it empty then */ + if (ftruncate(fd, 0) < 0) { + i_error("ftruncate(%s) failed: %m", path); + return -1; + } + } + return 0; +} + +static int try_create_lock_hardlink(struct lock_info *lock_info, bool write_pid, + string_t *tmp_path, time_t now) +{ + const char *temp_prefix = lock_info->set->temp_prefix; + const char *p; + mode_t old_mask; + struct stat st; + + if (lock_info->temp_path == NULL) { + /* we'll need our temp file first. */ + i_assert(lock_info->fd == -1); + + p = strrchr(lock_info->lock_path, '/'); + + str_truncate(tmp_path, 0); + if (temp_prefix != NULL) { + if (*temp_prefix != '/' && p != NULL) { + /* add directory */ + str_append_data(tmp_path, lock_info->lock_path, + p - lock_info->lock_path); + str_append_c(tmp_path, '/'); + } + str_append(tmp_path, temp_prefix); + } else { + if (p != NULL) { + /* add directory */ + str_append_data(tmp_path, lock_info->lock_path, + p - lock_info->lock_path); + str_append_c(tmp_path, '/'); + } + str_printfa(tmp_path, ".temp.%s.%s.", + my_hostname, my_pid); + } + + old_mask = umask(0666); + lock_info->fd = safe_mkstemp(tmp_path, 0666 ^ old_mask, + (uid_t)-1, (gid_t)-1); + umask(old_mask); + if (lock_info->fd == -1) + return -1; + + if (write_pid) { + if (file_write_pid(lock_info->fd, + str_c(tmp_path), + lock_info->set->nfs_flush) < 0) { + i_close_fd(&lock_info->fd); + return -1; + } + } + + lock_info->temp_path = str_c(tmp_path); + } else if (fstat(lock_info->fd, &st) < 0) { + i_error("fstat(%s) failed: %m", lock_info->temp_path); + return -1; + } else if (st.st_ctime < now) { + /* we've been waiting for a while. + refresh the file's timestamp. */ + if (utime(lock_info->temp_path, NULL) < 0) + i_error("utime(%s) failed: %m", lock_info->temp_path); + } + + if (nfs_safe_link(lock_info->temp_path, + lock_info->lock_path, TRUE) < 0) { + if (errno == EEXIST) + return 0; + + if (errno != EACCES) { + i_error("link(%s, %s) failed: %m", + lock_info->temp_path, lock_info->lock_path); + } + return -1; + } + + if (i_unlink(lock_info->temp_path) < 0) { + /* non-fatal, continue */ + } + lock_info->temp_path = NULL; + return 1; +} + +static int try_create_lock_excl(struct lock_info *lock_info, bool write_pid) +{ + int fd; + + fd = open(lock_info->lock_path, O_RDWR | O_EXCL | O_CREAT, 0666); + if (fd == -1) { + if (errno == EEXIST) + return 0; + + if (errno != ENOENT && errno != EACCES) + i_error("open(%s) failed: %m", lock_info->lock_path); + return -1; + } + + if (write_pid) { + if (file_write_pid(fd, lock_info->lock_path, + lock_info->set->nfs_flush) < 0) { + i_close_fd(&fd); + return -1; + } + } + + lock_info->fd = fd; + return 1; +} + +static void dotlock_wait_end(struct ioloop *ioloop) +{ + io_loop_stop(ioloop); +} + +static void dotlock_wait(struct lock_info *lock_info) +{ + struct ioloop *ioloop; + struct io *io; + struct timeout *to; + + if (!lock_info->use_io_notify) { + i_sleep_usecs(lock_info->wait_usecs); + return; + } + + ioloop = io_loop_create(); + switch (io_add_notify(lock_info->lock_path, dotlock_wait_end, + ioloop, &io)) { + case IO_NOTIFY_ADDED: + break; + case IO_NOTIFY_NOTFOUND: + /* the lock file doesn't exist anymore, don't sleep */ + io_loop_destroy(&ioloop); + return; + case IO_NOTIFY_NOSUPPORT: + /* listening for files not supported */ + io_loop_destroy(&ioloop); + lock_info->use_io_notify = FALSE; + i_sleep_usecs(LOCK_RANDOM_USLEEP_TIME); + return; + } + /* timeout after a random time even when using notify, since it + doesn't work reliably with e.g. NFS. */ + to = timeout_add(lock_info->wait_usecs/1000, + dotlock_wait_end, ioloop); + io_loop_run(ioloop); + io_remove(&io); + timeout_remove(&to); + io_loop_destroy(&ioloop); +} + +static int +dotlock_create(struct dotlock *dotlock, enum dotlock_create_flags flags, + bool write_pid, const char **lock_path_r) +{ + const struct dotlock_settings *set = &dotlock->settings; + const char *lock_path; + struct lock_info lock_info; + struct stat st; + unsigned int stale_notify_threshold; + unsigned int change_secs, wait_left; + time_t now, max_wait_time, last_notify; + time_t prev_last_change = 0, prev_wait_update = 0; + string_t *tmp_path; + int ret; + bool do_wait; + + now = time(NULL); + + lock_path = *lock_path_r = + t_strconcat(dotlock->path, set->lock_suffix, NULL); + stale_notify_threshold = set->stale_timeout / 2; + max_wait_time = (flags & DOTLOCK_CREATE_FLAG_NONBLOCK) != 0 ? 0 : + now + set->timeout; + tmp_path = t_str_new(256); + + i_zero(&lock_info); + lock_info.path = dotlock->path; + lock_info.set = set; + lock_info.lock_path = lock_path; + lock_info.fd = -1; + lock_info.use_io_notify = set->use_io_notify; + + last_notify = 0; do_wait = FALSE; + + file_lock_wait_start(); + do { + if (do_wait) { + if (prev_last_change != lock_info.last_change) { + /* dotlock changed since last check, + reset the wait time */ + lock_info.wait_usecs = LOCK_RANDOM_USLEEP_TIME; + prev_last_change = lock_info.last_change; + prev_wait_update = now; + } else if (prev_wait_update != now && + lock_info.wait_usecs < LOCK_MAX_WAIT_USECS) { + /* we've been waiting for a while now, increase + the wait time to avoid wasting CPU */ + prev_wait_update = now; + lock_info.wait_usecs += lock_info.wait_usecs/2; + } + dotlock_wait(&lock_info); + now = time(NULL); + } + + ret = check_lock(now, &lock_info); + if (ret < 0) + break; + + if (ret == 1) { + if ((flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0) + break; + + ret = set->use_excl_lock ? + try_create_lock_excl(&lock_info, write_pid) : + try_create_lock_hardlink(&lock_info, write_pid, + tmp_path, now); + if (ret != 0) { + /* if we succeeded, get the current time once + more in case disk I/O usage was really high + and it took a long time to create the lock */ + now = time(NULL); + break; + } + } + + if (last_notify != now && set->callback != NULL && + now < max_wait_time) { + last_notify = now; + change_secs = now - lock_info.last_change; + wait_left = max_wait_time - now; + + if (change_secs >= stale_notify_threshold && + change_secs <= wait_left) { + unsigned int secs_left = + set->stale_timeout < change_secs ? + 0 : set->stale_timeout - change_secs; + if (!set->callback(secs_left, TRUE, + set->context)) { + /* we don't want to override */ + lock_info.last_change = now; + } + } else if (wait_left > 0) { + (void)set->callback(wait_left, FALSE, + set->context); + } + } + + do_wait = TRUE; + now = time(NULL); + } while (now < max_wait_time); + file_lock_wait_end(dotlock->path); + + if (ret > 0) { + i_assert(lock_info.fd != -1); + if (fstat(lock_info.fd, &st) < 0) { + i_error("fstat(%s) failed: %m", lock_path); + ret = -1; + } else { + /* successful dotlock creation */ + dotlock->dev = st.st_dev; + dotlock->ino = st.st_ino; + + dotlock->fd = lock_info.fd; + dotlock->lock_time = now; + lock_info.fd = -1; + + if (st.st_ctime + MAX_TIME_DIFF < now || + st.st_ctime - MAX_TIME_DIFF > now) { + i_warning("Created dotlock file's timestamp is " + "different than current time " + "(%s vs %s): %s", dec2str(st.st_ctime), + dec2str(now), dotlock->path); + } + } + } + + if (lock_info.fd != -1) { + int old_errno = errno; + + if (close(lock_info.fd) < 0) + i_error("close(%s) failed: %m", lock_path); + errno = old_errno; + } + if (lock_info.temp_path != NULL) + i_unlink(lock_info.temp_path); + + if (ret == 0) + errno = EAGAIN; + return ret; +} + +static void file_dotlock_free(struct dotlock **_dotlock) +{ + struct dotlock *dotlock = *_dotlock; + int old_errno; + + *_dotlock = NULL; + + if (dotlock->fd != -1) { + old_errno = errno; + if (close(dotlock->fd) < 0) + i_error("close(%s) failed: %m", dotlock->path); + dotlock->fd = -1; + errno = old_errno; + } + + i_free(dotlock->path); + i_free(dotlock->lock_path); + i_free(dotlock); +} + +static int file_dotlock_create_real(struct dotlock *dotlock, + enum dotlock_create_flags flags) +{ + const char *lock_path; + struct stat st; + int fd, ret; + + ret = dotlock_create(dotlock, flags, TRUE, &lock_path); + if (ret <= 0 || (flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0) + return ret; + + fd = dotlock->fd; + dotlock->fd = -1; + + if (close(fd) < 0) { + i_error("close(%s) failed: %m", lock_path); + return -1; + } + + /* With NFS the writes may have been flushed only when closing the + file. Get the mtime again after that to avoid "dotlock was modified" + errors. */ + if (lstat(lock_path, &st) < 0) { + if (errno != ENOENT) + i_error("stat(%s) failed: %m", lock_path); + else { + i_error("dotlock %s was immediately deleted under us", + lock_path); + } + return -1; + } + /* extra sanity check won't hurt.. */ + if (st.st_dev != dotlock->dev || st.st_ino != dotlock->ino) { + errno = ENOENT; + i_error("dotlock %s was immediately recreated under us", + lock_path); + return -1; + } + dotlock->mtime = st.st_mtime; + return 1; +} + +int file_dotlock_create(const struct dotlock_settings *set, const char *path, + enum dotlock_create_flags flags, + struct dotlock **dotlock_r) +{ + struct dotlock *dotlock; + int ret; + + dotlock = file_dotlock_alloc(set, path); + T_BEGIN { + ret = file_dotlock_create_real(dotlock, flags); + } T_END; + if (ret <= 0 || (flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0) + file_dotlock_free(&dotlock); + + *dotlock_r = dotlock; + return ret; +} + +static void dotlock_replaced_warning(struct dotlock *dotlock, bool deleted) +{ + const char *lock_path; + time_t now = time(NULL); + + lock_path = file_dotlock_get_lock_path(dotlock); + if (dotlock->mtime == dotlock->lock_time) { + i_warning("Our dotlock file %s was %s " + "(locked %d secs ago, touched %d secs ago)", + lock_path, deleted ? "deleted" : "overridden", + (int)(now - dotlock->lock_time), + (int)(now - dotlock->mtime)); + } else { + i_warning("Our dotlock file %s was %s " + "(kept it %d secs)", lock_path, + deleted ? "deleted" : "overridden", + (int)(now - dotlock->lock_time)); + } +} + +static bool file_dotlock_has_mtime_changed(time_t t1, time_t t2) +{ + time_t diff; + + if (t1 == t2) + return FALSE; + + /* with NFS t1 may have been looked up from local cache. + allow it to be a little bit different. */ + diff = t1 > t2 ? t1-t2 : t2-t1; + return diff > FILE_DOTLOCK_MAX_STAT_MTIME_DIFF; +} + +int file_dotlock_delete(struct dotlock **dotlock_p) +{ + struct dotlock *dotlock; + const char *lock_path; + struct stat st; + int ret; + + dotlock = *dotlock_p; + *dotlock_p = NULL; + + lock_path = file_dotlock_get_lock_path(dotlock); + if (nfs_safe_lstat(lock_path, &st) < 0) { + if (errno == ENOENT) { + dotlock_replaced_warning(dotlock, TRUE); + file_dotlock_free(&dotlock); + return 0; + } + + i_error("lstat(%s) failed: %m", lock_path); + file_dotlock_free(&dotlock); + return -1; + } + + if (dotlock->ino != st.st_ino || + !CMP_DEV_T(dotlock->dev, st.st_dev)) { + dotlock_replaced_warning(dotlock, FALSE); + errno = EEXIST; + file_dotlock_free(&dotlock); + return 0; + } + + if (file_dotlock_has_mtime_changed(dotlock->mtime, st.st_mtime) && + dotlock->fd == -1) { + i_warning("Our dotlock file %s was modified (%s vs %s), " + "assuming it wasn't overridden (kept it %d secs)", + lock_path, + dec2str(dotlock->mtime), dec2str(st.st_mtime), + (int)(time(NULL) - dotlock->lock_time)); + } + + if ((ret = i_unlink_if_exists(lock_path)) == 0) + dotlock_replaced_warning(dotlock, TRUE); + file_dotlock_free(&dotlock); + return ret; +} + +int file_dotlock_open(const struct dotlock_settings *set, const char *path, + enum dotlock_create_flags flags, + struct dotlock **dotlock_r) +{ + struct dotlock *dotlock; + int ret; + + dotlock = file_dotlock_alloc(set, path); + T_BEGIN { + const char *lock_path; + + ret = dotlock_create(dotlock, flags, FALSE, &lock_path); + } T_END; + + if (ret <= 0) { + file_dotlock_free(&dotlock); + *dotlock_r = NULL; + return -1; + } + + *dotlock_r = dotlock; + return dotlock->fd; +} + +static int ATTR_NULL(7) +file_dotlock_open_mode_full(const struct dotlock_settings *set, const char *path, + enum dotlock_create_flags flags, + mode_t mode, uid_t uid, gid_t gid, + const char *gid_origin, struct dotlock **dotlock_r) +{ + struct dotlock *dotlock; + mode_t old_mask; + int fd; + + old_mask = umask(0666 ^ mode); + fd = file_dotlock_open(set, path, flags, &dotlock); + umask(old_mask); + + if (fd != -1 && (uid != (uid_t)-1 || gid != (gid_t)-1)) { + if (fchown(fd, uid, gid) < 0) { + if (errno == EPERM && uid == (uid_t)-1) { + i_error("%s", eperm_error_get_chgrp("fchown", + file_dotlock_get_lock_path(dotlock), + gid, gid_origin)); + } else { + i_error("fchown(%s, %ld, %ld) failed: %m", + file_dotlock_get_lock_path(dotlock), + (long)uid, (long)gid); + } + file_dotlock_delete(&dotlock); + return -1; + } + } + *dotlock_r = dotlock; + return fd; +} + +int file_dotlock_open_mode(const struct dotlock_settings *set, const char *path, + enum dotlock_create_flags flags, + mode_t mode, uid_t uid, gid_t gid, + struct dotlock **dotlock_r) +{ + return file_dotlock_open_mode_full(set, path, flags, mode, uid, gid, + NULL, dotlock_r); +} + +int file_dotlock_open_group(const struct dotlock_settings *set, const char *path, + enum dotlock_create_flags flags, + mode_t mode, gid_t gid, const char *gid_origin, + struct dotlock **dotlock_r) +{ + return file_dotlock_open_mode_full(set, path, flags, mode, (uid_t)-1, + gid, gid_origin, dotlock_r); +} + +int file_dotlock_replace(struct dotlock **dotlock_p, + enum dotlock_replace_flags flags) +{ + struct dotlock *dotlock; + const char *lock_path; + bool is_locked; + + dotlock = *dotlock_p; + *dotlock_p = NULL; + + is_locked = (flags & DOTLOCK_REPLACE_FLAG_VERIFY_OWNER) == 0 ? TRUE : + file_dotlock_is_locked(dotlock); + + if ((flags & DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) != 0) + dotlock->fd = -1; + + if (!is_locked) { + dotlock_replaced_warning(dotlock, FALSE); + errno = EEXIST; + file_dotlock_free(&dotlock); + return 0; + } + + lock_path = file_dotlock_get_lock_path(dotlock); + if (rename(lock_path, dotlock->path) < 0) { + i_error("rename(%s, %s) failed: %m", lock_path, dotlock->path); + if (errno == ENOENT) + dotlock_replaced_warning(dotlock, TRUE); + file_dotlock_free(&dotlock); + return -1; + } + file_dotlock_free(&dotlock); + return 1; +} + +int file_dotlock_touch(struct dotlock *dotlock) +{ + time_t now = time(NULL); + struct utimbuf buf; + int ret = 0; + + if (dotlock->mtime == now) + return 0; + + dotlock->mtime = now; + buf.actime = buf.modtime = now; + + T_BEGIN { + const char *lock_path = file_dotlock_get_lock_path(dotlock); + if (utime(lock_path, &buf) < 0) { + i_error("utime(%s) failed: %m", lock_path); + ret = -1; + } + } T_END; + return ret; +} + +bool file_dotlock_is_locked(struct dotlock *dotlock) +{ + struct stat st, st2; + const char *lock_path; + + lock_path = file_dotlock_get_lock_path(dotlock); + if (fstat(dotlock->fd, &st) < 0) { + i_error("fstat(%s) failed: %m", lock_path); + return FALSE; + } + + if (nfs_safe_lstat(lock_path, &st2) < 0) { + i_error("lstat(%s) failed: %m", lock_path); + return FALSE; + } + return st.st_ino == st2.st_ino && CMP_DEV_T(st.st_dev, st2.st_dev); +} + +const char *file_dotlock_get_lock_path(struct dotlock *dotlock) +{ + if (dotlock->lock_path == NULL) { + dotlock->lock_path = + i_strconcat(dotlock->path, + dotlock->settings.lock_suffix, NULL); + } + return dotlock->lock_path; +} diff --git a/src/lib/file-dotlock.h b/src/lib/file-dotlock.h new file mode 100644 index 0000000..0b958c8 --- /dev/null +++ b/src/lib/file-dotlock.h @@ -0,0 +1,94 @@ +#ifndef FILE_DOTLOCK_H +#define FILE_DOTLOCK_H + +#include <unistd.h> +#include <fcntl.h> + +struct dotlock; + +struct dotlock_settings { + /* Dotlock files are created by first creating a temp file and then + link()ing it to the dotlock. temp_prefix specifies the prefix to + use for temp files. It may contain a full path. Default is + ".temp.hostname.pid.". */ + const char *temp_prefix; + /* Use this suffix for dotlock filenames. Default is ".lock". */ + const char *lock_suffix; + + /* Abort after this many seconds. */ + unsigned int timeout; + /* Override the lock file when it and the file we're protecting is + older than stale_timeout. */ + unsigned int stale_timeout; + + /* Callback is called once in a while. stale is set to TRUE if stale + lock is detected and will be overridden in secs_left. If callback + returns FALSE then, the lock will not be overridden. */ + bool (*callback)(unsigned int secs_left, bool stale, void *context); + void *context; + + /* Rely on O_EXCL locking to work instead of using hardlinks. + It's faster, but doesn't work with all NFS implementations. */ + bool use_excl_lock:1; + /* Flush NFS attribute cache before stating files. */ + bool nfs_flush:1; + /* Use io_add_notify() to speed up finding out when an existing + dotlock is deleted */ + bool use_io_notify:1; +}; + +enum dotlock_create_flags { + /* If lock already exists, fail immediately */ + DOTLOCK_CREATE_FLAG_NONBLOCK = 0x01, + /* Don't actually create the lock file, only make sure it doesn't + exist. This is racy, so you shouldn't rely on it much. */ + DOTLOCK_CREATE_FLAG_CHECKONLY = 0x02 +}; + +enum dotlock_replace_flags { + /* Check that lock file hasn't been overridden before renaming. */ + DOTLOCK_REPLACE_FLAG_VERIFY_OWNER = 0x01, + /* Don't close the file descriptor. */ + DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD = 0x02 +}; + +/* Create dotlock. Returns 1 if successful, 0 if timeout or -1 if error. + When returning 0, errno is also set to EAGAIN. */ +int file_dotlock_create(const struct dotlock_settings *set, const char *path, + enum dotlock_create_flags flags, + struct dotlock **dotlock_r); + +/* Delete the dotlock file. Returns 1 if successful, 0 if the file had already + been deleted or reused by someone else, -1 if I/O error. */ +int ATTR_NOWARN_UNUSED_RESULT +file_dotlock_delete(struct dotlock **dotlock); + +/* Use dotlock as the new content for file. This provides read safety without + locks, but it's not very good for large files. Returns fd for lock file. + If locking timed out, returns -1 and errno = EAGAIN. */ +int file_dotlock_open(const struct dotlock_settings *set, const char *path, + enum dotlock_create_flags flags, + struct dotlock **dotlock_r); +/* Like file_dotlock_open(), but use the given file permissions. */ +int file_dotlock_open_mode(const struct dotlock_settings *set, const char *path, + enum dotlock_create_flags flags, + mode_t mode, uid_t uid, gid_t gid, + struct dotlock **dotlock_r); +int file_dotlock_open_group(const struct dotlock_settings *set, const char *path, + enum dotlock_create_flags flags, + mode_t mode, gid_t gid, const char *gid_origin, + struct dotlock **dotlock_r); +/* Replaces the file dotlock protects with the dotlock file itself. */ +int file_dotlock_replace(struct dotlock **dotlock, + enum dotlock_replace_flags flags); +/* Update dotlock's mtime. If you're keeping the dotlock for a long time, + it's a good idea to update it once in a while so others won't override it. + If the timestamp is less than a second old, it's not updated. */ +int file_dotlock_touch(struct dotlock *dotlock); +/* Returns TRUE if the lock is still ok, FALSE if it's been overridden. */ +bool file_dotlock_is_locked(struct dotlock *dotlock); + +/* Returns the lock file path. */ +const char *file_dotlock_get_lock_path(struct dotlock *dotlock); + +#endif diff --git a/src/lib/file-lock.c b/src/lib/file-lock.c new file mode 100644 index 0000000..f17beea --- /dev/null +++ b/src/lib/file-lock.c @@ -0,0 +1,530 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream.h" +#include "file-lock.h" +#include "file-dotlock.h" +#include "time-util.h" + +#include <time.h> +#include <sys/stat.h> +#ifdef HAVE_FLOCK +# include <sys/file.h> +#endif + +struct file_lock { + struct file_lock_settings set; + + int fd; + char *path; + struct dotlock *dotlock; + + struct timeval locked_time; + int lock_type; +}; + +static struct timeval lock_wait_start; +static uint64_t file_lock_wait_usecs = 0; +static long long file_lock_slow_warning_usecs = -1; + +static void file_lock_log_warning_if_slow(struct file_lock *lock); + +bool file_lock_method_parse(const char *name, enum file_lock_method *method_r) +{ + if (strcasecmp(name, "fcntl") == 0) + *method_r = FILE_LOCK_METHOD_FCNTL; + else if (strcasecmp(name, "flock") == 0) + *method_r = FILE_LOCK_METHOD_FLOCK; + else if (strcasecmp(name, "dotlock") == 0) + *method_r = FILE_LOCK_METHOD_DOTLOCK; + else + return FALSE; + return TRUE; +} + +const char *file_lock_method_to_str(enum file_lock_method method) +{ + switch (method) { + case FILE_LOCK_METHOD_FCNTL: + return "fcntl"; + case FILE_LOCK_METHOD_FLOCK: + return "flock"; + case FILE_LOCK_METHOD_DOTLOCK: + return "dotlock"; + } + i_unreached(); +} + +int file_try_lock(int fd, const char *path, int lock_type, + const struct file_lock_settings *set, + struct file_lock **lock_r, const char **error_r) +{ + return file_wait_lock(fd, path, lock_type, set, 0, lock_r, error_r); +} + +static const char * +file_lock_find_fcntl(int lock_fd, int lock_type) +{ + struct flock fl; + + i_zero(&fl); + fl.l_type = lock_type; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + if (fcntl(lock_fd, F_GETLK, &fl) < 0 || + fl.l_type == F_UNLCK || fl.l_pid == -1 || fl.l_pid == 0) + return ""; + return t_strdup_printf(" (%s lock held by pid %ld)", + fl.l_type == F_RDLCK ? "READ" : "WRITE", (long)fl.l_pid); +} + +static const char * +file_lock_find_proc_locks(int lock_fd ATTR_UNUSED) +{ + /* do anything except Linux support this? don't bother trying it for + OSes we don't know about. */ +#ifdef __linux__ + static bool have_proc_locks = TRUE; + struct stat st; + char node_buf[MAX_INT_STRLEN * 3 + 2]; + struct istream *input; + const char *line, *lock_type = ""; + pid_t pid = 0; + int fd; + + if (!have_proc_locks) + return NULL; + + if (fstat(lock_fd, &st) < 0) + return ""; + i_snprintf(node_buf, sizeof(node_buf), "%02x:%02x:%llu", + major(st.st_dev), minor(st.st_dev), + (unsigned long long)st.st_ino); + fd = open("/proc/locks", O_RDONLY); + if (fd == -1) { + have_proc_locks = FALSE; + return ""; + } + input = i_stream_create_fd_autoclose(&fd, 512); + while (pid == 0 && (line = i_stream_read_next_line(input)) != NULL) T_BEGIN { + const char *const *args = t_strsplit_spaces(line, " "); + + /* number: FLOCK/POSIX ADVISORY READ/WRITE pid + major:minor:inode region-start region-end */ + if (str_array_length(args) < 8) { + ; /* don't continue from within a T_BEGIN {...} T_END */ + } else if (strcmp(args[5], node_buf) == 0) { + lock_type = strcmp(args[3], "READ") == 0 ? + "READ" : "WRITE"; + if (str_to_pid(args[4], &pid) < 0) + pid = 0; + } + } T_END; + i_stream_destroy(&input); + if (pid == 0) { + /* not found */ + return ""; + } + if (pid == getpid()) + return " (BUG: lock is held by our own process)"; + return t_strdup_printf(" (%s lock held by pid %ld)", lock_type, (long)pid); +#else + return ""; +#endif +} + +const char *file_lock_find(int lock_fd, enum file_lock_method lock_method, + int lock_type) +{ + const char *ret; + + if (lock_method == FILE_LOCK_METHOD_FCNTL) { + ret = file_lock_find_fcntl(lock_fd, lock_type); + if (ret[0] != '\0') + return ret; + } + return file_lock_find_proc_locks(lock_fd); +} + +static bool err_is_lock_timeout(time_t started, unsigned int timeout_secs) +{ + /* if EINTR took at least timeout_secs-1 number of seconds, + assume it was the alarm. otherwise log EINTR failure. + (We most likely don't want to retry EINTR since a signal + means somebody wants us to stop blocking). */ + return errno == EINTR && + (unsigned long)(time(NULL) - started + 1) >= timeout_secs; +} + +static int file_lock_do(int fd, const char *path, int lock_type, + const struct file_lock_settings *set, + unsigned int timeout_secs, const char **error_r) +{ + const char *lock_type_str; + time_t started = time(NULL); + int ret; + + i_assert(fd != -1); + + if (timeout_secs != 0) { + alarm(timeout_secs); + file_lock_wait_start(); + } + + lock_type_str = lock_type == F_UNLCK ? "unlock" : + (lock_type == F_RDLCK ? "read-lock" : "write-lock"); + + switch (set->lock_method) { + case FILE_LOCK_METHOD_FCNTL: { +#ifndef HAVE_FCNTL + *error_r = t_strdup_printf( + "Can't lock file %s: fcntl() locks not supported", path); + return -1; +#else + struct flock fl; + + fl.l_type = lock_type; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + ret = fcntl(fd, timeout_secs != 0 ? F_SETLKW : F_SETLK, &fl); + if (timeout_secs != 0) { + alarm(0); + file_lock_wait_end(path); + } + + if (ret == 0) + break; + + if (timeout_secs == 0 && + (errno == EACCES || errno == EAGAIN)) { + /* locked by another process */ + *error_r = t_strdup_printf( + "fcntl(%s, %s, F_SETLK) locking failed: %m " + "(File is already locked)", path, lock_type_str); + return 0; + } + + if (err_is_lock_timeout(started, timeout_secs)) { + errno = EAGAIN; + *error_r = t_strdup_printf( + "fcntl(%s, %s, F_SETLKW) locking failed: " + "Timed out after %u seconds%s", + path, lock_type_str, timeout_secs, + file_lock_find(fd, set->lock_method, + lock_type)); + return 0; + } + *error_r = t_strdup_printf("fcntl(%s, %s, %s) locking failed: %m", + path, lock_type_str, timeout_secs == 0 ? "F_SETLK" : "F_SETLKW"); + if (errno == EDEADLK && !set->allow_deadlock) { + i_panic("%s%s", *error_r, + file_lock_find(fd, set->lock_method, + lock_type)); + } + return -1; +#endif + } + case FILE_LOCK_METHOD_FLOCK: { +#ifndef HAVE_FLOCK + *error_r = t_strdup_printf( + "Can't lock file %s: flock() not supported", path); + return -1; +#else + int operation = timeout_secs != 0 ? 0 : LOCK_NB; + + switch (lock_type) { + case F_RDLCK: + operation |= LOCK_SH; + break; + case F_WRLCK: + operation |= LOCK_EX; + break; + case F_UNLCK: + operation |= LOCK_UN; + break; + } + + ret = flock(fd, operation); + if (timeout_secs != 0) { + alarm(0); + file_lock_wait_end(path); + } + + if (ret == 0) + break; + + if (timeout_secs == 0 && errno == EWOULDBLOCK) { + /* locked by another process */ + *error_r = t_strdup_printf( + "flock(%s, %s) failed: %m " + "(File is already locked)", path, lock_type_str); + return 0; + } + if (err_is_lock_timeout(started, timeout_secs)) { + errno = EAGAIN; + *error_r = t_strdup_printf("flock(%s, %s) failed: " + "Timed out after %u seconds%s", + path, lock_type_str, timeout_secs, + file_lock_find(fd, set->lock_method, + lock_type)); + return 0; + } + *error_r = t_strdup_printf("flock(%s, %s) failed: %m", + path, lock_type_str); + if (errno == EDEADLK && !set->allow_deadlock) { + i_panic("%s%s", *error_r, + file_lock_find(fd, set->lock_method, + lock_type)); + } + return -1; +#endif + } + case FILE_LOCK_METHOD_DOTLOCK: + /* we shouldn't get here */ + i_unreached(); + } + + return 1; +} + +int file_wait_lock(int fd, const char *path, int lock_type, + const struct file_lock_settings *set, + unsigned int timeout_secs, + struct file_lock **lock_r, const char **error_r) +{ + struct file_lock *lock; + int ret; + + ret = file_lock_do(fd, path, lock_type, set, timeout_secs, error_r); + if (ret <= 0) + return ret; + + lock = i_new(struct file_lock, 1); + lock->set = *set; + lock->fd = fd; + lock->path = i_strdup(path); + lock->lock_type = lock_type; + i_gettimeofday(&lock->locked_time); + *lock_r = lock; + return 1; +} + +int file_lock_try_update(struct file_lock *lock, int lock_type) +{ + const char *error; + int ret; + + ret = file_lock_do(lock->fd, lock->path, lock_type, &lock->set, 0, + &error); + if (ret <= 0) + return ret; + file_lock_log_warning_if_slow(lock); + lock->lock_type = lock_type; + return 1; +} + +void file_lock_set_unlink_on_free(struct file_lock *lock, bool set) +{ + lock->set.unlink_on_free = set; +} + +void file_lock_set_close_on_free(struct file_lock *lock, bool set) +{ + lock->set.close_on_free = set; +} + +struct file_lock *file_lock_from_dotlock(struct dotlock **dotlock) +{ + struct file_lock *lock; + + lock = i_new(struct file_lock, 1); + lock->set.lock_method = FILE_LOCK_METHOD_DOTLOCK; + lock->fd = -1; + lock->path = i_strdup(file_dotlock_get_lock_path(*dotlock)); + lock->lock_type = F_WRLCK; + i_gettimeofday(&lock->locked_time); + lock->dotlock = *dotlock; + + *dotlock = NULL; + return lock; +} + +static void file_unlock_real(struct file_lock *lock) +{ + const char *error; + + if (file_lock_do(lock->fd, lock->path, F_UNLCK, &lock->set, 0, + &error) == 0) { + /* this shouldn't happen */ + i_error("file_unlock(%s) failed: %m", lock->path); + } +} + +void file_unlock(struct file_lock **_lock) +{ + struct file_lock *lock = *_lock; + + *_lock = NULL; + + /* unlocking is unnecessary when the file is unlinked. or alternatively + the unlink() must be done before unlocking, because otherwise it + could be deleting the new lock. */ + i_assert(!lock->set.unlink_on_free); + + if (lock->dotlock == NULL) + file_unlock_real(lock); + file_lock_free(&lock); +} + +static void file_try_unlink_locked(struct file_lock *lock) +{ + struct file_lock *temp_lock = NULL; + struct file_lock_settings temp_set = lock->set; + struct stat st1, st2; + const char *error; + int ret; + + temp_set.close_on_free = FALSE; + temp_set.unlink_on_free = FALSE; + + file_unlock_real(lock); + ret = file_try_lock(lock->fd, lock->path, F_WRLCK, &temp_set, + &temp_lock, &error); + if (ret < 0) { + i_error("file_lock_free(): Unexpectedly failed to retry locking %s: %s", + lock->path, error); + } else if (ret == 0) { + /* already locked by someone else */ + } else if (fstat(lock->fd, &st1) < 0) { + /* not expected to happen */ + i_error("file_lock_free(): fstat(%s) failed: %m", lock->path); + } else if (stat(lock->path, &st2) < 0) { + if (errno != ENOENT) + i_error("file_lock_free(): stat(%s) failed: %m", lock->path); + } else if (st1.st_ino != st2.st_ino || + !CMP_DEV_T(st1.st_dev, st2.st_dev)) { + /* lock file was recreated already - don't delete it */ + } else { + /* nobody was waiting on the lock - unlink it */ + i_unlink(lock->path); + } + file_lock_free(&temp_lock); +} + +void file_lock_free(struct file_lock **_lock) +{ + struct file_lock *lock = *_lock; + + if (lock == NULL) + return; + + *_lock = NULL; + + if (lock->dotlock != NULL) + file_dotlock_delete(&lock->dotlock); + if (lock->set.unlink_on_free) + file_try_unlink_locked(lock); + if (lock->set.close_on_free) + i_close_fd(&lock->fd); + + file_lock_log_warning_if_slow(lock); + i_free(lock->path); + i_free(lock); +} + +const char *file_lock_get_path(struct file_lock *lock) +{ + return lock->path; +} + +void file_lock_set_path(struct file_lock *lock, const char *path) +{ + if (path != lock->path) { + i_free(lock->path); + lock->path = i_strdup(path); + } +} + +void file_lock_wait_start(void) +{ + i_assert(lock_wait_start.tv_sec == 0); + + i_gettimeofday(&lock_wait_start); +} + +static void file_lock_wait_init_warning(void) +{ + const char *value; + + i_assert(file_lock_slow_warning_usecs == -1); + + value = getenv("FILE_LOCK_SLOW_WARNING_MSECS"); + if (value == NULL) + file_lock_slow_warning_usecs = LLONG_MAX; + else if (str_to_llong(value, &file_lock_slow_warning_usecs) == 0 && + file_lock_slow_warning_usecs > 0) { + file_lock_slow_warning_usecs *= 1000; + } else { + i_error("FILE_LOCK_SLOW_WARNING_MSECS: " + "Invalid value '%s' - ignoring", value); + file_lock_slow_warning_usecs = LLONG_MAX; + } +} + +static void file_lock_log_warning_if_slow(struct file_lock *lock) +{ + if (file_lock_slow_warning_usecs < 0) + file_lock_wait_init_warning(); + if (file_lock_slow_warning_usecs == LLONG_MAX) { + /* slowness checking is disabled */ + return; + } + if (lock->lock_type != F_WRLCK) { + /* some shared locks can legitimately be kept for a long time. + don't warn about them. */ + return; + } + + struct timeval now; + i_gettimeofday(&now); + + int diff = timeval_diff_msecs(&now, &lock->locked_time); + if (diff > file_lock_slow_warning_usecs/1000) { + i_warning("Lock %s kept for %d.%03d secs", lock->path, + diff / 1000, diff % 1000); + } +} + +void file_lock_wait_end(const char *lock_name) +{ + struct timeval now; + + i_assert(lock_wait_start.tv_sec != 0); + + i_gettimeofday(&now); + long long diff = timeval_diff_usecs(&now, &lock_wait_start); + if (diff < 0) { + /* time moved backwards */ + diff = 0; + } + if (diff > file_lock_slow_warning_usecs) { + if (file_lock_slow_warning_usecs < 0) + file_lock_wait_init_warning(); + if (diff > file_lock_slow_warning_usecs) { + int diff_msecs = (diff + 999) / 1000; + i_warning("Locking %s took %d.%03d secs", lock_name, + diff_msecs / 1000, diff_msecs % 1000); + } + } + file_lock_wait_usecs += diff; + lock_wait_start.tv_sec = 0; +} + +uint64_t file_lock_wait_get_total_usecs(void) +{ + return file_lock_wait_usecs; +} diff --git a/src/lib/file-lock.h b/src/lib/file-lock.h new file mode 100644 index 0000000..0939adc --- /dev/null +++ b/src/lib/file-lock.h @@ -0,0 +1,87 @@ +#ifndef FILE_LOCK_H +#define FILE_LOCK_H + +#include <unistd.h> +#include <fcntl.h> + +#define DEFAULT_LOCK_TIMEOUT 120 + +struct file_lock; +struct dotlock; + +enum file_lock_method { + FILE_LOCK_METHOD_FCNTL, + FILE_LOCK_METHOD_FLOCK, + FILE_LOCK_METHOD_DOTLOCK +}; + +struct file_lock_settings { + enum file_lock_method lock_method; + + /* When the lock is freed, close the fd automatically. This can + be useful for files that are only created to exist as lock files. */ + bool unlink_on_free:1; + /* When the lock is freed, unlink() the file automatically, unless other + processes are already waiting on the lock. This can be useful for + files that are only created to exist as lock files. */ + bool close_on_free:1; + /* Do not panic when the kernel returns EDEADLK while acquiring the + lock. */ + bool allow_deadlock:1; +}; + +/* Parse lock method from given string. Returns TRUE if ok, + FALSE if name is unknown. */ +bool file_lock_method_parse(const char *name, enum file_lock_method *method_r); +/* Convert lock method to string. */ +const char *file_lock_method_to_str(enum file_lock_method method); + +/* Lock the file. Returns 1 if successful, 0 if file is already locked, + or -1 if error. lock_type is F_WRLCK or F_RDLCK. */ +int file_try_lock(int fd, const char *path, int lock_type, + const struct file_lock_settings *set, + struct file_lock **lock_r, const char **error_r); +/* Like lock_try_lock(), but return 0 only after having tried to lock for + timeout_secs. */ +int file_wait_lock(int fd, const char *path, int lock_type, + const struct file_lock_settings *set, + unsigned int timeout_secs, + struct file_lock **lock_r, const char **error_r); +/* Change the lock type. WARNING: This isn't an atomic operation! + The result is the same as file_unlock() + file_try_lock(). */ +int file_lock_try_update(struct file_lock *lock, int lock_type); +/* When the lock is freed, unlink() the file automatically, unless other + processes are already waiting on the lock. This can be useful for files that + are only created to exist as lock files. */ +void file_lock_set_unlink_on_free(struct file_lock *lock, bool set); +/* When the lock is freed, close the fd automatically. This can + be useful for files that are only created to exist as lock files. */ +void file_lock_set_close_on_free(struct file_lock *lock, bool set); + +/* Convert dotlock into file_lock, which can be deleted with either + file_unlock() or file_lock_free(). */ +struct file_lock *file_lock_from_dotlock(struct dotlock **dotlock); + +/* Unlock and free the lock. */ +void file_unlock(struct file_lock **lock); +/* Free the lock without unlocking it (because you're closing the fd anyway). */ +void file_lock_free(struct file_lock **lock); + +/* Returns the path given as parameter to file_*lock*(). */ +const char *file_lock_get_path(struct file_lock *lock); +/* Update lock file's path (after it gets renamed by the caller). This is + useful mainly together with file_lock_set_unlink_on_free(). */ +void file_lock_set_path(struct file_lock *lock, const char *path); + +/* Returns human-readable string containing the process that has the file + currently locked. Returns "" if unknown, otherwise " (string)". */ +const char *file_lock_find(int lock_fd, enum file_lock_method lock_method, + int lock_type); + +/* Track the duration of a lock wait. */ +void file_lock_wait_start(void); +void file_lock_wait_end(const char *lock_name); +/* Return how many microseconds has been spent on lock waiting. */ +uint64_t file_lock_wait_get_total_usecs(void); + +#endif diff --git a/src/lib/file-set-size.c b/src/lib/file-set-size.c new file mode 100644 index 0000000..1f5938f --- /dev/null +++ b/src/lib/file-set-size.c @@ -0,0 +1,108 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_POSIX_FALLOCATE +# define _XOPEN_SOURCE 600 /* Required by glibc, breaks Solaris 9 */ +#endif +#define _GNU_SOURCE /* for fallocate() */ +#include "lib.h" +#include "file-set-size.h" + +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#if defined(HAVE_LINUX_FALLOC_H) && !defined(FALLOC_FL_KEEP_SIZE) +/* Legacy Linux does not have the FALLOC_FL_* flags under fcntl.h */ +# include <linux/falloc.h> +#endif + +int file_set_size(int fd, off_t size) +{ +#ifdef HAVE_POSIX_FALLOCATE + static bool posix_fallocate_supported = TRUE; +#endif + char block[IO_BLOCK_SIZE]; + off_t offset; + ssize_t ret; + struct stat st; + + i_assert(size >= 0); + + if (fstat(fd, &st) < 0) { + i_error("fstat() failed: %m"); + return -1; + } + + if (size < st.st_size) { + if (ftruncate(fd, size) < 0) { + i_error("ftruncate() failed: %m"); + return -1; + } + return 0; + } + if (size == st.st_size) + return 0; + +#ifdef HAVE_POSIX_FALLOCATE + if (posix_fallocate_supported) { + int err; + + err = posix_fallocate(fd, st.st_size, size - st.st_size); + if (err == 0) + return 0; + + if (err != EINVAL /* Solaris */ && + err != EOPNOTSUPP /* AOX */) { + if (!ENOSPACE(err)) + i_error("posix_fallocate() failed: %m"); + return -1; + } + /* Not supported by kernel, fallback to writing. */ + posix_fallocate_supported = FALSE; + } +#endif + /* start growing the file */ + offset = st.st_size; + memset(block, 0, I_MIN((ssize_t)sizeof(block), size - offset)); + + while (offset < size) { + ret = pwrite(fd, block, + I_MIN((ssize_t)sizeof(block), size - offset), + offset); + if (ret < 0) { + if (!ENOSPACE(errno)) + i_error("pwrite() failed: %m"); + return -1; + } + offset += ret; + } + return 0; +} + +int file_preallocate(int fd ATTR_UNUSED, off_t size ATTR_UNUSED) +{ +#if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_KEEP_SIZE) + /* Linux */ + if (fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, size) < 0) + return errno == ENOSYS ? 0 : -1; + return 1; +#elif defined (F_PREALLOCATE) + /* OSX */ + fstore_t fs; + + i_zero(&fs); + fs.fst_flags = F_ALLOCATECONTIG; + fs.fst_posmode = F_PEOFPOSMODE; + fs.fst_offset = 0; + fs.fst_length = size; + fs.fst_bytesalloc = 0; + if (fcntl(fd, F_PREALLOCATE, &fs) < 0) + return -1; + return fs.fst_bytesalloc > 0 ? 1 : 0; +#else + return 0; +#endif +} diff --git a/src/lib/file-set-size.h b/src/lib/file-set-size.h new file mode 100644 index 0000000..6752d32 --- /dev/null +++ b/src/lib/file-set-size.h @@ -0,0 +1,13 @@ +#ifndef FILE_SET_SIZE_H +#define FILE_SET_SIZE_H + +/* Shrink/grow file. If file is grown, the new data is guaranteed to + be zeros. The file offset may be anywhere after this call. + Returns -1 if failed, 0 if successful. */ +int file_set_size(int fd, off_t size); +/* Preallocate file to given size, without actually changing the size + reported by stat(). Returns 1 if ok, 0 if not supported by this filesystem, + -1 if error. */ +int file_preallocate(int fd, off_t size); + +#endif diff --git a/src/lib/fsync-mode.h b/src/lib/fsync-mode.h new file mode 100644 index 0000000..5873614 --- /dev/null +++ b/src/lib/fsync-mode.h @@ -0,0 +1,14 @@ +#ifndef FSYNC_MODE_H +#define FSYNC_MODE_H + +enum fsync_mode { + /* fsync when it's necessary for data safety. */ + FSYNC_MODE_OPTIMIZED = 0, + /* never fsync (in case of a crash can lose data) */ + FSYNC_MODE_NEVER, + /* fsync after all writes. this is necessary with NFS to avoid + write failures being delayed until file is close(). */ + FSYNC_MODE_ALWAYS +}; + +#endif diff --git a/src/lib/guid.c b/src/lib/guid.c new file mode 100644 index 0000000..7a825fd --- /dev/null +++ b/src/lib/guid.c @@ -0,0 +1,174 @@ +/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "buffer.h" +#include "str.h" +#include "sha1.h" +#include "hash.h" +#include "hex-binary.h" +#include "hostpid.h" +#include "guid.h" + +#include <unistd.h> +#include <time.h> + +const char *guid_generate(void) +{ + static struct timespec ts = { 0, 0 }; + static unsigned int pid = 0; + + /* we'll use the current time in nanoseconds as the initial 64bit + counter. */ + if (ts.tv_sec == 0) { + if (clock_gettime(CLOCK_REALTIME, &ts) < 0) + i_fatal("clock_gettime() failed: %m"); + pid = getpid(); + } else if ((uint32_t)ts.tv_nsec < (uint32_t)-1) { + ts.tv_nsec++; + } else { + ts.tv_sec++; + ts.tv_nsec = 0; + } + return t_strdup_printf("%08x%08lx.%x.%s", + (unsigned int)ts.tv_nsec, + (unsigned long)ts.tv_sec, + pid, my_hostname); +} + +void guid_128_host_hash_get(const char *host, + unsigned char hash_r[STATIC_ARRAY GUID_128_HOST_HASH_SIZE]) +{ + unsigned char full_hash[SHA1_RESULTLEN]; + + sha1_get_digest(host, strlen(host), full_hash); + memcpy(hash_r, full_hash + sizeof(full_hash)-GUID_128_HOST_HASH_SIZE, + GUID_128_HOST_HASH_SIZE); +} + +void guid_128_generate(guid_128_t guid_r) +{ +#if GUID_128_HOST_HASH_SIZE != 4 +# error GUID_128_HOST_HASH_SIZE must be 4 +#endif + static struct timespec ts = { 0, 0 }; + static uint8_t guid_static[8]; + uint32_t pid; + + /* we'll use the current time in nanoseconds as the initial 64bit + counter. */ + if (ts.tv_sec == 0) { + if (clock_gettime(CLOCK_REALTIME, &ts) < 0) + i_fatal("clock_gettime() failed: %m"); + pid = getpid(); + + guid_static[0] = (pid & 0x000000ff); + guid_static[1] = (pid & 0x0000ff00) >> 8; + guid_static[2] = (pid & 0x00ff0000) >> 16; + guid_static[3] = (pid & 0xff000000) >> 24; + guid_128_host_hash_get(my_hostdomain(), guid_static+4); + } else if (ioloop_timeval.tv_sec > ts.tv_sec || + (ioloop_timeval.tv_sec == ts.tv_sec && + ioloop_timeval.tv_usec * 1000 > ts.tv_nsec)) { + /* use ioloop's time since we have it. it doesn't provide any + more uniqueness, but it allows finding out more reliably + when a GUID was created. */ + ts.tv_sec = ioloop_timeval.tv_sec; + ts.tv_nsec = ioloop_timeval.tv_usec*1000; + } else if (ts.tv_nsec < 999999999L) { + ts.tv_nsec++; + } else { + ts.tv_sec++; + ts.tv_nsec = 0; + } + + guid_r[0] = (ts.tv_nsec & 0x000000ff); + guid_r[1] = (ts.tv_nsec & 0x0000ff00) >> 8; + guid_r[2] = (ts.tv_nsec & 0x00ff0000) >> 16; + guid_r[3] = (ts.tv_nsec & 0xff000000) >> 24; + guid_r[4] = (ts.tv_sec & 0x000000ff); + guid_r[5] = (ts.tv_sec & 0x0000ff00) >> 8; + guid_r[6] = (ts.tv_sec & 0x00ff0000) >> 16; + guid_r[7] = (ts.tv_sec & 0xff000000) >> 24; + memcpy(guid_r + 8, guid_static, 8); +} + +bool guid_128_is_empty(const guid_128_t guid) +{ + unsigned int i; + + for (i = 0; i < GUID_128_SIZE; i++) { + if (guid[i] != 0) + return FALSE; + } + return TRUE; +} + +bool guid_128_equals(const guid_128_t guid1, const guid_128_t guid2) +{ + return memcmp(guid1, guid2, GUID_128_SIZE) == 0; +} + +int guid_128_from_string(const char *str, guid_128_t guid_r) +{ + buffer_t buf; + + buffer_create_from_data(&buf, guid_r, GUID_128_SIZE); + return strlen(str) == GUID_128_SIZE*2 && + hex_to_binary(str, &buf) == 0 && + buf.used == GUID_128_SIZE ? 0 : -1; +} + +const char *guid_128_to_string(const guid_128_t guid) +{ + return binary_to_hex(guid, GUID_128_SIZE); +} + +unsigned int guid_128_hash(const guid_128_t guid) +{ + return mem_hash(guid, GUID_128_SIZE); +} + +int guid_128_cmp(const guid_128_t guid1, const guid_128_t guid2) +{ + return memcmp(guid1, guid2, GUID_128_SIZE); +} + +const char *guid_128_to_uuid_string(const guid_128_t guid, enum uuid_format format) +{ + switch(format) { + case FORMAT_COMPACT: + return guid_128_to_string(guid); + case FORMAT_RECORD: + return t_strdup_printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + guid[0], guid[1], guid[2], guid[3], guid[4], + guid[5], guid[6], guid[7], guid[8], guid[9], + guid[10], guid[11], guid[12], guid[13], guid[14], + guid[15]); + case FORMAT_MICROSOFT: + return t_strdup_printf("{%s}", guid_128_to_uuid_string(guid, FORMAT_RECORD)); + } + i_unreached(); +} + +int guid_128_from_uuid_string(const char *str, guid_128_t guid_r) +{ + size_t i,len,m=0; + int ret; + T_BEGIN { + len = strlen(str); + string_t *str2 = t_str_new(len); + for(i=0; i < len; i++) { + /* Microsoft format */ + if (i==0 && str[i] == '{') { m=1; continue; } + else if (i == len-1 && str[i] == '}') continue; + /* 8-4-4-4-12 */ + if (((i==8+m) || (i==13+m) || (i==18+m) || (i==23+m)) && + str[i] == '-') continue; + str_append_c(str2, str[i]); + } + ret = guid_128_from_string(str_c(str2), guid_r); + } T_END; + + return ret; +} diff --git a/src/lib/guid.h b/src/lib/guid.h new file mode 100644 index 0000000..0bf58d8 --- /dev/null +++ b/src/lib/guid.h @@ -0,0 +1,52 @@ +#ifndef GUID_H +#define GUID_H + +#define GUID_128_SIZE 16 +typedef uint8_t guid_128_t[GUID_128_SIZE]; + +#define GUID_128_HOST_HASH_SIZE 4 + +ARRAY_DEFINE_TYPE(guid_128_t, guid_128_t); + +enum uuid_format { + FORMAT_RECORD, + FORMAT_COMPACT, + FORMAT_MICROSOFT, +}; +/* Generate a GUID (contains host name) */ +const char *guid_generate(void); +/* Generate 128 bit GUID */ +void guid_128_generate(guid_128_t guid_r); +/* Returns TRUE if GUID is empty (not set / unknown). */ +bool guid_128_is_empty(const guid_128_t guid) ATTR_PURE; +static inline void guid_128_empty(guid_128_t guid) +{ + memset(guid, 0, GUID_128_SIZE); +} +/* Returns TRUE if two GUIDs are equal. */ +bool guid_128_equals(const guid_128_t guid1, const guid_128_t guid2) ATTR_PURE; +/* Copy GUID */ +static inline void guid_128_copy(guid_128_t dest, const guid_128_t src) +{ + memcpy(dest, src, GUID_128_SIZE); +} + +/* Returns GUID as a hex string. */ +const char *guid_128_to_string(const guid_128_t guid); +/* Parse GUID from a string. Returns 0 if ok, -1 if GUID isn't valid. */ +int guid_128_from_string(const char *str, guid_128_t guid_r); + +/* Returns GUID as a UUID hex string. */ +const char *guid_128_to_uuid_string(const guid_128_t guid, enum uuid_format format); +/* Parse GUID from a UUID string. Returns 0 if ok, -1 if UUID isn't valid. */ +int guid_128_from_uuid_string(const char *str, guid_128_t guid_r); + +/* guid_128 hash/cmp functions for hash.h */ +unsigned int guid_128_hash(const guid_128_t guid) ATTR_PURE; +int guid_128_cmp(const guid_128_t guid1, const guid_128_t guid2) ATTR_PURE; + +/* Return the hash of host used by guid_128_generate(). */ +void guid_128_host_hash_get(const char *host, + unsigned char hash_r[STATIC_ARRAY GUID_128_HOST_HASH_SIZE]); + +#endif diff --git a/src/lib/hash-decl.h b/src/lib/hash-decl.h new file mode 100644 index 0000000..60fb4e1 --- /dev/null +++ b/src/lib/hash-decl.h @@ -0,0 +1,20 @@ +#ifndef HASH_DECL_H +#define HASH_DECL_H + +#define HASH_TABLE_UNION(key_type, value_type) { \ + struct hash_table *_table; \ + key_type _key; \ + key_type *_keyp; \ + const key_type _const_key; \ + value_type _value; \ + value_type *_valuep; \ + } + +#define HASH_TABLE_DEFINE_TYPE(name, key_type, value_type) \ + union hash ## __ ## name HASH_TABLE_UNION(key_type, value_type) +#define HASH_TABLE(key_type, value_type) \ + union HASH_TABLE_UNION(key_type, value_type) +#define HASH_TABLE_TYPE(name) \ + union hash ## __ ## name + +#endif diff --git a/src/lib/hash-format.c b/src/lib/hash-format.c new file mode 100644 index 0000000..41ed495 --- /dev/null +++ b/src/lib/hash-format.c @@ -0,0 +1,245 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "base64.h" +#include "hex-binary.h" +#include "str.h" +#include "hash-method.h" +#include "hash-format.h" + +enum hash_encoding { + HASH_ENCODING_HEX, + HASH_ENCODING_HEX_SHORT, + HASH_ENCODING_BASE64 +}; + +struct hash_format_list { + struct hash_format_list *next; + + const struct hash_method *method; + void *context; + unsigned int bits; + enum hash_encoding encoding; +}; + +struct hash_format { + pool_t pool; + const char *str; + + struct hash_format_list *list, **pos; + unsigned char *digest; +}; + +static int +hash_format_parse(const char *str, unsigned int *idxp, + const struct hash_method **method_r, + unsigned int *bits_r, const char **error_r) +{ + const char *name, *end, *bitsp; + unsigned int bits, i = *idxp; + + /* we should have "hash_name}" or "hash_name:bits}" */ + end = strchr(str+i, '}'); + if (end == NULL) { + *error_r = "Missing '}'"; + return -1; + } + *idxp = end - str; + name = t_strdup_until(str+i, end); + + bitsp = strchr(name, ':'); + if (bitsp != NULL) + name = t_strdup_until(name, bitsp++); + + *method_r = hash_method_lookup(name); + if (*method_r == NULL) { + *error_r = t_strconcat("Unknown hash method: ", name, NULL); + return -1; + } + + bits = (*method_r)->digest_size * 8; + if (bitsp != NULL) { + if (str_to_uint(bitsp, &bits) < 0 || + bits == 0 || bits > (*method_r)->digest_size*8) { + *error_r = t_strconcat("Invalid :bits number: ", + bitsp, NULL); + return -1; + } + if ((bits % 8) != 0) { + *error_r = t_strconcat( + "Currently :bits must be divisible by 8: ", + bitsp, NULL); + return -1; + } + } + *bits_r = bits; + return 0; +} + +static int +hash_format_string_analyze(struct hash_format *format, const char *str, + const char **error_r) +{ + struct hash_format_list *list; + unsigned int i; + + for (i = 0; str[i] != '\0'; i++) { + if (str[i] != '%') + continue; + i++; + + list = p_new(format->pool, struct hash_format_list, 1); + list->encoding = HASH_ENCODING_HEX; + *format->pos = list; + format->pos = &list->next; + + if (str[i] == 'B') { + list->encoding = HASH_ENCODING_BASE64; + i++; + } else if (str[i] == 'X') { + list->encoding = HASH_ENCODING_HEX_SHORT; + i++; + } + if (str[i++] != '{') { + *error_r = "No '{' after '%'"; + return -1; + } + if (hash_format_parse(str, &i, &list->method, + &list->bits, error_r) < 0) + return -1; + list->context = p_malloc(format->pool, + list->method->context_size); + list->method->init(list->context); + } + return 0; +} + +int hash_format_init(const char *format_string, struct hash_format **format_r, + const char **error_r) +{ + struct hash_format *format; + pool_t pool; + int ret; + + pool = pool_alloconly_create("hash format", 1024); + format = p_new(pool, struct hash_format, 1); + format->pool = pool; + format->str = p_strdup(pool, format_string); + format->pos = &format->list; + T_BEGIN { + ret = hash_format_string_analyze(format, format_string, + error_r); + if (ret < 0) + *error_r = p_strdup(format->pool, *error_r); + } T_END; + if (ret < 0) { + *error_r = t_strdup(*error_r); + pool_unref(&pool); + return -1; + } + *format_r = format; + return 0; +} + +void hash_format_loop(struct hash_format *format, + const void *data, size_t size) +{ + struct hash_format_list *list; + + for (list = format->list; list != NULL; list = list->next) + list->method->loop(list->context, data, size); +} + +void hash_format_reset(struct hash_format *format) +{ + struct hash_format_list *list; + + for (list = format->list; list != NULL; list = list->next) { + memset(list->context, 0, list->method->context_size); + list->method->init(list->context); + } +} + +static void +hash_format_digest(string_t *dest, const struct hash_format_list *list, + const unsigned char *digest) +{ + unsigned int i, orig_len, size = list->bits / 8; + + i_assert(list->bits % 8 == 0); + + switch (list->encoding) { + case HASH_ENCODING_HEX: + binary_to_hex_append(dest, digest, size); + break; + case HASH_ENCODING_HEX_SHORT: + orig_len = str_len(dest); + binary_to_hex_append(dest, digest, size); + /* drop leading zeros, except if it's the only one */ + for (i = orig_len; i < str_len(dest); i++) { + if (str_data(dest)[i] != '0') + break; + } + if (i == str_len(dest)) i--; + str_delete(dest, orig_len, i-orig_len); + break; + case HASH_ENCODING_BASE64: + orig_len = str_len(dest); + base64_encode(digest, size, dest); + /* drop trailing '=' chars */ + while (str_len(dest) > orig_len && + str_data(dest)[str_len(dest)-1] == '=') + str_truncate(dest, str_len(dest)-1); + break; + } +} + +void hash_format_write(struct hash_format *format, string_t *dest) +{ + struct hash_format_list *list; + const char *p; + unsigned int i, max_digest_size = 0; + + for (list = format->list; list != NULL; list = list->next) { + if (max_digest_size < list->method->digest_size) + max_digest_size = list->method->digest_size; + } + if (format->digest == NULL) + format->digest = p_malloc(format->pool, max_digest_size); + + list = format->list; + for (i = 0; format->str[i] != '\0'; i++) { + if (format->str[i] != '%') { + str_append_c(dest, format->str[i]); + continue; + } + + /* we already verified that the string is ok */ + i_assert(list != NULL); + list->method->result(list->context, format->digest); + hash_format_digest(dest, list, format->digest); + list = list->next; + + p = strchr(format->str+i, '}'); + i_assert(p != NULL); + i = p - format->str; + } +} + +void hash_format_deinit(struct hash_format **_format, string_t *dest) +{ + struct hash_format *format = *_format; + + *_format = NULL; + + hash_format_write(format, dest); + pool_unref(&format->pool); +} + +void hash_format_deinit_free(struct hash_format **_format) +{ + struct hash_format *format = *_format; + + *_format = NULL; + pool_unref(&format->pool); +} diff --git a/src/lib/hash-format.h b/src/lib/hash-format.h new file mode 100644 index 0000000..177dc6f --- /dev/null +++ b/src/lib/hash-format.h @@ -0,0 +1,23 @@ +#ifndef HASH_FORMAT_H +#define HASH_FORMAT_H + +struct hash_format; + +/* Initialize formatting hash. Format can contain text with %{sha1} style + variables. Each hash hash can be also truncated by specifying the number + of bits to truncate to, such as %{sha1:80}. */ +int hash_format_init(const char *format_string, struct hash_format **format_r, + const char **error_r); +/* Add more data to hash. */ +void hash_format_loop(struct hash_format *format, + const void *data, size_t size); +/* Finish the hash and write it into given string. */ +void hash_format_write(struct hash_format *format, string_t *dest); +/* Reset hash to initial state. */ +void hash_format_reset(struct hash_format *format); +/* Write the hash into given string and free used memory. */ +void hash_format_deinit(struct hash_format **format, string_t *dest); +/* Free used memory without writing to string. */ +void hash_format_deinit_free(struct hash_format **format); + +#endif diff --git a/src/lib/hash-method.c b/src/lib/hash-method.c new file mode 100644 index 0000000..d7a6bed --- /dev/null +++ b/src/lib/hash-method.c @@ -0,0 +1,98 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "md4.h" +#include "md5.h" +#include "sha1.h" +#include "sha2.h" +#include "sha3.h" +#include "hash-method.h" + +const struct hash_method *hash_method_lookup(const char *name) +{ + unsigned int i; + + for (i = 0; hash_methods[i] != NULL; i++) { + if (strcmp(hash_methods[i]->name, name) == 0) + return hash_methods[i]; + } + return NULL; +} + +static void hash_method_init_size(void *context) +{ + uint64_t *ctx = context; + + *ctx = 0; +} + +static void +hash_method_loop_size(void *context, const void *data ATTR_UNUSED, size_t size) +{ + uint64_t *ctx = context; + + *ctx += size; +} + +static void hash_method_result_size(void *context, unsigned char *result_r) +{ + uint64_t *ctx = context; + + result_r[0] = (*ctx & 0xff00000000000000ULL) >> 56; + result_r[1] = (*ctx & 0x00ff000000000000ULL) >> 48; + result_r[2] = (*ctx & 0x0000ff0000000000ULL) >> 40; + result_r[3] = (*ctx & 0x000000ff00000000ULL) >> 32; + result_r[4] = (*ctx & 0x00000000ff000000ULL) >> 24; + result_r[5] = (*ctx & 0x0000000000ff0000ULL) >> 16; + result_r[6] = (*ctx & 0x000000000000ff00ULL) >> 8; + result_r[7] = (*ctx & 0x00000000000000ffULL); +} + +void hash_method_get_digest(const struct hash_method *meth, + const void *data, size_t data_len, + unsigned char *result_r) +{ + i_assert(meth != NULL); + i_assert(data_len == 0 || data != NULL); + unsigned char ctx[meth->context_size]; + + meth->init(ctx); + meth->loop(ctx, data == NULL ? "" : data, data_len); + meth->result(ctx, result_r); +} + +buffer_t *t_hash_data(const struct hash_method *meth, + const void *data, size_t data_len) +{ + i_assert(meth != NULL); + buffer_t *result = t_buffer_create(meth->digest_size); + unsigned char *resptr = buffer_append_space_unsafe(result, + meth->digest_size); + + hash_method_get_digest(meth, data, data_len, resptr); + return result; +} + +static const struct hash_method hash_method_size = { + .name = "size", + .block_size = 1, + .context_size = sizeof(uint64_t), + .digest_size = sizeof(uint64_t), + + .init = hash_method_init_size, + .loop = hash_method_loop_size, + .result = hash_method_result_size +}; + +const struct hash_method *hash_methods[] = { + &hash_method_md4, + &hash_method_md5, + &hash_method_sha1, + &hash_method_sha256, + &hash_method_sha384, + &hash_method_sha512, + &hash_method_sha3_256, + &hash_method_sha3_512, + &hash_method_size, + NULL +}; diff --git a/src/lib/hash-method.h b/src/lib/hash-method.h new file mode 100644 index 0000000..c4250de --- /dev/null +++ b/src/lib/hash-method.h @@ -0,0 +1,55 @@ +#ifndef HASH_METHOD_H +#define HASH_METHOD_H + +#include "buffer.h" + +struct hash_method { + const char *name; + /* Block size for the algorithm */ + unsigned int block_size; + /* Number of bytes that must be allocated for context */ + unsigned int context_size; + /* Number of bytes that must be allocated for result()'s digest */ + unsigned int digest_size; + + void (*init)(void *context); + void (*loop)(void *context, const void *data, size_t size); + void (*result)(void *context, unsigned char *digest_r); +}; + +const struct hash_method *hash_method_lookup(const char *name); + +/* NULL-terminated list of all hash methods */ +extern const struct hash_method *hash_methods[]; + +void hash_method_get_digest(const struct hash_method *meth, + const void *data, size_t data_len, + unsigned char *result_r); + +/** Simple datastack helpers for digesting (hashing) + + * USAGE: + + buffer_t *result = t_hash_str(hash_method_lookup("sha256"), "hello world"); + const char *hex = binary_to_hex(result->data, result->used); + +*/ + +buffer_t *t_hash_data(const struct hash_method *meth, + const void *data, size_t data_len); + +static inline +buffer_t *t_hash_buffer(const struct hash_method *meth, + const buffer_t *data) +{ + return t_hash_data(meth, data->data, data->used); +} + +static inline +buffer_t *t_hash_str(const struct hash_method *meth, + const char *data) +{ + return t_hash_data(meth, data, strlen(data)); +} + +#endif diff --git a/src/lib/hash.c b/src/lib/hash.c new file mode 100644 index 0000000..5530de1 --- /dev/null +++ b/src/lib/hash.c @@ -0,0 +1,593 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +/* @UNSAFE: whole file */ + +#include "lib.h" +#include "hash.h" +#include "primes.h" + +#include <ctype.h> + +#define HASH_TABLE_MIN_SIZE 67 + +#undef hash_table_create +#undef hash_table_create_direct +#undef hash_table_destroy +#undef hash_table_clear +#undef hash_table_lookup +#undef hash_table_lookup_full +#undef hash_table_insert +#undef hash_table_update +#undef hash_table_try_remove +#undef hash_table_count +#undef hash_table_iterate_init +#undef hash_table_iterate +#undef hash_table_freeze +#undef hash_table_thaw +#undef hash_table_copy + +struct hash_node { + struct hash_node *next; + void *key; + void *value; +}; + +struct hash_table { + pool_t node_pool; + + int frozen; + unsigned int initial_size, nodes_count, removed_count; + + unsigned int size; + struct hash_node *nodes; + struct hash_node *free_nodes; + + hash_callback_t *hash_cb; + hash_cmp_callback_t *key_compare_cb; +}; + +struct hash_iterate_context { + struct hash_table *table; + struct hash_node *next; + unsigned int pos; +}; + +enum hash_table_operation{ + HASH_TABLE_OP_INSERT, + HASH_TABLE_OP_UPDATE, + HASH_TABLE_OP_RESIZE +}; + +static bool hash_table_resize(struct hash_table *table, bool grow); + +void hash_table_create(struct hash_table **table_r, pool_t node_pool, + unsigned int initial_size, hash_callback_t *hash_cb, + hash_cmp_callback_t *key_compare_cb) +{ + struct hash_table *table; + + pool_ref(node_pool); + table = i_new(struct hash_table, 1); + table->node_pool = node_pool; + table->initial_size = + I_MAX(primes_closest(initial_size), HASH_TABLE_MIN_SIZE); + + table->hash_cb = hash_cb; + table->key_compare_cb = key_compare_cb; + + table->size = table->initial_size; + table->nodes = i_new(struct hash_node, table->size); + *table_r = table; +} + +static unsigned int direct_hash(const void *p) +{ + /* NOTE: may truncate the value, but that doesn't matter. */ + return POINTER_CAST_TO(p, unsigned int); +} + +static int direct_cmp(const void *p1, const void *p2) +{ + return p1 == p2 ? 0 : 1; +} + +void hash_table_create_direct(struct hash_table **table_r, pool_t node_pool, + unsigned int initial_size) +{ + hash_table_create(table_r, node_pool, initial_size, + direct_hash, direct_cmp); +} + +static void free_node(struct hash_table *table, struct hash_node *node) +{ + if (!table->node_pool->alloconly_pool) + p_free(table->node_pool, node); + else { + node->next = table->free_nodes; + table->free_nodes = node; + } +} + +static void destroy_node_list(struct hash_table *table, struct hash_node *node) +{ + struct hash_node *next; + + while (node != NULL) { + next = node->next; + p_free(table->node_pool, node); + node = next; + } +} + +static void hash_table_destroy_nodes(struct hash_table *table) +{ + unsigned int i; + + for (i = 0; i < table->size; i++) { + if (table->nodes[i].next != NULL) + destroy_node_list(table, table->nodes[i].next); + } +} + +void hash_table_destroy(struct hash_table **_table) +{ + struct hash_table *table = *_table; + + if (table == NULL) + return; + *_table = NULL; + + i_assert(table->frozen == 0); + + if (!table->node_pool->alloconly_pool) { + hash_table_destroy_nodes(table); + destroy_node_list(table, table->free_nodes); + } + + pool_unref(&table->node_pool); + i_free(table->nodes); + i_free(table); +} + +void hash_table_clear(struct hash_table *table, bool free_nodes) +{ + i_assert(table->frozen == 0); + + if (!table->node_pool->alloconly_pool) + hash_table_destroy_nodes(table); + + if (free_nodes) { + if (!table->node_pool->alloconly_pool) + destroy_node_list(table, table->free_nodes); + table->free_nodes = NULL; + } + + memset(table->nodes, 0, sizeof(struct hash_node) * table->size); + + table->nodes_count = 0; + table->removed_count = 0; +} + +static struct hash_node * +hash_table_lookup_node(const struct hash_table *table, + const void *key, unsigned int hash) +{ + struct hash_node *node; + + node = &table->nodes[hash % table->size]; + + do { + if (node->key != NULL) { + if (table->key_compare_cb(node->key, key) == 0) + return node; + } + node = node->next; + } while (node != NULL); + + return NULL; +} + +void *hash_table_lookup(const struct hash_table *table, const void *key) +{ + struct hash_node *node; + + node = hash_table_lookup_node(table, key, table->hash_cb(key)); + return node != NULL ? node->value : NULL; +} + +bool hash_table_lookup_full(const struct hash_table *table, + const void *lookup_key, + void **orig_key, void **value) +{ + struct hash_node *node; + + node = hash_table_lookup_node(table, lookup_key, + table->hash_cb(lookup_key)); + if (node == NULL) + return FALSE; + + *orig_key = node->key; + *value = node->value; + return TRUE; +} + +static void +hash_table_insert_node(struct hash_table *table, void *key, void *value, + enum hash_table_operation opcode) +{ + struct hash_node *node, *prev; + unsigned int hash; + bool check_existing = TRUE; + + i_assert(table->nodes_count < UINT_MAX); + i_assert(key != NULL); + + if(opcode == HASH_TABLE_OP_RESIZE) + check_existing = FALSE; + hash = table->hash_cb(key); + + if (check_existing && table->removed_count > 0) { + /* there may be holes, have to check everything */ + node = hash_table_lookup_node(table, key, hash); + if (node != NULL) { + i_assert(opcode == HASH_TABLE_OP_UPDATE); + node->value = value; + return; + } + + check_existing = FALSE; + } + + /* a) primary node */ + node = &table->nodes[hash % table->size]; + if (node->key == NULL) { + table->nodes_count++; + + node->key = key; + node->value = value; + return; + } + if (check_existing) { + if (table->key_compare_cb(node->key, key) == 0) { + i_assert(opcode == HASH_TABLE_OP_UPDATE); + node->value = value; + return; + } + } + + /* b) collisions list */ + prev = node; node = node->next; + while (node != NULL) { + if (node->key == NULL) + break; + + if (check_existing) { + if (table->key_compare_cb(node->key, key) == 0) { + i_assert(opcode == HASH_TABLE_OP_UPDATE); + node->value = value; + return; + } + } + prev = node; + node = node->next; + } + + if (node == NULL) { + if (table->frozen == 0 && hash_table_resize(table, TRUE)) { + /* resized table, try again */ + hash_table_insert_node(table, key, value, HASH_TABLE_OP_RESIZE); + return; + } + + if (table->free_nodes == NULL) + node = p_new(table->node_pool, struct hash_node, 1); + else { + node = table->free_nodes; + table->free_nodes = node->next; + node->next = NULL; + } + prev->next = node; + } + + node->key = key; + node->value = value; + + table->nodes_count++; +} + +void hash_table_insert(struct hash_table *table, void *key, void *value) +{ + hash_table_insert_node(table, key, value, HASH_TABLE_OP_INSERT); +} + +void hash_table_update(struct hash_table *table, void *key, void *value) +{ + hash_table_insert_node(table, key, value, HASH_TABLE_OP_UPDATE); +} + +static void +hash_table_compress(struct hash_table *table, struct hash_node *root) +{ + struct hash_node *node, *next; + + i_assert(table->frozen == 0); + + /* remove deleted nodes from the list */ + for (node = root; node->next != NULL; ) { + next = node->next; + + if (next->key == NULL) { + node->next = next->next; + free_node(table, next); + } else { + node = next; + } + } + + /* update root */ + if (root->key == NULL && root->next != NULL) { + next = root->next; + *root = *next; + free_node(table, next); + } +} + +static void hash_table_compress_removed(struct hash_table *table) +{ + unsigned int i; + + for (i = 0; i < table->size; i++) + hash_table_compress(table, &table->nodes[i]); + + table->removed_count = 0; +} + +bool hash_table_try_remove(struct hash_table *table, const void *key) +{ + struct hash_node *node; + unsigned int hash; + + hash = table->hash_cb(key); + + node = hash_table_lookup_node(table, key, hash); + if (unlikely(node == NULL)) + return FALSE; + + node->key = NULL; + table->nodes_count--; + + if (table->frozen != 0) + table->removed_count++; + else if (!hash_table_resize(table, FALSE)) + hash_table_compress(table, &table->nodes[hash % table->size]); + return TRUE; +} + +unsigned int hash_table_count(const struct hash_table *table) +{ + return table->nodes_count; +} + +struct hash_iterate_context *hash_table_iterate_init(struct hash_table *table) +{ + struct hash_iterate_context *ctx; + + hash_table_freeze(table); + + ctx = i_new(struct hash_iterate_context, 1); + ctx->table = table; + ctx->next = &table->nodes[0]; + return ctx; +} + +static struct hash_node * +hash_table_iterate_next(struct hash_iterate_context *ctx, + struct hash_node *node) +{ + do { + node = node->next; + if (node == NULL) { + if (++ctx->pos == ctx->table->size) { + ctx->pos--; + return NULL; + } + node = &ctx->table->nodes[ctx->pos]; + } + } while (node->key == NULL); + + return node; +} + +bool hash_table_iterate(struct hash_iterate_context *ctx, + void **key_r, void **value_r) +{ + struct hash_node *node; + + node = ctx->next; + if (node != NULL && node->key == NULL) + node = hash_table_iterate_next(ctx, node); + if (node == NULL) { + *key_r = *value_r = NULL; + return FALSE; + } + *key_r = node->key; + *value_r = node->value; + + ctx->next = hash_table_iterate_next(ctx, node); + return TRUE; +} + +void hash_table_iterate_deinit(struct hash_iterate_context **_ctx) +{ + struct hash_iterate_context *ctx = *_ctx; + + if (ctx == NULL) + return; + + *_ctx = NULL; + hash_table_thaw(ctx->table); + i_free(ctx); +} + +void hash_table_freeze(struct hash_table *table) +{ + table->frozen++; +} + +void hash_table_thaw(struct hash_table *table) +{ + i_assert(table->frozen > 0); + + if (--table->frozen > 0) + return; + + if (table->removed_count > 0) { + if (!hash_table_resize(table, FALSE)) + hash_table_compress_removed(table); + } +} + +static bool hash_table_resize(struct hash_table *table, bool grow) +{ + struct hash_node *old_nodes, *node, *next; + unsigned int next_size, old_size, i; + float nodes_per_list; + + i_assert(table->frozen == 0); + + nodes_per_list = (float) table->nodes_count / (float) table->size; + if (nodes_per_list > 0.3 && nodes_per_list < 2.0) + return FALSE; + + next_size = I_MAX(primes_closest(table->nodes_count+1), + table->initial_size); + if (next_size == table->size) + return FALSE; + + if (grow && table->size >= next_size) + return FALSE; + + /* recreate primary table */ + old_size = table->size; + old_nodes = table->nodes; + + table->size = I_MAX(next_size, HASH_TABLE_MIN_SIZE); + table->nodes = i_new(struct hash_node, table->size); + + table->nodes_count = 0; + table->removed_count = 0; + + table->frozen++; + + /* move the data */ + for (i = 0; i < old_size; i++) { + node = &old_nodes[i]; + if (node->key != NULL) { + hash_table_insert_node(table, node->key, + node->value, HASH_TABLE_OP_RESIZE); + } + + for (node = node->next; node != NULL; node = next) { + next = node->next; + + if (node->key != NULL) { + hash_table_insert_node(table, node->key, + node->value, HASH_TABLE_OP_RESIZE); + } + free_node(table, node); + } + } + + table->frozen--; + + i_free(old_nodes); + return TRUE; +} + +void hash_table_copy(struct hash_table *dest, struct hash_table *src) +{ + struct hash_iterate_context *iter; + void *key, *value; + + hash_table_freeze(dest); + + iter = hash_table_iterate_init(src); + while (hash_table_iterate(iter, &key, &value)) + hash_table_insert(dest, key, value); + hash_table_iterate_deinit(&iter); + + hash_table_thaw(dest); +} + +/* a char* hash function from ASU -- from glib */ +unsigned int ATTR_NO_SANITIZE_INTEGER +str_hash(const char *p) +{ + const unsigned char *s = (const unsigned char *)p; + unsigned int g, h = 0; + + while (*s != '\0') { + h = (h << 4) + *s; + if ((g = h & 0xf0000000UL) != 0) { + h = h ^ (g >> 24); + h = h ^ g; + } + s++; + } + + return h; +} + +/* a char* hash function from ASU -- from glib */ +unsigned int ATTR_NO_SANITIZE_INTEGER +strcase_hash(const char *p) +{ + const unsigned char *s = (const unsigned char *)p; + unsigned int g, h = 0; + + while (*s != '\0') { + h = (h << 4) + i_toupper(*s); + if ((g = h & 0xf0000000UL) != 0) { + h = h ^ (g >> 24); + h = h ^ g; + } + s++; + } + + return h; +} + +unsigned int ATTR_NO_SANITIZE_INTEGER +mem_hash(const void *p, unsigned int size) +{ + const unsigned char *s = p; + unsigned int i, g, h = 0; + + for (i = 0; i < size; i++) { + h = (h << 4) + *s; + if ((g = h & 0xf0000000UL) != 0) { + h = h ^ (g >> 24); + h = h ^ g; + } + s++; + } + return h; +} + +unsigned int ATTR_NO_SANITIZE_INTEGER +strfastcase_hash(const char *p) +{ + const unsigned char *s = (const unsigned char *)p; + unsigned int g, h = 0; + + while (*s != '\0') { + h = (h << 4) + ((*s) & ~0x20U); + if ((g = h & 0xf0000000UL) != 0) { + h = h ^ (g >> 24); + h = h ^ g; + } + s++; + } + + return h; +} diff --git a/src/lib/hash.h b/src/lib/hash.h new file mode 100644 index 0000000..65b6b8e --- /dev/null +++ b/src/lib/hash.h @@ -0,0 +1,175 @@ +#ifndef HASH_H +#define HASH_H + +struct hash_table; + +#ifdef HAVE_TYPEOF +# define HASH_VALUE_CAST(table) (typeof((table)._value)) +#else +# define HASH_VALUE_CAST(table) +#endif + +/* Returns hash code. */ +typedef unsigned int hash_callback_t(const void *p); +/* Returns 0 if the pointers are equal. */ +typedef int hash_cmp_callback_t(const void *p1, const void *p2); + +/* Create a new hash table. If initial_size is 0, the default value is used. + table_pool is used to allocate/free large hash tables, node_pool is used + for smaller allocations and can also be alloconly pool. The pools must not + be free'd before hash_table_destroy() is called. */ +void hash_table_create(struct hash_table **table_r, pool_t node_pool, + unsigned int initial_size, + hash_callback_t *hash_cb, + hash_cmp_callback_t *key_compare_cb); +#define hash_table_create(table, pool, size, hash_cb, key_cmp_cb) \ + TYPE_CHECKS(void, \ + COMPILE_ERROR_IF_TRUE( \ + sizeof((*table)._key) != sizeof(void *) || \ + sizeof((*table)._value) != sizeof(void *)) || \ + COMPILE_ERROR_IF_TRUE( \ + !__builtin_types_compatible_p(typeof(&key_cmp_cb), \ + int (*)(typeof((*table)._key), typeof((*table)._key))) && \ + !__builtin_types_compatible_p(typeof(&key_cmp_cb), \ + int (*)(typeof((*table)._const_key), typeof((*table)._const_key)))) || \ + COMPILE_ERROR_IF_TRUE( \ + !__builtin_types_compatible_p(typeof(&hash_cb), \ + unsigned int (*)(typeof((*table)._key))) && \ + !__builtin_types_compatible_p(typeof(&hash_cb), \ + unsigned int (*)(typeof((*table)._const_key)))), \ + hash_table_create(&(*table)._table, pool, size, \ + (hash_callback_t *)hash_cb, \ + (hash_cmp_callback_t *)key_cmp_cb)) + +/* Create hash table where comparisons are done directly with the pointers. */ +void hash_table_create_direct(struct hash_table **table_r, pool_t node_pool, + unsigned int initial_size); +#define hash_table_create_direct(table, pool, size) \ + TYPE_CHECKS(void, \ + COMPILE_ERROR_IF_TRUE( \ + sizeof((*table)._key) != sizeof(void *) || \ + sizeof((*table)._value) != sizeof(void *)), \ + hash_table_create_direct(&(*table)._table, pool, size)) + +#define hash_table_is_created(table) \ + ((table)._table != NULL) + +void hash_table_destroy(struct hash_table **table); +#define hash_table_destroy(table) \ + hash_table_destroy(&(*table)._table) +/* Remove all nodes from hash table. If free_collisions is TRUE, the + memory allocated from node_pool is freed, or discarded with alloconly pools. + WARNING: If you p_clear() the node_pool, the free_collisions must be TRUE. */ +void hash_table_clear(struct hash_table *table, bool free_collisions); +#define hash_table_clear(table, free_collisions) \ + hash_table_clear((table)._table, free_collisions) + +void *hash_table_lookup(const struct hash_table *table, const void *key) ATTR_PURE; +#define hash_table_lookup(table, key) \ + TYPE_CHECKS(void *, \ + COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE((table)._key, (table)._const_key, key), \ + HASH_VALUE_CAST(table)hash_table_lookup((table)._table, (key))) + +bool hash_table_lookup_full(const struct hash_table *table, + const void *lookup_key, + void **orig_key_r, void **value_r); +#ifndef __cplusplus +# define hash_table_lookup_full(table, lookup_key, orig_key_r, value_r) \ + TYPE_CHECKS(bool, \ + COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE((table)._const_key, (table)._key, lookup_key) || \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._keyp, orig_key_r) || \ + COMPILE_ERROR_IF_TRUE(sizeof(*(orig_key_r)) != sizeof(void *)) || \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._valuep, value_r) || \ + COMPILE_ERROR_IF_TRUE(sizeof(*(value_r)) != sizeof(void *)), \ + hash_table_lookup_full((table)._table, \ + (lookup_key), (void *)(orig_key_r), (void *)(value_r))) +#else +/* C++ requires (void **) casting, but that's not possible with strict + aliasing, so .. we'll just disable the type checks */ +# define hash_table_lookup_full(table, lookup_key, orig_key_r, value_r) \ + hash_table_lookup_full((table)._table, lookup_key, orig_key_r, value_r) +#endif + +/* Suppose to insert a new key-value node to the hash table. + If the key already exists, assert-crash. */ +void hash_table_insert(struct hash_table *table, void *key, void *value); +/* If the key doesn't exists, do the exact same as hash_table_insert() + If the key already exists, preserve the original key and update only the value.*/ +void hash_table_update(struct hash_table *table, void *key, void *value); +#define hash_table_insert(table, key, value) \ + TYPE_CHECKS(void, \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._key, key) || \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._value, value), \ + hash_table_insert((table)._table, (void *)(key), (void *)(value))) +#define hash_table_update(table, key, value) \ + TYPE_CHECKS(void, \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._key, key) || \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._value, value), \ + hash_table_update((table)._table, (void *)(key), (void *)(value))) + +bool hash_table_try_remove(struct hash_table *table, const void *key); +#define hash_table_try_remove(table, key) \ + TYPE_CHECKS(bool, \ + COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE((table)._const_key, (table)._key, key), \ + hash_table_try_remove((table)._table, (const void *)(key))) +#define hash_table_remove(table, key) \ + STMT_START { \ + if (unlikely(!hash_table_try_remove(table, key))) \ + i_panic("key not found from hash"); \ + } STMT_END +unsigned int hash_table_count(const struct hash_table *table) ATTR_PURE; +#define hash_table_count(table) \ + hash_table_count((table)._table) + +/* Iterates through all nodes in hash table. You may safely call hash_table_*() + functions while iterating, but if you add any new nodes, they may or may + not be called for in this iteration. */ +struct hash_iterate_context *hash_table_iterate_init(struct hash_table *table); +#define hash_table_iterate_init(table) \ + hash_table_iterate_init((table)._table) +bool hash_table_iterate(struct hash_iterate_context *ctx, + void **key_r, void **value_r); +#ifndef __cplusplus +# define hash_table_iterate(ctx, table, key_r, value_r) \ + TYPE_CHECKS(bool, \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._keyp, key_r) || \ + COMPILE_ERROR_IF_TRUE(sizeof(*(key_r)) != sizeof(void *)) || \ + COMPILE_ERROR_IF_TRUE(sizeof(*(value_r)) != sizeof(void *)) || \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._valuep, value_r), \ + hash_table_iterate(ctx, (void *)(key_r), (void *)(value_r))) +#else +/* C++ requires (void **) casting, but that's not possible with strict + aliasing, so .. we'll just disable the type checks */ +# define hash_table_iterate(ctx, table, key_r, value_r) \ + hash_table_iterate(ctx, key_r, value_r) +#endif + +void hash_table_iterate_deinit(struct hash_iterate_context **ctx); + +/* Hash table isn't resized, and removed nodes aren't removed from + the list while hash table is freezed. Supports nesting. */ +void hash_table_freeze(struct hash_table *table); +void hash_table_thaw(struct hash_table *table); +#define hash_table_freeze(table) \ + hash_table_freeze((table)._table) +#define hash_table_thaw(table) \ + hash_table_thaw((table)._table) + +/* Copy all nodes from one hash table to another */ +void hash_table_copy(struct hash_table *dest, struct hash_table *src); +#define hash_table_copy(table1, table2) \ + hash_table_copy((table1)._table, (table2)._table) + +/* hash function for strings */ +unsigned int str_hash(const char *p) ATTR_PURE; +unsigned int strcase_hash(const char *p) ATTR_PURE; + +/* fast hash function which uppercases a-z. Does not work well + with input that consists from non number/letter input, as + it works by dropping 0x20. */ +unsigned int strfastcase_hash(const char *p) ATTR_PURE; + +/* a generic hash for a given memory block */ +unsigned int mem_hash(const void *p, unsigned int size) ATTR_PURE; + +#endif diff --git a/src/lib/hash2.c b/src/lib/hash2.c new file mode 100644 index 0000000..a8a23e0 --- /dev/null +++ b/src/lib/hash2.c @@ -0,0 +1,242 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "primes.h" +#include "hash2.h" + +#define HASH_TABLE_MIN_SIZE 131 + +struct hash2_value { + struct hash2_value *next; + unsigned int key_hash; + /* user_data[value_size] */ +}; +ARRAY_DEFINE_TYPE(hash2_value, struct hash2_value *); + +struct hash2_table { + unsigned int count; + unsigned int initial_size; + unsigned int hash_table_size; + unsigned int value_size; + + pool_t value_pool; + struct hash2_value *deleted_values; + + ARRAY_TYPE(hash2_value) hash_table; + + hash2_key_callback_t *key_hash_cb; + hash2_cmp_callback_t *key_compare_cb; + void *context; +}; + +static void hash2_alloc_table(struct hash2_table *hash, unsigned int size) +{ + hash->hash_table_size = size; + + i_array_init(&hash->hash_table, hash->hash_table_size); + (void)array_idx_get_space(&hash->hash_table, hash->hash_table_size-1); +} + +struct hash2_table * +hash2_create(unsigned int initial_size, unsigned int value_size, + hash2_key_callback_t *key_hash_cb, + hash2_cmp_callback_t *key_compare_cb, void *context) +{ + struct hash2_table *hash; + + hash = i_new(struct hash2_table, 1); + hash->initial_size = I_MAX(initial_size, HASH_TABLE_MIN_SIZE); + hash->value_size = value_size; + hash->key_hash_cb = key_hash_cb; + hash->key_compare_cb = key_compare_cb; + hash->context = context; + + hash->value_pool = pool_alloconly_create("hash2 value pool", 16384); + hash2_alloc_table(hash, hash->initial_size); + return hash; +} + +void hash2_destroy(struct hash2_table **_hash) +{ + struct hash2_table *hash = *_hash; + + *_hash = NULL; + array_free(&hash->hash_table); + pool_unref(&hash->value_pool); + i_free(hash); +} + +void hash2_clear(struct hash2_table *hash) +{ + array_free(&hash->hash_table); + hash2_alloc_table(hash, hash->initial_size); + p_clear(hash->value_pool); + hash->count = 0; + hash->deleted_values = NULL; +} + +static void hash2_resize(struct hash2_table *hash, bool grow) +{ + ARRAY_TYPE(hash2_value) old_hash_table; + struct hash2_value *old_hash, *value, **valuep, *next; + unsigned int next_size, old_count, i, idx; + float nodes_per_list; + + nodes_per_list = (float)hash->count / (float)hash->hash_table_size; + if (nodes_per_list > 0.3 && nodes_per_list < 2.0) + return; + + next_size = I_MAX(primes_closest(hash->count + 1), hash->initial_size); + if (hash->hash_table_size >= next_size && + (grow || next_size == hash->hash_table_size)) + return; + + old_hash_table = hash->hash_table; + hash2_alloc_table(hash, next_size); + + old_count = array_count(&old_hash_table); + for (i = 0; i < old_count; i++) { + old_hash = array_idx_elem(&old_hash_table, i); + for (value = old_hash; value != NULL; value = next) { + next = value->next; + + idx = value->key_hash % hash->hash_table_size; + valuep = array_idx_modifiable(&hash->hash_table, idx); + value->next = *valuep; + *valuep = value; + } + } + array_free(&old_hash_table); +} + +void *hash2_lookup(const struct hash2_table *hash, const void *key) +{ + unsigned int key_hash = hash->key_hash_cb(key); + struct hash2_value *value; + void *user_value; + + value = array_idx_elem(&hash->hash_table, + key_hash % hash->hash_table_size); + while (value != NULL) { + if (value->key_hash == key_hash) { + user_value = value + 1; + if (hash->key_compare_cb(key, user_value, + hash->context)) + return user_value; + } + value = value->next; + } + return NULL; +} + +void *hash2_iterate(const struct hash2_table *hash, + unsigned int key_hash, struct hash2_iter *iter) +{ + struct hash2_value *value; + + if (iter->value == NULL) { + iter->key_hash = key_hash; + value = array_idx_elem(&hash->hash_table, + key_hash % hash->hash_table_size); + iter->next_value = value; + } + while (iter->next_value != NULL) { + if (iter->next_value->key_hash == key_hash) { + iter->value = iter->next_value; + iter->next_value = iter->next_value->next; + return iter->value + 1; + } + iter->next_value = iter->next_value->next; + } + return NULL; +} + +void *hash2_insert(struct hash2_table *hash, const void *key) +{ + return hash2_insert_hash(hash, hash->key_hash_cb(key)); +} + +void *hash2_insert_hash(struct hash2_table *hash, unsigned int key_hash) +{ + struct hash2_value *value, **valuep; + + hash2_resize(hash, TRUE); + + if (hash->deleted_values != NULL) { + value = hash->deleted_values; + hash->deleted_values = value->next; + value->next = NULL; + memset(value + 1, 0, hash->value_size); + } else { + value = p_malloc(hash->value_pool, + sizeof(*value) + hash->value_size); + } + value->key_hash = key_hash; + + valuep = array_idx_modifiable(&hash->hash_table, + key_hash % hash->hash_table_size); + value->next = *valuep; + *valuep = value; + + hash->count++; + return value + 1; +} + +static void +hash2_remove_value_p(struct hash2_table *hash, struct hash2_value **valuep) +{ + struct hash2_value *deleted_value; + + deleted_value = *valuep; + *valuep = deleted_value->next; + + deleted_value->next = hash->deleted_values; + hash->deleted_values = deleted_value; + + hash->count--; +} + +void hash2_remove(struct hash2_table *hash, const void *key) +{ + unsigned int key_hash = hash->key_hash_cb(key); + struct hash2_value **valuep; + + valuep = array_idx_modifiable(&hash->hash_table, + key_hash % hash->hash_table_size); + while (*valuep != NULL) { + if ((*valuep)->key_hash == key_hash && + hash->key_compare_cb(key, (*valuep) + 1, hash->context)) { + hash2_remove_value_p(hash, valuep); + hash2_resize(hash, FALSE); + return; + } + valuep = &(*valuep)->next; + } + i_panic("hash2_remove(): key not found"); +} + +void hash2_remove_iter(struct hash2_table *hash, struct hash2_iter *iter) +{ + struct hash2_value **valuep, *next; + + valuep = array_idx_modifiable(&hash->hash_table, + iter->key_hash % hash->hash_table_size); + while (*valuep != NULL) { + if (*valuep == iter->value) { + next = (*valuep)->next; + /* don't allow resizing, otherwise iterating would + break completely */ + hash2_remove_value_p(hash, valuep); + iter->next_value = next; + return; + } + valuep = &(*valuep)->next; + } + i_panic("hash2_remove_value(): key/value not found"); +} + +unsigned int hash2_count(const struct hash2_table *hash) +{ + return hash->count; +} diff --git a/src/lib/hash2.h b/src/lib/hash2.h new file mode 100644 index 0000000..d7febe6 --- /dev/null +++ b/src/lib/hash2.h @@ -0,0 +1,56 @@ +#ifndef HASH2_H +#define HASH2_H + +#include "hash.h" + +struct hash2_iter { + struct hash2_value *value, *next_value; + unsigned int key_hash; +}; + +/* Returns hash code for the key. */ +typedef unsigned int hash2_key_callback_t(const void *key); +/* Returns TRUE if the key matches the value. */ +typedef bool hash2_cmp_callback_t(const void *key, const void *value, + void *context); + +/* Create a new hash table. If initial_size is 0, the default value is used. */ +struct hash2_table * +hash2_create(unsigned int initial_size, unsigned int value_size, + hash2_key_callback_t *key_hash_cb, + hash2_cmp_callback_t *key_compare_cb, void *context) ATTR_NULL(5); +void hash2_destroy(struct hash2_table **hash); +/* Remove all nodes from hash table. */ +void hash2_clear(struct hash2_table *hash); + +void *hash2_lookup(const struct hash2_table *hash, const void *key) ATTR_PURE; +/* Iterate through all nodes with the given hash. iter must initially be + zero-filled. */ +void *hash2_iterate(const struct hash2_table *hash, + unsigned int key_hash, struct hash2_iter *iter); +/* Insert node to the hash table and returns pointer to the value that can be + written to. Assumes it doesn't already exist (or that a duplicate entry + is wanted). */ +void *hash2_insert(struct hash2_table *hash, const void *key); +/* Like hash2_insert(), but insert directly using a hash. */ +void *hash2_insert_hash(struct hash2_table *hash, unsigned int key_hash); +/* Remove a node. */ +void hash2_remove(struct hash2_table *hash, const void *key); +/* Remove the last node iterator returned. Iterating continues from the next + node. */ +void hash2_remove_iter(struct hash2_table *hash, struct hash2_iter *iter); +/* Return the number of nodes in hash table. */ +unsigned int hash2_count(const struct hash2_table *hash) ATTR_PURE; + +/* can be used with string keys */ +static inline bool hash2_strcmp(const void *a, const void *b, void *ctx ATTR_UNUSED) +{ + return strcmp(a, b) == 0; +} + +static inline unsigned int hash2_str_hash(const void *key) +{ + return str_hash(key); +} + +#endif diff --git a/src/lib/hex-binary.c b/src/lib/hex-binary.c new file mode 100644 index 0000000..a6d7849 --- /dev/null +++ b/src/lib/hex-binary.c @@ -0,0 +1,85 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "hex-binary.h" + +static void +binary_to_hex_case(unsigned char *dest, const unsigned char *data, + size_t size, bool ucase) +{ + unsigned char *p; + char base_char; + size_t i; + int value; + + /* @UNSAFE */ + base_char = ucase ? 'A' : 'a'; + + p = dest; + for (i = 0; i < size; i++) { + value = data[i] >> 4; + *p++ = value < 10 ? value + '0' : value - 10 + base_char; + + value = data[i] & 0x0f; + *p++ = value < 10 ? value + '0' : value - 10 + base_char; + } +} + +const char *binary_to_hex(const unsigned char *data, size_t size) +{ + unsigned char *dest = t_malloc_no0(MALLOC_MULTIPLY(size, 2) + 1); + + binary_to_hex_case(dest, data, size, FALSE); + dest[size*2] = '\0'; + return (char *)dest; +} + +const char *binary_to_hex_ucase(const unsigned char *data, size_t size) +{ + unsigned char *dest = t_malloc_no0(MALLOC_MULTIPLY(size, 2) + 1); + + binary_to_hex_case(dest, data, size, TRUE); + dest[size*2] = '\0'; + return (char *)dest; +} + +void binary_to_hex_append(string_t *dest, const unsigned char *data, + size_t size) +{ + unsigned char *buf; + + buf = buffer_append_space_unsafe(dest, size * 2); + binary_to_hex_case(buf, data, size, FALSE); +} + +int hex_to_binary(const char *data, buffer_t *dest) +{ + int value; + + while (*data != '\0') { + if (*data >= '0' && *data <= '9') + value = (*data - '0') << 4; + else if (*data >= 'a' && *data <= 'f') + value = (*data - 'a' + 10) << 4; + else if (*data >= 'A' && *data <= 'F') + value = (*data - 'A' + 10) << 4; + else + return -1; + + data++; + if (*data >= '0' && *data <= '9') + value |= *data - '0'; + else if (*data >= 'a' && *data <= 'f') + value |= *data - 'a' + 10; + else if (*data >= 'A' && *data <= 'F') + value |= *data - 'A' + 10; + else + return -1; + + buffer_append_c(dest, value); + data++; + } + + return 0; +} diff --git a/src/lib/hex-binary.h b/src/lib/hex-binary.h new file mode 100644 index 0000000..24c3a39 --- /dev/null +++ b/src/lib/hex-binary.h @@ -0,0 +1,15 @@ +#ifndef HEX_BINARY_H +#define HEX_BINARY_H + +/* Convert binary to hex digits allocating return value from data stack */ +const char *binary_to_hex(const unsigned char *data, size_t size); +const char *binary_to_hex_ucase(const unsigned char *data, size_t size); + +void binary_to_hex_append(string_t *dest, const unsigned char *data, + size_t size); + +/* Convert hex to binary. data and dest may point to same value. + Returns 0 if all ok, -1 if data is invalid. */ +int hex_to_binary(const char *data, buffer_t *dest); + +#endif diff --git a/src/lib/hex-dec.c b/src/lib/hex-dec.c new file mode 100644 index 0000000..1c68001 --- /dev/null +++ b/src/lib/hex-dec.c @@ -0,0 +1,38 @@ +/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "hex-dec.h" + +void dec2hex(unsigned char *hexstr, uintmax_t dec, unsigned int hexstr_size) +{ + unsigned int i; + + for (i = 0; i < hexstr_size; i++) { + unsigned int value = dec & 0x0f; + if (value < 10) + hexstr[hexstr_size-i-1] = value + '0'; + else + hexstr[hexstr_size-i-1] = value - 10 + 'A'; + dec >>= 4; + } +} + +uintmax_t hex2dec(const unsigned char *data, unsigned int len) +{ + unsigned int i; + uintmax_t value = 0; + + for (i = 0; i < len; i++) { + value = value*0x10; + if (data[i] >= '0' && data[i] <= '9') + value += data[i]-'0'; + else if (data[i] >= 'A' && data[i] <= 'F') + value += data[i]-'A' + 10; + else if (data[i] >= 'a' && data[i] <= 'f') + value += data[i]-'a' + 10; + else + return 0; + } + return value; +} + diff --git a/src/lib/hex-dec.h b/src/lib/hex-dec.h new file mode 100644 index 0000000..33e87bb --- /dev/null +++ b/src/lib/hex-dec.h @@ -0,0 +1,12 @@ +#ifndef HEX_DEC_H +#define HEX_DEC_H + +#define DEC2HEX(hexstr, str) \ + dec2hex(hexstr, str, sizeof(hexstr)) + +/* Decimal -> hex string translation. The result isn't NUL-terminated. */ +void dec2hex(unsigned char *hexstr, uintmax_t dec, unsigned int hexstr_size); +/* Parses hex string and returns its decimal value, or 0 in case of errors */ +uintmax_t hex2dec(const unsigned char *data, unsigned int len) ATTR_PURE; + +#endif diff --git a/src/lib/hmac-cram-md5.c b/src/lib/hmac-cram-md5.c new file mode 100644 index 0000000..46d73c4 --- /dev/null +++ b/src/lib/hmac-cram-md5.c @@ -0,0 +1,65 @@ +/* + * CRAM-MD5 (RFC 2195) compatibility code + * Copyright (c) 2003 Joshua Goodall <joshua@roughtrade.net> + * + * This software is released under the MIT license. + */ + +#include "lib.h" +#include "md5.h" +#include "hmac-cram-md5.h" + +void hmac_md5_get_cram_context(struct hmac_context *_hmac_ctx, + unsigned char context_digest[CRAM_MD5_CONTEXTLEN]) +{ + struct hmac_context_priv *hmac_ctx = &_hmac_ctx->u.priv; + unsigned char *cdp; + + struct md5_context *ctx = (void*)hmac_ctx->ctx; + struct md5_context *ctxo = (void*)hmac_ctx->ctxo; + +#define CDPUT(p, c) STMT_START { \ + *(p)++ = (c) & 0xff; \ + *(p)++ = (c) >> 8 & 0xff; \ + *(p)++ = (c) >> 16 & 0xff; \ + *(p)++ = (c) >> 24 & 0xff; \ +} STMT_END + cdp = context_digest; + CDPUT(cdp, ctxo->a); + CDPUT(cdp, ctxo->b); + CDPUT(cdp, ctxo->c); + CDPUT(cdp, ctxo->d); + CDPUT(cdp, ctx->a); + CDPUT(cdp, ctx->b); + CDPUT(cdp, ctx->c); + CDPUT(cdp, ctx->d); +} + +void hmac_md5_set_cram_context(struct hmac_context *_hmac_ctx, + const unsigned char context_digest[CRAM_MD5_CONTEXTLEN]) +{ + struct hmac_context_priv *hmac_ctx = &_hmac_ctx->u.priv; + const unsigned char *cdp; + + struct md5_context *ctx = (void*)hmac_ctx->ctx; + struct md5_context *ctxo = (void*)hmac_ctx->ctxo; + +#define CDGET(p, c) STMT_START { \ + (c) = (*p++); \ + (c) += (*p++ << 8); \ + (c) += (*p++ << 16); \ + (c) += ((uint32_t)(*p++) << 24); \ +} STMT_END + cdp = context_digest; + CDGET(cdp, ctxo->a); + CDGET(cdp, ctxo->b); + CDGET(cdp, ctxo->c); + CDGET(cdp, ctxo->d); + CDGET(cdp, ctx->a); + CDGET(cdp, ctx->b); + CDGET(cdp, ctx->c); + CDGET(cdp, ctx->d); + + ctxo->lo = ctx->lo = 64; + ctxo->hi = ctx->hi = 0; +} diff --git a/src/lib/hmac-cram-md5.h b/src/lib/hmac-cram-md5.h new file mode 100644 index 0000000..5b0a384 --- /dev/null +++ b/src/lib/hmac-cram-md5.h @@ -0,0 +1,14 @@ +#ifndef HMAC_CRAM_MD5_H +#define HMAC_CRAM_MD5_H + +#include "hmac.h" + +#define CRAM_MD5_CONTEXTLEN 32 + +void hmac_md5_get_cram_context(struct hmac_context *ctx, + unsigned char context_digest[CRAM_MD5_CONTEXTLEN]); +void hmac_md5_set_cram_context(struct hmac_context *ctx, + const unsigned char context_digest[CRAM_MD5_CONTEXTLEN]); + + +#endif diff --git a/src/lib/hmac.c b/src/lib/hmac.c new file mode 100644 index 0000000..0138a4a --- /dev/null +++ b/src/lib/hmac.c @@ -0,0 +1,152 @@ +/* + * HMAC (RFC-2104) implementation. + * + * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru> + * Copyright (c) 2011-2016 Florian Zeitz <florob@babelmonkeys.de> + * + * This software is released under the MIT license. + */ + +#include "lib.h" +#include "hmac.h" +#include "safe-memset.h" +#include "buffer.h" + +#include "hex-binary.h" + +void hmac_init(struct hmac_context *_ctx, const unsigned char *key, + size_t key_len, const struct hash_method *meth) +{ + struct hmac_context_priv *ctx = &_ctx->u.priv; + unsigned int i; + unsigned char k_ipad[meth->block_size]; + unsigned char k_opad[meth->block_size]; + unsigned char hashedkey[meth->digest_size]; + + i_assert(meth->context_size <= HMAC_MAX_CONTEXT_SIZE); + + ctx->hash = meth; + + if (key_len > meth->block_size) { + meth->init(ctx->ctx); + meth->loop(ctx->ctx, key, key_len); + meth->result(ctx->ctx, hashedkey); + key = hashedkey; + key_len = meth->digest_size; + } + + memcpy(k_ipad, key, key_len); + memset(k_ipad + key_len, 0, meth->block_size - key_len); + memcpy(k_opad, k_ipad, meth->block_size); + + for (i = 0; i < meth->block_size; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + meth->init(ctx->ctx); + meth->loop(ctx->ctx, k_ipad, meth->block_size); + meth->init(ctx->ctxo); + meth->loop(ctx->ctxo, k_opad, meth->block_size); + + safe_memset(k_ipad, 0, meth->block_size); + safe_memset(k_opad, 0, meth->block_size); +} + +void hmac_final(struct hmac_context *_ctx, unsigned char *digest) +{ + struct hmac_context_priv *ctx = &_ctx->u.priv; + + ctx->hash->result(ctx->ctx, digest); + + ctx->hash->loop(ctx->ctxo, digest, ctx->hash->digest_size); + ctx->hash->result(ctx->ctxo, digest); +} + +buffer_t *t_hmac_data(const struct hash_method *meth, + const unsigned char *key, size_t key_len, + const void *data, size_t data_len) +{ + struct hmac_context ctx; + i_assert(meth != NULL); + i_assert(key != NULL && key_len > 0); + i_assert(data != NULL || data_len == 0); + + buffer_t *res = t_buffer_create(meth->digest_size); + hmac_init(&ctx, key, key_len, meth); + if (data_len > 0) + hmac_update(&ctx, data, data_len); + unsigned char *buf = buffer_get_space_unsafe(res, 0, meth->digest_size); + hmac_final(&ctx, buf); + return res; +} + +buffer_t *t_hmac_buffer(const struct hash_method *meth, + const unsigned char *key, size_t key_len, + const buffer_t *data) +{ + return t_hmac_data(meth, key, key_len, data->data, data->used); +} + +buffer_t *t_hmac_str(const struct hash_method *meth, + const unsigned char *key, size_t key_len, + const char *data) +{ + return t_hmac_data(meth, key, key_len, data, strlen(data)); +} + +void hmac_hkdf(const struct hash_method *method, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + const unsigned char *info, size_t info_len, + buffer_t *okm_r, size_t okm_len) +{ + i_assert(method != NULL); + i_assert(okm_len < 255*method->digest_size); + struct hmac_context key_mac; + struct hmac_context info_mac; + size_t remain = okm_len; + unsigned char prk[method->digest_size]; + unsigned char okm[method->digest_size]; + /* N = ceil(L/HashLen) */ + unsigned int rounds = (okm_len + method->digest_size - 1)/method->digest_size; + + /* salt and info can be NULL */ + i_assert(salt != NULL || salt_len == 0); + i_assert(info != NULL || info_len == 0); + + i_assert(ikm != NULL && ikm_len > 0); + i_assert(okm_r != NULL && okm_len > 0); + + /* but they still need valid pointer, reduces + complains from static analysers */ + if (salt == NULL) + salt = &uchar_nul; + if (info == NULL) + info = &uchar_nul; + + /* extract */ + hmac_init(&key_mac, salt, salt_len, method); + hmac_update(&key_mac, ikm, ikm_len); + hmac_final(&key_mac, prk); + + /* expand */ + for (unsigned int i = 0; remain > 0 && i < rounds; i++) { + unsigned char round = (i+1); + size_t amt = remain; + if (amt > method->digest_size) + amt = method->digest_size; + hmac_init(&info_mac, prk, method->digest_size, method); + if (i > 0) + hmac_update(&info_mac, okm, method->digest_size); + hmac_update(&info_mac, info, info_len); + hmac_update(&info_mac, &round, 1); + memset(okm, 0, method->digest_size); + hmac_final(&info_mac, okm); + buffer_append(okm_r, okm, amt); + remain -= amt; + } + + safe_memset(prk, 0, sizeof(prk)); + safe_memset(okm, 0, sizeof(okm)); +} diff --git a/src/lib/hmac.h b/src/lib/hmac.h new file mode 100644 index 0000000..b32c039 --- /dev/null +++ b/src/lib/hmac.h @@ -0,0 +1,65 @@ +#ifndef HMAC_H +#define HMAC_H + +#include "hash-method.h" +#include "sha1.h" +#include "sha2.h" + +#define HMAC_MAX_CONTEXT_SIZE sizeof(struct sha512_ctx) + +struct hmac_context_priv { + char ctx[HMAC_MAX_CONTEXT_SIZE]; + char ctxo[HMAC_MAX_CONTEXT_SIZE]; + const struct hash_method *hash; +}; + +struct hmac_context { + union { + struct hmac_context_priv priv; + uint64_t padding_requirement; + } u; +}; + +void hmac_init(struct hmac_context *ctx, const unsigned char *key, + size_t key_len, const struct hash_method *meth); +void hmac_final(struct hmac_context *ctx, unsigned char *digest); + + +static inline void +hmac_update(struct hmac_context *_ctx, const void *data, size_t size) +{ + struct hmac_context_priv *ctx = &_ctx->u.priv; + + ctx->hash->loop(ctx->ctx, data, size); +} + +buffer_t *t_hmac_data(const struct hash_method *meth, + const unsigned char *key, size_t key_len, + const void *data, size_t data_len); +buffer_t *t_hmac_buffer(const struct hash_method *meth, + const unsigned char *key, size_t key_len, + const buffer_t *data); +buffer_t *t_hmac_str(const struct hash_method *meth, + const unsigned char *key, size_t key_len, + const char *data); + +void hmac_hkdf(const struct hash_method *method, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + const unsigned char *info, size_t info_len, + buffer_t *okm_r, size_t okm_len); + +static inline buffer_t * +t_hmac_hkdf(const struct hash_method *method, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + const unsigned char *info, size_t info_len, + size_t okm_len) +{ + buffer_t *okm_buffer = t_buffer_create(okm_len); + hmac_hkdf(method, salt, salt_len, ikm, ikm_len, info, info_len, + okm_buffer, okm_len); + return okm_buffer; +} + +#endif diff --git a/src/lib/home-expand.c b/src/lib/home-expand.c new file mode 100644 index 0000000..282ede9 --- /dev/null +++ b/src/lib/home-expand.c @@ -0,0 +1,72 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ipwd.h" +#include "home-expand.h" + + +int home_try_expand(const char **_path) +{ + const char *path = *_path; + const char *name, *home, *p; + struct passwd pw; + + if (path == NULL || *path != '~') + return 0; + + path++; + if (*path == '/' || *path == '\0') { + home = getenv("HOME"); + if (*path != '\0') path++; + } else { + p = strchr(path, '/'); + if (p == NULL) { + name = path; + path = ""; + } else { + name = t_strdup_until(path, p); + path = p+1; + } + switch (i_getpwnam(name, &pw)) { + case -1: + i_error("getpwnam(%s) failed: %m", name); + home = NULL; + break; + case 0: + home = NULL; + break; + default: + home = pw.pw_dir; + break; + } + } + + if (home == NULL) + return -1; + + if (*path == '\0') + *_path = t_strdup(home); + else + *_path = t_strconcat(home, "/", path, NULL); + return 0; +} + +const char *home_expand(const char *path) +{ + (void)home_try_expand(&path); + return path; +} + +const char *home_expand_tilde(const char *path, const char *home) +{ + if (path == NULL || *path != '~') + return path; + + if (path[1] == '\0') + return home; + if (path[1] != '/') + return path; + + /* ~/ used */ + return t_strconcat(home, path + 1, NULL); +} diff --git a/src/lib/home-expand.h b/src/lib/home-expand.h new file mode 100644 index 0000000..5ba2ff7 --- /dev/null +++ b/src/lib/home-expand.h @@ -0,0 +1,12 @@ +#ifndef HOME_EXPAND_H +#define HOME_EXPAND_H + +/* expand ~/ or ~user/ in beginning of path. If user is unknown, the original + path is returned without modification. */ +const char *home_expand(const char *path); +/* Returns 0 if ok, -1 if user wasn't found. */ +int home_try_expand(const char **path); +/* Expand ~/ in the beginning of the path with the give home directory. */ +const char *home_expand_tilde(const char *path, const char *home); + +#endif diff --git a/src/lib/hook-build.c b/src/lib/hook-build.c new file mode 100644 index 0000000..3b4a409 --- /dev/null +++ b/src/lib/hook-build.c @@ -0,0 +1,121 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "llist.h" +#include "hook-build.h" + +struct hook_stack { + struct hook_stack *prev, *next; + + /* Pointer to vfuncs struct. This assumes that a struct containing + function pointers equals to an array of function pointers. Not + ANSI-C, but should work in all OSes supported by Dovecot. Much + easier anyway than doing this work manually.. */ + void (**vfuncs)(); + /* nonzero in the areas where vfuncs has been changed */ + void (**mask)(); +}; + +struct hook_build_context { + pool_t pool; + /* size of the vfuncs struct */ + size_t size; + /* number of function pointers in the struct */ + unsigned int count; + + struct hook_stack *head, *tail; +}; + +static void hook_build_append(struct hook_build_context *ctx, void (**vfuncs)()) +{ + struct hook_stack *stack; + + stack = p_new(ctx->pool, struct hook_stack, 1); + stack->vfuncs = vfuncs; + stack->mask = p_malloc(ctx->pool, ctx->size); + DLLIST2_APPEND(&ctx->head, &ctx->tail, stack); +} + +struct hook_build_context *hook_build_init(void (**vfuncs)(), size_t size) +{ + struct hook_build_context *ctx; + pool_t pool; + + i_assert((size % sizeof(void (*)())) == 0); + + pool = pool_alloconly_create("hook build context", 2048); + ctx = p_new(pool, struct hook_build_context, 1); + ctx->pool = pool; + ctx->size = size; + ctx->count = size / sizeof(void (*)()); + hook_build_append(ctx, vfuncs); + return ctx; +} + +static void +hook_update_mask(struct hook_build_context *ctx, struct hook_stack *stack, + void (**vlast)()) +{ + unsigned int i; + + for (i = 0; i < ctx->count; i++) { + if (stack->vfuncs[i] != vlast[i]) { + i_assert(stack->vfuncs[i] != NULL); + stack->mask[i] = stack->vfuncs[i]; + } + } +} + +static void +hook_copy_stack(struct hook_build_context *ctx, struct hook_stack *stack) +{ + unsigned int i; + + i_assert(stack->next != NULL); + + for (i = 0; i < ctx->count; i++) { + if (stack->mask[i] == NULL) { + stack->vfuncs[i] = stack->next->vfuncs[i]; + stack->mask[i] = stack->next->mask[i]; + } + } +} + +void hook_build_update(struct hook_build_context *ctx, void *_vlast) +{ + void (**vlast)() = _vlast; + struct hook_stack *stack; + + if (ctx->tail->vfuncs == vlast) { + /* no vfuncs overridden */ + return; + } + + /* ctx->vfuncs_stack->vfuncs points to the root vfuncs, + ctx->vfuncs_stack->next->vfuncs points to the first super function + that is being called, and so on. + + the previous plugin added its vfuncs to the stack tail. + vlast contains the previous plugin's super vfuncs, which is where + the next plugin should put its own vfuncs. + + first we'll need to figure out what vfuncs the previous plugin + changed and update the mask */ + hook_update_mask(ctx, ctx->tail, vlast); + + /* now go up in the stack as long as the mask isn't set, + and update the vfuncs */ + for (stack = ctx->tail->prev; stack != NULL; stack = stack->prev) + hook_copy_stack(ctx, stack); + + /* add vlast to stack */ + hook_build_append(ctx, vlast); +} + +void hook_build_deinit(struct hook_build_context **_ctx) +{ + struct hook_build_context *ctx = *_ctx; + *_ctx = NULL; + pool_unref(&ctx->pool); +} diff --git a/src/lib/hook-build.h b/src/lib/hook-build.h new file mode 100644 index 0000000..03c1579 --- /dev/null +++ b/src/lib/hook-build.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ +#ifndef HOOK_BUILD_H +#define HOOK_BUILD_H 1 + +struct hook_build_context; +struct hook_stack; + +/* Initialize new hook building context, vfuncs should point to + the functions table that is being manipulated, and size should be + the size of this table. */ +struct hook_build_context *hook_build_init(void (**vfuncs)(), size_t size); + +/* This is called after a hook may have updated vfuncs */ +void hook_build_update(struct hook_build_context *ctx, void *_vlast); + +/* Free memory used by build context */ +void hook_build_deinit(struct hook_build_context **_ctx); + +#endif diff --git a/src/lib/hostpid.c b/src/lib/hostpid.c new file mode 100644 index 0000000..3e91ade --- /dev/null +++ b/src/lib/hostpid.c @@ -0,0 +1,69 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "hostpid.h" + +#include <unistd.h> +#include <netdb.h> + +#define HOSTNAME_DISALLOWED_CHARS "/\r\n\t" + +const char *my_hostname = NULL; +const char *my_pid = NULL; + +static char *my_hostname_dup = NULL; +static char *my_domain = NULL; + +void hostpid_init(void) +{ + static char pid[MAX_INT_STRLEN]; + char hostname[256]; + const char *value; + + /* allow calling hostpid_init() multiple times to reset hostname */ + i_free_and_null(my_hostname_dup); + i_free_and_null(my_domain); + + value = getenv(MY_HOSTNAME_ENV); + if (value == NULL) { + if (gethostname(hostname, sizeof(hostname)-1) < 0) + i_fatal("gethostname() failed: %m"); + hostname[sizeof(hostname)-1] = '\0'; + value = hostname; + } + + if (value[0] == '\0' || + strcspn(value, HOSTNAME_DISALLOWED_CHARS) != strlen(value)) + i_fatal("Invalid system hostname: '%s'", value); + my_hostname_dup = i_strdup(value); + my_hostname = my_hostname_dup; + + i_snprintf(pid, sizeof(pid), "%lld", (long long)getpid()); + my_pid = pid; +} + +void hostpid_deinit(void) +{ + i_free(my_hostname_dup); + i_free(my_domain); +} + +const char *my_hostdomain(void) +{ + struct hostent *hent; + const char *name; + + if (my_domain == NULL) { + name = getenv(MY_HOSTDOMAIN_ENV); + if (name == NULL) { + hent = gethostbyname(my_hostname); + name = hent != NULL ? hent->h_name : NULL; + if (name == NULL) { + /* failed, use just the hostname */ + name = my_hostname; + } + } + my_domain = i_strdup(name); + } + return my_domain; +} diff --git a/src/lib/hostpid.h b/src/lib/hostpid.h new file mode 100644 index 0000000..0581e78 --- /dev/null +++ b/src/lib/hostpid.h @@ -0,0 +1,21 @@ +#ifndef HOSTPID_H +#define HOSTPID_H + +/* These environments override the hostname/hostdomain if they're set. + Master process normally sets these to child processes. */ +#define MY_HOSTNAME_ENV "DOVECOT_HOSTNAME" +#define MY_HOSTDOMAIN_ENV "DOVECOT_HOSTDOMAIN" + +extern const char *my_hostname; +extern const char *my_pid; + +/* Initializes my_hostname and my_pid. */ +void hostpid_init(void); +void hostpid_deinit(void); + +/* Returns the current host+domain, or if it fails fallback to returning + hostname. */ +const char *my_hostdomain(void); + +#endif + diff --git a/src/lib/imem.c b/src/lib/imem.c new file mode 100644 index 0000000..a991a92 --- /dev/null +++ b/src/lib/imem.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" + +pool_t default_pool = &static_system_pool; + +void *i_malloc(size_t size) +{ + return p_malloc(default_pool, size); +} + +void *i_realloc(void *mem, size_t old_size, size_t new_size) +{ + return p_realloc(default_pool, mem, old_size, new_size); +} + +char *i_strdup(const char *str) +{ + return p_strdup(default_pool, str); +} + +void *i_memdup(const void *data, size_t size) +{ + return p_memdup(default_pool, data, size); +} + +char *i_strdup_empty(const char *str) +{ + return p_strdup_empty(default_pool, str); +} + +char *i_strdup_until(const void *str, const void *end) +{ + return p_strdup_until(default_pool, str, end); +} + +char *i_strndup(const void *str, size_t max_chars) +{ + i_assert(str != NULL); + return p_strndup(default_pool, str, max_chars); +} + +char *i_strdup_printf(const char *format, ...) +{ + va_list args; + char *ret; + + va_start(args, format); + ret = p_strdup_vprintf(default_pool, format, args); + va_end(args); + return ret; +} + +char *i_strdup_vprintf(const char *format, va_list args) +{ + return p_strdup_vprintf(default_pool, format, args); +} + +char *i_strconcat(const char *str1, ...) +{ + va_list args; + char *ret; + size_t len; + + i_assert(str1 != NULL); + + va_start(args, str1); + + T_BEGIN { + const char *temp = vstrconcat(str1, args, &len); + t_buffer_alloc(len); + ret = p_malloc(default_pool, len); + memcpy(ret, temp, len); + } T_END; + + va_end(args); + return ret; +} diff --git a/src/lib/imem.h b/src/lib/imem.h new file mode 100644 index 0000000..ed8c2eb --- /dev/null +++ b/src/lib/imem.h @@ -0,0 +1,45 @@ +#ifndef IMEM_H +#define IMEM_H + +/* For easy allocation of memory from default memory pool. */ + +extern pool_t default_pool; + +#define i_new(type, count) p_new(default_pool, type, count) +#define i_realloc_type(mem, type, old_count, new_count) \ + p_realloc_type(default_pool, mem, type, old_count, new_count) + +void *i_malloc(size_t size) ATTR_MALLOC ATTR_RETURNS_NONNULL; +void *i_realloc(void *mem, size_t old_size, size_t new_size) + ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; + +/* i_free() and i_free_and_null() are now guaranteed to both set mem=NULL, + so either one of them can be used. */ +#ifndef STATIC_CHECKER +# define i_free(mem) p_free_and_null(default_pool, mem) +#else +# define i_free(mem) \ + STMT_START { \ + pool_system_free(default_pool, mem); \ + (mem) = NULL; \ + } STMT_END +#endif +#define i_free_and_null(mem) i_free(mem) + +/* string functions */ +char *i_strdup(const char *str) ATTR_MALLOC; +void *i_memdup(const void *data, size_t size) ATTR_MALLOC; +/* like i_strdup(), but if str == "", return NULL */ +char *i_strdup_empty(const char *str) ATTR_MALLOC; +/* *end isn't included */ +char *i_strdup_until(const void *str, const void *end) + ATTR_MALLOC ATTR_RETURNS_NONNULL; +char *i_strndup(const void *str, size_t max_chars) ATTR_MALLOC; +char *i_strdup_printf(const char *format, ...) + ATTR_FORMAT(1, 2) ATTR_MALLOC ATTR_RETURNS_NONNULL; +char *i_strdup_vprintf(const char *format, va_list args) + ATTR_FORMAT(1, 0) ATTR_MALLOC ATTR_RETURNS_NONNULL; + +char *i_strconcat(const char *str1, ...) ATTR_SENTINEL ATTR_MALLOC; + +#endif diff --git a/src/lib/ioloop-epoll.c b/src/lib/ioloop-epoll.c new file mode 100644 index 0000000..ad41008 --- /dev/null +++ b/src/lib/ioloop-epoll.c @@ -0,0 +1,230 @@ +/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "sleep.h" +#include "ioloop-private.h" +#include "ioloop-iolist.h" + +#ifdef IOLOOP_EPOLL + +#include <sys/epoll.h> +#include <unistd.h> + +struct ioloop_handler_context { + int epfd; + + unsigned int deleted_count; + ARRAY(struct io_list *) fd_index; + ARRAY(struct epoll_event) events; +}; + +void io_loop_handler_init(struct ioloop *ioloop, unsigned int initial_fd_count) +{ + struct ioloop_handler_context *ctx; + + ioloop->handler_context = ctx = i_new(struct ioloop_handler_context, 1); + + i_array_init(&ctx->events, initial_fd_count); + i_array_init(&ctx->fd_index, initial_fd_count); + + ctx->epfd = epoll_create(initial_fd_count); + if (ctx->epfd < 0) { + if (errno != EMFILE) + i_fatal("epoll_create(): %m"); + else { + i_fatal("epoll_create(): %m (you may need to increase " + "/proc/sys/fs/epoll/max_user_instances)"); + } + } + fd_close_on_exec(ctx->epfd, TRUE); +} + +void io_loop_handler_deinit(struct ioloop *ioloop) +{ + struct ioloop_handler_context *ctx = ioloop->handler_context; + struct io_list **list; + unsigned int i, count; + + list = array_get_modifiable(&ctx->fd_index, &count); + for (i = 0; i < count; i++) + i_free(list[i]); + + if (close(ctx->epfd) < 0) + i_error("close(epoll) failed: %m"); + array_free(&ioloop->handler_context->fd_index); + array_free(&ioloop->handler_context->events); + i_free(ioloop->handler_context); +} + +#define IO_EPOLL_ERROR (EPOLLERR | EPOLLHUP) +#define IO_EPOLL_INPUT (EPOLLIN | EPOLLPRI | IO_EPOLL_ERROR) +#define IO_EPOLL_OUTPUT (EPOLLOUT | IO_EPOLL_ERROR) + +static int epoll_event_mask(struct io_list *list) +{ + int events = 0, i; + struct io_file *io; + + for (i = 0; i < IOLOOP_IOLIST_IOS_PER_FD; i++) { + io = list->ios[i]; + + if (io == NULL) + continue; + + if ((io->io.condition & IO_READ) != 0) + events |= IO_EPOLL_INPUT; + if ((io->io.condition & IO_WRITE) != 0) + events |= IO_EPOLL_OUTPUT; + if ((io->io.condition & IO_ERROR) != 0) + events |= IO_EPOLL_ERROR; + } + + return events; +} + +void io_loop_handle_add(struct io_file *io) +{ + struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; + struct io_list **list; + struct epoll_event event; + int op; + bool first; + + list = array_idx_get_space(&ctx->fd_index, io->fd); + if (*list == NULL) + *list = i_new(struct io_list, 1); + + first = ioloop_iolist_add(*list, io); + + i_zero(&event); + event.data.ptr = *list; + event.events = epoll_event_mask(*list); + + op = first ? EPOLL_CTL_ADD : EPOLL_CTL_MOD; + + if (epoll_ctl(ctx->epfd, op, io->fd, &event) < 0) { + if (errno == EPERM && op == EPOLL_CTL_ADD) { + i_panic("epoll_ctl(add, %d) failed: %m " + "(fd doesn't support epoll%s)", io->fd, + io->fd != STDIN_FILENO ? "" : + " - instead of '<file', try 'cat file|'"); + } + i_panic("epoll_ctl(%s, %d) failed: %m", + op == EPOLL_CTL_ADD ? "add" : "mod", io->fd); + } + + if (first) { + /* allow epoll_wait() to return the maximum number of events + by keeping space allocated for each file descriptor */ + if (ctx->deleted_count > 0) + ctx->deleted_count--; + else + array_append_zero(&ctx->events); + } +} + +void io_loop_handle_remove(struct io_file *io, bool closed) +{ + struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; + struct io_list **list; + struct epoll_event event; + int op; + bool last; + + list = array_idx_modifiable(&ctx->fd_index, io->fd); + last = ioloop_iolist_del(*list, io); + + if (!closed) { + i_zero(&event); + event.data.ptr = *list; + event.events = epoll_event_mask(*list); + + op = last ? EPOLL_CTL_DEL : EPOLL_CTL_MOD; + + if (epoll_ctl(ctx->epfd, op, io->fd, &event) < 0) { + const char *errstr = t_strdup_printf( + "epoll_ctl(%s, %d) failed: %m", + op == EPOLL_CTL_DEL ? "del" : "mod", io->fd); + if (errno != ENOSPC && errno != ENOMEM) + i_panic("%s", errstr); + else + i_error("%s", errstr); + } + } + if (last) { + /* since we're not freeing memory in any case, just increase + deleted counter so next handle_add() can just decrease it + instead of appending to the events array */ + ctx->deleted_count++; + } + i_free(io); +} + +void io_loop_handler_run_internal(struct ioloop *ioloop) +{ + struct ioloop_handler_context *ctx = ioloop->handler_context; + struct epoll_event *events; + const struct epoll_event *event; + struct io_list *list; + struct io_file *io; + struct timeval tv; + unsigned int events_count; + int msecs, ret, i, j; + bool call; + + i_assert(ctx != NULL); + + /* get the time left for next timeout task */ + msecs = io_loop_run_get_wait_time(ioloop, &tv); + + events = array_get_modifiable(&ctx->events, &events_count); + if (ioloop->io_files != NULL && events_count > ctx->deleted_count) { + ret = epoll_wait(ctx->epfd, events, events_count, msecs); + if (ret < 0 && errno != EINTR) + i_fatal("epoll_wait(): %m"); + } else { + /* no I/Os, but we should have some timeouts. + just wait for them. */ + i_assert(msecs >= 0); + i_sleep_intr_msecs(msecs); + ret = 0; + } + + /* execute timeout handlers */ + io_loop_handle_timeouts(ioloop); + + if (!ioloop->running) + return; + + for (i = 0; i < ret; i++) { + /* io_loop_handle_add() may cause events array reallocation, + so we have use array_idx() */ + event = array_idx(&ctx->events, i); + list = event->data.ptr; + + for (j = 0; j < IOLOOP_IOLIST_IOS_PER_FD; j++) { + io = list->ios[j]; + if (io == NULL) + continue; + + call = FALSE; + if ((event->events & (EPOLLHUP | EPOLLERR)) != 0) + call = TRUE; + else if ((io->io.condition & IO_READ) != 0) + call = (event->events & EPOLLIN) != 0; + else if ((io->io.condition & IO_WRITE) != 0) + call = (event->events & EPOLLOUT) != 0; + else if ((io->io.condition & IO_ERROR) != 0) + call = (event->events & IO_EPOLL_ERROR) != 0; + + if (call) { + io_loop_call_io(&io->io); + if (!ioloop->running) + return; + } + } + } +} + +#endif /* IOLOOP_EPOLL */ diff --git a/src/lib/ioloop-iolist.c b/src/lib/ioloop-iolist.c new file mode 100644 index 0000000..28229af --- /dev/null +++ b/src/lib/ioloop-iolist.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru> + * + * This software is released under the MIT license. + */ + +#include "lib.h" +#include "ioloop-private.h" +#include "ioloop-iolist.h" + +bool ioloop_iolist_add(struct io_list *list, struct io_file *io) +{ + int i, idx; + + if ((io->io.condition & IO_READ) != 0) + idx = IOLOOP_IOLIST_INPUT; + else if ((io->io.condition & IO_WRITE) != 0) + idx = IOLOOP_IOLIST_OUTPUT; + else if ((io->io.condition & IO_ERROR) != 0) + idx = IOLOOP_IOLIST_ERROR; + else { + i_unreached(); + } + + if (list->ios[idx] != NULL) { + i_panic("io_add(0x%x) called twice fd=%d, callback=%p -> %p", + io->io.condition, io->fd, list->ios[idx]->io.callback, + io->io.callback); + } + i_assert(list->ios[idx] == NULL); + list->ios[idx] = io; + + /* check if this was the first one */ + for (i = 0; i < IOLOOP_IOLIST_IOS_PER_FD; i++) { + if (i != idx && list->ios[i] != NULL) + return FALSE; + } + + return TRUE; +} + +bool ioloop_iolist_del(struct io_list *list, struct io_file *io) +{ + bool last = TRUE; + int i; + + for (i = 0; i < IOLOOP_IOLIST_IOS_PER_FD; i++) { + if (list->ios[i] != NULL) { + if (list->ios[i] == io) + list->ios[i] = NULL; + else + last = FALSE; + } + } + return last; +} diff --git a/src/lib/ioloop-iolist.h b/src/lib/ioloop-iolist.h new file mode 100644 index 0000000..24fa871 --- /dev/null +++ b/src/lib/ioloop-iolist.h @@ -0,0 +1,19 @@ +#ifndef IOLOOP_IOLIST_H +#define IOLOOP_IOLIST_H + +enum { + IOLOOP_IOLIST_INPUT, + IOLOOP_IOLIST_OUTPUT, + IOLOOP_IOLIST_ERROR, + + IOLOOP_IOLIST_IOS_PER_FD +}; + +struct io_list { + struct io_file *ios[IOLOOP_IOLIST_IOS_PER_FD]; +}; + +bool ioloop_iolist_add(struct io_list *list, struct io_file *io); +bool ioloop_iolist_del(struct io_list *list, struct io_file *io); + +#endif diff --git a/src/lib/ioloop-kqueue.c b/src/lib/ioloop-kqueue.c new file mode 100644 index 0000000..061b1b1 --- /dev/null +++ b/src/lib/ioloop-kqueue.c @@ -0,0 +1,175 @@ +/* + * BSD kqueue() based ioloop handler. + * + * Copyright (c) 2005 Vaclav Haisman <v.haisman@sh.cvut.cz> + */ + +#include "lib.h" + +#ifdef IOLOOP_KQUEUE + +#include "array.h" +#include "sleep.h" +#include "ioloop-private.h" + +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/event.h> +#include <sys/time.h> + +/* kevent.udata's type just has to be different in NetBSD than in + FreeBSD and OpenBSD.. */ +#ifdef __NetBSD__ +# define MY_EV_SET(a, b, c, d, e, f, g) \ + EV_SET(a, b, c, d, e, f, (intptr_t)g) +#else +# define MY_EV_SET(a, b, c, d, e, f, g) \ + EV_SET(a, b, c, d, e, f, g) +#endif + +struct ioloop_handler_context { + int kq; + + unsigned int deleted_count; + ARRAY(struct kevent) events; +}; + +void io_loop_handler_init(struct ioloop *ioloop, unsigned int initial_fd_count) +{ + struct ioloop_handler_context *ctx; + + ioloop->handler_context = ctx = i_new(struct ioloop_handler_context, 1); + ctx->kq = kqueue(); + if (ctx->kq < 0) + i_fatal("kqueue() in io_loop_handler_init() failed: %m"); + fd_close_on_exec(ctx->kq, TRUE); + + i_array_init(&ctx->events, initial_fd_count); +} + +void io_loop_handler_deinit(struct ioloop *ioloop) +{ + if (close(ioloop->handler_context->kq) < 0) + i_error("close(kqueue) in io_loop_handler_deinit() failed: %m"); + array_free(&ioloop->handler_context->events); + i_free(ioloop->handler_context); +} + +void io_loop_handle_add(struct io_file *io) +{ + struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; + struct kevent ev; + + if ((io->io.condition & (IO_READ | IO_ERROR)) != 0) { + MY_EV_SET(&ev, io->fd, EVFILT_READ, EV_ADD, 0, 0, io); + if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) + i_panic("kevent(EV_ADD, READ, %d) failed: %m", io->fd); + } + if ((io->io.condition & IO_WRITE) != 0) { + MY_EV_SET(&ev, io->fd, EVFILT_WRITE, EV_ADD, 0, 0, io); + if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) + i_panic("kevent(EV_ADD, WRITE, %d) failed: %m", io->fd); + } + + /* allow kevent() to return the maximum number of events + by keeping space allocated for each handle */ + if (ctx->deleted_count > 0) + ctx->deleted_count--; + else + array_append_zero(&ctx->events); +} + +void io_loop_handle_remove(struct io_file *io, bool closed) +{ + struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; + struct kevent ev; + + i_assert(io->io.condition != 0); + if ((io->io.condition & (IO_READ | IO_ERROR)) != 0 && !closed) { + MY_EV_SET(&ev, io->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); + if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) + i_error("kevent(EV_DELETE, %d) failed: %m", io->fd); + } + if ((io->io.condition & IO_WRITE) != 0 && !closed) { + MY_EV_SET(&ev, io->fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) + i_error("kevent(EV_DELETE, %d) failed: %m", io->fd); + } + io->io.condition = 0; + + /* since we're not freeing memory in any case, just increase + deleted counter so next handle_add() can just decrease it + instead of appending to the events array */ + ctx->deleted_count++; + + i_assert(io->refcount > 0); + if (--io->refcount == 0) + i_free(io); +} + +void io_loop_handler_run_internal(struct ioloop *ioloop) +{ + struct ioloop_handler_context *ctx = ioloop->handler_context; + struct kevent *events; + const struct kevent *event; + struct timeval tv; + struct timespec ts; + struct io_file *io; + unsigned int events_count; + int ret, i, msecs; + + /* get the time left for next timeout task */ + msecs = io_loop_run_get_wait_time(ioloop, &tv); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + + /* wait for events */ + events = array_get_modifiable(&ctx->events, &events_count); + + if (events_count > 0) { + ret = kevent (ctx->kq, NULL, 0, events, events_count, &ts); + if (ret < 0 && errno != EINTR) { + i_panic("kevent(events=%u, ts=%ld.%u) failed: %m", + events_count, (long)ts.tv_sec, + (unsigned int)ts.tv_nsec); + } + } else { + i_assert(msecs >= 0); + i_sleep_intr_msecs(msecs); + ret = 0; + } + + /* reference all IOs */ + for (i = 0; i < ret; i++) { + io = (void *)events[i].udata; + i_assert(io->refcount > 0); + io->refcount++; + } + + /* execute timeout handlers */ + io_loop_handle_timeouts(ioloop); + + if (!ioloop->running) + return; + + for (i = 0; i < ret; i++) { + /* io_loop_handle_add() may cause events array reallocation, + so we have use array_idx() */ + event = array_idx(&ctx->events, i); + io = (void *)event->udata; + + /* callback is NULL if io_remove() was already called */ + if (io->io.callback != NULL) { + io_loop_call_io(&io->io); + if (!ioloop->running) + break; + } + + i_assert(io->refcount > 0); + if (--io->refcount == 0) + i_free(io); + } +} + +#endif diff --git a/src/lib/ioloop-notify-fd.c b/src/lib/ioloop-notify-fd.c new file mode 100644 index 0000000..d3ec29b --- /dev/null +++ b/src/lib/ioloop-notify-fd.c @@ -0,0 +1,55 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop-private.h" +#include "ioloop-notify-fd.h" + +#if defined(IOLOOP_NOTIFY_INOTIFY) + +struct io *io_notify_fd_add(struct ioloop_notify_fd_context *ctx, int fd, + io_callback_t *callback, void *context) +{ + struct io_notify *io; + + io = i_new(struct io_notify, 1); + io->io.condition = IO_NOTIFY; + io->io.callback = callback; + io->io.context = context; + io->io.ioloop = current_ioloop; + io->fd = fd; + + if (ctx->notifies != NULL) { + ctx->notifies->prev = io; + io->next = ctx->notifies; + } + ctx->notifies = io; + return &io->io; +} + +void io_notify_fd_free(struct ioloop_notify_fd_context *ctx, + struct io_notify *io) +{ + if (io->prev != NULL) + io->prev->next = io->next; + else + ctx->notifies = io->next; + + if (io->next != NULL) + io->next->prev = io->prev; + i_free(io); +} + +struct io_notify * +io_notify_fd_find(struct ioloop_notify_fd_context *ctx, int fd) +{ + struct io_notify *io; + + for (io = ctx->notifies; io != NULL; io = io->next) { + if (io->fd == fd) + return io; + } + + return NULL; +} + +#endif diff --git a/src/lib/ioloop-notify-fd.h b/src/lib/ioloop-notify-fd.h new file mode 100644 index 0000000..95bbb73 --- /dev/null +++ b/src/lib/ioloop-notify-fd.h @@ -0,0 +1,28 @@ +#ifndef IOLOOP_NOTIFY_FD_H +#define IOLOOP_NOTIFY_FD_H + +/* common notify code for fd-based notifications (dnotify, inotify) */ + +struct io_notify { + struct io io; + + /* use a doubly linked list so that io_remove() is quick */ + struct io_notify *prev, *next; + + int fd; +}; + +struct ioloop_notify_fd_context { + struct io_notify *notifies; +}; + +struct io * +io_notify_fd_add(struct ioloop_notify_fd_context *ctx, int fd, + io_callback_t *callback, void *context) ATTR_NULL(4); +void io_notify_fd_free(struct ioloop_notify_fd_context *ctx, + struct io_notify *io); + +struct io_notify * +io_notify_fd_find(struct ioloop_notify_fd_context *ctx, int fd); + +#endif diff --git a/src/lib/ioloop-notify-inotify.c b/src/lib/ioloop-notify-inotify.c new file mode 100644 index 0000000..796ac31 --- /dev/null +++ b/src/lib/ioloop-notify-inotify.c @@ -0,0 +1,237 @@ +/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */ + +#define _GNU_SOURCE +#include "lib.h" + +#ifdef IOLOOP_NOTIFY_INOTIFY + +#include "ioloop-private.h" +#include "ioloop-notify-fd.h" +#include "buffer.h" +#include "net.h" +#include "ipwd.h" +#include "time-util.h" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/inotify.h> + +#define INOTIFY_BUFLEN (32*1024) + +struct ioloop_notify_handler_context { + struct ioloop_notify_fd_context fd_ctx; + + int inotify_fd; + struct io *event_io; + + bool disabled; +}; + +static struct ioloop_notify_handler_context *io_loop_notify_handler_init(void); + +static bool inotify_input_more(struct ioloop *ioloop) +{ + struct ioloop_notify_handler_context *ctx = + ioloop->notify_handler_context; + const struct inotify_event *event; + unsigned char event_buf[INOTIFY_BUFLEN]; + struct io_notify *io; + ssize_t ret, pos; + + /* read as many events as there is available and fit into our buffer. + only full events are returned by the kernel. */ + ret = read(ctx->inotify_fd, event_buf, sizeof(event_buf)); + if (ret <= 0) { + if (ret == 0 || errno == EAGAIN) { + /* nothing more to read */ + return FALSE; + } + i_fatal("read(inotify) failed: %m"); + } + + i_gettimeofday(&ioloop_timeval); + ioloop_time = ioloop_timeval.tv_sec; + + for (pos = 0; pos < ret; ) { + if ((size_t)(ret - pos) < sizeof(*event)) + break; + + event = (struct inotify_event *)(event_buf + pos); + i_assert(event->len < (size_t)ret); + pos += sizeof(*event) + event->len; + + io = io_notify_fd_find(&ctx->fd_ctx, event->wd); + if (io != NULL) { + if ((event->mask & IN_IGNORED) != 0) { + /* calling inotify_rm_watch() would now give + EINVAL */ + io->fd = -1; + } + io_loop_call_io(&io->io); + } + } + if (pos != ret) + i_error("read(inotify) returned partial event"); + return (size_t)ret >= sizeof(event_buf)-512; +} + +static void inotify_input(struct ioloop *ioloop) +{ + while (inotify_input_more(ioloop)) ; +} + +#undef io_add_notify +enum io_notify_result +io_add_notify(const char *path, const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context, struct io **io_r) +{ + struct ioloop_notify_handler_context *ctx = + current_ioloop->notify_handler_context; + int wd; + + *io_r = NULL; + + if (ctx == NULL) + ctx = io_loop_notify_handler_init(); + if (ctx->disabled) + return IO_NOTIFY_NOSUPPORT; + + wd = inotify_add_watch(ctx->inotify_fd, path, + IN_CREATE | IN_DELETE | IN_DELETE_SELF | + IN_MOVE | IN_MODIFY); + if (wd < 0) { + /* ESTALE could happen with NFS. Don't bother giving an error + message then. */ + if (errno == ENOENT || errno == ESTALE) + return IO_NOTIFY_NOTFOUND; + + if (errno != ENOSPC) + i_error("inotify_add_watch(%s) failed: %m", path); + else { + i_warning("Inotify watch limit for user exceeded, " + "disabling. Increase " + "/proc/sys/fs/inotify/max_user_watches"); + } + ctx->disabled = TRUE; + return IO_NOTIFY_NOSUPPORT; + } + + if (ctx->event_io == NULL) { + ctx->event_io = io_add(ctx->inotify_fd, IO_READ, + inotify_input, current_ioloop); + } + + *io_r = io_notify_fd_add(&ctx->fd_ctx, wd, callback, context); + (*io_r)->source_filename = source_filename; + (*io_r)->source_linenum = source_linenum; + return IO_NOTIFY_ADDED; +} + +void io_loop_notify_remove(struct io *_io) +{ + struct ioloop_notify_handler_context *ctx = + _io->ioloop->notify_handler_context; + struct io_notify *io = (struct io_notify *)_io; + + if (io->fd != -1) { + /* ernro=EINVAL happens if the file itself is deleted and + kernel has sent IN_IGNORED event which we haven't read. */ + if (inotify_rm_watch(ctx->inotify_fd, io->fd) < 0 && + errno != EINVAL) + i_error("inotify_rm_watch() failed: %m"); + } + + io_notify_fd_free(&ctx->fd_ctx, io); + + if (ctx->fd_ctx.notifies == NULL && ctx->event_io != NULL) + io_remove(&ctx->event_io); +} + +static void ioloop_inotify_user_limit_exceeded(void) +{ + struct passwd pw; + const char *name; + uid_t uid = geteuid(); + + if (i_getpwuid(uid, &pw) <= 0) + name = t_strdup_printf("UID %s", dec2str(uid)); + else { + name = t_strdup_printf("%s (UID %s)", + dec2str(uid), pw.pw_name); + } + i_warning("Inotify instance limit for user %s exceeded, disabling. " + "Increase /proc/sys/fs/inotify/max_user_instances", name); +} + +static struct ioloop_notify_handler_context *io_loop_notify_handler_init(void) +{ + struct ioloop *ioloop = current_ioloop; + struct ioloop_notify_handler_context *ctx; + + ctx = ioloop->notify_handler_context = + i_new(struct ioloop_notify_handler_context, 1); + + ctx->inotify_fd = inotify_init(); + if (ctx->inotify_fd == -1) { + if (errno != EMFILE) + i_error("inotify_init() failed: %m"); + else + ioloop_inotify_user_limit_exceeded(); + ctx->disabled = TRUE; + } else { + fd_close_on_exec(ctx->inotify_fd, TRUE); + fd_set_nonblock(ctx->inotify_fd, TRUE); + } + return ctx; +} + +void io_loop_notify_handler_deinit(struct ioloop *ioloop) +{ + struct ioloop_notify_handler_context *ctx = + ioloop->notify_handler_context; + + while (ctx->fd_ctx.notifies != NULL) { + struct io_notify *io = ctx->fd_ctx.notifies; + struct io *_io = &io->io; + + i_warning("I/O notify leak: %p (%s:%u, fd %d)", + (void *)_io->callback, + _io->source_filename, + _io->source_linenum, io->fd); + io_remove(&_io); + } + + i_close_fd(&ctx->inotify_fd); + i_free(ctx); +} + +int io_loop_extract_notify_fd(struct ioloop *ioloop) +{ + struct ioloop_notify_handler_context *ctx = + ioloop->notify_handler_context; + struct io_notify *io; + int fd, new_inotify_fd; + + if (ctx == NULL || ctx->inotify_fd == -1) + return -1; + + new_inotify_fd = inotify_init(); + if (new_inotify_fd == -1) { + if (errno != EMFILE) + i_error("inotify_init() failed: %m"); + else + ioloop_inotify_user_limit_exceeded(); + return -1; + } + for (io = ctx->fd_ctx.notifies; io != NULL; io = io->next) + io->fd = -1; + io_remove(&ctx->event_io); + fd = ctx->inotify_fd; + ctx->inotify_fd = new_inotify_fd; + return fd; +} + +#endif diff --git a/src/lib/ioloop-notify-kqueue.c b/src/lib/ioloop-notify-kqueue.c new file mode 100644 index 0000000..d12d878 --- /dev/null +++ b/src/lib/ioloop-notify-kqueue.c @@ -0,0 +1,224 @@ +/* + * BSD kqueue() based ioloop notify handler. + * + * Copyright (c) 2005 Vaclav Haisman <v.haisman@sh.cvut.cz> + */ + +#define _GNU_SOURCE +#include "lib.h" + +#ifdef IOLOOP_NOTIFY_KQUEUE + +#include "ioloop-private.h" +#include "llist.h" +#include "time-util.h" +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/event.h> +#include <sys/time.h> +#include <sys/stat.h> + +/* kevent.udata's type just has to be different in NetBSD than in + FreeBSD and OpenBSD.. */ +#ifdef __NetBSD__ +# define MY_EV_SET(a, b, c, d, e, f, g) \ + EV_SET(a, b, c, d, e, f, (intptr_t)g) +#else +# define MY_EV_SET(a, b, c, d, e, f, g) \ + EV_SET(a, b, c, d, e, f, g) +#endif + +struct io_notify { + struct io io; + int refcount; + int fd; + struct io_notify *prev, *next; +}; + +struct ioloop_notify_handler_context { + int kq; + struct io *event_io; + struct io_notify *notifies; +}; + +static void +io_loop_notify_free(struct ioloop_notify_handler_context *ctx, + struct io_notify *io) +{ + DLLIST_REMOVE(&ctx->notifies, io); + i_free(io); +} + +static void event_callback(struct ioloop_notify_handler_context *ctx) +{ + struct io_notify *io; + struct kevent events[64]; + struct timespec ts; + int i, ret; + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + ret = kevent(ctx->kq, NULL, 0, events, N_ELEMENTS(events), &ts); + if (ret <= 0) { + if (ret == 0 || errno == EINTR) + return; + + i_fatal("kevent(notify) failed: %m"); + } + + i_gettimeofday(&ioloop_timeval); + ioloop_time = ioloop_timeval.tv_sec; + + for (i = 0; i < ret; i++) { + io = (void *)events[i].udata; + i_assert(io->refcount >= 1); + io->refcount++; + } + for (i = 0; i < ret; i++) { + io = (void *)events[i].udata; + /* there can be multiple events for a single io. + call the callback only once if that happens. */ + if (io->refcount == 2 && io->io.callback != NULL) + io_loop_call_io(&io->io); + + if (--io->refcount == 0) + io_loop_notify_free(ctx, io); + } +} + +static struct ioloop_notify_handler_context *io_loop_notify_handler_init(void) +{ + struct ioloop_notify_handler_context *ctx; + + ctx = current_ioloop->notify_handler_context = + i_new(struct ioloop_notify_handler_context, 1); + ctx->kq = kqueue(); + if (ctx->kq < 0) + i_fatal("kqueue(notify) failed: %m"); + fd_close_on_exec(ctx->kq, TRUE); + return ctx; +} + +void io_loop_notify_handler_deinit(struct ioloop *ioloop) +{ + struct ioloop_notify_handler_context *ctx = + ioloop->notify_handler_context; + + while (ctx->notifies != NULL) { + struct io_notify *io = ctx->notifies; + struct io *_io = &io->io; + + i_warning("I/O notify leak: %p (%s:%u, fd %d)", + (void *)_io->callback, + _io->source_filename, + _io->source_linenum, io->fd); + io_remove(&_io); + } + + io_remove(&ctx->event_io); + if (close(ctx->kq) < 0) + i_error("close(kqueue notify) failed: %m"); + i_free(ctx); +} + +#undef io_add_notify +enum io_notify_result +io_add_notify(const char *path, const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context, struct io **io_r) +{ + struct ioloop_notify_handler_context *ctx = + current_ioloop->notify_handler_context; + struct kevent ev; + struct io_notify *io; + int fd; + + if (ctx == NULL) + ctx = io_loop_notify_handler_init(); + + fd = open(path, O_RDONLY); + if (fd == -1) { + /* ESTALE could happen with NFS. Don't bother giving an error + message then. */ + if (errno != ENOENT && errno != ESTALE) + i_error("open(%s) for kq notify failed: %m", path); + return IO_NOTIFY_NOTFOUND; + } + fd_close_on_exec(fd, TRUE); + + io = i_new(struct io_notify, 1); + io->io.condition = IO_NOTIFY; + io->io.source_filename = source_filename; + io->io.source_linenum = source_linenum; + io->io.callback = callback; + io->io.context = context; + io->io.ioloop = current_ioloop; + io->refcount = 1; + io->fd = fd; + + /* EV_CLEAR flag is needed because the EVFILT_VNODE filter reports + event state transitions and not the current state. With this flag, + the same event is only returned once. */ + MY_EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, + NOTE_DELETE | NOTE_RENAME | NOTE_WRITE | NOTE_EXTEND | + NOTE_REVOKE, 0, io); + if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) { + i_error("kevent(%d, %s) for notify failed: %m", fd, path); + i_close_fd(&fd); + i_free(io); + return IO_NOTIFY_NOSUPPORT; + } + + if (ctx->event_io == NULL) { + ctx->event_io = io_add(ctx->kq, IO_READ, event_callback, + io->io.ioloop->notify_handler_context); + } + DLLIST_PREPEND(&ctx->notifies, io); + *io_r = &io->io; + return IO_NOTIFY_ADDED; +} + +void io_loop_notify_remove(struct io *_io) +{ + struct ioloop_notify_handler_context *ctx = + _io->ioloop->notify_handler_context; + struct io_notify *io = (struct io_notify *)_io; + struct kevent ev; + + MY_EV_SET(&ev, io->fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL); + if (kevent(ctx->kq, &ev, 1, NULL, 0, 0) < 0) + i_error("kevent(%d) for notify remove failed: %m", io->fd); + if (close(io->fd) < 0) + i_error("close(%d) for notify remove failed: %m", io->fd); + io->fd = -1; + + if (--io->refcount == 0) + io_loop_notify_free(ctx, io); +} + +int io_loop_extract_notify_fd(struct ioloop *ioloop) +{ + struct ioloop_notify_handler_context *ctx = + ioloop->notify_handler_context; + struct io_notify *io; + int fd, new_kq; + + if (ctx == NULL || ctx->kq == -1) + return -1; + + new_kq = kqueue(); + if (new_kq < 0) { + i_error("kqueue(notify) failed: %m"); + return -1; + } + for (io = ctx->notifies; io != NULL; io = io->next) + io->fd = -1; + io_remove(&ctx->event_io); + fd = ctx->kq; + ctx->kq = new_kq; + return fd; +} + +#endif diff --git a/src/lib/ioloop-notify-none.c b/src/lib/ioloop-notify-none.c new file mode 100644 index 0000000..f91c26f --- /dev/null +++ b/src/lib/ioloop-notify-none.c @@ -0,0 +1,33 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop-private.h" + +#ifdef IOLOOP_NOTIFY_NONE + +#undef io_add_notify +enum io_notify_result +io_add_notify(const char *path ATTR_UNUSED, + const char *source_filename ATTR_UNUSED, + unsigned int source_linenum ATTR_UNUSED, + io_callback_t *callback ATTR_UNUSED, + void *context ATTR_UNUSED, struct io **io_r) +{ + *io_r = NULL; + return IO_NOTIFY_NOSUPPORT; +} + +void io_loop_notify_remove(struct io *io ATTR_UNUSED) +{ +} + +void io_loop_notify_handler_deinit(struct ioloop *ioloop ATTR_UNUSED) +{ +} + +int io_loop_extract_notify_fd(struct ioloop *ioloop ATTR_UNUSED) +{ + return -1; +} + +#endif diff --git a/src/lib/ioloop-poll.c b/src/lib/ioloop-poll.c new file mode 100644 index 0000000..02cdce6 --- /dev/null +++ b/src/lib/ioloop-poll.c @@ -0,0 +1,221 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +/* @UNSAFE: whole file */ + +#include "lib.h" +#include "ioloop-private.h" + +#ifdef IOLOOP_POLL + +#include <fcntl.h> +#include <sys/poll.h> + +struct ioloop_handler_context { + unsigned int fds_count, fds_pos; + struct pollfd *fds; + + unsigned int idx_count; + int *fd_index; +}; + +void io_loop_handler_init(struct ioloop *ioloop, unsigned int initial_fd_count) +{ + struct ioloop_handler_context *ctx; + + ioloop->handler_context = ctx = i_new(struct ioloop_handler_context, 1); + ctx->fds_count = initial_fd_count; + ctx->fds = i_new(struct pollfd, ctx->fds_count); + + ctx->idx_count = initial_fd_count; + ctx->fd_index = i_new(int, ctx->idx_count); + memset(ctx->fd_index, 0xff, sizeof(int) * ctx->idx_count); +} + +void io_loop_handler_deinit(struct ioloop *ioloop) +{ + i_free(ioloop->handler_context->fds); + i_free(ioloop->handler_context->fd_index); + i_free(ioloop->handler_context); +} + +#define IO_POLL_ERROR (POLLERR | POLLHUP | POLLNVAL) +#define IO_POLL_INPUT (POLLIN | POLLPRI | IO_POLL_ERROR) +#define IO_POLL_OUTPUT (POLLOUT | IO_POLL_ERROR) + +void io_loop_handle_add(struct io_file *io) +{ + struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; + enum io_condition condition = io->io.condition; + unsigned int old_count; + int index, old_events, fd = io->fd; + + if ((unsigned int)fd >= ctx->idx_count) { + /* grow the fd -> index array */ + old_count = ctx->idx_count; + + ctx->idx_count = nearest_power((unsigned int) fd+1); + + ctx->fd_index = i_realloc_type(ctx->fd_index, int, + old_count, ctx->idx_count); + memset(ctx->fd_index + old_count, 0xff, + sizeof(int) * (ctx->idx_count-old_count)); + } + + if (ctx->fds_pos >= ctx->fds_count) { + /* grow the fd array */ + old_count = ctx->fds_count; + + ctx->fds_count = nearest_power(ctx->fds_count+1); + + ctx->fds = i_realloc_type(ctx->fds, struct pollfd, + old_count, ctx->fds_count); + } + + if (ctx->fd_index[fd] != -1) { + /* update existing pollfd */ + index = ctx->fd_index[fd]; + } else { + /* add new pollfd */ + index = ctx->fds_pos++; + + ctx->fd_index[fd] = index; + ctx->fds[index].fd = fd; + ctx->fds[index].events = 0; + ctx->fds[index].revents = 0; + } + + old_events = ctx->fds[index].events; + if ((condition & IO_READ) != 0) + ctx->fds[index].events |= IO_POLL_INPUT; + if ((condition & IO_WRITE) != 0) + ctx->fds[index].events |= IO_POLL_OUTPUT; + if ((condition & IO_ERROR) != 0) + ctx->fds[index].events |= IO_POLL_ERROR; + i_assert(ctx->fds[index].events != old_events); +} + +void io_loop_handle_remove(struct io_file *io, bool closed ATTR_UNUSED) +{ + struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; + enum io_condition condition = io->io.condition; + int index, fd = io->fd; + + index = ctx->fd_index[fd]; + i_assert(index >= 0 && (unsigned int) index < ctx->fds_count); + +#ifdef DEBUG + if (!closed) { + /* io_remove() is required to be called before fd is closed. + This is required by epoll/kqueue, but since poll is more + commonly used while developing, this check here should catch + the error early enough not to cause problems for kqueue + users. */ + if (fcntl(io->fd, F_GETFD, 0) < 0) { + if (errno == EBADF) + i_panic("io_remove(%d) called too late", io->fd); + else + i_error("fcntl(%d, F_GETFD) failed: %m", io->fd); + } + } +#endif + i_free(io); + + if ((condition & IO_READ) != 0) { + ctx->fds[index].events &= ENUM_NEGATE(POLLIN | POLLPRI); + ctx->fds[index].revents &= ENUM_NEGATE(POLLIN | POLLPRI); + } + if ((condition & IO_WRITE) != 0) { + ctx->fds[index].events &= ENUM_NEGATE(POLLOUT); + ctx->fds[index].revents &= ENUM_NEGATE(POLLOUT); + } + + if ((ctx->fds[index].events & (POLLIN|POLLOUT)) == 0) { + /* remove the whole pollfd */ + ctx->fd_index[ctx->fds[index].fd] = -1; + if (--ctx->fds_pos == (unsigned int) index) + return; /* removing last one */ + + /* move the last pollfd over the removed one */ + ctx->fds[index] = ctx->fds[ctx->fds_pos]; + ctx->fd_index[ctx->fds[index].fd] = index; + } +} + +void io_loop_handler_run_internal(struct ioloop *ioloop) +{ + struct ioloop_handler_context *ctx = ioloop->handler_context; + struct pollfd *pollfd; + struct timeval tv; + struct io_file *io; + int msecs, ret; + bool call; + + /* get the time left for next timeout task */ + msecs = io_loop_run_get_wait_time(ioloop, &tv); +#ifdef _AIX + if (msecs > 1000) { + /* AIX seems to check IO_POLL_ERRORs only at the beginning of + the poll() call, not during it. keep timeouts short enough + so that we'll notice them pretty quickly. */ + msecs = 1000; + } +#endif + + ret = poll(ctx->fds, ctx->fds_pos, msecs); + if (ret < 0 && errno != EINTR) + i_fatal("poll(): %m"); + + /* execute timeout handlers */ + io_loop_handle_timeouts(ioloop); + + if (ret <= 0 || !ioloop->running) { + /* no I/O events */ + return; + } + + io = ioloop->io_files; + for (; io != NULL && ret > 0; io = ioloop->next_io_file) { + ioloop->next_io_file = io->next; + + if (io->fd == -1) { + /* io_add_istream() without fd */ + continue; + } + pollfd = &ctx->fds[ctx->fd_index[io->fd]]; + if (pollfd->revents != 0) { + if (pollfd->revents & POLLNVAL) { + i_error("invalid I/O fd %d, callback %p", + io->fd, (void *) io->io.callback); + pollfd->events = 0; + pollfd->revents = 0; + call = TRUE; + } else if ((io->io.condition & + (IO_READ|IO_WRITE)) == (IO_READ|IO_WRITE)) { + call = TRUE; + pollfd->revents = 0; + } else if ((io->io.condition & IO_READ) != 0) { + call = (pollfd->revents & IO_POLL_INPUT) != 0; + pollfd->revents &= ENUM_NEGATE(IO_POLL_INPUT); + } else if ((io->io.condition & IO_WRITE) != 0) { + call = (pollfd->revents & IO_POLL_OUTPUT) != 0; + pollfd->revents &= ENUM_NEGATE(IO_POLL_OUTPUT); + } else if ((io->io.condition & IO_ERROR) != 0) { + call = (pollfd->revents & IO_POLL_ERROR) != 0; + pollfd->revents &= ENUM_NEGATE(IO_POLL_ERROR); + } else { + call = FALSE; + } + + if (pollfd->revents == 0) + ret--; + + if (call) { + io_loop_call_io(&io->io); + if (!ioloop->running) + return; + } + } + } +} + +#endif diff --git a/src/lib/ioloop-private.h b/src/lib/ioloop-private.h new file mode 100644 index 0000000..799ca4d --- /dev/null +++ b/src/lib/ioloop-private.h @@ -0,0 +1,129 @@ +#ifndef IOLOOP_PRIVATE_H +#define IOLOOP_PRIVATE_H + +#include "priorityq.h" +#include "ioloop.h" +#include "array-decl.h" + +#ifndef IOLOOP_INITIAL_FD_COUNT +# define IOLOOP_INITIAL_FD_COUNT 128 +#endif + +struct ioloop { + struct ioloop *prev; + + struct ioloop_context *cur_ctx; + + struct io_file *io_files; + struct io_file *next_io_file; + struct priorityq *timeouts; + ARRAY(struct timeout *) timeouts_new; + struct io_wait_timer *wait_timers; + + struct ioloop_handler_context *handler_context; + struct ioloop_notify_handler_context *notify_handler_context; + unsigned int max_fd_count; + + io_loop_time_moved_callback_t *time_moved_callback; + struct timeval next_max_time; + uint64_t ioloop_wait_usecs; + struct timeval wait_started; + + unsigned int io_pending_count; + + bool running:1; + bool iolooping:1; + bool stop_after_run_loop:1; +}; + +struct io { + enum io_condition condition; + const char *source_filename; + unsigned int source_linenum; + /* trigger I/O callback even if OS doesn't think there is input + pending */ + bool pending; + /* This IO event shouldn't be the only thing being waited on, because + it would just result in infinite wait. */ + bool never_wait_alone; + + io_callback_t *callback; + void *context; + + struct ioloop *ioloop; + struct ioloop_context *ctx; +}; + +struct io_file { + struct io io; + + /* use a doubly linked list so that io_remove() is quick */ + struct io_file *prev, *next; + + int refcount; + int fd; + + /* only for io_add_istream(), a bit kludgy to be here.. */ + struct istream *istream; +}; + +struct timeout { + struct priorityq_item item; + const char *source_filename; + unsigned int source_linenum; + + unsigned int msecs; + struct timeval next_run; + + timeout_callback_t *callback; + void *context; + + struct ioloop *ioloop; + struct ioloop_context *ctx; + + bool one_shot:1; +}; + +struct io_wait_timer { + struct io_wait_timer *prev, *next; + const char *source_filename; + unsigned int source_linenum; + + struct ioloop *ioloop; + uint64_t usecs; +}; + +struct ioloop_context_callback { + io_callback_t *activate; + io_callback_t *deactivate; + void *context; + bool activated; +}; + +struct ioloop_context { + int refcount; + struct ioloop *ioloop; + ARRAY(struct ioloop_context_callback) callbacks; + ARRAY(struct event *) global_event_stack; + struct event *root_global_event; +}; + +int io_loop_run_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r); +void io_loop_handle_timeouts(struct ioloop *ioloop); +void io_loop_call_io(struct io *io); + +void io_loop_handler_run_internal(struct ioloop *ioloop); + +/* I/O handler calls */ +void io_loop_handle_add(struct io_file *io); +void io_loop_handle_remove(struct io_file *io, bool closed); + +void io_loop_handler_init(struct ioloop *ioloop, unsigned int initial_fd_count); +void io_loop_handler_deinit(struct ioloop *ioloop); + +void io_loop_notify_remove(struct io *io); +void io_loop_notify_handler_deinit(struct ioloop *ioloop); + +struct event *io_loop_get_active_global_root(void); + +#endif diff --git a/src/lib/ioloop-select.c b/src/lib/ioloop-select.c new file mode 100644 index 0000000..d5deb16 --- /dev/null +++ b/src/lib/ioloop-select.c @@ -0,0 +1,148 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop-private.h" + +#ifdef IOLOOP_SELECT + +#ifdef HAVE_SYS_SELECT_H +# include <sys/select.h> /* According to POSIX 1003.1-2001 */ +#endif +#include <sys/time.h> +#include <unistd.h> + +struct ioloop_handler_context { + int highest_fd; + fd_set read_fds, write_fds, except_fds; + fd_set tmp_read_fds, tmp_write_fds, tmp_except_fds; +}; + +static void update_highest_fd(struct ioloop *ioloop) +{ + struct ioloop_handler_context *ctx = ioloop->handler_context; + struct io_file *io; + int max_highest_fd; + + max_highest_fd = ctx->highest_fd-1; + ctx->highest_fd = -1; + + for (io = ioloop->io_files; io != NULL; io = io->next) { + if (io->fd <= ctx->highest_fd) + continue; + + ctx->highest_fd = io->fd; + + if (ctx->highest_fd == max_highest_fd) + break; + } +} + +void io_loop_handler_init(struct ioloop *ioloop, + unsigned int initial_fd_count ATTR_UNUSED) +{ + struct ioloop_handler_context *ctx; + + ioloop->handler_context = ctx = i_new(struct ioloop_handler_context, 1); + ctx->highest_fd = -1; + FD_ZERO(&ctx->read_fds); + FD_ZERO(&ctx->write_fds); + FD_ZERO(&ctx->except_fds); +} + +void io_loop_handler_deinit(struct ioloop *ioloop) +{ + i_free(ioloop->handler_context); +} + +void io_loop_handle_add(struct io_file *io) +{ + struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; + enum io_condition condition = io->io.condition; + int fd = io->fd; + + i_assert(fd >= 0); + + if (fd >= FD_SETSIZE) + i_fatal("fd %d too large for select()", fd); + + if ((condition & (IO_READ | IO_ERROR)) != 0) + FD_SET(fd, &ctx->read_fds); + if ((condition & IO_WRITE) != 0) + FD_SET(fd, &ctx->write_fds); + FD_SET(fd, &ctx->except_fds); + + if (io->fd > ctx->highest_fd) + ctx->highest_fd = io->fd; +} + +void io_loop_handle_remove(struct io_file *io, bool closed ATTR_UNUSED) +{ + struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; + enum io_condition condition = io->io.condition; + int fd = io->fd; + + i_assert(fd >= 0 && fd < FD_SETSIZE); + + if ((condition & (IO_READ | IO_ERROR)) != 0) + FD_CLR(fd, &ctx->read_fds); + if ((condition & IO_WRITE) != 0) + FD_CLR(fd, &ctx->write_fds); + + if (!FD_ISSET(fd, &ctx->read_fds) && !FD_ISSET(fd, &ctx->write_fds)) { + FD_CLR(fd, &ctx->except_fds); + + /* check if we removed the highest fd */ + if (io->fd == ctx->highest_fd) + update_highest_fd(io->io.ioloop); + } + i_free(io); +} + +#define io_check_condition(ctx, fd, cond) \ + ((FD_ISSET((fd), &(ctx)->tmp_read_fds) && ((cond) & (IO_READ|IO_ERROR)) != 0) || \ + (FD_ISSET((fd), &(ctx)->tmp_write_fds) && ((cond) & IO_WRITE) != 0) || \ + (FD_ISSET((fd), &(ctx)->tmp_except_fds))) + +void io_loop_handler_run_internal(struct ioloop *ioloop) +{ + struct ioloop_handler_context *ctx = ioloop->handler_context; + struct timeval tv; + struct io_file *io; + int ret; + + /* get the time left for next timeout task */ + io_loop_run_get_wait_time(ioloop, &tv); + + memcpy(&ctx->tmp_read_fds, &ctx->read_fds, sizeof(fd_set)); + memcpy(&ctx->tmp_write_fds, &ctx->write_fds, sizeof(fd_set)); + memcpy(&ctx->tmp_except_fds, &ctx->except_fds, sizeof(fd_set)); + + ret = select(ctx->highest_fd + 1, &ctx->tmp_read_fds, + &ctx->tmp_write_fds, &ctx->tmp_except_fds, &tv); + if (ret < 0 && errno != EINTR) + i_warning("select() : %m"); + + /* execute timeout handlers */ + io_loop_handle_timeouts(ioloop); + + if (ret <= 0 || !ioloop->running) { + /* no I/O events */ + return; + } + + io = ioloop->io_files; + for (; io != NULL && ret > 0; io = ioloop->next_io_file) { + ioloop->next_io_file = io->next; + + if (io->fd == -1) { + /* io_add_istream() without fd */ + } else if (io_check_condition(ctx, io->fd, io->io.condition)) { + ret--; + io_loop_call_io(&io->io); + if (!ioloop->running) + break; + } + } +} + +#endif diff --git a/src/lib/ioloop.c b/src/lib/ioloop.c new file mode 100644 index 0000000..e248808 --- /dev/null +++ b/src/lib/ioloop.c @@ -0,0 +1,1389 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "backtrace-string.h" +#include "llist.h" +#include "time-util.h" +#include "istream-private.h" +#include "ioloop-private.h" + +#include <unistd.h> + +/* Dovecot attempts to detect also when time suddenly jumps forwards. + This is done by getting the minimum timeout wait in epoll() (or similar) + and then seeing if the current time after epoll() is past the timeout. + This can't be very exact, so likely the difference is always at least + 1 microsecond. In high load situations it can be somewhat higher. + Dovecot generally doesn't have very important short timeouts, so to avoid + logging many warnings about this, use a rather high value. */ +#define IOLOOP_TIME_MOVED_FORWARDS_MIN_USECS (100000) + +time_t ioloop_time = 0; +struct timeval ioloop_timeval; +struct ioloop *current_ioloop = NULL; +uint64_t ioloop_global_wait_usecs = 0; + +static ARRAY(io_switch_callback_t *) io_switch_callbacks = ARRAY_INIT; +static ARRAY(io_destroy_callback_t *) io_destroy_callbacks = ARRAY_INIT; +static bool panic_on_leak = FALSE, panic_on_leak_set = FALSE; + +static time_t data_stack_last_free_unused = 0; + +static void io_loop_initialize_handler(struct ioloop *ioloop) +{ + unsigned int initial_fd_count; + + initial_fd_count = ioloop->max_fd_count > 0 && + ioloop->max_fd_count < IOLOOP_INITIAL_FD_COUNT ? + ioloop->max_fd_count : IOLOOP_INITIAL_FD_COUNT; + io_loop_handler_init(ioloop, initial_fd_count); +} + +static struct io_file * +io_add_file(struct ioloop *ioloop, int fd, enum io_condition condition, + const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context) +{ + struct io_file *io; + + i_assert(callback != NULL); + i_assert((condition & IO_NOTIFY) == 0); + + io = i_new(struct io_file, 1); + io->io.condition = condition; + io->io.callback = callback; + io->io.context = context; + io->io.ioloop = ioloop; + io->io.source_filename = source_filename; + io->io.source_linenum = source_linenum; + io->refcount = 1; + io->fd = fd; + + if (io->io.ioloop->cur_ctx != NULL) { + io->io.ctx = io->io.ioloop->cur_ctx; + io_loop_context_ref(io->io.ctx); + } + + if (io->io.ioloop->handler_context == NULL) + io_loop_initialize_handler(io->io.ioloop); + if (fd != -1) + io_loop_handle_add(io); + else { + /* we're adding an istream whose only way to get notified + is to call i_stream_set_input_pending() */ + } + + if (io->io.ioloop->io_files != NULL) { + io->io.ioloop->io_files->prev = io; + io->next = io->io.ioloop->io_files; + } + io->io.ioloop->io_files = io; + return io; +} + +#undef io_add_to +struct io *io_add_to(struct ioloop *ioloop, int fd, enum io_condition condition, + const char *source_filename, unsigned int source_linenum, + io_callback_t *callback, void *context) +{ + struct io_file *io; + + i_assert(fd >= 0); + io = io_add_file(ioloop, fd, condition, + source_filename, source_linenum, + callback, context); + return &io->io; +} + +#undef io_add +struct io *io_add(int fd, enum io_condition condition, + const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context) +{ + return io_add_to(current_ioloop, fd, condition, + source_filename, source_linenum, + callback, context); +} + +#undef io_add_istream_to +struct io *io_add_istream_to(struct ioloop *ioloop, struct istream *input, + const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context) +{ + struct io_file *io; + + io = io_add_file(ioloop, i_stream_get_fd(input), IO_READ, + source_filename, source_linenum, callback, context); + io->istream = input; + i_stream_ref(io->istream); + i_stream_set_io(io->istream, &io->io); + return &io->io; +} + +#undef io_add_istream +struct io *io_add_istream(struct istream *input, const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context) +{ + return io_add_istream_to(current_ioloop, input, + source_filename, source_linenum, + callback, context); +} + +static void io_file_unlink(struct io_file *io) +{ + if (io->prev != NULL) + io->prev->next = io->next; + else + io->io.ioloop->io_files = io->next; + + if (io->next != NULL) + io->next->prev = io->prev; + + /* if we got here from an I/O handler callback, make sure we + don't try to handle this one next. */ + if (io->io.ioloop->next_io_file == io) + io->io.ioloop->next_io_file = io->next; +} + +static void io_remove_full(struct io **_io, bool closed) +{ + struct io *io = *_io; + + i_assert(io->callback != NULL); + + *_io = NULL; + + /* make sure the callback doesn't get called anymore. + kqueue code relies on this. */ + io->callback = NULL; + + if (io->pending) { + i_assert(io->ioloop->io_pending_count > 0); + io->ioloop->io_pending_count--; + } + + if (io->ctx != NULL) + io_loop_context_unref(&io->ctx); + + if ((io->condition & IO_NOTIFY) != 0) + io_loop_notify_remove(io); + else { + struct io_file *io_file = (struct io_file *)io; + struct istream *istream = io_file->istream; + + if (istream != NULL) { + /* remove io before it's freed */ + i_stream_unset_io(istream, io); + } + + io_file_unlink(io_file); + if (io_file->fd != -1) + io_loop_handle_remove(io_file, closed); + else + i_free(io); + + /* remove io from the ioloop before unreferencing the istream, + because a destroyed istream may automatically close the + fd. */ + i_stream_unref(&istream); + } +} + +void io_remove(struct io **io) +{ + if (*io == NULL) + return; + + io_remove_full(io, FALSE); +} + +void io_remove_closed(struct io **io) +{ + if (*io == NULL) + return; + + i_assert(((*io)->condition & IO_NOTIFY) == 0); + + io_remove_full(io, TRUE); +} + +void io_set_pending(struct io *io) +{ + i_assert((io->condition & IO_NOTIFY) == 0); + + if (!io->pending) { + io->pending = TRUE; + io->ioloop->io_pending_count++; + } +} + +bool io_is_pending(struct io *io) +{ + return io->pending; +} + +void io_set_never_wait_alone(struct io *io, bool set) +{ + io->never_wait_alone = set; +} + +static void timeout_update_next(struct timeout *timeout, struct timeval *tv_now) +{ + if (tv_now == NULL) + i_gettimeofday(&timeout->next_run); + else { + timeout->next_run.tv_sec = tv_now->tv_sec; + timeout->next_run.tv_usec = tv_now->tv_usec; + } + + /* we don't want microsecond accuracy or this function will be + called all the time - millisecond is more than enough */ + timeout->next_run.tv_usec -= timeout->next_run.tv_usec % 1000; + + timeout->next_run.tv_sec += timeout->msecs/1000; + timeout->next_run.tv_usec += (timeout->msecs%1000)*1000; + + if (timeout->next_run.tv_usec >= 1000000) { + timeout->next_run.tv_sec++; + timeout->next_run.tv_usec -= 1000000; + } +} + +static struct timeout * +timeout_add_common(struct ioloop *ioloop, const char *source_filename, + unsigned int source_linenum, + timeout_callback_t *callback, void *context) +{ + struct timeout *timeout; + + timeout = i_new(struct timeout, 1); + timeout->item.idx = UINT_MAX; + timeout->source_filename = source_filename; + timeout->source_linenum = source_linenum; + timeout->ioloop = ioloop; + + timeout->callback = callback; + timeout->context = context; + + if (timeout->ioloop->cur_ctx != NULL) { + timeout->ctx = timeout->ioloop->cur_ctx; + io_loop_context_ref(timeout->ctx); + } + + return timeout; +} + +#undef timeout_add_to +struct timeout *timeout_add_to(struct ioloop *ioloop, unsigned int msecs, + const char *source_filename, + unsigned int source_linenum, + timeout_callback_t *callback, void *context) +{ + struct timeout *timeout; + + timeout = timeout_add_common(ioloop, source_filename, source_linenum, + callback, context); + timeout->msecs = msecs; + + if (msecs > 0) { + /* start this timeout in the next run cycle */ + array_push_back(&timeout->ioloop->timeouts_new, &timeout); + } else { + /* Trigger zero timeouts as soon as possible. When ioloop is + running, refresh the timestamp to prevent infinite loops + in case a timeout callback keeps recreating the 0-timeout. */ + timeout_update_next(timeout, timeout->ioloop->running ? + NULL : &ioloop_timeval); + priorityq_add(timeout->ioloop->timeouts, &timeout->item); + } + return timeout; +} + +#undef timeout_add +struct timeout *timeout_add(unsigned int msecs, const char *source_filename, + unsigned int source_linenum, + timeout_callback_t *callback, void *context) +{ + return timeout_add_to(current_ioloop, msecs, + source_filename, source_linenum, + callback, context); +} + +#undef timeout_add_short_to +struct timeout * +timeout_add_short_to(struct ioloop *ioloop, unsigned int msecs, + const char *source_filename, unsigned int source_linenum, + timeout_callback_t *callback, void *context) +{ + return timeout_add_to(ioloop, msecs, + source_filename, source_linenum, + callback, context); +} + +#undef timeout_add_short +struct timeout * +timeout_add_short(unsigned int msecs, const char *source_filename, + unsigned int source_linenum, + timeout_callback_t *callback, void *context) +{ + return timeout_add(msecs, source_filename, source_linenum, + callback, context); +} + +#undef timeout_add_absolute_to +struct timeout * +timeout_add_absolute_to(struct ioloop *ioloop, const struct timeval *time, + const char *source_filename, + unsigned int source_linenum, + timeout_callback_t *callback, void *context) +{ + struct timeout *timeout; + + timeout = timeout_add_common(ioloop, source_filename, source_linenum, + callback, context); + timeout->one_shot = TRUE; + timeout->next_run = *time; + + priorityq_add(timeout->ioloop->timeouts, &timeout->item); + return timeout; +} + +#undef timeout_add_absolute +struct timeout * +timeout_add_absolute(const struct timeval *time, + const char *source_filename, + unsigned int source_linenum, + timeout_callback_t *callback, void *context) +{ + return timeout_add_absolute_to(current_ioloop, time, + source_filename, source_linenum, + callback, context); +} + +static struct timeout * +timeout_copy(const struct timeout *old_to, struct ioloop *ioloop) +{ + struct timeout *new_to; + + new_to = timeout_add_common(ioloop, + old_to->source_filename, old_to->source_linenum, + old_to->callback, old_to->context); + new_to->one_shot = old_to->one_shot; + new_to->msecs = old_to->msecs; + new_to->next_run = old_to->next_run; + + if (old_to->item.idx != UINT_MAX) + priorityq_add(new_to->ioloop->timeouts, &new_to->item); + else if (!new_to->one_shot) { + i_assert(new_to->msecs > 0); + array_push_back(&new_to->ioloop->timeouts_new, &new_to); + } + + return new_to; +} + +static void timeout_free(struct timeout *timeout) +{ + if (timeout->ctx != NULL) + io_loop_context_unref(&timeout->ctx); + i_free(timeout); +} + +void timeout_remove(struct timeout **_timeout) +{ + struct timeout *timeout = *_timeout; + struct ioloop *ioloop; + + if (timeout == NULL) + return; + + ioloop = timeout->ioloop; + + *_timeout = NULL; + if (timeout->item.idx != UINT_MAX) + priorityq_remove(timeout->ioloop->timeouts, &timeout->item); + else if (!timeout->one_shot && timeout->msecs > 0) { + struct timeout *const *to_idx; + array_foreach(&ioloop->timeouts_new, to_idx) { + if (*to_idx == timeout) { + array_delete(&ioloop->timeouts_new, + array_foreach_idx(&ioloop->timeouts_new, to_idx), 1); + break; + } + } + } + timeout_free(timeout); +} + +static void ATTR_NULL(2) +timeout_reset_timeval(struct timeout *timeout, struct timeval *tv_now) +{ + if (timeout->item.idx == UINT_MAX) + return; + + timeout_update_next(timeout, tv_now); + /* If we came here from io_loop_handle_timeouts_real(), next_run must + be larger than tv_now or it can go to infinite loop. This would + mainly happen with 0 ms timeouts. Avoid this by making sure + next_run is at least 1 us higher than tv_now. + + Note that some callers (like master process's process_min_avail + preforking timeout) really do want the 0 ms timeout to trigger + multiple times as rapidly as it can (but in separate ioloop runs). + So don't increase it more than by 1 us. */ + if (tv_now != NULL && timeval_cmp(&timeout->next_run, tv_now) <= 0) { + timeout->next_run = *tv_now; + timeval_add_usecs(&timeout->next_run, 1); + } + priorityq_remove(timeout->ioloop->timeouts, &timeout->item); + priorityq_add(timeout->ioloop->timeouts, &timeout->item); +} + +void timeout_reset(struct timeout *timeout) +{ + i_assert(!timeout->one_shot); + timeout_reset_timeval(timeout, NULL); +} + +static int timeout_get_wait_time(struct timeout *timeout, struct timeval *tv_r, + struct timeval *tv_now, bool in_timeout_loop) +{ + int ret; + + if (tv_now->tv_sec == 0) + i_gettimeofday(tv_now); + tv_r->tv_sec = tv_now->tv_sec; + tv_r->tv_usec = tv_now->tv_usec; + + i_assert(tv_r->tv_sec > 0); + i_assert(timeout->next_run.tv_sec > 0); + + tv_r->tv_sec = timeout->next_run.tv_sec - tv_r->tv_sec; + tv_r->tv_usec = timeout->next_run.tv_usec - tv_r->tv_usec; + if (tv_r->tv_usec < 0) { + tv_r->tv_sec--; + tv_r->tv_usec += 1000000; + } + + if (tv_r->tv_sec < 0) { + /* The timeout should have been called already */ + tv_r->tv_sec = 0; + tv_r->tv_usec = 0; + return 0; + } + if (tv_r->tv_sec == 0 && tv_r->tv_usec == 1 && !in_timeout_loop) { + /* Possibly 0 ms timeout. Don't wait for a full millisecond + for it to trigger. */ + tv_r->tv_usec = 0; + return 0; + } + if (tv_r->tv_sec > INT_MAX/1000-1) + tv_r->tv_sec = INT_MAX/1000-1; + + /* round wait times up to next millisecond */ + ret = tv_r->tv_sec * 1000 + (tv_r->tv_usec + 999) / 1000; + i_assert(ret >= 0 && tv_r->tv_sec >= 0 && tv_r->tv_usec >= 0); + return ret; +} + +static int io_loop_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r) +{ + struct timeval tv_now; + struct priorityq_item *item; + struct timeout *timeout; + int msecs; + + item = priorityq_peek(ioloop->timeouts); + timeout = (struct timeout *)item; + + /* we need to see if there are pending IO waiting, + if there is, we set msecs = 0 to ensure they are + processed without delay */ + if (timeout == NULL && ioloop->io_pending_count == 0) { + /* no timeouts. use INT_MAX msecs for timeval and + return -1 for poll/epoll infinity. */ + tv_r->tv_sec = INT_MAX / 1000; + tv_r->tv_usec = 0; + ioloop->next_max_time.tv_sec = (1ULL << (TIME_T_MAX_BITS-1)) - 1; + ioloop->next_max_time.tv_usec = 0; + return -1; + } + + if (ioloop->io_pending_count > 0) { + i_gettimeofday(&tv_now); + msecs = 0; + tv_r->tv_sec = 0; + tv_r->tv_usec = 0; + } else { + tv_now.tv_sec = 0; + msecs = timeout_get_wait_time(timeout, tv_r, &tv_now, FALSE); + } + ioloop->next_max_time = tv_now; + timeval_add_msecs(&ioloop->next_max_time, msecs); + + /* update ioloop_timeval - this is meant for io_loop_handle_timeouts()'s + ioloop_wait_usecs calculation. normally after this we go to the + ioloop and after that we update ioloop_timeval immediately again. */ + ioloop_timeval = tv_now; + ioloop_time = tv_now.tv_sec; + i_assert(msecs == 0 || timeout->msecs > 0 || timeout->one_shot); + return msecs; +} + +static bool io_loop_have_waitable_io_files(struct ioloop *ioloop) +{ + struct io_file *io; + + for (io = ioloop->io_files; io != NULL; io = io->next) { + if (io->io.callback != NULL && !io->io.never_wait_alone) + return TRUE; + } + return FALSE; +} + +int io_loop_run_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r) +{ + int msecs = io_loop_get_wait_time(ioloop, tv_r); + if (msecs < 0 && !io_loop_have_waitable_io_files(ioloop)) + i_panic("BUG: No IOs or timeouts set. Not waiting for infinity."); + return msecs; +} + +static int timeout_cmp(const void *p1, const void *p2) +{ + const struct timeout *to1 = p1, *to2 = p2; + + return timeval_cmp(&to1->next_run, &to2->next_run); +} + +static void +io_loop_default_time_moved(const struct timeval *old_time, + const struct timeval *new_time) +{ + long long diff = timeval_diff_usecs(old_time, new_time); + if (diff > 0) { + i_warning("Time moved backwards by %lld.%06lld seconds.", + diff / 1000000, diff % 1000000); + } +} + +static void io_loop_timeouts_start_new(struct ioloop *ioloop) +{ + struct timeout *timeout; + + if (array_count(&ioloop->timeouts_new) == 0) + return; + + io_loop_time_refresh(); + + array_foreach_elem(&ioloop->timeouts_new, timeout) { + i_assert(timeout->next_run.tv_sec == 0 && + timeout->next_run.tv_usec == 0); + i_assert(!timeout->one_shot); + i_assert(timeout->msecs > 0); + timeout_update_next(timeout, &ioloop_timeval); + priorityq_add(ioloop->timeouts, &timeout->item); + } + array_clear(&ioloop->timeouts_new); +} + +static void io_loop_timeouts_update(struct ioloop *ioloop, long long diff_usecs) +{ + struct priorityq_item *const *items; + unsigned int i, count; + + count = priorityq_count(ioloop->timeouts); + items = priorityq_items(ioloop->timeouts); + for (i = 0; i < count; i++) { + struct timeout *to = (struct timeout *)items[i]; + + if (diff_usecs > 0) + timeval_add_usecs(&to->next_run, diff_usecs); + else + timeval_sub_usecs(&to->next_run, -diff_usecs); + } +} + +static void io_loops_timeouts_update(long long diff_usecs) +{ + struct ioloop *ioloop; + + for (ioloop = current_ioloop; ioloop != NULL; ioloop = ioloop->prev) + io_loop_timeouts_update(ioloop, diff_usecs); +} + +static void ioloop_add_wait_time(struct ioloop *ioloop) +{ + struct io_wait_timer *timer; + long long diff; + + diff = timeval_diff_usecs(&ioloop_timeval, &ioloop->wait_started); + if (diff < 0) { + /* time moved backwards */ + diff = 0; + } + + ioloop->ioloop_wait_usecs += diff; + ioloop_global_wait_usecs += diff; + + for (timer = ioloop->wait_timers; timer != NULL; timer = timer->next) + timer->usecs += diff; +} + +static void io_loop_handle_timeouts_real(struct ioloop *ioloop) +{ + struct priorityq_item *item; + struct timeval tv_old, tv, tv_call; + long long diff_usecs; + data_stack_frame_t t_id; + + tv_old = ioloop_timeval; + i_gettimeofday(&ioloop_timeval); + + diff_usecs = timeval_diff_usecs(&ioloop_timeval, &tv_old); + if (unlikely(diff_usecs < 0)) { + /* time moved backwards */ + io_loops_timeouts_update(diff_usecs); + ioloop->time_moved_callback(&tv_old, &ioloop_timeval); + i_assert(ioloop == current_ioloop); + /* the callback may have slept, so check the time again. */ + i_gettimeofday(&ioloop_timeval); + } else { + diff_usecs = timeval_diff_usecs(&ioloop->next_max_time, + &ioloop_timeval); + if (unlikely(-diff_usecs >= IOLOOP_TIME_MOVED_FORWARDS_MIN_USECS)) { + io_loops_timeouts_update(-diff_usecs); + /* time moved forwards */ + ioloop->time_moved_callback(&ioloop->next_max_time, + &ioloop_timeval); + i_assert(ioloop == current_ioloop); + } + ioloop_add_wait_time(ioloop); + } + + ioloop_time = ioloop_timeval.tv_sec; + tv_call = ioloop_timeval; + + while (ioloop->running && + (item = priorityq_peek(ioloop->timeouts)) != NULL) { + struct timeout *timeout = (struct timeout *)item; + + /* use tv_call to make sure we don't get to infinite loop in + case callbacks update ioloop_timeval. */ + if (timeout_get_wait_time(timeout, &tv, &tv_call, TRUE) > 0) + break; + + if (timeout->one_shot) { + /* remove timeout from queue */ + priorityq_remove(timeout->ioloop->timeouts, &timeout->item); + } else { + /* update timeout's next_run and reposition it in the queue */ + timeout_reset_timeval(timeout, &tv_call); + } + + if (timeout->ctx != NULL) + io_loop_context_activate(timeout->ctx); + t_id = t_push_named("ioloop timeout handler %p", + (void *)timeout->callback); + timeout->callback(timeout->context); + if (!t_pop(&t_id)) { + i_panic("Leaked a t_pop() call in timeout handler %p", + (void *)timeout->callback); + } + if (ioloop->cur_ctx != NULL) + io_loop_context_deactivate(ioloop->cur_ctx); + i_assert(ioloop == current_ioloop); + } +} + +void io_loop_handle_timeouts(struct ioloop *ioloop) +{ + T_BEGIN { + io_loop_handle_timeouts_real(ioloop); + } T_END; + + /* Free the unused memory in data stack once per second. This way if + the data stack has grown excessively large temporarily, it won't + permanently waste memory. And if the data stack grows back to the + same large size, re-allocating it once per second doesn't cause + performance problems. */ + if (data_stack_last_free_unused != ioloop_time) { + if (data_stack_last_free_unused != 0) + data_stack_free_unused(); + data_stack_last_free_unused = ioloop_time; + } +} + +void io_loop_call_io(struct io *io) +{ + struct ioloop *ioloop = io->ioloop; + data_stack_frame_t t_id; + + if (io->pending) { + i_assert(ioloop->io_pending_count > 0); + ioloop->io_pending_count--; + io->pending = FALSE; + } + + if (io->ctx != NULL) + io_loop_context_activate(io->ctx); + t_id = t_push_named("ioloop handler %p", + (void *)io->callback); + io->callback(io->context); + if (!t_pop(&t_id)) { + i_panic("Leaked a t_pop() call in I/O handler %p", + (void *)io->callback); + } + if (ioloop->cur_ctx != NULL) + io_loop_context_deactivate(ioloop->cur_ctx); + i_assert(ioloop == current_ioloop); +} + +void io_loop_run(struct ioloop *ioloop) +{ + if (ioloop->handler_context == NULL) + io_loop_initialize_handler(ioloop); + + if (ioloop->cur_ctx != NULL) + io_loop_context_deactivate(ioloop->cur_ctx); + + /* recursive io_loop_run() isn't allowed for the same ioloop. + it can break backends. */ + i_assert(!ioloop->iolooping); + ioloop->iolooping = TRUE; + + ioloop->running = TRUE; + while (ioloop->running) + io_loop_handler_run(ioloop); + ioloop->iolooping = FALSE; +} + +static void io_loop_call_pending(struct ioloop *ioloop) +{ + struct io_file *io; + + while (ioloop->io_pending_count > 0) { + io = ioloop->io_files; + do { + ioloop->next_io_file = io->next; + if (io->io.pending) + io_loop_call_io(&io->io); + if (ioloop->io_pending_count == 0) + break; + io = ioloop->next_io_file; + } while (io != NULL); + } +} + +void io_loop_handler_run(struct ioloop *ioloop) +{ + i_assert(ioloop == current_ioloop); + + io_loop_timeouts_start_new(ioloop); + ioloop->wait_started = ioloop_timeval; + io_loop_handler_run_internal(ioloop); + io_loop_call_pending(ioloop); + if (ioloop->stop_after_run_loop) + io_loop_stop(ioloop); + + i_assert(ioloop == current_ioloop); +} + +void io_loop_stop(struct ioloop *ioloop) +{ + ioloop->running = FALSE; + ioloop->stop_after_run_loop = FALSE; +} + +void io_loop_stop_delayed(struct ioloop *ioloop) +{ + ioloop->stop_after_run_loop = TRUE; +} + +void io_loop_set_running(struct ioloop *ioloop) +{ + ioloop->running = TRUE; +} + +void io_loop_set_max_fd_count(struct ioloop *ioloop, unsigned int max_fds) +{ + ioloop->max_fd_count = max_fds; +} + +bool io_loop_is_running(struct ioloop *ioloop) +{ + return ioloop->running; +} + +void io_loop_time_refresh(void) +{ + i_gettimeofday(&ioloop_timeval); + ioloop_time = ioloop_timeval.tv_sec; +} + +struct ioloop *io_loop_create(void) +{ + struct ioloop *ioloop; + + if (!panic_on_leak_set) { + panic_on_leak_set = TRUE; + panic_on_leak = getenv("CORE_IO_LEAK") != NULL; + } + + /* initialize time */ + i_gettimeofday(&ioloop_timeval); + ioloop_time = ioloop_timeval.tv_sec; + + ioloop = i_new(struct ioloop, 1); + ioloop->timeouts = priorityq_init(timeout_cmp, 32); + i_array_init(&ioloop->timeouts_new, 8); + + ioloop->time_moved_callback = current_ioloop != NULL ? + current_ioloop->time_moved_callback : + io_loop_default_time_moved; + + ioloop->prev = current_ioloop; + io_loop_set_current(ioloop); + return ioloop; +} + +void io_loop_destroy(struct ioloop **_ioloop) +{ + struct ioloop *ioloop = *_ioloop; + struct timeout *to; + struct priorityq_item *item; + bool leaks = FALSE; + + *_ioloop = NULL; + + /* ->prev won't work unless loops are destroyed in create order */ + i_assert(ioloop == current_ioloop); + if (array_is_created(&io_destroy_callbacks)) { + io_destroy_callback_t *callback; + array_foreach_elem(&io_destroy_callbacks, callback) T_BEGIN { + callback(current_ioloop); + } T_END; + } + + io_loop_set_current(current_ioloop->prev); + + if (ioloop->notify_handler_context != NULL) + io_loop_notify_handler_deinit(ioloop); + + while (ioloop->io_files != NULL) { + struct io_file *io = ioloop->io_files; + struct io *_io = &io->io; + const char *error = t_strdup_printf( + "I/O leak: %p (%s:%u, fd %d)", + (void *)io->io.callback, + io->io.source_filename, + io->io.source_linenum, io->fd); + + if (panic_on_leak) + i_panic("%s", error); + else + i_warning("%s", error); + io_remove(&_io); + leaks = TRUE; + } + i_assert(ioloop->io_pending_count == 0); + + array_foreach_elem(&ioloop->timeouts_new, to) { + const char *error = t_strdup_printf( + "Timeout leak: %p (%s:%u)", (void *)to->callback, + to->source_filename, + to->source_linenum); + + if (panic_on_leak) + i_panic("%s", error); + else + i_warning("%s", error); + timeout_free(to); + leaks = TRUE; + } + array_free(&ioloop->timeouts_new); + + while ((item = priorityq_pop(ioloop->timeouts)) != NULL) { + struct timeout *to = (struct timeout *)item; + const char *error = t_strdup_printf( + "Timeout leak: %p (%s:%u)", (void *)to->callback, + to->source_filename, + to->source_linenum); + + if (panic_on_leak) + i_panic("%s", error); + else + i_warning("%s", error); + timeout_free(to); + leaks = TRUE; + } + priorityq_deinit(&ioloop->timeouts); + + while (ioloop->wait_timers != NULL) { + struct io_wait_timer *timer = ioloop->wait_timers; + const char *error = t_strdup_printf( + "IO wait timer leak: %s:%u", + timer->source_filename, + timer->source_linenum); + + if (panic_on_leak) + i_panic("%s", error); + else + i_warning("%s", error); + io_wait_timer_remove(&timer); + leaks = TRUE; + } + + if (leaks) { + const char *backtrace; + if (backtrace_get(&backtrace) == 0) + i_warning("Raw backtrace for leaks: %s", backtrace); + } + + if (ioloop->handler_context != NULL) + io_loop_handler_deinit(ioloop); + if (ioloop->cur_ctx != NULL) + io_loop_context_unref(&ioloop->cur_ctx); + i_free(ioloop); +} + +void io_loop_set_time_moved_callback(struct ioloop *ioloop, + io_loop_time_moved_callback_t *callback) +{ + ioloop->time_moved_callback = callback; +} + +static void io_switch_callbacks_free(void) +{ + array_free(&io_switch_callbacks); +} + +static void io_destroy_callbacks_free(void) +{ + array_free(&io_destroy_callbacks); +} + +void io_loop_set_current(struct ioloop *ioloop) +{ + io_switch_callback_t *callback; + struct ioloop *prev_ioloop = current_ioloop; + + if (ioloop == current_ioloop) + return; + + current_ioloop = ioloop; + if (array_is_created(&io_switch_callbacks)) { + array_foreach_elem(&io_switch_callbacks, callback) T_BEGIN { + callback(prev_ioloop); + } T_END; + } +} + +struct ioloop *io_loop_get_root(void) +{ + struct ioloop *ioloop = current_ioloop; + + while (ioloop->prev != NULL) + ioloop = ioloop->prev; + return ioloop; +} + +void io_loop_add_switch_callback(io_switch_callback_t *callback) +{ + if (!array_is_created(&io_switch_callbacks)) { + i_array_init(&io_switch_callbacks, 4); + lib_atexit_priority(io_switch_callbacks_free, LIB_ATEXIT_PRIORITY_LOW); + } + array_push_back(&io_switch_callbacks, &callback); +} + +void io_loop_remove_switch_callback(io_switch_callback_t *callback) +{ + io_switch_callback_t *const *callbackp; + unsigned int idx; + + array_foreach(&io_switch_callbacks, callbackp) { + if (*callbackp == callback) { + idx = array_foreach_idx(&io_switch_callbacks, callbackp); + array_delete(&io_switch_callbacks, idx, 1); + return; + } + } + i_unreached(); +} + +void io_loop_add_destroy_callback(io_destroy_callback_t *callback) +{ + if (!array_is_created(&io_destroy_callbacks)) { + i_array_init(&io_destroy_callbacks, 4); + lib_atexit_priority(io_destroy_callbacks_free, LIB_ATEXIT_PRIORITY_LOW); + } + array_push_back(&io_destroy_callbacks, &callback); +} + +void io_loop_remove_destroy_callback(io_destroy_callback_t *callback) +{ + io_destroy_callback_t *const *callbackp; + unsigned int idx; + + array_foreach(&io_destroy_callbacks, callbackp) { + if (*callbackp == callback) { + idx = array_foreach_idx(&io_destroy_callbacks, callbackp); + array_delete(&io_destroy_callbacks, idx, 1); + return; + } + } + i_unreached(); +} + +struct ioloop_context *io_loop_context_new(struct ioloop *ioloop) +{ + struct ioloop_context *ctx; + + ctx = i_new(struct ioloop_context, 1); + ctx->refcount = 1; + ctx->ioloop = ioloop; + i_array_init(&ctx->callbacks, 4); + return ctx; +} + +void io_loop_context_ref(struct ioloop_context *ctx) +{ + i_assert(ctx->refcount > 0); + + ctx->refcount++; +} + +void io_loop_context_unref(struct ioloop_context **_ctx) +{ + struct ioloop_context *ctx = *_ctx; + + *_ctx = NULL; + + i_assert(ctx->refcount > 0); + if (--ctx->refcount > 0) + return; + + /* cur_ctx itself keeps a reference */ + i_assert(ctx->ioloop->cur_ctx != ctx); + + array_free(&ctx->callbacks); + array_free(&ctx->global_event_stack); + i_free(ctx); +} + +#undef io_loop_context_add_callbacks +void io_loop_context_add_callbacks(struct ioloop_context *ctx, + io_callback_t *activate, + io_callback_t *deactivate, void *context) +{ + struct ioloop_context_callback cb; + + i_zero(&cb); + cb.activate = activate; + cb.deactivate = deactivate; + cb.context = context; + + array_push_back(&ctx->callbacks, &cb); +} + +#undef io_loop_context_remove_callbacks +void io_loop_context_remove_callbacks(struct ioloop_context *ctx, + io_callback_t *activate, + io_callback_t *deactivate, void *context) +{ + struct ioloop_context_callback *cb; + + array_foreach_modifiable(&ctx->callbacks, cb) { + if (cb->context == context && + cb->activate == activate && cb->deactivate == deactivate) { + /* simply mark it as deleted, since we could get + here from activate/deactivate loop */ + cb->activate = NULL; + cb->deactivate = NULL; + cb->context = NULL; + return; + } + } + i_panic("io_loop_context_remove_callbacks() context not found"); +} + +static void +io_loop_context_remove_deleted_callbacks(struct ioloop_context *ctx) +{ + const struct ioloop_context_callback *cbs; + unsigned int i, count; + + cbs = array_get(&ctx->callbacks, &count); + for (i = 0; i < count; ) { + if (cbs[i].activate != NULL) + i++; + else { + array_delete(&ctx->callbacks, i, 1); + cbs = array_get(&ctx->callbacks, &count); + } + } +} + +static void io_loop_context_push_global_events(struct ioloop_context *ctx) +{ + struct event *const *events; + unsigned int i, count; + + ctx->root_global_event = event_get_global(); + + if (!array_is_created(&ctx->global_event_stack)) + return; + + /* push the global events from stack in reverse order */ + events = array_get(&ctx->global_event_stack, &count); + if (count == 0) + return; + + /* Remember the oldest global event. We're going to pop until that + event when deactivating the context. */ + for (i = count; i > 0; i--) + event_push_global(events[i-1]); + array_clear(&ctx->global_event_stack); +} + +static void io_loop_context_pop_global_events(struct ioloop_context *ctx) +{ + struct event *event; + + /* ioloop context is always global, so we can't push one ioloop context + on top of another one. We'll need to rewind the global event stack + until we've reached the event that started this context. We'll push + these global events back when the ioloop context is activated + again. (We'll assert-crash if the root event is freed before these + global events have been popped.) */ + while ((event = event_get_global()) != ctx->root_global_event) { + i_assert(event != NULL); + if (!array_is_created(&ctx->global_event_stack)) + i_array_init(&ctx->global_event_stack, 4); + array_push_back(&ctx->global_event_stack, &event); + event_pop_global(event); + } + ctx->root_global_event = NULL; +} + +void io_loop_context_activate(struct ioloop_context *ctx) +{ + struct ioloop_context_callback *cb; + + i_assert(ctx->ioloop->cur_ctx == NULL); + + ctx->ioloop->cur_ctx = ctx; + io_loop_context_push_global_events(ctx); + io_loop_context_ref(ctx); + array_foreach_modifiable(&ctx->callbacks, cb) { + i_assert(!cb->activated); + if (cb->activate != NULL) T_BEGIN { + cb->activate(cb->context); + } T_END; + cb->activated = TRUE; + } +} + +void io_loop_context_deactivate(struct ioloop_context *ctx) +{ + struct ioloop_context_callback *cb; + + i_assert(ctx->ioloop->cur_ctx == ctx); + + array_foreach_modifiable(&ctx->callbacks, cb) { + if (!cb->activated) { + /* we just added this callback. don't deactivate it + before it gets first activated. */ + } else { + if (cb->deactivate != NULL) T_BEGIN { + cb->deactivate(cb->context); + } T_END; + cb->activated = FALSE; + } + } + ctx->ioloop->cur_ctx = NULL; + io_loop_context_pop_global_events(ctx); + io_loop_context_remove_deleted_callbacks(ctx); + io_loop_context_unref(&ctx); +} + +void io_loop_context_switch(struct ioloop_context *ctx) +{ + if (ctx->ioloop->cur_ctx != NULL) { + if (ctx->ioloop->cur_ctx == ctx) + return; + io_loop_context_deactivate(ctx->ioloop->cur_ctx); + /* deactivation may remove the cur_ctx */ + if (ctx->ioloop->cur_ctx != NULL) + io_loop_context_unref(&ctx->ioloop->cur_ctx); + } + io_loop_context_activate(ctx); +} + +struct ioloop_context *io_loop_get_current_context(struct ioloop *ioloop) +{ + return ioloop->cur_ctx; +} + +struct io *io_loop_move_io_to(struct ioloop *ioloop, struct io **_io) +{ + struct io *old_io = *_io; + struct io_file *old_io_file, *new_io_file; + + if (old_io == NULL) + return NULL; + + i_assert((old_io->condition & IO_NOTIFY) == 0); + + if (old_io->ioloop == ioloop) + return old_io; + + old_io_file = (struct io_file *)old_io; + new_io_file = io_add_file(ioloop, old_io_file->fd, + old_io->condition, old_io->source_filename, + old_io->source_linenum, + old_io->callback, old_io->context); + if (old_io_file->istream != NULL) { + /* reference before io_remove() */ + new_io_file->istream = old_io_file->istream; + i_stream_ref(new_io_file->istream); + } + if (old_io->pending) + io_set_pending(&new_io_file->io); + io_remove(_io); + if (new_io_file->istream != NULL) { + /* update istream io after it was removed with io_remove() */ + i_stream_set_io(new_io_file->istream, &new_io_file->io); + } + return &new_io_file->io; +} + +struct io *io_loop_move_io(struct io **_io) +{ + return io_loop_move_io_to(current_ioloop, _io); +} + +struct timeout *io_loop_move_timeout_to(struct ioloop *ioloop, + struct timeout **_timeout) +{ + struct timeout *new_to, *old_to = *_timeout; + + if (old_to == NULL || old_to->ioloop == ioloop) + return old_to; + + new_to = timeout_copy(old_to, ioloop); + timeout_remove(_timeout); + return new_to; +} + +struct timeout *io_loop_move_timeout(struct timeout **_timeout) +{ + return io_loop_move_timeout_to(current_ioloop, _timeout); +} + +bool io_loop_have_ios(struct ioloop *ioloop) +{ + return ioloop->io_files != NULL; +} + +bool io_loop_have_immediate_timeouts(struct ioloop *ioloop) +{ + struct timeval tv; + + return io_loop_get_wait_time(ioloop, &tv) == 0; +} + +bool io_loop_is_empty(struct ioloop *ioloop) +{ + return ioloop->io_files == NULL && + priorityq_count(ioloop->timeouts) == 0 && + array_count(&ioloop->timeouts_new) == 0; +} + +uint64_t io_loop_get_wait_usecs(struct ioloop *ioloop) +{ + return ioloop->ioloop_wait_usecs; +} + +enum io_condition io_loop_find_fd_conditions(struct ioloop *ioloop, int fd) +{ + enum io_condition conditions = 0; + struct io_file *io; + + i_assert(fd >= 0); + + for (io = ioloop->io_files; io != NULL; io = io->next) { + if (io->fd == fd) + conditions |= io->io.condition; + } + return conditions; +} + +#undef io_wait_timer_add_to +struct io_wait_timer * +io_wait_timer_add_to(struct ioloop *ioloop, const char *source_filename, + unsigned int source_linenum) +{ + struct io_wait_timer *timer; + + timer = i_new(struct io_wait_timer, 1); + timer->ioloop = ioloop; + timer->source_filename = source_filename; + timer->source_linenum = source_linenum; + DLLIST_PREPEND(&ioloop->wait_timers, timer); + return timer; +} + +#undef io_wait_timer_add +struct io_wait_timer * +io_wait_timer_add(const char *source_filename, unsigned int source_linenum) +{ + return io_wait_timer_add_to(current_ioloop, source_filename, + source_linenum); +} + +struct io_wait_timer *io_wait_timer_move_to(struct io_wait_timer **_timer, + struct ioloop *ioloop) +{ + struct io_wait_timer *timer = *_timer; + + *_timer = NULL; + DLLIST_REMOVE(&timer->ioloop->wait_timers, timer); + DLLIST_PREPEND(&ioloop->wait_timers, timer); + timer->ioloop = ioloop; + return timer; +} + +struct io_wait_timer *io_wait_timer_move(struct io_wait_timer **_timer) +{ + return io_wait_timer_move_to(_timer, current_ioloop); +} + +void io_wait_timer_remove(struct io_wait_timer **_timer) +{ + struct io_wait_timer *timer = *_timer; + + *_timer = NULL; + DLLIST_REMOVE(&timer->ioloop->wait_timers, timer); + i_free(timer); +} + +uint64_t io_wait_timer_get_usecs(struct io_wait_timer *timer) +{ + return timer->usecs; +} + +struct event *io_loop_get_active_global_root(void) +{ + if (current_ioloop == NULL) + return NULL; + if (current_ioloop->cur_ctx == NULL) + return NULL; + return current_ioloop->cur_ctx->root_global_event; +} diff --git a/src/lib/ioloop.h b/src/lib/ioloop.h new file mode 100644 index 0000000..32e2892 --- /dev/null +++ b/src/lib/ioloop.h @@ -0,0 +1,323 @@ +#ifndef IOLOOP_H +#define IOLOOP_H + +#include <sys/time.h> +#include <time.h> + +struct io; +struct timeout; +struct ioloop; +struct istream; + +enum io_condition { + IO_READ = 0x01, + IO_WRITE = 0x02, + /* IO_ERROR can be used to check when writable pipe's reader side + closes the pipe. For other uses IO_READ should work just as well. */ + IO_ERROR = 0x04, + + /* internal */ + IO_NOTIFY = 0x08 +}; + +enum io_notify_result { + /* Notify added successfully */ + IO_NOTIFY_ADDED, + /* Specified file doesn't exist, can't wait on it */ + IO_NOTIFY_NOTFOUND, + /* Can't add notify for specified file. Main reasons for this: + a) No notify support at all, b) Only directory notifies supported */ + IO_NOTIFY_NOSUPPORT +}; + +typedef void io_callback_t(void *context); +typedef void timeout_callback_t(void *context); +typedef void io_loop_time_moved_callback_t(const struct timeval *old_time, + const struct timeval *new_time); +typedef void io_switch_callback_t(struct ioloop *prev_ioloop); +typedef void io_destroy_callback_t(struct ioloop *ioloop); + +/* Time when the I/O loop started calling handlers. + Can be used instead of time(NULL). */ +extern time_t ioloop_time; +extern struct timeval ioloop_timeval; + +extern struct ioloop *current_ioloop; +/* Number of microseconds spent on all the ioloops waiting for themselves. */ +extern uint64_t ioloop_global_wait_usecs; + +/* You can create different handlers for IO_READ and IO_WRITE. IO_READ and + IO_ERROR can't use different handlers (and there's no point anyway). + + Don't try to add multiple handlers for the same type. It's not checked and + the behavior will be undefined. */ +struct io *io_add(int fd, enum io_condition condition, + const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context) ATTR_NULL(5); +#define io_add(fd, condition, callback, context) \ + io_add(fd, condition, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) +struct io *io_add_to(struct ioloop *ioloop, int fd, enum io_condition condition, + const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context) ATTR_NULL(5); +#define io_add_to(ioloop, fd, condition, callback, context) \ + io_add_to(ioloop, fd, condition, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) + +enum io_notify_result +io_add_notify(const char *path, const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context, + struct io **io_r) ATTR_NULL(3); +#define io_add_notify(path, callback, context, io_r) \ + io_add_notify(path, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context, io_r) + +struct io *io_add_istream(struct istream *input, const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context) ATTR_NULL(3); +#define io_add_istream(input, callback, context) \ + io_add_istream(input, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) +struct io *io_add_istream_to(struct ioloop *ioloop, struct istream *input, + const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context) + ATTR_NULL(3); +#define io_add_istream_to(ioloop, input, callback, context) \ + io_add_istream_to(ioloop, input, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) + +/* Remove I/O handler, and set io pointer to NULL. */ +void io_remove(struct io **io); +/* Like io_remove(), but assume that the file descriptor is already closed. + With some backends this simply frees the memory. */ +void io_remove_closed(struct io **io); + +/* Make sure the I/O callback is called by io_loop_run() even if there isn't + any input actually pending currently as seen by the OS. This may be useful + if some of the input has already read into some internal buffer and the + caller wants to handle it the same way as if the fd itself had input. */ +void io_set_pending(struct io *io); +/* Returns TRUE if io_set_pending() has been called for the IO and its callback + hasn't been called yet. */ +bool io_is_pending(struct io *io); +/* If set, this IO shouldn't be the only thing being waited on, because + it would just result in infinite wait. In those situations rather just + crash to indicate that there's a bug. */ +void io_set_never_wait_alone(struct io *io, bool set); + +/* Timeout handlers */ +struct timeout * +timeout_add(unsigned int msecs, const char *source_filename, + unsigned int source_linenum, + timeout_callback_t *callback, void *context) ATTR_NULL(4); +#define timeout_add(msecs, callback, context) \ + timeout_add(msecs, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))) - \ + COMPILE_ERROR_IF_TRUE(__builtin_constant_p(msecs) && \ + ((msecs) > 0 && (msecs) < 1000)), \ + (io_callback_t *)callback, context) +struct timeout * +timeout_add_to(struct ioloop *ioloop, unsigned int msecs, + const char *source_filename, unsigned int source_linenum, + timeout_callback_t *callback, void *context) ATTR_NULL(4); +#define timeout_add_to(ioloop, msecs, callback, context) \ + timeout_add_to(ioloop, msecs, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))) - \ + COMPILE_ERROR_IF_TRUE(__builtin_constant_p(msecs) && \ + ((msecs) > 0 && (msecs) < 1000)), \ + (io_callback_t *)callback, context) + +struct timeout * +timeout_add_short(unsigned int msecs, const char *source_filename, + unsigned int source_linenum, + timeout_callback_t *callback, void *context) ATTR_NULL(4); +#define timeout_add_short(msecs, callback, context) \ + timeout_add_short(msecs, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) +struct timeout * +timeout_add_short_to(struct ioloop *ioloop, unsigned int msecs, + const char *source_filename, unsigned int source_linenum, + timeout_callback_t *callback, void *context) ATTR_NULL(4); +#define timeout_add_short_to(ioloop, msecs, callback, context) \ + timeout_add_short_to(ioloop, msecs, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) + +struct timeout * +timeout_add_absolute(const struct timeval *time, + const char *source_filename, + unsigned int source_linenum, + timeout_callback_t *callback, void *context) ATTR_NULL(4); +#define timeout_add_absolute(time, callback, context) \ + timeout_add_absolute(time, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) +struct timeout * +timeout_add_absolute_to(struct ioloop *ioloop, + const struct timeval *time, + const char *source_filename, + unsigned int source_linenum, + timeout_callback_t *callback, void *context) ATTR_NULL(4); +#define timeout_add_absolute_to(ioloop, time, callback, context) \ + timeout_add_absolute_to(ioloop, time, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) + +/* Remove timeout handler, and set timeout pointer to NULL. */ +void timeout_remove(struct timeout **timeout); +/* Reset timeout so it's next run after now+msecs. */ +void timeout_reset(struct timeout *timeout); + +/* Refresh ioloop_time and ioloop_timeval variables. */ +void io_loop_time_refresh(void); + +void io_loop_run(struct ioloop *ioloop); +/* Stop the ioloop immediately. No further IO or timeout callbacks are called. + Warning: This is not safe to be called in non-delayed signal handlers. */ +void io_loop_stop(struct ioloop *ioloop); +/* Stop ioloop after finishing all the pending IOs and timeouts. */ +void io_loop_stop_delayed(struct ioloop *ioloop); + +bool io_loop_is_running(struct ioloop *ioloop); + +/* call these if you wish to run the iteration only once */ +void io_loop_set_running(struct ioloop *ioloop); +void io_loop_handler_run(struct ioloop *ioloop); + +struct ioloop *io_loop_create(void); +/* Specify the maximum number of fds we're expecting to use. */ +void io_loop_set_max_fd_count(struct ioloop *ioloop, unsigned int max_fds); +/* Destroy I/O loop and set ioloop pointer to NULL. */ +void io_loop_destroy(struct ioloop **ioloop); + +/* If time moves backwards or jumps forwards call the callback. */ +void io_loop_set_time_moved_callback(struct ioloop *ioloop, + io_loop_time_moved_callback_t *callback); + +/* Change the current_ioloop. */ +void io_loop_set_current(struct ioloop *ioloop); +/* Return the root ioloop. */ +struct ioloop *io_loop_get_root(void); +/* Call the callback whenever ioloop is changed. */ +void io_loop_add_switch_callback(io_switch_callback_t *callback); +void io_loop_remove_switch_callback(io_switch_callback_t *callback); +/* Call the callback whenever ioloop is destroyed. */ +void io_loop_add_destroy_callback(io_destroy_callback_t *callback); +void io_loop_remove_destroy_callback(io_destroy_callback_t *callback); + +/* Create a new ioloop context. While the context is activated, it's + automatically attached to all the following I/Os and timeouts that are + added until the context is deactivated (e.g. returning to back to a running + ioloop). Whenever such added I/O or timeout callback is called, this context + is automatically activated. + + After the context is created, callbacks should be added to it and the + context should be activated with either io_loop_context_activate() or + io_loop_context_switch(). */ +struct ioloop_context *io_loop_context_new(struct ioloop *ioloop); +void io_loop_context_ref(struct ioloop_context *ctx); +void io_loop_context_unref(struct ioloop_context **ctx); +/* Call the activate callback when this context is activated (I/O callback is + about to be called), and the deactivate callback when the context is + deactivated (I/O callback has returned). You can add multiple callbacks. + + The ioloop context is a global state, so only a single context can be active + at a time. The callbacks are guaranteed to be called only at their proper + states, i.e. activate() callback is called only when switching from + no context to the active context, and deactive() is called only when + switching from previously activated context into no context. No context is + active at a time when the ioloop is destroyed. */ +void io_loop_context_add_callbacks(struct ioloop_context *ctx, + io_callback_t *activate, + io_callback_t *deactivate, void *context); +#define io_loop_context_add_callbacks(ctx, activate, deactivate, context) \ + io_loop_context_add_callbacks(ctx, 1 ? (io_callback_t *)activate : \ + CALLBACK_TYPECHECK(activate, void (*)(typeof(context))) - \ + CALLBACK_TYPECHECK(deactivate, void (*)(typeof(context))), \ + (io_callback_t *)deactivate, context) +/* Remove callbacks with the given callbacks and context. */ +void io_loop_context_remove_callbacks(struct ioloop_context *ctx, + io_callback_t *activate, + io_callback_t *deactivate, void *context); +#define io_loop_context_remove_callbacks(ctx, activate, deactivate, context) \ + io_loop_context_remove_callbacks(ctx, 1 ? (io_callback_t *)activate : \ + CALLBACK_TYPECHECK(activate, void (*)(typeof(context))) - \ + CALLBACK_TYPECHECK(deactivate, void (*)(typeof(context))), \ + (io_callback_t *)deactivate, context) +/* Returns the current context set to ioloop. */ +struct ioloop_context *io_loop_get_current_context(struct ioloop *ioloop); + +/* Explicitly activate an ioloop context. There must not be any context active + at the moment, so this most likely shouldn't be called while ioloop is + running. An activated context must be explicitly deactivated with + io_loop_context_deactivate() before the ioloop is destroyed, or before + any ioloop is run. */ +void io_loop_context_activate(struct ioloop_context *ctx); +/* Explicitly deactivate an ioloop context. The given context must be currently + active or it assert-crashes. This should be called only after a context + was explicitly activated with io_loop_context_activate(). */ +void io_loop_context_deactivate(struct ioloop_context *ctx); +/* If there's an active ioloop context, deactivate it. Then activate the given + context. Usually used after creating a new context. */ +void io_loop_context_switch(struct ioloop_context *ctx); + +/* Returns fd, which contains all of the ioloop's current notifications. + When it becomes readable, there is a new notification. Calling this function + stops the existing notifications in the ioloop from working anymore. + This function's main idea is that the fd can be passed to another process, + which can use it to find out if an interesting notification happens. + Returns fd on success, -1 on error. */ +int io_loop_extract_notify_fd(struct ioloop *ioloop); + +/* IO wait timers can be used to track how much time the io_wait_timer has + spent on waiting in its ioloops. This is similar to + io_loop_get_wait_usecs(), but it's easier to use when the wait time needs + to be tracked across multiple ioloops. */ +struct io_wait_timer * +io_wait_timer_add(const char *source_filename, unsigned int source_linenum); +#define io_wait_timer_add() \ + io_wait_timer_add(__FILE__, __LINE__) +struct io_wait_timer * +io_wait_timer_add_to(struct ioloop *ioloop, const char *source_filename, + unsigned int source_linenum); +#define io_wait_timer_add_to(ioloop) \ + io_wait_timer_add_to(ioloop, __FILE__, __LINE__) + +struct io_wait_timer *io_wait_timer_move(struct io_wait_timer **timer); +struct io_wait_timer *io_wait_timer_move_to(struct io_wait_timer **timer, + struct ioloop *ioloop); +void io_wait_timer_remove(struct io_wait_timer **timer); +uint64_t io_wait_timer_get_usecs(struct io_wait_timer *timer); + +/* Move the given I/O into the provided/current I/O loop if it's not already + there. New I/O is returned, while the old one is freed. */ +struct io *io_loop_move_io_to(struct ioloop *ioloop, struct io **_io); +struct io *io_loop_move_io(struct io **io); +/* Like io_loop_move_io(), but for timeouts. */ +struct timeout *io_loop_move_timeout_to(struct ioloop *ioloop, + struct timeout **timeout); +struct timeout *io_loop_move_timeout(struct timeout **timeout); +/* Returns TRUE if any IOs have been added to the ioloop. */ +bool io_loop_have_ios(struct ioloop *ioloop); +/* Returns TRUE if there is a pending timeout that is going to be run + immediately. */ +bool io_loop_have_immediate_timeouts(struct ioloop *ioloop); +/* Returns TRUE if there are no IOs or timeouts in the ioloop. */ +bool io_loop_is_empty(struct ioloop *ioloop); +/* Returns number of microseconds spent on the ioloop waiting itself. */ +uint64_t io_loop_get_wait_usecs(struct ioloop *ioloop); +/* Return all io conditions added for the given fd. This needs to scan through + all the file ios in the ioloop. */ +enum io_condition io_loop_find_fd_conditions(struct ioloop *ioloop, int fd); + +#endif diff --git a/src/lib/iostream-private.h b/src/lib/iostream-private.h new file mode 100644 index 0000000..9aa72e9 --- /dev/null +++ b/src/lib/iostream-private.h @@ -0,0 +1,51 @@ +#ifndef IOSTREAM_PRIVATE_H +#define IOSTREAM_PRIVATE_H + +#include "iostream.h" + +/* This file is private to input stream and output stream implementations */ + +struct iostream_destroy_callback { + void (*callback)(void *context); + void *context; +}; + +struct iostream_private { + int refcount; + char *name; + char *error; + struct ioloop *ioloop; + + void (*close)(struct iostream_private *streami, bool close_parent); + void (*destroy)(struct iostream_private *stream); + void (*set_max_buffer_size)(struct iostream_private *stream, + size_t max_size); + + ARRAY(struct iostream_destroy_callback) destroy_callbacks; +}; + +void io_stream_init(struct iostream_private *stream); +void io_stream_ref(struct iostream_private *stream); +bool io_stream_unref(struct iostream_private *stream); +void io_stream_free(struct iostream_private *stream); +void io_stream_close(struct iostream_private *stream, bool close_parent); +void io_stream_set_max_buffer_size(struct iostream_private *stream, + size_t max_size); +void io_stream_add_destroy_callback(struct iostream_private *stream, + void (*callback)(void *), void *context); +void io_stream_remove_destroy_callback(struct iostream_private *stream, + void (*callback)(void *)); +/* Set a specific error for the stream. This shouldn't be used for regular + syscall errors where stream's errno is enough, since it's used by default. + The stream errno must always be set even if the error string is also set. + Setting this error replaces the previously set error. */ +void io_stream_set_error(struct iostream_private *stream, + const char *fmt, ...) ATTR_FORMAT(2, 3); +void io_stream_set_verror(struct iostream_private *stream, + const char *fmt, va_list args) ATTR_FORMAT(2, 0); + +void io_stream_switch_ioloop_to(struct iostream_private *stream, + struct ioloop *ioloop); +struct ioloop *io_stream_get_ioloop(struct iostream_private *stream); + +#endif diff --git a/src/lib/iostream-proxy.c b/src/lib/iostream-proxy.c new file mode 100644 index 0000000..2663420 --- /dev/null +++ b/src/lib/iostream-proxy.c @@ -0,0 +1,173 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file + */ +#include "lib.h" +#include "buffer.h" +#include "str.h" +#include "iostream-pump.h" +#include "iostream-proxy.h" +#include <unistd.h> + +#undef iostream_proxy_set_completion_callback + +struct iostream_proxy { + struct iostream_pump *ltr; + struct iostream_pump *rtl; + + unsigned int ref; + + iostream_proxy_callback_t *callback; + void *context; +}; + +static void +iostream_proxy_completion(struct iostream_proxy *proxy, + enum iostream_proxy_side side, + enum iostream_pump_status pump_status) +{ + enum iostream_proxy_status status; + + switch (pump_status) { + case IOSTREAM_PUMP_STATUS_INPUT_EOF: + status = IOSTREAM_PROXY_STATUS_INPUT_EOF; + break; + case IOSTREAM_PUMP_STATUS_INPUT_ERROR: + status = IOSTREAM_PROXY_STATUS_INPUT_ERROR; + break; + case IOSTREAM_PUMP_STATUS_OUTPUT_ERROR: + status = IOSTREAM_PROXY_STATUS_OTHER_SIDE_OUTPUT_ERROR; + break; + default: + i_unreached(); + } + proxy->callback(side, status, proxy->context); +} + +static +void iostream_proxy_rtl_completion(enum iostream_pump_status status, + struct iostream_proxy *proxy) +{ + iostream_proxy_completion(proxy, IOSTREAM_PROXY_SIDE_RIGHT, status); +} + +static +void iostream_proxy_ltr_completion(enum iostream_pump_status status, + struct iostream_proxy *proxy) +{ + iostream_proxy_completion(proxy, IOSTREAM_PROXY_SIDE_LEFT, status); +} + +struct iostream_proxy * +iostream_proxy_create(struct istream *left_input, struct ostream *left_output, + struct istream *right_input, struct ostream *right_output) +{ + i_assert(left_input != NULL && + right_input != NULL && + left_output != NULL && + right_output != NULL); + + /* create proxy */ + struct iostream_proxy *proxy = i_new(struct iostream_proxy, 1); + + proxy->ltr = iostream_pump_create(left_input, right_output); + proxy->rtl = iostream_pump_create(right_input, left_output); + + iostream_pump_set_completion_callback(proxy->ltr, iostream_proxy_ltr_completion, proxy); + iostream_pump_set_completion_callback(proxy->rtl, iostream_proxy_rtl_completion, proxy); + + proxy->ref = 1; + + return proxy; +} + +void iostream_proxy_start(struct iostream_proxy *proxy) +{ + i_assert(proxy != NULL); + i_assert(proxy->callback != NULL); + + iostream_pump_start(proxy->rtl); + iostream_pump_start(proxy->ltr); +} + +void iostream_proxy_set_completion_callback(struct iostream_proxy *proxy, + iostream_proxy_callback_t *callback, + void *context) +{ + i_assert(proxy != NULL); + + proxy->callback = callback; + proxy->context = context; +} + +struct istream *iostream_proxy_get_istream(struct iostream_proxy *proxy, enum iostream_proxy_side side) +{ + i_assert(proxy != NULL); + + switch(side) { + case IOSTREAM_PROXY_SIDE_LEFT: return iostream_pump_get_input(proxy->ltr); + case IOSTREAM_PROXY_SIDE_RIGHT: return iostream_pump_get_input(proxy->rtl); + default: i_unreached(); + } +} + +struct ostream *iostream_proxy_get_ostream(struct iostream_proxy *proxy, enum iostream_proxy_side side) +{ + i_assert(proxy != NULL); + + switch(side) { + case IOSTREAM_PROXY_SIDE_LEFT: return iostream_pump_get_output(proxy->ltr); + case IOSTREAM_PROXY_SIDE_RIGHT: return iostream_pump_get_output(proxy->rtl); + default: i_unreached(); + } +} + +void iostream_proxy_ref(struct iostream_proxy *proxy) +{ + i_assert(proxy != NULL && proxy->ref > 0); + proxy->ref++; +} + +void iostream_proxy_unref(struct iostream_proxy **proxy_r) +{ + struct iostream_proxy *proxy; + + if (proxy_r == NULL || *proxy_r == NULL) + return; + + proxy = *proxy_r; + *proxy_r = NULL; + + i_assert(proxy->ref > 0); + if (--proxy->ref == 0) { + /* pumps will call stop internally + if refcount drops to 0 */ + iostream_pump_unref(&proxy->ltr); + iostream_pump_unref(&proxy->rtl); + i_free(proxy); + } +} + +void iostream_proxy_stop(struct iostream_proxy *proxy) +{ + i_assert(proxy != NULL); + iostream_pump_stop(proxy->ltr); + iostream_pump_stop(proxy->rtl); +} + +bool iostream_proxy_is_waiting_output(struct iostream_proxy *proxy, + enum iostream_proxy_side side) +{ + switch (side) { + case IOSTREAM_PROXY_SIDE_LEFT: + return iostream_pump_is_waiting_output(proxy->ltr); + case IOSTREAM_PROXY_SIDE_RIGHT: + return iostream_pump_is_waiting_output(proxy->rtl); + } + i_unreached(); +} + +void iostream_proxy_switch_ioloop(struct iostream_proxy *proxy) +{ + i_assert(proxy != NULL); + iostream_pump_switch_ioloop(proxy->ltr); + iostream_pump_switch_ioloop(proxy->rtl); +} diff --git a/src/lib/iostream-proxy.h b/src/lib/iostream-proxy.h new file mode 100644 index 0000000..5e8850f --- /dev/null +++ b/src/lib/iostream-proxy.h @@ -0,0 +1,87 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file + */ +#ifndef IOSTREAM_PROXY_H +#define IOSTREAM_PROXY_H 1 + +/** + +iostream-proxy +============= + +This construct will proxy data between two pairs of +istream and ostream. Data is proxied from left to right +and right to left using iostream-pump. + +The proxy requires you to provide completion callback. The +completion callback is called with success parameter to +indicate whether it ended with error. + +The istreams and ostreams are reffed on creation and unreffed +on unref. + +**/ + +struct istream; +struct ostream; +struct iostream_proxy; + +enum iostream_proxy_side { + /* Input is coming from left side's istream and is proxied to + right side's ostream. */ + IOSTREAM_PROXY_SIDE_LEFT, + /* Input is coming from right side's istream and is proxied to + left side's ostream. */ + IOSTREAM_PROXY_SIDE_RIGHT +}; + +enum iostream_proxy_status { + /* proxy succeeded - EOF received from istream and all output was + written successfully to ostream. */ + IOSTREAM_PROXY_STATUS_INPUT_EOF, + /* proxy failed - istream returned an error */ + IOSTREAM_PROXY_STATUS_INPUT_ERROR, + /* proxy failed - other side's ostream returned an error */ + IOSTREAM_PROXY_STATUS_OTHER_SIDE_OUTPUT_ERROR, +}; + +/* The callback maybe be called once or twice. Usually the first call should + destroy the proxy, but it's possible for it to just wait for the other + direction of the proxy to finish as well and call the callback. + + Note that the sides mean which side is the reader side. If the failure is in + ostream, it's the other side's ostream that failed. So for example if + side=left, the write failed to the right side's ostream. + + The callback is called when the proxy succeeds or fails due to + iostreams. (It's not called if proxy is destroyed.) */ +typedef void iostream_proxy_callback_t(enum iostream_proxy_side side, + enum iostream_proxy_status status, + void *context); + +struct iostream_proxy * +iostream_proxy_create(struct istream *left_input, struct ostream *left_output, + struct istream *right_input, struct ostream *right_output); + +struct istream *iostream_proxy_get_istream(struct iostream_proxy *proxy, enum iostream_proxy_side); +struct ostream *iostream_proxy_get_ostream(struct iostream_proxy *proxy, enum iostream_proxy_side); + +void iostream_proxy_start(struct iostream_proxy *proxy); +void iostream_proxy_stop(struct iostream_proxy *proxy); + +/* See iostream_pump_is_waiting_output() */ +bool iostream_proxy_is_waiting_output(struct iostream_proxy *proxy, + enum iostream_proxy_side side); + +void iostream_proxy_set_completion_callback(struct iostream_proxy *proxy, + iostream_proxy_callback_t *callback, void *context); +#define iostream_proxy_set_completion_callback(proxy, callback, context) \ + iostream_proxy_set_completion_callback(proxy, (iostream_proxy_callback_t *)callback, \ + TRUE ? context : \ + CALLBACK_TYPECHECK(callback, void (*)(enum iostream_proxy_side side, enum iostream_proxy_status, typeof(context)))) + +void iostream_proxy_ref(struct iostream_proxy *proxy); +void iostream_proxy_unref(struct iostream_proxy **proxy_r); + +void iostream_proxy_switch_ioloop(struct iostream_proxy *proxy); + +#endif diff --git a/src/lib/iostream-pump.c b/src/lib/iostream-pump.c new file mode 100644 index 0000000..cebae3d --- /dev/null +++ b/src/lib/iostream-pump.c @@ -0,0 +1,251 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "str.h" +#include "iostream-pump.h" +#include "istream.h" +#include "ostream.h" +#include <unistd.h> + +#undef iostream_pump_set_completion_callback + +struct iostream_pump { + int refcount; + + struct istream *input; + struct ostream *output; + + struct io *io; + + iostream_pump_callback_t *callback; + void *context; + + bool waiting_output; + bool completed; +}; + +static void iostream_pump_copy(struct iostream_pump *pump) +{ + enum ostream_send_istream_result res; + size_t old_size; + + o_stream_cork(pump->output); + old_size = o_stream_get_max_buffer_size(pump->output); + o_stream_set_max_buffer_size(pump->output, + I_MIN(IO_BLOCK_SIZE, + o_stream_get_max_buffer_size(pump->output))); + res = o_stream_send_istream(pump->output, pump->input); + o_stream_set_max_buffer_size(pump->output, old_size); + o_stream_uncork(pump->output); + + switch(res) { + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: + io_remove(&pump->io); + pump->callback(IOSTREAM_PUMP_STATUS_INPUT_ERROR, + pump->context); + return; + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: + io_remove(&pump->io); + pump->callback(IOSTREAM_PUMP_STATUS_OUTPUT_ERROR, + pump->context); + return; + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: + i_assert(!pump->output->blocking); + pump->waiting_output = TRUE; + io_remove(&pump->io); + return; + case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: + pump->waiting_output = FALSE; + io_remove(&pump->io); + /* flush it */ + switch (o_stream_flush(pump->output)) { + case -1: + pump->callback(IOSTREAM_PUMP_STATUS_OUTPUT_ERROR, + pump->context); + break; + case 0: + pump->waiting_output = TRUE; + pump->completed = TRUE; + break; + default: + pump->callback(IOSTREAM_PUMP_STATUS_INPUT_EOF, + pump->context); + break; + } + return; + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: + i_assert(!pump->input->blocking); + pump->waiting_output = FALSE; + return; + } + i_unreached(); +} + +static int iostream_pump_flush(struct iostream_pump *pump) +{ + int ret; + + if ((ret = o_stream_flush(pump->output)) <= 0) { + if (ret < 0) { + pump->callback(IOSTREAM_PUMP_STATUS_OUTPUT_ERROR, + pump->context); + } + return ret; + } + pump->waiting_output = FALSE; + if (pump->completed) { + pump->callback(IOSTREAM_PUMP_STATUS_INPUT_EOF, pump->context); + return 1; + } + + if (pump->input->blocking) + iostream_pump_copy(pump); + else if (pump->io == NULL) { + pump->io = io_add_istream(pump->input, + iostream_pump_copy, pump); + io_set_pending(pump->io); + } + return ret; +} + +struct iostream_pump * +iostream_pump_create(struct istream *input, struct ostream *output) +{ + struct iostream_pump *pump; + + i_assert(input != NULL && + output != NULL); + i_assert(!input->blocking || !output->blocking); + + /* ref streams */ + i_stream_ref(input); + o_stream_ref(output); + + /* create pump */ + pump = i_new(struct iostream_pump, 1); + pump->refcount = 1; + pump->input = input; + pump->output = output; + + return pump; +} + +void iostream_pump_start(struct iostream_pump *pump) +{ + i_assert(pump != NULL); + i_assert(pump->callback != NULL); + + /* add flush handler */ + if (!pump->output->blocking) { + o_stream_set_flush_callback(pump->output, + iostream_pump_flush, pump); + } + + /* make IO objects */ + if (pump->input->blocking) { + i_assert(!pump->output->blocking); + o_stream_set_flush_pending(pump->output, TRUE); + } else { + pump->io = io_add_istream(pump->input, + iostream_pump_copy, pump); + io_set_pending(pump->io); + } +} + +struct istream *iostream_pump_get_input(struct iostream_pump *pump) +{ + i_assert(pump != NULL); + return pump->input; +} + +struct ostream *iostream_pump_get_output(struct iostream_pump *pump) +{ + i_assert(pump != NULL); + return pump->output; +} + +void iostream_pump_set_completion_callback(struct iostream_pump *pump, + iostream_pump_callback_t *callback, + void *context) +{ + i_assert(pump != NULL); + pump->callback = callback; + pump->context = context; +} + +void iostream_pump_ref(struct iostream_pump *pump) +{ + i_assert(pump != NULL); + i_assert(pump->refcount > 0); + pump->refcount++; +} + +void iostream_pump_unref(struct iostream_pump **_pump) +{ + i_assert(_pump != NULL); + struct iostream_pump *pump = *_pump; + + if (pump == NULL) + return; + + i_assert(pump->refcount > 0); + + *_pump = NULL; + + if (--pump->refcount > 0) + return; + + iostream_pump_stop(pump); + + o_stream_unref(&pump->output); + i_stream_unref(&pump->input); + i_free(pump); +} + +void iostream_pump_destroy(struct iostream_pump **_pump) +{ + i_assert(_pump != NULL); + struct iostream_pump *pump = *_pump; + + if (pump == NULL) + return; + + *_pump = NULL; + + iostream_pump_stop(pump); + o_stream_unref(&pump->output); + i_stream_unref(&pump->input); + + iostream_pump_unref(&pump); +} + +void iostream_pump_stop(struct iostream_pump *pump) +{ + i_assert(pump != NULL); + + if (pump->output != NULL) + o_stream_unset_flush_callback(pump->output); + + io_remove(&pump->io); +} + +bool iostream_pump_is_waiting_output(struct iostream_pump *pump) +{ + return pump->waiting_output; +} + +void iostream_pump_switch_ioloop_to(struct iostream_pump *pump, + struct ioloop *ioloop) +{ + i_assert(pump != NULL); + if (pump->io != NULL) + pump->io = io_loop_move_io_to(ioloop, &pump->io); + o_stream_switch_ioloop_to(pump->output, ioloop); + i_stream_switch_ioloop_to(pump->input, ioloop); +} + +void iostream_pump_switch_ioloop(struct iostream_pump *pump) +{ + iostream_pump_switch_ioloop_to(pump, current_ioloop); +} diff --git a/src/lib/iostream-pump.h b/src/lib/iostream-pump.h new file mode 100644 index 0000000..d7317ae --- /dev/null +++ b/src/lib/iostream-pump.h @@ -0,0 +1,69 @@ +#ifndef IOSTREAM_PUMP_H +#define IOSTREAM_PUMP_H + +/* iostream-pump + ============= + + This construct pumps data from istream to ostream asynchronously. + + The pump requires you to provide completion callback. The completion + callback is called with success parameter to indicate whether it ended + with error. + + The istream and ostream are reffed on creation and unreffed + on unref. + */ + +struct istream; +struct ostream; +struct ioloop; +struct iostream_pump; + +enum iostream_pump_status { + /* pump succeeded - EOF received from istream and all output was + written successfully to ostream. */ + IOSTREAM_PUMP_STATUS_INPUT_EOF, + /* pump failed - istream returned an error */ + IOSTREAM_PUMP_STATUS_INPUT_ERROR, + /* pump failed - ostream returned an error */ + IOSTREAM_PUMP_STATUS_OUTPUT_ERROR, +}; + +/* The callback is called once when the pump succeeds or fails due to + iostreams. (It's not called if pump is destroyed.) */ +typedef void iostream_pump_callback_t(enum iostream_pump_status status, + void *context); + +struct iostream_pump * +iostream_pump_create(struct istream *input, struct ostream *output); + +struct istream *iostream_pump_get_input(struct iostream_pump *pump); +struct ostream *iostream_pump_get_output(struct iostream_pump *pump); + +void iostream_pump_start(struct iostream_pump *pump); +void iostream_pump_stop(struct iostream_pump *pump); + +void iostream_pump_ref(struct iostream_pump *pump); +void iostream_pump_unref(struct iostream_pump **_pump); +void iostream_pump_destroy(struct iostream_pump **_pump); + +void iostream_pump_set_completion_callback(struct iostream_pump *pump, + iostream_pump_callback_t *callback, + void *context); +#define iostream_pump_set_completion_callback(pump, callback, context) \ + iostream_pump_set_completion_callback(pump, \ + (iostream_pump_callback_t *)callback, TRUE ? context : \ + CALLBACK_TYPECHECK(callback, \ + void (*)(enum iostream_pump_status, typeof(context)))) + +/* Returns TRUE if the pump is currently only writing to the ostream. The input + listener has been removed either because the ostream buffer is full or + because the istream already returned EOF. This function can also be called + from the completion callback in error conditions. */ +bool iostream_pump_is_waiting_output(struct iostream_pump *pump); + +void iostream_pump_switch_ioloop_to(struct iostream_pump *pump, + struct ioloop *ioloop); +void iostream_pump_switch_ioloop(struct iostream_pump *pump); + +#endif diff --git a/src/lib/iostream-rawlog-private.h b/src/lib/iostream-rawlog-private.h new file mode 100644 index 0000000..9a409e0 --- /dev/null +++ b/src/lib/iostream-rawlog-private.h @@ -0,0 +1,25 @@ +#ifndef IOSTREAM_RAWLOG_PRIVATE_H +#define IOSTREAM_RAWLOG_PRIVATE_H + +#include "iostream-rawlog.h" + +#define IOSTREAM_RAWLOG_MAX_PREFIX_LEN 3 + +struct rawlog_iostream { + struct iostream_private *iostream; + enum iostream_rawlog_flags flags; + + struct ostream *rawlog_output; + buffer_t *buffer; + + bool input; + bool line_continued; +}; + +void iostream_rawlog_init(struct rawlog_iostream *rstream, + enum iostream_rawlog_flags flags, bool input); +void iostream_rawlog_write(struct rawlog_iostream *rstream, + const unsigned char *data, size_t size); +void iostream_rawlog_close(struct rawlog_iostream *rstream); + +#endif diff --git a/src/lib/iostream-rawlog.c b/src/lib/iostream-rawlog.c new file mode 100644 index 0000000..5805c9b --- /dev/null +++ b/src/lib/iostream-rawlog.c @@ -0,0 +1,298 @@ +/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "hostpid.h" +#include "ioloop.h" +#include "buffer.h" +#include "str.h" +#include "net.h" +#include "write-full.h" +#include "time-util.h" +#include "istream.h" +#include "ostream.h" +#include "iostream-private.h" +#include "iostream-rawlog-private.h" +#include "istream-rawlog.h" +#include "ostream-rawlog.h" +#include "iostream-rawlog.h" + +#include <unistd.h> +#include <fcntl.h> + +#define RAWLOG_MAX_LINE_LEN 8192 + +static void +rawlog_write_timestamp(struct rawlog_iostream *rstream, bool line_ends) +{ + unsigned char data[MAX_INT_STRLEN + 1 + 6 + 1 + 3]; + buffer_t buf; + + if ((rstream->flags & IOSTREAM_RAWLOG_FLAG_TIMESTAMP) == 0) + return; + + buffer_create_from_data(&buf, data, sizeof(data)); + str_printfa(&buf, "%"PRIdTIME_T".%06u ", + ioloop_timeval.tv_sec, + (unsigned int)ioloop_timeval.tv_usec); + if ((rstream->flags & IOSTREAM_RAWLOG_FLAG_BUFFERED) != 0) { + str_append_c(&buf, rstream->input ? 'I' : 'O'); + str_append_c(&buf, line_ends ? ':' : '>'); + str_append_c(&buf, ' '); + } + o_stream_nsend(rstream->rawlog_output, buf.data, buf.used); +} + +void iostream_rawlog_init(struct rawlog_iostream *rstream, + enum iostream_rawlog_flags flags, bool input) +{ + rstream->flags = flags; + rstream->input = input; + if ((rstream->flags & IOSTREAM_RAWLOG_FLAG_BUFFERED) != 0) + rstream->buffer = buffer_create_dynamic(default_pool, 1024); +} + +static void +iostream_rawlog_write_buffered(struct rawlog_iostream *rstream, + const unsigned char *data, size_t size) +{ + const unsigned char *p; + size_t pos; + bool line_ends; + + while (size > 0) { + p = memchr(data, '\n', size); + if (p != NULL) { + line_ends = TRUE; + pos = p-data + 1; + } else if (rstream->buffer->used + size < RAWLOG_MAX_LINE_LEN) { + buffer_append(rstream->buffer, data, size); + break; + } else { + line_ends = FALSE; + pos = size; + } + + rawlog_write_timestamp(rstream, line_ends); + if (rstream->buffer->used > 0) { + o_stream_nsend(rstream->rawlog_output, + rstream->buffer->data, + rstream->buffer->used); + buffer_set_used_size(rstream->buffer, 0); + } + o_stream_nsend(rstream->rawlog_output, data, pos); + + data += pos; + size -= pos; + } +} + +static void +iostream_rawlog_write_unbuffered(struct rawlog_iostream *rstream, + const unsigned char *data, size_t size) +{ + size_t i, start; + + if (!rstream->line_continued) + rawlog_write_timestamp(rstream, TRUE); + + for (start = 0, i = 1; i < size; i++) { + if (data[i-1] == '\n') { + o_stream_nsend(rstream->rawlog_output, + data + start, i - start); + rawlog_write_timestamp(rstream, TRUE); + start = i; + } + } + if (start != size) { + o_stream_nsend(rstream->rawlog_output, + data + start, size - start); + } + rstream->line_continued = data[size-1] != '\n'; +} + +void iostream_rawlog_write(struct rawlog_iostream *rstream, + const unsigned char *data, size_t size) +{ + if (size == 0 || rstream->rawlog_output == NULL) + return; + + io_loop_time_refresh(); + + o_stream_cork(rstream->rawlog_output); + if ((rstream->flags & IOSTREAM_RAWLOG_FLAG_BUFFERED) != 0) + iostream_rawlog_write_buffered(rstream, data, size); + else + iostream_rawlog_write_unbuffered(rstream, data, size); + o_stream_uncork(rstream->rawlog_output); + + if (o_stream_flush(rstream->rawlog_output) < 0) { + i_error("write(%s) failed: %s", + o_stream_get_name(rstream->rawlog_output), + o_stream_get_error(rstream->rawlog_output)); + iostream_rawlog_close(rstream); + } +} + +void iostream_rawlog_close(struct rawlog_iostream *rstream) +{ + o_stream_unref(&rstream->rawlog_output); + buffer_free(&rstream->buffer); +} + +static void +iostream_rawlog_create_fd(int fd, const char *path, struct istream **input, + struct ostream **output) +{ + struct istream *old_input; + struct ostream *old_output; + + old_input = *input; + old_output = *output; + *input = i_stream_create_rawlog(old_input, path, fd, + IOSTREAM_RAWLOG_FLAG_BUFFERED | + IOSTREAM_RAWLOG_FLAG_TIMESTAMP); + *output = o_stream_create_rawlog(old_output, path, fd, + IOSTREAM_RAWLOG_FLAG_AUTOCLOSE | + IOSTREAM_RAWLOG_FLAG_BUFFERED | + IOSTREAM_RAWLOG_FLAG_TIMESTAMP); + i_stream_unref(&old_input); + o_stream_unref(&old_output); +} + +static int +iostream_rawlog_try_create_tcp(const char *path, + struct istream **input, struct ostream **output) +{ + const char *host; + struct ip_addr *ips; + unsigned int ips_count; + in_port_t port; + int ret, fd; + + /* tcp:host:port */ + if (!str_begins(path, "tcp:")) + return 0; + path += 4; + + if (strchr(path, '/') != NULL) + return 0; + if (net_str2hostport(path, 0, &host, &port) < 0 || port == 0) + return 0; + + ret = net_gethostbyname(host, &ips, &ips_count); + if (ret != 0) { + i_error("net_gethostbyname(%s) failed: %s", host, + net_gethosterror(ret)); + return -1; + } + fd = net_connect_ip_blocking(&ips[0], port, NULL); + if (fd == -1) { + i_error("connect(%s:%u) failed: %m", net_ip2addr(&ips[0]), port); + return -1; + } + iostream_rawlog_create_fd(fd, path, input, output); + return 1; +} + +int iostream_rawlog_create(const char *dir, struct istream **input, + struct ostream **output) +{ + static unsigned int counter = 0; + const char *timestamp, *prefix; + struct stat st; + int ret; + + if ((ret = iostream_rawlog_try_create_tcp(dir, input, output)) != 0) + return ret < 0 ? -1 : 0; + if (stat(dir, &st) < 0) { + if (errno != ENOENT && errno != EACCES) + i_error("rawlog: stat(%s) failed: %m", dir); + return -1; + } + + timestamp = t_strflocaltime("%Y%m%d-%H%M%S", ioloop_time); + + counter++; + prefix = t_strdup_printf("%s/%s.%s.%u", dir, timestamp, my_pid, counter); + return iostream_rawlog_create_prefix(prefix, input, output); +} + +int iostream_rawlog_create_prefix(const char *prefix, struct istream **input, + struct ostream **output) +{ + const char *in_path, *out_path; + struct istream *old_input; + struct ostream *old_output; + int in_fd, out_fd; + + in_path = t_strdup_printf("%s.in", prefix); + in_fd = open(in_path, O_CREAT | O_APPEND | O_WRONLY, 0600); + if (in_fd == -1) { + i_error("creat(%s) failed: %m", in_path); + return -1; + } + + out_path = t_strdup_printf("%s.out", prefix); + out_fd = open(out_path, O_CREAT | O_APPEND | O_WRONLY, 0600); + if (out_fd == -1) { + i_error("creat(%s) failed: %m", out_path); + i_close_fd(&in_fd); + i_unlink(in_path); + return -1; + } + + old_input = *input; + old_output = *output; + + *input = i_stream_create_rawlog(old_input, in_path, in_fd, + IOSTREAM_RAWLOG_FLAG_AUTOCLOSE | + IOSTREAM_RAWLOG_FLAG_TIMESTAMP); + *output = o_stream_create_rawlog(old_output, out_path, out_fd, + IOSTREAM_RAWLOG_FLAG_AUTOCLOSE | + IOSTREAM_RAWLOG_FLAG_TIMESTAMP); + i_stream_unref(&old_input); + o_stream_unref(&old_output); + return 0; +} + +int iostream_rawlog_create_path(const char *path, struct istream **input, + struct ostream **output) +{ + int ret, fd; + + if ((ret = iostream_rawlog_try_create_tcp(path, input, output)) != 0) + return ret < 0 ? -1 : 0; + fd = open(path, O_CREAT | O_APPEND | O_WRONLY, 0600); + if (fd == -1) { + i_error("creat(%s) failed: %m", path); + return -1; + } + iostream_rawlog_create_fd(fd, path, input, output); + return 0; +} + +void iostream_rawlog_create_from_stream(struct ostream *rawlog_output, + struct istream **input, + struct ostream **output) +{ + const enum iostream_rawlog_flags rawlog_flags = + IOSTREAM_RAWLOG_FLAG_BUFFERED | + IOSTREAM_RAWLOG_FLAG_TIMESTAMP; + struct istream *old_input; + struct ostream *old_output; + + if (input != NULL) { + old_input = *input; + *input = i_stream_create_rawlog_from_stream(old_input, + rawlog_output, rawlog_flags); + i_stream_unref(&old_input); + } + if (output != NULL) { + old_output = *output; + *output = o_stream_create_rawlog_from_stream(old_output, + rawlog_output, rawlog_flags); + o_stream_unref(&old_output); + } + if (input != NULL && output != NULL) + o_stream_ref(rawlog_output); +} diff --git a/src/lib/iostream-rawlog.h b/src/lib/iostream-rawlog.h new file mode 100644 index 0000000..e90f9c0 --- /dev/null +++ b/src/lib/iostream-rawlog.h @@ -0,0 +1,28 @@ +#ifndef IOSTREAM_RAWLOG_H +#define IOSTREAM_RAWLOG_H + +enum iostream_rawlog_flags { + IOSTREAM_RAWLOG_FLAG_AUTOCLOSE = 0x01, + IOSTREAM_RAWLOG_FLAG_BUFFERED = 0x02, + IOSTREAM_RAWLOG_FLAG_TIMESTAMP = 0x04 +}; + +/* Create rawlog *.in and *.out files to the given directory. */ +int ATTR_NOWARN_UNUSED_RESULT +iostream_rawlog_create(const char *dir, struct istream **input, + struct ostream **output); +/* Create rawlog prefix.in and prefix.out files. */ +int ATTR_NOWARN_UNUSED_RESULT +iostream_rawlog_create_prefix(const char *prefix, struct istream **input, + struct ostream **output); +/* Create rawlog path, writing both input and output to the same file. */ +int ATTR_NOWARN_UNUSED_RESULT +iostream_rawlog_create_path(const char *path, struct istream **input, + struct ostream **output); +/* Create rawlog that appends to the given rawlog_output. + Both input and output are written to the same stream. */ +void iostream_rawlog_create_from_stream(struct ostream *rawlog_output, + struct istream **input, + struct ostream **output); + +#endif diff --git a/src/lib/iostream-temp.c b/src/lib/iostream-temp.c new file mode 100644 index 0000000..6e14ff5 --- /dev/null +++ b/src/lib/iostream-temp.c @@ -0,0 +1,404 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "str.h" +#include "safe-mkstemp.h" +#include "write-full.h" +#include "istream-private.h" +#include "ostream-private.h" +#include "iostream-temp.h" + +#include <unistd.h> + +#define IOSTREAM_TEMP_MAX_BUF_SIZE_DEFAULT (1024*128) + +struct temp_ostream { + struct ostream_private ostream; + + char *temp_path_prefix; + enum iostream_temp_flags flags; + size_t max_mem_size; + + struct istream *dupstream; + uoff_t dupstream_offset, dupstream_start_offset; + char *name; + + buffer_t *buf; + int fd; + bool fd_tried; + uoff_t fd_size; +}; + +static bool o_stream_temp_dup_cancel(struct temp_ostream *tstream, + enum ostream_send_istream_result *res_r); + +static void +o_stream_temp_close(struct iostream_private *stream, + bool close_parent ATTR_UNUSED) +{ + struct temp_ostream *tstream = + container_of(stream, struct temp_ostream, ostream.iostream); + + i_close_fd(&tstream->fd); + buffer_free(&tstream->buf); + i_free(tstream->temp_path_prefix); + i_free(tstream->name); +} + +static int o_stream_temp_move_to_fd(struct temp_ostream *tstream) +{ + string_t *path; + + if (tstream->fd_tried) + return -1; + tstream->fd_tried = TRUE; + + path = t_str_new(128); + str_append(path, tstream->temp_path_prefix); + tstream->fd = safe_mkstemp_hostpid(path, 0600, (uid_t)-1, (gid_t)-1); + if (tstream->fd == -1) { + i_error("safe_mkstemp(%s) failed: %m", str_c(path)); + return -1; + } + if (i_unlink(str_c(path)) < 0) { + i_close_fd(&tstream->fd); + return -1; + } + if (write_full(tstream->fd, tstream->buf->data, tstream->buf->used) < 0) { + i_error("write(%s) failed: %m", str_c(path)); + i_close_fd(&tstream->fd); + return -1; + } + /* make the fd available also to o_stream_get_fd(), + e.g. for unit tests */ + tstream->ostream.fd = tstream->fd; + tstream->fd_size = tstream->buf->used; + buffer_free(&tstream->buf); + return 0; +} + +int o_stream_temp_move_to_memory(struct ostream *output) +{ + struct temp_ostream *tstream = + container_of(output->real_stream, struct temp_ostream, ostream); + unsigned char buf[IO_BLOCK_SIZE]; + uoff_t offset = 0; + ssize_t ret = 0; + + i_assert(tstream->buf == NULL); + tstream->buf = buffer_create_dynamic(default_pool, 8192); + while (offset < tstream->ostream.ostream.offset && + (ret = pread(tstream->fd, buf, sizeof(buf), offset)) > 0) { + if ((size_t)ret > tstream->ostream.ostream.offset - offset) + ret = tstream->ostream.ostream.offset - offset; + buffer_append(tstream->buf, buf, ret); + offset += ret; + } + if (ret < 0) { + /* not really expecting this to happen */ + i_error("iostream-temp %s: read(%s*) failed: %m", + o_stream_get_name(&tstream->ostream.ostream), + tstream->temp_path_prefix); + tstream->ostream.ostream.stream_errno = EIO; + return -1; + } + i_close_fd(&tstream->fd); + tstream->ostream.fd = -1; + return 0; +} + +static ssize_t +o_stream_temp_fd_sendv(struct temp_ostream *tstream, + const struct const_iovec *iov, unsigned int iov_count) +{ + size_t bytes = 0; + unsigned int i; + + for (i = 0; i < iov_count; i++) { + if (write_full(tstream->fd, iov[i].iov_base, iov[i].iov_len) < 0) { + i_error("iostream-temp %s: write(%s*) failed: %m - moving to memory", + o_stream_get_name(&tstream->ostream.ostream), + tstream->temp_path_prefix); + if (o_stream_temp_move_to_memory(&tstream->ostream.ostream) < 0) + return -1; + for (; i < iov_count; i++) { + buffer_append(tstream->buf, iov[i].iov_base, iov[i].iov_len); + bytes += iov[i].iov_len; + tstream->ostream.ostream.offset += iov[i].iov_len; + } + i_assert(tstream->fd_tried); + return bytes; + } + bytes += iov[i].iov_len; + tstream->ostream.ostream.offset += iov[i].iov_len; + } + tstream->fd_size += bytes; + return bytes; +} + +static ssize_t +o_stream_temp_sendv(struct ostream_private *stream, + const struct const_iovec *iov, unsigned int iov_count) +{ + struct temp_ostream *tstream = + container_of(stream, struct temp_ostream, ostream); + ssize_t ret = 0; + unsigned int i; + enum ostream_send_istream_result res; + + + tstream->flags &= ENUM_NEGATE(IOSTREAM_TEMP_FLAG_TRY_FD_DUP); + if (tstream->dupstream != NULL) { + if (o_stream_temp_dup_cancel(tstream, &res)) + return -1; + } + + if (tstream->fd != -1) + return o_stream_temp_fd_sendv(tstream, iov, iov_count); + + for (i = 0; i < iov_count; i++) { + if (tstream->buf->used + iov[i].iov_len > tstream->max_mem_size) { + if (o_stream_temp_move_to_fd(tstream) == 0) { + i_assert(tstream->fd != -1); + return o_stream_temp_fd_sendv(tstream, iov+i, + iov_count-i); + } + /* failed to move to temp fd, just keep it in memory */ + } + buffer_append(tstream->buf, iov[i].iov_base, iov[i].iov_len); + ret += iov[i].iov_len; + stream->ostream.offset += iov[i].iov_len; + } + return ret; +} + +static bool o_stream_temp_dup_cancel(struct temp_ostream *tstream, + enum ostream_send_istream_result *res_r) +{ + struct istream *input; + uoff_t size = tstream->dupstream_offset - + tstream->dupstream_start_offset; + bool ret = TRUE; /* use res_r to return error */ + + i_stream_seek(tstream->dupstream, tstream->dupstream_start_offset); + tstream->ostream.ostream.offset = 0; + + input = i_stream_create_limit(tstream->dupstream, size); + i_stream_unref(&tstream->dupstream); + + *res_r = io_stream_copy(&tstream->ostream.ostream, input); + switch (*res_r) { + case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: + /* everything copied */ + ret = FALSE; + break; + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: + i_unreached(); + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: + tstream->ostream.ostream.stream_errno = input->stream_errno; + io_stream_set_error(&tstream->ostream.iostream, + "iostream-temp: read(%s) failed: %s", + i_stream_get_name(input), + i_stream_get_error(input)); + break; + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: + break; + } + i_stream_destroy(&input); + return ret; +} + +static bool +o_stream_temp_dup_istream(struct temp_ostream *outstream, + struct istream *instream, + enum ostream_send_istream_result *res_r) +{ + uoff_t in_size; + + if (!instream->readable_fd || i_stream_get_fd(instream) == -1) + return FALSE; + + if (i_stream_get_size(instream, TRUE, &in_size) <= 0) { + if (outstream->dupstream != NULL) + return o_stream_temp_dup_cancel(outstream, res_r); + return FALSE; + } + i_assert(instream->v_offset <= in_size); + + if (outstream->dupstream == NULL) { + outstream->dupstream = instream; + outstream->dupstream_start_offset = instream->v_offset; + i_stream_ref(outstream->dupstream); + } else { + if (outstream->dupstream != instream || + outstream->dupstream_offset != instream->v_offset || + outstream->dupstream_offset > in_size) + return o_stream_temp_dup_cancel(outstream, res_r); + } + i_stream_seek(instream, in_size); + /* we should be at EOF now. o_stream_send_istream() asserts if + eof isn't set. */ + instream->eof = TRUE; + outstream->dupstream_offset = instream->v_offset; + outstream->ostream.ostream.offset = + outstream->dupstream_offset - outstream->dupstream_start_offset; + *res_r = OSTREAM_SEND_ISTREAM_RESULT_FINISHED; + return TRUE; +} + +static enum ostream_send_istream_result +o_stream_temp_send_istream(struct ostream_private *_outstream, + struct istream *instream) +{ + struct temp_ostream *outstream = + container_of(_outstream, struct temp_ostream, ostream); + enum ostream_send_istream_result res; + + if ((outstream->flags & IOSTREAM_TEMP_FLAG_TRY_FD_DUP) != 0) { + if (o_stream_temp_dup_istream(outstream, instream, &res)) + return res; + outstream->flags &= ENUM_NEGATE(IOSTREAM_TEMP_FLAG_TRY_FD_DUP); + } + return io_stream_copy(&outstream->ostream.ostream, instream); +} + +static int +o_stream_temp_write_at(struct ostream_private *stream, + const void *data, size_t size, uoff_t offset) +{ + struct temp_ostream *tstream = + container_of(stream, struct temp_ostream, ostream); + + if (tstream->fd == -1) { + i_assert(stream->ostream.offset == tstream->buf->used); + buffer_write(tstream->buf, offset, data, size); + stream->ostream.offset = tstream->buf->used; + } else { + if (pwrite_full(tstream->fd, data, size, offset) < 0) { + stream->ostream.stream_errno = errno; + i_close_fd(&tstream->fd); + return -1; + } + if (tstream->fd_size < offset + size) + tstream->fd_size = offset + size; + } + return 0; +} + +static int o_stream_temp_seek(struct ostream_private *_stream, uoff_t offset) +{ + _stream->ostream.offset = offset; + return 0; +} + +struct ostream *iostream_temp_create(const char *temp_path_prefix, + enum iostream_temp_flags flags) +{ + return iostream_temp_create_named(temp_path_prefix, flags, ""); +} + +struct ostream *iostream_temp_create_named(const char *temp_path_prefix, + enum iostream_temp_flags flags, + const char *name) +{ + return iostream_temp_create_sized(temp_path_prefix, flags, name, + IOSTREAM_TEMP_MAX_BUF_SIZE_DEFAULT); +} + +struct ostream *iostream_temp_create_sized(const char *temp_path_prefix, + enum iostream_temp_flags flags, + const char *name, + size_t max_mem_size) +{ + struct temp_ostream *tstream; + struct ostream *output; + + tstream = i_new(struct temp_ostream, 1); + tstream->ostream.ostream.blocking = TRUE; + tstream->ostream.sendv = o_stream_temp_sendv; + tstream->ostream.send_istream = o_stream_temp_send_istream; + tstream->ostream.write_at = o_stream_temp_write_at; + tstream->ostream.seek = o_stream_temp_seek; + tstream->ostream.iostream.close = o_stream_temp_close; + tstream->temp_path_prefix = i_strdup(temp_path_prefix); + tstream->flags = flags; + tstream->max_mem_size = max_mem_size; + tstream->buf = buffer_create_dynamic(default_pool, 8192); + tstream->fd = -1; + + output = o_stream_create(&tstream->ostream, NULL, -1); + tstream->name = i_strdup(name); + if (name[0] == '\0') { + o_stream_set_name(output, t_strdup_printf( + "(temp iostream in %s)", temp_path_prefix)); + } else { + o_stream_set_name(output, t_strdup_printf( + "(temp iostream in %s for %s)", temp_path_prefix, name)); + } + return output; +} + +static void iostream_temp_buf_destroyed(buffer_t *buf) +{ + buffer_free(&buf); +} + +struct istream *iostream_temp_finish(struct ostream **output, + size_t max_buffer_size) +{ + struct temp_ostream *tstream = + container_of((*output)->real_stream, struct temp_ostream, + ostream); + struct istream *input, *input2; + uoff_t abs_offset, size; + const char *for_path; + int fd; + + if (tstream->name[0] == '\0') + for_path = ""; + else + for_path = t_strdup_printf(" for %s", tstream->name); + + if (tstream->dupstream != NULL && !tstream->dupstream->closed) { + abs_offset = i_stream_get_absolute_offset(tstream->dupstream) - + tstream->dupstream->v_offset + + tstream->dupstream_start_offset; + size = tstream->dupstream_offset - + tstream->dupstream_start_offset; + fd = dup(i_stream_get_fd(tstream->dupstream)); + if (fd == -1) + input = i_stream_create_error_str(errno, "dup() failed: %m"); + else { + input2 = i_stream_create_fd_autoclose(&fd, max_buffer_size); + i_stream_seek(input2, abs_offset); + input = i_stream_create_limit(input2, size); + i_stream_unref(&input2); + } + i_stream_set_name(input, t_strdup_printf( + "(Temp file in %s%s, from %s)", tstream->temp_path_prefix, + for_path, i_stream_get_name(tstream->dupstream))); + i_stream_unref(&tstream->dupstream); + } else if (tstream->dupstream != NULL) { + /* return the original failed stream. */ + input = tstream->dupstream; + } else if (tstream->fd != -1) { + int fd = tstream->fd; + input = i_stream_create_fd_autoclose(&tstream->fd, max_buffer_size); + i_stream_set_name(input, t_strdup_printf( + "(Temp file fd %d in %s%s, %"PRIuUOFF_T" bytes)", + fd, tstream->temp_path_prefix, for_path, tstream->fd_size)); + } else { + input = i_stream_create_from_data(tstream->buf->data, + tstream->buf->used); + i_stream_set_name(input, t_strdup_printf( + "(Temp buffer in %s%s, %zu bytes)", + tstream->temp_path_prefix, for_path, tstream->buf->used)); + i_stream_add_destroy_callback(input, iostream_temp_buf_destroyed, + tstream->buf); + tstream->buf = NULL; + } + o_stream_destroy(output); + return input; +} diff --git a/src/lib/iostream-temp.h b/src/lib/iostream-temp.h new file mode 100644 index 0000000..bf5a45d --- /dev/null +++ b/src/lib/iostream-temp.h @@ -0,0 +1,31 @@ +#ifndef IOSTREAM_TEMP_H +#define IOSTREAM_TEMP_H + +enum iostream_temp_flags { + /* if o_stream_send_istream() is called with a readable fd, don't + actually copy the input stream, just have iostream_temp_finish() + return a new iostream pointing to the fd dup()ed */ + IOSTREAM_TEMP_FLAG_TRY_FD_DUP = 0x01 +}; + +/* Start writing to given output stream. The data is initially written to + memory, and later to a temporary file that is immediately unlinked. */ +struct ostream *iostream_temp_create(const char *temp_path_prefix, + enum iostream_temp_flags flags); +struct ostream *iostream_temp_create_named(const char *temp_path_prefix, + enum iostream_temp_flags flags, + const char *name); +struct ostream *iostream_temp_create_sized(const char *temp_path_prefix, + enum iostream_temp_flags flags, + const char *name, + size_t max_mem_size); +/* Finished writing to stream. Return input stream for it and free the + output stream. (It's also possible to abort iostream-temp by simply + destroying the ostream.) */ +struct istream *iostream_temp_finish(struct ostream **output, + size_t max_buffer_size); + +/* For internal testing: */ +int o_stream_temp_move_to_memory(struct ostream *output); + +#endif diff --git a/src/lib/iostream.c b/src/lib/iostream.c new file mode 100644 index 0000000..f2ba000 --- /dev/null +++ b/src/lib/iostream.c @@ -0,0 +1,154 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "istream.h" +#include "ostream.h" +#include "iostream-private.h" + +static void +io_stream_default_close(struct iostream_private *stream ATTR_UNUSED, + bool close_parent ATTR_UNUSED) +{ +} + +static void +io_stream_default_destroy(struct iostream_private *stream ATTR_UNUSED) +{ +} + +void io_stream_init(struct iostream_private *stream) +{ + if (stream->close == NULL) + stream->close = io_stream_default_close; + if (stream->destroy == NULL) + stream->destroy = io_stream_default_destroy; + stream->ioloop = current_ioloop; + + stream->refcount = 1; +} + +void io_stream_ref(struct iostream_private *stream) +{ + i_assert(stream->refcount > 0); + + stream->refcount++; +} + +bool io_stream_unref(struct iostream_private *stream) +{ + i_assert(stream->refcount > 0); + if (--stream->refcount != 0) + return TRUE; + + stream->close(stream, FALSE); + stream->destroy(stream); + return FALSE; +} + +void io_stream_free(struct iostream_private *stream) +{ + const struct iostream_destroy_callback *dc; + + if (array_is_created(&stream->destroy_callbacks)) { + array_foreach(&stream->destroy_callbacks, dc) + dc->callback(dc->context); + array_free(&stream->destroy_callbacks); + } + + i_free(stream->error); + i_free(stream->name); + i_free(stream); +} + +void io_stream_close(struct iostream_private *stream, bool close_parent) +{ + stream->close(stream, close_parent); +} + +void io_stream_set_max_buffer_size(struct iostream_private *stream, + size_t max_size) +{ + stream->set_max_buffer_size(stream, max_size); +} + +void io_stream_add_destroy_callback(struct iostream_private *stream, + void (*callback)(void *), void *context) +{ + struct iostream_destroy_callback *dc; + + if (!array_is_created(&stream->destroy_callbacks)) + i_array_init(&stream->destroy_callbacks, 2); + dc = array_append_space(&stream->destroy_callbacks); + dc->callback = callback; + dc->context = context; +} + +void io_stream_remove_destroy_callback(struct iostream_private *stream, + void (*callback)(void *)) +{ + const struct iostream_destroy_callback *dcs; + unsigned int i, count; + + dcs = array_get(&stream->destroy_callbacks, &count); + for (i = 0; i < count; i++) { + if (dcs[i].callback == callback) { + array_delete(&stream->destroy_callbacks, i, 1); + return; + } + } + i_unreached(); +} + +void io_stream_set_error(struct iostream_private *stream, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + io_stream_set_verror(stream, fmt, args); + va_end(args); +} + +void io_stream_set_verror(struct iostream_private *stream, + const char *fmt, va_list args) +{ + /* one of the parameters may be the old stream->error, so don't free + it before the new error is created. */ + char *error = i_strdup_vprintf(fmt, args); + i_free(stream->error); + stream->error = error; +} + +const char *io_stream_get_disconnect_reason(struct istream *input, + struct ostream *output) +{ + const char *errstr; + + if (input != NULL && input->stream_errno != 0) { + errno = input->stream_errno; + errstr = i_stream_get_error(input); + } else if (output != NULL && output->stream_errno != 0) { + errno = output->stream_errno; + errstr = o_stream_get_error(output); + } else { + errno = 0; + errstr = ""; + } + + if (errno == 0 || errno == EPIPE) + return "Connection closed"; + else + return t_strdup_printf("Connection closed: %s", errstr); +} + +void io_stream_switch_ioloop_to(struct iostream_private *stream, + struct ioloop *ioloop) +{ + stream->ioloop = ioloop; +} + +struct ioloop *io_stream_get_ioloop(struct iostream_private *stream) +{ + return (stream->ioloop == NULL ? current_ioloop : stream->ioloop); +} diff --git a/src/lib/iostream.h b/src/lib/iostream.h new file mode 100644 index 0000000..87bc374 --- /dev/null +++ b/src/lib/iostream.h @@ -0,0 +1,10 @@ +#ifndef IOSTREAM_H +#define IOSTREAM_H + +/* Returns human-readable reason for why iostream was disconnected. + The output is either "Connection closed" for clean disconnections or + "Connection closed: <error>" for unclean disconnections. */ +const char *io_stream_get_disconnect_reason(struct istream *input, + struct ostream *output); + +#endif diff --git a/src/lib/ipwd.c b/src/lib/ipwd.c new file mode 100644 index 0000000..8ac81fa --- /dev/null +++ b/src/lib/ipwd.c @@ -0,0 +1,103 @@ +/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */ + +#define _POSIX_PTHREAD_SEMANTICS /* for Solaris */ +#include "lib.h" +#include "ipwd.h" + +#include <unistd.h> + +#define PWBUF_MIN_SIZE 128 +#define GRBUF_MIN_SIZE 128 + +static void *pwbuf = NULL, *grbuf = NULL; +static size_t pwbuf_size, grbuf_size; + +static void pw_init(void) +{ + size_t old_pwbuf_size = pwbuf_size; + + if (pwbuf == NULL || errno == ERANGE) { + pwbuf_size = nearest_power(old_pwbuf_size + 1); + if (pwbuf_size < PWBUF_MIN_SIZE) + pwbuf_size = PWBUF_MIN_SIZE; + pwbuf = i_realloc(pwbuf, old_pwbuf_size, pwbuf_size); + } +} + +static void gr_init(void) +{ + size_t old_grbuf_size = grbuf_size; + + if (grbuf == NULL || errno == ERANGE) { + grbuf_size = nearest_power(old_grbuf_size + 1); + if (grbuf_size < PWBUF_MIN_SIZE) + grbuf_size = PWBUF_MIN_SIZE; + grbuf = i_realloc(grbuf, old_grbuf_size, grbuf_size); + } +} + +void ipwd_deinit(void) +{ + i_free_and_null(pwbuf); + i_free_and_null(grbuf); +} + +int i_getpwnam(const char *name, struct passwd *pwd_r) +{ + struct passwd *result; + + errno = 0; + do { + pw_init(); + errno = getpwnam_r(name, pwd_r, pwbuf, pwbuf_size, &result); + } while (errno == ERANGE); + if (result != NULL) + return 1; + if (errno == EINVAL) { + /* FreeBSD fails here when name="user@domain" */ + return 0; + } + return errno == 0 ? 0 : -1; +} + +int i_getpwuid(uid_t uid, struct passwd *pwd_r) +{ + struct passwd *result; + + errno = 0; + do { + pw_init(); + errno = getpwuid_r(uid, pwd_r, pwbuf, pwbuf_size, &result); + } while (errno == ERANGE); + if (result != NULL) + return 1; + return errno == 0 ? 0 : -1; +} + +int i_getgrnam(const char *name, struct group *grp_r) +{ + struct group *result; + + errno = 0; + do { + gr_init(); + errno = getgrnam_r(name, grp_r, grbuf, grbuf_size, &result); + } while (errno == ERANGE); + if (result != NULL) + return 1; + return errno == 0 ? 0 : -1; +} + +int i_getgrgid(gid_t gid, struct group *grp_r) +{ + struct group *result; + + errno = 0; + do { + gr_init(); + errno = getgrgid_r(gid, grp_r, grbuf, grbuf_size, &result); + } while (errno == ERANGE); + if (result != NULL) + return 1; + return errno == 0 ? 0 : -1; +} diff --git a/src/lib/ipwd.h b/src/lib/ipwd.h new file mode 100644 index 0000000..562ebb2 --- /dev/null +++ b/src/lib/ipwd.h @@ -0,0 +1,23 @@ +#ifndef IPWD_H +#define IPWD_H + +#include <pwd.h> +#include <grp.h> + +/* Replacements for standard getpw/gr*(), fixing their ability to report errors + properly. As with standard getpw/gr*(), second call overwrites data used + by the first one. + + Functions return 1 if user/group is found, 0 if not or + -1 if error (with errno set). */ + +int i_getpwnam(const char *name, struct passwd *pwd_r); +int i_getpwuid(uid_t uid, struct passwd *pwd_r); + +int i_getgrnam(const char *name, struct group *grp_r); +int i_getgrgid(gid_t gid, struct group *grp_r); + +/* Free memory used by above functions. */ +void ipwd_deinit(void); + +#endif diff --git a/src/lib/iso8601-date.c b/src/lib/iso8601-date.c new file mode 100644 index 0000000..a0c84ac --- /dev/null +++ b/src/lib/iso8601-date.c @@ -0,0 +1,309 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "utc-offset.h" +#include "utc-mktime.h" +#include "iso8601-date.h" + +#include <ctype.h> + +/* RFC3339/ISO8601 date-time syntax + + date-fullyear = 4DIGIT + date-month = 2DIGIT ; 01-12 + date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on + ; month/year + time-hour = 2DIGIT ; 00-23 + time-minute = 2DIGIT ; 00-59 + time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second + ; rules + time-secfrac = "." 1*DIGIT + time-numoffset = ("+" / "-") time-hour ":" time-minute + time-offset = "Z" / time-numoffset + + partial-time = time-hour ":" time-minute ":" time-second [time-secfrac] + full-date = date-fullyear "-" date-month "-" date-mday + full-time = partial-time time-offset + + date-time = full-date "T" full-time + */ + +struct iso8601_date_parser { + const unsigned char *cur, *end; + + struct tm tm; + int timezone_offset; +}; + +static inline int +iso8601_date_parse_number(struct iso8601_date_parser *parser, + int digits, int *number_r) +{ + int i; + + if (parser->cur >= parser->end || !i_isdigit(parser->cur[0])) + return 0; + + *number_r = parser->cur[0] - '0'; + parser->cur++; + + for (i=0; i < digits-1; i++) { + if (parser->cur >= parser->end || !i_isdigit(parser->cur[0])) + return -1; + *number_r = ((*number_r) * 10) + parser->cur[0] - '0'; + parser->cur++; + } + return 1; +} + +static int +iso8601_date_parse_secfrac(struct iso8601_date_parser *parser) +{ + /* time-secfrac = "." 1*DIGIT + + NOTE: Currently not applied anywhere, so fraction is just skipped. + */ + + /* "." */ + if (parser->cur >= parser->end || parser->cur[0] != '.') + return 0; + parser->cur++; + + /* 1DIGIT */ + if (parser->cur >= parser->end || !i_isdigit(parser->cur[0])) + return -1; + parser->cur++; + + /* *DIGIT */ + while (parser->cur < parser->end && i_isdigit(parser->cur[0])) + parser->cur++; + return 1; +} + +static int is08601_date_parse_time_offset(struct iso8601_date_parser *parser) +{ + int tz_sign = 1, tz_hour = 0, tz_min = 0; + + /* time-offset = "Z" / time-numoffset + time-numoffset = ("+" / "-") time-hour ":" time-minute + time-hour = 2DIGIT ; 00-23 + time-minute = 2DIGIT ; 00-59 + */ + + if (parser->cur >= parser->end) + return 0; + + /* time-offset = "Z" / time-numoffset */ + switch (parser->cur[0]) { + case '-': + tz_sign = -1; + /* fall through */ + case '+': + parser->cur++; + + /* time-hour = 2DIGIT */ + if (iso8601_date_parse_number(parser, 2, &tz_hour) <= 0) + return -1; + if (tz_hour > 23) + return -1; + + /* ":" */ + if (parser->cur >= parser->end || parser->cur[0] != ':') + return -1; + parser->cur++; + + /* time-minute = 2DIGIT */ + if (iso8601_date_parse_number(parser, 2, &tz_min) <= 0) + return -1; + if (tz_min > 59) + return -1; + break; + case 'Z': + case 'z': + parser->cur++; + break; + default: + return -1; + } + + parser->timezone_offset = tz_sign*(tz_hour*60 + tz_min); + return 1; +} + +static int is08601_date_parse_full_time(struct iso8601_date_parser *parser) +{ + /* full-time = partial-time time-offset + partial-time = time-hour ":" time-minute ":" time-second [time-secfrac] + time-hour = 2DIGIT ; 00-23 + time-minute = 2DIGIT ; 00-59 + time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second + ; rules + */ + + /* time-hour = 2DIGIT */ + if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_hour) <= 0) + return -1; + + /* ":" */ + if (parser->cur >= parser->end || parser->cur[0] != ':') + return -1; + parser->cur++; + + /* time-minute = 2DIGIT */ + if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_min) <= 0) + return -1; + + /* ":" */ + if (parser->cur >= parser->end || parser->cur[0] != ':') + return -1; + parser->cur++; + + /* time-second = 2DIGIT */ + if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_sec) <= 0) + return -1; + + /* [time-secfrac] */ + if (iso8601_date_parse_secfrac(parser) < 0) + return -1; + + /* time-offset */ + if (is08601_date_parse_time_offset(parser) <= 0) + return -1; + return 1; +} + +static int is08601_date_parse_full_date(struct iso8601_date_parser *parser) +{ + /* full-date = date-fullyear "-" date-month "-" date-mday + date-fullyear = 4DIGIT + date-month = 2DIGIT ; 01-12 + date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on + ; month/year + */ + + /* date-fullyear = 4DIGIT */ + if (iso8601_date_parse_number(parser, 4, &parser->tm.tm_year) <= 0) + return -1; + if (parser->tm.tm_year < 1900) + return -1; + parser->tm.tm_year -= 1900; + + /* "-" */ + if (parser->cur >= parser->end || parser->cur[0] != '-') + return -1; + parser->cur++; + + /* date-month = 2DIGIT */ + if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_mon) <= 0) + return -1; + parser->tm.tm_mon -= 1; + + /* "-" */ + if (parser->cur >= parser->end || parser->cur[0] != '-') + return -1; + parser->cur++; + + /* time-second = 2DIGIT */ + if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_mday) <= 0) + return -1; + return 1; +} + +static int iso8601_date_parse_date_time(struct iso8601_date_parser *parser) +{ + /* date-time = full-date "T" full-time */ + + /* full-date */ + if (is08601_date_parse_full_date(parser) <= 0) + return -1; + + /* "T" */ + if (parser->cur >= parser->end || + (parser->cur[0] != 'T' && parser->cur[0] != 't')) + return -1; + parser->cur++; + + /* full-time */ + if (is08601_date_parse_full_time(parser) <= 0) + return -1; + + if (parser->cur != parser->end) + return -1; + return 1; +} + +static bool +iso8601_date_do_parse(const unsigned char *data, size_t size, struct tm *tm_r, + time_t *timestamp_r, int *timezone_offset_r) +{ + struct iso8601_date_parser parser; + time_t timestamp; + + i_zero(&parser); + parser.cur = data; + parser.end = data + size; + + if (iso8601_date_parse_date_time(&parser) <= 0) + return FALSE; + + parser.tm.tm_isdst = -1; + timestamp = utc_mktime(&parser.tm); + if (timestamp == (time_t)-1) + return FALSE; + + *timezone_offset_r = parser.timezone_offset; + *tm_r = parser.tm; + *timestamp_r = timestamp - parser.timezone_offset * 60; + return TRUE; +} + +bool iso8601_date_parse(const unsigned char *data, size_t size, + time_t *timestamp_r, int *timezone_offset_r) +{ + struct tm tm; + + return iso8601_date_do_parse(data, size, &tm, + timestamp_r, timezone_offset_r); +} + +bool iso8601_date_parse_tm(const unsigned char *data, size_t size, + struct tm *tm_r, int *timezone_offset_r) +{ + time_t timestamp; + + return iso8601_date_do_parse(data, size, tm_r, + ×tamp, timezone_offset_r); +} + +const char *iso8601_date_create_tm(struct tm *tm, int timezone_offset) +{ + const char *time_offset; + + if (timezone_offset == INT_MAX) + time_offset = "Z"; + else { + char sign = '+'; + if (timezone_offset < 0) { + timezone_offset = -timezone_offset; + sign = '-'; + } + time_offset = t_strdup_printf("%c%02d:%02d", sign, + timezone_offset / 60, + timezone_offset % 60); + } + + return t_strdup_printf("%04d-%02d-%02dT%02d:%02d:%02d%s", + tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, time_offset); +} + +const char *iso8601_date_create(time_t timestamp) +{ + struct tm *tm; + int timezone_offset; + + tm = localtime(×tamp); + timezone_offset = utc_offset(tm, timestamp); + + return iso8601_date_create_tm(tm, timezone_offset); +} diff --git a/src/lib/iso8601-date.h b/src/lib/iso8601-date.h new file mode 100644 index 0000000..f015e7d --- /dev/null +++ b/src/lib/iso8601-date.h @@ -0,0 +1,21 @@ +#ifndef ISO8601_DATE_H +#define ISO8601_DATE_H + +/* Parses ISO8601 (RFC3339) date-time string. timezone_offset is filled with the + timezone's difference to UTC in minutes. Returned time_t timestamp is + compensated for time zone. */ +bool iso8601_date_parse(const unsigned char *data, size_t size, + time_t *timestamp_r, int *timezone_offset_r); +/* Equal to iso8601_date_parse, but writes uncompensated timestamp to tm_r. */ +bool iso8601_date_parse_tm(const unsigned char *data, size_t size, + struct tm *tm_r, int *timezone_offset_r); + +/* Create ISO8601 date-time string from given time struct in specified + timezone. A zone offset of zero will not to 'Z', but '+00:00'. If + zone_offset == INT_MAX, the time zone will be 'Z'. */ +const char *iso8601_date_create_tm(struct tm *tm, int zone_offset); + +/* Create ISO8601 date-time string from given time in local timezone. */ +const char *iso8601_date_create(time_t timestamp); + +#endif diff --git a/src/lib/istream-base64-decoder.c b/src/lib/istream-base64-decoder.c new file mode 100644 index 0000000..6eaa842 --- /dev/null +++ b/src/lib/istream-base64-decoder.c @@ -0,0 +1,171 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "base64.h" +#include "hex-binary.h" +#include "istream-private.h" +#include "istream-base64.h" + +struct base64_decoder_istream { + struct istream_private istream; + + struct base64_decoder decoder; +}; + +static int i_stream_read_parent(struct istream_private *stream) +{ + size_t size; + ssize_t ret; + + size = i_stream_get_data_size(stream->parent); + if (size >= 4) + return 1; + + /* we have less than one base64 block. + see if there is more data available. */ + ret = i_stream_read_memarea(stream->parent); + if (ret <= 0) { + stream->istream.stream_errno = stream->parent->stream_errno; + return ret; + } + size = i_stream_get_data_size(stream->parent); + i_assert(size != 0); + return 1; +} + +static int +i_stream_base64_try_decode_block(struct base64_decoder_istream *bstream) +{ + struct istream_private *stream = &bstream->istream; + const unsigned char *data; + size_t size, avail, pos; + buffer_t buf; + + data = i_stream_get_data(stream->parent, &size); + if (size == 0) + return 0; + + if (!i_stream_try_alloc(stream, (size+3)/4*3, &avail)) + return -2; + + buffer_create_from_data(&buf, stream->w_buffer + stream->pos, avail); + if (base64_decode_more(&bstream->decoder, data, size, &pos, &buf) < 0) { + io_stream_set_error(&stream->iostream, + "Invalid base64 data: 0x%s", + binary_to_hex(data+pos, I_MIN(size-pos, 8))); + stream->istream.stream_errno = EINVAL; + return -1; + } + + stream->pos += buf.used; + i_stream_skip(stream->parent, pos); + return pos > 0 ? 1 : 0; +} + +static void +i_stream_base64_finish_decode(struct base64_decoder_istream *bstream) +{ + struct istream_private *stream = &bstream->istream; + + i_assert(i_stream_get_data_size(stream->parent) == 0); + + if (base64_decode_finish(&bstream->decoder) < 0) { + io_stream_set_error(&stream->iostream, + "Base64 data ends prematurely"); + stream->istream.stream_errno = EPIPE; + } +} + +static ssize_t i_stream_base64_decoder_read(struct istream_private *stream) +{ + struct base64_decoder_istream *bstream = + container_of(stream, struct base64_decoder_istream, istream); + size_t pre_count, post_count; + int ret; + + if (base64_decode_is_finished(&bstream->decoder)) { + stream->istream.eof = TRUE; + return -1; + } + + do { + ret = i_stream_read_parent(stream); + if (ret == 0) + return 0; + if (ret < 0 && ret != -2) { + if (stream->istream.stream_errno != 0) + return -1; + if (i_stream_get_data_size(stream->parent) == 0) { + i_stream_base64_finish_decode(bstream); + stream->istream.eof = TRUE; + return -1; + } + } + + /* encode as many blocks as fits into destination buffer */ + pre_count = stream->pos - stream->skip; + while ((ret = i_stream_base64_try_decode_block(bstream)) > 0) ; + post_count = stream->pos - stream->skip; + } while (ret == 0 && pre_count == post_count); + + if (ret < 0 && pre_count == post_count) + return ret; + + i_assert(post_count > pre_count); + return post_count - pre_count; +} + +static void +i_stream_base64_decoder_seek(struct istream_private *stream, + uoff_t v_offset, bool mark) +{ + struct base64_decoder_istream *bstream = + container_of(stream, struct base64_decoder_istream, istream); + + if (v_offset < stream->istream.v_offset) { + /* seeking backwards - go back to beginning and seek + forward from there. */ + stream->parent_expected_offset = stream->parent_start_offset; + stream->skip = stream->pos = 0; + stream->istream.v_offset = 0; + i_stream_seek(stream->parent, 0); + + base64_decode_reset(&bstream->decoder); + } + i_stream_default_seek_nonseekable(stream, v_offset, mark); +} + +static struct istream * +i_stream_create_base64_decoder_common(const struct base64_scheme *b64, + struct istream *input) +{ + struct base64_decoder_istream *bstream; + + bstream = i_new(struct base64_decoder_istream, 1); + bstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + + bstream->istream.read = i_stream_base64_decoder_read; + bstream->istream.seek = i_stream_base64_decoder_seek; + + bstream->istream.istream.readable_fd = FALSE; + bstream->istream.istream.blocking = input->blocking; + bstream->istream.istream.seekable = input->seekable; + + base64_decode_init(&bstream->decoder, b64, 0); + + return i_stream_create(&bstream->istream, input, + i_stream_get_fd(input), 0); +} + +struct istream * +i_stream_create_base64_decoder(struct istream *input) +{ + return i_stream_create_base64_decoder_common(&base64_scheme, input); +} + +struct istream * +i_stream_create_base64url_decoder(struct istream *input) +{ + return i_stream_create_base64_decoder_common(&base64url_scheme, input); +} diff --git a/src/lib/istream-base64-encoder.c b/src/lib/istream-base64-encoder.c new file mode 100644 index 0000000..22d2786 --- /dev/null +++ b/src/lib/istream-base64-encoder.c @@ -0,0 +1,226 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "base64.h" +#include "istream-private.h" +#include "istream-base64.h" + +struct base64_encoder_istream { + struct istream_private istream; + + struct base64_encoder encoder; +}; + +static int i_stream_read_parent(struct istream_private *stream) +{ + size_t size; + ssize_t ret; + + size = i_stream_get_data_size(stream->parent); + if (size > 0) + return 1; + + ret = i_stream_read_memarea(stream->parent); + if (ret <= 0) { + stream->istream.stream_errno = stream->parent->stream_errno; + return ret; + } + size = i_stream_get_data_size(stream->parent); + i_assert(size != 0); + return 1; +} + +static int +i_stream_base64_try_encode(struct base64_encoder_istream *bstream) +{ + struct istream_private *stream = &bstream->istream; + struct base64_encoder *b64enc = &bstream->encoder; + const unsigned char *data; + size_t size, pos, out_size, avail; + buffer_t buf; + + data = i_stream_get_data(stream->parent, &size); + if (size == 0) + return 0; + + out_size = base64_encode_get_size(b64enc, size); + if (!i_stream_try_alloc(stream, out_size, &avail)) + return -2; + + buffer_create_from_data(&buf, stream->w_buffer + stream->pos, avail); + base64_encode_more(b64enc, data, size, &pos, &buf); + i_assert(buf.used > 0); + + stream->pos += buf.used; + i_stream_skip(stream->parent, pos); + return 1; +} + +static int +i_stream_base64_finish_encode(struct base64_encoder_istream *bstream) +{ + struct istream_private *stream = &bstream->istream; + struct base64_encoder *b64enc = &bstream->encoder; + size_t out_size, buffer_avail; + buffer_t buf; + + out_size = base64_encode_get_size(b64enc, 0); + if (out_size == 0) { + if (base64_encode_finish(b64enc, NULL)) + stream->istream.eof = TRUE; + return 1; + } + + if (!i_stream_try_alloc(stream, out_size, &buffer_avail)) + return -2; + + buffer_create_from_data(&buf, stream->w_buffer + stream->pos, + buffer_avail); + if (base64_encode_finish(b64enc, &buf)) + stream->istream.eof = TRUE; + i_assert(buf.used > 0); + + stream->pos += buf.used; + return 1; +} + +static ssize_t i_stream_base64_encoder_read(struct istream_private *stream) +{ + struct base64_encoder_istream *bstream = + container_of(stream, struct base64_encoder_istream, istream); + size_t pre_count, post_count; + int ret; + + if (base64_encode_is_finished(&bstream->encoder)) { + stream->istream.eof = TRUE; + return -1; + } + + pre_count = post_count = 0; + do { + ret = i_stream_read_parent(stream); + if (ret == 0) + return 0; + if (ret < 0) { + if (stream->istream.stream_errno != 0) + return -1; + if (i_stream_get_data_size(stream->parent) == 0) + break; + /* add the final partial block */ + } + + /* encode as many lines as fits into destination buffer */ + pre_count = stream->pos - stream->skip; + while ((ret = i_stream_base64_try_encode(bstream)) > 0) ; + post_count = stream->pos - stream->skip; + } while (ret == 0 && pre_count == post_count); + + if (ret == -2) { + if (pre_count == post_count) + return -2; + } else if (ret < 0) { + if (i_stream_get_data_size(stream->parent) == 0) { + i_assert(post_count == pre_count); + pre_count = stream->pos - stream->skip; + ret = i_stream_base64_finish_encode(bstream); + post_count = stream->pos - stream->skip; + if (ret <= 0) + return ret; + } + if (pre_count == post_count) { + stream->istream.eof = TRUE; + return -1; + } + } + + i_assert(post_count > pre_count); + return post_count - pre_count; +} + +static void +i_stream_base64_encoder_seek(struct istream_private *stream, + uoff_t v_offset, bool mark) +{ + struct base64_encoder_istream *bstream = + container_of(stream, struct base64_encoder_istream, istream); + + if (v_offset < stream->istream.v_offset) { + /* seeking backwards - go back to beginning and seek + forward from there. */ + stream->parent_expected_offset = stream->parent_start_offset; + stream->skip = stream->pos = 0; + stream->istream.v_offset = 0; + i_stream_seek(stream->parent, 0); + + base64_encode_reset(&bstream->encoder); + } + i_stream_default_seek_nonseekable(stream, v_offset, mark); +} + +static int +i_stream_base64_encoder_stat(struct istream_private *stream, + bool exact ATTR_UNUSED) +{ + struct base64_encoder_istream *bstream = + container_of(stream, struct base64_encoder_istream, istream); + const struct stat *st; + + if (i_stream_stat(stream->parent, exact, &st) < 0) { + stream->istream.stream_errno = stream->parent->stream_errno; + return -1; + } + + stream->statbuf = *st; + if (st->st_size == 0) + return 0; + + stream->statbuf.st_size = + base64_get_full_encoded_size(&bstream->encoder, st->st_size); + return 0; +} + +static struct istream * +i_stream_create_base64_encoder_common(const struct base64_scheme *b64, + struct istream *input, + unsigned int chars_per_line, bool crlf) +{ + struct base64_encoder_istream *bstream; + enum base64_encode_flags b64_flags = 0; + + i_assert(chars_per_line % 4 == 0); + + bstream = i_new(struct base64_encoder_istream, 1); + bstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + + bstream->istream.read = i_stream_base64_encoder_read; + bstream->istream.seek = i_stream_base64_encoder_seek; + bstream->istream.stat = i_stream_base64_encoder_stat; + + bstream->istream.istream.readable_fd = FALSE; + bstream->istream.istream.blocking = input->blocking; + bstream->istream.istream.seekable = input->seekable; + + if (crlf) + b64_flags |= BASE64_ENCODE_FLAG_CRLF; + base64_encode_init(&bstream->encoder, b64, b64_flags, chars_per_line); + + return i_stream_create(&bstream->istream, input, + i_stream_get_fd(input), 0); +} + +struct istream * +i_stream_create_base64_encoder(struct istream *input, + unsigned int chars_per_line, bool crlf) +{ + return i_stream_create_base64_encoder_common(&base64_scheme, input, + chars_per_line, crlf); +} + +struct istream * +i_stream_create_base64url_encoder(struct istream *input, + unsigned int chars_per_line, bool crlf) +{ + return i_stream_create_base64_encoder_common(&base64url_scheme, input, + chars_per_line, crlf); +} diff --git a/src/lib/istream-base64.h b/src/lib/istream-base64.h new file mode 100644 index 0000000..4f422f7 --- /dev/null +++ b/src/lib/istream-base64.h @@ -0,0 +1,16 @@ +#ifndef ISTREAM_BASE64_H +#define ISTREAM_BASE64_H + +struct istream * +i_stream_create_base64_encoder(struct istream *input, + unsigned int chars_per_line, bool crlf); +struct istream * +i_stream_create_base64url_encoder(struct istream *input, + unsigned int chars_per_line, bool crlf); + +struct istream * +i_stream_create_base64_decoder(struct istream *input); +struct istream * +i_stream_create_base64url_decoder(struct istream *input); + +#endif diff --git a/src/lib/istream-callback.c b/src/lib/istream-callback.c new file mode 100644 index 0000000..6f07d50 --- /dev/null +++ b/src/lib/istream-callback.c @@ -0,0 +1,118 @@ +/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "istream-private.h" +#include "istream-callback.h" + +struct callback_istream { + struct istream_private istream; + istream_callback_read_t *callback; + void *context; + + buffer_t *buf; + size_t prev_pos; +}; + +static void i_stream_callback_destroy(struct iostream_private *stream) +{ + struct callback_istream *cstream = + container_of(stream, struct callback_istream, istream.iostream); + + buffer_free(&cstream->buf); +} + +static ssize_t i_stream_callback_read(struct istream_private *stream) +{ + struct callback_istream *cstream = + container_of(stream, struct callback_istream, istream); + size_t pos; + + if (cstream->callback == NULL) { + /* already returned EOF / error */ + stream->istream.eof = TRUE; + return -1; + } + + if (stream->skip > 0) { + buffer_delete(cstream->buf, 0, stream->skip); + stream->pos -= stream->skip; + cstream->prev_pos -= stream->skip; + stream->skip = 0; + } + i_assert(cstream->buf->used >= cstream->prev_pos); + pos = cstream->prev_pos; + if (cstream->buf->used > pos) { + /* data was added outside the callback */ + } else if (!cstream->callback(cstream->buf, cstream->context)) { + /* EOF / error */ + stream->istream.eof = TRUE; + cstream->callback = NULL; + if (cstream->buf->used == pos || + stream->istream.stream_errno != 0) + return -1; + /* EOF was returned with some data still added to the buffer. + return the buffer first and EOF only on the next call. */ + } else if (cstream->buf->used == pos) { + /* buffer full */ + i_assert(cstream->buf->used > 0); + return -2; + } + i_assert(cstream->buf->used > pos); + stream->buffer = cstream->buf->data; + cstream->prev_pos = stream->pos = cstream->buf->used; + return cstream->buf->used - pos; +} + +#undef i_stream_create_callback +struct istream * +i_stream_create_callback(istream_callback_read_t *callback, void *context) +{ + struct callback_istream *cstream; + struct istream *istream; + + i_assert(callback != NULL); + + cstream = i_new(struct callback_istream, 1); + cstream->callback = callback; + cstream->context = context; + cstream->buf = buffer_create_dynamic(default_pool, 1024); + + cstream->istream.iostream.destroy = i_stream_callback_destroy; + cstream->istream.read = i_stream_callback_read; + + istream = i_stream_create(&cstream->istream, NULL, -1, 0); + istream->blocking = TRUE; + return istream; +} + +void i_stream_callback_append(struct istream *input, + const void *data, size_t size) +{ + struct callback_istream *cstream = + container_of(input->real_stream, + struct callback_istream, istream); + + buffer_append(cstream->buf, data, size); +} + +void i_stream_callback_append_str(struct istream *input, const char *str) +{ + i_stream_callback_append(input, str, strlen(str)); +} + +buffer_t *i_stream_callback_get_buffer(struct istream *input) +{ + struct callback_istream *cstream = + container_of(input->real_stream, + struct callback_istream, istream); + + return cstream->buf; +} + +void i_stream_callback_set_error(struct istream *input, int stream_errno, + const char *error) +{ + input->stream_errno = stream_errno; + io_stream_set_error(&input->real_stream->iostream, "%s", error); +} diff --git a/src/lib/istream-callback.h b/src/lib/istream-callback.h new file mode 100644 index 0000000..1d91ced --- /dev/null +++ b/src/lib/istream-callback.h @@ -0,0 +1,39 @@ +#ifndef ISTREAM_CALLBACK_H +#define ISTREAM_CALLBACK_H + +/* istream-callback can be used to implement an istream that returns data + by calling the specified callback. The callback needs to do: + + a) Add data to buffer unless the buffer size is already too large + (the callback can decide by itself what is too large). Return TRUE + regardless of whether any data was added. + + b) Return FALSE when it's finished adding data or when it reaches an error. + On error i_stream_callback_set_error() must be called before returning. + + i_stream_add_destroy_callback() can be also added to do any cleanups that + the callback may need to do. +*/ +typedef bool istream_callback_read_t(buffer_t *buf, void *context); + +struct istream * +i_stream_create_callback(istream_callback_read_t *callback, void *context); +#define i_stream_create_callback(callback, context) \ + i_stream_create_callback(1 ? (istream_callback_read_t *)callback : \ + CALLBACK_TYPECHECK(callback, bool (*)(buffer_t *buf, typeof(context))), \ + context) + +/* Append data to the istream externally. Typically this is used to add a + header to the stream before the callbacks are called. */ +void i_stream_callback_append(struct istream *input, + const void *data, size_t size); +void i_stream_callback_append_str(struct istream *input, const char *str); + +/* Returns the istream-callback's internal buffer. This buffer can be used to + append data to the stream. */ +buffer_t *i_stream_callback_get_buffer(struct istream *input); + +void i_stream_callback_set_error(struct istream *input, int stream_errno, + const char *error); + +#endif diff --git a/src/lib/istream-chain.c b/src/lib/istream-chain.c new file mode 100644 index 0000000..3b9a87b --- /dev/null +++ b/src/lib/istream-chain.c @@ -0,0 +1,349 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "llist.h" +#include "memarea.h" +#include "istream-private.h" +#include "istream-chain.h" + +struct chain_istream; + +struct istream_chain_link { + struct istream_chain_link *prev, *next; + + struct istream *stream; + bool eof; +}; + +struct istream_chain { + struct istream_chain_link *head, *tail; + + struct chain_istream *stream; +}; + +struct chain_istream { + struct istream_private istream; + + /* how much of the previous link's stream still exists at the + beginning of our buffer. skipping through this should point to + the beginning of the current link's stream. */ + size_t prev_stream_left; + size_t prev_skip; + + struct istream_chain chain; +}; + +static void ATTR_NULL(2) +i_stream_chain_append_internal(struct istream_chain *chain, + struct istream *stream) +{ + struct istream_chain_link *link; + + if (stream == NULL && chain->tail != NULL && chain->tail->stream == NULL) + return; + + link = i_new(struct istream_chain_link, 1); + link->stream = stream; + link->eof = stream == NULL; + + if (stream != NULL) + i_stream_ref(stream); + + if (chain->head == NULL && stream != NULL) { + i_stream_set_max_buffer_size(stream, + chain->stream->istream.max_buffer_size); + } + DLLIST2_APPEND(&chain->head, &chain->tail, link); + /* if io_add_istream() has been added to this chain stream, notify + the callback that we have more data available. */ + if (stream != NULL) + i_stream_set_input_pending(stream, TRUE); +} + +void i_stream_chain_append(struct istream_chain *chain, struct istream *stream) +{ + i_stream_chain_append_internal(chain, stream); +} + +void i_stream_chain_append_eof(struct istream_chain *chain) +{ + i_stream_chain_append_internal(chain, NULL); +} + +static void +i_stream_chain_set_max_buffer_size(struct iostream_private *stream, + size_t max_size) +{ + struct chain_istream *cstream = + container_of(stream, struct chain_istream, istream.iostream); + struct istream_chain_link *link = cstream->chain.head; + + cstream->istream.max_buffer_size = max_size; + while (link != NULL) { + if (link->stream != NULL) + i_stream_set_max_buffer_size(link->stream, max_size); + link = link->next; + } +} + +static void i_stream_chain_destroy(struct iostream_private *stream) +{ + struct chain_istream *cstream = + container_of(stream, struct chain_istream, istream.iostream); + struct istream_chain_link *link = cstream->chain.head; + + while (link != NULL) { + struct istream_chain_link *next = link->next; + + i_stream_unref(&link->stream); + i_free(link); + link = next; + } + i_stream_free_buffer(&cstream->istream); +} + +static void i_stream_chain_read_next(struct chain_istream *cstream) +{ + struct istream_chain_link *link = cstream->chain.head; + struct istream *prev_input; + const unsigned char *data; + size_t data_size, cur_data_pos; + + i_assert(link != NULL && link->stream != NULL); + i_assert(link->stream->eof); + + prev_input = link->stream; + data = i_stream_get_data(prev_input, &data_size); + + DLLIST2_REMOVE(&cstream->chain.head, &cstream->chain.tail, link); + i_free(link); + + /* a) we have more streams, b) we have EOF, c) we need to wait + for more streams */ + link = cstream->chain.head; + if (link != NULL && link->stream != NULL) + i_stream_seek(link->stream, 0); + + if (cstream->prev_stream_left > 0) { + /* we've already buffered some of the prev_input. continue + appending the rest to it. if it's already at EOF, there's + nothing more to append. */ + cur_data_pos = cstream->istream.pos - + (cstream->istream.skip + cstream->prev_stream_left); + i_assert(cur_data_pos <= data_size); + data += cur_data_pos; + data_size -= cur_data_pos; + /* the stream has now become "previous", so its contents in + buffer are now part of prev_stream_left. */ + cstream->prev_stream_left += cur_data_pos; + } else { + cstream->istream.pos = 0; + cstream->istream.skip = 0; + cstream->prev_stream_left = 0; + } + + if (data_size > 0) { + if (cstream->istream.memarea != NULL && + memarea_get_refcount(cstream->istream.memarea) > 1) + i_stream_memarea_detach(&cstream->istream); + memcpy(i_stream_alloc(&cstream->istream, data_size), + data, data_size); + cstream->istream.pos += data_size; + cstream->prev_stream_left += data_size; + } + + i_stream_skip(prev_input, i_stream_get_data_size(prev_input)); + i_stream_unref(&prev_input); +} + +static bool i_stream_chain_skip(struct chain_istream *cstream) +{ + struct istream_private *stream = &cstream->istream; + struct istream_chain_link *link = cstream->chain.head; + size_t bytes_skipped; + + i_assert(stream->skip >= cstream->prev_skip); + bytes_skipped = stream->skip - cstream->prev_skip; + + if (cstream->prev_stream_left == 0) { + /* no need to worry about buffers, skip everything */ + } else if (bytes_skipped < cstream->prev_stream_left) { + /* we're still skipping inside buffer */ + cstream->prev_stream_left -= bytes_skipped; + bytes_skipped = 0; + } else { + /* done with the buffer */ + bytes_skipped -= cstream->prev_stream_left; + cstream->prev_stream_left = 0; + } + if (bytes_skipped > 0) { + i_assert(stream->buffer != NULL); + stream->pos -= bytes_skipped; + stream->skip -= bytes_skipped; + stream->buffer += bytes_skipped; + } + cstream->prev_skip = stream->skip; + if (link == NULL || link->eof) { + i_assert(bytes_skipped == 0); + return FALSE; + } + i_stream_skip(link->stream, bytes_skipped); + return TRUE; +} + +static ssize_t i_stream_chain_read(struct istream_private *stream) +{ + struct chain_istream *cstream = + container_of(stream, struct chain_istream, istream); + struct istream_chain_link *link = cstream->chain.head; + const unsigned char *data; + size_t data_size, cur_data_pos, new_pos; + size_t new_bytes_count; + ssize_t ret; + + if (link != NULL && link->eof) { + stream->istream.eof = TRUE; + return -1; + } + + if (!i_stream_chain_skip(cstream)) + return 0; + i_assert(link != NULL); + + i_assert(stream->pos >= stream->skip + cstream->prev_stream_left); + cur_data_pos = stream->pos - (stream->skip + cstream->prev_stream_left); + + data = i_stream_get_data(link->stream, &data_size); + if (data_size > cur_data_pos) + ret = 0; + else { + /* need to read more */ + i_assert(cur_data_pos == data_size); + ret = i_stream_read_memarea(link->stream); + if (ret == -2 || ret == 0) + return ret; + + if (ret == -1) { + if (link->stream->stream_errno != 0) { + io_stream_set_error(&stream->iostream, + "read(%s) failed: %s", + i_stream_get_name(link->stream), + i_stream_get_error(link->stream)); + stream->istream.stream_errno = + link->stream->stream_errno; + return -1; + } + /* EOF of this stream, go to next stream */ + i_stream_chain_read_next(cstream); + cstream->prev_skip = stream->skip; + return i_stream_chain_read(stream); + } + /* we read something */ + data = i_stream_get_data(link->stream, &data_size); + } + + if (data_size == cur_data_pos) { + /* nothing new read - preserve the buffer as it was */ + i_assert(ret == 0 || ret == -1); + return ret; + } + if (cstream->prev_stream_left == 0) { + /* we can point directly to the current stream's buffers */ + stream->buffer = data; + stream->pos -= stream->skip; + stream->skip = 0; + new_pos = data_size; + } else { + /* we still have some of the previous stream left. merge the + new data with it. */ + i_assert(data_size > cur_data_pos); + new_bytes_count = data_size - cur_data_pos; + memcpy(i_stream_alloc(stream, new_bytes_count), + data + cur_data_pos, new_bytes_count); + stream->buffer = stream->w_buffer; + new_pos = stream->pos + new_bytes_count; + } + + i_assert(new_pos > stream->pos); + ret = (ssize_t)(new_pos - stream->pos); + stream->pos = new_pos; + cstream->prev_skip = stream->skip; + return ret; +} + +static void i_stream_chain_close(struct iostream_private *stream, + bool close_parent) +{ + struct chain_istream *cstream = + container_of(stream, struct chain_istream, istream.iostream); + + /* seek to the correct position in parent stream in case it didn't + end with EOF */ + (void)i_stream_chain_skip(cstream); + + if (close_parent) { + struct istream_chain_link *link = cstream->chain.head; + while (link != NULL) { + i_stream_close(link->stream); + link = link->next; + } + } +} + +static struct istream_snapshot * +i_stream_chain_snapshot(struct istream_private *stream, + struct istream_snapshot *prev_snapshot) +{ + if (stream->buffer == stream->w_buffer) { + /* Two or more istreams have been combined. Snapshot the + w_buffer's contents that contains their data. */ + i_assert(stream->memarea != NULL); + return i_stream_default_snapshot(stream, prev_snapshot); + } + /* Individual istreams are being read. Snapshot the istream directly. */ + struct chain_istream *cstream = + container_of(stream, struct chain_istream, istream); + struct istream_chain_link *link = cstream->chain.head; + if (link == NULL || link->stream == NULL) + return prev_snapshot; + + struct istream_private *_link_stream = link->stream->real_stream; + struct istream_snapshot *snapshot = i_new(struct istream_snapshot, 1); + snapshot->prev_snapshot = + _link_stream->snapshot(_link_stream, prev_snapshot); + if (snapshot->prev_snapshot == prev_snapshot) { + /* The link stream didn't implement snapshotting in any way. + This could cause trouble if the link stream is freed while + it's still referred to in this snapshot. Fix this by + referencing the link istream. Normally avoid doing this, + since the extra references can cause unexpected problems. */ + snapshot->istream = link->stream; + i_stream_ref(snapshot->istream); + } + return snapshot; +} + +struct istream *i_stream_create_chain(struct istream_chain **chain_r, + size_t max_buffer_size) +{ + struct chain_istream *cstream; + + cstream = i_new(struct chain_istream, 1); + cstream->chain.stream = cstream; + cstream->istream.max_buffer_size = max_buffer_size; + + cstream->istream.iostream.close = i_stream_chain_close; + cstream->istream.iostream.destroy = i_stream_chain_destroy; + cstream->istream.iostream.set_max_buffer_size = + i_stream_chain_set_max_buffer_size; + + cstream->istream.read = i_stream_chain_read; + cstream->istream.snapshot = i_stream_chain_snapshot; + + cstream->istream.istream.readable_fd = FALSE; + cstream->istream.istream.blocking = FALSE; + cstream->istream.istream.seekable = FALSE; + + *chain_r = &cstream->chain; + return i_stream_create(&cstream->istream, NULL, -1, 0); +} diff --git a/src/lib/istream-chain.h b/src/lib/istream-chain.h new file mode 100644 index 0000000..e5ba68b --- /dev/null +++ b/src/lib/istream-chain.h @@ -0,0 +1,21 @@ +#ifndef ISTREAM_CHAIN_H +#define ISTREAM_CHAIN_H + +struct istream_chain; + +/* Flexibly couple input streams into a single chain stream. Input streams can + be added after creation of the chain stream, and the chain stream will not + signal EOF until all streams are read to EOF and the last stream added was + NULL. Streams that were finished to EOF are unreferenced. The chain stream + is obviously not seekable and it has no determinable size. The chain_r + argument returns a pointer to the chain object. */ +struct istream *i_stream_create_chain(struct istream_chain **chain_r, + size_t max_buffer_size); + +/* Append an input stream to the chain. */ +void i_stream_chain_append(struct istream_chain *chain, struct istream *stream); +/* Mark the end of the chain. Only then reads from the chain stream can + return EOF. */ +void i_stream_chain_append_eof(struct istream_chain *chain); + +#endif diff --git a/src/lib/istream-concat.c b/src/lib/istream-concat.c new file mode 100644 index 0000000..4c58b86 --- /dev/null +++ b/src/lib/istream-concat.c @@ -0,0 +1,391 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "memarea.h" +#include "istream-private.h" +#include "istream-concat.h" + +struct concat_istream { + struct istream_private istream; + + struct istream **input, *cur_input; + uoff_t *input_size; + unsigned int input_count; + + unsigned int cur_idx, unknown_size_idx; + size_t prev_stream_left, prev_stream_skip, prev_skip; +}; + +static void i_stream_concat_skip(struct concat_istream *cstream); + +static void i_stream_concat_close(struct iostream_private *stream, + bool close_parent) +{ + struct concat_istream *cstream = + container_of(stream, struct concat_istream, istream.iostream); + i_assert(cstream->cur_input == cstream->input[cstream->cur_idx]); + unsigned int i; + + if (cstream->istream.istream.stream_errno == 0) { + /* get the parent streams to the wanted offset */ + (void)i_stream_concat_skip(cstream); + } + + if (close_parent) { + for (i = 0; i < cstream->input_count; i++) + i_stream_close(cstream->input[i]); + } +} + +static void i_stream_concat_destroy(struct iostream_private *stream) +{ + struct concat_istream *cstream = + container_of(stream, struct concat_istream, istream.iostream); + i_assert(cstream->cur_input == cstream->input[cstream->cur_idx]); + unsigned int i; + + for (i = 0; i < cstream->input_count; i++) + i_stream_unref(&cstream->input[i]); + i_free(cstream->input); + i_free(cstream->input_size); + i_stream_free_buffer(&cstream->istream); +} + +static void +i_stream_concat_set_max_buffer_size(struct iostream_private *stream, + size_t max_size) +{ + struct concat_istream *cstream = + container_of(stream, struct concat_istream, istream.iostream); + i_assert(cstream->cur_input == cstream->input[cstream->cur_idx]); + unsigned int i; + + cstream->istream.max_buffer_size = max_size; + for (i = 0; i < cstream->input_count; i++) + i_stream_set_max_buffer_size(cstream->input[i], max_size); +} + +static void i_stream_concat_read_next(struct concat_istream *cstream) +{ + struct istream *prev_input = cstream->cur_input; + const unsigned char *data; + size_t data_size, size; + + i_assert(cstream->cur_input->eof); + + if (cstream->prev_stream_skip != 0) { + i_stream_skip(cstream->input[cstream->cur_idx-1], cstream->prev_stream_skip); + cstream->prev_stream_skip = 0; + } + + data = i_stream_get_data(cstream->cur_input, &data_size); + cstream->cur_idx++; + cstream->cur_input = cstream->input[cstream->cur_idx]; + i_stream_seek(cstream->cur_input, 0); + + if (cstream->prev_stream_left > 0 || cstream->istream.pos == 0) { + /* all the pending data is already in w_buffer */ + cstream->prev_stream_skip = data_size; + cstream->prev_stream_left += data_size; + i_assert(cstream->prev_stream_left == + cstream->istream.pos - cstream->istream.skip); + return; + } + i_assert(cstream->prev_stream_skip == 0); + + /* we already verified that the data size is less than the + maximum buffer size */ + cstream->istream.skip = 0; + cstream->istream.pos = 0; + if (data_size > 0) { + if (cstream->istream.memarea != NULL && + memarea_get_refcount(cstream->istream.memarea) > 1) + i_stream_memarea_detach(&cstream->istream); + if (!i_stream_try_alloc(&cstream->istream, data_size, &size)) + i_unreached(); + i_assert(size >= data_size); + } + + cstream->prev_stream_left = data_size; + memcpy(cstream->istream.w_buffer, data, data_size); + i_stream_skip(prev_input, data_size); + cstream->istream.skip = 0; + cstream->istream.pos = data_size; +} + +static void i_stream_concat_skip(struct concat_istream *cstream) +{ + struct istream_private *stream = &cstream->istream; + size_t bytes_skipped; + + i_assert(stream->skip >= cstream->prev_skip); + bytes_skipped = stream->skip - cstream->prev_skip; + + if (cstream->prev_stream_left == 0) { + /* no need to worry about buffers, skip everything */ + } else if (bytes_skipped < cstream->prev_stream_left) { + /* we're still skipping inside buffer */ + cstream->prev_stream_left -= bytes_skipped; + bytes_skipped = 0; + } else { + /* done with the buffer */ + i_stream_skip(cstream->input[cstream->cur_idx-1], cstream->prev_stream_skip); + cstream->prev_stream_skip = 0; + + bytes_skipped -= cstream->prev_stream_left; + cstream->prev_stream_left = 0; + } + if (bytes_skipped > 0) { + i_assert(stream->buffer != NULL); + stream->pos -= bytes_skipped; + stream->skip -= bytes_skipped; + stream->buffer += bytes_skipped; + } + cstream->prev_skip = stream->skip; + i_stream_skip(cstream->cur_input, bytes_skipped); +} + +static ssize_t i_stream_concat_read(struct istream_private *stream) +{ + struct concat_istream *cstream = + container_of(stream, struct concat_istream, istream); + i_assert(cstream->cur_input == cstream->input[cstream->cur_idx]); + const unsigned char *data; + size_t size, data_size, cur_data_pos, new_pos; + size_t new_bytes_count; + ssize_t ret; + bool last_stream; + + i_assert(cstream->cur_input != NULL); + i_stream_concat_skip(cstream); + + i_assert(stream->pos >= stream->skip + cstream->prev_stream_left); + cur_data_pos = stream->pos - (stream->skip + cstream->prev_stream_left); + + data = i_stream_get_data(cstream->cur_input, &data_size); + if (data_size > cur_data_pos) + ret = 0; + else { + /* need to read more - NOTE: Can't use i_stream_read_memarea() + here, because our stream->buffer may point to the parent + istream. Implementing explicit snapshot function to avoid + this isn't easy for seekable concat-istreams, because due to + seeking it's not necessarily the cur_input that needs to be + snapshotted. */ + i_assert(cur_data_pos == data_size); + ret = i_stream_read(cstream->cur_input); + if (ret == -2 || ret == 0) + return ret; + + if (ret == -1 && cstream->cur_input->stream_errno != 0) { + io_stream_set_error(&cstream->istream.iostream, + "read(%s) failed: %s", + i_stream_get_name(cstream->cur_input), + i_stream_get_error(cstream->cur_input)); + stream->istream.stream_errno = + cstream->cur_input->stream_errno; + return -1; + } + + /* we either read something or we're at EOF */ + last_stream = cstream->cur_idx+1 >= cstream->input_count; + if (ret == -1 && !last_stream) { + if (stream->pos - stream->skip >= i_stream_get_max_buffer_size(&stream->istream)) + return -2; + + i_stream_concat_read_next(cstream); + cstream->prev_skip = stream->skip; + return i_stream_concat_read(stream); + } + + stream->istream.eof = cstream->cur_input->eof && last_stream; + i_assert(ret != -1 || stream->istream.eof); + data = i_stream_get_data(cstream->cur_input, &data_size); + } + + if (data_size == cur_data_pos) { + /* nothing new read - preserve the buffer as it was */ + i_assert(ret == 0 || ret == -1); + return ret; + } + if (cstream->prev_stream_left == 0) { + /* we can point directly to the current stream's buffers */ + stream->buffer = data; + stream->pos -= stream->skip; + stream->skip = 0; + new_pos = data_size; + } else { + /* we still have some of the previous stream left. merge the + new data with it. */ + i_assert(data_size > cur_data_pos); + new_bytes_count = data_size - cur_data_pos; + if (!i_stream_try_alloc(stream, new_bytes_count, &size)) { + stream->buffer = stream->w_buffer; + return -2; + } + stream->buffer = stream->w_buffer; + + /* we'll copy all the new input to w_buffer. if we skip over + prev_stream_left bytes, the next read will switch to + pointing to cur_input's data directly. */ + if (new_bytes_count > size) + new_bytes_count = size; + memcpy(stream->w_buffer + stream->pos, + data + cur_data_pos, new_bytes_count); + new_pos = stream->pos + new_bytes_count; + } + + i_assert(new_pos > stream->pos); + ret = (ssize_t)(new_pos - stream->pos); + stream->pos = new_pos; + cstream->prev_skip = stream->skip; + return ret; +} + +static int +find_v_offset(struct concat_istream *cstream, uoff_t *v_offset, + unsigned int *idx_r) +{ + const struct stat *st; + unsigned int i; + + for (i = 0; i < cstream->input_count; i++) { + if (*v_offset == 0) { + /* seek to beginning of this stream */ + break; + } + if (i == cstream->unknown_size_idx) { + /* we'll need to figure out this stream's size */ + if (i_stream_stat(cstream->input[i], TRUE, &st) < 0) { + io_stream_set_error(&cstream->istream.iostream, + "stat(%s) failed: %s", + i_stream_get_name(cstream->input[i]), + i_stream_get_error(cstream->input[i])); + i_error("istream-concat: stat(%s) failed: %s", + i_stream_get_name(cstream->input[i]), + i_stream_get_error(cstream->input[i])); + cstream->istream.istream.stream_errno = + cstream->input[i]->stream_errno; + return -1; + } + + /* @UNSAFE */ + cstream->input_size[i] = st->st_size; + cstream->unknown_size_idx = i + 1; + } + if (*v_offset < cstream->input_size[i]) + break; + *v_offset -= cstream->input_size[i]; + } + + *idx_r = i; + return 0; +} + +static void i_stream_concat_seek(struct istream_private *stream, + uoff_t v_offset, bool mark ATTR_UNUSED) +{ + struct concat_istream *cstream = + container_of(stream, struct concat_istream, istream); + i_assert(cstream->cur_input == cstream->input[cstream->cur_idx]); + + stream->istream.v_offset = v_offset; + stream->skip = stream->pos = 0; + cstream->prev_stream_left = 0; + cstream->prev_stream_skip = 0; + cstream->prev_skip = 0; + + if (find_v_offset(cstream, &v_offset, &cstream->cur_idx) < 0) { + /* failed */ + stream->istream.stream_errno = EINVAL; + return; + } + if (cstream->cur_idx < cstream->input_count) + cstream->cur_input = cstream->input[cstream->cur_idx]; + else { + /* we allow seeking to EOF, but not past it. */ + if (v_offset != 0) { + io_stream_set_error(&cstream->istream.iostream, + "Seeking past EOF by %"PRIuUOFF_T" bytes", v_offset); + cstream->istream.istream.stream_errno = EINVAL; + return; + } + i_assert(cstream->cur_idx > 0); + /* Position ourselves at the EOF of the last actual stream. */ + cstream->cur_idx--; + cstream->cur_input = cstream->input[cstream->cur_idx]; + v_offset = cstream->input_size[cstream->cur_idx]; + } + i_stream_seek(cstream->cur_input, v_offset); +} + +static int +i_stream_concat_stat(struct istream_private *stream, bool exact ATTR_UNUSED) +{ + struct concat_istream *cstream = + container_of(stream, struct concat_istream, istream); + i_assert(cstream->cur_input == cstream->input[cstream->cur_idx]); + uoff_t v_offset = UOFF_T_MAX; + unsigned int i, cur_idx; + + /* make sure we have all sizes */ + if (find_v_offset(cstream, &v_offset, &cur_idx) < 0) + return -1; + + stream->statbuf.st_size = 0; + for (i = 0; i < cstream->unknown_size_idx; i++) + stream->statbuf.st_size += cstream->input_size[i]; + return 0; +} + +struct istream *i_stream_create_concat(struct istream *input[]) +{ + struct concat_istream *cstream; + unsigned int count; + size_t max_buffer_size = 0; + bool blocking = TRUE, seekable = TRUE; + + /* if any of the streams isn't blocking or seekable, set ourself also + nonblocking/nonseekable */ + for (count = 0; input[count] != NULL; count++) { + size_t cur_max = i_stream_get_max_buffer_size(input[count]); + + i_assert(cur_max != 0); + if (cur_max != SIZE_MAX && cur_max > max_buffer_size) + max_buffer_size = cur_max; + if (!input[count]->blocking) + blocking = FALSE; + if (!input[count]->seekable) + seekable = FALSE; + i_stream_ref(input[count]); + } + i_assert(count != 0); + if (max_buffer_size == 0) + max_buffer_size = SIZE_MAX; + if (max_buffer_size < I_STREAM_MIN_SIZE) + max_buffer_size = I_STREAM_MIN_SIZE; + + cstream = i_new(struct concat_istream, 1); + cstream->input_count = count; + cstream->input = p_memdup(default_pool, input, sizeof(*input) * count); + cstream->input_size = i_new(uoff_t, count); + + cstream->cur_input = cstream->input[0]; + i_stream_seek(cstream->cur_input, 0); + + cstream->istream.iostream.close = i_stream_concat_close; + cstream->istream.iostream.destroy = i_stream_concat_destroy; + cstream->istream.iostream.set_max_buffer_size = + i_stream_concat_set_max_buffer_size; + + cstream->istream.max_buffer_size = max_buffer_size; + cstream->istream.read = i_stream_concat_read; + cstream->istream.seek = i_stream_concat_seek; + cstream->istream.stat = i_stream_concat_stat; + + cstream->istream.istream.readable_fd = FALSE; + cstream->istream.istream.blocking = blocking; + cstream->istream.istream.seekable = seekable; + return i_stream_create(&cstream->istream, NULL, -1, 0); +} diff --git a/src/lib/istream-concat.h b/src/lib/istream-concat.h new file mode 100644 index 0000000..a028534 --- /dev/null +++ b/src/lib/istream-concat.h @@ -0,0 +1,7 @@ +#ifndef ISTREAM_CONCAT_H +#define ISTREAM_CONCAT_H + +/* Concatenate input streams into a single stream. */ +struct istream *i_stream_create_concat(struct istream *input[]); + +#endif diff --git a/src/lib/istream-crlf.c b/src/lib/istream-crlf.c new file mode 100644 index 0000000..2d111b9 --- /dev/null +++ b/src/lib/istream-crlf.c @@ -0,0 +1,208 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream-private.h" +#include "istream-crlf.h" + +struct crlf_istream { + struct istream_private istream; + + bool pending_cr:1; + bool last_cr:1; +}; + +static int i_stream_crlf_read_common(struct crlf_istream *cstream) +{ + struct istream_private *stream = &cstream->istream; + size_t size, avail; + ssize_t ret; + + size = i_stream_get_data_size(stream->parent); + if (size == 0) { + ret = i_stream_read_memarea(stream->parent); + if (ret <= 0) { + i_assert(ret != -2); /* 0 sized buffer can't be full */ + stream->istream.stream_errno = + stream->parent->stream_errno; + stream->istream.eof = stream->parent->eof; + return ret; + } + size = i_stream_get_data_size(stream->parent); + i_assert(size != 0); + } + + if (!i_stream_try_alloc(stream, size, &avail)) + return -2; + return 1; +} + +static ssize_t i_stream_crlf_read_crlf(struct istream_private *stream) +{ + struct crlf_istream *cstream = + container_of(stream, struct crlf_istream, istream); + const unsigned char *data, *ptr, *src, *src_end; + unsigned char *dest, *dest_end; + size_t size, copy_len; + ssize_t ret; + + ret = i_stream_crlf_read_common(cstream); + if (ret <= 0) + return ret; + + /* at least one byte was read */ + data = i_stream_get_data(stream->parent, &size); + + dest = stream->w_buffer + stream->pos; + dest_end = stream->w_buffer + stream->buffer_size; + src = data; + src_end = data + size; + + /* @UNSAFE: add missing CRs */ + if (*src == '\n') { + if (!cstream->last_cr && dest < dest_end) + *dest++ = '\r'; + + if (dest < dest_end) { + *dest++ = '\n'; + src++; + } + } + + while (dest < dest_end) { + i_assert(src <= src_end); + ptr = memchr(src, '\n', src_end - src); + if (ptr == NULL) + ptr = src_end; + + /* copy data up to LF */ + copy_len = ptr - src; + if (dest + copy_len > dest_end) + copy_len = dest_end - dest; + + if (copy_len > 0) { + memcpy(dest, src, copy_len); + + dest += copy_len; + src += copy_len; + } + + i_assert(dest <= dest_end && src <= src_end); + if (dest == dest_end || src == src_end) + break; + + /* add the CR if necessary and copy the LF. + (src >= data+1, because data[0]=='\n' was + handled before this loop) */ + if (src[-1] != '\r') + *dest++ = '\r'; + + if (dest == dest_end) + break; + + *dest++ = '\n'; + src++; + i_assert(src == ptr + 1); + } + + i_assert(dest != stream->w_buffer); + cstream->last_cr = dest[-1] == '\r'; + i_stream_skip(stream->parent, src - data); + + ret = (dest - stream->w_buffer) - stream->pos; + i_assert(ret > 0); + stream->pos = dest - stream->w_buffer; + return ret; +} + +static ssize_t i_stream_crlf_read_lf(struct istream_private *stream) +{ + struct crlf_istream *cstream = + container_of(stream, struct crlf_istream, istream); + const unsigned char *data, *p; + size_t i, dest, size, max; + ssize_t ret; + bool pending_cr; + + ret = i_stream_crlf_read_common(cstream); + if (ret <= 0) + return ret; + + data = i_stream_get_data(stream->parent, &size); + + /* @UNSAFE */ + /* \r\n -> \n + \r<anything> -> \r<anything> + \r\r\n -> \r\n */ + dest = stream->pos; + pending_cr = cstream->pending_cr; + for (i = 0; i < size && dest < stream->buffer_size; ) { + if (data[i] == '\r') { + if (pending_cr) { + /* \r\r */ + stream->w_buffer[dest++] = '\r'; + } else { + pending_cr = TRUE; + } + i++; + } else if (data[i] == '\n') { + /* [\r]\n */ + pending_cr = FALSE; + stream->w_buffer[dest++] = '\n'; + i++; + } else if (pending_cr) { + /* \r<anything> */ + pending_cr = FALSE; + stream->w_buffer[dest++] = '\r'; + } else { + /* copy everything until the next \r */ + max = I_MIN(size - i, stream->buffer_size - dest); + p = memchr(data + i, '\r', max); + if (p != NULL) + max = p - (data+i); + memcpy(stream->w_buffer + dest, data + i, max); + dest += max; + i += max; + } + } + i_assert(i <= size); + i_assert(dest <= stream->buffer_size); + + cstream->pending_cr = pending_cr; + i_stream_skip(stream->parent, i); + + ret = dest - stream->pos; + if (ret == 0) { + i_assert(cstream->pending_cr && size == 1); + return i_stream_crlf_read_lf(stream); + } + i_assert(ret > 0); + stream->pos = dest; + return ret; +} + +static struct istream * +i_stream_create_crlf_full(struct istream *input, bool crlf) +{ + struct crlf_istream *cstream; + + cstream = i_new(struct crlf_istream, 1); + cstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + cstream->istream.read = crlf ? i_stream_crlf_read_crlf : + i_stream_crlf_read_lf; + + cstream->istream.istream.readable_fd = FALSE; + cstream->istream.istream.blocking = input->blocking; + cstream->istream.istream.seekable = FALSE; + return i_stream_create(&cstream->istream, input, + i_stream_get_fd(input), 0); +} + +struct istream *i_stream_create_crlf(struct istream *input) +{ + return i_stream_create_crlf_full(input, TRUE); +} + +struct istream *i_stream_create_lf(struct istream *input) +{ + return i_stream_create_crlf_full(input, FALSE); +} diff --git a/src/lib/istream-crlf.h b/src/lib/istream-crlf.h new file mode 100644 index 0000000..1ed44c6 --- /dev/null +++ b/src/lib/istream-crlf.h @@ -0,0 +1,9 @@ +#ifndef ISTREAM_CRLF_H +#define ISTREAM_CRLF_H + +/* Read all linefeeds as CRLF */ +struct istream *i_stream_create_crlf(struct istream *input); +/* Read all linefeeds as LF */ +struct istream *i_stream_create_lf(struct istream *input); + +#endif diff --git a/src/lib/istream-data.c b/src/lib/istream-data.c new file mode 100644 index 0000000..b97fc21 --- /dev/null +++ b/src/lib/istream-data.c @@ -0,0 +1,62 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream-private.h" + +static ssize_t i_stream_data_read(struct istream_private *stream) +{ + stream->istream.eof = TRUE; + return -1; +} + +static void i_stream_data_seek(struct istream_private *stream, uoff_t v_offset, + bool mark ATTR_UNUSED) +{ + stream->skip = v_offset; + stream->istream.v_offset = v_offset; +} + +struct istream *i_stream_create_from_data(const void *data, size_t size) +{ + struct istream_private *stream; + + stream = i_new(struct istream_private, 1); + stream->buffer = data; + stream->pos = size; + stream->max_buffer_size = SIZE_MAX; + + stream->read = i_stream_data_read; + stream->seek = i_stream_data_seek; + + stream->istream.readable_fd = FALSE; + stream->istream.blocking = TRUE; + stream->istream.seekable = TRUE; + i_stream_create(stream, NULL, -1, ISTREAM_CREATE_FLAG_NOOP_SNAPSHOT); + stream->statbuf.st_size = size; + i_stream_set_name(&stream->istream, "(buffer)"); + return &stream->istream; +} + +static void i_stream_copied_data_free(void *data) +{ + i_free(data); +} +struct istream * +i_stream_create_copy_from_data(const void *data, size_t size) +{ + struct istream *stream; + void *buffer; + + if (size == 0) { + buffer = ""; + } else { + buffer = i_malloc(size); + memcpy(buffer, data, size); + } + stream = i_stream_create_from_data(buffer, size); + if (size > 0) { + i_stream_add_destroy_callback + (stream, i_stream_copied_data_free, buffer); + } + return stream; +} diff --git a/src/lib/istream-failure-at.c b/src/lib/istream-failure-at.c new file mode 100644 index 0000000..7dccdd3 --- /dev/null +++ b/src/lib/istream-failure-at.c @@ -0,0 +1,97 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream-private.h" +#include "istream-failure-at.h" + +struct failure_at_istream { + struct istream_private istream; + int error_code; + char *error_string; + uoff_t failure_offset; +}; + +static void i_stream_failure_at_destroy(struct iostream_private *stream) +{ + struct failure_at_istream *fstream = + container_of(stream, struct failure_at_istream, + istream.iostream); + + i_free(fstream->error_string); +} + +static ssize_t +i_stream_failure_at_read(struct istream_private *stream) +{ + struct failure_at_istream *fstream = + container_of(stream, struct failure_at_istream, istream); + uoff_t new_offset; + ssize_t ret; + + i_stream_seek(stream->parent, stream->parent_start_offset + + stream->istream.v_offset); + + ret = i_stream_read_copy_from_parent(&stream->istream); + new_offset = stream->istream.v_offset + (stream->pos - stream->skip); + if (ret >= 0 && new_offset >= fstream->failure_offset) { + if (stream->istream.v_offset >= fstream->failure_offset) { + /* we already passed the wanted failure offset, + return error immediately. */ + stream->pos = stream->skip; + stream->istream.stream_errno = errno = + fstream->error_code; + io_stream_set_error(&stream->iostream, "%s", + fstream->error_string); + ret = -1; + } else { + /* return data up to the wanted failure offset and + on the next read() call return failure */ + size_t new_pos = fstream->failure_offset - + stream->istream.v_offset + stream->skip; + i_assert(new_pos >= stream->skip && + stream->pos >= new_pos); + ret -= stream->pos - new_pos; + stream->pos = new_pos; + } + } else if (ret < 0 && stream->istream.stream_errno == 0 && + fstream->failure_offset == UOFF_T_MAX) { + /* failure at EOF */ + stream->istream.stream_errno = errno = + fstream->error_code; + io_stream_set_error(&stream->iostream, "%s", + fstream->error_string); + } + return ret; +} + +struct istream * +i_stream_create_failure_at(struct istream *input, uoff_t failure_offset, + int stream_errno, const char *error_string) +{ + struct failure_at_istream *fstream; + + fstream = i_new(struct failure_at_istream, 1); + fstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + fstream->istream.stream_size_passthrough = TRUE; + + fstream->istream.read = i_stream_failure_at_read; + fstream->istream.iostream.destroy = i_stream_failure_at_destroy; + + fstream->istream.istream.readable_fd = input->readable_fd; + fstream->istream.istream.blocking = input->blocking; + fstream->istream.istream.seekable = input->seekable; + + fstream->error_code = stream_errno; + fstream->error_string = i_strdup(error_string); + fstream->failure_offset = failure_offset; + return i_stream_create(&fstream->istream, input, + i_stream_get_fd(input), 0); +} + +struct istream * +i_stream_create_failure_at_eof(struct istream *input, int stream_errno, + const char *error_string) +{ + return i_stream_create_failure_at(input, UOFF_T_MAX, stream_errno, + error_string); +} diff --git a/src/lib/istream-failure-at.h b/src/lib/istream-failure-at.h new file mode 100644 index 0000000..2ba05d5 --- /dev/null +++ b/src/lib/istream-failure-at.h @@ -0,0 +1,11 @@ +#ifndef ISTREAM_FAILURE_AT_H +#define ISTREAM_FAILURE_AT_H + +struct istream * +i_stream_create_failure_at(struct istream *input, uoff_t failure_offset, + int stream_errno, const char *error_string); +struct istream * +i_stream_create_failure_at_eof(struct istream *input, int stream_errno, + const char *error_string); + +#endif diff --git a/src/lib/istream-file-private.h b/src/lib/istream-file-private.h new file mode 100644 index 0000000..c4701ed --- /dev/null +++ b/src/lib/istream-file-private.h @@ -0,0 +1,23 @@ +#ifndef ISTREAM_FILE_PRIVATE_H +#define ISTREAM_FILE_PRIVATE_H + +#include "istream-private.h" + +struct file_istream { + struct istream_private istream; + + uoff_t skip_left; + + bool file:1; + bool autoclose_fd:1; + bool seen_eof:1; +}; + +struct istream * +i_stream_create_file_common(struct file_istream *fstream, + int fd, const char *path, + size_t max_buffer_size, bool autoclose_fd); +ssize_t i_stream_file_read(struct istream_private *stream); +void i_stream_file_close(struct iostream_private *stream, bool close_parent); + +#endif diff --git a/src/lib/istream-file.c b/src/lib/istream-file.c new file mode 100644 index 0000000..8c945bd --- /dev/null +++ b/src/lib/istream-file.c @@ -0,0 +1,282 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +/* @UNSAFE: whole file */ + +#include "lib.h" +#include "ioloop.h" +#include "istream-file-private.h" +#include "net.h" + +#include <time.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> + +void i_stream_file_close(struct iostream_private *stream, + bool close_parent ATTR_UNUSED) +{ + struct istream_private *_stream = + container_of(stream, struct istream_private, iostream); + struct file_istream *fstream = + container_of(_stream, struct file_istream, istream); + + if (fstream->autoclose_fd && _stream->fd != -1) { + /* Ignore ECONNRESET because we don't really care about it here, + as we are closing the socket down in any case. There might be + unsent data but nothing we can do about that. */ + if (unlikely(close(_stream->fd) < 0 && errno != ECONNRESET)) { + i_error("file_istream.close(%s) failed: %m", + i_stream_get_name(&_stream->istream)); + } + } + _stream->fd = -1; +} + +static int i_stream_file_open(struct istream_private *stream) +{ + const char *path = i_stream_get_name(&stream->istream); + + stream->fd = open(path, O_RDONLY); + if (stream->fd == -1) { + io_stream_set_error(&stream->iostream, + "open(%s) failed: %m", path); + stream->istream.stream_errno = errno; + return -1; + } + return 0; +} + +ssize_t i_stream_file_read(struct istream_private *stream) +{ + struct file_istream *fstream = + container_of(stream, struct file_istream, istream); + uoff_t offset; + size_t size; + ssize_t ret; + + if (!i_stream_try_alloc(stream, 1, &size)) + return -2; + + if (stream->fd == -1) { + if (i_stream_file_open(stream) < 0) + return -1; + i_assert(stream->fd != -1); + } + + offset = stream->istream.v_offset + (stream->pos - stream->skip); + + if (fstream->file) { + ret = pread(stream->fd, stream->w_buffer + stream->pos, + size, offset); + } else if (fstream->seen_eof) { + /* don't try to read() again. EOF from keyboard (^D) + requires this to work right. */ + ret = 0; + } else { + ret = read(stream->fd, stream->w_buffer + stream->pos, + size); + } + + if (ret == 0) { + /* EOF */ + stream->istream.eof = TRUE; + fstream->seen_eof = TRUE; + return -1; + } + + if (unlikely(ret < 0)) { + if ((errno == EINTR || errno == EAGAIN) && + !stream->istream.blocking) { + ret = 0; + } else { + i_assert(errno != 0); + /* if we get EBADF for a valid fd, it means something's + really wrong and we'd better just crash. */ + i_assert(errno != EBADF); + if (fstream->file) { + io_stream_set_error(&stream->iostream, + "pread(size=%zu offset=%"PRIuUOFF_T") failed: %m", + size, offset); + } else { + io_stream_set_error(&stream->iostream, + "read(size=%zu) failed: %m", + size); + } + stream->istream.stream_errno = errno; + return -1; + } + } + + if (ret > 0 && fstream->skip_left > 0) { + i_assert(!fstream->file); + i_assert(stream->skip == stream->pos); + + if (fstream->skip_left >= (size_t)ret) { + fstream->skip_left -= ret; + ret = 0; + } else { + ret -= fstream->skip_left; + stream->pos += fstream->skip_left; + stream->skip += fstream->skip_left; + fstream->skip_left = 0; + } + } + + stream->pos += ret; + i_assert(ret != 0 || !fstream->file); + i_assert(ret != -1); + return ret; +} + +static void i_stream_file_seek(struct istream_private *stream, uoff_t v_offset, + bool mark ATTR_UNUSED) +{ + struct file_istream *fstream = + container_of(stream, struct file_istream, istream); + + if (!stream->istream.seekable) { + if (v_offset < stream->istream.v_offset) + i_panic("stream doesn't support seeking backwards"); + fstream->skip_left += v_offset - stream->istream.v_offset; + } + + stream->istream.v_offset = v_offset; + stream->skip = stream->pos = 0; + fstream->seen_eof = FALSE; +} + +static void i_stream_file_sync(struct istream_private *stream) +{ + if (!stream->istream.seekable) { + /* can't do anything or data would be lost */ + return; + } + + stream->skip = stream->pos = 0; + stream->istream.eof = FALSE; +} + +static int +i_stream_file_stat(struct istream_private *stream, bool exact ATTR_UNUSED) +{ + struct file_istream *fstream = + container_of(stream, struct file_istream, istream); + const char *name = i_stream_get_name(&stream->istream); + + if (!fstream->file) { + /* return defaults */ + } else if (stream->fd != -1) { + if (fstat(stream->fd, &stream->statbuf) < 0) { + stream->istream.stream_errno = errno; + io_stream_set_error(&stream->iostream, + "file_istream.fstat(%s) failed: %m", name); + i_error("%s", i_stream_get_error(&stream->istream)); + return -1; + } + } else { + if (stat(name, &stream->statbuf) < 0) { + stream->istream.stream_errno = errno; + io_stream_set_error(&stream->iostream, + "file_istream.stat(%s) failed: %m", name); + i_error("%s", i_stream_get_error(&stream->istream)); + return -1; + } + } + return 0; +} + +struct istream * +i_stream_create_file_common(struct file_istream *fstream, + int fd, const char *path, + size_t max_buffer_size, bool autoclose_fd) +{ + struct istream *input; + struct stat st; + bool is_file; + int flags; + + fstream->autoclose_fd = autoclose_fd; + + fstream->istream.iostream.close = i_stream_file_close; + fstream->istream.max_buffer_size = max_buffer_size; + fstream->istream.read = i_stream_file_read; + fstream->istream.seek = i_stream_file_seek; + fstream->istream.sync = i_stream_file_sync; + fstream->istream.stat = i_stream_file_stat; + + /* if it's a file, set the flags properly */ + if (fd == -1) { + /* only the path is known for now - the fd is opened later */ + is_file = TRUE; + } else if (fstat(fd, &st) < 0) + is_file = FALSE; + else if (S_ISREG(st.st_mode)) + is_file = TRUE; + else if (!S_ISDIR(st.st_mode)) + is_file = FALSE; + else { + /* we're trying to open a directory. + we're not designed for it. */ + io_stream_set_error(&fstream->istream.iostream, + "%s is a directory, can't read it as file", + path != NULL ? path : t_strdup_printf("<fd %d>", fd)); + fstream->istream.istream.stream_errno = EISDIR; + is_file = FALSE; + } + if (is_file) { + fstream->file = TRUE; + fstream->istream.istream.blocking = TRUE; + fstream->istream.istream.seekable = TRUE; + } else if ((flags = fcntl(fd, F_GETFL, 0)) < 0) { + i_assert(fd > -1); + /* shouldn't happen */ + fstream->istream.istream.stream_errno = errno; + io_stream_set_error(&fstream->istream.iostream, + "fcntl(%d, F_GETFL) failed: %m", fd); + } else if ((flags & O_NONBLOCK) == 0) { + /* blocking socket/fifo */ + fstream->istream.istream.blocking = TRUE; + } + fstream->istream.istream.readable_fd = TRUE; + + input = i_stream_create(&fstream->istream, NULL, fd, 0); + i_stream_set_name(input, is_file ? "(file)" : "(fd)"); + return input; +} + +struct istream *i_stream_create_fd(int fd, size_t max_buffer_size) +{ + struct file_istream *fstream; + + i_assert(fd != -1); + + fstream = i_new(struct file_istream, 1); + return i_stream_create_file_common(fstream, fd, NULL, + max_buffer_size, FALSE); +} + +struct istream *i_stream_create_fd_autoclose(int *fd, size_t max_buffer_size) +{ + struct istream *input; + struct file_istream *fstream; + + i_assert(*fd != -1); + + fstream = i_new(struct file_istream, 1); + input = i_stream_create_file_common(fstream, *fd, NULL, + max_buffer_size, TRUE); + *fd = -1; + return input; +} + +struct istream *i_stream_create_file(const char *path, size_t max_buffer_size) +{ + struct file_istream *fstream; + struct istream *input; + + fstream = i_new(struct file_istream, 1); + input = i_stream_create_file_common(fstream, -1, path, + max_buffer_size, TRUE); + i_stream_set_name(input, path); + return input; +} diff --git a/src/lib/istream-hash.c b/src/lib/istream-hash.c new file mode 100644 index 0000000..09d2ac2 --- /dev/null +++ b/src/lib/istream-hash.c @@ -0,0 +1,87 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "hash-method.h" +#include "istream-private.h" +#include "istream-hash.h" + +struct hash_istream { + struct istream_private istream; + + const struct hash_method *method; + void *hash_context; + uoff_t high_offset; +}; + +static ssize_t +i_stream_hash_read(struct istream_private *stream) +{ + struct hash_istream *hstream = + container_of(stream, struct hash_istream, istream); + const unsigned char *data; + size_t size; + uoff_t skip; + ssize_t ret; + + i_stream_seek(stream->parent, stream->parent_start_offset + + stream->istream.v_offset); + + ret = i_stream_read_copy_from_parent(&stream->istream); + if (ret > 0 && hstream->hash_context != NULL) { + data = i_stream_get_data(&stream->istream, &size); + i_assert((size_t)ret <= size); + + i_assert(stream->istream.v_offset <= hstream->high_offset); + skip = hstream->high_offset - stream->istream.v_offset; + if (skip < (size_t)size) { + hstream->high_offset += (size-skip); + hstream->method->loop(hstream->hash_context, + data+skip, size-skip); + } + } else if (ret < 0) { + /* we finished hashing it. don't access it anymore, because + the memory pointed by the hash may be freed before the + istream itself */ + hstream->hash_context = NULL; + } + return ret; +} + +static void +i_stream_hash_seek(struct istream_private *stream, + uoff_t v_offset, bool mark ATTR_UNUSED) +{ + struct hash_istream *hstream = + container_of(stream, struct hash_istream, istream); + + if (hstream->hash_context != NULL) { + io_stream_set_error(&stream->iostream, + "Seeking not supported before hashing is finished"); + stream->istream.stream_errno = ESPIPE; + } + stream->istream.v_offset = v_offset; + stream->skip = stream->pos = 0; +} + +struct istream * +i_stream_create_hash(struct istream *input, const struct hash_method *method, + void *hash_context) +{ + struct hash_istream *hstream; + + hstream = i_new(struct hash_istream, 1); + hstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + hstream->istream.stream_size_passthrough = TRUE; + + hstream->istream.read = i_stream_hash_read; + hstream->istream.seek = i_stream_hash_seek; + + hstream->istream.istream.readable_fd = input->readable_fd; + hstream->istream.istream.blocking = input->blocking; + hstream->istream.istream.seekable = input->seekable; + + hstream->method = method; + hstream->hash_context = hash_context; + return i_stream_create(&hstream->istream, input, + i_stream_get_fd(input), 0); +} diff --git a/src/lib/istream-hash.h b/src/lib/istream-hash.h new file mode 100644 index 0000000..bd22201 --- /dev/null +++ b/src/lib/istream-hash.h @@ -0,0 +1,12 @@ +#ifndef ISTREAM_HASH_H +#define ISTREAM_HASH_H + +struct hash_method; + +/* hash_context must be allocated and initialized by caller. This istream will + simply call method->loop() for all the data going through the istream. */ +struct istream * +i_stream_create_hash(struct istream *input, const struct hash_method *method, + void *hash_context); + +#endif diff --git a/src/lib/istream-jsonstr.c b/src/lib/istream-jsonstr.c new file mode 100644 index 0000000..727f21c --- /dev/null +++ b/src/lib/istream-jsonstr.c @@ -0,0 +1,217 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "hex-dec.h" +#include "unichar.h" +#include "istream-private.h" +#include "istream-jsonstr.h" + +#define MAX_UTF8_LEN 6 + +struct jsonstr_istream { + struct istream_private istream; + + /* The end '"' was found */ + bool str_end:1; +}; + +static int +i_stream_jsonstr_read_parent(struct jsonstr_istream *jstream, + unsigned int min_bytes) +{ + struct istream_private *stream = &jstream->istream; + size_t size, avail; + ssize_t ret; + + size = i_stream_get_data_size(stream->parent); + while (size < min_bytes) { + ret = i_stream_read_memarea(stream->parent); + if (ret <= 0) { + if (ret == -2) { + /* tiny parent buffer size - shouldn't happen */ + return -2; + } + stream->istream.stream_errno = + stream->parent->stream_errno; + stream->istream.eof = stream->parent->eof; + if (ret == -1 && stream->istream.stream_errno == 0) { + io_stream_set_error(&stream->iostream, + "EOF before trailing <\"> was seen"); + stream->istream.stream_errno = EPIPE; + } + return ret; + } + size = i_stream_get_data_size(stream->parent); + } + + if (!i_stream_try_alloc(stream, size, &avail)) + return -2; + return 1; +} + +static int +i_stream_json_unescape(const unsigned char *src, size_t len, + unsigned char *dest, + unsigned int *src_size_r, unsigned int *dest_size_r) +{ + switch (*src) { + case '"': + case '\\': + case '/': + *dest = *src; + break; + case 'b': + *dest = '\b'; + break; + case 'f': + *dest = '\f'; + break; + case 'n': + *dest = '\n'; + break; + case 'r': + *dest = '\r'; + break; + case 't': + *dest = '\t'; + break; + case 'u': { + char chbuf[5] = {0}; + unichar_t chr,chr2 = 0; + buffer_t buf; + if (len < 5) + return 5; + buffer_create_from_data(&buf, dest, MAX_UTF8_LEN); + memcpy(chbuf, src+1, 4); + if (str_to_uint32_hex(chbuf, &chr)<0) + return -1; + if (UTF16_VALID_LOW_SURROGATE(chr)) + return -1; + /* if we encounter surrogate, we need another \\uxxxx */ + if (UTF16_VALID_HIGH_SURROGATE(chr)) { + if (len < 5+2+4) + return 5+2+4; + if (src[5] != '\\' && src[6] != 'u') + return -1; + memcpy(chbuf, src+7, 4); + if (str_to_uint32_hex(chbuf, &chr2)<0) + return -1; + if (!UTF16_VALID_LOW_SURROGATE(chr2)) + return -1; + chr = uni_join_surrogate(chr, chr2); + } + if (!uni_is_valid_ucs4(chr)) + return -1; + uni_ucs4_to_utf8_c(chr, &buf); + *src_size_r = 5 + (chr2>0?6:0); + *dest_size_r = buf.used; + return 0; + } + default: + return -1; + } + *src_size_r = 1; + *dest_size_r = 1; + return 0; +} + +static ssize_t i_stream_jsonstr_read(struct istream_private *stream) +{ + struct jsonstr_istream *jstream = + container_of(stream, struct jsonstr_istream, istream); + const unsigned char *data; + unsigned int srcskip, destskip, extra; + size_t i, dest, size; + ssize_t ret, ret2; + + if (jstream->str_end) { + stream->istream.eof = TRUE; + return -1; + } + + ret = i_stream_jsonstr_read_parent(jstream, 1); + if (ret <= 0) + return ret; + + /* @UNSAFE */ + dest = stream->pos; + extra = 0; + + data = i_stream_get_data(stream->parent, &size); + for (i = 0; i < size && dest < stream->buffer_size; ) { + if (data[i] == '"') { + jstream->str_end = TRUE; + if (dest == stream->pos) { + stream->istream.eof = TRUE; + return -1; + } + break; + } else if (data[i] == '\\') { + if (i+1 == size) { + /* not enough input for \x */ + extra = 1; + break; + } + if (data[i+1] == 'u' && stream->buffer_size - dest < MAX_UTF8_LEN) { + /* UTF8 output is max. 6 chars */ + if (dest == stream->pos) + return -2; + break; + } + i++; + if ((ret2 = i_stream_json_unescape(data + i, size - i, + stream->w_buffer + dest, + &srcskip, &destskip)) < 0) { + /* invalid string */ + io_stream_set_error(&stream->iostream, + "Invalid JSON string"); + stream->istream.stream_errno = EINVAL; + return -1; + } else if (ret2 > 0) { + /* we need to get more bytes, do not consume + escape slash */ + i--; + extra = ret2; + break; + } + i += srcskip; + i_assert(i <= size); + dest += destskip; + i_assert(dest <= stream->buffer_size); + } else { + stream->w_buffer[dest++] = data[i]; + i++; + } + } + i_stream_skip(stream->parent, i); + + ret = dest - stream->pos; + if (ret == 0) { + /* not enough input */ + i_assert(i == 0); + i_assert(extra > 0); + ret = i_stream_jsonstr_read_parent(jstream, extra+1); + if (ret <= 0) + return ret; + return i_stream_jsonstr_read(stream); + } + i_assert(ret > 0); + stream->pos = dest; + return ret; +} + +struct istream *i_stream_create_jsonstr(struct istream *input) +{ + struct jsonstr_istream *dstream; + + dstream = i_new(struct jsonstr_istream, 1); + dstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + dstream->istream.read = i_stream_jsonstr_read; + + dstream->istream.istream.readable_fd = FALSE; + dstream->istream.istream.blocking = input->blocking; + dstream->istream.istream.seekable = FALSE; + return i_stream_create(&dstream->istream, input, + i_stream_get_fd(input), 0); +} diff --git a/src/lib/istream-jsonstr.h b/src/lib/istream-jsonstr.h new file mode 100644 index 0000000..255eccd --- /dev/null +++ b/src/lib/istream-jsonstr.h @@ -0,0 +1,7 @@ +#ifndef ISTREAM_JSONSTR_H +#define ISTREAM_JSONSTR_H + +/* Parse input until '"' is reached. Unescape JSON \x codes. */ +struct istream *i_stream_create_jsonstr(struct istream *input); + +#endif diff --git a/src/lib/istream-limit.c b/src/lib/istream-limit.c new file mode 100644 index 0000000..f66cef3 --- /dev/null +++ b/src/lib/istream-limit.c @@ -0,0 +1,144 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream-private.h" + +struct limit_istream { + struct istream_private istream; + + uoff_t v_size; +}; + +static void i_stream_limit_destroy(struct iostream_private *stream) +{ + struct limit_istream *lstream = + container_of(stream, struct limit_istream, istream.iostream); + uoff_t v_offset; + + v_offset = lstream->istream.parent_start_offset + + lstream->istream.istream.v_offset; + if (lstream->istream.parent->seekable || + v_offset > lstream->istream.parent->v_offset) { + /* get to same position in parent stream */ + i_stream_seek(lstream->istream.parent, v_offset); + } +} + +static ssize_t i_stream_limit_read(struct istream_private *stream) +{ + struct limit_istream *lstream = + container_of(stream, struct limit_istream, istream); + uoff_t left; + ssize_t ret; + size_t pos; + + i_stream_seek(stream->parent, lstream->istream.parent_start_offset + + stream->istream.v_offset); + + if (stream->istream.v_offset + + (stream->pos - stream->skip) >= lstream->v_size) { + stream->istream.eof = TRUE; + return -1; + } + + stream->pos -= stream->skip; + stream->skip = 0; + + stream->buffer = i_stream_get_data(stream->parent, &pos); + if (pos > stream->pos) + ret = 0; + else do { + ret = i_stream_read_memarea(stream->parent); + stream->istream.stream_errno = stream->parent->stream_errno; + stream->istream.eof = stream->parent->eof; + stream->buffer = i_stream_get_data(stream->parent, &pos); + } while (pos <= stream->pos && ret > 0); + if (ret == -2) + return -2; + + if (lstream->v_size != UOFF_T_MAX) { + left = lstream->v_size - stream->istream.v_offset; + if (pos >= left) { + pos = left; + stream->istream.eof = TRUE; + } + } + + ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : + (ret == 0 ? 0 : -1); + stream->pos = pos; + i_assert(ret != -1 || stream->istream.eof || + stream->istream.stream_errno != 0); + return ret; +} + +static int +i_stream_limit_stat(struct istream_private *stream, bool exact) +{ + struct limit_istream *lstream = + container_of(stream, struct limit_istream, istream); + const struct stat *st; + + if (i_stream_stat(stream->parent, exact, &st) < 0) { + stream->istream.stream_errno = stream->parent->stream_errno; + return -1; + } + + stream->statbuf = *st; + if (lstream->v_size != UOFF_T_MAX) + stream->statbuf.st_size = lstream->v_size; + return 0; +} + +static int i_stream_limit_get_size(struct istream_private *stream, + bool exact, uoff_t *size_r) +{ + struct limit_istream *lstream = + container_of(stream, struct limit_istream, istream); + const struct stat *st; + + if (lstream->v_size != UOFF_T_MAX) { + *size_r = lstream->v_size; + return 1; + } + + if (i_stream_stat(&stream->istream, exact, &st) < 0) + return -1; + if (st->st_size == -1) + return 0; + + *size_r = st->st_size; + return 1; +} + +struct istream *i_stream_create_limit(struct istream *input, uoff_t v_size) +{ + struct limit_istream *lstream; + + lstream = i_new(struct limit_istream, 1); + lstream->v_size = v_size; + lstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + + lstream->istream.iostream.destroy = i_stream_limit_destroy; + lstream->istream.read = i_stream_limit_read; + lstream->istream.stat = i_stream_limit_stat; + lstream->istream.get_size = i_stream_limit_get_size; + + lstream->istream.istream.readable_fd = input->readable_fd; + lstream->istream.istream.blocking = input->blocking; + lstream->istream.istream.seekable = input->seekable; + return i_stream_create(&lstream->istream, input, + i_stream_get_fd(input), 0); +} + +struct istream *i_stream_create_range(struct istream *input, + uoff_t v_offset, uoff_t v_size) +{ + uoff_t orig_offset = input->v_offset; + struct istream *ret; + + input->v_offset = v_offset; + ret = i_stream_create_limit(input, v_size); + input->v_offset = orig_offset; + return ret; +} diff --git a/src/lib/istream-multiplex.c b/src/lib/istream-multiplex.c new file mode 100644 index 0000000..c70d0cc --- /dev/null +++ b/src/lib/istream-multiplex.c @@ -0,0 +1,298 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "array.h" +#include "istream-private.h" +#include "istream-multiplex.h" + +/* all multiplex packets are [1 byte cid][4 byte length][data] */ + +struct multiplex_istream; + +struct multiplex_ichannel { + struct istream_private istream; + struct multiplex_istream *mstream; + uint8_t cid; + size_t pending_pos; + bool closed:1; +}; + +struct multiplex_istream { + struct istream *parent; + + /* channel 0 is main channel */ + uint8_t cur_channel; + unsigned int remain; + size_t bufsize; + ARRAY(struct multiplex_ichannel *) channels; + + bool blocking:1; +}; + +static ssize_t i_stream_multiplex_ichannel_read(struct istream_private *stream); + +static struct multiplex_ichannel * +get_channel(struct multiplex_istream *mstream, uint8_t cid) +{ + struct multiplex_ichannel *channel; + i_assert(mstream != NULL); + array_foreach_elem(&mstream->channels, channel) { + if (channel != NULL && channel->cid == cid) + return channel; + } + return NULL; +} + +static void propagate_error(struct multiplex_istream *mstream, int stream_errno) +{ + struct multiplex_ichannel *channel; + array_foreach_elem(&mstream->channels, channel) + if (channel != NULL) + channel->istream.istream.stream_errno = stream_errno; +} + +static void propagate_eof(struct multiplex_istream *mstream) +{ + struct multiplex_ichannel *channel; + array_foreach_elem(&mstream->channels, channel) { + if (channel == NULL) + continue; + + channel->istream.istream.eof = TRUE; + if (mstream->remain > 0) { + channel->istream.istream.stream_errno = EPIPE; + io_stream_set_error(&channel->istream.iostream, + "Unexpected EOF - %u bytes remaining in packet", + mstream->remain); + } + } +} + +static ssize_t +i_stream_multiplex_read(struct multiplex_istream *mstream, + struct multiplex_ichannel *req_channel) +{ + const unsigned char *data; + size_t len = 0, used, wanted, avail; + ssize_t ret, got = 0; + + if (mstream->parent == NULL) { + req_channel->istream.istream.eof = TRUE; + return -1; + } + + (void)i_stream_get_data(mstream->parent, &len); + + if (len == 0 && mstream->parent->closed) { + req_channel->istream.istream.eof = TRUE; + return -1; + } + + if (((mstream->remain > 0 && len == 0) || + (mstream->remain == 0 && len < 5)) && + (ret = i_stream_read_memarea(mstream->parent)) <= 0) { + propagate_error(mstream, mstream->parent->stream_errno); + if (mstream->parent->eof) + propagate_eof(mstream); + return ret; + } + + for(;;) { + data = i_stream_get_data(mstream->parent, &len); + if (len == 0) { + if (got == 0 && mstream->blocking) { + /* can't return 0 with blocking istreams, + so try again from the beginning. */ + return i_stream_multiplex_read(mstream, req_channel); + } + break; + } + + if (mstream->remain > 0) { + struct multiplex_ichannel *channel = + get_channel(mstream, mstream->cur_channel); + wanted = I_MIN(len, mstream->remain); + /* is it open? */ + if (channel != NULL && !channel->closed) { + struct istream_private *stream = &channel->istream; + stream->pos += channel->pending_pos; + bool alloc_ret = i_stream_try_alloc(stream, wanted, &avail); + stream->pos -= channel->pending_pos; + if (!alloc_ret) { + i_stream_set_input_pending(&stream->istream, TRUE); + if (channel->cid != req_channel->cid) + return 0; + if (got > 0) + break; + return -2; + } + + used = I_MIN(wanted, avail); + + /* dump into buffer */ + if (channel->cid != req_channel->cid) { + i_assert(stream->pos + channel->pending_pos + used <= stream->buffer_size); + memcpy(stream->w_buffer + stream->pos + channel->pending_pos, + data, used); + channel->pending_pos += used; + i_stream_set_input_pending(&stream->istream, TRUE); + } else { + i_assert(stream->pos + used <= stream->buffer_size); + memcpy(stream->w_buffer + stream->pos, data, used); + stream->pos += used; + got += used; + } + } else { + used = wanted; + } + mstream->remain -= used; + i_stream_skip(mstream->parent, used); + /* see if there is more to read */ + continue; + } + if (mstream->remain == 0) { + /* need more data */ + if (len < 5) { + ret = i_stream_multiplex_ichannel_read(&req_channel->istream); + if (ret > 0) + got += ret; + break; + } + /* channel ID */ + mstream->cur_channel = data[0]; + /* data length */ + mstream->remain = be32_to_cpu_unaligned(data+1); + i_stream_skip(mstream->parent, 5); + } + } + + propagate_error(mstream, mstream->parent->stream_errno); + if (mstream->parent->eof) + propagate_eof(mstream); + + return got; +} + +static ssize_t i_stream_multiplex_ichannel_read(struct istream_private *stream) +{ + struct multiplex_ichannel *channel = + container_of(stream, struct multiplex_ichannel, istream); + /* if previous multiplex read dumped data for us + actually serve it here. */ + if (channel->pending_pos > 0) { + ssize_t ret = channel->pending_pos; + stream->pos += channel->pending_pos; + channel->pending_pos = 0; + return ret; + } + return i_stream_multiplex_read(channel->mstream, channel); +} + +static void +i_stream_multiplex_ichannel_switch_ioloop_to(struct istream_private *stream, + struct ioloop *ioloop) +{ + struct multiplex_ichannel *channel = + container_of(stream, struct multiplex_ichannel, istream); + + i_stream_switch_ioloop_to(channel->mstream->parent, ioloop); +} + +static void +i_stream_multiplex_ichannel_close(struct iostream_private *stream, bool close_parent) +{ + struct multiplex_ichannel *arr_channel; + struct multiplex_ichannel *channel = + container_of(stream, struct multiplex_ichannel, + istream.iostream); + channel->closed = TRUE; + if (close_parent) { + array_foreach_elem(&channel->mstream->channels, arr_channel) + if (arr_channel != NULL && !arr_channel->closed) + return; + i_stream_close(channel->mstream->parent); + } +} + +static void i_stream_multiplex_try_destroy(struct multiplex_istream *mstream) +{ + struct multiplex_ichannel *channel; + /* can't do anything until they are all closed */ + array_foreach_elem(&mstream->channels, channel) + if (channel != NULL) + return; + i_stream_unref(&mstream->parent); + array_free(&mstream->channels); + i_free(mstream); +} + +static void i_stream_multiplex_ichannel_destroy(struct iostream_private *stream) +{ + struct multiplex_ichannel **channelp; + struct multiplex_ichannel *channel = + container_of(stream, struct multiplex_ichannel, + istream.iostream); + i_stream_multiplex_ichannel_close(stream, TRUE); + i_stream_free_buffer(&channel->istream); + array_foreach_modifiable(&channel->mstream->channels, channelp) { + if (*channelp == channel) { + *channelp = NULL; + break; + } + } + i_stream_multiplex_try_destroy(channel->mstream); +} + +static struct istream * +i_stream_add_channel_real(struct multiplex_istream *mstream, uint8_t cid) +{ + struct multiplex_ichannel *channel = i_new(struct multiplex_ichannel, 1); + channel->cid = cid; + channel->mstream = mstream; + channel->istream.read = i_stream_multiplex_ichannel_read; + channel->istream.switch_ioloop_to = i_stream_multiplex_ichannel_switch_ioloop_to; + channel->istream.iostream.close = i_stream_multiplex_ichannel_close; + channel->istream.iostream.destroy = i_stream_multiplex_ichannel_destroy; + channel->istream.max_buffer_size = mstream->bufsize; + channel->istream.istream.blocking = mstream->blocking; + if (cid == 0) + channel->istream.fd = i_stream_get_fd(mstream->parent); + else + channel->istream.fd = -1; + array_push_back(&channel->mstream->channels, &channel); + + return i_stream_create(&channel->istream, NULL, channel->istream.fd, 0); +} + +struct istream *i_stream_multiplex_add_channel(struct istream *stream, uint8_t cid) +{ + struct multiplex_ichannel *chan = + container_of(stream->real_stream, + struct multiplex_ichannel, istream); + i_assert(get_channel(chan->mstream, cid) == NULL); + + return i_stream_add_channel_real(chan->mstream, cid); +} + +struct istream *i_stream_create_multiplex(struct istream *parent, size_t bufsize) +{ + struct multiplex_istream *mstream; + + mstream = i_new(struct multiplex_istream, 1); + mstream->parent = parent; + mstream->bufsize = bufsize; + mstream->blocking = parent->blocking; + i_array_init(&mstream->channels, 8); + i_stream_ref(parent); + + return i_stream_add_channel_real(mstream, 0); +} + +uint8_t i_stream_multiplex_get_channel_id(struct istream *stream) +{ + struct multiplex_ichannel *channel = + container_of(stream->real_stream, + struct multiplex_ichannel, istream); + return channel->cid; +} diff --git a/src/lib/istream-multiplex.h b/src/lib/istream-multiplex.h new file mode 100644 index 0000000..3f289f4 --- /dev/null +++ b/src/lib/istream-multiplex.h @@ -0,0 +1,8 @@ +#ifndef ISTREAM_MULTIPLEX +#define ISTREAM_MULTIPLEX 1 + +struct istream *i_stream_create_multiplex(struct istream *parent, size_t bufsize); +struct istream *i_stream_multiplex_add_channel(struct istream *stream, uint8_t cid); +uint8_t i_stream_multiplex_get_channel_id(struct istream *stream); + +#endif diff --git a/src/lib/istream-private.h b/src/lib/istream-private.h new file mode 100644 index 0000000..b612b9c --- /dev/null +++ b/src/lib/istream-private.h @@ -0,0 +1,140 @@ +#ifndef ISTREAM_PRIVATE_H +#define ISTREAM_PRIVATE_H + +#include "istream.h" +#include "iostream-private.h" + +#define I_STREAM_MIN_SIZE IO_BLOCK_SIZE + +struct io; + +struct istream_private { +/* inheritance: */ + struct iostream_private iostream; + +/* methods: */ + ssize_t (*read)(struct istream_private *stream); + void (*seek)(struct istream_private *stream, + uoff_t v_offset, bool mark); + void (*sync)(struct istream_private *stream); + int (*stat)(struct istream_private *stream, bool exact); + int (*get_size)(struct istream_private *stream, bool exact, uoff_t *size_r); + void (*switch_ioloop_to)(struct istream_private *stream, + struct ioloop *ioloop); + struct istream_snapshot * + (*snapshot)(struct istream_private *stream, + struct istream_snapshot *prev_snapshot); + +/* data: */ + struct istream istream; + + int fd; + uoff_t start_offset; + struct stat statbuf; + /* added by io_add_istream() -> i_stream_set_io() */ + struct io *io; + + const unsigned char *buffer; + unsigned char *w_buffer; /* may be NULL */ + + size_t buffer_size, max_buffer_size, init_buffer_size, data_limit; + size_t skip, pos; + /* If seeking backwards within the buffer, the next read() will + return again pos..high_pos */ + size_t high_pos; + + struct istream *parent; /* for filter streams */ + uoff_t parent_start_offset; + /* Initially UOFF_T_MAX. Otherwise it's the exact known stream size, + which can be used by stat() / get_size(). */ + uoff_t cached_stream_size; + + /* parent stream's expected offset is kept here. i_stream_read() + always seeks parent stream to here before calling read(). */ + uoff_t parent_expected_offset; + + struct memarea *memarea; + struct istream_snapshot *prev_snapshot; + /* increased every time the stream is changed (e.g. seek, read). + this way streams can check if their parent streams have been + accessed behind them. */ + unsigned int access_counter; + /* Timestamp when read() last returned >0 */ + struct timeval last_read_timeval; + + string_t *line_str; /* for i_stream_next_line() if w_buffer == NULL */ + bool line_crlf:1; + bool return_nolf_line:1; + bool stream_size_passthrough:1; /* stream is parent's size */ + bool nonpersistent_buffers:1; + bool io_pending:1; +}; + +struct istream_snapshot { + struct istream_snapshot *prev_snapshot; + struct memarea *old_memarea; + struct istream *istream; + void (*free)(struct istream_snapshot *snapshot); +}; + +enum istream_create_flag { + /* The stream guarantees that the buffer pointer stays valid when it + returns <= 0. */ + ISTREAM_CREATE_FLAG_NOOP_SNAPSHOT = 0x01, +}; + +struct istream * ATTR_NOWARN_UNUSED_RESULT +i_stream_create(struct istream_private *stream, struct istream *parent, int fd, + enum istream_create_flag flags) ATTR_NULL(2); +/* Initialize parent lazily after i_stream_create() has already been called. */ +void i_stream_init_parent(struct istream_private *_stream, + struct istream *parent); + +void i_stream_compress(struct istream_private *stream); +void i_stream_grow_buffer(struct istream_private *stream, size_t bytes); +bool ATTR_NOWARN_UNUSED_RESULT +i_stream_try_alloc(struct istream_private *stream, + size_t wanted_size, size_t *size_r); +/* Like i_stream_try_alloc(), but compress only if it's the only way to get + more space. This can be useful when stream is marked with + i_stream_seek_mark() */ +bool ATTR_NOWARN_UNUSED_RESULT +i_stream_try_alloc_avoid_compress(struct istream_private *stream, + size_t wanted_size, size_t *size_r); +void *i_stream_alloc(struct istream_private *stream, size_t size); +/* Detach istream from its current memarea. This unreferences the memarea and + resets the w_buffer to empty. This can be used to make sure i_stream_*alloc() + won't return a pointer to memory referenced to in a snapshot. */ +void i_stream_memarea_detach(struct istream_private *stream); +/* Free memory allocated by i_stream_*alloc() */ +void i_stream_free_buffer(struct istream_private *stream); +ssize_t i_stream_read_copy_from_parent(struct istream *istream); +void i_stream_default_seek_nonseekable(struct istream_private *stream, + uoff_t v_offset, bool mark); +/* Returns FALSE if seeking must be done by starting from the beginning. + The caller is then expected to reset the stream and call this function + again, which should work then. If TRUE is returned, the seek was either + successfully done or stream_errno is set. */ +bool i_stream_nonseekable_try_seek(struct istream_private *stream, + uoff_t v_offset); + +/* Default snapshot handling: use memarea if it exists, otherwise snapshot + parent stream. */ +struct istream_snapshot * +i_stream_default_snapshot(struct istream_private *stream, + struct istream_snapshot *prev_snapshot); +void i_stream_snapshot_free(struct istream_snapshot **snapshot); + +struct istream *i_stream_get_root_io(struct istream *stream); +void i_stream_set_io(struct istream *stream, struct io *io); +void i_stream_unset_io(struct istream *stream, struct io *io); + +/* Filter istreams should be calling this instead of i_stream_read() to avoid + unnecessarily referencing memareas. After this call any pointers to the + parent istream's content must be considered as potentially invalid and have + to be updated, even if the return value is <=0. */ +ssize_t i_stream_read_memarea(struct istream *stream); +int i_stream_read_more_memarea(struct istream *stream, + const unsigned char **data_r, size_t *size_r); + +#endif diff --git a/src/lib/istream-rawlog.c b/src/lib/istream-rawlog.c new file mode 100644 index 0000000..2a44429 --- /dev/null +++ b/src/lib/istream-rawlog.c @@ -0,0 +1,119 @@ +/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ostream.h" +#include "iostream-rawlog-private.h" +#include "istream-private.h" +#include "istream-rawlog.h" + +struct rawlog_istream { + struct istream_private istream; + struct rawlog_iostream riostream; +}; + +static void i_stream_rawlog_close(struct iostream_private *stream, + bool close_parent) +{ + struct rawlog_istream *rstream = + container_of(stream, struct rawlog_istream, istream.iostream); + + iostream_rawlog_close(&rstream->riostream); + if (close_parent) + i_stream_close(rstream->istream.parent); +} + +static void i_stream_rawlog_destroy(struct iostream_private *stream) +{ + struct rawlog_istream *rstream = + container_of(stream, struct rawlog_istream, istream.iostream); + uoff_t v_offset; + + v_offset = rstream->istream.parent_start_offset + + rstream->istream.istream.v_offset; + if (rstream->istream.parent->seekable || + v_offset > rstream->istream.parent->v_offset) { + /* get to same position in parent stream */ + i_stream_seek(rstream->istream.parent, v_offset); + } +} + +static ssize_t i_stream_rawlog_read(struct istream_private *stream) +{ + struct rawlog_istream *rstream = + container_of(stream, struct rawlog_istream, istream); + ssize_t ret; + size_t pos; + + i_stream_seek(stream->parent, rstream->istream.parent_start_offset + + stream->istream.v_offset); + + stream->pos -= stream->skip; + stream->skip = 0; + + stream->buffer = i_stream_get_data(stream->parent, &pos); + if (pos > stream->pos) + ret = 0; + else do { + ret = i_stream_read_memarea(stream->parent); + stream->istream.stream_errno = stream->parent->stream_errno; + stream->istream.eof = stream->parent->eof; + stream->buffer = i_stream_get_data(stream->parent, &pos); + } while (pos <= stream->pos && ret > 0); + if (ret == -2) + return -2; + + if (pos <= stream->pos) + ret = ret == 0 ? 0 : -1; + else { + ret = (ssize_t)(pos - stream->pos); + iostream_rawlog_write(&rstream->riostream, + stream->buffer + stream->pos, ret); + } + stream->pos = pos; + i_assert(ret != -1 || stream->istream.eof || + stream->istream.stream_errno != 0); + return ret; +} + +struct istream * +i_stream_create_rawlog(struct istream *input, const char *rawlog_path, + int rawlog_fd, enum iostream_rawlog_flags flags) +{ + struct ostream *rawlog_output; + bool autoclose_fd = (flags & IOSTREAM_RAWLOG_FLAG_AUTOCLOSE) != 0; + + i_assert(rawlog_path != NULL); + i_assert(rawlog_fd != -1); + + rawlog_output = autoclose_fd ? + o_stream_create_fd_autoclose(&rawlog_fd, 0) : + o_stream_create_fd(rawlog_fd, 0); + o_stream_set_name(rawlog_output, + t_strdup_printf("rawlog(%s)", rawlog_path)); + return i_stream_create_rawlog_from_stream(input, rawlog_output, flags); +} + +struct istream * +i_stream_create_rawlog_from_stream(struct istream *input, + struct ostream *rawlog_output, + enum iostream_rawlog_flags flags) +{ + struct rawlog_istream *rstream; + + rstream = i_new(struct rawlog_istream, 1); + rstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + rstream->istream.stream_size_passthrough = TRUE; + + rstream->riostream.rawlog_output = rawlog_output; + iostream_rawlog_init(&rstream->riostream, flags, TRUE); + + rstream->istream.read = i_stream_rawlog_read; + rstream->istream.iostream.close = i_stream_rawlog_close; + rstream->istream.iostream.destroy = i_stream_rawlog_destroy; + + rstream->istream.istream.readable_fd = input->readable_fd; + rstream->istream.istream.blocking = input->blocking; + rstream->istream.istream.seekable = input->seekable; + return i_stream_create(&rstream->istream, input, + i_stream_get_fd(input), 0); +} diff --git a/src/lib/istream-rawlog.h b/src/lib/istream-rawlog.h new file mode 100644 index 0000000..4126f23 --- /dev/null +++ b/src/lib/istream-rawlog.h @@ -0,0 +1,14 @@ +#ifndef ISTREAM_RAWLOG_H +#define ISTREAM_RAWLOG_H + +#include "iostream-rawlog.h" + +struct istream * +i_stream_create_rawlog(struct istream *input, const char *rawlog_path, + int rawlog_fd, enum iostream_rawlog_flags flags); +struct istream * +i_stream_create_rawlog_from_stream(struct istream *input, + struct ostream *rawlog_output, + enum iostream_rawlog_flags flags); + +#endif diff --git a/src/lib/istream-seekable.c b/src/lib/istream-seekable.c new file mode 100644 index 0000000..5bc19ad --- /dev/null +++ b/src/lib/istream-seekable.c @@ -0,0 +1,558 @@ +/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "str.h" +#include "memarea.h" +#include "read-full.h" +#include "write-full.h" +#include "safe-mkstemp.h" +#include "istream-private.h" +#include "istream-concat.h" +#include "istream-seekable.h" + +#include <unistd.h> + +#define BUF_INITIAL_SIZE (1024*32) + +struct seekable_istream { + struct istream_private istream; + + char *temp_path; + uoff_t write_peak; + uoff_t size; + size_t buffer_peak; + + int (*fd_callback)(const char **path_r, void *context); + void *context; + + struct istream **input, *cur_input; + struct istream *fd_input; + unsigned int cur_idx; + int fd; + bool free_context; +}; + +static void i_stream_seekable_close(struct iostream_private *stream, + bool close_parent ATTR_UNUSED) +{ + struct seekable_istream *sstream = + container_of(stream, struct seekable_istream, istream.iostream); + + sstream->fd = -1; + i_stream_close(sstream->fd_input); +} + +static void unref_streams(struct seekable_istream *sstream) +{ + unsigned int i; + + for (i = 0; sstream->input[i] != NULL; i++) + i_stream_unref(&sstream->input[i]); +} + +static void i_stream_seekable_destroy(struct iostream_private *stream) +{ + struct seekable_istream *sstream = + container_of(stream, struct seekable_istream, istream.iostream); + + i_stream_free_buffer(&sstream->istream); + i_stream_unref(&sstream->fd_input); + unref_streams(sstream); + + if (sstream->free_context) + i_free(sstream->context); + i_free(sstream->temp_path); + i_free(sstream->input); +} + +static void +i_stream_seekable_set_max_buffer_size(struct iostream_private *stream, + size_t max_size) +{ + struct seekable_istream *sstream = + container_of(stream, struct seekable_istream, istream.iostream); + unsigned int i; + + sstream->istream.max_buffer_size = max_size; + if (sstream->fd_input != NULL) + i_stream_set_max_buffer_size(sstream->fd_input, max_size); + for (i = 0; sstream->input[i] != NULL; i++) + i_stream_set_max_buffer_size(sstream->input[i], max_size); +} + +static int copy_to_temp_file(struct seekable_istream *sstream) +{ + struct istream_private *stream = &sstream->istream; + const char *path; + const unsigned char *buffer; + size_t size; + int fd; + + fd = sstream->fd_callback(&path, sstream->context); + if (fd == -1) + return -1; + + /* copy our currently read buffer to it */ + i_assert(stream->pos <= sstream->buffer_peak); + if (write_full(fd, stream->buffer, sstream->buffer_peak) < 0) { + if (!ENOSPACE(errno)) + i_error("istream-seekable: write_full(%s) failed: %m", path); + i_close_fd(&fd); + return -1; + } + sstream->temp_path = i_strdup(path); + sstream->write_peak = sstream->buffer_peak; + + sstream->fd = fd; + sstream->fd_input = i_stream_create_fd_autoclose(&fd, + I_MAX(stream->pos, sstream->istream.max_buffer_size)); + i_stream_set_name(sstream->fd_input, t_strdup_printf( + "(seekable temp-istream for: %s)", i_stream_get_name(&stream->istream))); + + /* read back the data we just had in our buffer */ + for (;;) { + buffer = i_stream_get_data(sstream->fd_input, &size); + if (size >= stream->pos) + break; + + ssize_t ret; + if ((ret = i_stream_read_memarea(sstream->fd_input)) <= 0) { + i_assert(ret != 0); + i_assert(ret != -2); + i_error("istream-seekable: Couldn't read back " + "in-memory input %s: %s", + i_stream_get_name(&stream->istream), + i_stream_get_error(sstream->fd_input)); + i_stream_destroy(&sstream->fd_input); + sstream->fd = -1; /* autoclosed by fd_input */ + return -1; + } + } + /* Set the max buffer size only after we've already read everything + into memory. For example with istream-data it's possible that + more data exists in buffer than max_buffer_size. */ + i_stream_set_max_buffer_size(sstream->fd_input, + sstream->istream.max_buffer_size); + stream->buffer = buffer; + i_stream_free_buffer(&sstream->istream); + return 0; +} + +static ssize_t read_more(struct seekable_istream *sstream) +{ + size_t size; + ssize_t ret; + + if (sstream->cur_input == NULL) { + sstream->istream.istream.eof = TRUE; + return -1; + } + + while ((ret = i_stream_read_memarea(sstream->cur_input)) == -1) { + if (sstream->cur_input->stream_errno != 0) { + io_stream_set_error(&sstream->istream.iostream, + "read(%s) failed: %s", + i_stream_get_name(sstream->cur_input), + i_stream_get_error(sstream->cur_input)); + sstream->istream.istream.eof = TRUE; + sstream->istream.istream.stream_errno = + sstream->cur_input->stream_errno; + return -1; + } + + /* go to next stream */ + sstream->cur_input = sstream->input[sstream->cur_idx++]; + if (sstream->cur_input == NULL) { + /* last one, EOF */ + sstream->size = sstream->istream.istream.v_offset + + (sstream->istream.pos - sstream->istream.skip); + sstream->istream.istream.eof = TRUE; + /* Now that EOF is reached, the stream can't return 0 + anymore. Callers can now use this stream in places + that assert that blocking==TRUE. */ + sstream->istream.istream.blocking = TRUE; + unref_streams(sstream); + return -1; + } + + /* see if stream has pending data */ + size = i_stream_get_data_size(sstream->cur_input); + if (size != 0) + return size; + } + return ret; +} + +static bool read_from_buffer(struct seekable_istream *sstream, ssize_t *ret_r) +{ + struct istream_private *stream = &sstream->istream; + const unsigned char *data; + size_t size, avail_size; + + if (stream->pos < sstream->buffer_peak) { + /* This could be the first read() or we could have already + seeked backwards. */ + i_assert(stream->pos == 0 && stream->skip == 0); + stream->skip = stream->istream.v_offset; + stream->pos = sstream->buffer_peak; + size = stream->pos - stream->skip; + if (stream->istream.v_offset == sstream->buffer_peak) { + /* this could happen after write to temp file failed */ + return read_from_buffer(sstream, ret_r); + } + } else { + /* need to read more */ + i_assert(stream->pos == sstream->buffer_peak); + size = sstream->cur_input == NULL ? 0 : + i_stream_get_data_size(sstream->cur_input); + if (size == 0) { + /* read more to buffer */ + *ret_r = read_more(sstream); + if (*ret_r == 0 || *ret_r == -1) + return TRUE; + } + + /* we should have more now. */ + data = i_stream_get_data(sstream->cur_input, &size); + i_assert(size > 0); + + /* change skip to 0 temporarily so i_stream_try_alloc() won't try to + compress the buffer. */ + size_t old_skip = stream->skip; + stream->skip = 0; + bool have_space = i_stream_try_alloc(stream, size, &avail_size); + stream->skip = old_skip; + if (!have_space) + return FALSE; + + if (size > avail_size) + size = avail_size; + memcpy(stream->w_buffer + stream->pos, data, size); + stream->pos += size; + sstream->buffer_peak += size; + i_stream_skip(sstream->cur_input, size); + } + + *ret_r = size; + i_assert(*ret_r > 0); + return TRUE; +} + +static int i_stream_seekable_write_failed(struct seekable_istream *sstream) +{ + struct istream_private *stream = &sstream->istream; + void *data; + size_t old_pos = stream->pos; + + i_assert(sstream->fd != -1); + i_assert(stream->skip == 0); + + stream->max_buffer_size = SIZE_MAX; + stream->pos = 0; + data = i_stream_alloc(stream, sstream->write_peak); + stream->pos = old_pos; + + if (pread_full(sstream->fd, data, sstream->write_peak, 0) < 0) { + sstream->istream.istream.stream_errno = errno; + sstream->istream.istream.eof = TRUE; + io_stream_set_error(&sstream->istream.iostream, + "istream-seekable: read(%s) failed: %m", + sstream->temp_path); + return -1; + } + i_stream_destroy(&sstream->fd_input); + sstream->fd = -1; /* autoclosed by fd_input */ + + i_free_and_null(sstream->temp_path); + return 0; +} + +static ssize_t i_stream_seekable_read(struct istream_private *stream) +{ + struct seekable_istream *sstream = + container_of(stream, struct seekable_istream, istream); + const unsigned char *data; + size_t size, pos; + ssize_t ret; + + if (sstream->fd == -1) { + if (read_from_buffer(sstream, &ret)) + return ret; + + /* copy everything to temp file and use it as the stream */ + if (copy_to_temp_file(sstream) < 0) { + stream->max_buffer_size = SIZE_MAX; + if (!read_from_buffer(sstream, &ret)) + i_unreached(); + return ret; + } + i_assert(sstream->fd != -1); + } + + stream->buffer = CONST_PTR_OFFSET(stream->buffer, stream->skip); + stream->pos -= stream->skip; + stream->skip = 0; + + i_assert(stream->istream.v_offset + stream->pos <= sstream->write_peak); + if (stream->istream.v_offset + stream->pos == sstream->write_peak) { + /* need to read more */ + if (sstream->cur_input == NULL || + i_stream_get_data_size(sstream->cur_input) == 0) { + ret = read_more(sstream); + if (ret == -1 || ret == 0) + return ret; + } + + /* save to our file */ + data = i_stream_get_data(sstream->cur_input, &size); + ret = write(sstream->fd, data, size); + if (ret <= 0) { + if (ret < 0 && !ENOSPACE(errno)) { + i_error("istream-seekable: write_full(%s) failed: %m", + sstream->temp_path); + } + if (i_stream_seekable_write_failed(sstream) < 0) + return -1; + if (!read_from_buffer(sstream, &ret)) + i_unreached(); + return ret; + } + i_stream_sync(sstream->fd_input); + i_stream_skip(sstream->cur_input, ret); + sstream->write_peak += ret; + } + + i_stream_seek(sstream->fd_input, stream->istream.v_offset); + ret = i_stream_read_memarea(sstream->fd_input); + if (ret <= 0) { + stream->istream.eof = sstream->fd_input->eof; + stream->istream.stream_errno = + sstream->fd_input->stream_errno; + } else { + ret = -2; + } + + stream->buffer = i_stream_get_data(sstream->fd_input, &pos); + stream->pos -= stream->skip; + stream->skip = 0; + + ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : ret; + stream->pos = pos; + return ret; +} + +static int +i_stream_seekable_stat(struct istream_private *stream, bool exact) +{ + struct seekable_istream *sstream = + container_of(stream, struct seekable_istream, istream); + const struct stat *st; + uoff_t old_offset, len; + ssize_t ret; + + if (sstream->size != UOFF_T_MAX) { + /* we've already reached EOF and know the size */ + stream->statbuf.st_size = sstream->size; + return 0; + } + + /* we want to know the full size of the file, so read until + we're finished */ + old_offset = stream->istream.v_offset; + do { + i_stream_skip(&stream->istream, + stream->pos - stream->skip); + } while ((ret = i_stream_seekable_read(stream)) > 0); + + if (ret == 0) { + i_panic("i_stream_stat() used for non-blocking " + "seekable stream %s offset %"PRIuUOFF_T, + i_stream_get_name(sstream->cur_input), + sstream->cur_input->v_offset); + } + i_stream_skip(&stream->istream, stream->pos - stream->skip); + len = stream->pos; + i_stream_seek(&stream->istream, old_offset); + unref_streams(sstream); + + if (stream->istream.stream_errno != 0) + return -1; + + if (sstream->fd_input != NULL) { + /* using a file backed buffer, we can use real fstat() */ + if (i_stream_stat(sstream->fd_input, exact, &st) < 0) + return -1; + stream->statbuf = *st; + } else { + /* buffer is completely in memory */ + i_assert(sstream->fd == -1); + + stream->statbuf.st_size = len; + } + return 0; +} + +static void i_stream_seekable_seek(struct istream_private *stream, + uoff_t v_offset, bool mark) +{ + if (v_offset <= stream->istream.v_offset) { + /* seeking backwards */ + stream->istream.v_offset = v_offset; + stream->skip = stream->pos = 0; + } else { + /* we can't skip over data we haven't yet read and written to + our buffer/temp file */ + i_stream_default_seek_nonseekable(stream, v_offset, mark); + } +} + +static struct istream_snapshot * +i_stream_seekable_snapshot(struct istream_private *stream, + struct istream_snapshot *prev_snapshot) +{ + struct seekable_istream *sstream = + container_of(stream, struct seekable_istream, istream); + + if (sstream->fd == -1) { + /* still in memory */ + if (stream->memarea == NULL) + return prev_snapshot; + return i_stream_default_snapshot(stream, prev_snapshot); + } else { + /* using the fd_input stream */ + return sstream->fd_input->real_stream-> + snapshot(sstream->fd_input->real_stream, prev_snapshot); + } +} + +struct istream * +i_streams_merge(struct istream *input[], size_t max_buffer_size, + int (*fd_callback)(const char **path_r, void *context), + void *context) ATTR_NULL(4) +{ + struct seekable_istream *sstream; + const unsigned char *data; + unsigned int count; + size_t size; + bool blocking = TRUE; + + i_assert(max_buffer_size > 0); + + /* if any of the streams isn't blocking, set ourself also nonblocking */ + for (count = 0; input[count] != NULL; count++) { + if (!input[count]->blocking) + blocking = FALSE; + i_stream_ref(input[count]); + } + i_assert(count != 0); + + sstream = i_new(struct seekable_istream, 1); + sstream->fd_callback = fd_callback; + sstream->context = context; + sstream->istream.max_buffer_size = max_buffer_size; + sstream->fd = -1; + sstream->size = UOFF_T_MAX; + + sstream->input = i_new(struct istream *, count + 1); + memcpy(sstream->input, input, sizeof(*input) * count); + sstream->cur_input = sstream->input[0]; + + sstream->istream.iostream.close = i_stream_seekable_close; + sstream->istream.iostream.destroy = i_stream_seekable_destroy; + sstream->istream.iostream.set_max_buffer_size = + i_stream_seekable_set_max_buffer_size; + + sstream->istream.read = i_stream_seekable_read; + sstream->istream.stat = i_stream_seekable_stat; + sstream->istream.seek = i_stream_seekable_seek; + sstream->istream.snapshot = i_stream_seekable_snapshot; + + sstream->istream.istream.readable_fd = FALSE; + sstream->istream.istream.blocking = blocking; + sstream->istream.istream.seekable = TRUE; + (void)i_stream_create(&sstream->istream, NULL, -1, 0); + + /* initialize our buffer from first stream's pending data */ + data = i_stream_get_data(sstream->cur_input, &size); + if (size > 0) { + memcpy(i_stream_alloc(&sstream->istream, size), data, size); + sstream->buffer_peak = size; + i_stream_skip(sstream->cur_input, size); + } + return &sstream->istream.istream; +} + +static bool inputs_are_seekable(struct istream *input[]) +{ + unsigned int count; + + for (count = 0; input[count] != NULL; count++) { + if (!input[count]->seekable) + return FALSE; + } + return TRUE; +} + +struct istream * +i_stream_create_seekable(struct istream *input[], + size_t max_buffer_size, + int (*fd_callback)(const char **path_r, void *context), + void *context) +{ + i_assert(max_buffer_size > 0); + + /* If all input streams are seekable, use concat istream instead */ + if (inputs_are_seekable(input)) + return i_stream_create_concat(input); + + return i_streams_merge(input, max_buffer_size, fd_callback, context); +} + +static int seekable_fd_callback(const char **path_r, void *context) +{ + char *temp_path_prefix = context; + string_t *path; + int fd; + + path = t_str_new(128); + str_append(path, temp_path_prefix); + fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); + if (fd == -1) { + i_error("istream-seekable: safe_mkstemp(%s) failed: %m", str_c(path)); + return -1; + } + + /* we just want the fd, unlink it */ + if (i_unlink(str_c(path)) < 0) { + /* shouldn't happen.. */ + i_close_fd(&fd); + return -1; + } + + *path_r = str_c(path); + return fd; +} + +struct istream * +i_stream_create_seekable_path(struct istream *input[], + size_t max_buffer_size, + const char *temp_path_prefix) +{ + struct seekable_istream *sstream; + struct istream *stream; + + i_assert(temp_path_prefix != NULL); + i_assert(max_buffer_size > 0); + + if (inputs_are_seekable(input)) + return i_stream_create_concat(input); + + stream = i_stream_create_seekable(input, max_buffer_size, + seekable_fd_callback, + i_strdup(temp_path_prefix)); + sstream = container_of(stream->real_stream, + struct seekable_istream, istream); + sstream->free_context = TRUE; + return stream; +} diff --git a/src/lib/istream-seekable.h b/src/lib/istream-seekable.h new file mode 100644 index 0000000..4cee396 --- /dev/null +++ b/src/lib/istream-seekable.h @@ -0,0 +1,28 @@ +#ifndef ISTREAM_SEEKABLE_H +#define ISTREAM_SEEKABLE_H + +/* Create a seekable stream from given NULL-terminated list of input streams. + Try to keep it in memory, but use a temporary file if it's too large. + + When max_buffer_size is reached, fd_callback is called. It should return + the fd and path of the created file. Typically the callback would also + unlink the file before returning. */ +struct istream * +i_streams_merge(struct istream *input[], size_t max_buffer_size, + int (*fd_callback)(const char **path_r, void *context), + void *context) ATTR_NULL(4); + +/* Same as i_streams_merge(), but if all of the inputs are seekable already, + create a concat stream instead. */ +struct istream * +i_stream_create_seekable(struct istream *input[], + size_t max_buffer_size, + int (*fd_callback)(const char **path_r, void *context), + void *context) ATTR_NULL(4); + +struct istream * +i_stream_create_seekable_path(struct istream *input[], + size_t max_buffer_size, + const char *temp_path_prefix); + +#endif diff --git a/src/lib/istream-sized.c b/src/lib/istream-sized.c new file mode 100644 index 0000000..afe95b7 --- /dev/null +++ b/src/lib/istream-sized.c @@ -0,0 +1,232 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream-private.h" +#include "istream-sized.h" + +struct sized_istream { + struct istream_private istream; + + istream_sized_callback_t *error_callback; + void *error_context; + + uoff_t size; + bool min_size_only; +}; + +static void i_stream_sized_destroy(struct iostream_private *stream) +{ + struct sized_istream *sstream = + container_of(stream, struct sized_istream, istream.iostream); + uoff_t v_offset; + + v_offset = sstream->istream.parent_start_offset + + sstream->istream.istream.v_offset; + if (sstream->istream.parent->seekable || + v_offset > sstream->istream.parent->v_offset) { + /* get to same position in parent stream */ + i_stream_seek(sstream->istream.parent, v_offset); + } +} + +static const char * +i_stream_create_sized_default_error_callback( + const struct istream_sized_error_data *data, void *context ATTR_UNUSED) +{ + if (data->v_offset + data->new_bytes < data->wanted_size) { + return t_strdup_printf("Stream is smaller than expected " + "(%"PRIuUOFF_T" < %"PRIuUOFF_T")", + data->v_offset + data->new_bytes, data->wanted_size); + } else { + return t_strdup_printf("Stream is larger than expected " + "(%"PRIuUOFF_T" > %"PRIuUOFF_T", eof=%d)", + data->v_offset + data->new_bytes, data->wanted_size, + data->eof ? 1 : 0); + } +} + +static ssize_t +i_stream_sized_parent_read(struct istream_private *stream, size_t *pos_r) +{ + ssize_t ret; + + do { + ret = i_stream_read_memarea(stream->parent); + stream->istream.stream_errno = stream->parent->stream_errno; + stream->istream.eof = stream->parent->eof; + stream->buffer = i_stream_get_data(stream->parent, pos_r); + } while (*pos_r <= stream->pos && ret > 0); + return ret; +} + +static ssize_t i_stream_sized_read(struct istream_private *stream) +{ + struct sized_istream *sstream = + container_of(stream, struct sized_istream, istream); + struct istream_sized_error_data data; + const char *error; + uoff_t left; + ssize_t ret; + size_t pos; + + i_stream_seek(stream->parent, sstream->istream.parent_start_offset + + stream->istream.v_offset); + + stream->pos -= stream->skip; + stream->skip = 0; + + stream->buffer = i_stream_get_data(stream->parent, &pos); + if (pos > stream->pos) + ret = 0; + else { + if ((ret = i_stream_sized_parent_read(stream, &pos)) == -2) + return -2; + } + + left = sstream->size - stream->istream.v_offset; + if (pos == left && ret != -1) { + /* we have exactly the wanted amount of data left, but we + don't know yet if there is more data in parent. */ + ret = i_stream_sized_parent_read(stream, &pos); + } + + i_zero(&data); + data.v_offset = stream->istream.v_offset; + data.new_bytes = pos; + data.wanted_size = sstream->size; + data.eof = stream->istream.eof; + + if (pos == left) { + /* we may or may not be finished, depending on whether + parent is at EOF. */ + } else if (pos > left) { + /* parent has more data available than expected */ + if (!sstream->min_size_only) { + error = sstream->error_callback(&data, sstream->error_context); + io_stream_set_error(&stream->iostream, "%s", error); + stream->istream.stream_errno = EINVAL; + return -1; + } + pos = left; + if (pos <= stream->pos) { + stream->istream.eof = TRUE; + ret = -1; + } + } else if (!stream->istream.eof) { + /* still more to read */ + } else if (stream->istream.stream_errno == ENOENT) { + /* lost the file */ + } else { + /* EOF before we reached the wanted size */ + error = sstream->error_callback(&data, sstream->error_context); + io_stream_set_error(&stream->iostream, "%s", error); + stream->istream.stream_errno = EPIPE; + } + + ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : + (ret == 0 ? 0 : -1); + stream->pos = pos; + i_assert(ret != -1 || stream->istream.eof || + stream->istream.stream_errno != 0); + return ret; +} + +static int +i_stream_sized_stat(struct istream_private *stream, bool exact ATTR_UNUSED) +{ + struct sized_istream *sstream = + container_of(stream, struct sized_istream, istream); + const struct stat *st; + + /* parent stream may be base64-decoder. don't waste time decoding the + entire stream, since we already know what the size is supposed + to be. */ + if (i_stream_stat(stream->parent, FALSE, &st) < 0) { + stream->istream.stream_errno = stream->parent->stream_errno; + return -1; + } + + stream->statbuf = *st; + stream->statbuf.st_size = sstream->size; + return 0; +} + +static struct sized_istream * +i_stream_create_sized_common(struct istream *input, uoff_t size) +{ + struct sized_istream *sstream; + + sstream = i_new(struct sized_istream, 1); + sstream->size = size; + sstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + + sstream->istream.iostream.destroy = i_stream_sized_destroy; + sstream->istream.read = i_stream_sized_read; + sstream->istream.stat = i_stream_sized_stat; + + sstream->istream.istream.readable_fd = input->readable_fd; + sstream->istream.istream.blocking = input->blocking; + sstream->istream.istream.seekable = input->seekable; + (void)i_stream_create(&sstream->istream, input, + i_stream_get_fd(input), 0); + return sstream; +} + +struct istream *i_stream_create_sized(struct istream *input, uoff_t size) +{ + struct sized_istream *sstream; + + sstream = i_stream_create_sized_common(input, size); + sstream->error_callback = i_stream_create_sized_default_error_callback; + sstream->error_context = sstream; + return &sstream->istream.istream; +} + +struct istream *i_stream_create_sized_range(struct istream *input, + uoff_t offset, uoff_t size) +{ + uoff_t orig_offset = input->v_offset; + struct istream *ret; + + input->v_offset = offset; + ret = i_stream_create_sized(input, size); + input->v_offset = orig_offset; + return ret; +} + +struct istream *i_stream_create_min_sized(struct istream *input, uoff_t min_size) +{ + struct istream *ret; + + ret= i_stream_create_sized(input, min_size); + struct sized_istream *ret_sstream = + container_of(ret->real_stream, struct sized_istream, istream); + ret_sstream->min_size_only = TRUE; + return ret; +} + +struct istream *i_stream_create_min_sized_range(struct istream *input, + uoff_t offset, uoff_t min_size) +{ + struct istream *ret; + + ret = i_stream_create_sized_range(input, offset, min_size); + struct sized_istream *ret_sstream = + container_of(ret->real_stream, struct sized_istream, istream); + ret_sstream->min_size_only = TRUE; + return ret; +} + +#undef i_stream_create_sized_with_callback +struct istream * +i_stream_create_sized_with_callback(struct istream *input, uoff_t size, + istream_sized_callback_t *error_callback, + void *context) +{ + struct sized_istream *sstream; + + sstream = i_stream_create_sized_common(input, size); + sstream->error_callback = error_callback; + sstream->error_context = context; + return &sstream->istream.istream; +} diff --git a/src/lib/istream-sized.h b/src/lib/istream-sized.h new file mode 100644 index 0000000..7cbb6c6 --- /dev/null +++ b/src/lib/istream-sized.h @@ -0,0 +1,41 @@ +#ifndef ISTREAM_SIZED_H +#define ISTREAM_SIZED_H + +struct istream_sized_error_data { + /* Stream's current v_offset */ + uoff_t v_offset; + /* How many more bytes are being added within this read() */ + size_t new_bytes; + /* What's the original wanted size. */ + uoff_t wanted_size; + /* TRUE if we're at EOF now */ + bool eof; +}; + +typedef const char * +istream_sized_callback_t(const struct istream_sized_error_data *data, + void *context); + +/* Assume that input stream is exactly the given size. If the stream is too + small, fail with stream_errno=EPIPE. If stream is too large, fail with + stream_errno=EINVAL. */ +struct istream *i_stream_create_sized(struct istream *input, uoff_t size); +struct istream *i_stream_create_sized_range(struct istream *input, + uoff_t offset, uoff_t size); +/* Like i_stream_create_sized*(), but allow input stream's size to be larger. */ +struct istream *i_stream_create_min_sized(struct istream *input, uoff_t min_size); +struct istream *i_stream_create_min_sized_range(struct istream *input, + uoff_t offset, uoff_t min_size); +/* Same as i_stream_create_sized(), but set the error message via the + callback. */ +struct istream * +i_stream_create_sized_with_callback(struct istream *input, uoff_t size, + istream_sized_callback_t *error_callback, + void *context); +#define i_stream_create_sized_with_callback(input, size, error_callback, context) \ + i_stream_create_sized_with_callback(input, size - \ + CALLBACK_TYPECHECK(error_callback, \ + const char *(*)(const struct istream_sized_error_data *, typeof(context))), \ + (istream_sized_callback_t *)error_callback, context) + +#endif diff --git a/src/lib/istream-tee.c b/src/lib/istream-tee.c new file mode 100644 index 0000000..858afd7 --- /dev/null +++ b/src/lib/istream-tee.c @@ -0,0 +1,258 @@ +/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream-private.h" +#include "istream-tee.h" + +struct tee_istream { + struct istream *input; + struct tee_child_istream *children; + + uoff_t max_read_offset; +}; + +struct tee_child_istream { + struct istream_private istream; + + struct tee_istream *tee; + struct tee_child_istream *next; + + bool last_read_waiting:1; +}; + +static void tee_streams_update_buffer(struct tee_istream *tee) +{ + struct tee_child_istream *tstream = tee->children; + const unsigned char *data; + size_t size, old_used; + + data = i_stream_get_data(tee->input, &size); + for (; tstream != NULL; tstream = tstream->next) { + if (tstream->istream.istream.closed) { + tstream->istream.skip = tstream->istream.pos = 0; + continue; + } + old_used = tstream->istream.pos - tstream->istream.skip; + + tstream->istream.buffer = data; + i_assert(tstream->istream.istream.v_offset >= tee->input->v_offset); + tstream->istream.skip = tstream->istream.istream.v_offset - + tee->input->v_offset; + i_assert(tstream->istream.skip + old_used <= size); + tstream->istream.pos = tstream->istream.skip + old_used; + + tstream->istream.parent_expected_offset = + tee->input->v_offset; + tstream->istream.access_counter = + tee->input->real_stream->access_counter; + } +} + +static void tee_streams_skip(struct tee_istream *tee) +{ + struct tee_child_istream *tstream = tee->children; + size_t min_skip; + + min_skip = SIZE_MAX; + for (; tstream != NULL; tstream = tstream->next) { + if (tstream->istream.skip < min_skip && + !tstream->istream.istream.closed) + min_skip = tstream->istream.skip; + } + + if (min_skip > 0 && min_skip != SIZE_MAX) { + i_stream_skip(tee->input, min_skip); + tee_streams_update_buffer(tee); + } +} + +static void i_stream_tee_close(struct iostream_private *stream, + bool close_parent ATTR_UNUSED) +{ + struct tee_child_istream *tstream = + container_of(stream, struct tee_child_istream, + istream.iostream); + + tee_streams_skip(tstream->tee); +} + +static void i_stream_tee_destroy(struct iostream_private *stream) +{ + struct tee_child_istream *tstream = + container_of(stream, struct tee_child_istream, + istream.iostream); + struct tee_istream *tee = tstream->tee; + struct tee_child_istream **p; + + if (tstream->istream.istream.v_offset > tee->max_read_offset) + tee->max_read_offset = tstream->istream.istream.v_offset; + + for (p = &tee->children; *p != NULL; p = &(*p)->next) { + if (*p == tstream) { + *p = tstream->next; + break; + } + } + + if (tee->children == NULL) { + /* last child. the tee is now destroyed */ + i_assert(tee->input->v_offset <= tee->max_read_offset); + i_stream_skip(tee->input, + tee->max_read_offset - tee->input->v_offset); + + i_stream_unref(&tee->input); + i_free(tee); + } else { + tee_streams_skip(tstream->tee); + } + /* i_stream_unref() shouldn't unref the parent */ + tstream->istream.parent = NULL; +} + +static void +i_stream_tee_set_max_buffer_size(struct iostream_private *stream, + size_t max_size) +{ + struct tee_child_istream *tstream = + container_of(stream, struct tee_child_istream, + istream.iostream); + + tstream->istream.max_buffer_size = max_size; + i_stream_set_max_buffer_size(tstream->tee->input, max_size); +} + +static ssize_t i_stream_tee_read(struct istream_private *stream) +{ + struct tee_child_istream *tstream = + container_of(stream, struct tee_child_istream, istream); + struct istream *input = tstream->tee->input; + const unsigned char *data; + size_t size; + uoff_t last_high_offset; + ssize_t ret; + + tstream->last_read_waiting = FALSE; + if (stream->buffer == NULL) { + /* initial read */ + tee_streams_update_buffer(tstream->tee); + } + data = i_stream_get_data(input, &size); + + /* last_high_offset contains how far we have read this child tee stream + so far. input->v_offset + size contains how much is available in + the parent stream without having to read more. */ + last_high_offset = stream->istream.v_offset + + (stream->pos - stream->skip); + if (stream->pos == size) { + /* we've read everything, need to read more */ + i_assert(last_high_offset == input->v_offset + size); + tee_streams_skip(tstream->tee); + ret = i_stream_read(input); + if (ret <= 0) { + size = i_stream_get_data_size(input); + if (ret == -2 && stream->skip != 0) { + /* someone else is holding the data, + wait for it */ + tstream->last_read_waiting = TRUE; + return 0; + } + stream->istream.stream_errno = input->stream_errno; + stream->istream.eof = input->eof; + return ret; + } + tee_streams_update_buffer(tstream->tee); + data = i_stream_get_data(input, &size); + } else { + /* there's still some data available from parent */ + i_assert(last_high_offset < input->v_offset + size); + tee_streams_update_buffer(tstream->tee); + i_assert(stream->pos < size); + } + + i_assert(stream->buffer == data); + ret = size - stream->pos; + i_assert(ret > 0); + stream->pos = size; + + i_assert(stream->istream.v_offset + (stream->pos - stream->skip) == + input->v_offset + size); + return ret; +} + +static int +i_stream_tee_stat(struct istream_private *stream, bool exact) +{ + struct tee_child_istream *tstream = + container_of(stream, struct tee_child_istream, istream); + const struct stat *st; + + if (i_stream_stat(tstream->tee->input, exact, &st) < 0) + return -1; + stream->statbuf = *st; + return 0; +} + +static void i_stream_tee_sync(struct istream_private *stream) +{ + struct tee_child_istream *tstream = + container_of(stream, struct tee_child_istream, istream); + + tee_streams_skip(tstream->tee); + if (i_stream_get_data_size(tstream->tee->input) != 0) { + i_panic("tee-istream: i_stream_sync() called " + "with data still buffered"); + } + i_stream_sync(tstream->tee->input); +} + +struct tee_istream *tee_i_stream_create(struct istream *input) +{ + struct tee_istream *tee; + + tee = i_new(struct tee_istream, 1); + if (input->v_offset == 0) { + i_stream_ref(input); + tee->input = input; + } else { + tee->input = i_stream_create_limit(input, UOFF_T_MAX); + } + return tee; +} + +struct istream *tee_i_stream_create_child(struct tee_istream *tee) +{ + struct tee_child_istream *tstream; + struct istream *ret, *input = tee->input; + + tstream = i_new(struct tee_child_istream, 1); + tstream->tee = tee; + + tstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + tstream->istream.iostream.close = i_stream_tee_close; + tstream->istream.iostream.destroy = i_stream_tee_destroy; + tstream->istream.iostream.set_max_buffer_size = + i_stream_tee_set_max_buffer_size; + + tstream->istream.read = i_stream_tee_read; + tstream->istream.stat = i_stream_tee_stat; + tstream->istream.sync = i_stream_tee_sync; + + tstream->next = tee->children; + tee->children = tstream; + + ret = i_stream_create(&tstream->istream, input, i_stream_get_fd(input), + ISTREAM_CREATE_FLAG_NOOP_SNAPSHOT); + i_stream_set_name(&tstream->istream.istream, i_stream_get_name(input)); + /* we keep the reference in tee stream, no need for extra references */ + i_stream_unref(&input); + return ret; +} + +bool tee_i_stream_child_is_waiting(struct istream *input) +{ + struct tee_child_istream *tstream = + container_of(input->real_stream, + struct tee_child_istream, istream); + + return tstream->last_read_waiting; +} diff --git a/src/lib/istream-tee.h b/src/lib/istream-tee.h new file mode 100644 index 0000000..82c0c93 --- /dev/null +++ b/src/lib/istream-tee.h @@ -0,0 +1,17 @@ +#ifndef ISTREAM_TEE_H +#define ISTREAM_TEE_H + +/* Tee can be used to create multiple child input streams which can access + a single non-blocking input stream in a way that data isn't removed from + memory until all child streams have consumed the input. + + If the stream's buffer gets full because some child isn't consuming the + data, other streams get returned 0 by i_stream_read(). */ +struct tee_istream *tee_i_stream_create(struct istream *input); +/* Returns TRUE if last read() operation returned 0, because it was waiting + for another tee stream to read more of its data. */ +bool tee_i_stream_child_is_waiting(struct istream *input); + +struct istream *tee_i_stream_create_child(struct tee_istream *tee); + +#endif diff --git a/src/lib/istream-timeout.c b/src/lib/istream-timeout.c new file mode 100644 index 0000000..4fcced1 --- /dev/null +++ b/src/lib/istream-timeout.c @@ -0,0 +1,150 @@ +/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "time-util.h" +#include "istream-private.h" +#include "istream-timeout.h" + +struct timeout_istream { + struct istream_private istream; + + struct timeout *to; + struct timeval last_read_timestamp; + time_t created; + + unsigned int timeout_msecs; + bool update_timestamp; +}; + +static void i_stream_timeout_close(struct iostream_private *stream, + bool close_parent) +{ + struct timeout_istream *tstream = + container_of(stream, struct timeout_istream, istream.iostream); + + timeout_remove(&tstream->to); + if (close_parent) + i_stream_close(tstream->istream.parent); +} + +static void i_stream_timeout_switch_ioloop_to(struct istream_private *stream, + struct ioloop *ioloop) +{ + struct timeout_istream *tstream = + container_of(stream, struct timeout_istream, istream); + + if (tstream->to != NULL) + tstream->to = io_loop_move_timeout_to(ioloop, &tstream->to); +} + +static void i_stream_timeout(struct timeout_istream *tstream) +{ + struct iostream_private *iostream = &tstream->istream.iostream; + unsigned int over_msecs; + int diff; + + if (tstream->update_timestamp) { + /* we came here after a long-running code. timeouts are handled + before IOs, so wait for i_stream_read() to be called again + before assuming that we've timed out. */ + return; + } + + timeout_remove(&tstream->to); + + diff = timeval_diff_msecs(&ioloop_timeval, &tstream->last_read_timestamp); + if (diff < (int)tstream->timeout_msecs) { + /* we haven't reached the read timeout yet, update it */ + if (diff < 0) + diff = 0; + tstream->to = timeout_add_to(io_stream_get_ioloop(iostream), + tstream->timeout_msecs - diff, + i_stream_timeout, tstream); + return; + } + over_msecs = diff - tstream->timeout_msecs; + + io_stream_set_error(&tstream->istream.iostream, + "Read timeout in %u.%03u s after %"PRIuUOFF_T" bytes%s", + diff/1000, diff%1000, + tstream->istream.istream.v_offset, + over_msecs < 1000 ? "" : t_strdup_printf( + " (requested timeout in %u ms)", tstream->timeout_msecs)); + tstream->istream.istream.stream_errno = ETIMEDOUT; + + i_stream_set_input_pending(tstream->istream.parent, TRUE); +} + +static void i_stream_timeout_set_pending(struct timeout_istream *tstream) +{ + /* make sure we get called again on the next ioloop run. this updates + the timeout to the timestamp where we actually would have wanted to + start waiting for more data (so if there is long-running code + outside the ioloop it's not counted) */ + tstream->update_timestamp = TRUE; + tstream->last_read_timestamp = ioloop_timeval; + i_stream_set_input_pending(&tstream->istream.istream, TRUE); +} + +static ssize_t +i_stream_timeout_read(struct istream_private *stream) +{ + struct timeout_istream *tstream = + container_of(stream, struct timeout_istream, istream); + struct iostream_private *iostream = &tstream->istream.iostream; + ssize_t ret; + + i_stream_seek(stream->parent, stream->parent_start_offset + + stream->istream.v_offset); + + ret = i_stream_read_copy_from_parent(&stream->istream); + if (ret < 0) { + /* failed */ + if (errno == ECONNRESET || errno == EPIPE) { + int diff = ioloop_time - tstream->created; + + io_stream_set_error(&tstream->istream.iostream, + "%s (opened %d secs ago)", + i_stream_get_error(stream->parent), diff); + } + } else if (tstream->to == NULL && tstream->timeout_msecs > 0) { + /* first read. add the timeout here instead of in init + in case the stream is created long before it's actually + read from. */ + tstream->to = timeout_add_to(io_stream_get_ioloop(iostream), + tstream->timeout_msecs, + i_stream_timeout, tstream); + i_stream_timeout_set_pending(tstream); + } else if (ret > 0 && tstream->to != NULL) { + /* we read something, reset the timeout */ + timeout_reset(tstream->to); + i_stream_timeout_set_pending(tstream); + } else if (tstream->update_timestamp) { + tstream->update_timestamp = FALSE; + tstream->last_read_timestamp = ioloop_timeval; + } + return ret; +} + +struct istream * +i_stream_create_timeout(struct istream *input, unsigned int timeout_msecs) +{ + struct timeout_istream *tstream; + + tstream = i_new(struct timeout_istream, 1); + tstream->timeout_msecs = timeout_msecs; + tstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + tstream->istream.stream_size_passthrough = TRUE; + tstream->created = ioloop_time; + + tstream->istream.read = i_stream_timeout_read; + tstream->istream.switch_ioloop_to = i_stream_timeout_switch_ioloop_to; + tstream->istream.iostream.close = i_stream_timeout_close; + + tstream->istream.istream.readable_fd = input->readable_fd; + tstream->istream.istream.blocking = input->blocking; + tstream->istream.istream.seekable = input->seekable; + return i_stream_create(&tstream->istream, input, + i_stream_get_fd(input), 0); +} diff --git a/src/lib/istream-timeout.h b/src/lib/istream-timeout.h new file mode 100644 index 0000000..a26318a --- /dev/null +++ b/src/lib/istream-timeout.h @@ -0,0 +1,9 @@ +#ifndef ISTREAM_TIMEOUT_H +#define ISTREAM_TIMEOUT_H + +/* Return ETIMEDOUT error if read() doesn't return anything for timeout_msecs. + If timeout_msecs=0, there is no timeout. */ +struct istream * +i_stream_create_timeout(struct istream *input, unsigned int timeout_msecs); + +#endif diff --git a/src/lib/istream-try.c b/src/lib/istream-try.c new file mode 100644 index 0000000..3b83a7f --- /dev/null +++ b/src/lib/istream-try.c @@ -0,0 +1,165 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream-private.h" +#include "istream-try.h" + +struct try_istream { + struct istream_private istream; + + size_t min_buffer_full_size; + unsigned int try_input_count; + struct istream **try_input; + unsigned int try_idx; + + struct istream *final_input; +}; + +static void i_stream_unref_try_inputs(struct try_istream *tstream) +{ + for (unsigned int i = 0; i < tstream->try_input_count; i++) { + if (tstream->try_input[i] != NULL) + i_stream_unref(&tstream->try_input[i]); + } + tstream->try_input_count = 0; + i_free(tstream->try_input); +} + +static void i_stream_try_close(struct iostream_private *stream, + bool close_parent) +{ + struct try_istream *tstream = + container_of(stream, struct try_istream, istream.iostream); + + if (close_parent) { + if (tstream->istream.parent != NULL) + i_stream_close(tstream->istream.parent); + for (unsigned int i = 0; i < tstream->try_input_count; i++) { + if (tstream->try_input[i] != NULL) + i_stream_close(tstream->try_input[i]); + } + } + i_stream_unref_try_inputs(tstream); +} + +static bool +i_stream_try_is_buffer_full(struct try_istream *tstream, + struct istream *try_input) +{ + /* See if one of the parent istreams have their buffer full. + This is mainly intended to check with istream-tee whether its + parent is full. That means that the try_input has already seen + a full buffer of input, but it hasn't decided to return anything + yet. But it also hasn't failed, so we'll assume that the input is + correct for it and it simply needs a lot more input before it can + return anything (e.g. istream-bzlib). + + Note that it's common for buffer_size to be 0 for all parents. This + could be e.g. because the root is istream-concat, which breaks the + parent hierarchy since it has multiple parents. So the buffer_size + check can be thought of just as an optional extra check that + sometimes works and sometimes doesn't. + + Note that we don't check whether skip==pos. An istream could be + reading its buffer full without skipping over anything. */ + while (try_input->real_stream->parent != NULL) { + try_input = try_input->real_stream->parent; + if (try_input->real_stream->pos >= try_input->real_stream->buffer_size && + try_input->real_stream->pos >= tstream->min_buffer_full_size) + return TRUE; + } + return FALSE; +} + +static int i_stream_try_detect(struct try_istream *tstream) +{ + int ret; + + for (; tstream->try_idx < tstream->try_input_count; tstream->try_idx++) { + struct istream *try_input = + tstream->try_input[tstream->try_idx]; + + ret = i_stream_read(try_input); + if (ret == 0 && i_stream_try_is_buffer_full(tstream, try_input)) + ret = 1; + if (ret > 0) { + i_stream_init_parent(&tstream->istream, try_input); + i_stream_unref_try_inputs(tstream); + return 1; + } + if (ret == 0) + return 0; + if (try_input->stream_errno == 0) { + /* empty file */ + tstream->istream.istream.eof = TRUE; + return -1; + } + if (try_input->stream_errno != EINVAL) { + tstream->istream.istream.stream_errno = + try_input->stream_errno; + io_stream_set_error(&tstream->istream.iostream, + "Unexpected error while detecting stream format: %s", + i_stream_get_error(try_input)); + return -1; + } + } + + /* All streams failed with EINVAL. */ + io_stream_set_error(&tstream->istream.iostream, + "Failed to detect stream format"); + tstream->istream.istream.stream_errno = EINVAL; + return -1; +} + +static ssize_t +i_stream_try_read(struct istream_private *stream) +{ + struct try_istream *tstream = + container_of(stream, struct try_istream, istream); + int ret; + + if (stream->parent == NULL) { + if ((ret = i_stream_try_detect(tstream)) <= 0) + return ret; + } + + i_stream_seek(stream->parent, stream->parent_start_offset + + stream->istream.v_offset); + return i_stream_read_copy_from_parent(&stream->istream); +} + +struct istream *istream_try_create(struct istream *const input[], + size_t min_buffer_full_size) +{ + struct try_istream *tstream; + unsigned int count; + size_t max_buffer_size = I_STREAM_MIN_SIZE; + bool blocking = TRUE, seekable = TRUE; + + for (count = 0; input[count] != NULL; count++) { + max_buffer_size = I_MAX(max_buffer_size, + i_stream_get_max_buffer_size(input[count])); + if (!input[count]->blocking) + blocking = FALSE; + if (!input[count]->seekable) + seekable = FALSE; + i_stream_ref(input[count]); + } + i_assert(count != 0); + + tstream = i_new(struct try_istream, 1); + tstream->min_buffer_full_size = min_buffer_full_size; + tstream->try_input_count = count; + tstream->try_input = p_memdup(default_pool, input, + sizeof(*input) * count); + + tstream->istream.iostream.close = i_stream_try_close; + + tstream->istream.max_buffer_size = max_buffer_size; + tstream->istream.read = i_stream_try_read; + + tstream->istream.istream.readable_fd = FALSE; + tstream->istream.istream.blocking = blocking; + tstream->istream.istream.seekable = seekable; + return i_stream_create(&tstream->istream, NULL, -1, 0); +} diff --git a/src/lib/istream-try.h b/src/lib/istream-try.h new file mode 100644 index 0000000..6f0f8b7 --- /dev/null +++ b/src/lib/istream-try.h @@ -0,0 +1,25 @@ +#ifndef ISTREAM_TRY_H +#define ISTREAM_TRY_H + +/* Read from the first input stream that doesn't fail with EINVAL. If any of + the streams fail with non-EINVAL, it's treated as a fatal failure and the + error is immediately returned. If a stream returns 0, more data is waited + for before continuing to the next stream. This allows the last stream to + be a fallback stream that always succeeds. + + Once the stream is detected, all the other streams are unreferenced. + The streams should usually be children of the same parent tee-istream. + + Detecting whether istream-tee buffer is full or not is a bit tricky. + There's no visible difference between non-blocking istream returning 0 and + istream-tee buffer being full. To work around this, we treat used buffer + sizes <= min_buffer_full_size as being non-blocking istreams, while + buffer sizes > min_buffer_full_size are assumed to be due to istream-tee + max buffer size being reached. Practically this means that + min_buffer_full_size must be smaller than the smallest of the istreams' + maximum buffer sizes, but large enough that all the istreams would have + returned EINVAL on invalid input by that position. */ +struct istream *istream_try_create(struct istream *const input[], + size_t min_buffer_full_size); + +#endif diff --git a/src/lib/istream-unix.c b/src/lib/istream-unix.c new file mode 100644 index 0000000..3a8fabd --- /dev/null +++ b/src/lib/istream-unix.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "fdpass.h" +#include "istream-file-private.h" +#include "istream-unix.h" + +struct unix_istream { + struct file_istream fstream; + bool next_read_fd; + int read_fd; +}; + +static void +i_stream_unix_close(struct iostream_private *stream, bool close_parent) +{ + struct unix_istream *ustream = + container_of(stream, struct unix_istream, + fstream.istream.iostream); + + i_close_fd(&ustream->read_fd); + i_stream_file_close(stream, close_parent); +} + +static ssize_t i_stream_unix_read(struct istream_private *stream) +{ + struct unix_istream *ustream = + container_of(stream, struct unix_istream, fstream.istream); + size_t size; + ssize_t ret; + + if (!ustream->next_read_fd) + return i_stream_file_read(stream); + + i_assert(ustream->read_fd == -1); + i_assert(ustream->fstream.skip_left == 0); /* not supported here.. */ + if (!i_stream_try_alloc(stream, 1, &size)) + return -2; + + ret = fd_read(stream->fd, stream->w_buffer + stream->pos, size, + &ustream->read_fd); + if (ustream->read_fd != -1) + ustream->next_read_fd = FALSE; + + if (ret == 0) { + /* EOF */ + stream->istream.eof = TRUE; + ustream->fstream.seen_eof = TRUE; + return -1; + } + + if (unlikely(ret < 0)) { + if ((errno == EINTR || errno == EAGAIN) && + !stream->istream.blocking) { + return 0; + } else { + i_assert(errno != 0); + /* if we get EBADF for a valid fd, it means something's + really wrong and we'd better just crash. */ + i_assert(errno != EBADF); + stream->istream.stream_errno = errno; + return -1; + } + } + stream->pos += ret; + return ret; +} + +struct istream *i_stream_create_unix(int fd, size_t max_buffer_size) +{ + struct unix_istream *ustream; + struct istream *input; + + i_assert(fd != -1); + + ustream = i_new(struct unix_istream, 1); + ustream->read_fd = -1; + input = i_stream_create_file_common(&ustream->fstream, fd, NULL, + max_buffer_size, FALSE); + input->real_stream->iostream.close = i_stream_unix_close; + input->real_stream->read = i_stream_unix_read; + return input; +} + +void i_stream_unix_set_read_fd(struct istream *input) +{ + struct unix_istream *ustream = + container_of(input->real_stream, struct unix_istream, + fstream.istream); + + ustream->next_read_fd = TRUE; +} + +void i_stream_unix_unset_read_fd(struct istream *input) +{ + struct unix_istream *ustream = + container_of(input->real_stream, struct unix_istream, + fstream.istream); + + ustream->next_read_fd = FALSE; +} + +int i_stream_unix_get_read_fd(struct istream *input) +{ + struct unix_istream *ustream = + container_of(input->real_stream, struct unix_istream, + fstream.istream); + int fd; + + fd = ustream->read_fd; + ustream->read_fd = -1; + return fd; +} diff --git a/src/lib/istream-unix.h b/src/lib/istream-unix.h new file mode 100644 index 0000000..0b29ccc --- /dev/null +++ b/src/lib/istream-unix.h @@ -0,0 +1,15 @@ +#ifndef ISTREAM_UNIX_H +#define ISTREAM_UNIX_H + +struct istream *i_stream_create_unix(int fd, size_t max_buffer_size); +/* Start trying to read a file descriptor from the UNIX socket. */ +void i_stream_unix_set_read_fd(struct istream *input); +/* Stop trying to read a file descriptor from the UNIX socket. */ +void i_stream_unix_unset_read_fd(struct istream *input); +/* Returns the fd that the last i_stream_read() received, or -1 if no fd + was received. This function must be called before + i_stream_unix_set_read_fd() is called again after successfully receiving + a file descriptor. */ +int i_stream_unix_get_read_fd(struct istream *input); + +#endif diff --git a/src/lib/istream.c b/src/lib/istream.c new file mode 100644 index 0000000..5fc5112 --- /dev/null +++ b/src/lib/istream.c @@ -0,0 +1,1316 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "array.h" +#include "str.h" +#include "memarea.h" +#include "istream-private.h" + +static bool i_stream_is_buffer_invalid(const struct istream_private *stream); + +void i_stream_set_name(struct istream *stream, const char *name) +{ + i_free(stream->real_stream->iostream.name); + stream->real_stream->iostream.name = i_strdup(name); +} + +const char *i_stream_get_name(struct istream *stream) +{ + while (stream->real_stream->iostream.name == NULL) { + stream = stream->real_stream->parent; + if (stream == NULL) + return ""; + } + return stream->real_stream->iostream.name; +} + +static void i_stream_close_full(struct istream *stream, bool close_parents) +{ + io_stream_close(&stream->real_stream->iostream, close_parents); + stream->closed = TRUE; + + if (stream->stream_errno == 0) + stream->stream_errno = EPIPE; +} + +void i_stream_destroy(struct istream **stream) +{ + if (*stream == NULL) + return; + + i_stream_close_full(*stream, FALSE); + i_stream_unref(stream); +} + +void i_stream_ref(struct istream *stream) +{ + io_stream_ref(&stream->real_stream->iostream); +} + +void i_stream_unref(struct istream **stream) +{ + struct istream_private *_stream; + + if (*stream == NULL) + return; + + _stream = (*stream)->real_stream; + + if (_stream->iostream.refcount > 1) { + if (!io_stream_unref(&_stream->iostream)) + i_unreached(); + } else { + /* The snapshot may contain pointers to the parent istreams. + Free it before io_stream_unref() frees the parents. */ + i_stream_snapshot_free(&_stream->prev_snapshot); + + if (io_stream_unref(&_stream->iostream)) + i_unreached(); + str_free(&_stream->line_str); + i_stream_unref(&_stream->parent); + io_stream_free(&_stream->iostream); + } + *stream = NULL; +} + +#undef i_stream_add_destroy_callback +void i_stream_add_destroy_callback(struct istream *stream, + istream_callback_t *callback, void *context) +{ + io_stream_add_destroy_callback(&stream->real_stream->iostream, + callback, context); +} + +void i_stream_remove_destroy_callback(struct istream *stream, + void (*callback)()) +{ + io_stream_remove_destroy_callback(&stream->real_stream->iostream, + callback); +} + +int i_stream_get_fd(struct istream *stream) +{ + struct istream_private *_stream = stream->real_stream; + + return _stream->fd; +} + +void i_stream_copy_fd(struct istream *dest, struct istream *source) +{ + int fd = i_stream_get_fd(source); + + i_assert(fd != -1); + i_assert(dest->real_stream->fd == -1); + dest->real_stream->fd = fd; + dest->readable_fd = source->readable_fd; +} + +const char *i_stream_get_error(struct istream *stream) +{ + struct istream *s; + + /* we'll only return errors for streams that have stream_errno set or + that have reached EOF. we might be returning unintended error + otherwise. */ + if (stream->stream_errno == 0) + return stream->eof ? "EOF" : "<no error>"; + + for (s = stream; s != NULL; s = s->real_stream->parent) { + if (s->stream_errno == 0) + break; + if (s->real_stream->iostream.error != NULL) + return s->real_stream->iostream.error; + } + return strerror(stream->stream_errno); +} + +const char *i_stream_get_disconnect_reason(struct istream *stream) +{ + return io_stream_get_disconnect_reason(stream, NULL); +} + +void i_stream_close(struct istream *stream) +{ + if (stream != NULL) + i_stream_close_full(stream, TRUE); +} + +void i_stream_set_init_buffer_size(struct istream *stream, size_t size) +{ + stream->real_stream->init_buffer_size = size; +} + +void i_stream_set_max_buffer_size(struct istream *stream, size_t max_size) +{ + io_stream_set_max_buffer_size(&stream->real_stream->iostream, max_size); +} + +size_t i_stream_get_max_buffer_size(struct istream *stream) +{ + size_t max_size = 0; + + do { + if (max_size < stream->real_stream->max_buffer_size) + max_size = stream->real_stream->max_buffer_size; + stream = stream->real_stream->parent; + } while (stream != NULL); + return max_size; +} + +void i_stream_set_return_partial_line(struct istream *stream, bool set) +{ + stream->real_stream->return_nolf_line = set; +} + +void i_stream_set_persistent_buffers(struct istream *stream, bool set) +{ + do { + stream->real_stream->nonpersistent_buffers = !set; + stream = stream->real_stream->parent; + } while (stream != NULL); +} + +void i_stream_set_blocking(struct istream *stream, bool blocking) +{ + int prev_fd = -1; + + do { + stream->blocking = blocking; + if (stream->real_stream->fd != -1 && + stream->real_stream->fd != prev_fd) { + fd_set_nonblock(stream->real_stream->fd, !blocking); + prev_fd = stream->real_stream->fd; + } + stream = stream->real_stream->parent; + } while (stream != NULL); +} + +static void i_stream_update(struct istream_private *stream) +{ + if (stream->parent == NULL) + stream->access_counter++; + else { + stream->access_counter = + stream->parent->real_stream->access_counter; + stream->parent_expected_offset = stream->parent->v_offset; + } +} + +static bool snapshot_has_memarea(struct istream_snapshot *snapshot, + struct memarea *memarea) +{ + if (snapshot->old_memarea == memarea) + return TRUE; + if (snapshot->prev_snapshot != NULL) + return snapshot_has_memarea(snapshot->prev_snapshot, memarea); + return FALSE; +} + +struct istream_snapshot * +i_stream_default_snapshot(struct istream_private *stream, + struct istream_snapshot *prev_snapshot) +{ + struct istream_snapshot *snapshot; + + if (stream->memarea != NULL) { + if (prev_snapshot != NULL) { + if (snapshot_has_memarea(prev_snapshot, stream->memarea)) + return prev_snapshot; + } + /* This stream has a memarea. Reference it, so we can later on + rollback if needed. */ + snapshot = i_new(struct istream_snapshot, 1); + snapshot->old_memarea = stream->memarea; + snapshot->prev_snapshot = prev_snapshot; + memarea_ref(snapshot->old_memarea); + return snapshot; + } + if (stream->parent == NULL) { + if (stream->nonpersistent_buffers) { + /* Assume that memarea would be used normally, but + now it's NULL because the buffer is empty and + empty buffers are freed. */ + i_assert(stream->skip == stream->pos); + return prev_snapshot; + } + i_panic("%s is missing istream.snapshot() implementation", + i_stream_get_name(&stream->istream)); + } + struct istream_private *_parent_stream = + stream->parent->real_stream; + return _parent_stream->snapshot(_parent_stream, prev_snapshot); +} + +void i_stream_snapshot_free(struct istream_snapshot **_snapshot) +{ + struct istream_snapshot *snapshot = *_snapshot; + + if (*_snapshot == NULL) + return; + *_snapshot = NULL; + + i_stream_snapshot_free(&snapshot->prev_snapshot); + if (snapshot->free != NULL) + snapshot->free(snapshot); + else { + if (snapshot->old_memarea != NULL) + memarea_unref(&snapshot->old_memarea); + i_stream_unref(&snapshot->istream); + i_free(snapshot); + } +} + +static struct istream_snapshot * +i_stream_noop_snapshot(struct istream_private *stream ATTR_UNUSED, + struct istream_snapshot *prev_snapshot) +{ + return prev_snapshot; +} + +ssize_t i_stream_read(struct istream *stream) +{ + struct istream_private *_stream = stream->real_stream; + ssize_t ret; +#ifdef DEBUG + unsigned char prev_buf[4]; + const unsigned char *prev_data = _stream->buffer; + size_t prev_skip = _stream->skip, prev_pos = _stream->pos; + bool invalid = i_stream_is_buffer_invalid(_stream); + + i_assert(prev_skip <= prev_pos); + if (invalid) + ; + else if (prev_pos - prev_skip <= 4) + memcpy(prev_buf, prev_data + prev_skip, prev_pos - prev_skip); + else { + memcpy(prev_buf, prev_data + prev_skip, 2); + memcpy(prev_buf+2, prev_data + prev_pos - 2, 2); + } +#endif + + if (_stream->skip != _stream->pos || _stream->prev_snapshot != NULL) { + _stream->prev_snapshot = + _stream->snapshot(_stream, _stream->prev_snapshot); + } + ret = i_stream_read_memarea(stream); + if (ret > 0) + i_stream_snapshot_free(&_stream->prev_snapshot); +#ifdef DEBUG + else if (!invalid) { + i_assert((_stream->pos - _stream->skip) == (prev_pos - prev_skip) || + prev_pos == prev_skip); + if (prev_pos - prev_skip <= 4) + i_assert(memcmp(prev_buf, prev_data + prev_skip, prev_pos - prev_skip) == 0); + else { + i_assert(memcmp(prev_buf, prev_data + prev_skip, 2) == 0); + i_assert(memcmp(prev_buf+2, prev_data + prev_pos - 2, 2) == 0); + } + } +#endif + return ret; +} + +ssize_t i_stream_read_memarea(struct istream *stream) +{ + struct istream_private *_stream = stream->real_stream; + size_t old_size; + ssize_t ret; + + if (unlikely(stream->closed || stream->stream_errno != 0)) { + stream->eof = TRUE; + errno = stream->stream_errno; + return -1; + } + + stream->eof = FALSE; + + if (_stream->parent != NULL) + i_stream_seek(_stream->parent, _stream->parent_expected_offset); + + old_size = _stream->pos - _stream->skip; + if (_stream->pos < _stream->high_pos) { + /* we're here because we seeked back within the read buffer. */ + ret = _stream->high_pos - _stream->pos; + _stream->pos = _stream->high_pos; + _stream->high_pos = 0; + } else { + _stream->high_pos = 0; + ret = _stream->read(_stream); + } + i_assert(old_size <= _stream->pos - _stream->skip); + switch (ret) { + case -2: + i_assert(_stream->skip != _stream->pos); + break; + case -1: + if (stream->stream_errno != 0) { + /* error handling should be easier if we now just + assume the stream is now at EOF */ + stream->eof = TRUE; + errno = stream->stream_errno; + } else { + i_assert(stream->eof); + i_assert(old_size == _stream->pos - _stream->skip); + } + break; + case 0: + i_assert(!stream->blocking); + break; + default: + i_assert(ret > 0); + i_assert(_stream->skip < _stream->pos); + i_assert((size_t)ret+old_size == _stream->pos - _stream->skip); + _stream->last_read_timeval = ioloop_timeval; + break; + } + + if (stream->stream_errno != 0) { + /* error handling should be easier if we now just + assume the stream is now at EOF. Note that we could get here + even if read() didn't return -1, although that's a little + bit sloppy istream implementation. */ + stream->eof = TRUE; + } + + i_stream_update(_stream); + /* verify that parents' access_counters are valid. the parent's + i_stream_read() should guarantee this. */ + i_assert(!i_stream_is_buffer_invalid(_stream)); + return ret; +} + +int i_stream_read_more_memarea(struct istream *stream, + const unsigned char **data_r, size_t *size_r) +{ + *data_r = i_stream_get_data(stream, size_r); + if (*size_r > 0) + return 1; + + int ret = i_stream_read_memarea(stream); + *data_r = i_stream_get_data(stream, size_r); + return ret; +} + +void i_stream_get_last_read_time(struct istream *stream, struct timeval *tv_r) +{ + *tv_r = stream->real_stream->last_read_timeval; +} + +ssize_t i_stream_read_copy_from_parent(struct istream *istream) +{ + struct istream_private *stream = istream->real_stream; + size_t pos; + ssize_t ret; + + stream->pos -= stream->skip; + stream->skip = 0; + + stream->buffer = i_stream_get_data(stream->parent, &pos); + if (pos > stream->pos) + ret = 0; + else do { + ret = i_stream_read_memarea(stream->parent); + stream->istream.stream_errno = stream->parent->stream_errno; + stream->istream.eof = stream->parent->eof; + stream->buffer = i_stream_get_data(stream->parent, &pos); + /* check again, in case the parent stream had been seeked + backwards and the previous read() didn't get us far + enough. */ + } while (pos <= stream->pos && ret > 0); + if (ret == -2) { + i_stream_update(stream); + return -2; + } + + ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : + (ret == 0 ? 0 : -1); + stream->pos = pos; + i_assert(ret != -1 || stream->istream.eof || + stream->istream.stream_errno != 0); + i_stream_update(stream); + return ret; +} + +void i_stream_free_buffer(struct istream_private *stream) +{ + if (stream->memarea != NULL) { + memarea_unref(&stream->memarea); + stream->w_buffer = NULL; + } else if (stream->w_buffer != NULL) { + i_free_and_null(stream->w_buffer); + } else { + /* don't know how to free it */ + return; + } + stream->buffer_size = 0; +} + +void i_stream_skip(struct istream *stream, uoff_t count) +{ + struct istream_private *_stream = stream->real_stream; + size_t data_size; + + data_size = _stream->pos - _stream->skip; + if (count <= data_size) { + /* within buffer */ + stream->v_offset += count; + _stream->skip += count; + if (_stream->nonpersistent_buffers && + _stream->skip == _stream->pos) { + _stream->skip = _stream->pos = 0; + i_stream_free_buffer(_stream); + } + return; + } + + /* have to seek forward */ + count -= data_size; + _stream->skip = _stream->pos; + stream->v_offset += data_size; + + if (unlikely(stream->closed || stream->stream_errno != 0)) + return; + + _stream->seek(_stream, stream->v_offset + count, FALSE); +} + +static bool i_stream_can_optimize_seek(struct istream_private *stream) +{ + if (stream->parent == NULL) + return TRUE; + + /* use the fast route only if the parent stream hasn't been changed */ + if (stream->access_counter != + stream->parent->real_stream->access_counter) + return FALSE; + + return i_stream_can_optimize_seek(stream->parent->real_stream); +} + +void i_stream_seek(struct istream *stream, uoff_t v_offset) +{ + struct istream_private *_stream = stream->real_stream; + + if (v_offset >= stream->v_offset && + i_stream_can_optimize_seek(_stream)) + i_stream_skip(stream, v_offset - stream->v_offset); + else { + if (unlikely(stream->closed || stream->stream_errno != 0)) { + stream->eof = TRUE; + return; + } + stream->eof = FALSE; + _stream->seek(_stream, v_offset, FALSE); + } + i_stream_update(_stream); +} + +void i_stream_seek_mark(struct istream *stream, uoff_t v_offset) +{ + struct istream_private *_stream = stream->real_stream; + + if (unlikely(stream->closed || stream->stream_errno != 0)) + return; + + stream->eof = FALSE; + _stream->seek(_stream, v_offset, TRUE); + i_stream_update(_stream); +} + +void i_stream_sync(struct istream *stream) +{ + struct istream_private *_stream = stream->real_stream; + + if (unlikely(stream->closed || stream->stream_errno != 0)) + return; + + if (_stream->sync != NULL) { + _stream->sync(_stream); + i_stream_update(_stream); + } +} + +int i_stream_stat(struct istream *stream, bool exact, const struct stat **st_r) +{ + struct istream_private *_stream = stream->real_stream; + + if (unlikely(stream->closed || stream->stream_errno != 0)) + return -1; + + if (_stream->stat(_stream, exact) < 0) { + stream->eof = TRUE; + return -1; + } + *st_r = &_stream->statbuf; + return 0; +} + +int i_stream_get_size(struct istream *stream, bool exact, uoff_t *size_r) +{ + struct istream_private *_stream = stream->real_stream; + + if (unlikely(stream->closed || stream->stream_errno != 0)) + return -1; + + int ret; + if ((ret = _stream->get_size(_stream, exact, size_r)) < 0) + stream->eof = TRUE; + return ret; +} + +bool i_stream_have_bytes_left(struct istream *stream) +{ + return i_stream_get_data_size(stream) > 0 || !stream->eof; +} + +bool i_stream_read_eof(struct istream *stream) +{ + if (i_stream_get_data_size(stream) == 0) + (void)i_stream_read(stream); + return !i_stream_have_bytes_left(stream); +} + +uoff_t i_stream_get_absolute_offset(struct istream *stream) +{ + uoff_t abs_offset = stream->v_offset; + while (stream != NULL) { + abs_offset += stream->real_stream->start_offset; + stream = stream->real_stream->parent; + } + return abs_offset; +} + +static char *i_stream_next_line_finish(struct istream_private *stream, size_t i) +{ + char *ret; + size_t end; + + if (i > stream->skip && stream->buffer[i-1] == '\r') { + end = i - 1; + stream->line_crlf = TRUE; + } else { + end = i; + stream->line_crlf = FALSE; + } + + if (stream->buffer == stream->w_buffer && + end < stream->buffer_size) { + /* modify the buffer directly */ + stream->w_buffer[end] = '\0'; + ret = (char *)stream->w_buffer + stream->skip; + } else { + /* use a temporary string to return it */ + if (stream->line_str == NULL) + stream->line_str = str_new(default_pool, 256); + str_truncate(stream->line_str, 0); + if (stream->skip < end) + str_append_data(stream->line_str, stream->buffer + stream->skip, + end - stream->skip); + ret = str_c_modifiable(stream->line_str); + } + + if (i < stream->pos) + i++; + stream->istream.v_offset += i - stream->skip; + stream->skip = i; + return ret; +} + +static char *i_stream_last_line(struct istream_private *_stream) +{ + if (_stream->istream.eof && _stream->skip != _stream->pos && + _stream->return_nolf_line) { + /* the last line is missing LF and we want to return it. */ + return i_stream_next_line_finish(_stream, _stream->pos); + } + return NULL; +} + +char *i_stream_next_line(struct istream *stream) +{ + struct istream_private *_stream = stream->real_stream; + const unsigned char *pos; + + if (_stream->skip >= _stream->pos) + return NULL; + + pos = memchr(_stream->buffer + _stream->skip, '\n', + _stream->pos - _stream->skip); + if (pos != NULL) { + return i_stream_next_line_finish(_stream, + pos - _stream->buffer); + } else { + return i_stream_last_line(_stream); + } +} + +char *i_stream_read_next_line(struct istream *stream) +{ + char *line; + + for (;;) { + line = i_stream_next_line(stream); + if (line != NULL) + break; + + switch (i_stream_read(stream)) { + case -2: + io_stream_set_error(&stream->real_stream->iostream, + "Line is too long (over %zu" + " bytes at offset %"PRIuUOFF_T")", + i_stream_get_data_size(stream), stream->v_offset); + stream->stream_errno = errno = ENOBUFS; + stream->eof = TRUE; + return NULL; + case -1: + return i_stream_last_line(stream->real_stream); + case 0: + return NULL; + } + } + return line; +} + +bool i_stream_last_line_crlf(struct istream *stream) +{ + return stream->real_stream->line_crlf; +} + +static bool i_stream_is_buffer_invalid(const struct istream_private *stream) +{ + if (stream->parent == NULL) { + /* the buffer can't point to parent, because it doesn't exist */ + return FALSE; + } + if (stream->w_buffer != NULL) { + /* we can pretty safely assume that the stream is using its + own private buffer, so it can never become invalid. */ + return FALSE; + } + if (stream->access_counter != + stream->parent->real_stream->access_counter) { + /* parent has been modified behind this stream, we can't trust + that our buffer is valid */ + return TRUE; + } + return i_stream_is_buffer_invalid(stream->parent->real_stream); +} + +const unsigned char * +i_stream_get_data(struct istream *stream, size_t *size_r) +{ + struct istream_private *_stream = stream->real_stream; + + if (_stream->skip >= _stream->pos) { + *size_r = 0; + return uchar_empty_ptr; + } + + if (unlikely(i_stream_is_buffer_invalid(_stream))) { + /* This stream may be using parent's buffer directly as + _stream->buffer, but the parent stream has already been + modified indirectly. This means that the buffer might no + longer point to where we assume it points to. So we'll + just return the stream as empty until it's read again. + + It's a bit ugly to suddenly drop data from the stream that + was already read, but since this happens only with shared + parent istreams the caller is hopefully aware enough that + something like this might happen. The other solutions would + be to a) try to automatically read the data back (but we + can't handle errors..) or b) always copy data to stream's + own buffer instead of pointing to parent's buffer (but this + causes data copying that is nearly always unnecessary). */ + *size_r = 0; + /* if we had already read until EOF, mark the stream again as + not being at the end of file. */ + if (stream->stream_errno == 0) { + _stream->skip = _stream->pos = 0; + stream->eof = FALSE; + } + return uchar_empty_ptr; + } + + *size_r = _stream->pos - _stream->skip; + return _stream->buffer + _stream->skip; +} + +size_t i_stream_get_data_size(struct istream *stream) +{ + size_t size; + + (void)i_stream_get_data(stream, &size); + return size; +} + +unsigned char *i_stream_get_modifiable_data(struct istream *stream, + size_t *size_r) +{ + struct istream_private *_stream = stream->real_stream; + + if (_stream->skip >= _stream->pos || _stream->w_buffer == NULL) { + *size_r = 0; + return NULL; + } + + *size_r = _stream->pos - _stream->skip; + return _stream->w_buffer + _stream->skip; +} + +int i_stream_read_data(struct istream *stream, const unsigned char **data_r, + size_t *size_r, size_t threshold) +{ + ssize_t ret = 0; + bool read_more = FALSE; + + do { + *data_r = i_stream_get_data(stream, size_r); + if (*size_r > threshold) + return 1; + + /* we need more data */ + ret = i_stream_read(stream); + if (ret > 0) + read_more = TRUE; + } while (ret > 0); + + *data_r = i_stream_get_data(stream, size_r); + if (ret == -2) + return -2; + + if (ret == 0) { + /* need to read more */ + i_assert(!stream->blocking); + return 0; + } + if (stream->eof) { + if (read_more) { + /* we read at least some new data */ + return 0; + } + } else { + i_assert(stream->stream_errno != 0); + } + return -1; +} + +int i_stream_read_limited(struct istream *stream, const unsigned char **data_r, + size_t *size_r, size_t limit) +{ + struct istream_private *_stream = stream->real_stream; + int ret; + + *data_r = i_stream_get_data(stream, size_r); + if (*size_r >= limit) { + *size_r = limit; + return 1; + } + + _stream->data_limit = limit; + ret = i_stream_read_more(stream, data_r, size_r); + _stream->data_limit = 0; + + if (*size_r >= limit) + *size_r = limit; + return ret; +} + +void i_stream_compress(struct istream_private *stream) +{ + i_assert(stream->memarea == NULL || + memarea_get_refcount(stream->memarea) == 1); + + if (stream->skip != stream->pos) { + memmove(stream->w_buffer, stream->w_buffer + stream->skip, + stream->pos - stream->skip); + } + stream->pos -= stream->skip; + + stream->skip = 0; +} + +static void i_stream_w_buffer_free(void *buf) +{ + i_free(buf); +} + +static void +i_stream_w_buffer_realloc(struct istream_private *stream, size_t old_size) +{ + void *new_buffer; + + if (stream->memarea != NULL && + memarea_get_refcount(stream->memarea) == 1) { + /* Nobody else is referencing the memarea. + We can just reallocate it. */ + memarea_free_without_callback(&stream->memarea); + new_buffer = i_realloc(stream->w_buffer, old_size, + stream->buffer_size); + } else { + new_buffer = i_malloc(stream->buffer_size); + if (old_size > 0) { + i_assert(stream->w_buffer != NULL); + memcpy(new_buffer, stream->w_buffer, old_size); + } + if (stream->memarea != NULL) + memarea_unref(&stream->memarea); + } + + stream->w_buffer = new_buffer; + stream->buffer = new_buffer; + + stream->memarea = memarea_init(stream->w_buffer, stream->buffer_size, + i_stream_w_buffer_free, new_buffer); +} + +void i_stream_grow_buffer(struct istream_private *stream, size_t bytes) +{ + size_t old_size, max_size; + + old_size = stream->buffer_size; + + stream->buffer_size = stream->pos + bytes; + if (stream->buffer_size <= stream->init_buffer_size) + stream->buffer_size = stream->init_buffer_size; + else + stream->buffer_size = nearest_power(stream->buffer_size); + + max_size = i_stream_get_max_buffer_size(&stream->istream); + i_assert(max_size > 0); + if (stream->buffer_size > max_size) + stream->buffer_size = max_size; + + if (stream->buffer_size <= old_size) + stream->buffer_size = old_size; + else + i_stream_w_buffer_realloc(stream, old_size); +} + +bool i_stream_try_alloc(struct istream_private *stream, + size_t wanted_size, size_t *size_r) +{ + i_assert(wanted_size > 0); + i_assert(stream->buffer_size >= stream->pos); + + if (wanted_size > stream->buffer_size - stream->pos) { + if (stream->skip > 0) { + /* remove the unused bytes from beginning of buffer */ + if (stream->memarea != NULL && + memarea_get_refcount(stream->memarea) > 1) { + /* The memarea is still referenced. We can't + overwrite data until extra references are + gone. */ + i_stream_w_buffer_realloc(stream, stream->buffer_size); + } + i_stream_compress(stream); + } else if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream)) { + /* buffer is full - grow it */ + i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE); + } + } + + if (stream->data_limit == 0 || + (stream->buffer_size - stream->skip) < stream->data_limit) + *size_r = stream->buffer_size - stream->pos; + else { + size_t buffered = (stream->pos - stream->skip); + + if (buffered >= stream->data_limit) + *size_r = 0; + else + *size_r = stream->data_limit - buffered; + } + i_assert(stream->w_buffer != NULL || *size_r == 0); + return *size_r > 0; +} + +bool ATTR_NOWARN_UNUSED_RESULT +i_stream_try_alloc_avoid_compress(struct istream_private *stream, + size_t wanted_size, size_t *size_r) +{ + size_t old_skip = stream->skip; + + /* try first with skip=0, so no compression is done */ + stream->skip = 0; + bool ret = i_stream_try_alloc(stream, wanted_size, size_r); + stream->skip = old_skip; + if (ret || old_skip == 0) + return ret; + /* it's full. try with compression. */ + return i_stream_try_alloc(stream, wanted_size, size_r); +} + +void *i_stream_alloc(struct istream_private *stream, size_t size) +{ + size_t old_size, avail_size; + + (void)i_stream_try_alloc(stream, size, &avail_size); + if (avail_size < size) { + old_size = stream->buffer_size; + stream->buffer_size = nearest_power(stream->pos + size); + i_stream_w_buffer_realloc(stream, old_size); + + (void)i_stream_try_alloc(stream, size, &avail_size); + i_assert(avail_size >= size); + } + return stream->w_buffer + stream->pos; +} + +void i_stream_memarea_detach(struct istream_private *stream) +{ + if (stream->memarea != NULL) { + /* Don't overwrite data in a snapshot. Allocate a new + buffer instead. */ + memarea_unref(&stream->memarea); + stream->buffer_size = 0; + stream->buffer = NULL; + stream->w_buffer = NULL; + } +} + +bool i_stream_add_data(struct istream *_stream, const unsigned char *data, + size_t size) +{ + struct istream_private *stream = _stream->real_stream; + size_t size2; + + (void)i_stream_try_alloc(stream, size, &size2); + if (size > size2) + return FALSE; + + memcpy(stream->w_buffer + stream->pos, data, size); + stream->pos += size; + return TRUE; +} + +struct istream *i_stream_get_root_io(struct istream *stream) +{ + while (stream->real_stream->parent != NULL) { + i_assert(stream->real_stream->io == NULL); + stream = stream->real_stream->parent; + } + return stream; +} + +void i_stream_set_input_pending(struct istream *stream, bool pending) +{ + if (!pending) + return; + + stream = i_stream_get_root_io(stream); + if (stream->real_stream->io != NULL) + io_set_pending(stream->real_stream->io); + else + stream->real_stream->io_pending = TRUE; +} + +void i_stream_switch_ioloop_to(struct istream *stream, struct ioloop *ioloop) +{ + io_stream_switch_ioloop_to(&stream->real_stream->iostream, ioloop); + + do { + if (stream->real_stream->switch_ioloop_to != NULL) { + stream->real_stream->switch_ioloop_to( + stream->real_stream, ioloop); + } + stream = stream->real_stream->parent; + } while (stream != NULL); +} + +void i_stream_switch_ioloop(struct istream *stream) +{ + i_stream_switch_ioloop_to(stream, current_ioloop); +} + +void i_stream_set_io(struct istream *stream, struct io *io) +{ + stream = i_stream_get_root_io(stream); + + i_assert(stream->real_stream->io == NULL); + stream->real_stream->io = io; + if (stream->real_stream->io_pending) { + io_set_pending(io); + stream->real_stream->io_pending = FALSE; + } +} + +void i_stream_unset_io(struct istream *stream, struct io *io) +{ + stream = i_stream_get_root_io(stream); + + i_assert(stream->real_stream->io == io); + if (io_is_pending(io)) + stream->real_stream->io_pending = TRUE; + stream->real_stream->io = NULL; +} + +static void +i_stream_default_set_max_buffer_size(struct iostream_private *stream, + size_t max_size) +{ + struct istream_private *_stream = + container_of(stream, struct istream_private, iostream); + + _stream->max_buffer_size = max_size; + if (_stream->parent != NULL) + i_stream_set_max_buffer_size(_stream->parent, max_size); +} + +static void i_stream_default_close(struct iostream_private *stream, + bool close_parent) +{ + struct istream_private *_stream = + container_of(stream, struct istream_private, iostream); + + if (close_parent) + i_stream_close(_stream->parent); +} + +static void i_stream_default_destroy(struct iostream_private *stream) +{ + struct istream_private *_stream = + container_of(stream, struct istream_private, iostream); + + i_stream_free_buffer(_stream); + i_stream_unref(&_stream->parent); +} + +static void +i_stream_default_seek_seekable(struct istream_private *stream, + uoff_t v_offset, bool mark ATTR_UNUSED) +{ + stream->istream.v_offset = v_offset; + stream->skip = stream->pos = 0; +} + +void i_stream_default_seek_nonseekable(struct istream_private *stream, + uoff_t v_offset, bool mark ATTR_UNUSED) +{ + size_t available; + + if (stream->istream.v_offset > v_offset) + i_panic("stream %s doesn't support seeking backwards", + i_stream_get_name(&stream->istream)); + + while (stream->istream.v_offset < v_offset) { + (void)i_stream_read(&stream->istream); + + available = stream->pos - stream->skip; + if (available == 0) { + if (stream->istream.stream_errno != 0) { + /* read failed */ + return; + } + io_stream_set_error(&stream->iostream, + "Can't seek to offset %"PRIuUOFF_T + ", because we have data only up to offset %" + PRIuUOFF_T" (eof=%d)", v_offset, + stream->istream.v_offset, stream->istream.eof ? 1 : 0); + stream->istream.stream_errno = ESPIPE; + return; + } + if (available <= v_offset - stream->istream.v_offset) + i_stream_skip(&stream->istream, available); + else { + i_stream_skip(&stream->istream, + v_offset - stream->istream.v_offset); + } + } +} + +bool i_stream_nonseekable_try_seek(struct istream_private *stream, + uoff_t v_offset) +{ + uoff_t start_offset = stream->istream.v_offset - stream->skip; + + if (v_offset < start_offset) { + /* have to seek backwards */ + i_stream_seek(stream->parent, stream->parent_start_offset); + stream->parent_expected_offset = stream->parent_start_offset; + stream->skip = stream->pos = 0; + stream->istream.v_offset = 0; + stream->high_pos = 0; + return FALSE; + } + + if (v_offset <= start_offset + stream->pos) { + /* seeking backwards within what's already cached */ + stream->skip = v_offset - start_offset; + stream->istream.v_offset = v_offset; + if (stream->high_pos == 0) + stream->high_pos = stream->pos; + stream->pos = stream->skip; + } else { + /* read forward */ + i_stream_default_seek_nonseekable(stream, v_offset, FALSE); + } + return TRUE; +} + +static int +seekable_i_stream_get_size(struct istream_private *stream) +{ + if (stream->cached_stream_size == UOFF_T_MAX) { + uoff_t old_offset = stream->istream.v_offset; + ssize_t ret; + + do { + i_stream_skip(&stream->istream, + i_stream_get_data_size(&stream->istream)); + } while ((ret = i_stream_read(&stream->istream)) > 0); + i_assert(ret == -1); + if (stream->istream.stream_errno != 0) + return -1; + + stream->cached_stream_size = stream->istream.v_offset; + i_stream_seek(&stream->istream, old_offset); + } + stream->statbuf.st_size = stream->cached_stream_size; + return 0; +} + +static int +i_stream_default_stat(struct istream_private *stream, bool exact) +{ + const struct stat *st; + + if (stream->parent == NULL) + return stream->istream.stream_errno == 0 ? 0 : -1; + + if (i_stream_stat(stream->parent, exact, &st) < 0) { + stream->istream.stream_errno = stream->parent->stream_errno; + return -1; + } + stream->statbuf = *st; + if (exact && !stream->stream_size_passthrough) { + /* exact size is not known, even if parent returned something */ + stream->statbuf.st_size = -1; + if (stream->istream.seekable) { + if (seekable_i_stream_get_size(stream) < 0) + return -1; + } + } else { + /* When exact=FALSE always return the parent stat's size, even + if we know the exact value. This is necessary because + otherwise e.g. mbox code can see two different values and + think that the mbox file keeps changing. */ + } + return 0; +} + +static int +i_stream_default_get_size(struct istream_private *stream, + bool exact, uoff_t *size_r) +{ + if (stream->stat(stream, exact) < 0) + return -1; + if (stream->statbuf.st_size == -1) + return 0; + + *size_r = stream->statbuf.st_size; + return 1; +} + +void i_stream_init_parent(struct istream_private *_stream, + struct istream *parent) +{ + _stream->access_counter = parent->real_stream->access_counter; + _stream->parent = parent; + _stream->parent_start_offset = parent->v_offset; + _stream->parent_expected_offset = parent->v_offset; + _stream->start_offset = parent->v_offset; + /* if parent stream is an istream-error, copy the error */ + _stream->istream.stream_errno = parent->stream_errno; + _stream->istream.eof = parent->eof; + i_stream_ref(parent); +} + +struct istream * +i_stream_create(struct istream_private *_stream, struct istream *parent, int fd, + enum istream_create_flag flags) +{ + bool noop_snapshot = (flags & ISTREAM_CREATE_FLAG_NOOP_SNAPSHOT) != 0; + + _stream->fd = fd; + if (parent != NULL) + i_stream_init_parent(_stream, parent); + else if (_stream->memarea == NULL && !noop_snapshot) { + /* The stream has no parent and no memarea yet. We'll assume + that it wants to be using memareas for the reads. */ + _stream->memarea = memarea_init_empty(); + } + _stream->istream.real_stream = _stream; + + if (_stream->iostream.close == NULL) + _stream->iostream.close = i_stream_default_close; + if (_stream->iostream.destroy == NULL) + _stream->iostream.destroy = i_stream_default_destroy; + if (_stream->seek == NULL) { + _stream->seek = _stream->istream.seekable ? + i_stream_default_seek_seekable : + i_stream_default_seek_nonseekable; + } + if (_stream->stat == NULL) + _stream->stat = i_stream_default_stat; + if (_stream->get_size == NULL) + _stream->get_size = i_stream_default_get_size; + if (_stream->snapshot == NULL) { + _stream->snapshot = noop_snapshot ? + i_stream_noop_snapshot : + i_stream_default_snapshot; + } + if (_stream->iostream.set_max_buffer_size == NULL) { + _stream->iostream.set_max_buffer_size = + i_stream_default_set_max_buffer_size; + } + if (_stream->init_buffer_size == 0) + _stream->init_buffer_size = I_STREAM_MIN_SIZE; + + i_zero(&_stream->statbuf); + _stream->statbuf.st_size = -1; + _stream->statbuf.st_atime = + _stream->statbuf.st_mtime = + _stream->statbuf.st_ctime = ioloop_time; + _stream->cached_stream_size = UOFF_T_MAX; + + io_stream_init(&_stream->iostream); + + if (_stream->istream.stream_errno != 0) + _stream->istream.eof = TRUE; + + return &_stream->istream; +} + +struct istream *i_stream_create_error(int stream_errno) +{ + struct istream_private *stream; + + stream = i_new(struct istream_private, 1); + stream->istream.closed = TRUE; + stream->istream.readable_fd = FALSE; + stream->istream.blocking = TRUE; + stream->istream.seekable = TRUE; + stream->istream.eof = TRUE; + stream->istream.stream_errno = stream_errno; + /* Nothing can ever actually be read from this stream, but set a + reasonable max_buffer_size anyway since some filter istreams don't + behave properly otherwise. */ + stream->max_buffer_size = IO_BLOCK_SIZE; + i_stream_create(stream, NULL, -1, 0); + i_stream_set_name(&stream->istream, "(error)"); + return &stream->istream; +} + +struct istream * +i_stream_create_error_str(int stream_errno, const char *fmt, ...) +{ + struct istream *input; + va_list args; + + va_start(args, fmt); + input = i_stream_create_error(stream_errno); + io_stream_set_verror(&input->real_stream->iostream, fmt, args); + va_end(args); + return input; +} diff --git a/src/lib/istream.h b/src/lib/istream.h new file mode 100644 index 0000000..671d3ff --- /dev/null +++ b/src/lib/istream.h @@ -0,0 +1,255 @@ +#ifndef ISTREAM_H +#define ISTREAM_H + +/* Note that some systems (Solaris) may use a macro to redefine struct stat */ +#include <sys/stat.h> + +struct ioloop; + +struct istream { + uoff_t v_offset; + + /* Commonly used errors: + + ENOENT - File/object doesn't exist. + EPIPE - Stream ended unexpectedly (or i_stream_close() was called). + ESPIPE - i_stream_seek() was used on a stream that can't be seeked. + ENOBUFS - i_stream_read_next_line() was used for a too long line. + EIO - Internal error. Retrying may work, but it may also be + because of a misconfiguration. + EINVAL - Stream is corrupted. + + If stream_errno != 0, eof==TRUE as well. + */ + int stream_errno; + + bool mmaped:1; /* be careful when copying data */ + bool blocking:1; /* read() shouldn't return 0 */ + bool closed:1; + bool readable_fd:1; /* fd can be read directly if necessary + (for sendfile()) */ + bool seekable:1; /* we can seek() backwards */ + /* read() has reached to end of file (but there may still be data + available in buffer) or stream_errno != 0 */ + bool eof:1; + + struct istream_private *real_stream; +}; + +typedef void istream_callback_t(void *context); + +struct istream *i_stream_create_fd(int fd, size_t max_buffer_size); +/* The fd is set to -1 immediately to avoid accidentally closing it twice. */ +struct istream *i_stream_create_fd_autoclose(int *fd, size_t max_buffer_size); +/* Open the given path only when something is actually tried to be read from + the stream. */ +struct istream *i_stream_create_file(const char *path, size_t max_buffer_size); +/* Create an input stream using the provided data block. That data block must +remain allocated during the full lifetime of the stream. */ +struct istream *i_stream_create_from_data(const void *data, size_t size); +#define i_stream_create_from_buffer(buf) \ + i_stream_create_from_data((buf)->data, (buf)->used) +#define i_stream_create_from_string(str) \ + i_stream_create_from_data(str_data(str), str_len(str)) +/* Create an input stream using a copy of the provided data block. The + provided data block may be freed at any time. The copy is freed when the + stream is destroyed. */ +struct istream * +i_stream_create_copy_from_data(const void *data, size_t size); +#define i_stream_create_copy_from_buffer(buf) \ + i_stream_create_copy_from_data((buf)->data, (buf)->used) +#define i_stream_create_copy_from_string(str) \ + i_stream_create_copy_from_data(str_data(str), str_len(str)) +struct istream *i_stream_create_limit(struct istream *input, uoff_t v_size); +struct istream *i_stream_create_range(struct istream *input, + uoff_t v_offset, uoff_t v_size); +struct istream *i_stream_create_error(int stream_errno); +struct istream * +i_stream_create_error_str(int stream_errno, const char *fmt, ...) + ATTR_FORMAT(2, 3); + +/* Set name (e.g. path) for input stream. */ +void i_stream_set_name(struct istream *stream, const char *name); +/* Get input stream's name. If stream itself doesn't have a name, + it looks up further into stream's parents until one of them has a name. + Returns "" if stream has no name. */ +const char *i_stream_get_name(struct istream *stream); + +/* Close this stream (but not its parents) and unreference it. */ +void i_stream_destroy(struct istream **stream); + +/* Reference counting. References start from 1, so calling i_stream_unref() + destroys the stream if i_stream_ref() is never used. */ +void i_stream_ref(struct istream *stream); +/* Unreferences the stream and sets stream pointer to NULL. */ +void i_stream_unref(struct istream **stream); +/* Call the given callback function when stream is destroyed. */ +void i_stream_add_destroy_callback(struct istream *stream, + istream_callback_t *callback, void *context) + ATTR_NULL(3); +#define i_stream_add_destroy_callback(stream, callback, context) \ + i_stream_add_destroy_callback(stream - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (istream_callback_t *)callback, context) +/* Remove the destroy callback. */ +void i_stream_remove_destroy_callback(struct istream *stream, + void (*callback)()); + +/* Return file descriptor for stream, or -1 if none is available. */ +int i_stream_get_fd(struct istream *stream); +/* Copy the file descriptor from source istream to destination istream. + The readable_fd is preserved. Assert-crashes if source doesn't have a + file descriptor. */ +void i_stream_copy_fd(struct istream *dest, struct istream *source); +/* Returns error string for the last error. It also returns "EOF" in case there + is no error, but eof is set. Otherwise it returns "<no error>". */ +const char *i_stream_get_error(struct istream *stream); +/* Returns human-readable reason for why istream was disconnected. + The output is either "Connection closed" for clean disconnections or + "Connection closed: <error>" for unclean disconnections. This is an + alternative to i_stream_get_error(), which is preferred to be used when + logging errors about client connections. */ +const char *i_stream_get_disconnect_reason(struct istream *stream); + +/* Mark the stream and all of its parent streams closed. Any reads after this + will return -1. The data already read can still be used. */ +void i_stream_close(struct istream *stream); +/* Sync the stream with the underlying backend, ie. if a file has been + modified, flush any cached data. */ +void i_stream_sync(struct istream *stream); + +/* Change the initial size for stream's input buffer. This basically just + grows the read buffer size from the default. This function has no effect + unless it's called before reading anything. */ +void i_stream_set_init_buffer_size(struct istream *stream, size_t size); +/* Change the maximum size for stream's input buffer to grow. Useful only + for buffered streams (currently only file). This changes also all the + parent streams' max buffer size. */ +void i_stream_set_max_buffer_size(struct istream *stream, size_t max_size); +/* Returns the current max. buffer size for the stream. This function also + goes through all of the parent streams and returns the highest seen max + buffer size. This is needed because some streams (e.g. istream-chain) change + their max buffer size dynamically. */ +size_t i_stream_get_max_buffer_size(struct istream *stream); +/* Enable/disable i_stream[_read]_next_line() returning the last line if it + doesn't end with LF. */ +void i_stream_set_return_partial_line(struct istream *stream, bool set); +/* Change whether buffers are allocated persistently (default=TRUE). When not, + the memory usage is minimized by freeing the stream's buffers whenever they + become empty. */ +void i_stream_set_persistent_buffers(struct istream *stream, bool set); +/* Set the istream blocking or nonblocking, including its parent streams. + If any of the istreams have an fd, its O_NONBLOCK flag is changed. */ +void i_stream_set_blocking(struct istream *stream, bool blocking); + +/* Returns number of bytes read if read was ok, 0 if stream is non-blocking and + no more data is available, -1 if EOF or error, -2 if the input buffer is + full. If <=0 is returned, pointers to existing data returned by the previous + i_stream_get_data() will stay valid, although calling it again may return + a different pointer. The pointers to old data are invalidated again when + return value is >0. */ +ssize_t i_stream_read(struct istream *stream); +/* Skip forward a number of bytes. Never fails, the next read tells if it + was successful. */ +void i_stream_skip(struct istream *stream, uoff_t count); +/* Seek to specified position from beginning of file. Never fails, the next + read tells if it was successful. This works only for files, others will + set stream_errno=ESPIPE. */ +void i_stream_seek(struct istream *stream, uoff_t v_offset); +/* Like i_stream_seek(), but also giving a hint that after reading some data + we could be seeking back to this mark or somewhere after it. If input + stream's implementation is slow in seeking backwards, it can use this hint + to cache some of the data in memory. */ +void i_stream_seek_mark(struct istream *stream, uoff_t v_offset); +/* Returns 0 if ok, -1 if error. As the underlying stream may not be + a file, only some of the fields might be set, others would be zero. + st_size is always set, and if it's not known, it's -1. + + If exact=FALSE, the stream may not return exactly correct values, but the + returned values can be compared to see if anything had changed (eg. in + compressed stream st_size could be compressed size) */ +int i_stream_stat(struct istream *stream, bool exact, const struct stat **st_r); +/* Similar to i_stream_stat() call. Returns 1 if size was successfully + set, 0 if size is unknown, -1 if error. */ +int i_stream_get_size(struct istream *stream, bool exact, uoff_t *size_r); +/* Returns TRUE if there are any bytes left to be read or in buffer. */ +bool i_stream_have_bytes_left(struct istream *stream); +/* Returns TRUE if there are no bytes currently buffered and i_stream_read() + returns EOF/error. Usually it's enough to check for stream->eof instead of + calling this function. Note that if the stream isn't at EOF, this function + has now read data into the stream buffer. */ +bool i_stream_read_eof(struct istream *stream); +/* Returns the absolute offset of the stream. This is the stream's current + v_offset + the parent's absolute offset when the stream was created. */ +uoff_t i_stream_get_absolute_offset(struct istream *stream); + +/* Gets the next line from stream and returns it, or NULL if more data is + needed to make a full line. i_stream_set_return_partial_line() specifies + if the last line should be returned if it doesn't end with LF. */ +char *i_stream_next_line(struct istream *stream); +/* Like i_stream_next_line(), but reads for more data if needed. Returns NULL + if more data is needed or error occurred. If the input buffer gets full, + stream_errno is set to ENOBUFS. */ +char *i_stream_read_next_line(struct istream *stream); +/* Returns TRUE if the last line read with i_stream_next_line() ended with + CRLF (instead of LF). */ +bool i_stream_last_line_crlf(struct istream *stream); + +/* Returns pointer to beginning of read data. */ +const unsigned char *i_stream_get_data(struct istream *stream, size_t *size_r); +size_t i_stream_get_data_size(struct istream *stream); +/* Like i_stream_get_data(), but returns non-const data. This only works with + buffered streams (currently only file), others return NULL. */ +unsigned char *i_stream_get_modifiable_data(struct istream *stream, + size_t *size_r); +/* Like i_stream_get_data(), but read more when needed. Returns 1 if more + than threshold bytes are available, 0 if as much or less, -1 if error or + EOF with no bytes read that weren't already in buffer, or -2 if stream's + input buffer is full. */ +int i_stream_read_data(struct istream *stream, const unsigned char **data_r, + size_t *size_r, size_t threshold); +/* Like i_stream_get_data(), but read more when needed. Returns 1 if at least + the wanted number of bytes are available, 0 if less, -1 if error or + EOF with no bytes read that weren't already in buffer, or -2 if stream's + input buffer is full. */ +static inline int +i_stream_read_bytes(struct istream *stream, const unsigned char **data_r, + size_t *size_r, size_t wanted) +{ + i_assert(wanted > 0); + return i_stream_read_data(stream, data_r, size_r, wanted - 1); +} +/* Short-hand for just requesting more data (i.e. even one byte) */ +static inline int +i_stream_read_more(struct istream *stream, const unsigned char **data_r, + size_t *size_r) +{ + int ret = i_stream_read_bytes(stream, data_r, size_r, 1); + i_assert(ret != -2); /* stream must have space for at least 1 byte */ + return ret; +} +/* Like i_stream_read_more(), but tries to avoid buffering more than the + indicated limit. Use this function to prevent growing the stream buffer + beyond what the application is willing to read immediately. Since this + function doesn't fully prevent buffering beyond the limit, the amount of data + actually buffered can exceed the limit. However, *size_r will allways be <= + limit to avoid confusion. */ +int i_stream_read_limited(struct istream *stream, const unsigned char **data_r, + size_t *size_r, size_t limit); +/* Return the timestamp when istream last successfully read something. + The timestamp is 0 if nothing has ever been read. */ +void i_stream_get_last_read_time(struct istream *stream, struct timeval *tv_r); + +/* Append external data to input stream. Returns TRUE if successful, FALSE if + there is not enough space in the stream. */ +bool i_stream_add_data(struct istream *stream, const unsigned char *data, + size_t size); + +void i_stream_set_input_pending(struct istream *stream, bool pending); + +/* If there are any I/O loop items associated with the stream, move all of + them to provided/current ioloop. */ +void i_stream_switch_ioloop_to(struct istream *stream, struct ioloop *ioloop); +void i_stream_switch_ioloop(struct istream *stream); + +#endif diff --git a/src/lib/json-parser.c b/src/lib/json-parser.c new file mode 100644 index 0000000..a4fb186 --- /dev/null +++ b/src/lib/json-parser.c @@ -0,0 +1,850 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "istream.h" +#include "hex-dec.h" +#include "unichar.h" +#include "istream-jsonstr.h" +#include "json-parser.h" + +enum json_state { + JSON_STATE_ROOT = 0, + JSON_STATE_OBJECT_OPEN, + JSON_STATE_OBJECT_KEY, + JSON_STATE_OBJECT_COLON, + JSON_STATE_OBJECT_VALUE, + JSON_STATE_OBJECT_SKIP_STRING, + JSON_STATE_OBJECT_NEXT, + JSON_STATE_ARRAY_OPEN, + JSON_STATE_ARRAY_VALUE, + JSON_STATE_ARRAY_SKIP_STRING, + JSON_STATE_ARRAY_NEXT, + JSON_STATE_ARRAY_NEXT_SKIP, + JSON_STATE_VALUE, + JSON_STATE_DONE +}; + +struct json_parser { + pool_t pool; + struct istream *input; + uoff_t highwater_offset; + enum json_parser_flags flags; + + const unsigned char *start, *end, *data; + const char *error; + string_t *value; + struct istream *strinput; + + enum json_state state; + ARRAY(enum json_state) nesting; + unsigned int nested_skip_count; + + bool skipping; + bool seen_eof; +}; + +static int json_parser_read_more(struct json_parser *parser) +{ + uoff_t cur_highwater = parser->input->v_offset + + i_stream_get_data_size(parser->input); + size_t size; + ssize_t ret; + + i_assert(parser->highwater_offset <= cur_highwater); + + if (parser->error != NULL) + return -1; + + if (parser->highwater_offset == cur_highwater) { + ret = i_stream_read(parser->input); + if (ret == -2) { + parser->error = "Token too large"; + return -1; + } + if (ret < 0 && !parser->seen_eof && + i_stream_get_data_size(parser->input) > 0 && + parser->input->stream_errno == 0) { + /* call it once more to finish any pending number */ + parser->seen_eof = TRUE; + } else if (ret <= 0) { + return ret; + } else { + cur_highwater = parser->input->v_offset + + i_stream_get_data_size(parser->input); + i_assert(parser->highwater_offset < cur_highwater); + parser->highwater_offset = cur_highwater; + } + } + + parser->start = parser->data = i_stream_get_data(parser->input, &size); + parser->end = parser->start + size; + i_assert(size > 0); + return 1; +} + +static void json_parser_update_input_pos(struct json_parser *parser) +{ + size_t size; + + if (parser->data == parser->start) + return; + + i_stream_skip(parser->input, parser->data - parser->start); + parser->start = parser->data = i_stream_get_data(parser->input, &size); + parser->end = parser->start + size; + if (size > 0) { + /* we skipped over some data and there's still data left. + no need to read() the next time. */ + parser->highwater_offset = 0; + } else { + parser->highwater_offset = parser->input->v_offset; + } +} + +struct json_parser *json_parser_init(struct istream *input) +{ + return json_parser_init_flags(input, 0); +} + +struct json_parser *json_parser_init_flags(struct istream *input, + enum json_parser_flags flags) +{ + struct json_parser *parser; + pool_t pool = pool_alloconly_create("json parser", + sizeof(struct json_parser)+64); + + parser = p_new(pool, struct json_parser, 1); + parser->pool = pool; + parser->input = input; + parser->flags = flags; + parser->value = str_new(default_pool, 128); + i_array_init(&parser->nesting, 8); + i_stream_ref(input); + + if ((flags & JSON_PARSER_NO_ROOT_OBJECT) != 0) + parser->state = JSON_STATE_VALUE; + return parser; +} + +int json_parser_deinit(struct json_parser **_parser, const char **error_r) +{ + struct json_parser *parser = *_parser; + + *_parser = NULL; + + if (parser->error != NULL) { + /* actual parser error */ + *error_r = t_strdup(parser->error); + } else if (parser->input->stream_errno != 0) { + *error_r = t_strdup_printf("read(%s) failed: %s", + i_stream_get_name(parser->input), + i_stream_get_error(parser->input)); + } else if (parser->data == parser->end && + !i_stream_have_bytes_left(parser->input) && + parser->state != JSON_STATE_DONE) { + *error_r = "Missing '}'"; + } else { + *error_r = NULL; + } + + i_stream_unref(&parser->input); + array_free(&parser->nesting); + str_free(&parser->value); + pool_unref(&parser->pool); + return *error_r != NULL ? -1 : 0; +} + +static bool json_parse_whitespace(struct json_parser *parser) +{ + for (; parser->data != parser->end; parser->data++) { + switch (*parser->data) { + case ' ': + case '\t': + case '\r': + case '\n': + break; + default: + json_parser_update_input_pos(parser); + return TRUE; + } + } + json_parser_update_input_pos(parser); + return FALSE; +} + +static int json_skip_string(struct json_parser *parser) +{ + for (; parser->data != parser->end; parser->data++) { + if (*parser->data == '"') { + parser->data++; + json_parser_update_input_pos(parser); + return 1; + } + if (*parser->data == '\\') { + parser->data++; + if (parser->data == parser->end) + break; + switch (*parser->data) { + case '"': + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + break; + case 'u': + if (parser->end - parser->data < 4) { + parser->data = parser->end; + return -1; + } + parser->data += 3; + break; + default: + parser->error = "Invalid escape string"; + return -1; + } + } + } + json_parser_update_input_pos(parser); + return 0; +} + +static int json_parse_unicode_escape(struct json_parser *parser) +{ + char chbuf[5] = {0}; + unichar_t chr, hi_surg; + + parser->data++; + if (parser->end - parser->data < 4) { + /* wait for more data */ + parser->data = parser->end; + return 0; + } + memcpy(chbuf, parser->data, 4); + if (str_to_uint32_hex(chbuf, &chr) < 0) { + parser->error = "Invalid unicode escape seen"; + return -1; + } + if (UTF16_VALID_HIGH_SURROGATE(chr)) { + /* possible surrogate pair */ + hi_surg = chr; + chr = 0; + parser->data += 4; + if (parser->data >= parser->end) { + /* wait for more data */ + parser->data = parser->end; + return 0; + } + if ((parser->end - parser->data) < 2) { + if (parser->data[0] == '\\') { + /* wait for more data */ + parser->data = parser->end; + return 0; + } + /* error */ + } + if ((parser->end - parser->data) < 6) { + if (parser->data[0] == '\\' && + parser->data[1] == 'u') { + /* wait for more data */ + parser->data = parser->end; + return 0; + } + /* error */ + } else { + memcpy(chbuf, &parser->data[2], 4); + if (str_to_uint32_hex(chbuf, &chr) < 0) { + parser->error = "Invalid unicode escape seen"; + return -1; + } + } + if (parser->data[0] != '\\' || parser->data[1] != 'u' || + !UTF16_VALID_LOW_SURROGATE(chr)) { + parser->error = p_strdup_printf(parser->pool, + "High surrogate 0x%04x seen, " + "but not followed by low surrogate", hi_surg); + return -1; + } + chr = uni_join_surrogate(hi_surg, chr); + parser->data += 2; + } + + if (!uni_is_valid_ucs4(chr)) { + parser->error = p_strdup_printf(parser->pool, + "Invalid unicode character U+%04x", chr); + return -1; + } + if (chr == 0) { + parser->error = "\\u0000 not supported in strings"; + return -1; + } + uni_ucs4_to_utf8_c(chr, parser->value); + parser->data += 3; + return 1; +} + +static int json_parse_string(struct json_parser *parser, bool allow_skip, + const char **value_r) +{ + int ret; + + if (*parser->data != '"') + return -1; + parser->data++; + + if (parser->skipping && allow_skip) { + *value_r = NULL; + return json_skip_string(parser); + } + + str_truncate(parser->value, 0); + for (; parser->data != parser->end; parser->data++) { + if (*parser->data == '"') { + parser->data++; + *value_r = str_c(parser->value); + return 1; + } + switch (*parser->data) { + case '\\': + if (++parser->data == parser->end) + return 0; + switch (*parser->data) { + case '"': + case '\\': + case '/': + str_append_c(parser->value, *parser->data); + break; + case 'b': + str_append_c(parser->value, '\b'); + break; + case 'f': + str_append_c(parser->value, '\f'); + break; + case 'n': + str_append_c(parser->value, '\n'); + break; + case 'r': + str_append_c(parser->value, '\r'); + break; + case 't': + str_append_c(parser->value, '\t'); + break; + case 'u': + if ((ret=json_parse_unicode_escape(parser)) <= 0) + return ret; + break; + default: + parser->error = "Invalid escape string"; + return -1; + } + break; + case '\0': + parser->error = "NULs not supported in strings"; + return -1; + default: + str_append_c(parser->value, *parser->data); + break; + } + } + return 0; +} + +static int +json_parse_digits(struct json_parser *parser) +{ + if (parser->data == parser->end) + return 0; + if (*parser->data < '0' || *parser->data > '9') + return -1; + + while (parser->data != parser->end && + *parser->data >= '0' && *parser->data <= '9') + str_append_c(parser->value, *parser->data++); + return 1; +} + +static int json_parse_int(struct json_parser *parser) +{ + int ret; + + if (*parser->data == '-') { + str_append_c(parser->value, *parser->data++); + if (parser->data == parser->end) + return 0; + } + if (*parser->data == '0') + str_append_c(parser->value, *parser->data++); + else { + if ((ret = json_parse_digits(parser)) <= 0) + return ret; + } + return 1; +} + +static int json_parse_number(struct json_parser *parser, const char **value_r) +{ + int ret; + + str_truncate(parser->value, 0); + if ((ret = json_parse_int(parser)) <= 0) + return ret; + if (parser->data != parser->end && *parser->data == '.') { + /* frac */ + str_append_c(parser->value, *parser->data++); + if ((ret = json_parse_digits(parser)) <= 0) + return ret; + } + if (parser->data != parser->end && + (*parser->data == 'e' || *parser->data == 'E')) { + /* exp */ + str_append_c(parser->value, *parser->data++); + if (parser->data == parser->end) + return 0; + if (*parser->data == '+' || *parser->data == '-') + str_append_c(parser->value, *parser->data++); + if ((ret = json_parse_digits(parser)) <= 0) + return ret; + } + if (parser->data == parser->end && !parser->input->eof) + return 0; + *value_r = str_c(parser->value); + return 1; +} + +static int json_parse_atom(struct json_parser *parser, const char *atom) +{ + size_t avail, len = strlen(atom); + + avail = parser->end - parser->data; + if (avail < len) { + if (memcmp(parser->data, atom, avail) != 0) + return -1; + + /* everything matches so far, but we need more data */ + parser->data += avail; + return 0; + } + if (memcmp(parser->data, atom, len) != 0) + return -1; + parser->data += len; + return 1; +} + +static int json_parse_denest(struct json_parser *parser) +{ + const enum json_state *nested_states; + unsigned count; + + parser->data++; + json_parser_update_input_pos(parser); + + nested_states = array_get(&parser->nesting, &count); + i_assert(count > 0); + if (count == 1) { + /* closing root */ + parser->state = JSON_STATE_DONE; + if ((parser->flags & JSON_PARSER_NO_ROOT_OBJECT) == 0) + return 0; + /* we want to return the ending "]" or "}" to caller */ + return 1; + } + + /* closing a nested object */ + parser->state = nested_states[count-2] == JSON_STATE_OBJECT_OPEN ? + JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT; + array_delete(&parser->nesting, count-1, 1); + + if (parser->nested_skip_count > 0) { + parser->nested_skip_count--; + return 0; + } + return 1; +} + +static int +json_parse_close_object(struct json_parser *parser, enum json_type *type_r) +{ + if (json_parse_denest(parser) == 0) + return 0; + *type_r = JSON_TYPE_OBJECT_END; + return 1; +} + +static int +json_parse_close_array(struct json_parser *parser, enum json_type *type_r) +{ + if (json_parse_denest(parser) == 0) + return 0; + *type_r = JSON_TYPE_ARRAY_END; + return 1; +} + +static void json_parser_object_open(struct json_parser *parser) +{ + parser->data++; + parser->state = JSON_STATE_OBJECT_OPEN; + array_push_back(&parser->nesting, &parser->state); + json_parser_update_input_pos(parser); +} + +static int +json_try_parse_next(struct json_parser *parser, enum json_type *type_r, + const char **value_r) +{ + bool skipping = parser->skipping; + int ret; + + if (!json_parse_whitespace(parser)) + return -1; + + switch (parser->state) { + case JSON_STATE_ROOT: + if (*parser->data != '{') { + parser->error = "Object doesn't begin with '{'"; + return -1; + } + json_parser_object_open(parser); + return 0; + case JSON_STATE_OBJECT_VALUE: + case JSON_STATE_ARRAY_VALUE: + case JSON_STATE_VALUE: + if (*parser->data == '{') { + json_parser_object_open(parser); + + if (parser->skipping) { + parser->nested_skip_count++; + return 0; + } + *type_r = JSON_TYPE_OBJECT; + return 1; + } else if (*parser->data == '[') { + parser->data++; + parser->state = JSON_STATE_ARRAY_OPEN; + array_push_back(&parser->nesting, &parser->state); + json_parser_update_input_pos(parser); + + if (parser->skipping) { + parser->nested_skip_count++; + return 0; + } + *type_r = JSON_TYPE_ARRAY; + return 1; + } + + if ((ret = json_parse_string(parser, TRUE, value_r)) >= 0) { + *type_r = JSON_TYPE_STRING; + } else if ((ret = json_parse_number(parser, value_r)) >= 0) { + *type_r = JSON_TYPE_NUMBER; + } else if ((ret = json_parse_atom(parser, "true")) >= 0) { + *type_r = JSON_TYPE_TRUE; + *value_r = "true"; + } else if ((ret = json_parse_atom(parser, "false")) >= 0) { + *type_r = JSON_TYPE_FALSE; + *value_r = "false"; + } else if ((ret = json_parse_atom(parser, "null")) >= 0) { + *type_r = JSON_TYPE_NULL; + *value_r = NULL; + } else { + if (parser->error == NULL) + parser->error = "Invalid data as value"; + return -1; + } + if (ret == 0) { + i_assert(parser->data == parser->end); + if (parser->skipping && *type_r == JSON_TYPE_STRING) { + /* a large string that we want to skip over. */ + json_parser_update_input_pos(parser); + parser->state = parser->state == JSON_STATE_OBJECT_VALUE ? + JSON_STATE_OBJECT_SKIP_STRING : + JSON_STATE_ARRAY_SKIP_STRING; + return 0; + } + return -1; + } + switch (parser->state) { + case JSON_STATE_OBJECT_VALUE: + parser->state = JSON_STATE_OBJECT_NEXT; + break; + case JSON_STATE_ARRAY_VALUE: + parser->state = JSON_STATE_ARRAY_NEXT; + break; + case JSON_STATE_VALUE: + parser->state = JSON_STATE_DONE; + break; + default: + i_unreached(); + } + break; + case JSON_STATE_OBJECT_OPEN: + if (*parser->data == '}') + return json_parse_close_object(parser, type_r); + parser->state = JSON_STATE_OBJECT_KEY; + /* fall through */ + case JSON_STATE_OBJECT_KEY: + if (json_parse_string(parser, FALSE, value_r) <= 0) { + parser->error = "Expected string as object key"; + return -1; + } + *type_r = JSON_TYPE_OBJECT_KEY; + parser->state = JSON_STATE_OBJECT_COLON; + break; + case JSON_STATE_OBJECT_COLON: + if (*parser->data != ':') { + parser->error = "Expected ':' after key"; + return -1; + } + parser->data++; + parser->state = JSON_STATE_OBJECT_VALUE; + json_parser_update_input_pos(parser); + return 0; + case JSON_STATE_OBJECT_NEXT: + if (parser->skipping && parser->nested_skip_count == 0) { + /* we skipped over the previous value */ + parser->skipping = FALSE; + } + if (*parser->data == '}') + return json_parse_close_object(parser, type_r); + if (*parser->data != ',') { + parser->error = "Expected ',' or '}' after object value"; + return -1; + } + parser->state = JSON_STATE_OBJECT_KEY; + parser->data++; + json_parser_update_input_pos(parser); + return 0; + case JSON_STATE_ARRAY_OPEN: + if (*parser->data == ']') + return json_parse_close_array(parser, type_r); + parser->state = JSON_STATE_ARRAY_VALUE; + return 0; + case JSON_STATE_ARRAY_NEXT: + if (parser->skipping && parser->nested_skip_count == 0) { + /* we skipped over the previous value */ + parser->skipping = FALSE; + } + /* fall through */ + case JSON_STATE_ARRAY_NEXT_SKIP: + if (*parser->data == ']') + return json_parse_close_array(parser, type_r); + if (*parser->data != ',') { + parser->error = "Expected ',' or '}' after array value"; + return -1; + } + parser->state = JSON_STATE_ARRAY_VALUE; + parser->data++; + json_parser_update_input_pos(parser); + return 0; + case JSON_STATE_OBJECT_SKIP_STRING: + case JSON_STATE_ARRAY_SKIP_STRING: + if (json_skip_string(parser) <= 0) + return -1; + parser->state = parser->state == JSON_STATE_OBJECT_SKIP_STRING ? + JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT; + return 0; + case JSON_STATE_DONE: + parser->error = "Unexpected data at the end"; + return -1; + } + json_parser_update_input_pos(parser); + return skipping ? 0 : 1; +} + +int json_parse_next(struct json_parser *parser, enum json_type *type_r, + const char **value_r) +{ + int ret; + + i_assert(parser->strinput == NULL); + + *value_r = NULL; + + while ((ret = json_parser_read_more(parser)) > 0) { + while ((ret = json_try_parse_next(parser, type_r, value_r)) == 0) + ; + if (ret > 0) + break; + if (parser->data != parser->end) + return -1; + /* parsing probably failed because there wasn't enough input. + reset the error and try reading more. */ + parser->error = NULL; + parser->highwater_offset = parser->input->v_offset + + i_stream_get_data_size(parser->input); + } + return ret; +} + +void json_parse_skip_next(struct json_parser *parser) +{ + i_assert(!parser->skipping); + i_assert(parser->strinput == NULL); + i_assert(parser->state == JSON_STATE_OBJECT_COLON || + parser->state == JSON_STATE_OBJECT_VALUE || + parser->state == JSON_STATE_ARRAY_VALUE || + parser->state == JSON_STATE_ARRAY_NEXT); + + parser->skipping = TRUE; + if (parser->state == JSON_STATE_ARRAY_NEXT) + parser->state = JSON_STATE_ARRAY_NEXT_SKIP; +} + +void json_parse_skip(struct json_parser *parser) +{ + i_assert(!parser->skipping); + i_assert(parser->strinput == NULL); + i_assert(parser->state == JSON_STATE_OBJECT_NEXT || + parser->state == JSON_STATE_OBJECT_OPEN || + parser->state == JSON_STATE_ARRAY_NEXT || + parser->state == JSON_STATE_ARRAY_OPEN); + + if (parser->state == JSON_STATE_OBJECT_OPEN || + parser->state == JSON_STATE_ARRAY_OPEN) + parser->nested_skip_count++; + + parser->skipping = TRUE; + if (parser->state == JSON_STATE_ARRAY_NEXT) + parser->state = JSON_STATE_ARRAY_NEXT_SKIP; +} + +static void json_strinput_destroyed(struct json_parser *parser) +{ + i_assert(parser->strinput != NULL); + + parser->strinput = NULL; +} + +static int +json_try_parse_stream_start(struct json_parser *parser, + struct istream **input_r) +{ + if (!json_parse_whitespace(parser)) + return -1; + + if (parser->state == JSON_STATE_OBJECT_COLON) { + if (*parser->data != ':') { + parser->error = "Expected ':' after key"; + return -1; + } + parser->data++; + parser->state = JSON_STATE_OBJECT_VALUE; + if (!json_parse_whitespace(parser)) + return -1; + } + + if (*parser->data != '"') + return -1; + parser->data++; + json_parser_update_input_pos(parser); + + parser->state = parser->state == JSON_STATE_OBJECT_VALUE ? + JSON_STATE_OBJECT_SKIP_STRING : JSON_STATE_ARRAY_SKIP_STRING; + parser->strinput = i_stream_create_jsonstr(parser->input); + i_stream_add_destroy_callback(parser->strinput, + json_strinput_destroyed, parser); + + *input_r = parser->strinput; + return 0; +} + +int json_parse_next_stream(struct json_parser *parser, + struct istream **input_r) +{ + int ret; + + i_assert(!parser->skipping); + i_assert(parser->strinput == NULL); + i_assert(parser->state == JSON_STATE_OBJECT_COLON || + parser->state == JSON_STATE_OBJECT_VALUE || + parser->state == JSON_STATE_ARRAY_VALUE); + + *input_r = NULL; + + while ((ret = json_parser_read_more(parser)) > 0) { + if (json_try_parse_stream_start(parser, input_r) == 0) + break; + if (parser->data != parser->end) + return -1; + /* parsing probably failed because there wasn't enough input. + reset the error and try reading more. */ + parser->error = NULL; + parser->highwater_offset = parser->input->v_offset + + i_stream_get_data_size(parser->input); + } + return ret; +} + +static void json_append_escaped_char(string_t *dest, unsigned char src) +{ + switch (src) { + case '\b': + str_append(dest, "\\b"); + break; + case '\f': + str_append(dest, "\\f"); + break; + case '\n': + str_append(dest, "\\n"); + break; + case '\r': + str_append(dest, "\\r"); + break; + case '\t': + str_append(dest, "\\t"); + break; + case '"': + str_append(dest, "\\\""); + break; + case '\\': + str_append(dest, "\\\\"); + break; + default: + if (src < 0x20 || src >= 0x80) + str_printfa(dest, "\\u%04x", src); + else + str_append_c(dest, src); + break; + } +} + +void json_append_escaped_ucs4(string_t *dest, unichar_t chr) +{ + if (chr < 0x80) + json_append_escaped_char(dest, (unsigned char)chr); + else if (chr == 0x2028 || chr == 0x2029) + str_printfa(dest, "\\u%04x", chr); + else + uni_ucs4_to_utf8_c(chr, dest); +} + +void ostream_escaped_json_format(string_t *dest, unsigned char src) +{ + json_append_escaped_char(dest, src); +} + +void json_append_escaped(string_t *dest, const char *src) +{ + json_append_escaped_data(dest, (const unsigned char*)src, strlen(src)); +} + +void json_append_escaped_data(string_t *dest, const unsigned char *src, size_t size) +{ + size_t i; + int bytes = 0; + unichar_t chr; + + for (i = 0; i < size;) { + bytes = uni_utf8_get_char_n(src+i, size-i, &chr); + if (bytes > 0 && uni_is_valid_ucs4(chr)) { + json_append_escaped_ucs4(dest, chr); + i += bytes; + } else { + str_append_data(dest, UNICODE_REPLACEMENT_CHAR_UTF8, + UTF8_REPLACEMENT_CHAR_LEN); + i++; + } + } +} diff --git a/src/lib/json-parser.h b/src/lib/json-parser.h new file mode 100644 index 0000000..745f4a7 --- /dev/null +++ b/src/lib/json-parser.h @@ -0,0 +1,61 @@ +#ifndef JSON_PARSER_H +#define JSON_PARSER_H + +#include "unichar.h" + +enum json_type { + /* { key: */ + JSON_TYPE_OBJECT_KEY, + /* : { new object */ + JSON_TYPE_OBJECT, + /* } (not returned for the root object) */ + JSON_TYPE_OBJECT_END, + + JSON_TYPE_ARRAY, + JSON_TYPE_ARRAY_END, + + JSON_TYPE_STRING, + JSON_TYPE_NUMBER, + JSON_TYPE_TRUE, + JSON_TYPE_FALSE, + JSON_TYPE_NULL +}; + +enum json_parser_flags { + /* By default we assume that the input is an object and parsing skips + the root level "{" and "}". If this flag is set, it's possible to + parse any other type of JSON values directly. */ + JSON_PARSER_NO_ROOT_OBJECT = 0x01 +}; + +/* Parse JSON tokens from the input stream. */ +struct json_parser *json_parser_init(struct istream *input); +struct json_parser *json_parser_init_flags(struct istream *input, + enum json_parser_flags flags); + +int json_parser_deinit(struct json_parser **parser, const char **error_r); + +/* Parse the next token. Returns 1 if found, 0 if more input stream is + non-blocking and needs more input, -1 if input stream is at EOF. */ +int json_parse_next(struct json_parser *parser, enum json_type *type_r, + const char **value_r); +/* Skip the next object value. If it's an object, its members are also + skipped. */ +void json_parse_skip_next(struct json_parser *parser); +/* Skip the remainder of the value parsed earlier by json_parse_next(). */ +void json_parse_skip(struct json_parser *parser); +/* Return the following string as input stream. Returns 1 if ok, 0 if + input stream is non-blocking and needs more input, -1 if the next token + isn't a string (call json_parse_next()). */ +int json_parse_next_stream(struct json_parser *parser, + struct istream **input_r); + +/* Append UCS4 to already opened JSON string. */ +void json_append_escaped_ucs4(string_t *dest, unichar_t chr); +/* Append data to already opened JSON string. src should be valid UTF-8 data. */ +void json_append_escaped(string_t *dest, const char *src); +/* Same as json_append_escaped(), but append non-\0 terminated input. */ +void json_append_escaped_data(string_t *dest, const unsigned char *src, size_t size); +void ostream_escaped_json_format(string_t *dest, unsigned char src); + +#endif diff --git a/src/lib/json-tree.c b/src/lib/json-tree.c new file mode 100644 index 0000000..cb721c2 --- /dev/null +++ b/src/lib/json-tree.c @@ -0,0 +1,173 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "json-tree.h" + +struct json_tree { + pool_t pool; + struct json_tree_node *root, *cur, *cur_child; +}; + +struct json_tree * +json_tree_init_type(enum json_type container) +{ + struct json_tree *tree; + pool_t pool; + + pool = pool_alloconly_create("json tree", 1024); + tree = p_new(pool, struct json_tree, 1); + tree->pool = pool; + tree->root = tree->cur = p_new(tree->pool, struct json_tree_node, 1); + tree->cur->value_type = container == JSON_TYPE_ARRAY ? container : JSON_TYPE_OBJECT; + return tree; +} + +void json_tree_deinit(struct json_tree **_tree) +{ + struct json_tree *tree = *_tree; + + *_tree = NULL; + pool_unref(&tree->pool); +} + +static void +json_tree_append_child(struct json_tree *tree, enum json_type type, + const char *value) +{ + struct json_tree_node *node; + + node = p_new(tree->pool, struct json_tree_node, 1); + node->parent = tree->cur; + node->value_type = type; + node->value.str = p_strdup(tree->pool, value); + + if (tree->cur_child == NULL) + tree->cur->value.child = node; + else + tree->cur_child->next = node; + tree->cur_child = node; +} + +static void +json_tree_set_cur(struct json_tree *tree, struct json_tree_node *node) +{ + tree->cur = node; + tree->cur_child = tree->cur->value.child; + if (tree->cur_child != NULL) { + while (tree->cur_child->next != NULL) + tree->cur_child = tree->cur_child->next; + } +} + +static int +json_tree_append_value(struct json_tree *tree, enum json_type type, + const char *value) +{ + switch (tree->cur->value_type) { + case JSON_TYPE_OBJECT_KEY: + /* "key": value - we already added the node and set its key, + so now just set the value */ + tree->cur->value_type = type; + tree->cur->value.str = p_strdup(tree->pool, value); + json_tree_set_cur(tree, tree->cur->parent); + break; + case JSON_TYPE_ARRAY: + /* element in array - add a new node */ + json_tree_append_child(tree, type, value); + break; + default: + return -1; + } + return 0; +} + +int json_tree_append(struct json_tree *tree, enum json_type type, + const char *value) +{ + switch (type) { + case JSON_TYPE_OBJECT_KEY: + if (tree->cur->value_type != JSON_TYPE_OBJECT) + return -1; + json_tree_append_child(tree, type, NULL); + json_tree_set_cur(tree, tree->cur_child); + tree->cur->key = p_strdup(tree->pool, value); + break; + case JSON_TYPE_ARRAY: + if (json_tree_append_value(tree, type, NULL) < 0) + return -1; + json_tree_set_cur(tree, tree->cur_child); + break; + case JSON_TYPE_OBJECT: + if (tree->cur->value_type == JSON_TYPE_OBJECT_KEY) + tree->cur->value_type = JSON_TYPE_OBJECT; + else if (tree->cur->value_type == JSON_TYPE_ARRAY) { + json_tree_append_child(tree, type, NULL); + json_tree_set_cur(tree, tree->cur_child); + } else { + return -1; + } + break; + case JSON_TYPE_OBJECT_END: + if (tree->cur->parent == NULL || + tree->cur->value_type != JSON_TYPE_OBJECT) + return -1; + json_tree_set_cur(tree, tree->cur->parent); + break; + case JSON_TYPE_ARRAY_END: + if (tree->cur->parent == NULL || + tree->cur->value_type != JSON_TYPE_ARRAY) + return -1; + json_tree_set_cur(tree, tree->cur->parent); + break; + case JSON_TYPE_STRING: + case JSON_TYPE_NUMBER: + case JSON_TYPE_TRUE: + case JSON_TYPE_FALSE: + case JSON_TYPE_NULL: + if (json_tree_append_value(tree, type, value) < 0) + return -1; + break; + } + return 0; +} + +const struct json_tree_node * +json_tree_root(const struct json_tree *tree) +{ + return tree->root; +} + +const struct json_tree_node * +json_tree_find_key(const struct json_tree_node *node, const char *key) +{ + i_assert(node->value_type == JSON_TYPE_OBJECT); + + node = json_tree_get_child(node); + for (; node != NULL; node = node->next) { + if (node->key != NULL && strcmp(node->key, key) == 0) + return node; + } + return NULL; +} + +const struct json_tree_node * +json_tree_find_child_with(const struct json_tree_node *node, + const char *key, const char *value) +{ + const struct json_tree_node *child; + + i_assert(node->value_type == JSON_TYPE_OBJECT || + node->value_type == JSON_TYPE_ARRAY); + + for (node = json_tree_get_child(node); node != NULL; node = node->next) { + if (node->value_type != JSON_TYPE_OBJECT) + continue; + + child = json_tree_find_key(node, key); + if (child != NULL && + json_tree_get_value_str(child) != NULL && + strcmp(json_tree_get_value_str(child), value) == 0) + return node; + } + return NULL; +} diff --git a/src/lib/json-tree.h b/src/lib/json-tree.h new file mode 100644 index 0000000..a995c75 --- /dev/null +++ b/src/lib/json-tree.h @@ -0,0 +1,62 @@ +#ifndef JSON_TREE_H +#define JSON_TREE_H + +#include "json-parser.h" + +/* Direct access to this structure is not encouraged, use the inline + function accessors where possible, so that the implementation + details can remain fluid, or, even better, hidden. */ +struct json_tree_node { + /* object key, or NULL if we're in a list */ + const char *key; + struct json_tree_node *parent, *next; + + enum json_type value_type; + struct { + /* for JSON_TYPE_OBJECT and JSON_TYPE_ARRAY */ + struct json_tree_node *child; + /* for other types */ + const char *str; + } value; +}; +static inline ATTR_PURE const struct json_tree_node *json_tree_get_child(const struct json_tree_node *node) +{ + return node->value.child; +} +static inline ATTR_PURE const char *json_tree_get_value_str(const struct json_tree_node *node) +{ + return node->value.str; +} + +/* You can build a list or an object, nothing else. */ +struct json_tree *json_tree_init_type(enum json_type container); +static inline struct json_tree *json_tree_init(void) +{ + return json_tree_init_type(JSON_TYPE_OBJECT); +} +static inline struct json_tree *json_tree_init_array(void) +{ + return json_tree_init_type(JSON_TYPE_ARRAY); +} + +void json_tree_deinit(struct json_tree **tree); + +/* Append data to a tree. The type/value should normally come from json-parser. + Returns 0 on success, -1 if the input was invalid (which should never happen + if it's coming from json-parser). */ +int json_tree_append(struct json_tree *tree, enum json_type type, + const char *value); + +/* Return the root node. */ +const struct json_tree_node * +json_tree_root(const struct json_tree *tree); +/* Find a node with the specified key from an OBJECT node */ +const struct json_tree_node * +json_tree_find_key(const struct json_tree_node *node, const char *key); +/* Find an object node (from an array), which contains the specified key=value + in its values. */ +const struct json_tree_node * +json_tree_find_child_with(const struct json_tree_node *node, + const char *key, const char *value); + +#endif diff --git a/src/lib/lib-event-private.h b/src/lib/lib-event-private.h new file mode 100644 index 0000000..f6d961a --- /dev/null +++ b/src/lib/lib-event-private.h @@ -0,0 +1,114 @@ +#ifndef LIB_EVENT_PRIVATE_H +#define LIB_EVENT_PRIVATE_H + +#include <sys/resource.h> + +struct event_pointer { + const char *key; + void *value; +}; + +struct event { + /* linked list of all events, newest first */ + struct event *prev, *next; + + int refcount; + pool_t pool; + struct event *parent; + uint64_t id; + + /* Avoid sending the event to stats over and over. The 'change_id' + increments every time something about this event changes. If + 'sent_to_stats_id' matches 'change_id', we skip sending this + event out. If it doesn't match, we send it and set + 'sent_to_stats_id' to 'change_id'. sent_to_stats_id=0 is reserved + for "event hasn't been sent". 'change_id' can never be 0. */ + uint32_t change_id; + uint32_t sent_to_stats_id; + + char *log_prefix; + unsigned int log_prefixes_dropped; + /* sending_debug_log can be used if this value matches + event_filter_replace_counter. */ + unsigned int debug_level_checked_filter_counter; + event_log_prefix_callback_t *log_prefix_callback; + void *log_prefix_callback_context; + event_log_message_callback_t *log_message_callback; + void *log_message_callback_context; + ARRAY(struct event_pointer) pointers; + /* If the event's log level is at least this high, log it. If it's + lower, check for debug log filters etc. */ + enum log_type min_log_level; + + bool log_prefix_from_system_pool:1; + bool log_prefix_replace:1; + bool passthrough:1; + bool forced_debug:1; + bool always_log_source:1; + bool sending_debug_log:1; + +/* Fields that are exported & imported: */ + struct timeval tv_created_ioloop; + struct timeval tv_created; + struct timeval tv_last_sent; + struct rusage ru_last; + + const char *source_filename; + unsigned int source_linenum; + + /* This is the event's name while it's being sent. It'll be removed + after the event is sent. */ + char *sending_name; + + ARRAY(struct event_category *) categories; + ARRAY(struct event_field) fields; +}; + +enum event_callback_type { + /* Event was just created */ + EVENT_CALLBACK_TYPE_CREATE, + /* Event is being sent */ + EVENT_CALLBACK_TYPE_SEND, + /* Event is being freed */ + EVENT_CALLBACK_TYPE_FREE, +}; + +/* Returns TRUE if the event should continue to the next handler. Unless + stopped, the final handler logs the event if it matches the log filter. */ +typedef bool event_callback_t(struct event *event, + enum event_callback_type type, + struct failure_context *ctx, + const char *fmt, va_list args); +/* Called when category is registered or unregistered. The parent category + is always already registered. */ +typedef void event_category_callback_t(struct event_category *category); + +void event_send(struct event *event, struct failure_context *ctx, + const char *fmt, ...) ATTR_FORMAT(3, 4); +void event_vsend(struct event *event, struct failure_context *ctx, + const char *fmt, va_list args) ATTR_FORMAT(3, 0); + +struct event *events_get_head(void); + +/* Find event category by name. This only finds registered categories. */ +struct event_category *event_category_find_registered(const char *name); +/* Return all registered categories. */ +struct event_category *const * +event_get_registered_categories(unsigned int *count_r); + +/* Register callback to be called for event's different states. */ +void event_register_callback(event_callback_t *callback); +void event_unregister_callback(event_callback_t *callback); + +/* Register callback to be called whenever categories are registered or + unregistered. */ +void event_category_register_callback(event_category_callback_t *callback); +void event_category_unregister_callback(event_category_callback_t *callback); + +static inline void event_recalculate_debug_level(struct event *event) +{ + event->debug_level_checked_filter_counter = + event_filter_replace_counter - 1; +} + +#endif diff --git a/src/lib/lib-event.c b/src/lib/lib-event.c new file mode 100644 index 0000000..864562d --- /dev/null +++ b/src/lib/lib-event.c @@ -0,0 +1,1776 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "lib-event-private.h" +#include "event-filter.h" +#include "array.h" +#include "llist.h" +#include "time-util.h" +#include "str.h" +#include "strescape.h" +#include "ioloop-private.h" + +#include <ctype.h> + +enum event_code { + EVENT_CODE_ALWAYS_LOG_SOURCE = 'a', + EVENT_CODE_CATEGORY = 'c', + EVENT_CODE_TV_LAST_SENT = 'l', + EVENT_CODE_SENDING_NAME = 'n', + EVENT_CODE_SOURCE = 's', + + EVENT_CODE_FIELD_INTMAX = 'I', + EVENT_CODE_FIELD_STR = 'S', + EVENT_CODE_FIELD_TIMEVAL = 'T', + EVENT_CODE_FIELD_STRLIST = 'L', +}; + +/* Internal event category state. + + Each (unique) event category maps to one internal category. (I.e., if + two places attempt to register the same category, they will share the + internal state.) + + This is required in order to support multiple registrations of the same + category. Currently, the only situation in which this occurs is the + stats process receiving categories from other processes and also using + the same categories internally. + + During registration, we look up the internal state based on the new + category's name. If found, we use it after sanity checking that the two + are identical (i.e., they both have the same name and parent). If not + found, we allocate a new internal state and use it. + + We stash a pointer to the internal state in struct event_category (the + "internal" member). As a result, all category structs for the same + category point to the same internal state. */ +struct event_internal_category { + /* More than one category can be represented by the internal state. + To give consumers a unique but consistent category pointer, we + return a pointer to this 'represetative' category structure. + Because we allocated it, we know that it will live exactly as + long as we need it to. */ + struct event_category representative; + + struct event_internal_category *parent; + char *name; + int refcount; +}; + +struct event_reason { + struct event *event; +}; + +extern struct event_passthrough event_passthrough_vfuncs; + +static struct event *events = NULL; +static struct event *current_global_event = NULL; +static struct event *event_last_passthrough = NULL; +static ARRAY(event_callback_t *) event_handlers; +static ARRAY(event_category_callback_t *) event_category_callbacks; +static ARRAY(struct event_internal_category *) event_registered_categories_internal; +static ARRAY(struct event_category *) event_registered_categories_representative; +static ARRAY(struct event *) global_event_stack; +static uint64_t event_id_counter = 0; + +static void get_self_rusage(struct rusage *ru_r) +{ + if (getrusage(RUSAGE_SELF, ru_r) < 0) + i_fatal("getrusage() failed: %m"); +} + +static struct event * +event_create_internal(struct event *parent, const char *source_filename, + unsigned int source_linenum); +static struct event_internal_category * +event_category_find_internal(const char *name); + +static struct event *last_passthrough_event(void) +{ + i_assert(event_last_passthrough != NULL); + return event_last_passthrough; +} + +static void event_copy_parent_defaults(struct event *event, + const struct event *parent) +{ + event->always_log_source = parent->always_log_source; + event->passthrough = parent->passthrough; + event->min_log_level = parent->min_log_level; + event->forced_debug = parent->forced_debug; +} + +static bool +event_find_category(const struct event *event, + const struct event_category *category); + +static void event_set_changed(struct event *event) +{ + event->change_id++; + /* It's unlikely that change_id will ever wrap, but lets be safe + anyway. */ + if (event->change_id == 0 || + event->change_id == event->sent_to_stats_id) + event->change_id++; +} + +static bool +event_call_callbacks(struct event *event, enum event_callback_type type, + struct failure_context *ctx, const char *fmt, va_list args) +{ + event_callback_t *callback; + + array_foreach_elem(&event_handlers, callback) { + bool ret; + + T_BEGIN { + ret = callback(event, type, ctx, fmt, args); + } T_END; + if (!ret) { + /* event sending was stopped */ + return FALSE; + } + } + return TRUE; +} + +static void +event_call_callbacks_noargs(struct event *event, + enum event_callback_type type, ...) +{ + va_list args; + + /* the args are empty and not used for anything, but there doesn't seem + to be any nice and standard way of passing an initialized va_list + as a parameter without va_start(). */ + va_start(args, type); + (void)event_call_callbacks(event, type, NULL, NULL, args); + va_end(args); +} + +void event_copy_categories(struct event *to, struct event *from) +{ + unsigned int cat_count; + struct event_category *const *categories = + event_get_categories(from, &cat_count); + for (unsigned int i = 1; i <= cat_count; i++) + event_add_category(to, categories[cat_count-i]); +} + +void event_copy_fields(struct event *to, struct event *from) +{ + const struct event_field *fld; + unsigned int count; + const char *const *values; + + if (!array_is_created(&from->fields)) + return; + array_foreach(&from->fields, fld) { + switch (fld->value_type) { + case EVENT_FIELD_VALUE_TYPE_STR: + event_add_str(to, fld->key, fld->value.str); + break; + case EVENT_FIELD_VALUE_TYPE_INTMAX: + event_add_int(to, fld->key, fld->value.intmax); + break; + case EVENT_FIELD_VALUE_TYPE_TIMEVAL: + event_add_timeval(to, fld->key, &fld->value.timeval); + break; + case EVENT_FIELD_VALUE_TYPE_STRLIST: + values = array_get(&fld->value.strlist, &count); + for (unsigned int i = 0; i < count; i++) + event_strlist_append(to, fld->key, values[i]); + break; + default: + break; + } + } +} + +bool event_has_all_categories(struct event *event, const struct event *other) +{ + struct event_category **cat; + if (!array_is_created(&other->categories)) + return TRUE; + if (!array_is_created(&event->categories)) + return FALSE; + array_foreach_modifiable(&other->categories, cat) { + if (!event_find_category(event, *cat)) + return FALSE; + } + return TRUE; +} + +bool event_has_all_fields(struct event *event, const struct event *other) +{ + struct event_field *fld; + if (!array_is_created(&other->fields)) + return TRUE; + array_foreach_modifiable(&other->fields, fld) { + if (event_find_field_nonrecursive(event, fld->key) == NULL) + return FALSE; + } + return TRUE; +} + +struct event *event_dup(const struct event *source) +{ + struct event *ret = + event_create_internal(source->parent, source->source_filename, + source->source_linenum); + string_t *str = t_str_new(256); + const char *err; + event_export(source, str); + if (!event_import(ret, str_c(str), &err)) + i_panic("event_import(%s) failed: %s", str_c(str), err); + ret->tv_created_ioloop = source->tv_created_ioloop; + return ret; +} + +/* + * Copy the source's categories and fields recursively. + * + * We recurse to the parent before copying this event's data because we may + * be overriding a field. + */ +static void event_flatten_recurse(struct event *dst, struct event *src, + struct event *limit) +{ + if (src->parent != limit) + event_flatten_recurse(dst, src->parent, limit); + + event_copy_categories(dst, src); + event_copy_fields(dst, src); +} + +struct event *event_flatten(struct event *src) +{ + struct event *dst; + + /* If we don't have a parent or a global event, + we have nothing to flatten. */ + if (src->parent == NULL && current_global_event == NULL) + return event_ref(src); + + /* We have to flatten the event. */ + + dst = event_create_internal(NULL, src->source_filename, + src->source_linenum); + dst = event_set_name(dst, src->sending_name); + + if (current_global_event != NULL) + event_flatten_recurse(dst, current_global_event, NULL); + event_flatten_recurse(dst, src, NULL); + + dst->tv_created_ioloop = src->tv_created_ioloop; + dst->tv_created = src->tv_created; + dst->tv_last_sent = src->tv_last_sent; + + return dst; +} + +static inline void replace_parent_ref(struct event *event, struct event *new) +{ + if (event->parent == new) + return; /* no-op */ + + if (new != NULL) + event_ref(new); + + event_unref(&event->parent); + + event->parent = new; +} + +/* + * Minimize the event and its ancestry. + * + * In general, the chain of parents starting from this event can be divided + * up into four consecutive ranges: + * + * 1. the event itself + * 2. a range of events that should be flattened into the event itself + * 3. a range of trivial (i.e., no categories or fields) events that should + * be skipped + * 4. the rest of the chain + * + * Except for the first range, the event itself, the remaining ranges can + * have zero events. + * + * As the names of these ranges imply, we want to flatten certain parts of + * the ancestry, skip other parts of the ancestry and leave the remainder + * untouched. + * + * For example, suppose that we have an event (A) with ancestors forming the + * following graph: + * + * A -> B -> C -> D -> E -> F + * + * Further, suppose that B, C, and F contain some categories or fields but + * have not yet been sent to an external process that knows how to reference + * previously encountered events, and D contains no fields or categories of + * its own (but it inherits some from E and F). + * + * We can define the 4 ranges: + * + * A: the event + * B-C: flattening + * D: skipping + * E-end: the rest + * + * The output would therefore be: + * + * G -> E -> F + * + * where G contains the fields and categories of A, B, and C (and trivially + * D beacuse D was empty). + * + * Note that even though F has not yet been sent out, we send it now because + * it is part of the "rest" range. + * + * TODO: We could likely apply this function recursively on the "rest" + * range, but further investigation is required to determine whether it is + * worth it. + */ +struct event *event_minimize(struct event *event) +{ + struct event *flatten_bound; + struct event *skip_bound; + struct event *new_event; + struct event *cur; + + if (event->parent == NULL) + return event_ref(event); + + /* find the bound for field/category flattening */ + flatten_bound = NULL; + for (cur = event->parent; cur != NULL; cur = cur->parent) { + if (cur->sent_to_stats_id == 0 && + timeval_cmp(&cur->tv_created_ioloop, + &event->tv_created_ioloop) == 0) + continue; + + flatten_bound = cur; + break; + } + + /* continue to find the bound for empty event skipping */ + skip_bound = NULL; + for (; cur != NULL; cur = cur->parent) { + if (cur->sent_to_stats_id == 0 && + (!array_is_created(&cur->fields) || + array_is_empty(&cur->fields)) && + (!array_is_created(&cur->categories) || + array_is_empty(&cur->categories))) + continue; + + skip_bound = cur; + break; + } + + /* fast path - no flattening and no skipping to do */ + if ((event->parent == flatten_bound) && + (event->parent == skip_bound)) + return event_ref(event); + + new_event = event_dup(event); + + /* flatten */ + event_flatten_recurse(new_event, event, flatten_bound); + replace_parent_ref(new_event, flatten_bound); + + /* skip */ + replace_parent_ref(new_event, skip_bound); + + return new_event; +} + +static struct event * +event_create_internal(struct event *parent, const char *source_filename, + unsigned int source_linenum) +{ + struct event *event; + pool_t pool = pool_alloconly_create(MEMPOOL_GROWING"event", 1024); + + event = p_new(pool, struct event, 1); + event->refcount = 1; + event->id = ++event_id_counter; + event->pool = pool; + event->tv_created_ioloop = ioloop_timeval; + event->min_log_level = LOG_TYPE_INFO; + i_gettimeofday(&event->tv_created); + event->source_filename = p_strdup(pool, source_filename); + event->source_linenum = source_linenum; + event->change_id = 1; + if (parent != NULL) { + event->parent = parent; + event_ref(event->parent); + event_copy_parent_defaults(event, parent); + } + DLLIST_PREPEND(&events, event); + return event; +} + +#undef event_create +struct event *event_create(struct event *parent, const char *source_filename, + unsigned int source_linenum) +{ + struct event *event; + + event = event_create_internal(parent, source_filename, source_linenum); + (void)event_call_callbacks_noargs(event, EVENT_CALLBACK_TYPE_CREATE); + return event; +} + +#undef event_create_passthrough +struct event_passthrough * +event_create_passthrough(struct event *parent, const char *source_filename, + unsigned int source_linenum) +{ + if (!parent->passthrough) { + if (event_last_passthrough != NULL) { + /* API is being used in a wrong or dangerous way */ + i_panic("Can't create multiple passthrough events - " + "finish the earlier with ->event()"); + } + struct event *event = + event_create(parent, source_filename, source_linenum); + event->passthrough = TRUE; + /* This event only intends to extend the parent event. + Use the parent's creation timestamp. */ + event->tv_created_ioloop = parent->tv_created_ioloop; + event->tv_created = parent->tv_created; + memcpy(&event->ru_last, &parent->ru_last, sizeof(parent->ru_last)); + event_last_passthrough = event; + } else { + event_last_passthrough = parent; + } + return &event_passthrough_vfuncs; +} + +struct event *event_ref(struct event *event) +{ + i_assert(event->refcount > 0); + + event->refcount++; + return event; +} + +void event_unref(struct event **_event) +{ + struct event *event = *_event; + + if (event == NULL) + return; + *_event = NULL; + + i_assert(event->refcount > 0); + if (--event->refcount > 0) + return; + i_assert(event != current_global_event); + + event_call_callbacks_noargs(event, EVENT_CALLBACK_TYPE_FREE); + + if (event_last_passthrough == event) + event_last_passthrough = NULL; + if (event->log_prefix_from_system_pool) + i_free(event->log_prefix); + i_free(event->sending_name); + event_unref(&event->parent); + + DLLIST_REMOVE(&events, event); + pool_unref(&event->pool); +} + +struct event *events_get_head(void) +{ + return events; +} + +struct event *event_push_global(struct event *event) +{ + i_assert(event != NULL); + + if (current_global_event != NULL) { + if (!array_is_created(&global_event_stack)) + i_array_init(&global_event_stack, 4); + array_push_back(&global_event_stack, ¤t_global_event); + } + current_global_event = event; + return event; +} + +struct event *event_pop_global(struct event *event) +{ + i_assert(event != NULL); + i_assert(event == current_global_event); + /* If the active context's root event is popped, we'll assert-crash + later on when deactivating the context and the root event no longer + exists. */ + i_assert(event != io_loop_get_active_global_root()); + + if (!array_is_created(&global_event_stack) || + array_count(&global_event_stack) == 0) + current_global_event = NULL; + else { + unsigned int event_count; + struct event *const *events = + array_get(&global_event_stack, &event_count); + + i_assert(event_count > 0); + current_global_event = events[event_count-1]; + array_delete(&global_event_stack, event_count-1, 1); + } + return current_global_event; +} + +struct event *event_get_global(void) +{ + return current_global_event; +} + +#undef event_reason_begin +struct event_reason * +event_reason_begin(const char *reason_code, const char *source_filename, + unsigned int source_linenum) +{ + struct event_reason *reason; + + reason = i_new(struct event_reason, 1); + reason->event = event_create(event_get_global(), + source_filename, source_linenum); + event_strlist_append(reason->event, EVENT_REASON_CODE, reason_code); + event_push_global(reason->event); + return reason; +} + +void event_reason_end(struct event_reason **_reason) +{ + struct event_reason *reason = *_reason; + + if (reason == NULL) + return; + event_pop_global(reason->event); + /* This event was created only for global use. It shouldn't be + permanently stored anywhere. This assert could help catch bugs. */ + i_assert(reason->event->refcount == 1); + event_unref(&reason->event); + i_free(reason); +} + +const char *event_reason_code(const char *module, const char *name) +{ + return event_reason_code_prefix(module, "", name); +} + +static bool event_reason_code_module_validate(const char *module) +{ + const char *p; + + for (p = module; *p != '\0'; p++) { + if (*p == ' ' || *p == '-' || *p == ':') + return FALSE; + if (i_isupper(*p)) + return FALSE; + } + return TRUE; +} + +const char *event_reason_code_prefix(const char *module, + const char *name_prefix, const char *name) +{ + const char *p; + + i_assert(module[0] != '\0'); + i_assert(name[0] != '\0'); + + if (!event_reason_code_module_validate(module)) { + i_panic("event_reason_code_prefix(): " + "Invalid module '%s'", module); + } + if (!event_reason_code_module_validate(name_prefix)) { + i_panic("event_reason_code_prefix(): " + "Invalid name_prefix '%s'", name_prefix); + } + + string_t *str = t_str_new(strlen(module) + 1 + + strlen(name_prefix) + strlen(name)); + str_append(str, module); + str_append_c(str, ':'); + str_append(str, name_prefix); + + for (p = name; *p != '\0'; p++) { + switch (*p) { + case ' ': + case '-': + str_append_c(str, '_'); + break; + case ':': + i_panic("event_reason_code_prefix(): " + "name has ':' (%s, %s%s)", + module, name_prefix, name); + default: + str_append_c(str, i_tolower(*p)); + break; + } + } + return str_c(str); +} + +static struct event * +event_set_log_prefix(struct event *event, const char *prefix, bool append) +{ + event->log_prefix_callback = NULL; + event->log_prefix_callback_context = NULL; + if (event->log_prefix == NULL) { + /* allocate the first log prefix from the pool */ + event->log_prefix = p_strdup(event->pool, prefix); + } else { + /* log prefix is being updated multiple times - + switch to system pool so we don't keep leaking memory */ + if (event->log_prefix_from_system_pool) + i_free(event->log_prefix); + else + event->log_prefix_from_system_pool = TRUE; + event->log_prefix = i_strdup(prefix); + } + event->log_prefix_replace = !append; + return event; +} + +struct event * +event_set_append_log_prefix(struct event *event, const char *prefix) +{ + return event_set_log_prefix(event, prefix, TRUE); +} + +struct event *event_replace_log_prefix(struct event *event, const char *prefix) +{ + return event_set_log_prefix(event, prefix, FALSE); +} + +struct event * +event_drop_parent_log_prefixes(struct event *event, unsigned int count) +{ + event->log_prefixes_dropped = count; + return event; +} + +#undef event_set_log_prefix_callback +struct event * +event_set_log_prefix_callback(struct event *event, + bool replace, + event_log_prefix_callback_t *callback, + void *context) +{ + if (event->log_prefix_from_system_pool) + i_free(event->log_prefix); + else + event->log_prefix = NULL; + event->log_prefix_replace = replace; + event->log_prefix_callback = callback; + event->log_prefix_callback_context = context; + return event; +} + +#undef event_set_log_message_callback +struct event * +event_set_log_message_callback(struct event *event, + event_log_message_callback_t *callback, + void *context) +{ + event->log_message_callback = callback; + event->log_message_callback_context = context; + return event; +} + +#undef event_unset_log_message_callback +void event_unset_log_message_callback(struct event *event, + event_log_message_callback_t *callback, + void *context) +{ + i_assert(event->log_message_callback == callback); + i_assert(event->log_message_callback_context == context); + + event->log_message_callback = NULL; + event->log_message_callback_context = NULL; +} + +struct event * +event_set_name(struct event *event, const char *name) +{ + i_free(event->sending_name); + event->sending_name = i_strdup(name); + return event; +} + +struct event * +event_set_source(struct event *event, const char *filename, + unsigned int linenum, bool literal_fname) +{ + if (strcmp(event->source_filename, filename) != 0) { + event->source_filename = literal_fname ? filename : + p_strdup(event->pool, filename); + } + event->source_linenum = linenum; + return event; +} + +struct event *event_set_always_log_source(struct event *event) +{ + event->always_log_source = TRUE; + return event; +} + +struct event *event_set_min_log_level(struct event *event, enum log_type level) +{ + event->min_log_level = level; + event_recalculate_debug_level(event); + return event; +} + +enum log_type event_get_min_log_level(const struct event *event) +{ + return event->min_log_level; +} + +struct event *event_set_ptr(struct event *event, const char *key, void *value) +{ + struct event_pointer *p; + + if (!array_is_created(&event->pointers)) + p_array_init(&event->pointers, event->pool, 4); + else { + /* replace existing pointer if the key already exists */ + array_foreach_modifiable(&event->pointers, p) { + if (strcmp(p->key, key) == 0) { + p->value = value; + return event; + } + } + } + p = array_append_space(&event->pointers); + p->key = p_strdup(event->pool, key); + p->value = value; + return event; +} + +void *event_get_ptr(const struct event *event, const char *key) +{ + const struct event_pointer *p; + + if (!array_is_created(&event->pointers)) + return NULL; + array_foreach(&event->pointers, p) { + if (strcmp(p->key, key) == 0) + return p->value; + } + return NULL; +} + +struct event_category *event_category_find_registered(const char *name) +{ + struct event_category *cat; + + array_foreach_elem(&event_registered_categories_representative, cat) { + if (strcmp(cat->name, name) == 0) + return cat; + } + return NULL; +} + +static struct event_internal_category * +event_category_find_internal(const char *name) +{ + struct event_internal_category *internal; + + array_foreach_elem(&event_registered_categories_internal, internal) { + if (strcmp(internal->name, name) == 0) + return internal; + } + + return NULL; +} + +struct event_category *const * +event_get_registered_categories(unsigned int *count_r) +{ + return array_get(&event_registered_categories_representative, count_r); +} + +static void +event_category_add_to_array(struct event_internal_category *internal) +{ + struct event_category *representative = &internal->representative; + + array_push_back(&event_registered_categories_internal, &internal); + array_push_back(&event_registered_categories_representative, + &representative); +} + +static struct event_category * +event_category_register(struct event_category *category) +{ + struct event_internal_category *internal = category->internal; + event_category_callback_t *callback; + bool allocated; + + if (internal != NULL) + return &internal->representative; /* case 2 - see below */ + + /* register parent categories first */ + if (category->parent != NULL) + (void) event_category_register(category->parent); + + /* There are four cases we need to handle: + + 1) a new category is registered + 2) same category struct is re-registered - already handled above + internal NULL check + 3) different category struct is registered, but it is identical + to the previously registered one + 4) different category struct is registered, and it is different + from the previously registered one - a programming error */ + internal = event_category_find_internal(category->name); + if (internal == NULL) { + /* case 1: first time we saw this name - allocate new */ + internal = i_new(struct event_internal_category, 1); + if (category->parent != NULL) + internal->parent = category->parent->internal; + internal->name = i_strdup(category->name); + internal->refcount = 1; + internal->representative.name = internal->name; + internal->representative.parent = category->parent; + internal->representative.internal = internal; + + event_category_add_to_array(internal); + + allocated = TRUE; + } else { + /* case 3 or 4: someone registered this name before - share */ + if ((category->parent != NULL) && + (internal->parent != category->parent->internal)) { + /* case 4 */ + struct event_internal_category *other = + category->parent->internal; + + i_panic("event category parent mismatch detected: " + "category %p internal %p (%s), " + "internal parent %p (%s), public parent %p (%s)", + category, internal, internal->name, + internal->parent, internal->parent->name, + other, other->name); + } + + internal->refcount++; + + allocated = FALSE; + } + + category->internal = internal; + + if (!allocated) { + /* not the first registration of this category */ + return &internal->representative; + } + + array_foreach_elem(&event_category_callbacks, callback) T_BEGIN { + callback(&internal->representative); + } T_END; + + return &internal->representative; +} + +static bool +event_find_category(const struct event *event, + const struct event_category *category) +{ + struct event_internal_category *internal = category->internal; + struct event_category *cat; + + /* make sure we're always looking for a representative */ + i_assert(category == &internal->representative); + + array_foreach_elem(&event->categories, cat) { + if (cat == category) + return TRUE; + } + return FALSE; +} + +struct event * +event_add_categories(struct event *event, + struct event_category *const *categories) +{ + struct event_category *representative; + + if (!array_is_created(&event->categories)) + p_array_init(&event->categories, event->pool, 4); + + for (unsigned int i = 0; categories[i] != NULL; i++) { + representative = event_category_register(categories[i]); + if (!event_find_category(event, representative)) + array_push_back(&event->categories, &representative); + } + event_set_changed(event); + event_recalculate_debug_level(event); + return event; +} + +struct event * +event_add_category(struct event *event, struct event_category *category) +{ + struct event_category *const categories[] = { category, NULL }; + return event_add_categories(event, categories); +} + +struct event_field * +event_find_field_nonrecursive(const struct event *event, const char *key) +{ + struct event_field *field; + + if (!array_is_created(&event->fields)) + return NULL; + + array_foreach_modifiable(&event->fields, field) { + if (strcmp(field->key, key) == 0) + return field; + } + return NULL; +} + +const struct event_field * +event_find_field_recursive(const struct event *event, const char *key) +{ + const struct event_field *field; + + do { + if ((field = event_find_field_nonrecursive(event, key)) != NULL) + return field; + event = event->parent; + } while (event != NULL); + + /* check also the global event and its parents */ + event = event_get_global(); + while (event != NULL) { + if ((field = event_find_field_nonrecursive(event, key)) != NULL) + return field; + event = event->parent; + } + return NULL; +} + +static void +event_get_recursive_strlist(const struct event *event, pool_t pool, + const char *key, ARRAY_TYPE(const_string) *dest) +{ + const struct event_field *field; + const char *str; + + if (event == NULL) + return; + + field = event_find_field_nonrecursive(event, key); + if (field != NULL) { + if (field->value_type != EVENT_FIELD_VALUE_TYPE_STRLIST) { + /* Value type unexpectedly changed. Stop recursing. */ + return; + } + array_foreach_elem(&field->value.strlist, str) { + if (array_lsearch(dest, &str, i_strcmp_p) == NULL) { + if (pool != NULL) + str = p_strdup(pool, str); + array_push_back(dest, &str); + } + } + } + event_get_recursive_strlist(event->parent, pool, key, dest); +} + +const char * +event_find_field_recursive_str(const struct event *event, const char *key) +{ + const struct event_field *field; + + field = event_find_field_recursive(event, key); + if (field == NULL) + return NULL; + + switch (field->value_type) { + case EVENT_FIELD_VALUE_TYPE_STR: + return field->value.str; + case EVENT_FIELD_VALUE_TYPE_INTMAX: + return dec2str(field->value.intmax); + case EVENT_FIELD_VALUE_TYPE_TIMEVAL: + return t_strdup_printf("%"PRIdTIME_T".%u", + field->value.timeval.tv_sec, + (unsigned int)field->value.timeval.tv_usec); + case EVENT_FIELD_VALUE_TYPE_STRLIST: { + ARRAY_TYPE(const_string) list; + t_array_init(&list, 8); + /* This is a bit different, because it needs to be merging + all of the parent events' and global events' lists + together. */ + event_get_recursive_strlist(event, NULL, key, &list); + event_get_recursive_strlist(event_get_global(), NULL, + key, &list); + return t_array_const_string_join(&list, ","); + } + } + i_unreached(); +} + +static struct event_field * +event_get_field(struct event *event, const char *key, bool clear) +{ + struct event_field *field; + + field = event_find_field_nonrecursive(event, key); + if (field == NULL) { + if (!array_is_created(&event->fields)) + p_array_init(&event->fields, event->pool, 8); + field = array_append_space(&event->fields); + field->key = p_strdup(event->pool, key); + } else if (clear) { + i_zero(&field->value); + } + event_set_changed(event); + return field; +} + +struct event * +event_add_str(struct event *event, const char *key, const char *value) +{ + struct event_field *field; + + if (value == NULL) { + /* silently ignoring is perhaps better than assert-crashing? */ + return event; + } + + field = event_get_field(event, key, TRUE); + field->value_type = EVENT_FIELD_VALUE_TYPE_STR; + field->value.str = p_strdup(event->pool, value); + return event; +} + +struct event * +event_strlist_append(struct event *event, const char *key, const char *value) +{ + struct event_field *field = event_get_field(event, key, FALSE); + + if (field->value_type != EVENT_FIELD_VALUE_TYPE_STRLIST) + i_zero(&field->value); + field->value_type = EVENT_FIELD_VALUE_TYPE_STRLIST; + + if (!array_is_created(&field->value.strlist)) + p_array_init(&field->value.strlist, event->pool, 1); + + /* lets not add empty values there though */ + if (value == NULL) + return event; + + const char *str = p_strdup(event->pool, value); + if (array_lsearch(&field->value.strlist, &str, i_strcmp_p) == NULL) + array_push_back(&field->value.strlist, &str); + return event; +} + +struct event * +event_strlist_replace(struct event *event, const char *key, + const char *const *values, unsigned int count) +{ + struct event_field *field = event_get_field(event, key, TRUE); + field->value_type = EVENT_FIELD_VALUE_TYPE_STRLIST; + + for (unsigned int i = 0; i < count; i++) + event_strlist_append(event, key, values[i]); + return event; +} + +struct event * +event_strlist_copy_recursive(struct event *dest, const struct event *src, + const char *key) +{ + event_strlist_append(dest, key, NULL); + struct event_field *field = event_get_field(dest, key, FALSE); + i_assert(field != NULL); + event_get_recursive_strlist(src, dest->pool, key, + &field->value.strlist); + return dest; +} + +struct event * +event_add_int(struct event *event, const char *key, intmax_t num) +{ + struct event_field *field; + + field = event_get_field(event, key, TRUE); + field->value_type = EVENT_FIELD_VALUE_TYPE_INTMAX; + field->value.intmax = num; + return event; +} + +struct event * +event_add_int_nonzero(struct event *event, const char *key, intmax_t num) +{ + if (num != 0) + return event_add_int(event, key, num); + return event; +} + +struct event * +event_inc_int(struct event *event, const char *key, intmax_t num) +{ + struct event_field *field; + + field = event_find_field_nonrecursive(event, key); + if (field == NULL || field->value_type != EVENT_FIELD_VALUE_TYPE_INTMAX) + return event_add_int(event, key, num); + + field->value.intmax += num; + event_set_changed(event); + return event; +} + +struct event * +event_add_timeval(struct event *event, const char *key, + const struct timeval *tv) +{ + struct event_field *field; + + field = event_get_field(event, key, TRUE); + field->value_type = EVENT_FIELD_VALUE_TYPE_TIMEVAL; + field->value.timeval = *tv; + return event; +} + +struct event * +event_add_fields(struct event *event, + const struct event_add_field *fields) +{ + for (unsigned int i = 0; fields[i].key != NULL; i++) { + if (fields[i].value != NULL) + event_add_str(event, fields[i].key, fields[i].value); + else if (fields[i].value_timeval.tv_sec != 0) { + event_add_timeval(event, fields[i].key, + &fields[i].value_timeval); + } else { + event_add_int(event, fields[i].key, + fields[i].value_intmax); + } + } + return event; +} + +void event_field_clear(struct event *event, const char *key) +{ + event_add_str(event, key, ""); +} + +struct event *event_get_parent(const struct event *event) +{ + return event->parent; +} + +void event_get_create_time(const struct event *event, struct timeval *tv_r) +{ + *tv_r = event->tv_created; +} + +bool event_get_last_send_time(const struct event *event, struct timeval *tv_r) +{ + *tv_r = event->tv_last_sent; + return tv_r->tv_sec != 0; +} + +void event_get_last_duration(const struct event *event, + uintmax_t *duration_usecs_r) +{ + if (event->tv_last_sent.tv_sec == 0) { + *duration_usecs_r = 0; + return; + } + long long diff = timeval_diff_usecs(&event->tv_last_sent, + &event->tv_created); + i_assert(diff >= 0); + *duration_usecs_r = diff; +} + +const struct event_field * +event_get_fields(const struct event *event, unsigned int *count_r) +{ + if (!array_is_created(&event->fields)) { + *count_r = 0; + return NULL; + } + return array_get(&event->fields, count_r); +} + +struct event_category *const * +event_get_categories(const struct event *event, unsigned int *count_r) +{ + if (!array_is_created(&event->categories)) { + *count_r = 0; + return NULL; + } + return array_get(&event->categories, count_r); +} + +void event_send(struct event *event, struct failure_context *ctx, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + event_vsend(event, ctx, fmt, args); + va_end(args); +} + +void event_vsend(struct event *event, struct failure_context *ctx, + const char *fmt, va_list args) +{ + i_gettimeofday(&event->tv_last_sent); + + /* Skip adding user_cpu_usecs if not enabled. */ + if (event->ru_last.ru_utime.tv_sec != 0 || + event->ru_last.ru_utime.tv_usec != 0) { + struct rusage ru_current; + get_self_rusage(&ru_current); + long long udiff = timeval_diff_usecs(&ru_current.ru_utime, + &event->ru_last.ru_utime); + event_add_int(event, "user_cpu_usecs", udiff > 0 ? udiff : 0); + } + if (event_call_callbacks(event, EVENT_CALLBACK_TYPE_SEND, + ctx, fmt, args)) { + if (ctx->type != LOG_TYPE_DEBUG || + event->sending_debug_log) + i_log_typev(ctx, fmt, args); + } + event_send_abort(event); +} + +void event_send_abort(struct event *event) +{ + /* if the event is sent again, it needs a new name */ + i_free(event->sending_name); + if (event->passthrough) + event_unref(&event); +} + +static void +event_export_field_value(string_t *dest, const struct event_field *field) +{ + switch (field->value_type) { + case EVENT_FIELD_VALUE_TYPE_STR: + str_append_c(dest, EVENT_CODE_FIELD_STR); + str_append_tabescaped(dest, field->key); + str_append_c(dest, '\t'); + str_append_tabescaped(dest, field->value.str); + break; + case EVENT_FIELD_VALUE_TYPE_INTMAX: + str_append_c(dest, EVENT_CODE_FIELD_INTMAX); + str_append_tabescaped(dest, field->key); + str_printfa(dest, "\t%jd", field->value.intmax); + break; + case EVENT_FIELD_VALUE_TYPE_TIMEVAL: + str_append_c(dest, EVENT_CODE_FIELD_TIMEVAL); + str_append_tabescaped(dest, field->key); + str_printfa(dest, "\t%"PRIdTIME_T"\t%u", + field->value.timeval.tv_sec, + (unsigned int)field->value.timeval.tv_usec); + break; + case EVENT_FIELD_VALUE_TYPE_STRLIST: { + unsigned int count; + const char *const *strlist = + array_get(&field->value.strlist, &count); + str_append_c(dest, EVENT_CODE_FIELD_STRLIST); + str_append_tabescaped(dest, field->key); + str_printfa(dest, "\t%u", count); + for (unsigned int i = 0; i < count; i++) { + str_append_c(dest, '\t'); + str_append_tabescaped(dest, strlist[i]); + } + } + } +} + +void event_export(const struct event *event, string_t *dest) +{ + /* required fields: */ + str_printfa(dest, "%"PRIdTIME_T"\t%u", + event->tv_created.tv_sec, + (unsigned int)event->tv_created.tv_usec); + + /* optional fields: */ + if (event->source_filename != NULL) { + str_append_c(dest, '\t'); + str_append_c(dest, EVENT_CODE_SOURCE); + str_append_tabescaped(dest, event->source_filename); + str_printfa(dest, "\t%u", event->source_linenum); + } + if (event->always_log_source) { + str_append_c(dest, '\t'); + str_append_c(dest, EVENT_CODE_ALWAYS_LOG_SOURCE); + } + if (event->tv_last_sent.tv_sec != 0) { + str_printfa(dest, "\t%c%"PRIdTIME_T"\t%u", + EVENT_CODE_TV_LAST_SENT, + event->tv_last_sent.tv_sec, + (unsigned int)event->tv_last_sent.tv_usec); + } + if (event->sending_name != NULL) { + str_append_c(dest, '\t'); + str_append_c(dest, EVENT_CODE_SENDING_NAME); + str_append_tabescaped(dest, event->sending_name); + } + + if (array_is_created(&event->categories)) { + struct event_category *cat; + array_foreach_elem(&event->categories, cat) { + str_append_c(dest, '\t'); + str_append_c(dest, EVENT_CODE_CATEGORY); + str_append_tabescaped(dest, cat->name); + } + } + + if (array_is_created(&event->fields)) { + const struct event_field *field; + array_foreach(&event->fields, field) { + str_append_c(dest, '\t'); + event_export_field_value(dest, field); + } + } +} + +bool event_import(struct event *event, const char *str, const char **error_r) +{ + return event_import_unescaped(event, t_strsplit_tabescaped(str), + error_r); +} + +static bool event_import_tv(const char *arg_secs, const char *arg_usecs, + struct timeval *tv_r, const char **error_r) +{ + unsigned int usecs; + + if (str_to_time(arg_secs, &tv_r->tv_sec) < 0) { + *error_r = "Invalid timeval seconds parameter"; + return FALSE; + } + + if (arg_usecs == NULL) { + *error_r = "Timeval missing microseconds parameter"; + return FALSE; + } + if (str_to_uint(arg_usecs, &usecs) < 0 || usecs >= 1000000) { + *error_r = "Invalid timeval microseconds parameter"; + return FALSE; + } + tv_r->tv_usec = usecs; + return TRUE; +} + +static bool +event_import_strlist(struct event *event, struct event_field *field, + const char *const **_args, const char **error_r) +{ + const char *const *args = *_args; + unsigned int count, i; + + field->value_type = EVENT_FIELD_VALUE_TYPE_STRLIST; + if (str_to_uint(args[0], &count) < 0) { + *error_r = t_strdup_printf("Field '%s' has invalid count: '%s'", + field->key, args[0]); + return FALSE; + } + p_array_init(&field->value.strlist, event->pool, count); + for (i = 1; i <= count && args[i] != NULL; i++) { + const char *str = p_strdup(event->pool, args[i]); + array_push_back(&field->value.strlist, &str); + } + if (i < count) { + *error_r = t_strdup_printf("Field '%s' has too few values", + field->key); + return FALSE; + } + *_args += count; + return TRUE; +} + +static bool +event_import_field(struct event *event, enum event_code code, const char *arg, + const char *const **_args, const char **error_r) +{ + const char *const *args = *_args; + const char *error; + + if (*arg == '\0') { + *error_r = "Field name is missing"; + return FALSE; + } + struct event_field *field = event_get_field(event, arg, TRUE); + if (args[0] == NULL) { + *error_r = "Field value is missing"; + return FALSE; + } + switch (code) { + case EVENT_CODE_FIELD_INTMAX: + field->value_type = EVENT_FIELD_VALUE_TYPE_INTMAX; + if (str_to_intmax(*args, &field->value.intmax) < 0) { + *error_r = t_strdup_printf( + "Invalid field value '%s' number for '%s'", + *args, field->key); + return FALSE; + } + break; + case EVENT_CODE_FIELD_STR: + if (field->value_type == EVENT_FIELD_VALUE_TYPE_STR && + null_strcmp(field->value.str, *args) == 0) { + /* already identical value */ + break; + } + field->value_type = EVENT_FIELD_VALUE_TYPE_STR; + field->value.str = p_strdup(event->pool, *args); + break; + case EVENT_CODE_FIELD_TIMEVAL: + field->value_type = EVENT_FIELD_VALUE_TYPE_TIMEVAL; + if (!event_import_tv(args[0], args[1], + &field->value.timeval, &error)) { + *error_r = t_strdup_printf("Field '%s' value '%s': %s", + field->key, args[1], error); + return FALSE; + } + args++; + break; + case EVENT_CODE_FIELD_STRLIST: + if (!event_import_strlist(event, field, &args, error_r)) + return FALSE; + break; + default: + i_unreached(); + } + *_args = args; + return TRUE; +} + + +static bool +event_import_arg(struct event *event, const char *const **_args, + const char **error_r) +{ + const char *const *args = *_args; + const char *error, *arg = *args; + enum event_code code = arg[0]; + + arg++; + switch (code) { + case EVENT_CODE_ALWAYS_LOG_SOURCE: + event->always_log_source = TRUE; + break; + case EVENT_CODE_CATEGORY: { + struct event_category *category = + event_category_find_registered(arg); + if (category == NULL) { + *error_r = t_strdup_printf( + "Unregistered category: '%s'", arg); + return FALSE; + } + if (!array_is_created(&event->categories)) + p_array_init(&event->categories, event->pool, 4); + if (!event_find_category(event, category)) + array_push_back(&event->categories, &category); + break; + } + case EVENT_CODE_TV_LAST_SENT: + if (!event_import_tv(arg, args[1], &event->tv_last_sent, + &error)) { + *error_r = t_strdup_printf( + "Invalid tv_last_sent: %s", error); + return FALSE; + } + args++; + break; + case EVENT_CODE_SENDING_NAME: + i_free(event->sending_name); + event->sending_name = i_strdup(arg); + break; + case EVENT_CODE_SOURCE: { + unsigned int linenum; + + if (args[1] == NULL) { + *error_r = "Source line number missing"; + return FALSE; + } + if (str_to_uint(args[1], &linenum) < 0) { + *error_r = "Invalid Source line number"; + return FALSE; + } + event_set_source(event, arg, linenum, FALSE); + args++; + break; + } + case EVENT_CODE_FIELD_INTMAX: + case EVENT_CODE_FIELD_STR: + case EVENT_CODE_FIELD_STRLIST: + case EVENT_CODE_FIELD_TIMEVAL: { + args++; + if (!event_import_field(event, code, arg, &args, error_r)) + return FALSE; + break; + } + } + *_args = args; + return TRUE; +} + +bool event_import_unescaped(struct event *event, const char *const *args, + const char **error_r) +{ + const char *error; + + /* Event's create callback has already added service:<name> category. + This imported event may be coming from another service process + though, so clear it out. */ + if (array_is_created(&event->categories)) + array_clear(&event->categories); + + /* required fields: */ + if (args[0] == NULL) { + *error_r = "Missing required fields"; + return FALSE; + } + if (!event_import_tv(args[0], args[1], &event->tv_created, &error)) { + *error_r = t_strdup_printf("Invalid tv_created: %s", error); + return FALSE; + } + args += 2; + + /* optional fields: */ + while (*args != NULL) { + if (!event_import_arg(event, &args, error_r)) + return FALSE; + args++; + } + return TRUE; +} + +void event_register_callback(event_callback_t *callback) +{ + array_push_back(&event_handlers, &callback); +} + +void event_unregister_callback(event_callback_t *callback) +{ + event_callback_t *const *callbackp; + + array_foreach(&event_handlers, callbackp) { + if (*callbackp == callback) { + unsigned int idx = + array_foreach_idx(&event_handlers, callbackp); + array_delete(&event_handlers, idx, 1); + return; + } + } + i_unreached(); +} + +void event_category_register_callback(event_category_callback_t *callback) +{ + array_push_back(&event_category_callbacks, &callback); +} + +void event_category_unregister_callback(event_category_callback_t *callback) +{ + event_category_callback_t *const *callbackp; + + array_foreach(&event_category_callbacks, callbackp) { + if (*callbackp == callback) { + unsigned int idx = + array_foreach_idx(&event_category_callbacks, + callbackp); + array_delete(&event_category_callbacks, idx, 1); + return; + } + } + i_unreached(); +} + +static struct event_passthrough * +event_passthrough_set_append_log_prefix(const char *prefix) +{ + event_set_append_log_prefix(last_passthrough_event(), prefix); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_replace_log_prefix(const char *prefix) +{ + event_replace_log_prefix(last_passthrough_event(), prefix); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_set_name(const char *name) +{ + event_set_name(last_passthrough_event(), name); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_set_source(const char *filename, + unsigned int linenum, bool literal_fname) +{ + event_set_source(last_passthrough_event(), filename, + linenum, literal_fname); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_set_always_log_source(void) +{ + event_set_always_log_source(last_passthrough_event()); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_add_categories(struct event_category *const *categories) +{ + event_add_categories(last_passthrough_event(), categories); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_add_category(struct event_category *category) +{ + event_add_category(last_passthrough_event(), category); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_add_fields(const struct event_add_field *fields) +{ + event_add_fields(last_passthrough_event(), fields); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_add_str(const char *key, const char *value) +{ + event_add_str(last_passthrough_event(), key, value); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_strlist_append(const char *key, const char *value) +{ + event_strlist_append(last_passthrough_event(), key, value); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_strlist_replace(const char *key, const char *const *values, + unsigned int count) +{ + event_strlist_replace(last_passthrough_event(), key, values, count); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_add_int(const char *key, intmax_t num) +{ + event_add_int(last_passthrough_event(), key, num); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_add_int_nonzero(const char *key, intmax_t num) +{ + event_add_int_nonzero(last_passthrough_event(), key, num); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_add_timeval(const char *key, const struct timeval *tv) +{ + event_add_timeval(last_passthrough_event(), key, tv); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_inc_int(const char *key, intmax_t num) +{ + event_inc_int(last_passthrough_event(), key, num); + return &event_passthrough_vfuncs; +} + +static struct event_passthrough * +event_passthrough_clear_field(const char *key) +{ + event_field_clear(last_passthrough_event(), key); + return &event_passthrough_vfuncs; +} + +static struct event *event_passthrough_event(void) +{ + struct event *event = last_passthrough_event(); + event_last_passthrough = NULL; + return event; +} + +struct event_passthrough event_passthrough_vfuncs = { + .append_log_prefix = event_passthrough_set_append_log_prefix, + .replace_log_prefix = event_passthrough_replace_log_prefix, + .set_name = event_passthrough_set_name, + .set_source = event_passthrough_set_source, + .set_always_log_source = event_passthrough_set_always_log_source, + .add_categories = event_passthrough_add_categories, + .add_category = event_passthrough_add_category, + .add_fields = event_passthrough_add_fields, + .add_str = event_passthrough_add_str, + .add_int = event_passthrough_add_int, + .add_int_nonzero = event_passthrough_add_int_nonzero, + .add_timeval = event_passthrough_add_timeval, + .inc_int = event_passthrough_inc_int, + .strlist_append = event_passthrough_strlist_append, + .strlist_replace = event_passthrough_strlist_replace, + .clear_field = event_passthrough_clear_field, + .event = event_passthrough_event, +}; + +void event_enable_user_cpu_usecs(struct event *event) +{ + get_self_rusage(&event->ru_last); +} + +void lib_event_init(void) +{ + i_array_init(&event_handlers, 4); + i_array_init(&event_category_callbacks, 4); + i_array_init(&event_registered_categories_internal, 16); + i_array_init(&event_registered_categories_representative, 16); +} + +void lib_event_deinit(void) +{ + struct event_internal_category *internal; + + event_unset_global_debug_log_filter(); + event_unset_global_debug_send_filter(); + event_unset_global_core_log_filter(); + for (struct event *event = events; event != NULL; event = event->next) { + i_warning("Event %p leaked (parent=%p): %s:%u", + event, event->parent, + event->source_filename, event->source_linenum); + } + /* categories cannot be unregistered, so just free them here */ + array_foreach_elem(&event_registered_categories_internal, internal) { + i_free(internal->name); + i_free(internal); + } + array_free(&event_handlers); + array_free(&event_category_callbacks); + array_free(&event_registered_categories_internal); + array_free(&event_registered_categories_representative); + array_free(&global_event_stack); +} diff --git a/src/lib/lib-event.h b/src/lib/lib-event.h new file mode 100644 index 0000000..2059501 --- /dev/null +++ b/src/lib/lib-event.h @@ -0,0 +1,440 @@ +#ifndef LIB_EVENT_H +#define LIB_EVENT_H +/* event.h name is probably a bit too generic, so lets avoid using it. */ + +#include <sys/time.h> + +/* Field name for the reason_code string list. */ +#define EVENT_REASON_CODE "reason_code" + +struct event; +struct event_log_params; + +/* Hierarchical category of events. Each event can belong to multiple + categories. For example [ lib-storage/maildir, syscall/io ]. The categories + are expected to live as long as they're used in events. */ +struct event_category { + struct event_category *parent; + const char *name; + + /* non-NULL if this category has been registered + + Do NOT dereference outside of event code in src/lib. + + At any point in time it is safe to (1) check the pointer for + NULL/non-NULL to determine if this particular category instance + has been registered, and (2) compare two categories' internal + pointers to determine if they represent the same category. */ + void *internal; +}; + +enum event_field_value_type { + EVENT_FIELD_VALUE_TYPE_STR, + EVENT_FIELD_VALUE_TYPE_INTMAX, + EVENT_FIELD_VALUE_TYPE_TIMEVAL, + EVENT_FIELD_VALUE_TYPE_STRLIST, +}; + +struct event_field { + const char *key; + enum event_field_value_type value_type; + struct { + const char *str; + intmax_t intmax; + struct timeval timeval; + ARRAY_TYPE(const_string) strlist; + } value; +}; + +struct event_add_field { + const char *key; + /* The first non-0/NULL value is used. */ + const char *value; + intmax_t value_intmax; + struct timeval value_timeval; +}; + +struct event_passthrough { + /* wrappers to event_set_*() and event_add_*() for passthrough events, + so these can be chained like: + event_create_passthrough(parent)->name("name")->...->event() */ + struct event_passthrough * + (*append_log_prefix)(const char *prefix); + struct event_passthrough * + (*replace_log_prefix)(const char *prefix); + struct event_passthrough * + (*set_name)(const char *name); + struct event_passthrough * + (*set_source)(const char *filename, + unsigned int linenum, bool literal_fname); + struct event_passthrough * + (*set_always_log_source)(void); + + struct event_passthrough * + (*add_categories)(struct event_category *const *categories); + struct event_passthrough * + (*add_category)(struct event_category *category); + struct event_passthrough * + (*add_fields)(const struct event_add_field *fields); + + struct event_passthrough * + (*add_str)(const char *key, const char *value); + struct event_passthrough * + (*add_int)(const char *key, intmax_t num); + struct event_passthrough * + (*add_int_nonzero)(const char *key, intmax_t num); + struct event_passthrough * + (*add_timeval)(const char *key, const struct timeval *tv); + + struct event_passthrough * + (*inc_int)(const char *key, intmax_t num); + + struct event_passthrough * + (*strlist_append)(const char *key, const char *value); + struct event_passthrough * + (*strlist_replace)(const char *key, const char *const *value, + unsigned int count); + + struct event_passthrough * + (*clear_field)(const char *key); + + struct event *(*event)(void); +}; + +typedef const char * +event_log_prefix_callback_t(void *context); +typedef const char * +event_log_message_callback_t(void *context, enum log_type log_type, + const char *message); + +/* Returns TRUE if the event has all the categories that the "other" event has + (and maybe more). */ +bool event_has_all_categories(struct event *event, const struct event *other); +/* Returns TRUE if the event has all the fields that the "other" event has + (and maybe more). Only the fields in the events themselves are checked. + Parent events' fields are not checked. */ +bool event_has_all_fields(struct event *event, const struct event *other); + +/* Returns the source event duplicated into a new event. Event pointers are + dropped. */ +struct event *event_dup(const struct event *source); +/* Returns a flattened version of the source event. + Both categories and fields will be flattened. + A new reference to the source event is returned if no flattening was + needed. Event pointers are dropped if a new event was created. */ +struct event *event_flatten(struct event *src); +/* Returns a minimized version of the source event. + Remove parents with no fields or categories, attempt to flatten fields + and categories to avoid sending one-off parent events. (There is a more + detailed description in a comment above the function implementation.) + A new reference to the source event is returned if no simplification + occured. Event pointers are dropped if a new event was created. */ +struct event *event_minimize(struct event *src); +/* Copy all categories from source to dest. + Only the categories in source event itself are copied. + Parent events' categories aren't copied. */ +void event_copy_categories(struct event *to, struct event *from); +/* Copy all fields from source to dest. + Only the fields in source event itself are copied. + Parent events' fields aren't copied. */ +void event_copy_fields(struct event *to, struct event *from); + +/* Create a new empty event under the parent event, or NULL for root event. */ +struct event *event_create(struct event *parent, const char *source_filename, + unsigned int source_linenum); +#define event_create(parent) \ + event_create((parent), __FILE__, __LINE__) +/* This is a temporary "passthrough" event. Its main purpose is to make it + easier to create temporary events as part of the event parameter in + e_error(), e_warning(), e_info() or e_debug(). These passthrough events are + automatically freed when the e_*() call is finished. Because this makes the + freeing less obvious, it should be avoided outside e_*()'s event parameter. + + The passthrough events also change the API to be more convenient towards + being used in a parameter. Instead of having to use e.g. + event_add_str(event_set_name(event_create(parent), "name"), "key", "value") + the event_passthrough API can be a bit more readable as: + event_create_passthrough(parent)->set_name("name")-> + add_str("key", "value")->event(). The passthrough event is converted to + a normal event at the end with the event() call. Note that this API works + by modifying the last created passthrough event, so it's not possible to + have multiple passthrough events created in parallel. */ +struct event_passthrough * +event_create_passthrough(struct event *parent, const char *source_filename, + unsigned int source_linenum); +#define event_create_passthrough(parent) \ + event_create_passthrough((parent), __FILE__, __LINE__) + +/* Reference the event. Returns the event parameter. */ +struct event *event_ref(struct event *event); +/* Unreference the event. If the reference count drops to 0, the event is + freed. The current global event's refcount must not drop to 0. */ +void event_unref(struct event **event); + +/* Set the event to be the global event and push it at the top of the global + event stack. Returns the event parameter. The event must be explicitly + popped before it's freed. + + The global event acts as the root event for all the events while they are + being emitted. The global events don't permanently affect the event + hierarchy. The global events are typically used to add extra fields to all + emitted events while some specific work is running. + + For example the global event can be "IMAP command SELECT", which can be used + for filtering events that happen while the SELECT command is being executed. + However, for the created struct mailbox the parent event should be the + mail_user, not the SELECT command. (If the mailbox used SELECT command as + the parent event, then any future event emitted via the mailbox event would + show SELECT command as the parent, even after SELECT had already finished.) + + The global event works the same as if all the events' roots were instead + pointing to the global event. Global events don't affect log prefixes. + + If ioloop contexts are used, the global events will automatically follow the + contexts. Any global events pushed while running in a context are popped + out when the context is deactivated, and pushed back when context is + activated again. + + The created global events should use event_get_global() as their parent + event. Only the last pushed global event is used. */ +struct event *event_push_global(struct event *event); +/* Pop the current global event and set the global event to the next one at + the top of the stack. Assert-crash if the current global event isn't the + given event parameter. Returns the next (now activated) global event in the + stack, or NULL if the stack is now empty. */ +struct event *event_pop_global(struct event *event); +/* Returns the current global event. */ +struct event *event_get_global(void); + +/* Shortcut to create and push a global event and set its reason_code field. */ +struct event_reason * +event_reason_begin(const char *reason_code, const char *source_filename, + unsigned int source_linenum); +#define event_reason_begin(reason_code) \ + event_reason_begin(reason_code, __FILE__, __LINE__) +/* Finish the reason event. It pops the global event, which means it must be + at the top of the stack. */ +void event_reason_end(struct event_reason **reason); +/* Generate a reason code as <module>:<name>. This function does some + sanity checks and conversions to make sure the reason codes are reasonable: + + - Assert-crash if module has space, '-', ':' or uppercase characters. + - Assert-crash if module is empty + - Convert name to lowercase. + - Replace all space and '-' in name with '_'. + - Assert-crash if name has ':' + - assert-crash if name is empty +*/ +const char *event_reason_code(const char *module, const char *name); +/* Same as event_reason_code(), but concatenate name_prefix and name. + The name_prefix must not contain spaces, '-', ':' or uppercase characters. */ +const char *event_reason_code_prefix(const char *module, + const char *name_prefix, const char *name); + +/* Set the appended log prefix string for this event. All the parent events' + log prefixes will be concatenated together when logging. The log type + text (e.g. "Info: ") will be inserted before appended log prefixes (but + after replaced log prefix). + + Clears log_prefix callback. + */ +struct event * +event_set_append_log_prefix(struct event *event, const char *prefix); +/* Replace the full log prefix string for this event. The parent events' log + prefixes won't be used. Also, any parent event's message amendment callback + is not used. + + Clears log_prefix callback. +*/ +struct event *event_replace_log_prefix(struct event *event, const char *prefix); + +/* Drop count prefixes from parents when this event is used for logging. This + does not affect the parent events. This only counts actual prefixes and not + parents. If the count is higher than the actual number of prefixes added by + parents, all will be dropped. */ +struct event * +event_drop_parent_log_prefixes(struct event *event, unsigned int count); + +/* Sets event prefix callback, sets log_prefix empty */ +struct event * +event_set_log_prefix_callback(struct event *event, bool replace, + event_log_prefix_callback_t *callback, + void *context); +#define event_set_log_prefix_callback(event, replace, callback, context) \ + event_set_log_prefix_callback(event, replace, \ + (event_log_prefix_callback_t*)callback, TRUE ? context : \ + CALLBACK_TYPECHECK(callback, const char *(*)(typeof(context)))) + +/* Sets event message amendment callback */ +struct event * +event_set_log_message_callback(struct event *event, + event_log_message_callback_t *callback, + void *context); +#define event_set_log_message_callback(event, callback, context) \ + event_set_log_message_callback(event, \ + (event_log_message_callback_t*)callback, TRUE ? context : \ + CALLBACK_TYPECHECK(callback, \ + const char *(*)(typeof(context), enum log_type, \ + const char *))) + +/* Unsets the event message amendment callback. */ +void event_unset_log_message_callback(struct event *event, + event_log_message_callback_t *callback, + void *context); +#define event_unset_log_message_callback(event, callback, context) \ + event_unset_log_message_callback(event, \ + (event_log_message_callback_t*)callback, context) + +/* Set the event's name. The name is specific to a single sending of an event, + and it'll be automatically cleared once the event is sent. This should + typically be used only in a parameter to e_debug(), etc. */ +struct event * +event_set_name(struct event *event, const char *name); +/* Set the source filename:linenum to the event. If literal_fname==TRUE, + it's assumed that __FILE__ has been used and the pointer is stored directly, + otherwise the filename is strdup()ed. */ +struct event * +event_set_source(struct event *event, const char *filename, + unsigned int linenum, bool literal_fname); +/* Always include the source path:line in the log replies. This is + especially useful when logging about unexpected syscall failures, because + it allow quickly finding which of the otherwise identical syscalls in the + code generated the error. */ +struct event *event_set_always_log_source(struct event *event); +/* Set minimum normal log level for the event. By default events with INFO + level and higher are logged. This can be used to easily hide even the INFO + log lines unless some verbose-setting is enabled. + + Note that this functionality is mostly independent of debug logging. + Don't use this to enable debug log - use event_set_forced_debug() instead. */ +struct event *event_set_min_log_level(struct event *event, enum log_type level); +enum log_type event_get_min_log_level(const struct event *event); + +/* Add an internal pointer to an event. It can be looked up only with + event_get_ptr(). The keys are in their own namespace and won't conflict + with event fields. The pointers are specific to this specific event only - + they will be dropped from any duplicated/flattened/minimized events. */ +struct event *event_set_ptr(struct event *event, const char *key, void *value); +/* Return a pointer set with event_set_ptr(), or NULL if it doesn't exist. + The pointer is looked up only from the event itself, not its parents. */ +void *event_get_ptr(const struct event *event, const char *key); + +/* Add NULL-terminated list of categories to the event. The categories pointer + doesn't need to stay valid afterwards, but the event_category structs + themselves must be. Returns the event parameter. */ +struct event * +event_add_categories(struct event *event, + struct event_category *const *categories); +/* Add a single category to the event. */ +struct event * +event_add_category(struct event *event, struct event_category *category); + +/* Add key=value field to the event. If a key already exists, it's replaced. + Child events automatically inherit key=values from their parents at the + time the event is sent. So changing a key in parent will change the values + in the child events as well, unless the key has been overwritten in the + child event. Setting the value to "" is the same as event_field_clear(). + Returns the event parameter. */ +struct event * +event_add_str(struct event *event, const char *key, const char *value); +struct event * +event_add_int(struct event *event, const char *key, intmax_t num); +/* Adds int value to event if it is non-zero */ +struct event * +event_add_int_nonzero(struct event *event, const char *key, intmax_t num); +/* Increase the key's value. If it's not set or isn't an integer type, + initialize the value to num. */ +struct event * +event_inc_int(struct event *event, const char *key, intmax_t num); +struct event * +event_add_timeval(struct event *event, const char *key, + const struct timeval *tv); +/* Append new value to list. If the key is not a list, it will + be cleared first. NULL values are ignored. Duplicate values are ignored. */ +struct event * +event_strlist_append(struct event *event, const char *key, const char *value); +/* Replace value with this strlist. */ +struct event * +event_strlist_replace(struct event *event, const char *key, + const char *const *value, unsigned int count); +/* Copy the string list from src and its parents to dest. This can be especially + useful to copy the current global events' reason_codes to a more permanent + (e.g. async) event that can exist after the global events are popped out. */ +struct event * +event_strlist_copy_recursive(struct event *dest, const struct event *src, + const char *key); +/* Same as event_add_str/int(), but do it via event_field struct. The fields + terminates with key=NULL. Returns the event parameter. */ +struct event * +event_add_fields(struct event *event, const struct event_add_field *fields); +/* Mark a field as nonexistent. If a parent event has the field set, this + allows removing it from the child event. Using an event filter with e.g. + "key=*" won't match this field anymore, although it's still visible in + event_find_field*() and event_get_fields(). This is the same as using + event_add_str() with value="". */ +void event_field_clear(struct event *event, const char *key); + +/* Returns the parent event, or NULL if it doesn't exist. */ +struct event *event_get_parent(const struct event *event); +/* Get the event's creation time. */ +void event_get_create_time(const struct event *event, struct timeval *tv_r); +/* Get the time when the event was last sent. Returns TRUE if time was + returned, FALSE if event has never been sent. */ +bool event_get_last_send_time(const struct event *event, struct timeval *tv_r); +/* Get the event duration field in microseconds. This is calculated from + the event's last sent time. */ +void event_get_last_duration(const struct event *event, + uintmax_t *duration_usecs_r); +/* Returns field for a given key, or NULL if it doesn't exist. */ +struct event_field * +event_find_field_nonrecursive(const struct event *event, const char *key); +/* Returns field for a given key, or NULL if it doesn't exist. If the key + isn't found from the event itself, find it from parent events, including + from the global event. */ +const struct event_field * +event_find_field_recursive(const struct event *event, const char *key); +/* Same as event_find_field(), but return the value converted to a string. + If the field isn't stored as a string, the result is allocated from + data stack. */ +const char * +event_find_field_recursive_str(const struct event *event, const char *key); +/* Returns all key=value fields that the event has. + Parent events' fields aren't returned. */ +const struct event_field * +event_get_fields(const struct event *event, unsigned int *count_r); +/* Return all categories that the event has. + Parent events' categories aren't returned. */ +struct event_category *const * +event_get_categories(const struct event *event, unsigned int *count_r); + +/* Export the event into a tabescaped string, so its fields are separated + with TABs and there are no NUL, CR or LF characters. */ +void event_export(const struct event *event, string_t *dest); +/* Import event. The string is expected to be generated by event_export(). + All the used categories must already be registered. + Returns TRUE on success, FALSE on invalid string. */ +bool event_import(struct event *event, const char *str, const char **error_r); +/* Same as event_import(), but string is already split into an array + of strings via *_strsplit_tabescaped(). */ +bool event_import_unescaped(struct event *event, const char *const *args, + const char **error_r); + +/* The event wasn't sent after all - free everything related to it. + Most importantly this frees any passthrough events. Typically this shouldn't + need to be called. */ +void event_send_abort(struct event *event); + +/* Enable "user_cpu_usecs" event field to event by getting current resource + usage which will be used in consequent event_send() to calculate + cpu time. This function can be called multiple times to update the current + resource usage. + + The "user_cpu_usecs" field is automatically inherited by passthrough events, + but not full events. +*/ +void event_enable_user_cpu_usecs(struct event *event); + +void lib_event_init(void); +void lib_event_deinit(void); + +#endif diff --git a/src/lib/lib-signals.c b/src/lib/lib-signals.c new file mode 100644 index 0000000..6ac657a --- /dev/null +++ b/src/lib/lib-signals.c @@ -0,0 +1,702 @@ +/* Copyright (c) 2001-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "ioloop.h" +#include "write-full.h" +#include "llist.h" +#include "lib-signals.h" + +#include <stdio.h> +#include <signal.h> +#include <unistd.h> + +#define MAX_SIGNAL_VALUE 63 + +#define SIGNAL_IS_TERMINAL(signo) \ + ((signo) == SIGINT || (signo) == SIGQUIT || (signo) == SIGTERM) + +#if !defined(SA_SIGINFO) && !defined(SI_NOINFO) +/* without SA_SIGINFO we don't know what the real code is. we need SI_NOINFO + to make sure lib_signal_code_to_str() returns "". */ +# define SI_NOINFO -1 +#endif + +struct signal_ioloop { + struct signal_ioloop *prev, *next; + + int refcount; + struct ioloop *ioloop; + struct io *io; +}; + +struct signal_handler { + signal_handler_t *handler; + void *context; + + enum libsig_flags flags; + struct signal_handler *next; + struct signal_ioloop *sig_ioloop; + + bool expected:1; + bool shadowed:1; +}; + +volatile unsigned int signal_term_counter = 0; + +/* Remember that these are accessed inside signal handler which may be called + even while we're initializing/deinitializing. Try hard to keep everything + in consistent state. */ +static struct signal_handler *signal_handlers[MAX_SIGNAL_VALUE+1] = { NULL, }; +static int sig_pipe_fd[2] = { -1, -1 }; + +static bool signals_initialized = FALSE; +static unsigned int signals_expected = 0; +static struct signal_ioloop *signal_ioloops = NULL; + +static siginfo_t pending_signals[MAX_SIGNAL_VALUE+1]; +static ARRAY(siginfo_t) pending_shadowed_signals; +static bool have_pending_signals = FALSE; +static bool have_missing_ioloops = FALSE; +static bool ioloop_switched = FALSE; + +static void signal_read(void *context); + +const char *lib_signal_code_to_str(int signo, int sicode) +{ + /* common */ + switch (sicode) { +#ifdef SI_NOINFO + case SI_NOINFO: + return ""; +#endif + case SI_USER: + return "kill"; +#ifdef SI_KERNEL + case SI_KERNEL: + return "kernel"; +#endif + case SI_TIMER: + return "timer"; + } + + /* If SEGV_MAPERR is supported, the rest of them must be too. + FreeBSD 6 at least doesn't support these. */ +#ifdef SEGV_MAPERR + switch (signo) { + case SIGSEGV: + switch (sicode) { + case SEGV_MAPERR: + return "address not mapped"; + case SEGV_ACCERR: + return "invalid permissions"; + } + break; + case SIGBUS: + switch (sicode) { + case BUS_ADRALN: + return "invalid address alignment"; +#ifdef BUS_ADRERR /* for OSX 10.3 */ + case BUS_ADRERR: + return "nonexistent physical address"; +#endif +#ifdef BUS_OBJERR /* for OSX 10.3 */ + case BUS_OBJERR: + return "object-specific hardware error"; +#endif + } + } +#endif + return t_strdup_printf("unknown %d", sicode); +} + +#ifdef SA_SIGINFO +static void sig_handler(int signo, siginfo_t *si, void *context ATTR_UNUSED) +#else +static void sig_handler(int signo) +#endif +{ + struct signal_handler *h; + int saved_errno; + char c = 0; + +#if defined(SI_NOINFO) || !defined(SA_SIGINFO) +#ifndef SA_SIGINFO + siginfo_t *si = NULL; +#endif + siginfo_t tmp_si; + + if (si == NULL) { + /* Solaris can leave this to NULL */ + i_zero(&tmp_si); + tmp_si.si_signo = signo; + tmp_si.si_code = SI_NOINFO; + si = &tmp_si; + } +#endif + + if (signo < 0 || signo > MAX_SIGNAL_VALUE) + return; + + if (SIGNAL_IS_TERMINAL(signo)) + signal_term_counter++; + + /* remember that we're inside a signal handler which might have been + called at any time. don't do anything that's unsafe. we might also + get interrupted by another signal while inside this handler. */ + saved_errno = errno; + for (h = signal_handlers[signo]; h != NULL; h = h->next) { + if ((h->flags & LIBSIG_FLAG_DELAYED) == 0) + h->handler(si, h->context); + else if (pending_signals[signo].si_signo == 0) { + pending_signals[signo] = *si; + if (!have_pending_signals) { + if (write(sig_pipe_fd[1], &c, 1) != 1) { + lib_signals_syscall_error( + "signal: write(sigpipe) failed: "); + } + have_pending_signals = TRUE; + } + } + } + errno = saved_errno; +} + +#ifdef SA_SIGINFO +static void sig_ignore(int signo ATTR_UNUSED, siginfo_t *si ATTR_UNUSED, + void *context ATTR_UNUSED) +#else +static void sig_ignore(int signo ATTR_UNUSED) +#endif +{ + /* if we used SIG_IGN instead of this function, + the system call might be restarted */ +} + +static struct signal_ioloop * +lib_signals_ioloop_find(struct ioloop *ioloop) +{ + struct signal_ioloop *l; + + for (l = signal_ioloops; l != NULL; l = l->next) { + if (l->ioloop == ioloop) + break; + } + return l; +} + +static void lib_signals_init_io(struct signal_ioloop *l) +{ + i_assert(sig_pipe_fd[0] != -1); + + l->io = io_add_to(l->ioloop, sig_pipe_fd[0], IO_READ, signal_read, NULL); + io_set_never_wait_alone(l->io, signals_expected == 0); +} + +static struct signal_ioloop * +lib_signals_ioloop_ref(struct ioloop *ioloop) +{ + struct signal_ioloop *l; + + l = lib_signals_ioloop_find(ioloop); + if (l == NULL) { + l = i_new(struct signal_ioloop, 1); + l->ioloop = ioloop; + lib_signals_init_io(l); + DLLIST_PREPEND(&signal_ioloops, l); + } + l->refcount++; + return l; +} + +static void lib_signals_ioloop_unref(struct signal_ioloop **_sig_ioloop) +{ + struct signal_ioloop *sig_ioloop = *_sig_ioloop; + + *_sig_ioloop = NULL; + + if (sig_ioloop == NULL) + return; + i_assert(sig_ioloop->refcount > 0); + if (--sig_ioloop->refcount > 0) + return; + io_remove(&sig_ioloop->io); + DLLIST_REMOVE(&signal_ioloops, sig_ioloop); + i_free(sig_ioloop); +} + +static void signal_handler_switch_ioloop(struct signal_handler *h) +{ + lib_signals_ioloop_unref(&h->sig_ioloop); + if (current_ioloop != NULL) + h->sig_ioloop = lib_signals_ioloop_ref(current_ioloop); + else + have_missing_ioloops = TRUE; +} + +static void signal_handler_free(struct signal_handler *h) +{ + lib_signals_ioloop_unref(&h->sig_ioloop); + i_free(h); +} + +static void signal_handle_shadowed(void) +{ + const siginfo_t *sis; + unsigned int count, i; + + if (!array_is_created(&pending_shadowed_signals) || + array_count(&pending_shadowed_signals) == 0) + return; + + sis = array_get(&pending_shadowed_signals, &count); + for (i = 0; i < count; i++) { + struct signal_handler *h; + bool shadowed = FALSE; + + i_assert(sis[i].si_signo > 0); + for (h = signal_handlers[sis[i].si_signo]; h != NULL; + h = h->next) { + i_assert(h->sig_ioloop != NULL); + if ((h->flags & LIBSIG_FLAG_DELAYED) == 0 || + (h->flags & LIBSIG_FLAG_IOLOOP_AUTOMOVE) != 0) + continue; + if (h->shadowed && + h->sig_ioloop->ioloop != current_ioloop) { + shadowed = TRUE; + continue; + } + /* handler can be called now */ + h->shadowed = FALSE; + h->handler(&sis[i], h->context); + } + if (!shadowed) { + /* no handlers are shadowed anymore; delete the signal + info */ + array_delete(&pending_shadowed_signals, i, 1); + sis = array_get(&pending_shadowed_signals, &count); + } + } +} + +static void signal_check_shadowed(void) +{ + struct signal_ioloop *sig_ioloop; + + if (!array_is_created(&pending_shadowed_signals) || + array_count(&pending_shadowed_signals) == 0) + return; + + sig_ioloop = lib_signals_ioloop_find(current_ioloop); + if (sig_ioloop != NULL) + io_set_pending(sig_ioloop->io); +} + +static void signal_shadow(int signo, const siginfo_t *si) +{ + const siginfo_t *sis; + unsigned int count, i; + + /* remember last signal info for handlers that cannot run in + current ioloop */ + if (!array_is_created(&pending_shadowed_signals)) + i_array_init(&pending_shadowed_signals, 4); + sis = array_get(&pending_shadowed_signals, &count); + for (i = 0; i < count; i++) { + i_assert(sis[i].si_signo != 0); + if (sis[i].si_signo == signo) + break; + } + array_idx_set(&pending_shadowed_signals, i, si); +} + +static void ATTR_NULL(1) signal_read(void *context ATTR_UNUSED) +{ + siginfo_t signals[MAX_SIGNAL_VALUE+1]; + sigset_t fullset, oldset; + struct signal_handler *h; + char buf[64]; + int signo; + ssize_t ret; + + if (ioloop_switched) { + ioloop_switched = FALSE; + /* handle any delayed signal handlers that emerged from the + shadow */ + signal_handle_shadowed(); + } + + if (sigfillset(&fullset) < 0) + i_fatal("sigfillset() failed: %m"); + if (sigprocmask(SIG_BLOCK, &fullset, &oldset) < 0) + i_fatal("sigprocmask() failed: %m"); + + /* typically we should read only a single byte, but if a signal is sent + while signal handler is running we might get more. */ + ret = read(sig_pipe_fd[0], buf, sizeof(buf)); + if (ret > 0) { + memcpy(signals, pending_signals, sizeof(signals)); + memset(pending_signals, 0, sizeof(pending_signals)); + have_pending_signals = FALSE; + } else if (ret < 0) { + if (errno != EAGAIN) + i_fatal("read(sigpipe) failed: %m"); + } else { + i_fatal("read(sigpipe) failed: EOF"); + } + if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0) + i_fatal("sigprocmask() failed: %m"); + + if (ret < 0) + return; + + /* call the delayed handlers after signals are copied and unblocked */ + for (signo = 0; signo < MAX_SIGNAL_VALUE; signo++) { + bool shadowed = FALSE; + + if (signals[signo].si_signo == 0) + continue; + + for (h = signal_handlers[signo]; h != NULL; h = h->next) { + i_assert(h->sig_ioloop != NULL); + if ((h->flags & LIBSIG_FLAG_DELAYED) == 0) { + /* handler already called immediately in signal + context */ + continue; + } + if ((h->flags & LIBSIG_FLAG_IOLOOP_AUTOMOVE) == 0 && + h->sig_ioloop->ioloop != current_ioloop) { + /* cannot run handler in current ioloop + (shadowed) */ + h->shadowed = TRUE; + shadowed = TRUE; + continue; + } + /* handler can be called now */ + h->handler(&signals[signo], h->context); + } + + if (shadowed) { + /* remember last signal info for handlers that cannot + run in current ioloop (shadowed) */ + signal_shadow(signo, &signals[signo]); + } + } +} + +static void lib_signals_update_expected_signals(bool expected) +{ + struct signal_ioloop *sig_ioloop; + + if (expected) + signals_expected++; + else { + i_assert(signals_expected > 0); + signals_expected--; + } + + sig_ioloop = signal_ioloops; + for (; sig_ioloop != NULL; sig_ioloop = sig_ioloop->next) { + if (sig_ioloop->io != NULL) { + io_set_never_wait_alone(sig_ioloop->io, + signals_expected == 0); + } + } +} + +static void lib_signals_ioloop_switch(void) +{ + struct signal_handler *h; + + if (current_ioloop == NULL || sig_pipe_fd[0] <= 0) + return; + + /* initialize current_ioloop for signal handlers created before the + first ioloop. */ + for (int signo = 0; signo < MAX_SIGNAL_VALUE; signo++) { + for (h = signal_handlers[signo]; h != NULL; h = h->next) { + if ((h->flags & LIBSIG_FLAG_IOLOOP_AUTOMOVE) != 0) + lib_signals_ioloop_unref(&h->sig_ioloop); + if (h->sig_ioloop == NULL) + h->sig_ioloop = lib_signals_ioloop_ref(current_ioloop); + } + } + have_missing_ioloops = FALSE; +} + +static void lib_signals_ioloop_switched(struct ioloop *prev_ioloop ATTR_UNUSED) +{ + ioloop_switched = TRUE; + + lib_signals_ioloop_switch(); + + /* check whether we can now handle any shadowed delayed signals */ + signal_check_shadowed(); +} + +static void lib_signals_ioloop_destroyed(struct ioloop *ioloop) +{ + struct signal_ioloop *sig_ioloop; + + sig_ioloop = lib_signals_ioloop_find(ioloop); + if (sig_ioloop != NULL) { + io_remove(&sig_ioloop->io); + sig_ioloop->ioloop = NULL; + } +} + +void lib_signals_ioloop_detach(void) +{ + struct signal_handler *h; + + for (int signo = 0; signo < MAX_SIGNAL_VALUE; signo++) { + for (h = signal_handlers[signo]; h != NULL; h = h->next) { + if (h->sig_ioloop != NULL) { + lib_signals_ioloop_unref(&h->sig_ioloop); + have_missing_ioloops = TRUE; + } + } + } +} + +void lib_signals_ioloop_attach(void) +{ + if (have_missing_ioloops) + lib_signals_ioloop_switch(); +} + +static void lib_signals_set(int signo, enum libsig_flags flags) +{ + struct sigaction act; + + if (sigemptyset(&act.sa_mask) < 0) + i_fatal("sigemptyset(): %m"); +#ifdef SA_SIGINFO + act.sa_flags = SA_SIGINFO; + act.sa_sigaction = sig_handler; +#else + act.sa_flags = 0; + act.sa_handler = sig_handler; +#endif + if ((flags & LIBSIG_FLAG_RESTART) != 0) + act.sa_flags |= SA_RESTART; + if (sigaction(signo, &act, NULL) < 0) + i_fatal("sigaction(%d): %m", signo); +} + +void lib_signals_set_handler(int signo, enum libsig_flags flags, + signal_handler_t *handler, void *context) +{ + struct signal_handler *h; + + i_assert(handler != NULL); + + if (signo < 0 || signo > MAX_SIGNAL_VALUE) { + i_panic("Trying to set signal %d handler, but max is %d", + signo, MAX_SIGNAL_VALUE); + } + + if (signal_handlers[signo] == NULL && signals_initialized) + lib_signals_set(signo, flags); + + h = i_new(struct signal_handler, 1); + h->handler = handler; + h->context = context; + h->flags = flags; + + /* atomically set to signal_handlers[] list */ + h->next = signal_handlers[signo]; + signal_handlers[signo] = h; + + if ((flags & LIBSIG_FLAG_DELAYED) != 0 && sig_pipe_fd[0] == -1) { + /* first delayed handler */ + if (pipe(sig_pipe_fd) < 0) + i_fatal("pipe() failed: %m"); + fd_set_nonblock(sig_pipe_fd[0], TRUE); + fd_set_nonblock(sig_pipe_fd[1], TRUE); + fd_close_on_exec(sig_pipe_fd[0], TRUE); + fd_close_on_exec(sig_pipe_fd[1], TRUE); + } + signal_handler_switch_ioloop(h); +} + +static void lib_signals_ignore_forced(int signo, bool restart_syscalls) +{ + struct sigaction act; + + if (sigemptyset(&act.sa_mask) < 0) + i_fatal("sigemptyset(): %m"); + if (restart_syscalls) { + act.sa_flags = SA_RESTART; + act.sa_handler = SIG_IGN; + } else { +#ifdef SA_SIGINFO + act.sa_flags = SA_SIGINFO; + act.sa_sigaction = sig_ignore; +#else + act.sa_flags = 0; + act.sa_handler = sig_ignore; +#endif + } + + if (sigaction(signo, &act, NULL) < 0) + i_fatal("sigaction(%d): %m", signo); +} + +void lib_signals_ignore(int signo, bool restart_syscalls) +{ + if (signo < 0 || signo > MAX_SIGNAL_VALUE) { + i_panic("Trying to ignore signal %d, but max is %d", + signo, MAX_SIGNAL_VALUE); + } + + i_assert(signal_handlers[signo] == NULL); + + lib_signals_ignore_forced(signo, restart_syscalls); +} + +void lib_signals_clear_handlers_and_ignore(int signo) +{ + struct signal_handler *h; + + if (signal_handlers[signo] == NULL) + return; + + lib_signals_ignore_forced(signo, TRUE); + + h = signal_handlers[signo]; + signal_handlers[signo] = NULL; + + while (h != NULL) { + struct signal_handler *h_next = h->next; + + if (h->expected) + signals_expected--; + signal_handler_free(h); + h = h_next; + } +} + +void lib_signals_unset_handler(int signo, signal_handler_t *handler, + void *context) +{ + struct signal_handler *h, **p; + + for (p = &signal_handlers[signo]; *p != NULL; p = &(*p)->next) { + if ((*p)->handler == handler && (*p)->context == context) { + if (p == &signal_handlers[signo] && + (*p)->next == NULL) { + /* last handler is to be removed */ + lib_signals_ignore_forced(signo, TRUE); + } + h = *p; + *p = h->next; + if (h->expected) + lib_signals_update_expected_signals(FALSE); + signal_handler_free(h); + return; + } + } + + i_panic("lib_signals_unset_handler(%d, %p, %p): handler not found", + signo, (void *)handler, context); +} + +void lib_signals_set_expected(int signo, bool expected, + signal_handler_t *handler, void *context) +{ + struct signal_handler *h; + + for (h = signal_handlers[signo]; h != NULL; h = h->next) { + if (h->handler == handler && h->context == context) { + if (h->expected == expected) + return; + h->expected = expected; + lib_signals_update_expected_signals(expected); + return; + } + } + + i_panic("lib_signals_set_expected(%d, %p, %p): handler not found", + signo, (void *)handler, context); +} + +void lib_signals_switch_ioloop(int signo, + signal_handler_t *handler, void *context) +{ + struct signal_handler *h; + + for (h = signal_handlers[signo]; h != NULL; h = h->next) { + if (h->handler == handler && h->context == context) { + i_assert((h->flags & LIBSIG_FLAG_DELAYED) != 0); + i_assert((h->flags & LIBSIG_FLAG_IOLOOP_AUTOMOVE) == 0); + signal_handler_switch_ioloop(h); + /* check whether we can now handle any shadowed delayed + signals */ + signal_check_shadowed(); + return; + } + } + + i_panic("lib_signals_switch_ioloop(%d, %p, %p): handler not found", + signo, (void *)handler, context); +} + +void lib_signals_syscall_error(const char *prefix) +{ + /* @UNSAFE: We're in a signal handler. It's very limited what is + allowed in here. Especially strerror() isn't at least officially + allowed. */ + char errno_buf[MAX_INT_STRLEN], *errno_str; + errno_str = dec2str_buf(errno_buf, errno); + + size_t prefix_len = strlen(prefix); + size_t errno_str_len = strlen(errno_str); + char buf[prefix_len + errno_str_len + 1]; + + memcpy(buf, prefix, prefix_len); + memcpy(buf + prefix_len, errno_str, errno_str_len); + buf[prefix_len + errno_str_len] = '\n'; + if (write_full(STDERR_FILENO, buf, + prefix_len + errno_str_len + 1) < 0) { + /* can't really do anything */ + } +} + +void lib_signals_init(void) +{ + int i; + + signals_initialized = TRUE; + io_loop_add_switch_callback(lib_signals_ioloop_switched); + io_loop_add_destroy_callback(lib_signals_ioloop_destroyed); + + /* add signals that were already registered */ + for (i = 0; i < MAX_SIGNAL_VALUE; i++) { + if (signal_handlers[i] != NULL) + lib_signals_set(i, signal_handlers[i]->flags); + } +} + +void lib_signals_deinit(void) +{ + int i; + + for (i = 0; i < MAX_SIGNAL_VALUE; i++) { + if (signal_handlers[i] != NULL) + lib_signals_clear_handlers_and_ignore(i); + } + i_assert(signals_expected == 0); + + if (sig_pipe_fd[0] != -1) { + if (close(sig_pipe_fd[0]) < 0) + i_error("close(sigpipe) failed: %m"); + if (close(sig_pipe_fd[1]) < 0) + i_error("close(sigpipe) failed: %m"); + sig_pipe_fd[0] = sig_pipe_fd[1] = -1; + } + + if (array_is_created(&pending_shadowed_signals)) + array_free(&pending_shadowed_signals); + i_assert(signal_ioloops == NULL); +} diff --git a/src/lib/lib-signals.h b/src/lib/lib-signals.h new file mode 100644 index 0000000..d491de7 --- /dev/null +++ b/src/lib/lib-signals.h @@ -0,0 +1,72 @@ +#ifndef LIB_SIGNALS_H +#define LIB_SIGNALS_H + +#include <signal.h> + +enum libsig_flags { + /* Signal handler will be called later from IO loop when it's safe to + do any kind of work */ + LIBSIG_FLAG_DELAYED = 0x01, + /* Restart syscalls instead of having them fail with EINTR */ + LIBSIG_FLAG_RESTART = 0x02, + /* Automatically shift delayed signal handling for this signal + to a newly started ioloop. */ + LIBSIG_FLAG_IOLOOP_AUTOMOVE = 0x04, +}; +#define LIBSIG_FLAGS_SAFE (LIBSIG_FLAG_DELAYED | LIBSIG_FLAG_RESTART) + +typedef void signal_handler_t(const siginfo_t *si, void *context); + +/* Number of times a "termination signal" has been received. + These signals are SIGINT, SIGQUIT and SIGTERM. Callers can compare this to + their saved previous value to see if a syscall returning EINTR should be + treated as someone wanting to end the process or just some internal signal + that should be ignored, such as SIGCHLD. + + This is marked as volatile so that compiler won't optimize away its + comparisons. It may not work perfectly everywhere, such as when accessing it + isn't atomic, so you shouldn't heavily rely on its actual value. */ +extern volatile unsigned int signal_term_counter; + +/* Convert si_code to string */ +const char *lib_signal_code_to_str(int signo, int sicode); + +/* Detach IOs from all ioloops. This isn't normally necessary, except when + forking a process. */ +void lib_signals_ioloop_detach(void); +void lib_signals_ioloop_attach(void); + +/* Set signal handler for specific signal. */ +void lib_signals_set_handler(int signo, enum libsig_flags flags, + signal_handler_t *handler, void *context) + ATTR_NULL(4); +/* Ignore given signal. */ +void lib_signals_ignore(int signo, bool restart_syscalls); +/* Clear all signal handlers for a specific signal and set the signal to be + ignored. */ +void lib_signals_clear_handlers_and_ignore(int signo); +/* Unset specific signal handler for specific signal. */ +void lib_signals_unset_handler(int signo, + signal_handler_t *handler, void *context) + ATTR_NULL(3); + +/* Indicate whether signals are expected for the indicated delayed handler. When + signals are expected, the io for delayed handlers will be allowed to wait + alone on the ioloop. */ +void lib_signals_set_expected(int signo, bool expected, + signal_handler_t *handler, void *context); + ATTR_NULL(4); + +/* Switch ioloop for a specific signal handler created with + LIBSIG_FLAG_NO_IOLOOP_AUTOMOVE. */ +void lib_signals_switch_ioloop(int signo, + signal_handler_t *handler, void *context); + +/* Log a syscall error inside a (non-delayed) signal handler where i_error() is + unsafe. errno number will be appended to the prefix. */ +void lib_signals_syscall_error(const char *prefix); + +void lib_signals_init(void); +void lib_signals_deinit(void); + +#endif diff --git a/src/lib/lib.c b/src/lib/lib.c new file mode 100644 index 0000000..bd2da91 --- /dev/null +++ b/src/lib/lib.c @@ -0,0 +1,206 @@ +/* Copyright (c) 2001-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "dovecot-version.h" +#include "array.h" +#include "event-filter.h" +#include "env-util.h" +#include "hostpid.h" +#include "ipwd.h" +#include "process-title.h" +#include "restrict-access.h" +#include "var-expand-private.h" +#include "randgen.h" + +#include <fcntl.h> +#include <unistd.h> +#include <sys/time.h> + +/* Mainly for including the full version information in core dumps. + NOTE: Don't set this const - otherwise it won't end up in core dumps. */ +char dovecot_build_info[] = DOVECOT_BUILD_INFO; + +static bool lib_initialized = FALSE; +int dev_null_fd = -1; + +struct atexit_callback { + int priority; + lib_atexit_callback_t *callback; +}; + +static ARRAY(struct atexit_callback) atexit_callbacks = ARRAY_INIT; +static bool lib_clean_exit; + +#undef i_unlink +int i_unlink(const char *path, const char *source_fname, + unsigned int source_linenum) +{ + if (unlink(path) < 0) { + i_error("unlink(%s) failed: %m (in %s:%u)", + path, source_fname, source_linenum); + return -1; + } + return 0; +} + +#undef i_unlink_if_exists +int i_unlink_if_exists(const char *path, const char *source_fname, + unsigned int source_linenum) +{ + if (unlink(path) == 0) + return 1; + else if (errno == ENOENT) + return 0; + else { + i_error("unlink(%s) failed: %m (in %s:%u)", + path, source_fname, source_linenum); + return -1; + } +} + +void i_getopt_reset(void) +{ +#ifdef __GLIBC__ + /* a) for subcommands allow -options anywhere in command line + b) this is actually required for the reset to work (glibc bug?) */ + optind = 0; +#else + optind = 1; +#endif +} + +void lib_atexit(lib_atexit_callback_t *callback) +{ + lib_atexit_priority(callback, 0); +} + +void lib_atexit_priority(lib_atexit_callback_t *callback, int priority) +{ + struct atexit_callback *cb; + const struct atexit_callback *callbacks; + unsigned int i, count; + + if (!array_is_created(&atexit_callbacks)) + i_array_init(&atexit_callbacks, 8); + else { + /* skip if it's already added */ + callbacks = array_get(&atexit_callbacks, &count); + for (i = count; i > 0; i--) { + if (callbacks[i-1].callback == callback) { + i_assert(callbacks[i-1].priority == priority); + return; + } + } + } + cb = array_append_space(&atexit_callbacks); + cb->priority = priority; + cb->callback = callback; +} + +static int atexit_callback_priority_cmp(const struct atexit_callback *cb1, + const struct atexit_callback *cb2) +{ + return cb1->priority - cb2->priority; +} + +void lib_atexit_run(void) +{ + const struct atexit_callback *cb; + + if (array_is_created(&atexit_callbacks)) { + array_sort(&atexit_callbacks, atexit_callback_priority_cmp); + array_foreach(&atexit_callbacks, cb) + (*cb->callback)(); + array_free(&atexit_callbacks); + } +} + +static void lib_open_non_stdio_dev_null(void) +{ + dev_null_fd = open("/dev/null", O_WRONLY); + if (dev_null_fd == -1) + i_fatal("open(/dev/null) failed: %m"); + /* Make sure stdin, stdout and stderr fds exist. We especially rely on + stderr being available and a lot of code doesn't like fd being 0. + We'll open /dev/null as write-only also for stdin, since if any + reads are attempted from it we'll want them to fail. */ + while (dev_null_fd < STDERR_FILENO) { + dev_null_fd = dup(dev_null_fd); + if (dev_null_fd == -1) + i_fatal("dup(/dev/null) failed: %m"); + } + /* close the actual /dev/null fd on exec*(), but keep it in stdio fds */ + fd_close_on_exec(dev_null_fd, TRUE); +} + +void lib_set_clean_exit(bool set) +{ + lib_clean_exit = set; +} + +void lib_exit(int status) +{ + lib_set_clean_exit(TRUE); + exit(status); +} + +static void lib_atexit_handler(void) +{ + /* We're already in exit code path. Avoid using any functions that + might cause strange breakage. Especially anything that could call + exit() again could cause infinite looping in some OSes. */ + if (!lib_clean_exit) { + const char *error = "Unexpected exit - converting to abort\n"; + if (write(STDERR_FILENO, error, strlen(error)) < 0) { + /* ignore */ + } + abort(); + } +} + +void lib_init(void) +{ + i_assert(!lib_initialized); + random_init(); + data_stack_init(); + hostpid_init(); + lib_open_non_stdio_dev_null(); + lib_event_init(); + event_filter_init(); + var_expand_extensions_init(); + + /* Default to clean exit. Otherwise there would be too many accidents + with e.g. command line parsing errors that try to return instead + of using lib_exit(). master_service_init_finish() will change this + again to be FALSE. */ + lib_set_clean_exit(TRUE); + atexit(lib_atexit_handler); + + lib_initialized = TRUE; +} + +bool lib_is_initialized(void) +{ + return lib_initialized; +} + +void lib_deinit(void) +{ + i_assert(lib_initialized); + lib_initialized = FALSE; + lib_atexit_run(); + ipwd_deinit(); + hostpid_deinit(); + var_expand_extensions_deinit(); + event_filter_deinit(); + data_stack_deinit_event(); + lib_event_deinit(); + restrict_access_deinit(); + i_close_fd(&dev_null_fd); + data_stack_deinit(); + failures_deinit(); + process_title_deinit(); + random_deinit(); + + lib_clean_exit = TRUE; +} diff --git a/src/lib/lib.h b/src/lib/lib.h new file mode 100644 index 0000000..9c3ca34 --- /dev/null +++ b/src/lib/lib.h @@ -0,0 +1,115 @@ +#ifndef LIB_H +#define LIB_H + +/* default lib includes */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* default system includes - keep these at minimum.. */ +#include <stddef.h> /* Solaris defines NULL wrong unless this is used */ +#include <stdlib.h> +#include <string.h> /* strcmp() etc. */ +#ifdef HAVE_STRINGS_H +# include <strings.h> /* strcasecmp() etc. */ +#endif +#include <stdarg.h> /* va_list is used everywhere */ +#include <limits.h> /* INT_MAX, etc. */ +#include <errno.h> /* error checking is good */ +#include <sys/types.h> /* many other includes want this */ +#include <inttypes.h> /* PRI* macros */ + +#ifdef HAVE_STDINT_H +# include <stdint.h> /* C99 int types, we mostly need uintmax_t */ +#endif + +#include "compat.h" +#include "macros.h" +#include "failures.h" + +#include "malloc-overflow.h" +#include "data-stack.h" +#include "mempool.h" +#include "imem.h" +#include "byteorder.h" +#include "fd-util.h" + +typedef struct buffer buffer_t; +typedef struct buffer string_t; + +struct istream; +struct ostream; + +typedef void lib_atexit_callback_t(void); + +#include "array-decl.h" /* ARRAY*()s may exist in any header */ +#include "bits.h" +#include "hash-decl.h" /* HASH_TABLE*()s may exist in any header */ +#include "strfuncs.h" +#include "strnum.h" +#include "event-log.h" + +#define LIB_ATEXIT_PRIORITY_HIGH -10 +#define LIB_ATEXIT_PRIORITY_DEFAULT 0 +#define LIB_ATEXIT_PRIORITY_LOW 10 + +/* /dev/null opened as O_WRONLY. Opened at lib_init(), so it can be accessed + also inside chroots. */ +extern int dev_null_fd; + +/* Call unlink(). If it fails, log an error including the source filename + and line number. */ +int i_unlink(const char *path, const char *source_fname, + unsigned int source_linenum); +#define i_unlink(path) i_unlink(path, __FILE__, __LINE__) +/* Same as i_unlink(), but don't log an error if errno=ENOENT. Returns 1 on + unlink() success, 0 if errno=ENOENT, -1 on other errors. */ +int i_unlink_if_exists(const char *path, const char *source_fname, + unsigned int source_linenum); +#define i_unlink_if_exists(path) i_unlink_if_exists(path, __FILE__, __LINE__) +/* Reset getopt() so it can be used for the next args. */ +void i_getopt_reset(void); + +/* Call the given callback at the beginning of lib_deinit(). The main + difference to atexit() is that liblib's memory allocation and logging + functions are still available. Also if lib_atexit() is called multiple times + to the same callback, it's added only once. */ +void lib_atexit(lib_atexit_callback_t *callback); +/* Specify the order in which the callback is called. Lowest numbered + priorities are called first. lib_atexit() is called with priority=0. */ +void lib_atexit_priority(lib_atexit_callback_t *callback, int priority); +/* Manually run the atexit callbacks. lib_deinit() also does this if not + explicitly called. */ +void lib_atexit_run(void); +/* Unless this or lib_deinit() is called, any unexpected exit() will result + in abort(). This can be helpful in catching unexpected exits. */ +void lib_set_clean_exit(bool set); +/* Same as lib_set_clean_exit(TRUE) followed by exit(status). */ +void lib_exit(int status) ATTR_NORETURN; + +void lib_init(void); +bool lib_is_initialized(void); +void lib_deinit(void); + +uint32_t i_rand(void); +/* Returns a random integer < upper_bound. */ +uint32_t i_rand_limit(uint32_t upper_bound); + +static inline unsigned short i_rand_ushort(void) +{ + return i_rand_limit(USHRT_MAX + 1); +} + +static inline unsigned char i_rand_uchar(void) +{ + return i_rand_limit(UCHAR_MAX + 1); +} + +/* Returns a random integer >= min_val, and <= max_val. */ +static inline uint32_t i_rand_minmax(uint32_t min_val, uint32_t max_val) +{ + i_assert(min_val <= max_val); + return min_val + i_rand_limit(max_val - min_val + 1); +} + +#endif diff --git a/src/lib/llist.h b/src/lib/llist.h new file mode 100644 index 0000000..8a52e87 --- /dev/null +++ b/src/lib/llist.h @@ -0,0 +1,81 @@ +#ifndef LLIST_H +#define LLIST_H + +/* Doubly linked list */ +#define DLLIST_PREPEND_FULL(list, item, prev, next) STMT_START { \ + (item)->prev = NULL; \ + (item)->next = *(list); \ + if (*(list) != NULL) (*(list))->prev = (item); \ + *(list) = (item); \ + } STMT_END + +#define DLLIST_PREPEND(list, item) \ + DLLIST_PREPEND_FULL(list, item, prev, next) + +#define DLLIST_REMOVE_FULL(list, item, prev, next) STMT_START { \ + if ((item)->prev != NULL) \ + (item)->prev->next = (item)->next; \ + else if ((*list) == item) \ + *(list) = (item)->next; \ + if ((item)->next != NULL) { \ + (item)->next->prev = (item)->prev; \ + (item)->next = NULL; \ + } \ + (item)->prev = NULL; \ + } STMT_END + +#define DLLIST_REMOVE(list, item) \ + DLLIST_REMOVE_FULL(list, item, prev, next) + +/* Doubly linked list with head and tail */ +#define DLLIST2_PREPEND_FULL(head, tail, item, prev, next) STMT_START { \ + (item)->prev = NULL; \ + (item)->next = *(head); \ + if (*(head) != NULL) (*(head))->prev = (item); else (*tail) = (item); \ + *(head) = (item); \ + } STMT_END + +#define DLLIST2_PREPEND(head, tail, item) \ + DLLIST2_PREPEND_FULL(head, tail, item, prev, next) + +#define DLLIST2_APPEND_FULL(head, tail, item, prev, next) STMT_START { \ + (item)->prev = *(tail); \ + (item)->next = NULL; \ + if (*(tail) != NULL) (*(tail))->next = (item); else (*head) = (item); \ + *(tail) = (item); \ + } STMT_END + +#define DLLIST2_APPEND(head, tail, item) \ + DLLIST2_APPEND_FULL(head, tail, item, prev, next) + +#define DLLIST2_INSERT_AFTER_FULL(head, tail, after, item, prev, next) \ + STMT_START { \ + (item)->prev = (after); \ + (item)->next = (after)->next; \ + if ((after)->next != NULL) \ + (after)->next->prev = (item); \ + (after)->next = (item); \ + if (*(tail) == (after)) \ + *(tail) = (item); \ + } STMT_END + +#define DLLIST2_INSERT_AFTER(head, tail, after, item) \ + DLLIST2_INSERT_AFTER_FULL(head, tail, after, item, prev, next) + +#define DLLIST2_REMOVE_FULL(head, tail, item, prev, next) STMT_START { \ + if ((item)->prev != NULL) \ + (item)->prev->next = (item)->next; \ + else if (*(head) == item) \ + *(head) = (item)->next; \ + if ((item)->next != NULL) { \ + (item)->next->prev = (item)->prev; \ + (item)->next = NULL; \ + } else if ((*tail) == item) \ + *(tail) = (item)->prev; \ + (item)->prev = NULL; \ + } STMT_END + +#define DLLIST2_REMOVE(head, tail, item) \ + DLLIST2_REMOVE_FULL(head, tail, item, prev, next) + +#endif diff --git a/src/lib/log-throttle.c b/src/lib/log-throttle.c new file mode 100644 index 0000000..d4b13c4 --- /dev/null +++ b/src/lib/log-throttle.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "time-util.h" +#include "log-throttle.h" + +struct log_throttle { + struct log_throttle_settings set; + log_throttle_callback_t *callback; + void *context; + + struct timeval last_time; + unsigned int last_count; + + struct timeout *to_throttled; +}; + +#undef log_throttle_init +struct log_throttle * +log_throttle_init(const struct log_throttle_settings *set, + log_throttle_callback_t *callback, void *context) +{ + struct log_throttle *throttle; + + i_assert(set->throttle_at_max_per_interval > 0); + i_assert(set->unthrottle_at_max_per_interval > 0); + + throttle = i_new(struct log_throttle, 1); + throttle->set = *set; + if (throttle->set.interval_msecs == 0) + throttle->set.interval_msecs = 1000; + throttle->callback = callback; + throttle->context = context; + throttle->last_time = ioloop_timeval; + return throttle; +} + +void log_throttle_deinit(struct log_throttle **_throttle) +{ + struct log_throttle *throttle = *_throttle; + + *_throttle = NULL; + timeout_remove(&throttle->to_throttled); + i_free(throttle); +} + +static void log_throttle_callback(struct log_throttle *throttle) +{ + if (throttle->last_count > 0) + throttle->callback(throttle->last_count, throttle->context); + if (throttle->last_count < throttle->set.unthrottle_at_max_per_interval) + timeout_remove(&throttle->to_throttled); + throttle->last_count = 0; +} + +bool log_throttle_accept(struct log_throttle *throttle) +{ + if (throttle->to_throttled != NULL) { + /* unthrottling and last_count resets are done only by + the callback */ + throttle->last_count++; + return FALSE; + } else if (timeval_diff_msecs(&ioloop_timeval, &throttle->last_time) >= + (int)throttle->set.interval_msecs) { + throttle->last_time = ioloop_timeval; + throttle->last_count = 1; + return TRUE; + } else if (++throttle->last_count <= throttle->set.throttle_at_max_per_interval) { + return TRUE; + } else { + throttle->last_count = 1; + throttle->to_throttled = + timeout_add(throttle->set.interval_msecs, + log_throttle_callback, throttle); + return FALSE; + } +} diff --git a/src/lib/log-throttle.h b/src/lib/log-throttle.h new file mode 100644 index 0000000..be98239 --- /dev/null +++ b/src/lib/log-throttle.h @@ -0,0 +1,32 @@ +#ifndef LOG_THROTTLE_H +#define LOG_THROTTLE_H + +struct log_throttle_settings { + /* Start throttling after we reach this many log events/interval. */ + unsigned int throttle_at_max_per_interval; + /* Throttling continues until there's only this many or below + log events/interval. */ + unsigned int unthrottle_at_max_per_interval; + /* Interval unit in milliseconds. The throttled-callback is also called + at this interval. Default (0) is 1000 milliseconds. */ + unsigned int interval_msecs; +}; + +typedef void +log_throttle_callback_t(unsigned int new_events_count, void *context); + +struct log_throttle * +log_throttle_init(const struct log_throttle_settings *set, + log_throttle_callback_t *callback, void *context); +#define log_throttle_init(set, callback, context) \ + log_throttle_init(set - \ + CALLBACK_TYPECHECK(callback, void (*)(unsigned int, typeof(context))), \ + (log_throttle_callback_t *)callback, context) +void log_throttle_deinit(struct log_throttle **throttle); + +/* Increase event count. Returns TRUE if the event should be logged, + FALSE if it's throttled. ioloop_timeval is used to determine the current + time. */ +bool log_throttle_accept(struct log_throttle *throttle); + +#endif diff --git a/src/lib/macros.h b/src/lib/macros.h new file mode 100644 index 0000000..8cd159f --- /dev/null +++ b/src/lib/macros.h @@ -0,0 +1,302 @@ +#ifndef MACROS_H +#define MACROS_H + +/* several useful macros, mostly from glib.h */ + +#ifndef NULL +# define NULL ((void *)0) +#endif + +#ifndef FALSE +# define FALSE (!1) +#endif + +#ifndef TRUE +# define TRUE (!FALSE) +#endif + +#define N_ELEMENTS(arr) \ + (sizeof(arr) / sizeof((arr)[0])) + +#define MEM_ALIGN(size) \ + (((size) + MEM_ALIGN_SIZE-1) & ~((size_t) MEM_ALIGN_SIZE-1)) + +#define PTR_OFFSET(ptr, offset) \ + ((void *) (((uintptr_t) (ptr)) + ((size_t) (offset)))) +#define CONST_PTR_OFFSET(ptr, offset) \ + ((const void *) (((uintptr_t) (ptr)) + ((size_t) (offset)))) + +#define container_of(ptr, type, name) \ + (type *)((char *)(ptr) - offsetof(type, name) + \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE(ptr, &((type *) 0)->name)) + +/* Don't use simply MIN/MAX, as they're often defined elsewhere in include + files that are included after this file generating tons of warnings. */ +#define I_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define I_MAX(a, b) (((a) > (b)) ? (a) : (b)) + +/* make it easier to cast from/to pointers. assumes that + sizeof(uintptr_t) == sizeof(void *) and they're both the largest datatypes + that are allowed to be used. so, long long isn't safe with these. */ +#define POINTER_CAST(i) \ + ((void *) (((uintptr_t)NULL) + (i))) +#define POINTER_CAST_TO(p, type) \ + ((type)(uintptr_t)(p)) + +/* Define VA_COPY() to do the right thing for copying va_list variables. + config.h may have already defined VA_COPY as va_copy or __va_copy. */ +#ifndef VA_COPY +# if defined (__GNUC__) && defined (__PPC__) && \ + (defined (_CALL_SYSV) || defined (_WIN32)) +# define VA_COPY(ap1, ap2) (*(ap1) = *(ap2)) +# elif defined (VA_COPY_AS_ARRAY) +# define VA_COPY(ap1, ap2) memmove ((ap1), (ap2), sizeof (va_list)) +# else /* va_list is a pointer */ +# define VA_COPY(ap1, ap2) ((ap1) = (ap2)) +# endif /* va_list is a pointer */ +#endif + +/* Provide convenience macros for handling structure + * fields through their offsets. + */ +#define STRUCT_MEMBER_P(struct_p, struct_offset) \ + ((void *) ((char *) (struct_p) + (long) (struct_offset))) +#define CONST_STRUCT_MEMBER_P(struct_p, struct_offset) \ + ((const void *) ((const char *) (struct_p) + (long) (struct_offset))) + +/* Provide simple macro statement wrappers: + STMT_START { statements; } STMT_END; + can be used as a single statement, as in + if (x) STMT_START { ... } STMT_END; else ... */ +#if !(defined (STMT_START) && defined (STMT_END)) +# define STMT_START do +# define STMT_END while (0) +#endif + +/* Provide macros to feature the GCC function attribute. */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +# define ATTRS_DEFINED +# define ATTR_FORMAT(format_idx, arg_idx) \ + __attribute__((format (printf, format_idx, arg_idx))) +# define ATTR_FORMAT_ARG(arg_idx) \ + __attribute__((format_arg (arg_idx))) +# define ATTR_SCANF(format_idx, arg_idx) \ + __attribute__((format (scanf, format_idx, arg_idx))) +# define ATTR_STRFTIME(format_idx) \ + __attribute__((format (strftime, format_idx, 0))) +# define ATTR_UNUSED __attribute__((unused)) +# define ATTR_NORETURN __attribute__((noreturn)) +# define ATTR_CONST __attribute__((const)) +# define ATTR_PURE __attribute__((pure)) +#else +# define ATTR_FORMAT(format_idx, arg_idx) +# define ATTR_FORMAT_ARG(arg_idx) +# define ATTR_SCANF(format_idx, arg_idx) +# define ATTR_STRFTIME(format_idx) +# define ATTR_UNUSED +# define ATTR_NORETURN +# define ATTR_CONST +# define ATTR_PURE +#endif +#ifdef HAVE_ATTR_NULL +# define ATTR_NULL(...) __attribute__((null(__VA_ARGS__))) +#else +# define ATTR_NULL(...) +#endif +#ifdef HAVE_ATTR_NOWARN_UNUSED_RESULT +# define ATTR_NOWARN_UNUSED_RESULT __attribute__((nowarn_unused_result)) +#else +# define ATTR_NOWARN_UNUSED_RESULT +#endif +#if __GNUC__ > 2 +# define ATTR_MALLOC __attribute__((malloc)) +#else +# define ATTR_MALLOC +#endif +#if __GNUC__ > 3 +/* GCC 4.0 and later */ +# define ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +# define ATTR_SENTINEL __attribute__((sentinel)) +#else +# define ATTR_WARN_UNUSED_RESULT +# define ATTR_SENTINEL +#endif +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) +/* GCC 4.3 and later */ +# define ATTR_HOT __attribute__((hot)) +# define ATTR_COLD __attribute__((cold)) +#else +# define ATTR_HOT +# define ATTR_COLD +#endif +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9) +/* GCC 4.9 and later */ +# define ATTR_RETURNS_NONNULL __attribute__((returns_nonnull)) +#else +# define ATTR_RETURNS_NONNULL +#endif +#ifdef HAVE_ATTR_DEPRECATED +# define ATTR_DEPRECATED(str) __attribute__((deprecated(str))) +#else +# define ATTR_DEPRECATED(str) +#endif + +/* Macros to provide type safety for callback functions' context parameters. + This is used like: + + // safe-api.h file: + typedef void safe_callback_t(struct foo *foo); + + void safe_run(safe_callback_t *callback, void *context); + #define safe_run((safe_callback_t *)callback, \ + TRUE ? context : CALLBACK_TYPECHECK(callback, void (*)(typeof(context)))) + + // safe-api.c file: + #undef safe_run + void safe_run(safe_callback_t *callback, void *context) + { + callback(context); + } + + // in caller code: + static void callback(struct foo *foo); + struct foo *foo = ...; + safe_run(callback, foo); + + The first step is to create the callback function in a normal way. Type + safety is added to it by creating a macro that overrides the function and + checks the callback type safety using CALLBACK_TYPECHECK(). + + The CALLBACK_TYPECHECK() macro works by giving a compiling failure if the + provided callback function isn't compatible with the specified function + type parameter. The function type parameter must use typeof(context) in + place of the "void *context" parameter, but otherwise use exactly the same + function type as what the callback is. The macro then casts the given + callback function into the type with "void *context". +*/ +#ifdef HAVE_TYPE_CHECKS +# define CALLBACK_TYPECHECK(callback, type) \ + (COMPILE_ERROR_IF_TRUE(!__builtin_types_compatible_p( \ + typeof(&callback), type)) ? 1 : 0) +#else +# define CALLBACK_TYPECHECK(callback, type) 0 +#endif + +#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 0)) && \ + !defined(__cplusplus) && !defined(STATIC_CHECKER) +# define COMPILE_ERROR_IF_TRUE(condition) \ + (sizeof(char[1 - 2 * ((condition) ? 1 : 0)]) > 0 ? FALSE : FALSE) +#else +# define COMPILE_ERROR_IF_TRUE(condition) FALSE +#endif + +#ifdef HAVE_TYPE_CHECKS +# define COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE(_a, _b) \ + COMPILE_ERROR_IF_TRUE( \ + !__builtin_types_compatible_p(typeof(_a), typeof(_b))) +#define COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE(_a1, _a2, _b) \ + COMPILE_ERROR_IF_TRUE( \ + !__builtin_types_compatible_p(typeof(_a1), typeof(_b)) && \ + !__builtin_types_compatible_p(typeof(_a2), typeof(_b))) +# define TYPE_CHECKS(return_type, checks, func) \ + (FALSE ? (return_type)(checks) : (func)) +#else +# define COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE(_a, _b) 0 +# define COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE(_a1, _a2, _b) 0 +# define TYPE_CHECKS(return_type, checks, func) (func) +#endif + +#if __GNUC__ > 2 +# define unlikely(expr) (__builtin_expect((expr) ? 1 : 0, 0) != 0) +# define likely(expr) (__builtin_expect((expr) ? 1 : 0, 1) != 0) +#else +# define unlikely(expr) expr +# define likely(expr) expr +#endif + +#if defined(__clang__) && ((__clang_major__ > 4) || (__clang_major__ == 3 && __clang_minor__ >= 9)) +# define ATTR_UNSIGNED_WRAPS __attribute__((no_sanitize("integer"))) +#else +# define ATTR_UNSIGNED_WRAPS +#endif + +/* Provide macros for error handling. */ +#ifdef DISABLE_ASSERTS +# define i_assert(expr) +#else +# define i_assert(expr) STMT_START{ \ + if (unlikely(!(expr))) \ + i_panic("file %s: line %d (%s): assertion failed: (%s)", \ + __FILE__, \ + __LINE__, \ + __func__, \ + #expr); }STMT_END +#endif + +/* Convenience macro to test the versions of dovecot. */ +#define DOVECOT_PREREQ(maj, min, micro) \ + ((DOVECOT_VERSION_MAJOR << 24) + \ + (DOVECOT_VERSION_MINOR << 16) + \ + DOVECOT_VERSION_MICRO >= ((maj) << 24) + ((min) << 16) + (micro)) + +#ifdef __cplusplus +# undef STATIC_ARRAY +# define STATIC_ARRAY +#endif + +/* Convenience wrappers for initializing a struct with zeros, although it can + be used for replacing other memset()s also. + + // NOTE: This is the correct way to zero the whole array + char arr[5]; i_zero(&arr); + // This will give compiler error (or zero only the first element): + char arr[5]; i_zero(arr); +*/ +#define i_zero(p) \ + memset(p, 0 + COMPILE_ERROR_IF_TRUE(sizeof(p) > sizeof(void *)), sizeof(*(p))) +#define i_zero_safe(p) \ + safe_memset(p, 0 + COMPILE_ERROR_IF_TRUE(sizeof(p) > sizeof(void *)), sizeof(*(p))) + +#define ST_CHANGED(st_a, st_b) \ + ((st_a).st_mtime != (st_b).st_mtime || \ + ST_MTIME_NSEC(st_a) != ST_MTIME_NSEC(st_b) || \ + (st_a).st_size != (st_b).st_size || \ + (st_a).st_ino != (st_b).st_ino) + +#ifdef HAVE_UNDEFINED_SANITIZER +# define ATTR_NO_SANITIZE(x) __attribute__((no_sanitize((x)))) +#else +# define ATTR_NO_SANITIZE(x) +#endif + +/* gcc and clang do this differently, see + https://gcc.gnu.org/onlinedocs/gcc-10.2.0/gcc/Common-Function-Attributes.html */ +#ifdef HAVE_FSANITIZE_UNDEFINED +# ifdef __clang__ +# define ATTR_NO_SANITIZE_UNDEFINED ATTR_NO_SANITIZE("undefined") +# else +# define ATTR_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize_undefined)) +# endif +#else +# define ATTR_NO_SANITIZE_UNDEFINED +#endif + +#ifdef HAVE_FSANITIZE_INTEGER +# define ATTR_NO_SANITIZE_INTEGER ATTR_NO_SANITIZE("integer") +# define ATTR_NO_SANITIZE_IMPLICIT_CONVERSION ATTR_NO_SANITIZE("implicit-conversion") +#else +# define ATTR_NO_SANITIZE_INTEGER +# define ATTR_NO_SANITIZE_IMPLICIT_CONVERSION +#endif + +/* negate enumeration flags in a way that avoids implicit conversion */ +#ifndef STATIC_CHECKER +# define ENUM_NEGATE(x) \ + ((unsigned int)(~(x)) + COMPILE_ERROR_IF_TRUE(sizeof((x)) > sizeof(int) || (x) < 0 || (x) > INT_MAX)) +#else +/* clang scan-build keeps complaining about x > 2147483647 case, so disable the + sizeof check. */ +# define ENUM_NEGATE(x) ((unsigned int)(~(x))) +#endif + +#endif diff --git a/src/lib/malloc-overflow.h b/src/lib/malloc-overflow.h new file mode 100644 index 0000000..108cc00 --- /dev/null +++ b/src/lib/malloc-overflow.h @@ -0,0 +1,54 @@ +#ifndef MALLOC_OVERFLOW_H +#define MALLOC_OVERFLOW_H + +/* MALLOC_*() can be used to calculate memory allocation sizes. If there's an + overflow, it'll cleanly panic instead of causing a potential buffer + overflow. + + Note that *_malloc(size+1) doesn't need to use MALLOC_ADD(size, 1). It wraps + to size==0 and the *_malloc() calls already panic if size==0. */ +static inline size_t +malloc_multiply_check(size_t a, size_t b, size_t sizeof_a, size_t sizeof_b, + const char *fname, unsigned int linenum) +{ + /* the first sizeof-checks are intended to optimize away this entire + if-check for types that are small enough to never wrap size_t. */ + if ((sizeof_a * 2 > sizeof(size_t) || sizeof_b * 2 > sizeof(size_t)) && + b != 0 && (a > SIZE_MAX / b)) { + i_panic("file %s: line %d: memory allocation overflow: %zu * %zu", + fname, linenum, a, b); + } + return a * b; +} +#ifndef STATIC_CHECKER +# define MALLOC_MULTIPLY(a, b) \ + malloc_multiply_check(a, b, sizeof(a), sizeof(b), __FILE__, __LINE__) +#else +/* avoid warning every time about sizeof(b) when b contains any arithmetic */ +# define MALLOC_MULTIPLY(a, b) \ + malloc_multiply_check(a, b, sizeof(a), sizeof(size_t), __FILE__, __LINE__) +#endif + +static inline size_t +malloc_add_check(size_t a, size_t b, size_t sizeof_a, size_t sizeof_b, + const char *fname, unsigned int linenum) +{ + /* the first sizeof-checks are intended to optimize away this entire + if-check for types that are small enough to never wrap size_t. */ + if ((sizeof_a >= sizeof(size_t) || sizeof_b >= sizeof(size_t)) && + SIZE_MAX - a < b) { + i_panic("file %s: line %d: memory allocation overflow: %zu + %zu", + fname, linenum, a, b); + } + return a + b; +} +#ifndef STATIC_CHECKER +# define MALLOC_ADD(a, b) \ + malloc_add_check(a, b, sizeof(a), sizeof(b), __FILE__, __LINE__) +#else +/* avoid warning every time about sizeof(b) when b contains any arithmetic */ +# define MALLOC_ADD(a, b) \ + malloc_add_check(a, b, sizeof(a), sizeof(size_t), __FILE__, __LINE__) +#endif + +#endif diff --git a/src/lib/md4.c b/src/lib/md4.c new file mode 100644 index 0000000..06e3231 --- /dev/null +++ b/src/lib/md4.c @@ -0,0 +1,300 @@ +/* + * MD4 (RFC-1320) message digest. + * Modified from MD5 code by Andrey Panin <pazke@donpac.ru> + * + * Written by Solar Designer <solar@openwall.com> in 2001, and placed in + * the public domain. There's absolutely no warranty. + * + * This differs from Colin Plumb's older public domain implementation in + * that no 32-bit integer data type is required, there's no compile-time + * endianness configuration, and the function prototypes match OpenSSL's. + * The primary goals are portability and ease of use. + * + * This implementation is meant to be fast, but not as fast as possible. + * Some known optimizations are not included to reduce source code size + * and avoid compile-time configuration. + */ + +#include "lib.h" +#include "safe-memset.h" +#include "md4.h" + +/* + * The basic MD4 functions. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +/* + * The MD4 transformation for all four rounds. + */ +#define STEP(f, a, b, c, d, x, s) \ + (a) += f((b), (c), (d)) + (x); \ + (a) = ((a) << (s)) | ((a) >> (32 - (s))) + + +/* + * SET reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + * + * The check for little-endian architectures which tolerate unaligned + * memory accesses is just an optimization. Nothing will break if it + * doesn't work. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +/* uint_fast32_t might be 64 bit, and thus may read 4 more bytes + * beyond the end of the buffer. So only read precisely 32 bits + */ +#define SET(n) \ + (*(const uint32_t *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = \ + (uint_fast32_t)ptr[(n) * 4] | \ + ((uint_fast32_t)ptr[(n) * 4 + 1] << 8) | \ + ((uint_fast32_t)ptr[(n) * 4 + 2] << 16) | \ + ((uint_fast32_t)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update + * the bit counters. There're no alignment requirements. + */ +static const void * ATTR_NOWARN_UNUSED_RESULT ATTR_UNSIGNED_WRAPS + ATTR_NO_SANITIZE_UNDEFINED ATTR_NO_SANITIZE_INTEGER + ATTR_NO_SANITIZE_IMPLICIT_CONVERSION +body(struct md4_context *ctx, const void *data, size_t size) +{ + const unsigned char *ptr; + uint32_t a, b, c, d; + uint32_t saved_a, saved_b, saved_c, saved_d; + + ptr = data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET( 0), 3); + STEP(F, d, a, b, c, SET( 1), 7); + STEP(F, c, d, a, b, SET( 2), 11); + STEP(F, b, c, d, a, SET( 3), 19); + + STEP(F, a, b, c, d, SET( 4), 3); + STEP(F, d, a, b, c, SET( 5), 7); + STEP(F, c, d, a, b, SET( 6), 11); + STEP(F, b, c, d, a, SET( 7), 19); + + STEP(F, a, b, c, d, SET( 8), 3); + STEP(F, d, a, b, c, SET( 9), 7); + STEP(F, c, d, a, b, SET(10), 11); + STEP(F, b, c, d, a, SET(11), 19); + + STEP(F, a, b, c, d, SET(12), 3); + STEP(F, d, a, b, c, SET(13), 7); + STEP(F, c, d, a, b, SET(14), 11); + STEP(F, b, c, d, a, SET(15), 19); +/* Round 2 */ + STEP(G, a, b, c, d, GET( 0) + 0x5A827999, 3); + STEP(G, d, a, b, c, GET( 4) + 0x5A827999, 5); + STEP(G, c, d, a, b, GET( 8) + 0x5A827999, 9); + STEP(G, b, c, d, a, GET(12) + 0x5A827999, 13); + + STEP(G, a, b, c, d, GET( 1) + 0x5A827999, 3); + STEP(G, d, a, b, c, GET( 5) + 0x5A827999, 5); + STEP(G, c, d, a, b, GET( 9) + 0x5A827999, 9); + STEP(G, b, c, d, a, GET(13) + 0x5A827999, 13); + + STEP(G, a, b, c, d, GET( 2) + 0x5A827999, 3); + STEP(G, d, a, b, c, GET( 6) + 0x5A827999, 5); + STEP(G, c, d, a, b, GET(10) + 0x5A827999, 9); + STEP(G, b, c, d, a, GET(14) + 0x5A827999, 13); + + STEP(G, a, b, c, d, GET( 3) + 0x5A827999, 3); + STEP(G, d, a, b, c, GET( 7) + 0x5A827999, 5); + STEP(G, c, d, a, b, GET(11) + 0x5A827999, 9); + STEP(G, b, c, d, a, GET(15) + 0x5A827999, 13); +/* Round 3 */ + STEP(H, a, b, c, d, GET( 0) + 0x6ED9EBA1, 3); + STEP(H, d, a, b, c, GET( 8) + 0x6ED9EBA1, 9); + STEP(H, c, d, a, b, GET( 4) + 0x6ED9EBA1, 11); + STEP(H, b, c, d, a, GET(12) + 0x6ED9EBA1, 15); + + STEP(H, a, b, c, d, GET( 2) + 0x6ED9EBA1, 3); + STEP(H, d, a, b, c, GET(10) + 0x6ED9EBA1, 9); + STEP(H, c, d, a, b, GET( 6) + 0x6ED9EBA1, 11); + STEP(H, b, c, d, a, GET(14) + 0x6ED9EBA1, 15); + + STEP(H, a, b, c, d, GET( 1) + 0x6ED9EBA1, 3); + STEP(H, d, a, b, c, GET( 9) + 0x6ED9EBA1, 9); + STEP(H, c, d, a, b, GET( 5) + 0x6ED9EBA1, 11); + STEP(H, b, c, d, a, GET(13) + 0x6ED9EBA1, 15); + + STEP(H, a, b, c, d, GET( 3) + 0x6ED9EBA1, 3); + STEP(H, d, a, b, c, GET(11) + 0x6ED9EBA1, 9); + STEP(H, c, d, a, b, GET( 7) + 0x6ED9EBA1, 11); + STEP(H, b, c, d, a, GET(15) + 0x6ED9EBA1, 15); + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while ((size -= 64) != 0); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void md4_init(struct md4_context *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void md4_update(struct md4_context *ctx, const void *data, size_t size) +{ + /* @UNSAFE */ + uint_fast32_t saved_lo; + unsigned long used, free; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used != 0) { + free = 64 - used; + + if (size < free) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, free); + data = (const unsigned char *) data + free; + size -= free; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = body(ctx, data, size & ~0x3fUL); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +void ATTR_NO_SANITIZE_UNDEFINED ATTR_NO_SANITIZE_INTEGER + ATTR_NO_SANITIZE_IMPLICIT_CONVERSION +md4_final(struct md4_context *ctx, unsigned char result[STATIC_ARRAY MD4_RESULTLEN]) +{ + /* @UNSAFE */ + unsigned long used, free; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + free = 64 - used; + + if (free < 8) { + memset(&ctx->buffer[used], 0, free); + body(ctx, ctx->buffer, 64); + used = 0; + free = 64; + } + + memset(&ctx->buffer[used], 0, free - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = ctx->lo; + ctx->buffer[57] = ctx->lo >> 8; + ctx->buffer[58] = ctx->lo >> 16; + ctx->buffer[59] = ctx->lo >> 24; + ctx->buffer[60] = ctx->hi; + ctx->buffer[61] = ctx->hi >> 8; + ctx->buffer[62] = ctx->hi >> 16; + ctx->buffer[63] = ctx->hi >> 24; + + body(ctx, ctx->buffer, 64); + + result[0] = ctx->a; + result[1] = ctx->a >> 8; + result[2] = ctx->a >> 16; + result[3] = ctx->a >> 24; + result[4] = ctx->b; + result[5] = ctx->b >> 8; + result[6] = ctx->b >> 16; + result[7] = ctx->b >> 24; + result[8] = ctx->c; + result[9] = ctx->c >> 8; + result[10] = ctx->c >> 16; + result[11] = ctx->c >> 24; + result[12] = ctx->d; + result[13] = ctx->d >> 8; + result[14] = ctx->d >> 16; + result[15] = ctx->d >> 24; + + i_zero_safe(ctx); +} + +void md4_get_digest(const void *data, size_t size, + unsigned char result[STATIC_ARRAY MD4_RESULTLEN]) +{ + struct md4_context ctx; + + md4_init(&ctx); + md4_update(&ctx, data, size); + md4_final(&ctx, result); +} + +static void hash_method_init_md4(void *context) +{ + md4_init(context); +} +static void hash_method_loop_md4(void *context, const void *data, size_t size) +{ + md4_update(context, data, size); +} + +static void hash_method_result_md4(void *context, unsigned char *result_r) +{ + md4_final(context, result_r); +} + +const struct hash_method hash_method_md4 = { + .name = "md4", + .block_size = 64, /* block size is 512 bits */ + .context_size = sizeof(struct md4_context), + .digest_size = MD4_RESULTLEN, + + .init = hash_method_init_md4, + .loop = hash_method_loop_md4, + .result = hash_method_result_md4, +}; diff --git a/src/lib/md4.h b/src/lib/md4.h new file mode 100644 index 0000000..1530d0d --- /dev/null +++ b/src/lib/md4.h @@ -0,0 +1,33 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, + * Inc. MD4 Message-Digest Algorithm. + * + * Written by Solar Designer <solar@openwall.com> in 2001, and placed in + * the public domain. See md4.c for more information. + */ + +#ifndef MD4_H +#define MD4_H + +#include "hash-method.h" + +#define MD4_RESULTLEN (128/8) + +struct md4_context { + uint_fast32_t lo, hi; + uint_fast32_t a, b, c, d; + unsigned char buffer[64]; + uint_fast32_t block[MD4_RESULTLEN]; +}; + +void md4_init(struct md4_context *ctx); +void md4_update(struct md4_context *ctx, const void *data, size_t size); +void md4_final(struct md4_context *ctx, + unsigned char result[STATIC_ARRAY MD4_RESULTLEN]); + +void md4_get_digest(const void *data, size_t size, + unsigned char result[STATIC_ARRAY MD4_RESULTLEN]); + +extern const struct hash_method hash_method_md4; + +#endif diff --git a/src/lib/md5.c b/src/lib/md5.c new file mode 100644 index 0000000..6b5da6c --- /dev/null +++ b/src/lib/md5.c @@ -0,0 +1,314 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, + * Inc. MD5 Message-Digest Algorithm. + * + * Written by Solar Designer <solar@openwall.com> in 2001, and placed in + * the public domain. There's absolutely no warranty. + * + * This differs from Colin Plumb's older public domain implementation in + * that no 32-bit integer data type is required, there's no compile-time + * endianness configuration, and the function prototypes match OpenSSL's. + * The primary goals are portability and ease of use. + * + * This implementation is meant to be fast, but not as fast as possible. + * Some known optimizations are not included to reduce source code size + * and avoid compile-time configuration. + */ + +#include "lib.h" +#include "safe-memset.h" +#include "md5.h" + +/* + * The basic MD5 functions. + * + * F is optimized compared to its RFC 1321 definition just like in Colin + * Plumb's implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + * + * The check for little-endian architectures which tolerate unaligned + * memory accesses is just an optimization. Nothing will break if it + * doesn't work. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) \ + (*(const uint32_t *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = \ + (uint_fast32_t)ptr[(n) * 4] | \ + ((uint_fast32_t)ptr[(n) * 4 + 1] << 8) | \ + ((uint_fast32_t)ptr[(n) * 4 + 2] << 16) | \ + ((uint_fast32_t)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update + * the bit counters. There're no alignment requirements. + */ +static const void * ATTR_NOWARN_UNUSED_RESULT ATTR_UNSIGNED_WRAPS + ATTR_NO_SANITIZE_UNDEFINED ATTR_NO_SANITIZE_INTEGER + ATTR_NO_SANITIZE_IMPLICIT_CONVERSION +body(struct md5_context *ctx, const void *data, size_t size) +{ + const unsigned char *ptr; + uint_fast32_t a, b, c, d; + uint_fast32_t saved_a, saved_b, saved_c, saved_d; + + ptr = data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + +/* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + +/* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23) + +/* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while ((size -= 64) != 0); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void md5_init(struct md5_context *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; + memset(ctx->block, 0, sizeof(ctx->block)); +} + +void ATTR_UNSIGNED_WRAPS +md5_update(struct md5_context *ctx, const void *data, size_t size) +{ + /* @UNSAFE */ + uint_fast32_t saved_lo; + unsigned long used, free; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used != 0) { + free = 64 - used; + + if (size < free) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, free); + data = (const unsigned char *) data + free; + size -= free; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = body(ctx, data, size & ~0x3fUL); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +void ATTR_UNSIGNED_WRAPS ATTR_NO_SANITIZE_UNDEFINED + ATTR_NO_SANITIZE_INTEGER ATTR_NO_SANITIZE_IMPLICIT_CONVERSION +md5_final(struct md5_context *ctx, unsigned char result[STATIC_ARRAY MD5_RESULTLEN]) +{ + /* @UNSAFE */ + unsigned long used, free; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + free = 64 - used; + + if (free < 8) { + memset(&ctx->buffer[used], 0, free); + body(ctx, ctx->buffer, 64); + used = 0; + free = 64; + } + + memset(&ctx->buffer[used], 0, free - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = ctx->lo; + ctx->buffer[57] = ctx->lo >> 8; + ctx->buffer[58] = ctx->lo >> 16; + ctx->buffer[59] = ctx->lo >> 24; + ctx->buffer[60] = ctx->hi; + ctx->buffer[61] = ctx->hi >> 8; + ctx->buffer[62] = ctx->hi >> 16; + ctx->buffer[63] = ctx->hi >> 24; + + body(ctx, ctx->buffer, 64); + + result[0] = ctx->a; + result[1] = ctx->a >> 8; + result[2] = ctx->a >> 16; + result[3] = ctx->a >> 24; + result[4] = ctx->b; + result[5] = ctx->b >> 8; + result[6] = ctx->b >> 16; + result[7] = ctx->b >> 24; + result[8] = ctx->c; + result[9] = ctx->c >> 8; + result[10] = ctx->c >> 16; + result[11] = ctx->c >> 24; + result[12] = ctx->d; + result[13] = ctx->d >> 8; + result[14] = ctx->d >> 16; + result[15] = ctx->d >> 24; + + i_zero_safe(ctx); +} + +void md5_get_digest(const void *data, size_t size, + unsigned char result[STATIC_ARRAY MD5_RESULTLEN]) +{ + struct md5_context ctx; + + md5_init(&ctx); + md5_update(&ctx, data, size); + md5_final(&ctx, result); +} + +static void hash_method_init_md5(void *context) +{ + md5_init(context); +} +static void hash_method_loop_md5(void *context, const void *data, size_t size) +{ + md5_update(context, data, size); +} + +static void hash_method_result_md5(void *context, unsigned char *result_r) +{ + md5_final(context, result_r); +} + +const struct hash_method hash_method_md5 = { + .name = "md5", + .block_size = 64, /* block size is 512 bits */ + .context_size = sizeof(struct md5_context), + .digest_size = MD5_RESULTLEN, + + .init = hash_method_init_md5, + .loop = hash_method_loop_md5, + .result = hash_method_result_md5, +}; diff --git a/src/lib/md5.h b/src/lib/md5.h new file mode 100644 index 0000000..682a6c5 --- /dev/null +++ b/src/lib/md5.h @@ -0,0 +1,33 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, + * Inc. MD5 Message-Digest Algorithm. + * + * Written by Solar Designer <solar@openwall.com> in 2001, and placed in + * the public domain. See md5.c for more information. + */ + +#ifndef MD5_H +#define MD5_H + +#include "hash-method.h" + +#define MD5_RESULTLEN (128/8) + +struct md5_context { + uint_fast32_t lo, hi; + uint_fast32_t a, b, c, d; + unsigned char buffer[64]; + uint_fast32_t block[MD5_RESULTLEN]; +}; + +void md5_init(struct md5_context *ctx); +void md5_update(struct md5_context *ctx, const void *data, size_t size); +void md5_final(struct md5_context *ctx, + unsigned char result[STATIC_ARRAY MD5_RESULTLEN]); + +void md5_get_digest(const void *data, size_t size, + unsigned char result[STATIC_ARRAY MD5_RESULTLEN]); + +extern const struct hash_method hash_method_md5; + +#endif diff --git a/src/lib/memarea.c b/src/lib/memarea.c new file mode 100644 index 0000000..747ea8b --- /dev/null +++ b/src/lib/memarea.c @@ -0,0 +1,93 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "memarea.h" + +struct memarea { + const void *data; + size_t size; + + memarea_free_callback_t *callback; + void *context; + + int refcount; +}; + +static struct memarea memarea_empty = { + .refcount = 1, +}; + +#undef memarea_init +struct memarea * +memarea_init(const void *data, size_t size, + memarea_free_callback_t *callback, void *context) +{ + struct memarea *area; + + i_assert(callback != NULL); + + area = i_new(struct memarea, 1); + area->data = data; + area->size = size; + area->callback = callback; + area->context = context; + area->refcount = 1; + return area; +} + +struct memarea *memarea_init_empty(void) +{ + i_assert(memarea_empty.refcount > 0); + memarea_empty.refcount++; + return &memarea_empty; +} + +void memarea_ref(struct memarea *area) +{ + i_assert(area->refcount > 0); + area->refcount++; +} + +void memarea_unref(struct memarea **_area) +{ + struct memarea *area = *_area; + + *_area = NULL; + i_assert(area->refcount > 0); + + if (--area->refcount > 0) + return; + i_assert(area != &memarea_empty); + area->callback(area->context); + i_free(area); +} + +void memarea_free_without_callback(struct memarea **_area) +{ + struct memarea *area = *_area; + + *_area = NULL; + i_assert(memarea_get_refcount(area) == 1); + i_free(area); +} + +unsigned int memarea_get_refcount(struct memarea *area) +{ + i_assert(area->refcount > 0); + return area->refcount; +} + +const void *memarea_get(struct memarea *area, size_t *size_r) +{ + *size_r = area->size; + return area->data; +} + +size_t memarea_get_size(struct memarea *area) +{ + return area->size; +} + +void memarea_free_callback_noop(void *context ATTR_UNUSED) +{ +} diff --git a/src/lib/memarea.h b/src/lib/memarea.h new file mode 100644 index 0000000..9c546df --- /dev/null +++ b/src/lib/memarea.h @@ -0,0 +1,31 @@ +#ifndef MEMAREA_H +#define MEMAREA_H + +typedef void memarea_free_callback_t(void *context); + +/* Create reference counted memory area. The callback is called when the + refcount drops to 0. */ +struct memarea * +memarea_init(const void *data, size_t size, + memarea_free_callback_t *callback, void *context); +#define memarea_init(data, size, callback, context) \ + memarea_init(data, size - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (memarea_free_callback_t *)callback, context) +/* Returns an empty memory area. */ +struct memarea *memarea_init_empty(void); + +void memarea_ref(struct memarea *area); +void memarea_unref(struct memarea **area); +/* Free the memory area without calling the callback. + This is allowed only when refcount==1. */ +void memarea_free_without_callback(struct memarea **area); + +unsigned int memarea_get_refcount(struct memarea *area); +const void *memarea_get(struct memarea *area, size_t *size_r); +size_t memarea_get_size(struct memarea *area); + +/* free-callback that does nothing */ +void memarea_free_callback_noop(void *context); + +#endif diff --git a/src/lib/mempool-allocfree.c b/src/lib/mempool-allocfree.c new file mode 100644 index 0000000..07b74a8 --- /dev/null +++ b/src/lib/mempool-allocfree.c @@ -0,0 +1,330 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +/* @UNSAFE: whole file */ +#include "lib.h" +#include "safe-memset.h" +#include "mempool.h" +#include "llist.h" + +/* + * As the name implies, allocfree pools support both allocating and freeing + * memory. + * + * Implementation + * ============== + * + * Each allocfree pool contains a pool structure (struct allocfree_pool) to + * keep track of allocfree-specific pool information and zero or more blocks + * (struct pool_block) that keep track of ranges of memory used to back the + * allocations. The blocks are kept in a doubly-linked list used to keep + * track of all allocations that belong to the pool. + * + * +-----------+ + * | allocfree | + * | pool | + * +-----+-----+ + * | + * | blocks +------------+ next +------------+ next + * \------->| pool block |<=====>| pool block |<=====>...<====> NULL + * +------------+ prev +------------+ prev + * | <data> | | <data> | + * . . + * . . + * . | <data> | + * . +------------+ + * | <data> | + * +------------+ + * + * Creation + * -------- + * + * When an allocfree pool is created the linked list of allocated blocks is + * initialized to be empty. + * + * Allocation & Freeing + * -------------------- + * + * Since each allocation (via p_malloc()) corresponds to one block, + * allocations are simply a matter of: + * + * - allocating enough memory from the system heap (via calloc()) to hold + * the block header and the requested number of bytes, + * - making a note of the user-requested size in the block header, + * - adding the new block to the pool's linked list of blocks, and + * - returning a pointer to the payload area of the block to the caller. + * + * Freeing memory is simpler. The passed in pointer is converted to a + * struct pool_block pointer. Then the block is removed from the pool's + * linked list and free()d. + * + * If the pool was created via pool_allocfree_create_clean(), all blocks are + * safe_memset() to zero just before being free()d. + * + * Reallocation + * ------------ + * + * Reallocation is done by calling realloc() with a new size that is large + * enough to cover the requested number of bytes plus the block header + * overhead. + * + * Clearing + * -------- + * + * Clearing the pool is supposed to return the pool to the same state it was + * in when it was first created. To that end, the allocfree pool frees all + * the blocks allocated since the pool's creation. In other words, clearing + * is equivalent to (but faster than) calling p_free() for each allocation + * in the pool. + * + * Finally, if the pool was created via pool_allocfree_create_clean(), all + * blocks are safe_memset() to zero before being free()d. + * + * Destruction + * ----------- + * + * Destroying a pool first clears it (see above) and then the pool structure + * itself is safe_memset() to zero (if pool_allocfree_create_clean() was + * used) and free()d. (The clearing leaves the pool in a minimal state + * with no blocks allocated.) + */ + +struct allocfree_pool { + struct pool pool; + int refcount; + size_t total_alloc_count; + size_t total_alloc_used; + + struct pool_block *blocks; +#ifdef DEBUG + char *name; +#endif + bool clean_frees; +}; + +struct pool_block { + struct pool_block *prev,*next; + + size_t size; + unsigned char *block; +}; + +#define SIZEOF_ALLOCFREE_POOL MEM_ALIGN(sizeof(struct allocfree_pool)) +#define SIZEOF_POOLBLOCK (MEM_ALIGN(sizeof(struct pool_block))) + +static const char *pool_allocfree_get_name(pool_t pool); +static void pool_allocfree_ref(pool_t pool); +static void pool_allocfree_unref(pool_t *pool); +static void *pool_allocfree_malloc(pool_t pool, size_t size); +static void pool_allocfree_free(pool_t pool, void *mem); +static void *pool_allocfree_realloc(pool_t pool, void *mem, + size_t old_size, size_t new_size); +static void pool_allocfree_clear(pool_t pool); +static size_t pool_allocfree_get_max_easy_alloc_size(pool_t pool); + +static const struct pool_vfuncs static_allocfree_pool_vfuncs = { + pool_allocfree_get_name, + + pool_allocfree_ref, + pool_allocfree_unref, + + pool_allocfree_malloc, + pool_allocfree_free, + + pool_allocfree_realloc, + + pool_allocfree_clear, + pool_allocfree_get_max_easy_alloc_size +}; + +static const struct pool static_allocfree_pool = { + .v = &static_allocfree_pool_vfuncs, + + .alloconly_pool = FALSE, + .datastack_pool = FALSE +}; + +pool_t pool_allocfree_create(const char *name ATTR_UNUSED) +{ + struct allocfree_pool *pool; + + if (SIZEOF_POOLBLOCK > (SSIZE_T_MAX - POOL_MAX_ALLOC_SIZE)) + i_panic("POOL_MAX_ALLOC_SIZE is too large"); + + pool = calloc(1, SIZEOF_ALLOCFREE_POOL); + if (pool == NULL) + i_fatal_status(FATAL_OUTOFMEM, "calloc(1, %zu): Out of memory", + SIZEOF_ALLOCFREE_POOL); +#ifdef DEBUG + pool->name = strdup(name); +#endif + pool->pool = static_allocfree_pool; + pool->refcount = 1; + return &pool->pool; +} + +pool_t pool_allocfree_create_clean(const char *name) +{ + struct allocfree_pool *apool; + pool_t pool; + + pool = pool_allocfree_create(name); + apool = (struct allocfree_pool *)pool; + apool->clean_frees = TRUE; + return pool; +} + +static void pool_allocfree_destroy(struct allocfree_pool *apool) +{ + pool_allocfree_clear(&apool->pool); + if (apool->clean_frees) + safe_memset(apool, 0, SIZEOF_ALLOCFREE_POOL); +#ifdef DEBUG + free(apool->name); +#endif + free(apool); +} + +static const char *pool_allocfree_get_name(pool_t pool ATTR_UNUSED) +{ +#ifdef DEBUG + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + return apool->name; +#else + return "alloc"; +#endif +} + +static void pool_allocfree_ref(pool_t pool) +{ + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + i_assert(apool->refcount > 0); + + apool->refcount++; +} + +static void pool_allocfree_unref(pool_t *_pool) +{ + pool_t pool = *_pool; + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + i_assert(apool->refcount > 0); + + /* erase the pointer before freeing anything, as the pointer may + exist inside the pool's memory area */ + *_pool = NULL; + + if (--apool->refcount > 0) + return; + + pool_allocfree_destroy(apool); +} + +static void *pool_block_attach(struct allocfree_pool *apool, struct pool_block *block) +{ + i_assert(block->size > 0); + DLLIST_PREPEND(&apool->blocks, block); + block->block = PTR_OFFSET(block,SIZEOF_POOLBLOCK); + apool->total_alloc_used += block->size; + apool->total_alloc_count++; + return block->block; +} + +static struct pool_block * +pool_block_detach(struct allocfree_pool *apool, unsigned char *mem) +{ + /* cannot use PTR_OFFSET because of negative value */ + i_assert((uintptr_t)mem >= SIZEOF_POOLBLOCK); + struct pool_block *block = (struct pool_block *)(mem - SIZEOF_POOLBLOCK); + + /* make sure the block we are dealing with is correct */ + i_assert(block->block == mem); + i_assert((block->prev == NULL || block->prev->next == block) && + (block->next == NULL || block->next->prev == block)); + + i_assert(apool->total_alloc_used >= block->size); + i_assert(apool->total_alloc_count > 0); + DLLIST_REMOVE(&apool->blocks, block); + apool->total_alloc_used -= block->size; + apool->total_alloc_count--; + + return block; +} + +static void *pool_allocfree_malloc(pool_t pool, size_t size) +{ + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + + struct pool_block *block = calloc(1, SIZEOF_POOLBLOCK + size); + if (block == NULL) + i_fatal_status(FATAL_OUTOFMEM, "calloc(1, %zu): Out of memory", + SIZEOF_POOLBLOCK + size); + block->size = size; + return pool_block_attach(apool, block); +} + +static void pool_allocfree_free(pool_t pool, void *mem) +{ + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + struct pool_block *block = pool_block_detach(apool, mem); + if (apool->clean_frees) + safe_memset(block, 0, SIZEOF_POOLBLOCK+block->size); + free(block); +} + +static void *pool_allocfree_realloc(pool_t pool, void *mem, + size_t old_size, size_t new_size) +{ + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + unsigned char *new_mem; + + struct pool_block *block = pool_block_detach(apool, mem); + if ((new_mem = realloc(block, SIZEOF_POOLBLOCK+new_size)) == NULL) + i_fatal_status(FATAL_OUTOFMEM, "realloc(block, %zu)", + SIZEOF_POOLBLOCK+new_size); + + /* zero out new memory */ + if (new_size > old_size) + memset(new_mem + SIZEOF_POOLBLOCK + old_size, 0, + new_size - old_size); + block = (struct pool_block*)new_mem; + block->size = new_size; + return pool_block_attach(apool, block); +} + +static void pool_allocfree_clear(pool_t pool) +{ + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + struct pool_block *block, *next; + + for (block = apool->blocks; block != NULL; block = next) { + next = block->next; + pool_allocfree_free(pool, block->block); + } + i_assert(apool->total_alloc_used == 0 && apool->total_alloc_count == 0); +} + +static size_t pool_allocfree_get_max_easy_alloc_size(pool_t pool ATTR_UNUSED) +{ + return 0; +} + +size_t pool_allocfree_get_total_used_size(pool_t pool) +{ + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + return apool->total_alloc_used; +} + +size_t pool_allocfree_get_total_alloc_size(pool_t pool) +{ + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + return apool->total_alloc_used + + SIZEOF_POOLBLOCK*apool->total_alloc_count + sizeof(*apool); +} diff --git a/src/lib/mempool-alloconly.c b/src/lib/mempool-alloconly.c new file mode 100644 index 0000000..26f3360 --- /dev/null +++ b/src/lib/mempool-alloconly.c @@ -0,0 +1,546 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +/* @UNSAFE: whole file */ +#include "lib.h" +#include "safe-memset.h" +#include "mempool.h" + +/* + * As the name implies, alloconly pools support only allocating memory. + * Memory freeing is not supported, except as a special case - the pool's + * last allocation can be freed. Additionally, p_realloc() also tries to + * grow an existing allocation if and only if it is the last allocation, + * otherwise it just allocates a new memory area and copies the data there. + * + * Alloconly pools are commonly used for an object that builds its state + * from many memory allocations, but doesn't change (much of) its state. + * It is simpler to free such an object by destroying the entire memory + * pool. + * + * Implementation + * ============== + * + * Each alloconly pool contains a pool structure (struct alloconly_pool) to + * keep track of alloconly-specific pool information and one or more blocks + * (struct pool_block) that keep track of ranges of memory used to back the + * allocations. The blocks are kept in a linked list implementing a stack. + * The block size decreases the further down the stack one goes. + * + * +-----------+ + * | alloconly | + * | pool | + * +-----+-----+ + * | + * | block +------------+ next +------------+ next + * \------->| pool block |------>| pool block |------>... + * +------------+ +------------+ + * | <data> | | <data> | + * . . + * . . + * . | <data> | + * . +------------+ + * | <data> | + * +------------+ + * + * Creation + * -------- + * + * When an alloconly pool is created, one block is allocated. This block is + * large enough to hold the necessary internal structures (struct + * alloconly_pool and struct pool_block) and still have enough space to + * satisfy allocations for at least the amount of space requested by the + * consumer via the size argument to pool_alloconly_create(). + * + * Allocation + * ---------- + * + * Each allocation (via p_malloc()) checks the top-most block to see whether + * or not it has enough space to satisfy the allocation. If there is not + * enough space, it allocates a new block (via block_alloc()) to serve as + * the new top-most block. This newly-allocated block is guaranteed to have + * enough space for the allocation. Then, regardless of whether or not a + * new block was allocated, the allocation code reserves enough space in the + * top-most block for the allocation and returns a pointer to it to the + * caller. + * + * The free space tracking within each block is very simple. In addition to + * keeping track of the size of the block, the block header contains a + * "pointer" to the beginning of free space. A new allocation simply moves + * this pointer by the number of bytes allocated. + * + * Reallocation + * ------------ + * + * If the passed in allocation is the last allocation in a block and there + * is enough space after it, the allocation is resized. Otherwise, a new + * buffer is allocated (see Allocation above) and the contents are copied + * over. + * + * Freeing + * ------- + * + * Freeing of the last allocation moves the "pointer" to free space back by + * the size of the last allocation. + * + * Freeing of any other allocation is a no-op. + * + * Clearing + * -------- + * + * Clearing the pool is supposed to return the pool to the same state it was + * in when it was first created. To that end, the alloconly pool frees all + * the blocks allocated since the pool's creation. The remaining block + * (allocated during creation) is reset to consider all the space for + * allocations as available. + * + * In other words, the per-block free space tracking variables are set to + * indicate that the full block is available and that there have been no + * allocations. + * + * Finally, if the pool was created via pool_alloconly_create_clean(), all + * blocks are safe_memset()/memset() to zero before being free()d. + * + * Destruction + * ----------- + * + * Destroying a pool first clears it (see above). The clearing leaves the + * pool in a minimal state with only one block allocated. This remaining + * block may be safe_memset() to zero if the pool was created with + * pool_alloconly_create_clean(). + * + * Since the pool structure itself is allocated from the first block, this + * final call to free() will release the memory allocated for struct + * alloconly_pool and struct pool. + */ + +#ifndef DEBUG +# define POOL_ALLOCONLY_MAX_EXTRA MEM_ALIGN(1) +#else +# define POOL_ALLOCONLY_MAX_EXTRA \ + (MEM_ALIGN(sizeof(size_t)) + MEM_ALIGN(1) + MEM_ALIGN(SENTRY_COUNT)) +#endif + +struct alloconly_pool { + struct pool pool; + int refcount; + + struct pool_block *block; +#ifdef DEBUG + const char *name; + size_t base_size; + bool disable_warning; +#endif + bool clean_frees; +}; + +struct pool_block { + struct pool_block *prev; + + size_t size; + size_t left; + size_t last_alloc_size; + + /* unsigned char data[]; */ +}; +#define SIZEOF_POOLBLOCK (MEM_ALIGN(sizeof(struct pool_block))) + +#define POOL_BLOCK_DATA(block) \ + ((unsigned char *) (block) + SIZEOF_POOLBLOCK) + +#define DEFAULT_BASE_SIZE MEM_ALIGN(sizeof(struct alloconly_pool)) + +#ifdef DEBUG +# define CLEAR_CHR 0xde +# define SENTRY_COUNT 8 +#else +# define SENTRY_COUNT 0 +# define CLEAR_CHR 0 +#endif + +static const char *pool_alloconly_get_name(pool_t pool); +static void pool_alloconly_ref(pool_t pool); +static void pool_alloconly_unref(pool_t *pool); +static void *pool_alloconly_malloc(pool_t pool, size_t size); +static void pool_alloconly_free(pool_t pool, void *mem); +static void *pool_alloconly_realloc(pool_t pool, void *mem, + size_t old_size, size_t new_size); +static void pool_alloconly_clear(pool_t pool); +static size_t pool_alloconly_get_max_easy_alloc_size(pool_t pool); + +static void block_alloc(struct alloconly_pool *pool, size_t size); + +static const struct pool_vfuncs static_alloconly_pool_vfuncs = { + pool_alloconly_get_name, + + pool_alloconly_ref, + pool_alloconly_unref, + + pool_alloconly_malloc, + pool_alloconly_free, + + pool_alloconly_realloc, + + pool_alloconly_clear, + pool_alloconly_get_max_easy_alloc_size +}; + +static const struct pool static_alloconly_pool = { + .v = &static_alloconly_pool_vfuncs, + + .alloconly_pool = TRUE, + .datastack_pool = FALSE +}; + +#ifdef DEBUG +static void check_sentries(struct pool_block *block) +{ + const unsigned char *data = POOL_BLOCK_DATA(block); + size_t i, max_pos, alloc_size, used_size; + + used_size = block->size - block->left; + for (i = 0; i < used_size; ) { + alloc_size = *(size_t *)(data + i); + if (alloc_size == 0 || used_size - i < alloc_size) + i_panic("mempool-alloconly: saved alloc size broken"); + i += MEM_ALIGN(sizeof(alloc_size)); + max_pos = i + MEM_ALIGN(alloc_size + SENTRY_COUNT); + i += alloc_size; + + for (; i < max_pos; i++) { + if (data[i] != CLEAR_CHR) + i_panic("mempool-alloconly: buffer overflow"); + } + } + + if (i != used_size) + i_panic("mempool-alloconly: used_size wrong"); + + /* The unused data must be NULs */ + for (; i < block->size; i++) { + if (data[i] != '\0') + i_unreached(); + } + if (block->prev != NULL) + check_sentries(block->prev); +} +#endif + +pool_t pool_alloconly_create(const char *name ATTR_UNUSED, size_t size) +{ + struct alloconly_pool apool, *new_apool; + size_t min_alloc = SIZEOF_POOLBLOCK + + MEM_ALIGN(sizeof(struct alloconly_pool) + SENTRY_COUNT); + + if (POOL_ALLOCONLY_MAX_EXTRA > (SSIZE_T_MAX - POOL_MAX_ALLOC_SIZE)) + i_panic("POOL_MAX_ALLOC_SIZE is too large"); + +#ifdef DEBUG + min_alloc += MEM_ALIGN(strlen(name) + 1 + SENTRY_COUNT) + + sizeof(size_t)*2; +#endif + + /* create a fake alloconly_pool so we can call block_alloc() */ + i_zero(&apool); + apool.pool = static_alloconly_pool; + apool.refcount = 1; + + if (size < min_alloc) + size = nearest_power(size + min_alloc); + block_alloc(&apool, size); + + /* now allocate the actual alloconly_pool from the created block */ + new_apool = p_new(&apool.pool, struct alloconly_pool, 1); + *new_apool = apool; +#ifdef DEBUG + if (str_begins(name, MEMPOOL_GROWING) || + getenv("DEBUG_SILENT") != NULL) { + name += strlen(MEMPOOL_GROWING); + new_apool->disable_warning = TRUE; + } + new_apool->name = p_strdup(&new_apool->pool, name); + + /* set base_size so p_clear() doesn't trash alloconly_pool structure. */ + new_apool->base_size = new_apool->block->size - new_apool->block->left; + new_apool->block->last_alloc_size = 0; +#endif + /* the first pool allocations must be from the first block */ + i_assert(new_apool->block->prev == NULL); + + return &new_apool->pool; +} + +pool_t pool_alloconly_create_clean(const char *name, size_t size) +{ + struct alloconly_pool *apool; + pool_t pool; + + pool = pool_alloconly_create(name, size); + apool = container_of(pool, struct alloconly_pool, pool); + apool->clean_frees = TRUE; + return pool; +} + +static void pool_alloconly_free_block(struct alloconly_pool *apool ATTR_UNUSED, + struct pool_block *block) +{ +#ifdef DEBUG + safe_memset(block, CLEAR_CHR, SIZEOF_POOLBLOCK + block->size); +#else + if (apool->clean_frees) { + safe_memset(block, CLEAR_CHR, + SIZEOF_POOLBLOCK + block->size); + } +#endif + free(block); +} + +static void +pool_alloconly_free_blocks_until_last(struct alloconly_pool *apool) +{ + struct pool_block *block; + + /* destroy all blocks but the oldest, which contains the + struct alloconly_pool allocation. */ + while (apool->block->prev != NULL) { + block = apool->block; + apool->block = block->prev; + + pool_alloconly_free_block(apool, block); + } +} + +static void pool_alloconly_destroy(struct alloconly_pool *apool) +{ + /* destroy all but the last block */ + pool_alloconly_free_blocks_until_last(apool); + + /* destroy the last block */ + pool_alloconly_free_block(apool, apool->block); +} + +static const char *pool_alloconly_get_name(pool_t pool ATTR_UNUSED) +{ +#ifdef DEBUG + struct alloconly_pool *apool = + container_of(pool, struct alloconly_pool, pool); + + return apool->name; +#else + return "alloconly"; +#endif +} + +static void pool_alloconly_ref(pool_t pool) +{ + struct alloconly_pool *apool = + container_of(pool, struct alloconly_pool, pool); + + apool->refcount++; +} + +static void pool_alloconly_unref(pool_t *pool) +{ + struct alloconly_pool *apool = + container_of(*pool, struct alloconly_pool, pool); + + /* erase the pointer before freeing anything, as the pointer may + exist inside the pool's memory area */ + *pool = NULL; + + if (--apool->refcount > 0) + return; + + pool_alloconly_destroy(apool); +} + +static void block_alloc(struct alloconly_pool *apool, size_t size) +{ + struct pool_block *block; + + i_assert(size > SIZEOF_POOLBLOCK); + i_assert(size <= SSIZE_T_MAX); + + if (apool->block != NULL) { + /* each block is at least twice the size of the previous one */ + if (size <= apool->block->size) + size += apool->block->size; + + /* avoid crashing in nearest_power() if size is too large */ + size = I_MIN(size, SSIZE_T_MAX); + size = nearest_power(size); + /* nearest_power() could have grown size to SSIZE_T_MAX+1 */ + size = I_MIN(size, SSIZE_T_MAX); +#ifdef DEBUG + if (!apool->disable_warning) { + /* i_debug() overwrites unallocated data in data + stack, so make sure everything is allocated before + calling it. */ + t_buffer_alloc_last_full(); + i_debug("Growing pool '%s' with: %zu", + apool->name, size); + } +#endif + } + + block = calloc(size, 1); + if (unlikely(block == NULL)) { + i_fatal_status(FATAL_OUTOFMEM, "block_alloc(%zu" + "): Out of memory", size); + } + block->prev = apool->block; + apool->block = block; + + block->size = size - SIZEOF_POOLBLOCK; + block->left = block->size; +} + +static void *pool_alloconly_malloc(pool_t pool, size_t size) +{ + struct alloconly_pool *apool = + container_of(pool, struct alloconly_pool, pool); + void *mem; + size_t alloc_size; + +#ifndef DEBUG + alloc_size = MEM_ALIGN(size); +#else + alloc_size = MEM_ALIGN(sizeof(size)) + MEM_ALIGN(size + SENTRY_COUNT); +#endif + + if (apool->block->left < alloc_size) { + /* we need a new block */ + block_alloc(apool, alloc_size + SIZEOF_POOLBLOCK); + } + + mem = POOL_BLOCK_DATA(apool->block) + + (apool->block->size - apool->block->left); + + apool->block->left -= alloc_size; + apool->block->last_alloc_size = alloc_size; +#ifdef DEBUG + memcpy(mem, &size, sizeof(size)); + mem = PTR_OFFSET(mem, MEM_ALIGN(sizeof(size))); + /* write CLEAR_CHRs to sentry */ + memset(PTR_OFFSET(mem, size), CLEAR_CHR, + MEM_ALIGN(size + SENTRY_COUNT) - size); +#endif + return mem; +} + +static void pool_alloconly_free(pool_t pool, void *mem) +{ + struct alloconly_pool *apool = + container_of(pool, struct alloconly_pool, pool); + + /* we can free only the last allocation */ + if (POOL_BLOCK_DATA(apool->block) + + (apool->block->size - apool->block->left - + apool->block->last_alloc_size) == mem) { + memset(mem, 0, apool->block->last_alloc_size); + apool->block->left += apool->block->last_alloc_size; + apool->block->last_alloc_size = 0; + } +} + +static bool pool_alloconly_try_grow(struct alloconly_pool *apool, void *mem, size_t size) +{ + /* see if we want to grow the memory we allocated last */ + if (POOL_BLOCK_DATA(apool->block) + + (apool->block->size - apool->block->left - + apool->block->last_alloc_size) == mem) { + /* yeah, see if we can grow */ + if (apool->block->left >= size-apool->block->last_alloc_size) { + /* just shrink the available size */ + apool->block->left -= + size - apool->block->last_alloc_size; + apool->block->last_alloc_size = size; + return TRUE; + } + } + + return FALSE; +} + +static void *pool_alloconly_realloc(pool_t pool, void *mem, + size_t old_size, size_t new_size) +{ + struct alloconly_pool *apool = + container_of(pool, struct alloconly_pool, pool); + unsigned char *new_mem; + + if (new_size <= old_size) + return mem; + + new_size = MEM_ALIGN(new_size); + + /* see if we can directly grow it */ + if (!pool_alloconly_try_grow(apool, mem, new_size)) { + /* slow way - allocate + copy */ + new_mem = pool_alloconly_malloc(pool, new_size); + memcpy(new_mem, mem, old_size); + mem = new_mem; + } + + return mem; +} + +static void pool_alloconly_clear(pool_t pool) +{ + struct alloconly_pool *apool = + container_of(pool, struct alloconly_pool, pool); + size_t base_size, avail_size; + +#ifdef DEBUG + check_sentries(apool->block); +#endif + + pool_alloconly_free_blocks_until_last(apool); + + /* clear the first block */ +#ifdef DEBUG + base_size = apool->base_size; +#else + base_size = DEFAULT_BASE_SIZE; +#endif + avail_size = apool->block->size - base_size; + memset(PTR_OFFSET(POOL_BLOCK_DATA(apool->block), base_size), 0, + avail_size - apool->block->left); + apool->block->left = avail_size; + apool->block->last_alloc_size = 0; +} + +static size_t pool_alloconly_get_max_easy_alloc_size(pool_t pool) +{ + struct alloconly_pool *apool = + container_of(pool, struct alloconly_pool, pool); + + return apool->block->left; +} + +size_t pool_alloconly_get_total_used_size(pool_t pool) +{ + struct alloconly_pool *apool = + container_of(pool, struct alloconly_pool, pool); + struct pool_block *block; + size_t size = 0; + + i_assert(pool->v == &static_alloconly_pool_vfuncs); + + for (block = apool->block; block != NULL; block = block->prev) + size += block->size - block->left; + return size; +} + +size_t pool_alloconly_get_total_alloc_size(pool_t pool) +{ + struct alloconly_pool *apool = + container_of(pool, struct alloconly_pool, pool); + struct pool_block *block; + size_t size = 0; + + i_assert(pool->v == &static_alloconly_pool_vfuncs); + + for (block = apool->block; block != NULL; block = block->prev) + size += block->size + SIZEOF_POOLBLOCK; + return size; +} diff --git a/src/lib/mempool-datastack.c b/src/lib/mempool-datastack.c new file mode 100644 index 0000000..b3c1094 --- /dev/null +++ b/src/lib/mempool-datastack.c @@ -0,0 +1,190 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "mempool.h" + +/* + * The datastack pool is a thin wrapper around the datastack API. It exists + * to allow datastack allocations via the pool API. + * + * Note: Do not confuse it with the *unsafe* datastack pool. + * + * Implementation + * ============== + * + * A datastack pool maintains information about the datastack frame that was + * in use when the pool was created so it can sanity check all p_new(), + * p_malloc(), and p_realloc() calls. + * + * Creation + * -------- + * + * When a datastack pool is created, a new pool structure is allocated from + * the datastack (via t_new()). The current datastack frame number is saved + * into the pool's private data (struct datastack_pool). + * + * Allocation & Reallocation + * ------------------------- + * + * After verifying that the saved datastack frame id matches the currently + * active one, the p_malloc() and p_realloc() calls get directed to + * t_malloc0() and t_try_realloc(), respectively. There is no + * per-allocation information to track. + * + * Freeing + * ------- + * + * Freeing is a no-op unless the currently active data stack frame id is + * different from the one saved during pool creation, in which case the + * process panics. + * + * Clearing + * -------- + * + * A no-op. + * + * Destruction + * ----------- + * + * Since the memory backing the pool structure itself is allocated from the + * datastack via t_new(), the pool and all allocations it made are freed + * when the datastack frame is popped. + * + * Even though the pool maintains a reference count, no memory is freed when + * it reaches zero. Once the reference count reaches zero, the state of the + * pool is undefined and none of its memory maybe be used. + */ + +static const char *pool_data_stack_get_name(pool_t pool); +static void pool_data_stack_ref(pool_t pool); +static void pool_data_stack_unref(pool_t *pool); +static void *pool_data_stack_malloc(pool_t pool, size_t size); +static void pool_data_stack_free(pool_t pool, void *mem); +static void *pool_data_stack_realloc(pool_t pool, void *mem, + size_t old_size, size_t new_size); +static void pool_data_stack_clear(pool_t pool); +static size_t pool_data_stack_get_max_easy_alloc_size(pool_t pool); + +static struct pool_vfuncs static_data_stack_pool_vfuncs = { + pool_data_stack_get_name, + + pool_data_stack_ref, + pool_data_stack_unref, + + pool_data_stack_malloc, + pool_data_stack_free, + + pool_data_stack_realloc, + + pool_data_stack_clear, + pool_data_stack_get_max_easy_alloc_size +}; + +static const struct pool static_data_stack_pool = { + .v = &static_data_stack_pool_vfuncs, + + .alloconly_pool = TRUE, + .datastack_pool = TRUE +}; + +struct datastack_pool { + struct pool pool; + int refcount; + + unsigned int data_stack_frame; +}; + +pool_t pool_datastack_create(void) +{ + struct datastack_pool *dpool; + + dpool = t_new(struct datastack_pool, 1); + dpool->pool = static_data_stack_pool; + dpool->refcount = 1; + dpool->data_stack_frame = data_stack_frame_id; + return &dpool->pool; +} + +static const char *pool_data_stack_get_name(pool_t pool ATTR_UNUSED) +{ + return "data stack"; +} + +static void pool_data_stack_ref(pool_t pool) +{ + struct datastack_pool *dpool = + container_of(pool, struct datastack_pool, pool); + + if (unlikely(dpool->data_stack_frame != data_stack_frame_id)) + i_panic("pool_data_stack_ref(): stack frame changed"); + + dpool->refcount++; +} + +static void pool_data_stack_unref(pool_t *pool) +{ + struct datastack_pool *dpool = + container_of(*pool, struct datastack_pool, pool); + + if (unlikely(dpool->data_stack_frame != data_stack_frame_id)) + i_panic("pool_data_stack_unref(): stack frame changed"); + + dpool->refcount--; + i_assert(dpool->refcount >= 0); + + *pool = NULL; +} + +static void *pool_data_stack_malloc(pool_t pool ATTR_UNUSED, size_t size) +{ + struct datastack_pool *dpool = + container_of(pool, struct datastack_pool, pool); + + if (unlikely(dpool->data_stack_frame != data_stack_frame_id)) + i_panic("pool_data_stack_malloc(): stack frame changed"); + + return t_malloc0(size); +} + +static void pool_data_stack_free(pool_t pool, void *mem ATTR_UNUSED) +{ + struct datastack_pool *dpool = + container_of(pool, struct datastack_pool, pool); + + if (unlikely(dpool->data_stack_frame != data_stack_frame_id)) + i_panic("pool_data_stack_free(): stack frame changed"); +} + +static void *pool_data_stack_realloc(pool_t pool, void *mem, + size_t old_size, size_t new_size) +{ + struct datastack_pool *dpool = + container_of(pool, struct datastack_pool, pool); + void *new_mem; + + /* @UNSAFE */ + if (unlikely(dpool->data_stack_frame != data_stack_frame_id)) + i_panic("pool_data_stack_realloc(): stack frame changed"); + + if (old_size >= new_size) + return mem; + + if (!t_try_realloc(mem, new_size)) { + new_mem = t_malloc_no0(new_size); + memcpy(new_mem, mem, old_size); + mem = new_mem; + } + + memset((char *) mem + old_size, 0, new_size - old_size); + return mem; +} + +static void pool_data_stack_clear(pool_t pool ATTR_UNUSED) +{ +} + +static size_t +pool_data_stack_get_max_easy_alloc_size(pool_t pool ATTR_UNUSED) +{ + return t_get_bytes_available(); +} diff --git a/src/lib/mempool-system.c b/src/lib/mempool-system.c new file mode 100644 index 0000000..7d1addc --- /dev/null +++ b/src/lib/mempool-system.c @@ -0,0 +1,163 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +/* @UNSAFE: whole file */ + +#include "lib.h" +#include "safe-memset.h" +#include "mempool.h" + +/* + * The system pool is a thin wrapper around calloc() and free(). It exists + * to allow direct heap usage via the pool API. + * + * Implementation + * ============== + * + * Creation + * -------- + * + * The system pool is created statically and therefore is available at any + * time. + * + * Allocation, Reallocation & Freeing + * ---------------------------------- + * + * The p_malloc(), p_realloc(), and p_free() calls get directed to calloc(), + * realloc(), and free(). There is no additional per-allocation information + * to track. + * + * Clearing + * -------- + * + * Not supported. Attempting to clear the system pool will result in a + * panic. + * + * Destruction + * ----------- + * + * It is not possible to destroy the system pool. Any attempt to unref the + * pool is a no-op. + */ + +#ifndef HAVE_MALLOC_USABLE_SIZE +/* no extra includes needed */ +#elif defined (HAVE_MALLOC_NP_H) +# include <malloc_np.h> /* FreeBSD */ +#elif defined (HAVE_MALLOC_H) +# include <malloc.h> /* Linux */ +#endif + +#define CLEAR_CHR 0xde + +static const char *pool_system_get_name(pool_t pool); +static void pool_system_ref(pool_t pool); +static void pool_system_unref(pool_t *pool); +static void *pool_system_malloc(pool_t pool, size_t size); +static void *pool_system_realloc(pool_t pool, void *mem, + size_t old_size, size_t new_size); +static void pool_system_clear(pool_t pool); +static size_t pool_system_get_max_easy_alloc_size(pool_t pool); + +static struct pool_vfuncs static_system_pool_vfuncs = { + pool_system_get_name, + + pool_system_ref, + pool_system_unref, + + pool_system_malloc, + pool_system_free, + + pool_system_realloc, + + pool_system_clear, + pool_system_get_max_easy_alloc_size +}; + +struct pool static_system_pool = { + .v = &static_system_pool_vfuncs, + + .alloconly_pool = FALSE, + .datastack_pool = FALSE +}; + +pool_t system_pool = &static_system_pool; + +static const char *pool_system_get_name(pool_t pool ATTR_UNUSED) +{ + return "system"; +} + +static void pool_system_ref(pool_t pool ATTR_UNUSED) +{ +} + +static void pool_system_unref(pool_t *pool ATTR_UNUSED) +{ +} + +static void *pool_system_malloc(pool_t pool ATTR_UNUSED, size_t size) +{ + void *mem; +#ifdef DEBUG + int old_errno = errno; +#endif + + mem = calloc(size, 1); + if (unlikely(mem == NULL)) { + i_fatal_status(FATAL_OUTOFMEM, "pool_system_malloc(%zu): " + "Out of memory", size); + } +#ifdef DEBUG + /* we rely on errno not changing. it shouldn't. */ + i_assert(errno == old_errno); +#endif + return mem; +} + +void pool_system_free(pool_t pool ATTR_UNUSED, void *mem ATTR_UNUSED) +{ +#ifdef DEBUG + int old_errno = errno; +#endif +#if defined(HAVE_MALLOC_USABLE_SIZE) && defined(DEBUG) + safe_memset(mem, CLEAR_CHR, malloc_usable_size(mem)); +#endif + free(mem); +#ifdef DEBUG + /* we rely on errno not changing. it shouldn't. */ + i_assert(errno == old_errno); +#endif +} + +static void *pool_system_realloc(pool_t pool ATTR_UNUSED, void *mem, + size_t old_size, size_t new_size) +{ +#if defined(HAVE_MALLOC_USABLE_SIZE) + i_assert(old_size == SIZE_MAX || mem == NULL || + old_size <= malloc_usable_size(mem)); +#endif + + mem = realloc(mem, new_size); + if (unlikely(mem == NULL)) { + i_fatal_status(FATAL_OUTOFMEM, "pool_system_realloc(%zu): " + "Out of memory", new_size); + } + + if (old_size < new_size) { + /* clear new data */ + memset((char *) mem + old_size, 0, new_size - old_size); + } + + return mem; +} + +static void ATTR_NORETURN +pool_system_clear(pool_t pool ATTR_UNUSED) +{ + i_panic("pool_system_clear() must not be called"); +} + +static size_t pool_system_get_max_easy_alloc_size(pool_t pool ATTR_UNUSED) +{ + return 0; +} diff --git a/src/lib/mempool-unsafe-datastack.c b/src/lib/mempool-unsafe-datastack.c new file mode 100644 index 0000000..2f2751d --- /dev/null +++ b/src/lib/mempool-unsafe-datastack.c @@ -0,0 +1,135 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "mempool.h" + +/* + * The unsafe datastack pool is a very thin wrapper around the datastack + * API. It is a simpler version of the datastack pool that does not do any + * sanity checking, it simply forwards the calls to the datastack API. It + * exists to allow some internal APIs to make datastack allocations via the + * pool API. + * + * Note to consumers: Consider using the (safe) datastack pool instead of + * this one. + * + * Implementation + * ============== + * + * Creation + * -------- + * + * The unsafe datastack pool is created statically and therefore is + * available at any time after the datastack allocator is initialized. + * + * Allocation & Reallocation + * ------------------------- + * + * The p_malloc() and p_realloc() calls get directed to t_malloc0() and + * t_try_realloc(), respectively. There is no additional per-allocation + * information to track. + * + * Freeing + * ------- + * + * A no-op. + * + * Clearing + * -------- + * + * A no-op. + * + * Destruction + * ----------- + * + * It is not possible to destroy the unsafe datastack pool. Any attempt to + * unref the pool is a no-op. + */ + +static const char *pool_unsafe_data_stack_get_name(pool_t pool); +static void pool_unsafe_data_stack_ref(pool_t pool); +static void pool_unsafe_data_stack_unref(pool_t *pool); +static void *pool_unsafe_data_stack_malloc(pool_t pool, size_t size); +static void pool_unsafe_data_stack_free(pool_t pool, void *mem); +static void *pool_unsafe_data_stack_realloc(pool_t pool, void *mem, + size_t old_size, size_t new_size); +static void pool_unsafe_data_stack_clear(pool_t pool); +static size_t pool_unsafe_data_stack_get_max_easy_alloc_size(pool_t pool); + +static struct pool_vfuncs static_unsafe_data_stack_pool_vfuncs = { + pool_unsafe_data_stack_get_name, + + pool_unsafe_data_stack_ref, + pool_unsafe_data_stack_unref, + + pool_unsafe_data_stack_malloc, + pool_unsafe_data_stack_free, + + pool_unsafe_data_stack_realloc, + + pool_unsafe_data_stack_clear, + pool_unsafe_data_stack_get_max_easy_alloc_size +}; + +static struct pool static_unsafe_data_stack_pool = { + .v = &static_unsafe_data_stack_pool_vfuncs, + + .alloconly_pool = TRUE, + .datastack_pool = TRUE +}; + +pool_t unsafe_data_stack_pool = &static_unsafe_data_stack_pool; + +static const char *pool_unsafe_data_stack_get_name(pool_t pool ATTR_UNUSED) +{ + return "unsafe data stack"; +} + +static void pool_unsafe_data_stack_ref(pool_t pool ATTR_UNUSED) +{ +} + +static void pool_unsafe_data_stack_unref(pool_t *pool ATTR_UNUSED) +{ +} + +static void *pool_unsafe_data_stack_malloc(pool_t pool ATTR_UNUSED, + size_t size) +{ + return t_malloc0(size); +} + +static void pool_unsafe_data_stack_free(pool_t pool ATTR_UNUSED, + void *mem ATTR_UNUSED) +{ +} + +static void *pool_unsafe_data_stack_realloc(pool_t pool ATTR_UNUSED, + void *mem, + size_t old_size, size_t new_size) +{ + void *new_mem; + + /* @UNSAFE */ + if (old_size >= new_size) + return mem; + + if (!t_try_realloc(mem, new_size)) { + new_mem = t_malloc_no0(new_size); + memcpy(new_mem, mem, old_size); + mem = new_mem; + } + + memset((char *) mem + old_size, 0, new_size - old_size); + return mem; +} + +static void pool_unsafe_data_stack_clear(pool_t pool ATTR_UNUSED) +{ +} + +static size_t +pool_unsafe_data_stack_get_max_easy_alloc_size(pool_t pool ATTR_UNUSED) +{ + return t_get_bytes_available(); +} diff --git a/src/lib/mempool.c b/src/lib/mempool.c new file mode 100644 index 0000000..a657481 --- /dev/null +++ b/src/lib/mempool.c @@ -0,0 +1,25 @@ +/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" + +/* The various implementations of pools API assume that they'll never be + asked for more than SSIZE_T_MAX bytes. This is a sanity check to make + sure nobody accidentally bumped the define beyond what's expected. */ +#if POOL_MAX_ALLOC_SIZE > SSIZE_T_MAX +#error "POOL_MAX_ALLOC_SIZE is too large" +#endif + +size_t pool_get_exp_grown_size(pool_t pool, size_t old_size, size_t min_size) +{ + size_t exp_size, easy_size; + + i_assert(old_size < min_size); + + exp_size = nearest_power(min_size); + easy_size = old_size + p_get_max_easy_alloc_size(pool); + + if (easy_size < exp_size && easy_size >= min_size) + exp_size = easy_size; + i_assert(exp_size >= min_size); + return exp_size; +} diff --git a/src/lib/mempool.h b/src/lib/mempool.h new file mode 100644 index 0000000..9c7aca0 --- /dev/null +++ b/src/lib/mempool.h @@ -0,0 +1,179 @@ +#ifndef MEMPOOL_H +#define MEMPOOL_H + +#include "macros.h" + +/* When DEBUG is enabled, Dovecot warns whenever a memory pool is grown. + This is done so that the initial pool size could be set large enough so that + it wouldn't grow in normal use. For some memory pools it's too difficult + to calculate a good initial size, so this prefix should be used with those + pools to disable the warning. */ +#define MEMPOOL_GROWING "GROWING-" + +/* The maximum allocation size that's allowed. Anything larger than that + will panic. No pool ever should need more than 4kB of overhead per + allocation. */ +#define POOL_MAX_ALLOC_SIZE (SSIZE_T_MAX - 4096) + +/* Memory allocated and reallocated (the new data in it) in pools is always + zeroed, it will cost only a few CPU cycles and may well save some debug + time. */ + +typedef struct pool *pool_t; + +struct pool_vfuncs { + const char *(*get_name)(pool_t pool); + + void (*ref)(pool_t pool); + void (*unref)(pool_t *pool); + + void *(*malloc)(pool_t pool, size_t size) ATTR_RETURNS_NONNULL; + void (*free)(pool_t pool, void *mem); + + /* memory in old_size..new_size will be zeroed */ + void *(*realloc)(pool_t pool, void *mem, + size_t old_size, size_t new_size) + ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; + + /* Frees all the memory in pool. NOTE: system_pool doesn't support + this and crashes if it's used */ + void (*clear)(pool_t pool); + + /* Returns the maximum amount of bytes that can be allocated with + minimal trouble. If there's no such concept, always returns 0. */ + size_t (*get_max_easy_alloc_size)(pool_t pool); +}; + +struct pool { + const struct pool_vfuncs *v; + + bool alloconly_pool:1; + bool datastack_pool:1; +}; + +/* system_pool uses calloc() + realloc() + free() */ +extern pool_t system_pool; +extern struct pool static_system_pool; + +/* memory allocated from data_stack is valid only until next t_pop() call. + No checks are performed. */ +extern pool_t unsafe_data_stack_pool; + +/* Create a new alloc-only pool. Note that `size' specifies the initial + malloc()ed block size, part of it is used internally. */ +pool_t pool_alloconly_create(const char *name, size_t size); +/* Like alloconly pool, but clear the memory before freeing it. The idea is + that you could allocate memory for storing sensitive information from this + pool, and be sure that it gets cleared from the memory when it's no longer + needed. */ +pool_t pool_alloconly_create_clean(const char *name, size_t size); + +/* When allocating memory from returned pool, the data stack frame must be + the same as it was when calling this function. pool_unref() also checks + that the stack frame is the same. This should make it quite safe to use. */ +pool_t pool_datastack_create(void); + +/* Create new alloc pool. This is very similar to system pool, but it + will deallocate all memory on deinit. */ +pool_t pool_allocfree_create(const char *name); + +/* Like alloc pool, but all memory is cleaned before freeing. + See pool_alloconly_create_clean. */ +pool_t pool_allocfree_create_clean(const char *name); + +/* Similar to nearest_power(), but try not to exceed buffer's easy + allocation size. If you don't have any explicit minimum size, use + old_size + 1. */ +size_t pool_get_exp_grown_size(pool_t pool, size_t old_size, size_t min_size); + +/* We require sizeof(type) to be <= UINT_MAX. This allows compiler to optimize + away the entire MALLOC_MULTIPLY() call on 64bit systems. */ +#define p_new(pool, type, count) \ + ((type *) p_malloc(pool, MALLOC_MULTIPLY((unsigned int)sizeof(type), (count))) + \ + COMPILE_ERROR_IF_TRUE(sizeof(type) > UINT_MAX)) + +#define p_realloc_type(pool, mem, type, old_count, new_count) \ + ((type *) p_realloc(pool, mem, \ + MALLOC_MULTIPLY((unsigned int)sizeof(type), (old_count)), \ + MALLOC_MULTIPLY((unsigned int)sizeof(type), (new_count))) + \ + COMPILE_ERROR_IF_TRUE(sizeof(type) > UINT_MAX)) + +static inline void * ATTR_MALLOC ATTR_RETURNS_NONNULL +p_malloc(pool_t pool, size_t size) +{ + if (unlikely(size == 0 || size > POOL_MAX_ALLOC_SIZE)) + i_panic("Trying to allocate %zu bytes", size); + + return pool->v->malloc(pool, size); +} + +static inline void * ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL +p_realloc(pool_t pool, void *mem, size_t old_size, size_t new_size) +{ + if (unlikely(new_size == 0 || new_size > POOL_MAX_ALLOC_SIZE)) + i_panic("Trying to reallocate %zu -> %zu bytes", + old_size, new_size); + + if (mem == NULL) + return pool->v->malloc(pool, new_size); + + return pool->v->realloc(pool, mem, old_size, new_size); +} + +/* Free the memory. p_free() and p_free_and_null() are now guaranteed to both + set mem=NULL, so either one of them can be used. */ +#define p_free(pool, mem) \ + STMT_START { \ + p_free_internal(pool, mem); \ + (mem) = NULL; \ + } STMT_END +#define p_free_and_null(pool, mem) p_free(pool, mem) + +static inline void p_free_internal(pool_t pool, void *mem) +{ + if (mem != NULL) + pool->v->free(pool, mem); +} + +static inline void p_clear(pool_t pool) +{ + pool->v->clear(pool); +} + +static inline size_t p_get_max_easy_alloc_size(pool_t pool) +{ + return pool->v->get_max_easy_alloc_size(pool); +} + +static inline const char *pool_get_name(pool_t pool) +{ + return pool->v->get_name(pool); +} + +static inline void pool_ref(pool_t pool) +{ + pool->v->ref(pool); +} + +static inline void pool_unref(pool_t *pool) +{ + if (*pool != NULL) + (*pool)->v->unref(pool); +} + +/* These functions are only for pools created with pool_alloconly_create(): */ + +/* Returns how much memory has been allocated from this pool. */ +size_t pool_alloconly_get_total_used_size(pool_t pool); +/* Returns how much system memory has been allocated for this pool. */ +size_t pool_alloconly_get_total_alloc_size(pool_t pool); + +/* Returns how much memory has been allocated from this pool. */ +size_t pool_allocfree_get_total_used_size(pool_t pool); +/* Returns how much system memory has been allocated for this pool. */ +size_t pool_allocfree_get_total_alloc_size(pool_t pool); + +/* private: */ +void pool_system_free(pool_t pool, void *mem); + +#endif diff --git a/src/lib/mkdir-parents.c b/src/lib/mkdir-parents.c new file mode 100644 index 0000000..93aa87d --- /dev/null +++ b/src/lib/mkdir-parents.c @@ -0,0 +1,177 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "eacces-error.h" +#include "mkdir-parents.h" +#include "ipwd.h" + +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +static int ATTR_NULL(5) +mkdir_chown_full(const char *path, mode_t mode, uid_t uid, + gid_t gid, const char *gid_origin) +{ + string_t *str; + mode_t old_mask; + unsigned int i; + int ret, fd = -1, orig_errno; + + for (i = 0;; i++) { + old_mask = umask(0); + ret = mkdir(path, mode); + umask(old_mask); + if (ret < 0) + break; + fd = open(path, O_RDONLY); + if (fd != -1) + break; + if (errno != ENOENT || i == 3) { + i_error("open(%s) failed: %m", path); + return -1; + } + /* it was just rmdir()ed by someone else? retry */ + } + + if (ret < 0) { + if (errno == EISDIR || errno == ENOSYS) { + /* EISDIR check is for BSD/OS which returns it if path + contains '/' at the end and it exists. + + ENOSYS check is for NFS mount points. */ + errno = EEXIST; + } + i_assert(fd == -1); + return -1; + } + if (fchown(fd, uid, gid) < 0) { + i_close_fd(&fd); + orig_errno = errno; + if (rmdir(path) < 0 && errno != ENOENT) + i_error("rmdir(%s) failed: %m", path); + errno = orig_errno; + + if (errno == EPERM && uid == (uid_t)-1) { + i_error("%s", eperm_error_get_chgrp("fchown", path, gid, + gid_origin)); + return -1; + } + + str = t_str_new(256); + str_printfa(str, "fchown(%s, %ld", path, + uid == (uid_t)-1 ? -1L : (long)uid); + if (uid != (uid_t)-1) { + struct passwd pw; + + if (i_getpwuid(uid, &pw) > 0) + str_printfa(str, "(%s)", pw.pw_name); + + } + str_printfa(str, ", %ld", + gid == (gid_t)-1 ? -1L : (long)gid); + if (gid != (gid_t)-1) { + struct group gr; + + if (i_getgrgid(uid, &gr) > 0) + str_printfa(str, "(%s)", gr.gr_name); + } + errno = orig_errno; + i_error("%s) failed: %m", str_c(str)); + return -1; + } + if (gid != (gid_t)-1 && (mode & S_ISGID) == 0) { + /* make sure the directory doesn't have setgid bit enabled + (in case its parent had) */ + if (fchmod(fd, mode) < 0) { + orig_errno = errno; + if (rmdir(path) < 0 && errno != ENOENT) + i_error("rmdir(%s) failed: %m", path); + errno = orig_errno; + i_error("fchmod(%s) failed: %m", path); + i_close_fd(&fd); + return -1; + } + } + i_close_fd(&fd); + return 0; +} + +int mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) +{ + return mkdir_chown_full(path, mode, uid, gid, NULL); +} + +int mkdir_chgrp(const char *path, mode_t mode, + gid_t gid, const char *gid_origin) +{ + return mkdir_chown_full(path, mode, (uid_t)-1, gid, gid_origin); +} + +static int ATTR_NULL(5) +mkdir_parents_chown_full(const char *path, mode_t mode, uid_t uid, gid_t gid, + const char *gid_origin) +{ + const char *p; + int ret; + + if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0) { + if (errno != ENOENT) + return -1; + + /* doesn't exist, try recursively creating our parent dir */ + p = strrchr(path, '/'); + if (p == NULL || p == path) + return -1; /* shouldn't happen */ + + T_BEGIN { + ret = mkdir_parents_chown_full(t_strdup_until(path, p), + mode, uid, + gid, gid_origin); + } T_END; + if (ret < 0 && errno != EEXIST) + return -1; + + /* should work now */ + if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0) + return -1; + } + return 0; +} + +int mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) +{ + return mkdir_parents_chown_full(path, mode, uid, gid, NULL); +} + +int mkdir_parents_chgrp(const char *path, mode_t mode, + gid_t gid, const char *gid_origin) +{ + return mkdir_parents_chown_full(path, mode, (uid_t)-1, gid, gid_origin); +} + +int mkdir_parents(const char *path, mode_t mode) +{ + return mkdir_parents_chown(path, mode, (uid_t)-1, (gid_t)-1); +} + +int stat_first_parent(const char *path, const char **root_dir_r, + struct stat *st_r) +{ + const char *p; + + while (stat(path, st_r) < 0) { + if (errno != ENOENT || strcmp(path, "/") == 0) { + *root_dir_r = path; + return -1; + } + p = strrchr(path, '/'); + if (p == NULL) + path = "/"; + else + path = t_strdup_until(path, p); + } + *root_dir_r = path; + return 0; +} diff --git a/src/lib/mkdir-parents.h b/src/lib/mkdir-parents.h new file mode 100644 index 0000000..9b5ddf0 --- /dev/null +++ b/src/lib/mkdir-parents.h @@ -0,0 +1,33 @@ +#ifndef MKDIR_PARENTS_H +#define MKDIR_PARENTS_H + +#include <sys/stat.h> + +/* Create path and all the directories under it if needed. Permissions for + existing directories isn't changed. Returns 0 if ok. If directory already + exists, returns -1 with errno=EEXIST. */ +int mkdir_parents(const char *path, mode_t mode); + +/* Like mkdir_parents(), but use the given uid/gid for newly created + directories. (uid_t)-1 or (gid_t)-1 can be used to indicate that it + doesn't need to be changed. If gid isn't (gid_t)-1 and the parent directory + had setgid-bit enabled, it's removed unless explicitly included in the + mode. */ +int mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); +/* Like mkdir_parents_chown(), but change only group. If chown() fails with + EACCES, use gid_origin in the error message. */ +int mkdir_parents_chgrp(const char *path, mode_t mode, + gid_t gid, const char *gid_origin); + +/* Like mkdir_parents_chown(), but don't actually create any parents. */ +int mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); +int mkdir_chgrp(const char *path, mode_t mode, + gid_t gid, const char *gid_origin); + +/* stat() the path or its first parent that exists. Returns 0 if ok, -1 if + failed. root_dir is set to the last stat()ed directory (on success and + on failure). */ +int stat_first_parent(const char *path, const char **root_dir_r, + struct stat *st_r); + +#endif diff --git a/src/lib/mmap-anon.c b/src/lib/mmap-anon.c new file mode 100644 index 0000000..fbb2c47 --- /dev/null +++ b/src/lib/mmap-anon.c @@ -0,0 +1,183 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +/* @UNSAFE: whole file */ + +#include "lib.h" +#include "mmap-util.h" + +#include <fcntl.h> + +#ifndef MAP_ANONYMOUS +# ifdef MAP_ANON +# define MAP_ANONYMOUS MAP_ANON +# else +# define MAP_ANONYMOUS 0 +# endif +#endif + +#ifndef HAVE_LINUX_MREMAP + +#include <sys/mman.h> + +#define MMAP_SIGNATURE 0xdeadbeef + +#define PAGE_ALIGN(size) \ + (((size) + (size_t)page_size-1) & ~(size_t)(page_size-1)) + +struct anon_header { + unsigned int signature; + size_t size; +}; + +static int page_size = 0; +static int header_size = 0; +static int zero_fd = -1; + +static void movable_mmap_init(void) +{ +#if MAP_ANONYMOUS == 0 + /* mmap()ing /dev/zero should be the same with some platforms */ + zero_fd = open("/dev/zero", O_RDWR); + if (zero_fd == -1) + i_fatal("Can't open /dev/zero for creating anonymous mmap: %m"); + fd_close_on_exec(zero_fd, TRUE); +#endif + + page_size = getpagesize(); + header_size = page_size; +} + +void *mmap_anon(size_t length) +{ + struct anon_header *hdr; + void *base; + + if (header_size == 0) + movable_mmap_init(); + + /* we need extra page to store the pieces which construct + the full mmap. also allocate only page-aligned mmap sizes. */ + length = PAGE_ALIGN(length + header_size); + + base = mmap(NULL, length, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, zero_fd, 0); + if (base == MAP_FAILED) + return MAP_FAILED; + + /* initialize the header */ + hdr = base; + hdr->signature = MMAP_SIGNATURE; + hdr->size = length - header_size; + + return (char *) hdr + header_size; +} + +static void *mremap_move(struct anon_header *hdr, size_t new_size) +{ + void *new_base; + char *p; + size_t block_size, old_size; + + new_base = mmap_anon(new_size); + if (new_base == MAP_FAILED) + return MAP_FAILED; + + /* If we're moving large memory areas, it takes less memory to + copy the memory pages in smaller blocks. */ + old_size = hdr->size; + block_size = 1024*1024; + + p = (char *) hdr + header_size + hdr->size; + do { + if (block_size > old_size) + block_size = old_size; + p -= block_size; + old_size -= block_size; + + memcpy((char *) new_base + old_size, p, block_size); + if (munmap((void *) p, block_size) < 0) + i_panic("munmap() failed: %m"); + } while (old_size != 0); + + if (munmap((void *) hdr, header_size) < 0) + i_panic("munmap() failed: %m"); + + return new_base; +} + +void *mremap_anon(void *old_address, size_t old_size ATTR_UNUSED, + size_t new_size, unsigned long flags) +{ + struct anon_header *hdr; + + if (old_address == NULL || old_address == MAP_FAILED) { + errno = EINVAL; + return MAP_FAILED; + } + + hdr = (struct anon_header *) ((char *) old_address - header_size); + if (hdr->signature != MMAP_SIGNATURE) + i_panic("movable_mremap(): Invalid old_address"); + + new_size = PAGE_ALIGN(new_size); + + if (new_size > hdr->size) { + /* grow */ + if ((flags & MREMAP_MAYMOVE) == 0) { + errno = ENOMEM; + return MAP_FAILED; + } + + return mremap_move(hdr, new_size); + } + + if (new_size < hdr->size) { + /* shrink */ + if (munmap((void *) ((char *) hdr + header_size + new_size), + hdr->size - new_size) < 0) + i_panic("munmap() failed: %m"); + hdr->size = new_size; + } + + return old_address; +} + +int munmap_anon(void *start, size_t length ATTR_UNUSED) +{ + struct anon_header *hdr; + + if (start == NULL || start == MAP_FAILED) { + errno = EINVAL; + return -1; + } + + hdr = (struct anon_header *) ((char *) start - header_size); + if (hdr->signature != MMAP_SIGNATURE) + i_panic("movable_munmap(): Invalid address"); + + if (munmap((void *) hdr, hdr->size + header_size) < 0) + i_panic("munmap() failed: %m"); + + return 0; +} + +#else + +void *mmap_anon(size_t length) +{ + return mmap(NULL, length, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); +} + +void *mremap_anon(void *old_address, size_t old_size, size_t new_size, + unsigned long flags) +{ + return mremap(old_address, old_size, new_size, flags); +} + +int munmap_anon(void *start, size_t length) +{ + return munmap(start, length); +} + +#endif diff --git a/src/lib/mmap-util.c b/src/lib/mmap-util.c new file mode 100644 index 0000000..8754226 --- /dev/null +++ b/src/lib/mmap-util.c @@ -0,0 +1,63 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "mmap-util.h" + +#include <sys/stat.h> + +void *mmap_file(int fd, size_t *length, int prot) +{ + struct stat st; + + if (fstat(fd, &st) < 0) + return MAP_FAILED; + +#if OFF_T_MAX > SSIZE_T_MAX + if (st.st_size > SSIZE_T_MAX) { + /* too large file to map into memory */ + errno = EFBIG; + return MAP_FAILED; + } +#endif + + *length = (size_t)st.st_size; + if (*length == 0) + return NULL; + + i_assert(*length > 0 && *length < SSIZE_T_MAX); + + return mmap(NULL, *length, prot, MAP_SHARED, fd, 0); +} + +void *mmap_ro_file(int fd, size_t *length) +{ + return mmap_file(fd, length, PROT_READ); +} + +void *mmap_rw_file(int fd, size_t *length) +{ + return mmap_file(fd, length, PROT_READ | PROT_WRITE); +} + +#undef madvise +int my_madvise(void *start ATTR_UNUSED, size_t length ATTR_UNUSED, + int advice ATTR_UNUSED) +{ +#ifdef HAVE_MADVISE + /* Ignore ENOSYS errors, which happen if the kernel hasn't implemented + the syscall even if libc has. */ + if (madvise(start, length, advice) < 0 && errno != ENOSYS) + return -1; +#endif + return 0; +} + +size_t mmap_get_page_size(void) +{ + static size_t size = 0; + + if (size != 0) + return size; + size = getpagesize(); + return size; +} diff --git a/src/lib/mmap-util.h b/src/lib/mmap-util.h new file mode 100644 index 0000000..0f4184e --- /dev/null +++ b/src/lib/mmap-util.h @@ -0,0 +1,42 @@ +#ifndef MMAP_UTIL_H +#define MMAP_UTIL_H + +#include <unistd.h> + +#ifdef HAVE_LINUX_MREMAP +# define __USE_GNU /* for MREMAP_MAYMOVE */ +#endif + +#include <sys/mman.h> +#undef __USE_GNU + +#if !defined (MREMAP_MAYMOVE) && !defined (HAVE_LINUX_MREMAP) +# define MREMAP_MAYMOVE 1 +#endif + +#define madvise my_madvise +int my_madvise(void *start, size_t length, int advice); +#ifndef HAVE_MADVISE +# ifndef MADV_NORMAL +# define MADV_NORMAL 0 +# define MADV_RANDOM 0 +# define MADV_SEQUENTIAL 0 +# define MADV_WILLNEED 0 +# define MADV_DONTNEED 0 +# endif +#endif + +void *mmap_file(int fd, size_t *length, int prot); +void *mmap_ro_file(int fd, size_t *length); +void *mmap_rw_file(int fd, size_t *length); + +/* for allocating anonymous mmap()s, with portable mremap(). these must not + be mixed with any standard mmap calls. */ +void *mmap_anon(size_t length); +void *mremap_anon(void *old_address, size_t old_size, size_t new_size, + unsigned long flags); +int munmap_anon(void *start, size_t length); + +size_t mmap_get_page_size(void) ATTR_CONST; + +#endif diff --git a/src/lib/module-context.h b/src/lib/module-context.h new file mode 100644 index 0000000..3d2b1d6 --- /dev/null +++ b/src/lib/module-context.h @@ -0,0 +1,116 @@ +#ifndef MODULE_CONTEXT_H +#define MODULE_CONTEXT_H + +#include "array.h" + +/* + This is a bit complex to use, but it prevents using wrong module IDs + in module_contexts arrays. + + --------- + The main structure is implemented like this: + + struct STRUCT_NAME_module_register { + unsigned int id; + }; + union STRUCT_NAME_module_context { + struct STRUCT_NAME_module_register *reg; + // it's allowed to have some structure here so it won't waste space. + // for example: struct STRUCT_NAME_vfuncs super; + }; + struct STRUCT_NAME { + ARRAY(union STRUCT_NAME_module_context *) module_contexts; + }; + extern struct STRUCT_NAME_module_register STRUCT_NAME_module_register; + + --------- + The usage in modules goes like: + + static MODULE_CONTEXT_DEFINE(mymodule_STRUCT_NAME_module, + &STRUCT_NAME_module_register); + struct mymodule_STRUCT_NAME { + union STRUCT_NAME_module_context module_ctx; + // module-specific data + }; + + struct mymodule_STRUCT_NAME *ctx = i_new(...); + MODULE_CONTEXT_SET(obj, mymodule_STRUCT_NAME_module, ctx); + + struct mymodule_STRUCT_NAME *ctx = + MODULE_CONTEXT(obj, mymodule_STRUCT_NAME_module); +*/ + +#define OBJ_REGISTER(obj) \ + ((**(obj)->module_contexts.v)->reg) +#define OBJ_REGISTER_COMPATIBLE(obj, id_ctx) \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE(OBJ_REGISTER(obj), (id_ctx).reg) + +#define MODULE_CONTEXT(obj, id_ctx) \ + (module_get_context_id(&(id_ctx).id) < array_count(&(obj)->module_contexts) ? \ + (*((void **)array_idx_modifiable(&(obj)->module_contexts, \ + module_get_context_id(&(id_ctx).id)) + \ + OBJ_REGISTER_COMPATIBLE(obj, id_ctx))) : NULL) + +/* Will crash if context is missing. This is mainly used to simplify code and + keep static analyzers happy. This syntax discards result of i_panic and + returns NULL instead to keep compilers happy. */ +#define MODULE_CONTEXT_REQUIRE(obj, id_ctx) \ + (module_get_context_id(&(id_ctx).id) < array_count(&(obj)->module_contexts) ? \ + (*((void **)array_idx_modifiable(&(obj)->module_contexts, \ + module_get_context_id(&(id_ctx).id)) + \ + OBJ_REGISTER_COMPATIBLE(obj, id_ctx))) : (i_panic("Module context " #id_ctx " missing"), NULL)) + +#ifdef HAVE_TYPEOF +# define MODULE_CONTEXT_DEFINE(_name, _reg) \ + struct _name { \ + struct module_context_id id; \ + typeof(_reg) reg; \ + } _name +# define MODULE_CONTEXT_INIT(_reg) \ + { { &(_reg)->id, 0, FALSE }, NULL } +#else +# define MODULE_CONTEXT_DEFINE(_name, _reg) \ + struct _name { \ + struct module_context_id id; \ + } _name +# define MODULE_CONTEXT_INIT(_reg) \ + { { &(_reg)->id, 0, FALSE } } +#endif + +#define MODULE_CONTEXT_DEFINE_INIT(_name, _reg) \ + MODULE_CONTEXT_DEFINE(_name, _reg) = MODULE_CONTEXT_INIT(_reg) + +struct module_context_id { + unsigned int *module_id_register; + unsigned int module_id; + bool module_id_set; +}; + +static inline unsigned int module_get_context_id(struct module_context_id *id) +{ + if (!id->module_id_set) { + id->module_id = *id->module_id_register; + id->module_id_set = TRUE; + *id->module_id_register += 1; + } + return id->module_id; +} + +#define MODULE_CONTEXT_SET_FULL(obj, id_ctx, ctx, module_ctx) STMT_START { \ + (void)COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE(module_ctx, \ + (**(obj)->module_contexts.v)); \ + (void)OBJ_REGISTER_COMPATIBLE(obj, id_ctx); \ + void *_module_tmp = ctx; \ + array_idx_set_i(&(obj)->module_contexts.arr, \ + module_get_context_id(&(id_ctx).id), &_module_tmp); \ + } STMT_END + +#define MODULE_CONTEXT_SET(obj, id_ctx, context) \ + MODULE_CONTEXT_SET_FULL(obj, id_ctx, context, &(context)->module_ctx) +#define MODULE_CONTEXT_SET_SELF(obj, id_ctx, context) \ + MODULE_CONTEXT_SET_FULL(obj, id_ctx, context, context) + +#define MODULE_CONTEXT_UNSET(obj, id_ctx) \ + array_idx_clear(&(obj)->module_contexts, (id_ctx).id.module_id) + +#endif diff --git a/src/lib/module-dir.c b/src/lib/module-dir.c new file mode 100644 index 0000000..26fdeac --- /dev/null +++ b/src/lib/module-dir.c @@ -0,0 +1,697 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "sort.h" +#include "module-dir.h" + +#ifdef HAVE_MODULES + +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> +#include <dlfcn.h> + +#ifndef RTLD_GLOBAL +# define RTLD_GLOBAL 0 +#endif + +#ifndef RTLD_NOW +# define RTLD_NOW 0 +#endif + +static const char *module_name_drop_suffix(const char *name); + +void *module_get_symbol_quiet(struct module *module, const char *symbol) +{ + /* clear out old errors */ + (void)dlerror(); + + return dlsym(module->handle, symbol); +} + +void *module_get_symbol(struct module *module, const char *symbol) +{ + const char *error; + void *ret; + + ret = module_get_symbol_quiet(module, symbol); + if (ret == NULL) { + error = dlerror(); + if (error != NULL) { + i_error("module %s: dlsym(%s) failed: %s", + module->path, symbol, error); + ret = NULL; + } + } + return ret; +} + +static void *get_symbol(struct module *module, const char *symbol, bool quiet) +{ + if (quiet) + return module_get_symbol_quiet(module, symbol); + + return module_get_symbol(module, symbol); +} + +static void module_free(struct module *module) +{ + if (module->deinit != NULL && module->initialized) + module->deinit(); + /* dlclose()ing removes all symbols from valgrind's visibility. + if GDB environment is set, don't actually unload the module + (the GDB environment is used elsewhere too) */ + if (getenv("GDB") == NULL) { + if (dlclose(module->handle) != 0) + i_error("dlclose(%s) failed: %m", module->path); + } + i_free(module->path); + i_free(module->name); + i_free(module); +} + +static bool +module_check_wrong_binary_dependency(const struct module_dir_load_settings *set, + struct module *module, const char **error_r) +{ + const char *symbol_name, *binary_dep, *const *names; + string_t *errstr; + + if (set->binary_name == NULL) + return TRUE; + + symbol_name = t_strconcat(module->name, "_binary_dependency", NULL); + binary_dep = dlsym(module->handle, symbol_name); + if (binary_dep == NULL) + return TRUE; + + names = t_strsplit(binary_dep, " "); + if (str_array_find(names, set->binary_name)) + return TRUE; + + errstr = t_str_new(128); + str_printfa(errstr, "Can't load plugin %s: " + "Plugin is intended to be used only by ", module->name); + if (names[1] == NULL) + str_printfa(errstr, "%s binary", binary_dep); + else + str_printfa(errstr, "binaries: %s", binary_dep); + str_printfa(errstr, " (we're %s)", set->binary_name); + *error_r = str_c(errstr); + return FALSE; +} + +static bool +module_check_missing_plugin_dependencies(const struct module_dir_load_settings *set, + struct module *module, + struct module *all_modules, + const char **error_r) +{ + const char **deps; + struct module *m; + string_t *errmsg; + size_t len; + + deps = dlsym(module->handle, + t_strconcat(module->name, "_dependencies", NULL)); + if (deps == NULL) + return TRUE; + + for (; *deps != NULL; deps++) { + len = strlen(*deps); + for (m = all_modules; m != NULL; m = m->next) { + if (strncmp(m->name, *deps, len) == 0 && + (m->name[len] == '\0' || + strcmp(m->name+len, "_plugin") == 0)) + break; + } + if (m == NULL) { + errmsg = t_str_new(128); + str_printfa(errmsg, "Plugin %s must be loaded also", + *deps); + if (set->setting_name != NULL) { + str_printfa(errmsg, + " (you must set: %s=$%s %s)", + set->setting_name, + set->setting_name, *deps); + } + *error_r = str_c(errmsg); + return FALSE; + } + } + return TRUE; +} + +static void *quiet_dlopen(const char *path, int flags) +{ +#ifndef __OpenBSD__ + return dlopen(path, flags); +#else + void *handle; + int fd; + + /* OpenBSD likes to print all "undefined symbol" errors to stderr. + Hide them by sending them to /dev/null. */ + fd = dup(STDERR_FILENO); + if (fd == -1) + i_fatal("dup() failed: %m"); + if (dup2(dev_null_fd, STDERR_FILENO) < 0) + i_fatal("dup2() failed: %m"); + handle = dlopen(path, flags); + if (dup2(fd, STDERR_FILENO) < 0) + i_fatal("dup2() failed: %m"); + if (close(fd) < 0) + i_error("close() failed: %m"); + return handle; +#endif +} + +static bool versions_equal(const char *str1, const char *str2) +{ + while (*str1 == *str2) { + if (*str1 == '\0' || *str1 == '(') + return TRUE; + str1++; + str2++; + } + return FALSE; +} + +static int +module_load(const char *path, const char *name, + const struct module_dir_load_settings *set, + struct module *all_modules, + struct module **module_r, const char **error_r) +{ + void *handle; + struct module *module; + const char *const *module_version; + void (*preinit)(void); + + *module_r = NULL; + *error_r = NULL; + + if (set->ignore_dlopen_errors) { + handle = quiet_dlopen(path, RTLD_GLOBAL | RTLD_NOW); + if (handle == NULL) { + if (set->debug) { + i_debug("Skipping module %s, " + "because dlopen() failed: %s " + "(this is usually intentional, " + "so just ignore this message)", + name, dlerror()); + } + return 0; + } + } else { + handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW); + if (handle == NULL) { + *error_r = t_strdup_printf("dlopen() failed: %s", + dlerror()); +#ifdef RTLD_LAZY + /* try to give a better error message by lazily loading + the plugin and checking its dependencies */ + handle = dlopen(path, RTLD_LAZY); + if (handle == NULL) + return -1; +#else + return -1; +#endif + } + } + + module = i_new(struct module, 1); + module->path = i_strdup(path); + module->name = i_strdup(name); + module->handle = handle; + + module_version = set->abi_version == NULL ? NULL : + get_symbol(module, t_strconcat(name, "_version", NULL), TRUE); + if (module_version != NULL && + !versions_equal(*module_version, set->abi_version)) { + *error_r = t_strdup_printf( + "Module is for different ABI version %s (we have %s)", + *module_version, set->abi_version); + module_free(module); + return -1; + } + + /* get our init func */ + module->init = (void (*)(struct module *)) + get_symbol(module, t_strconcat(name, "_init", NULL), + !set->require_init_funcs); + module->deinit = (void (*)(void)) + get_symbol(module, t_strconcat(name, "_deinit", NULL), + !set->require_init_funcs); + preinit = (void (*)(void)) + get_symbol(module, t_strconcat(name, "_preinit", NULL), + TRUE); + if (preinit != NULL) + preinit(); + + if ((module->init == NULL || module->deinit == NULL) && + set->require_init_funcs) { + *error_r = t_strdup_printf( + "Module doesn't have %s function", + module->init == NULL ? "init" : "deinit"); + } else if (!module_check_wrong_binary_dependency(set, module, error_r)) { + /* failed */ + } else if (!module_check_missing_plugin_dependencies(set, module, + all_modules, error_r)) { + /* failed */ + } + + if (*error_r != NULL) { + module->deinit = NULL; + module_free(module); + return -1; + } + + if (set->debug) + i_debug("Module loaded: %s", path); + *module_r = module; + return 1; +} + +static int module_name_cmp(const char *const *n1, const char *const *n2) +{ + const char *s1 = *n1, *s2 = *n2; + + if (str_begins(s1, "lib")) + s1 += 3; + if (str_begins(s2, "lib")) + s2 += 3; + + return strcmp(s1, s2); +} + +static bool module_want_load(const struct module_dir_load_settings *set, + const char **names, const char *name) +{ + if (set->filter_callback != NULL) { + if (!set->filter_callback(name, set->filter_context)) + return FALSE; + } + if (names == NULL) + return TRUE; + + for (; *names != NULL; names++) { + if (strcmp(*names, name) == 0) { + *names = ""; + return TRUE; + } + } + return FALSE; +} + +static void check_duplicates(ARRAY_TYPE(const_string) *names, + const char *name, const char *dir) +{ + const char *const *names_p, *base_name, *tmp; + unsigned int i, count; + + base_name = module_file_get_name(name); + names_p = array_get(names, &count); + for (i = 0; i < count; i++) T_BEGIN { + tmp = module_file_get_name(names_p[i]); + + if (strcmp(tmp, base_name) == 0) + i_fatal("Multiple files for module %s: %s/%s, %s/%s", + base_name, dir, name, dir, names_p[i]); + } T_END; +} + +struct module *module_dir_find(struct module *modules, const char *name) +{ + struct module *module; + size_t len = strlen(name); + + for (module = modules; module != NULL; module = module->next) { + if (strncmp(module->name, name, len) == 0) { + if (module->name[len] == '\0' || + strcmp(module->name + len, "_plugin") == 0) + return module; + } + } + return NULL; +} + +static bool module_is_loaded(struct module *modules, const char *name) +{ + return module_dir_find(modules, name) != NULL; +} + +static void module_names_fix(const char **module_names) +{ + unsigned int i, j; + + if (module_names[0] == NULL) + return; + + /* allow giving the module names also in non-base form. + convert them in here. */ + for (i = 0; module_names[i] != NULL; i++) + module_names[i] = module_file_get_name(module_names[i]); + + /* @UNSAFE: drop duplicates */ + i_qsort(module_names, i, sizeof(*module_names), i_strcmp_p); + for (i = j = 1; module_names[i] != NULL; i++) { + if (strcmp(module_names[i-1], module_names[i]) != 0) + module_names[j++] = module_names[i]; + } + module_names[j] = NULL; +} + +static bool +module_dir_is_all_loaded(struct module *old_modules, const char **module_names) +{ + unsigned int i; + + for (i = 0; module_names[i] != NULL; i++) { + if (!module_is_loaded(old_modules, module_names[i])) + return FALSE; + } + return TRUE; +} + +static int +module_dir_load_real(struct module **_modules, + const char *dir, const char **module_names, + const struct module_dir_load_settings *set, + char **error_r) +{ + DIR *dirp; + struct dirent *d; + const char *name, *p, *error, *const *names_p; + struct module *modules, *module, **module_pos, *old_modules = *_modules; + unsigned int i, count; + ARRAY_TYPE(const_string) names; + pool_t pool; + int ret; + + *error_r = NULL; + + if (module_names != NULL) { + if (module_dir_is_all_loaded(old_modules, module_names)) + return 0; + } + + if (set->debug) + i_debug("Loading modules from directory: %s", dir); + + dirp = opendir(dir); + if (dirp == NULL) { + *error_r = i_strdup_printf("opendir(%s) failed: %m", dir); + if (module_names != NULL) { + /* we were given a list of modules to load. + we can't fail. */ + return -1; + } + return errno == ENOENT ? 0 : -1; + } + + pool = pool_alloconly_create("module loader", 4096); + p_array_init(&names, pool, 32); + + modules = NULL; + for (errno = 0; (d = readdir(dirp)) != NULL; errno = 0) { + name = d->d_name; + + if (name[0] == '.') + continue; + + p = strstr(name, MODULE_SUFFIX); + if (p == NULL || strlen(p) != 3) + continue; + + T_BEGIN { + check_duplicates(&names, name, dir); + } T_END; + + name = p_strdup(pool, d->d_name); + array_push_back(&names, &name); + } + if (errno != 0) + *error_r = i_strdup_printf("readdir(%s) failed: %m", dir); + if (closedir(dirp) < 0 && *error_r == NULL) + *error_r = i_strdup_printf("closedir(%s) failed: %m", dir); + if (*error_r != NULL) { + pool_unref(&pool); + return -1; + } + + array_sort(&names, module_name_cmp); + names_p = array_get(&names, &count); + + modules = old_modules; + module_pos = &modules; + while (*module_pos != NULL) + module_pos = &(*module_pos)->next; + for (i = 0; i < count; i++) T_BEGIN { + const char *path, *stripped_name, *suffixless_name; + + name = names_p[i]; + stripped_name = module_file_get_name(name); + suffixless_name = module_name_drop_suffix(stripped_name); + if (!module_want_load(set, module_names, suffixless_name) || + module_is_loaded(old_modules, suffixless_name)) + module = NULL; + else { + path = t_strconcat(dir, "/", name, NULL); + ret = module_load(path, stripped_name, set, modules, &module, &error); + if (ret >= 0) + ; + else if (module_names != NULL) { + *error_r = i_strdup_printf("Couldn't load required plugin %s: %s", + path, error); + i = count; + } else { + i_error("Couldn't load plugin %s: %s", path, error); + } + } + + if (module != NULL) { + *module_pos = module; + module_pos = &module->next; + } + } T_END; + pool_unref(&pool); + + if (module_names != NULL && *error_r == NULL && !set->ignore_missing) { + /* make sure all modules were found */ + for (; *module_names != NULL; module_names++) { + if (**module_names != '\0') { + *error_r = i_strdup_printf("Plugin '%s' not found from directory %s", + *module_names, dir); + break; + } + } + } + *_modules = modules; + return *error_r != NULL ? -1 : 0; +} + +int module_dir_try_load_missing(struct module **modules, + const char *dir, const char *module_names, + const struct module_dir_load_settings *set, + const char **error_r) +{ + char *error = NULL; + int ret; + + T_BEGIN { + const char **arr = NULL; + + if (module_names != NULL) { + arr = t_strsplit_spaces(module_names, ", "); + module_names_fix(arr); + } + + ret = module_dir_load_real(modules, dir, arr, set, &error); + } T_END; + *error_r = t_strdup(error); + i_free(error); + return ret; +} + +struct module * +module_dir_load_missing(struct module *old_modules, + const char *dir, const char *module_names, + const struct module_dir_load_settings *set) +{ + struct module *new_modules = old_modules; + const char *error; + + if (module_dir_try_load_missing(&new_modules, dir, module_names, + set, &error) < 0) { + if (module_names != NULL) + i_fatal("%s", error); + else + i_error("%s", error); + } + return new_modules; +} + +void module_dir_init(struct module *modules) +{ + struct module *module; + + for (module = modules; module != NULL; module = module->next) { + if (!module->initialized) { + module->initialized = TRUE; + if (module->init != NULL) T_BEGIN { + module->init(module); + } T_END; + } + } +} + +void module_dir_deinit(struct module *modules) +{ + struct module *module, **rev; + unsigned int i, count = 0; + + for (module = modules; module != NULL; module = module->next) { + if (module->deinit != NULL && module->initialized) + count++; + } + + if (count == 0) + return; + + /* @UNSAFE: deinitialize in reverse order */ + T_BEGIN { + rev = t_new(struct module *, count); + for (i = 0, module = modules; i < count; ) { + if (module->deinit != NULL && module->initialized) { + rev[count-i-1] = module; + i++; + } + module = module->next; + } + + for (i = 0; i < count; i++) { + module = rev[i]; + + T_BEGIN { + module->deinit(); + } T_END; + module->initialized = FALSE; + } + } T_END; +} + +void module_dir_unload(struct module **modules) +{ + struct module *module, *next; + + /* Call all modules' deinit() first, so that they may still call each + others' functions. */ + module_dir_deinit(*modules); + + for (module = *modules; module != NULL; module = next) { + next = module->next; + module_free(module); + } + + *modules = NULL; +} + +#else + +#ifndef MODULE_SUFFIX +# define MODULE_SUFFIX ".so" /* just to avoid build failure */ +#endif + +struct module * +module_dir_load_missing(struct module *old_modules ATTR_UNUSED, + const char *dir ATTR_UNUSED, + const char *module_names, + const struct module_dir_load_settings *set ATTR_UNUSED) +{ +#define NO_SUPPORT_ERRSTR "Dynamically loadable module support not built in" + if (module_names == NULL) + i_error(NO_SUPPORT_ERRSTR); + else { + i_fatal(NO_SUPPORT_ERRSTR", can't load plugins: %s", + module_names); + } + return NULL; +} + +void module_dir_init(struct module *modules ATTR_UNUSED) +{ +} + +void module_dir_deinit(struct module *modules ATTR_UNUSED) +{ +} + +void module_dir_unload(struct module **modules ATTR_UNUSED) +{ +} + +struct module *module_dir_find(struct module *modules ATTR_UNUSED, + const char *name ATTR_UNUSED) +{ + return NULL; +} + +void *module_get_symbol(struct module *module ATTR_UNUSED, + const char *symbol ATTR_UNUSED) +{ + return NULL; +} + +void *module_get_symbol_quiet(struct module *module ATTR_UNUSED, + const char *symbol ATTR_UNUSED) +{ + return NULL; +} + +#endif + +struct module *module_dir_load(const char *dir, const char *module_names, + const struct module_dir_load_settings *set) +{ + return module_dir_load_missing(NULL, dir, module_names, set); +} + +const char *module_file_get_name(const char *fname) +{ + const char *p; + + /* [lib][nn_]name(.so) */ + if (str_begins(fname, "lib")) + fname += 3; + + for (p = fname; *p != '\0'; p++) { + if (*p < '0' || *p > '9') + break; + } + if (*p == '_') + fname = p + 1; + + p = strstr(fname, MODULE_SUFFIX); + if (p == NULL) + return fname; + + return t_strdup_until(fname, p); +} + +static const char *module_name_drop_suffix(const char *name) +{ + size_t len; + + len = strlen(name); + if (len > 7 && strcmp(name + len - 7, "_plugin") == 0) + name = t_strndup(name, len - 7); + return name; +} + +const char *module_get_plugin_name(struct module *module) +{ + return module_name_drop_suffix(module->name); +} diff --git a/src/lib/module-dir.h b/src/lib/module-dir.h new file mode 100644 index 0000000..f62e906 --- /dev/null +++ b/src/lib/module-dir.h @@ -0,0 +1,77 @@ +#ifndef MODULE_DIR_H +#define MODULE_DIR_H + +struct module_dir_load_settings { + /* If abi_version is non-NULL and the module contains a version symbol, + fail the load if they're different. In both strings ignore anything + after the first '(' character, so the version can be e.g.: + 2.2.ABIv1(2.2.15) */ + const char *abi_version; + /* Binary name used for checking if plugin is tried to be loaded for + wrong binary. */ + const char *binary_name; + /* Setting name used in plugin dependency error message */ + const char *setting_name; + + /* If non-NULL, load only modules where filter_callback returns TRUE */ + bool (*filter_callback)(const char *name, void *context); + void *filter_context; + + /* Require all plugins to have <plugin_name>_init() function */ + bool require_init_funcs:1; + /* Enable debug logging */ + bool debug:1; + /* If dlopen() fails for some modules, silently skip it. */ + bool ignore_dlopen_errors:1; + /* Don't fail if some specified modules weren't found */ + bool ignore_missing:1; +}; + +struct module { + char *path, *name; + + void *handle; + void (*init)(struct module *module); + void (*deinit)(void); + + bool initialized:1; + + struct module *next; +}; + +/* Load modules in given directory. module_names is a space separated list of + module names to load. */ +struct module *module_dir_load(const char *dir, const char *module_names, + const struct module_dir_load_settings *set) + ATTR_NULL(2); +/* Load modules that aren't already loaded. */ +struct module * +module_dir_load_missing(struct module *old_modules, + const char *dir, const char *module_names, + const struct module_dir_load_settings *set) + ATTR_NULL(1, 3); +/* Load modules that aren't already loaded. */ +int module_dir_try_load_missing(struct module **modules, + const char *dir, const char *module_names, + const struct module_dir_load_settings *set, + const char **error_r) + ATTR_NULL(1, 3); +/* Call init() in all modules */ +void module_dir_init(struct module *modules); +/* Call deinit() in all modules and mark them NULL so module_dir_unload() + won't do it again. */ +void module_dir_deinit(struct module *modules); +/* Unload all modules */ +void module_dir_unload(struct module **modules); +/* Find a module by name. */ +struct module *module_dir_find(struct module *modules, const char *name); + +void *module_get_symbol(struct module *module, const char *symbol); +void *module_get_symbol_quiet(struct module *module, const char *symbol); + +/* Returns module's base name from the filename. */ +const char *module_file_get_name(const char *fname); +/* Returns module's name without "_plugin" suffix. */ +const char *module_get_plugin_name(struct module *module); + +#endif diff --git a/src/lib/mountpoint.c b/src/lib/mountpoint.c new file mode 100644 index 0000000..3d2150e --- /dev/null +++ b/src/lib/mountpoint.c @@ -0,0 +1,336 @@ +/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "mountpoint.h" + +#include <sys/stat.h> + +#ifdef HAVE_SYS_VMOUNT_H +# include <stdio.h> +# include <sys/vmount.h> /* AIX */ +# define MOUNTPOINT_AIX_MNTCTL +#elif defined(HAVE_STATVFS_MNTFROMNAME) +# include <sys/statvfs.h> /* NetBSD 3.0+, FreeBSD 5.0+ */ +# define STATVFS_STR "statvfs" +# define MOUNTPOINT_STATVFS +#elif defined(HAVE_STATFS_MNTFROMNAME) +# include <sys/param.h> /* Older BSDs */ +# include <sys/mount.h> +# define statvfs statfs +# define STATVFS_STR "statfs" +# define MOUNTPOINT_STATVFS +#elif defined(HAVE_MNTENT_H) +# include <stdio.h> +# include <mntent.h> /* Linux */ +# define MOUNTPOINT_LINUX +#elif defined(HAVE_SYS_MNTTAB_H) +# include <stdio.h> +# include <sys/mnttab.h> /* Solaris */ +# include <sys/mntent.h> +# define MOUNTPOINT_SOLARIS +#else +# define MOUNTPOINT_UNKNOWN +#endif + +#ifdef MOUNTPOINT_SOLARIS +# define MTAB_PATH MNTTAB /* Solaris */ +#else +# define MTAB_PATH "/etc/mtab" /* Linux */ +#endif + +/* AIX doesn't have these defined */ +#ifndef MNTTYPE_SWAP +# define MNTTYPE_SWAP "swap" +#endif +#ifndef MNTTYPE_IGNORE +# define MNTTYPE_IGNORE "ignore" +#endif +#ifndef MNTTYPE_JFS +# define MNTTYPE_JFS "jfs" +#endif +#ifndef MNTTYPE_NFS +# define MNTTYPE_NFS "nfs" +#endif + +/* Linux sometimes has mtab entry for "rootfs" as well as the real root + entry. Skip the rootfs. */ +#ifndef MNTTYPE_ROOTFS +# define MNTTYPE_ROOTFS "rootfs" +#endif + +#ifdef MOUNTPOINT_STATVFS +static int +mountpoint_get_statvfs(const char *path, pool_t pool, + struct mountpoint *point_r) +{ + struct statvfs buf; + + i_zero(point_r); + if (statvfs(path, &buf) < 0) { + if (errno == ENOENT) + return 0; + + i_error(STATVFS_STR"(%s) failed: %m", path); + return -1; + } + + point_r->device_path = p_strdup(pool, buf.f_mntfromname); + point_r->mount_path = p_strdup(pool, buf.f_mntonname); +#ifdef __osf__ /* Tru64 */ + point_r->type = p_strdup(pool, getvfsbynumber(buf.f_type)); +#else + point_r->type = p_strdup(pool, buf.f_fstypename); +#endif + point_r->block_size = buf.f_bsize; + return 1; +} +#endif + +int mountpoint_get(const char *path, pool_t pool, struct mountpoint *point_r) +{ +#ifdef MOUNTPOINT_UNKNOWN + i_zero(point_r); + errno = ENOSYS; + return -1; +#elif defined (MOUNTPOINT_STATVFS) + /* BSDs, Tru64 */ + return mountpoint_get_statvfs(path, pool, point_r); +#else + /* find via mount iteration */ + struct mountpoint_iter *iter; + const struct mountpoint *mnt; + struct stat st; + + i_zero(point_r); + if (stat(path, &st) < 0) { + if (errno == ENOENT) + return 0; + + i_error("stat(%s) failed: %m", path); + return -1; + } + + iter = mountpoint_iter_init(); + while ((mnt = mountpoint_iter_next(iter)) != NULL) { + if (minor(st.st_dev) == minor(mnt->dev) && + major(st.st_dev) == major(mnt->dev)) + break; + } + if (mnt != NULL) { + point_r->device_path = p_strdup(pool, mnt->device_path); + point_r->mount_path = p_strdup(pool, mnt->mount_path); + point_r->type = p_strdup(pool, mnt->type); + point_r->dev = mnt->dev; + point_r->block_size = st.st_blksize; + } + if (mountpoint_iter_deinit(&iter) < 0 && mnt == NULL) + return -1; + return mnt != NULL ? 1 : 0; +#endif +} + +struct mountpoint_iter { +#ifdef MOUNTPOINT_AIX_MNTCTL + char *mtab; + struct vmount *vmt; + int count; +#elif defined(MOUNTPOINT_SOLARIS) || defined(MOUNTPOINT_LINUX) + FILE *f; +#elif defined(HAVE_GETMNTINFO) /* BSDs */ +#ifndef __NetBSD__ + struct statfs *fs; +#else + struct statvfs *fs; +#endif + int count; +#endif + struct mountpoint mnt; + bool failed; +}; + +struct mountpoint_iter *mountpoint_iter_init(void) +{ + struct mountpoint_iter *iter = i_new(struct mountpoint_iter, 1); +#ifdef MOUNTPOINT_AIX_MNTCTL + unsigned int size = STATIC_MTAB_SIZE; + char *mtab; + int count; + + mtab = t_buffer_get(size); + while ((count = mntctl(MCTL_QUERY, size, mtab)) == 0) { + size = *(unsigned int *)mtab; + mtab = t_buffer_get(size); + } + if (count < 0) { + i_error("mntctl(MCTL_QUERY) failed: %m"); + iter->failed = TRUE; + return iter; + } + iter->count = count; + iter->mtab = i_malloc(size); + memcpy(iter->mtab, mtab, size); + iter->vmt = (void *)iter->mtab; +#elif defined(MOUNTPOINT_SOLARIS) + iter->f = fopen(MTAB_PATH, "r"); + if (iter->f == NULL) { + i_error("fopen(%s) failed: %m", MTAB_PATH); + iter->failed = TRUE; + return iter; + } + resetmnttab(iter->f); +#elif defined(MOUNTPOINT_LINUX) + iter->f = setmntent(MTAB_PATH, "r"); + if (iter->f == NULL) { + i_error("setmntent(%s) failed: %m", MTAB_PATH); + iter->failed = TRUE; + } +#elif defined(HAVE_GETMNTINFO) /* BSDs */ + iter->count = getmntinfo(&iter->fs, MNT_NOWAIT); + if (iter->count < 0) { + i_error("getmntinfo() failed: %m"); + iter->failed = TRUE; + } +#else + iter->failed = TRUE; +#endif + return iter; +} + +const struct mountpoint *mountpoint_iter_next(struct mountpoint_iter *iter) +{ +#ifdef MOUNTPOINT_AIX_MNTCTL + struct vmount *vmt = iter->vmt; + char *vmt_base = (char *)vmt; + char *vmt_object, *vmt_stub, *vmt_hostname; + struct stat vst; + + if (iter->count == 0) + return NULL; + iter->count--; + + iter->vmt = PTR_OFFSET(vmt, vmt->vmt_length); + vmt_hostname = vmt_base + vmt->vmt_data[VMT_HOSTNAME].vmt_off; + vmt_object = vmt_base + vmt->vmt_data[VMT_OBJECT].vmt_off; + vmt_stub = vmt_base + vmt->vmt_data[VMT_STUB].vmt_off; + + i_zero(&iter->mnt); + switch (vmt->vmt_gfstype) { + case MNT_NFS: + case MNT_NFS3: + case MNT_NFS4: + case MNT_RFS4: + iter->mnt.device_path = + t_strconcat(vmt_hostname, ":", vmt_object, NULL); + iter->mnt.mount_path = vmt_stub; + iter->mnt.type = MNTTYPE_NFS; + break; + + case MNT_J2: + case MNT_JFS: + iter->mnt.device_path = vmt_object; + iter->mnt.mount_path = vmt_stub; + iter->mnt.type = MNTTYPE_JFS; + break; + default: + /* unwanted filesystem */ + return mountpoint_iter_next(iter); + } + if (stat(iter->mnt.mount_path, &vst) == 0) { + iter->mnt.dev = vst.st_dev; + iter->mnt.block_size = vst.st_blksize; + } + return &iter->mnt; +#elif defined (MOUNTPOINT_SOLARIS) + union { + struct mnttab ent; + struct extmnttab ext; + } ent; + + if (iter->f == NULL) + return NULL; + + i_zero(&iter->mnt); + while ((getextmntent(iter->f, &ent.ext, sizeof(ent.ext))) == 0) { + if (hasmntopt(&ent.ent, MNTOPT_IGNORE) != NULL) + continue; + + /* mnt_type contains tmpfs with swap */ + if (strcmp(ent.ent.mnt_special, MNTTYPE_SWAP) == 0) + continue; + + iter->mnt.device_path = ent.ent.mnt_special; + iter->mnt.mount_path = ent.ent.mnt_mountp; + iter->mnt.type = ent.ent.mnt_fstype; + iter->mnt.dev = makedev(ent.ext.mnt_major, ent.ext.mnt_minor); + return &iter->mnt; + } + return NULL; +#elif defined (MOUNTPOINT_LINUX) + const struct mntent *ent; + struct stat st; + + if (iter->f == NULL) + return NULL; + + i_zero(&iter->mnt); + while ((ent = getmntent(iter->f)) != NULL) { + if (strcmp(ent->mnt_type, MNTTYPE_SWAP) == 0 || + strcmp(ent->mnt_type, MNTTYPE_IGNORE) == 0 || + strcmp(ent->mnt_type, MNTTYPE_ROOTFS) == 0) + continue; + + iter->mnt.device_path = ent->mnt_fsname; + iter->mnt.mount_path = ent->mnt_dir; + iter->mnt.type = ent->mnt_type; + if (stat(ent->mnt_dir, &st) == 0) { + iter->mnt.dev = st.st_dev; + iter->mnt.block_size = st.st_blksize; + } + return &iter->mnt; + } + return NULL; +#elif defined(HAVE_GETMNTINFO) /* BSDs */ + while (iter->count > 0) { +#ifndef __NetBSD__ + struct statfs *fs = iter->fs; +#else + struct statvfs *fs = iter->fs; +#endif + + iter->fs++; + iter->count--; + + iter->mnt.device_path = fs->f_mntfromname; + iter->mnt.mount_path = fs->f_mntonname; +#ifdef __osf__ /* Tru64 */ + iter->mnt.type = getvfsbynumber(fs->f_type); +#else + iter->mnt.type = fs->f_fstypename; +#endif + iter->mnt.block_size = fs->f_bsize; + return &iter->mnt; + } + return NULL; +#else + return NULL; +#endif +} + +int mountpoint_iter_deinit(struct mountpoint_iter **_iter) +{ + struct mountpoint_iter *iter = *_iter; + int ret = iter->failed ? -1 : 0; + + *_iter = NULL; +#ifdef MOUNTPOINT_AIX_MNTCTL + i_free(iter->mtab); +#elif defined (MOUNTPOINT_SOLARIS) + if (iter->f != NULL) + fclose(iter->f); +#elif defined (MOUNTPOINT_LINUX) + if (iter->f != NULL) + endmntent(iter->f); +#endif + i_free(iter); + return ret; +} diff --git a/src/lib/mountpoint.h b/src/lib/mountpoint.h new file mode 100644 index 0000000..728085d --- /dev/null +++ b/src/lib/mountpoint.h @@ -0,0 +1,23 @@ +#ifndef MOUNTPOINT_H +#define MOUNTPOINT_H + +struct mountpoint { + char *device_path; + char *mount_path; + char *type; + dev_t dev; + unsigned int block_size; /* may not be set for iteration */ +}; + +/* Returns 1 = found, 0 = not found (from mount tabs, or the path itself), + -1 = error */ +int mountpoint_get(const char *path, pool_t pool, struct mountpoint *point_r); + +/* Iterate through mountpoints */ +struct mountpoint_iter *mountpoint_iter_init(void); +/* Returns the next mountpoint or NULL if there are no more. */ +const struct mountpoint *mountpoint_iter_next(struct mountpoint_iter *iter); +/* Returns 0 if mountpoints were iterated successfully, -1 if it failed. */ +int mountpoint_iter_deinit(struct mountpoint_iter **iter); + +#endif diff --git a/src/lib/net.c b/src/lib/net.c new file mode 100644 index 0000000..fe69faf --- /dev/null +++ b/src/lib/net.c @@ -0,0 +1,1237 @@ +/* Copyright (c) 1999-2018 Dovecot authors, see the included COPYING file */ + +#define _GNU_SOURCE /* For Linux's struct ucred */ +#include "lib.h" +#include "time-util.h" +#include "net.h" + +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/un.h> +#include <netinet/tcp.h> +#if defined(HAVE_UCRED_H) +# include <ucred.h> /* for getpeerucred() */ +#elif defined(HAVE_SYS_UCRED_H) +# include <sys/ucred.h> /* for FreeBSD struct xucred */ +#endif + +union sockaddr_union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; + +union sockaddr_union_unix { + struct sockaddr sa; + struct sockaddr_un un; +}; + +#define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \ + sizeof(so.sin6) : sizeof(so.sin)) + +#if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && !defined(HAVE_GETPEERUCRED) && defined(MSG_WAITALL) && defined(LOCAL_CREDS) +# define NEEDS_LOCAL_CREDS 1 +#else +# undef NEEDS_LOCAL_CREDS +#endif + +/* If connect() fails with EADDRNOTAVAIL (or some others on FreeBSD), retry it + this many times. + + This is needed on busy systems kernel may assign the same source port to two + sockets at bind() stage, which is what we generally want to allow more than + 64k outgoing connections to different destinations. However, at bind() stage + the kernel doesn't know the destination yet. So it's possible that it + assigns the same source port to two (or more) sockets that have the same + destination IP+port as well. In this case connect() will fail with + EADDRNOTAVAIL. We'll need to retry this and hope that the next attempt won't + conflict. */ +#define MAX_CONNECT_RETRIES 20 + +bool net_ip_compare(const struct ip_addr *ip1, const struct ip_addr *ip2) +{ + return net_ip_cmp(ip1, ip2) == 0; +} + +int net_ip_cmp(const struct ip_addr *ip1, const struct ip_addr *ip2) +{ + if (ip1->family != ip2->family) + return ip1->family - ip2->family; + + switch (ip1->family) { + case AF_INET6: + return memcmp(&ip1->u.ip6, &ip2->u.ip6, sizeof(ip1->u.ip6)); + case AF_INET: + return memcmp(&ip1->u.ip4, &ip2->u.ip4, sizeof(ip1->u.ip4)); + default: + break; + } + return 0; +} + +unsigned int net_ip_hash(const struct ip_addr *ip) +{ + const unsigned char *p; + unsigned int len, g, h = 0; + + if (ip->family == AF_INET6) { + p = ip->u.ip6.s6_addr; + len = sizeof(ip->u.ip6); + } else + { + return ip->u.ip4.s_addr; + } + + for (; len > 0; len--, p++) { + h = (h << 4) + *p; + if ((g = h & 0xf0000000UL) != 0) { + h = h ^ (g >> 24); + h = h ^ g; + } + } + + return h; +} + +/* copy IP to sockaddr */ +static inline void +sin_set_ip(union sockaddr_union *so, const struct ip_addr *ip) +{ + if (ip == NULL) { + so->sin6.sin6_family = AF_INET6; + so->sin6.sin6_addr = in6addr_any; + return; + } + + so->sin.sin_family = ip->family; + if (ip->family == AF_INET6) + memcpy(&so->sin6.sin6_addr, &ip->u.ip6, sizeof(ip->u.ip6)); + else + memcpy(&so->sin.sin_addr, &ip->u.ip4, sizeof(ip->u.ip4)); +} + +static inline void +sin_get_ip(const union sockaddr_union *so, struct ip_addr *ip) +{ + /* IP structs may be sent across processes. Clear the whole struct + first to make sure it won't leak any data across processes. */ + i_zero(ip); + + ip->family = so->sin.sin_family; + + if (ip->family == AF_INET6) + memcpy(&ip->u.ip6, &so->sin6.sin6_addr, sizeof(ip->u.ip6)); + else + if (ip->family == AF_INET) + memcpy(&ip->u.ip4, &so->sin.sin_addr, sizeof(ip->u.ip4)); + else + i_zero(&ip->u); +} + +static inline void sin_set_port(union sockaddr_union *so, in_port_t port) +{ + if (so->sin.sin_family == AF_INET6) + so->sin6.sin6_port = htons(port); + else + so->sin.sin_port = htons(port); +} + +static inline in_port_t sin_get_port(union sockaddr_union *so) +{ + if (so->sin.sin_family == AF_INET6) + return ntohs(so->sin6.sin6_port); + if (so->sin.sin_family == AF_INET) + return ntohs(so->sin.sin_port); + + return 0; +} + +static int net_connect_ip_once(const struct ip_addr *ip, in_port_t port, + const struct ip_addr *my_ip, int sock_type, bool blocking) +{ + union sockaddr_union so; + int fd, ret, opt = 1; + + if (my_ip != NULL && ip->family != my_ip->family) { + i_warning("net_connect_ip(): ip->family != my_ip->family"); + my_ip = NULL; + } + + /* create the socket */ + i_zero(&so); + so.sin.sin_family = ip->family; + fd = socket(ip->family, sock_type, 0); + + if (fd == -1) { + i_error("socket() failed: %m"); + return -1; + } + + /* set socket options */ + (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + if (sock_type == SOCK_STREAM) + (void)setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt)); + if (!blocking) + net_set_nonblock(fd, TRUE); + + /* set our own address */ + if (my_ip != NULL) { + sin_set_ip(&so, my_ip); + if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) { + i_error("bind(%s) failed: %m", net_ip2addr(my_ip)); + i_close_fd(&fd); + return -1; + } + } + + /* connect */ + sin_set_ip(&so, ip); + sin_set_port(&so, port); + ret = connect(fd, &so.sa, SIZEOF_SOCKADDR(so)); + +#ifndef WIN32 + if (ret < 0 && errno != EINPROGRESS) +#else + if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK) +#endif + { + i_close_fd(&fd); + return -1; + } + + return fd; +} + +static int net_connect_ip_full(const struct ip_addr *ip, in_port_t port, + const struct ip_addr *my_ip, int sock_type, + bool blocking) +{ + int fd, try; + + for (try = 0;;) { + fd = net_connect_ip_once(ip, port, my_ip, sock_type, blocking); + if (fd != -1 || try++ >= MAX_CONNECT_RETRIES || + (errno != EADDRNOTAVAIL +#ifdef __FreeBSD__ + /* busy */ + && errno != EADDRINUSE + /* pf may cause this if another connection used + the same port recently */ + && errno != EACCES +#endif + )) + break; + } + return fd; +} + +int net_connect_ip(const struct ip_addr *ip, in_port_t port, + const struct ip_addr *my_ip) +{ + return net_connect_ip_full(ip, port, my_ip, SOCK_STREAM, FALSE); +} + +int net_connect_ip_blocking(const struct ip_addr *ip, in_port_t port, + const struct ip_addr *my_ip) +{ + return net_connect_ip_full(ip, port, my_ip, SOCK_STREAM, TRUE); +} + +int net_connect_udp(const struct ip_addr *ip, in_port_t port, + const struct ip_addr *my_ip) +{ + return net_connect_ip_full(ip, port, my_ip, SOCK_DGRAM, FALSE); +} + +int net_try_bind(const struct ip_addr *ip) +{ + union sockaddr_union so; + int fd; + + /* create the socket */ + i_zero(&so); + so.sin.sin_family = ip->family; + fd = socket(ip->family, SOCK_STREAM, 0); + if (fd == -1) { + i_error("socket() failed: %m"); + return -1; + } + + sin_set_ip(&so, ip); + if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) { + i_close_fd(&fd); + return -1; + } + i_close_fd(&fd); + return 0; +} + +int net_connect_unix(const char *path) +{ + union sockaddr_union_unix sa; + int fd, ret; + + i_zero(&sa); + sa.un.sun_family = AF_UNIX; + if (i_strocpy(sa.un.sun_path, path, sizeof(sa.un.sun_path)) < 0) { + /* too long path */ +#ifdef ENAMETOOLONG + errno = ENAMETOOLONG; +#else + errno = EOVERFLOW; +#endif + return -1; + } + + /* create the socket */ + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + i_error("socket(%s) failed: %m", path); + return -1; + } + + net_set_nonblock(fd, TRUE); + + /* connect */ + ret = connect(fd, &sa.sa, sizeof(sa)); + if (ret < 0 && errno != EINPROGRESS) { + i_close_fd(&fd); + return -1; + } + +#ifdef NEEDS_LOCAL_CREDS + { + int on = 1; + if (setsockopt(fd, 0, LOCAL_CREDS, &on, sizeof on)) { + i_error("setsockopt(LOCAL_CREDS) failed: %m"); + return -1; + } + } +#endif + + return fd; +} + +int net_connect_unix_with_retries(const char *path, unsigned int msecs) +{ + struct timeval start, now; + int fd; + + i_gettimeofday(&start); + + do { + fd = net_connect_unix(path); + if (fd != -1 || (errno != EAGAIN && errno != ECONNREFUSED)) + break; + + /* busy. wait for a while. */ + usleep(i_rand_minmax(1, 10) * 10000); + i_gettimeofday(&now); + } while (timeval_diff_msecs(&now, &start) < (int)msecs); + return fd; +} + +void net_disconnect(int fd) +{ + /* FreeBSD's close() fails with ECONNRESET if socket still has unsent + data in transmit buffer. We don't care. */ + if (close(fd) < 0 && errno != ECONNRESET) + i_error("net_disconnect() failed: %m"); +} + +void net_set_nonblock(int fd, bool nonblock) +{ + fd_set_nonblock(fd, nonblock); +} + +int net_set_cork(int fd ATTR_UNUSED, bool cork ATTR_UNUSED) +{ +#ifdef TCP_CORK + int val = cork; + + return setsockopt(fd, IPPROTO_TCP, TCP_CORK, &val, sizeof(val)); +#else + errno = ENOPROTOOPT; + return -1; +#endif +} + +int net_set_tcp_nodelay(int fd, bool nodelay) +{ + int val = nodelay; + + return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); +} + +int net_set_tcp_quickack(int fd ATTR_UNUSED, bool quickack ATTR_UNUSED) +{ +#ifdef TCP_QUICKACK + int val = quickack; + + return setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, &val, sizeof(val)); +#else + errno = ENOPROTOOPT; + return -1; +#endif +} + +int net_set_send_buffer_size(int fd, size_t size) +{ + int opt; + + if (size > INT_MAX) { + errno = EINVAL; + return -1; + } + opt = (int)size; + return setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)); +} + +int net_set_recv_buffer_size(int fd, size_t size) +{ + int opt; + + if (size > INT_MAX) { + errno = EINVAL; + return -1; + } + opt = (int)size; + return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)); +} + +const struct ip_addr net_ip4_any = { + .family = AF_INET, + .u.ip4.s_addr = INADDR_ANY +}; + +const struct ip_addr net_ip6_any = { + .family = AF_INET6, + .u.ip6 = IN6ADDR_ANY_INIT +}; + +const struct ip_addr net_ip4_loopback = { + .family = AF_INET, + .u.ip4.s_addr = INADDR_LOOPBACK +}; + +const struct ip_addr net_ip6_loopback = { + .family = AF_INET6, + .u.ip6 = IN6ADDR_LOOPBACK_INIT +}; + +int net_listen(const struct ip_addr *my_ip, in_port_t *port, int backlog) +{ + enum net_listen_flags flags = 0; + + return net_listen_full(my_ip, port, &flags, backlog); +} + +int net_listen_full(const struct ip_addr *my_ip, in_port_t *port, + enum net_listen_flags *flags, int backlog) +{ + union sockaddr_union so; + int ret, fd, opt = 1; + socklen_t len; + + i_zero(&so); + sin_set_port(&so, *port); + sin_set_ip(&so, my_ip); + + /* create the socket */ + fd = socket(so.sin.sin_family, SOCK_STREAM, 0); + if (fd == -1 && my_ip == NULL && + (errno == EINVAL || errno == EAFNOSUPPORT)) { + /* IPv6 is not supported by OS */ + so.sin.sin_family = AF_INET; + so.sin.sin_addr.s_addr = INADDR_ANY; + + fd = socket(AF_INET, SOCK_STREAM, 0); + } + if (fd == -1) { + i_error("socket() failed: %m"); + return -1; + } + + /* set socket options */ + (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + (void)setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt)); + + if ((*flags & NET_LISTEN_FLAG_REUSEPORT) != 0) { +#ifdef SO_REUSEPORT + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, + &opt, sizeof(opt)) < 0) +#endif + *flags &= ENUM_NEGATE(NET_LISTEN_FLAG_REUSEPORT); + } + + /* If using IPv6, bind only to IPv6 if possible. This avoids + ambiguities with IPv4-mapped IPv6 addresses. */ +#ifdef IPV6_V6ONLY + if (so.sin.sin_family == AF_INET6) { + opt = 1; + (void)setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); + } +#endif + /* specify the address/port we want to listen in */ + ret = bind(fd, &so.sa, SIZEOF_SOCKADDR(so)); + if (ret < 0) { + if (errno != EADDRINUSE) { + i_error("bind(%s, %u) failed: %m", + my_ip == NULL ? "" : net_ip2addr(my_ip), *port); + } + } else { + /* get the actual port we started listen */ + len = SIZEOF_SOCKADDR(so); + ret = getsockname(fd, &so.sa, &len); + if (ret >= 0) { + *port = sin_get_port(&so); + + /* start listening */ + if (listen(fd, backlog) >= 0) + return fd; + + if (errno != EADDRINUSE) + i_error("listen() failed: %m"); + } + } + + /* error */ + i_close_fd(&fd); + return -1; +} + +int net_listen_unix(const char *path, int backlog) +{ + union { + struct sockaddr sa; + struct sockaddr_un un; + } sa; + int fd; + + i_zero(&sa); + sa.un.sun_family = AF_UNIX; + if (i_strocpy(sa.un.sun_path, path, sizeof(sa.un.sun_path)) < 0) { + /* too long path */ +#ifdef ENAMETOOLONG + errno = ENAMETOOLONG; +#else + errno = EOVERFLOW; +#endif + return -1; + } + + /* create the socket */ + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + i_error("socket() failed: %m"); + return -1; + } + +#ifdef NEEDS_LOCAL_CREDS + { + int on = 1; + if (setsockopt(fd, 0, LOCAL_CREDS, &on, sizeof on)) { + i_error("setsockopt(LOCAL_CREDS) failed: %m"); + return -1; + } + } +#endif + + /* bind */ + if (bind(fd, &sa.sa, sizeof(sa)) < 0) { + if (errno != EADDRINUSE) + i_error("bind(%s) failed: %m", path); + } else { + /* start listening */ + if (listen(fd, backlog) == 0) + return fd; + + if (errno != EADDRINUSE) + i_error("listen() failed: %m"); + } + + i_close_fd(&fd); + return -1; +} + +int net_listen_unix_unlink_stale(const char *path, int backlog) +{ + unsigned int i = 0; + int fd; + + while ((fd = net_listen_unix(path, backlog)) == -1) { + if (errno != EADDRINUSE || ++i == 2) + return -1; + + /* see if it really exists */ + fd = net_connect_unix(path); + if (fd != -1 || errno != ECONNREFUSED) { + i_close_fd(&fd); + errno = EADDRINUSE; + return -1; + } + + /* delete and try again */ + if (i_unlink_if_exists(path) < 0) { + errno = EADDRINUSE; + return -1; + } + } + return fd; +} + +int net_accept(int fd, struct ip_addr *addr_r, in_port_t *port_r) +{ + union sockaddr_union so; + int ret; + socklen_t addrlen; + + i_assert(fd >= 0); + + i_zero(&so); + addrlen = sizeof(so); + ret = accept(fd, &so.sa, &addrlen); + + if (ret < 0) { + if (errno == EAGAIN || errno == ECONNABORTED) + return -1; + else + return -2; + } + if (so.sin.sin_family == AF_UNIX) { + if (addr_r != NULL) + i_zero(addr_r); + if (port_r != NULL) *port_r = 0; + } else { + if (addr_r != NULL) sin_get_ip(&so, addr_r); + if (port_r != NULL) *port_r = sin_get_port(&so); + } + return ret; +} + +ssize_t net_receive(int fd, void *buf, size_t len) +{ + ssize_t ret; + + i_assert(fd >= 0); + i_assert(len <= SSIZE_T_MAX); + + ret = read(fd, buf, len); + if (ret == 0) { + /* disconnected */ + errno = 0; + return -2; + } + + if (unlikely(ret < 0)) { + if (errno == EINTR || errno == EAGAIN) + return 0; + + if (errno == ECONNRESET || errno == ETIMEDOUT) { + /* treat as disconnection */ + return -2; + } + } + + return ret; +} + +int net_gethostbyname(const char *addr, struct ip_addr **ips, + unsigned int *ips_count) +{ + /* @UNSAFE */ + union sockaddr_union *so; + struct addrinfo hints, *ai, *origai; + struct ip_addr ip; + int host_error; + int count; + + *ips = NULL; + *ips_count = 0; + + /* support [ipv6] style addresses here so they work globally */ + if (addr[0] == '[' && net_addr2ip(addr, &ip) == 0) { + *ips_count = 1; + *ips = t_new(struct ip_addr, 1); + **ips = ip; + return 0; + } + + i_zero(&hints); + hints.ai_socktype = SOCK_STREAM; + + /* save error to host_error for later use */ + host_error = getaddrinfo(addr, NULL, &hints, &ai); + if (host_error != 0) + return host_error; + + /* get number of IPs */ + origai = ai; + for (count = 0; ai != NULL; ai = ai->ai_next) + count++; + + *ips_count = count; + *ips = t_new(struct ip_addr, count); + + count = 0; + for (ai = origai; ai != NULL; ai = ai->ai_next, count++) { + so = (union sockaddr_union *) ai->ai_addr; + + sin_get_ip(so, &(*ips)[count]); + } + freeaddrinfo(origai); + + return 0; +} + +int net_gethostbyaddr(const struct ip_addr *ip, const char **name_r) +{ + union sockaddr_union so; + socklen_t addrlen = sizeof(so); + char hbuf[NI_MAXHOST]; + int ret; + + i_zero(&so); + sin_set_ip(&so, ip); + ret = getnameinfo(&so.sa, addrlen, hbuf, sizeof(hbuf), NULL, 0, + NI_NAMEREQD); + if (ret != 0) + return ret; + + *name_r = t_strdup(hbuf); + return 0; +} + +int net_getsockname(int fd, struct ip_addr *addr, in_port_t *port) +{ + union sockaddr_union so; + socklen_t addrlen; + + i_assert(fd >= 0); + + i_zero(&so); + addrlen = sizeof(so); + if (getsockname(fd, &so.sa, &addrlen) == -1) + return -1; + if (so.sin.sin_family == AF_UNIX) { + if (addr != NULL) + i_zero(addr); + if (port != NULL) *port = 0; + } else { + if (addr != NULL) sin_get_ip(&so, addr); + if (port != NULL) *port = sin_get_port(&so); + } + return 0; +} + +int net_getpeername(int fd, struct ip_addr *addr, in_port_t *port) +{ + union sockaddr_union so; + socklen_t addrlen; + + i_assert(fd >= 0); + + i_zero(&so); + addrlen = sizeof(so); + if (getpeername(fd, &so.sa, &addrlen) == -1) + return -1; + if (so.sin.sin_family == AF_UNIX) { + if (addr != NULL) + i_zero(addr); + if (port != NULL) *port = 0; + } else { + if (addr != NULL) sin_get_ip(&so, addr); + if (port != NULL) *port = sin_get_port(&so); + } + return 0; +} + +int net_getunixname(int fd, const char **name_r) +{ + union sockaddr_union_unix so; + socklen_t addrlen = sizeof(so); + + i_zero(&so); + if (getsockname(fd, &so.sa, &addrlen) < 0) + return -1; + if (so.un.sun_family != AF_UNIX) { + errno = ENOTSOCK; + return -1; + } + *name_r = t_strdup(so.un.sun_path); + return 0; +} + +int net_getunixcred(int fd, struct net_unix_cred *cred_r) +{ +#if defined(SO_PEERCRED) +# if defined(HAVE_STRUCT_SOCKPEERCRED) + /* OpenBSD (may also provide getpeereid, but we also want pid) */ + struct sockpeercred ucred; +# else + /* Linux */ + struct ucred ucred; +# endif + socklen_t len = sizeof(ucred); + + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) { + i_error("getsockopt(SO_PEERCRED) failed: %m"); + return -1; + } + cred_r->uid = ucred.uid; + cred_r->gid = ucred.gid; + cred_r->pid = ucred.pid; + return 0; +#elif defined(LOCAL_PEEREID) + /* NetBSD (may also provide getpeereid, but we also want pid) */ + struct unpcbid ucred; + socklen_t len = sizeof(ucred); + + if (getsockopt(fd, 0, LOCAL_PEEREID, &ucred, &len) < 0) { + i_error("getsockopt(LOCAL_PEEREID) failed: %m"); + return -1; + } + + cred_r->uid = ucred.unp_euid; + cred_r->gid = ucred.unp_egid; + cred_r->pid = ucred.unp_pid; + return 0; +#elif defined(HAVE_GETPEEREID) + /* OSX 10.4+, FreeBSD 4.6+, OpenBSD 3.0+, NetBSD 5.0+ */ + if (getpeereid(fd, &cred_r->uid, &cred_r->gid) < 0) { + i_error("getpeereid() failed: %m"); + return -1; + } + cred_r->pid = (pid_t)-1; + return 0; +#elif defined(LOCAL_PEERCRED) + /* Older FreeBSD */ + struct xucred ucred; + socklen_t len = sizeof(ucred); + + if (getsockopt(fd, 0, LOCAL_PEERCRED, &ucred, &len) < 0) { + i_error("getsockopt(LOCAL_PEERCRED) failed: %m"); + return -1; + } + + if (ucred.cr_version != XUCRED_VERSION) { + errno = EINVAL; + return -1; + } + + cred_r->uid = ucred.cr_uid; + cred_r->gid = ucred.cr_gid; + cred_r->pid = (pid_t)-1; + return 0; +#elif defined(HAVE_GETPEERUCRED) + /* Solaris */ + ucred_t *ucred = NULL; + + if (getpeerucred(fd, &ucred) < 0) { + i_error("getpeerucred() failed: %m"); + return -1; + } + cred_r->uid = ucred_geteuid(ucred); + cred_r->gid = ucred_getrgid(ucred); + cred_r->pid = ucred_getpid(ucred); + ucred_free(ucred); + + if (cred_r->uid == (uid_t)-1 || + cred_r->gid == (gid_t)-1) { + errno = EINVAL; + return -1; + } + return 0; +#elif defined(NEEDS_LOCAL_CREDS) + /* NetBSD < 5 */ + int i, n, on; + struct iovec iov; + struct msghdr msg; + struct { + struct cmsghdr ch; + char buf[110]; + } cdata; + struct sockcred *sc; + + iov.iov_base = (char *)&on; + iov.iov_len = 1; + + sc = (struct sockcred *)cdata.buf; + sc->sc_uid = sc->sc_euid = sc->sc_gid = sc->sc_egid = -1; + i_zero(&cdata.ch); + + i_zero(&msg); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &cdata; + msg.msg_controllen = sizeof(cdata.ch) + sizeof(cdata.buf); + + for (i = 0; i < 10; i++) { + n = recvmsg(fd, &msg, MSG_WAITALL | MSG_PEEK); + if (n >= 0 || errno != EAGAIN) + break; + usleep(100); + } + if (n < 0) { + i_error("recvmsg() failed: %m"); + return -1; + } + cred_r->uid = sc->sc_euid; + cred_r->gid = sc->sc_egid; + cred_r->pid = (pid_t)-1; + return 0; +#else + errno = EINVAL; + return -1; +#endif +} + +const char *net_ip2addr(const struct ip_addr *ip) +{ + char *addr = t_malloc_no0(MAX_IP_LEN+1); + + if (inet_ntop(ip->family, &ip->u.ip6, addr, MAX_IP_LEN) == NULL) + return ""; + + return addr; +} + +static bool net_addr2ip_inet4_fast(const char *addr, struct ip_addr *ip) +{ + uint8_t *saddr = (void *)&ip->u.ip4.s_addr; + unsigned int i, num; + + if (str_parse_uint(addr, &num, &addr) < 0) + return FALSE; + if (*addr == '\0' && num <= 0xffffffff) { + /* single-number IPv4 address */ + ip->u.ip4.s_addr = htonl(num); + ip->family = AF_INET; + return TRUE; + } + + /* try to parse as a.b.c.d */ + i = 0; + for (;;) { + if (num >= 256) + return FALSE; + saddr[i] = num; + if (i == 3) + break; + i++; + if (*addr != '.') + return FALSE; + addr++; + if (str_parse_uint(addr, &num, &addr) < 0) + return FALSE; + } + if (*addr != '\0') + return FALSE; + ip->family = AF_INET; + return TRUE; +} + +int net_addr2ip(const char *addr, struct ip_addr *ip) +{ + int ret; + + if (net_addr2ip_inet4_fast(addr, ip)) + return 0; + + if (strchr(addr, ':') != NULL) { + /* IPv6 */ + T_BEGIN { + if (addr[0] == '[') { + /* allow [ipv6 addr] */ + size_t len = strlen(addr); + if (addr[len-1] == ']') + addr = t_strndup(addr+1, len-2); + } + ret = inet_pton(AF_INET6, addr, &ip->u.ip6); + } T_END; + if (ret == 0) + return -1; + ip->family = AF_INET6; + } else { + /* IPv4 */ + if (inet_aton(addr, &ip->u.ip4) == 0) + return -1; + ip->family = AF_INET; + } + return 0; +} + +int net_str2port(const char *str, in_port_t *port_r) +{ + uintmax_t l; + + if (str_to_uintmax(str, &l) < 0) + return -1; + + if (l == 0 || l > (in_port_t)-1) + return -1; + *port_r = (in_port_t)l; + return 0; +} + +int net_str2port_zero(const char *str, in_port_t *port_r) +{ + uintmax_t l; + + if (str_to_uintmax(str, &l) < 0) + return -1; + + if (l > (in_port_t)-1) + return -1; + *port_r = (in_port_t)l; + return 0; +} + +int net_str2hostport(const char *str, in_port_t default_port, + const char **host_r, in_port_t *port_r) +{ + const char *p, *host; + in_port_t port; + + if (str[0] == '[') { + /* [IPv6] address, possibly followed by :port */ + p = strchr(str, ']'); + if (p == NULL) + return -1; + host = t_strdup_until(str+1, p++); + } else { + p = strchr(str, ':'); + if (p == NULL || strchr(p+1, ':') != NULL) { + /* host or IPv6 address */ + *host_r = str; + *port_r = default_port; + return 0; + } + host = t_strdup_until(str, p); + } + if (p[0] == '\0') { + *host_r = host; + *port_r = default_port; + return 0; + } + if (p[0] != ':') + return -1; + if (net_str2port(p+1, &port) < 0) + return -1; + *host_r = host; + *port_r = port; + return 0; +} + +int net_ipport2str(const struct ip_addr *ip, in_port_t port, const char **str_r) +{ + if (!IPADDR_IS_V4(ip) && !IPADDR_IS_V6(ip)) return -1; + + *str_r = t_strdup_printf("%s%s%s:%u", + IPADDR_IS_V6(ip) ? "[" : "", + net_ip2addr(ip), + IPADDR_IS_V6(ip) ? "]" : "", + port); + return 0; +} + +int net_ipv6_mapped_ipv4_convert(const struct ip_addr *src, + struct ip_addr *dest) +{ + static uint8_t v4_prefix[] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; + + if (!IPADDR_IS_V6(src)) + return -1; + if (memcmp(src->u.ip6.s6_addr, v4_prefix, sizeof(v4_prefix)) != 0) + return -1; + + i_zero(dest); + dest->family = AF_INET; + memcpy(&dest->u.ip6, &src->u.ip6.s6_addr[3*4], 4); + return 0; +} + +int net_geterror(int fd) +{ + int data; + socklen_t len = sizeof(data); + + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &data, &len) == -1) { + /* we're now really returning the getsockopt()'s error code + instead of the socket's, but normally we should never get + here anyway. */ + return errno; + } + + return data; +} + +const char *net_gethosterror(int error) +{ + i_assert(error != 0); + + return gai_strerror(error); +} + +enum net_hosterror_type net_get_hosterror_type(int error) +{ + const struct { + int error; + enum net_hosterror_type type; + } error_map[] = { +#ifdef EAI_ADDRFAMILY /* Obsoleted by RFC 2553bis-02 */ + { EAI_ADDRFAMILY, NET_HOSTERROR_TYPE_NOT_FOUND }, +#endif + { EAI_AGAIN, NET_HOSTERROR_TYPE_NAMESERVER }, + { EAI_BADFLAGS, NET_HOSTERROR_TYPE_INTERNAL_ERROR }, + { EAI_FAIL, NET_HOSTERROR_TYPE_NAMESERVER }, + { EAI_FAMILY, NET_HOSTERROR_TYPE_INTERNAL_ERROR }, + { EAI_MEMORY, NET_HOSTERROR_TYPE_INTERNAL_ERROR }, +#ifdef EAI_NODATA /* Obsoleted by RFC 2553bis-02 */ + { EAI_NODATA, NET_HOSTERROR_TYPE_NOT_FOUND }, +#endif + { EAI_NONAME, NET_HOSTERROR_TYPE_NOT_FOUND }, + { EAI_SERVICE, NET_HOSTERROR_TYPE_INTERNAL_ERROR }, + { EAI_SOCKTYPE, NET_HOSTERROR_TYPE_INTERNAL_ERROR }, + { EAI_SYSTEM, NET_HOSTERROR_TYPE_INTERNAL_ERROR }, + }; + for (unsigned int i = 0; i < N_ELEMENTS(error_map); i++) { + if (error_map[i].error == error) + return error_map[i].type; + } + + /* shouldn't happen? assume internal error */ + return NET_HOSTERROR_TYPE_INTERNAL_ERROR; +} + +int net_hosterror_notfound(int error) +{ +#ifdef EAI_NODATA /* NODATA is depricated */ + return (error != 1 && (error == EAI_NONAME || error == EAI_NODATA)) ? 1 : 0; +#else + return (error != 1 && (error == EAI_NONAME)) ? 1 : 0; +#endif +} + +const char *net_getservbyport(in_port_t port) +{ + struct servent *entry; + + entry = getservbyport(htons(port), "tcp"); + return entry == NULL ? NULL : entry->s_name; +} + +bool is_ipv4_address(const char *addr) +{ + while (*addr != '\0') { + if (*addr != '.' && !i_isdigit(*addr)) + return FALSE; + addr++; + } + + return TRUE; +} + +bool is_ipv6_address(const char *addr) +{ + bool have_prefix = FALSE; + + if (*addr == '[') { + have_prefix = TRUE; + addr++; + } + while (*addr != '\0') { + if (*addr != ':' && !i_isxdigit(*addr)) { + if (have_prefix && *addr == ']' && addr[1] == '\0') + break; + return FALSE; + } + addr++; + } + + return TRUE; +} + +int net_parse_range(const char *network, struct ip_addr *ip_r, + unsigned int *bits_r) +{ + const char *p; + unsigned int bits, max_bits; + + p = strchr(network, '/'); + if (p != NULL) + network = t_strdup_until(network, p++); + + if (net_addr2ip(network, ip_r) < 0) + return -1; + + max_bits = IPADDR_BITS(ip_r); + if (p == NULL) { + /* full IP address must match */ + bits = max_bits; + } else { + /* get the network mask */ + if (str_to_uint(p, &bits) < 0 || bits > max_bits) + return -1; + } + *bits_r = bits; + return 0; +} + +bool net_is_in_network(const struct ip_addr *ip, + const struct ip_addr *net_ip, unsigned int bits) +{ + struct ip_addr tmp_ip; + const uint32_t *ip1, *ip2; + uint32_t mask, i1, i2; + unsigned int pos, i; + + if (net_ipv6_mapped_ipv4_convert(ip, &tmp_ip) == 0) { + /* IPv4 address mapped disguised as IPv6 address */ + ip = &tmp_ip; + } + + if (ip->family == 0 || net_ip->family == 0) { + /* non-IPv4/IPv6 address (e.g. UNIX socket) never matches + anything */ + return FALSE; + } + if (IPADDR_IS_V4(ip) != IPADDR_IS_V4(net_ip)) { + /* one is IPv6 and one is IPv4 */ + return FALSE; + } + i_assert(IPADDR_IS_V6(ip) == IPADDR_IS_V6(net_ip)); + + if (IPADDR_IS_V4(ip)) { + ip1 = &ip->u.ip4.s_addr; + ip2 = &net_ip->u.ip4.s_addr; + } else { + ip1 = (const void *)&ip->u.ip6; + ip2 = (const void *)&net_ip->u.ip6; + } + + /* check first the full 32bit ints */ + for (pos = 0, i = 0; pos + 32 <= bits; pos += 32, i++) { + if (ip1[i] != ip2[i]) + return FALSE; + } + i1 = htonl(ip1[i]); + i2 = htonl(ip2[i]); + + /* check the last full bytes */ + for (mask = 0xff000000; pos + 8 <= bits; pos += 8, mask >>= 8) { + if ((i1 & mask) != (i2 & mask)) + return FALSE; + } + + /* check the last bits, they're reversed in bytes */ + bits -= pos; + for (mask = 0x80000000 >> (pos % 32); bits > 0; bits--, mask >>= 1) { + if ((i1 & mask) != (i2 & mask)) + return FALSE; + } + return TRUE; +} diff --git a/src/lib/net.h b/src/lib/net.h new file mode 100644 index 0000000..7f8abb7 --- /dev/null +++ b/src/lib/net.h @@ -0,0 +1,199 @@ +#ifndef NET_H +#define NET_H + +#ifndef WIN32 +# include <sys/socket.h> +# include <netinet/in.h> +# include <netdb.h> +# include <arpa/inet.h> +#endif + +#ifdef HAVE_SOCKS_H +#include <socks.h> +#endif + +#ifndef AF_INET6 +# ifdef PF_INET6 +# define AF_INET6 PF_INET6 +# else +# define AF_INET6 10 +# endif +#endif + +struct ip_addr { + unsigned short family; + union { + struct in6_addr ip6; + struct in_addr ip4; + } u; +}; +ARRAY_DEFINE_TYPE(ip_addr, struct ip_addr); + +struct net_unix_cred { + uid_t uid; + gid_t gid; + pid_t pid; +}; + +/* maximum string length of IP address */ +#define MAX_IP_LEN INET6_ADDRSTRLEN + +#define IPADDR_IS_V4(ip) ((ip)->family == AF_INET) +#define IPADDR_IS_V6(ip) ((ip)->family == AF_INET6) +#define IPADDR_BITS(ip) (IPADDR_IS_V4(ip) ? 32 : 128) + +enum net_listen_flags { + /* Try to use SO_REUSEPORT if available. If it's not, this flag is + cleared on return. */ + NET_LISTEN_FLAG_REUSEPORT = 0x01 +}; + +enum net_hosterror_type { + /* Internal error - should be logged as an error */ + NET_HOSTERROR_TYPE_INTERNAL_ERROR, + /* Host not found or no valid IP addresses found */ + NET_HOSTERROR_TYPE_NOT_FOUND, + /* Nameserver returned an error */ + NET_HOSTERROR_TYPE_NAMESERVER, +}; + +/* INADDR_ANY for IPv4 or IPv6. The IPv6 any address may + include IPv4 depending on the system (Linux yes, BSD no). */ +extern const struct ip_addr net_ip4_any; +extern const struct ip_addr net_ip6_any; + +extern const struct ip_addr net_ip4_loopback; +extern const struct ip_addr net_ip6_loopback; + +/* Returns TRUE if IPs are the same */ +bool net_ip_compare(const struct ip_addr *ip1, const struct ip_addr *ip2); +/* Returns 0 if IPs are the same, -1 or 1 otherwise. */ +int net_ip_cmp(const struct ip_addr *ip1, const struct ip_addr *ip2); +unsigned int net_ip_hash(const struct ip_addr *ip); + +/* Connect to TCP socket with ip address. The socket and connect() is + non-blocking. */ +int net_connect_ip(const struct ip_addr *ip, in_port_t port, + const struct ip_addr *my_ip) ATTR_NULL(3); +/* Like net_connect_ip(), but do a blocking connect(). */ +int net_connect_ip_blocking(const struct ip_addr *ip, in_port_t port, + const struct ip_addr *my_ip) ATTR_NULL(3); +/* Like net_connect_ip(), but open a UDP socket. */ +int net_connect_udp(const struct ip_addr *ip, in_port_t port, + const struct ip_addr *my_ip); +/* Returns 0 if we can bind() as given IP, -1 if not. */ +int net_try_bind(const struct ip_addr *ip); +/* Connect to named UNIX socket */ +int net_connect_unix(const char *path); +/* Try to connect to UNIX socket for give number of seconds when connect() + returns EAGAIN or ECONNREFUSED. */ +int net_connect_unix_with_retries(const char *path, unsigned int msecs); +/* Disconnect socket */ +void net_disconnect(int fd); + +/* Set socket blocking/nonblocking */ +void net_set_nonblock(int fd, bool nonblock); +/* Set TCP_CORK if supported, ie. don't send out partial frames. + Returns 0 if ok, -1 if failed. */ +int net_set_cork(int fd, bool cork) ATTR_NOWARN_UNUSED_RESULT; +/* Set TCP_NODELAY, which disables the Nagle algorithm. */ +int net_set_tcp_nodelay(int fd, bool nodelay); +/* Set TCP_QUICKACK, which tells the kernel to not delay ACKs. Note that the + kernel can (and will) re-enable delayed ACKs while processing the TCP stack. + This means that this function needs to be called repeatedly. */ +int net_set_tcp_quickack(int fd, bool quickack); + +/* Set socket kernel buffer sizes */ +int net_set_send_buffer_size(int fd, size_t size); +int net_set_recv_buffer_size(int fd, size_t size); + +/* Listen for connections on a socket */ +int net_listen(const struct ip_addr *my_ip, in_port_t *port, int backlog); +int net_listen_full(const struct ip_addr *my_ip, in_port_t *port, + enum net_listen_flags *flags, int backlog); +/* Listen for connections on an UNIX socket */ +int net_listen_unix(const char *path, int backlog); +/* Like net_listen_unix(), but if socket already exists, try to connect to it. + If it fails with ECONNREFUSED, unlink the socket and try creating it + again. */ +int net_listen_unix_unlink_stale(const char *path, int backlog); +/* Accept a connection on a socket. Returns -1 if the connection got closed, + -2 for other failures. For UNIX sockets addr_r->family=port=0. */ +int net_accept(int fd, struct ip_addr *addr_r, in_port_t *port_r) + ATTR_NULL(2, 3); + +/* Read data from socket, return number of bytes read, + -1 = error, -2 = disconnected */ +ssize_t net_receive(int fd, void *buf, size_t len); + +/* Get IP addresses for host. ips contains ips_count of IPs, they don't need + to be free'd. Returns 0 = ok, others = error code for net_gethosterror() */ +int net_gethostbyname(const char *addr, struct ip_addr **ips, + unsigned int *ips_count); +/* Return host for the IP address. Returns 0 = ok, others = error code for + net_gethosterror(). */ +int net_gethostbyaddr(const struct ip_addr *ip, const char **name_r); +/* get error of net_gethostname() */ +const char *net_gethosterror(int error) ATTR_CONST; +/* Return type of the error returned by net_gethostname() */ +enum net_hosterror_type net_get_hosterror_type(int error); +/* return TRUE if host lookup failed because it didn't exist (ie. not + some error with name server) */ +int net_hosterror_notfound(int error) ATTR_CONST; + +/* Get socket local address/port. For UNIX sockets addr->family=port=0. */ +int net_getsockname(int fd, struct ip_addr *addr, in_port_t *port) + ATTR_NULL(2, 3); +/* Get socket remote address/port. For UNIX sockets addr->family=port=0. */ +int net_getpeername(int fd, struct ip_addr *addr, in_port_t *port) + ATTR_NULL(2, 3); +/* Get UNIX socket name. */ +int net_getunixname(int fd, const char **name_r); +/* Get UNIX socket peer process's credentials. The pid may be (pid_t)-1 if + unavailable. */ +int net_getunixcred(int fd, struct net_unix_cred *cred_r); + +/* Returns ip_addr as string, or "" if ip isn't valid IPv4 or IPv6 address. */ +const char *net_ip2addr(const struct ip_addr *ip); +/* char* -> struct ip_addr translation. */ +int net_addr2ip(const char *addr, struct ip_addr *ip); +/* char* -> in_port_t translation */ +int net_str2port(const char *str, in_port_t *port_r); +/* char* -> in_port_t translation (allows port zero) */ +int net_str2port_zero(const char *str, in_port_t *port_r); +/* Parse "host", "host:port", "IPv4", "IPv4:port", "IPv6", "[IPv6]" or + "[IPv6]:port" to its host and port components. [IPv6] address is returned + without []. If no port is given, return default_port. The :port in the + parsed string isn't allowed to be zero, but default_port=0 is passed + through. */ +int net_str2hostport(const char *str, in_port_t default_port, + const char **host_r, in_port_t *port_r); +/* Converts ip and port to ipv4:port or [ipv6]:port. Returns -1 if + ip is not valid IPv4 or IPv6 address. */ +int net_ipport2str(const struct ip_addr *ip, in_port_t port, const char **str_r); + +/* Convert IPv6 mapped IPv4 address to an actual IPv4 address. Returns 0 if + successful, -1 if the source address isn't IPv6 mapped IPv4 address. */ +int net_ipv6_mapped_ipv4_convert(const struct ip_addr *src, + struct ip_addr *dest); + +/* Get socket error */ +int net_geterror(int fd); + +/* Get name of TCP service */ +const char *net_getservbyport(in_port_t port) ATTR_CONST; + +bool is_ipv4_address(const char *addr) ATTR_PURE; +bool is_ipv6_address(const char *addr) ATTR_PURE; + +/* Parse network as ip/bits. Returns 0 if successful, -1 if invalid input. */ +int net_parse_range(const char *network, struct ip_addr *ip_r, + unsigned int *bits_r); +/* Returns TRUE if ip is in net_ip/bits network. IPv4-mapped IPv6 addresses + in "ip" parameter are converted to plain IPv4 addresses before matching. + No conversion is done to net_ip though, so using IPv4-mapped IPv6 addresses + there will always fail. Invalid IPs (family=0) never match anything. */ +bool net_is_in_network(const struct ip_addr *ip, const struct ip_addr *net_ip, + unsigned int bits) ATTR_PURE; + +#endif diff --git a/src/lib/nfs-workarounds.c b/src/lib/nfs-workarounds.c new file mode 100644 index 0000000..4bbea8a --- /dev/null +++ b/src/lib/nfs-workarounds.c @@ -0,0 +1,406 @@ +/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */ + +/* + These tests were done with various Linux 2.6 kernels, FreeBSD 6.2 and + Solaris 8 and 10. + + Attribute cache is usually flushed with chown()ing or fchown()ing the file. + The safest way would be to use uid=-1 gid=-1, but this doesn't work with + Linux (it does with FreeBSD 6.2 and Solaris). So we'll first get the + file's owner and use it. As long as we're not root the file's owner can't + change accidentally. If would be possible to also use chmod()/fchmod(), but + that's riskier since it could actually cause an unwanted change. + + Write cache can be flushed with fdatasync(). It's all we need, but other + tested alternatives are: fcntl locking (Linux 2.6, Solaris), + fchown() (Solaris) and dup()+close() (Linux 2.6, Solaris). + + Read cache flushing is more problematic. There's no universal way to do it. + The working methods are: + + Linux 2.6: fcntl(), O_DIRECT + Solaris: fchown(), fcntl(), dup()+close() + FreeBSD 6.2: fchown() + + fchown() can be easily used for Solaris and FreeBSD, but Linux requires + playing with locks. O_DIRECT requires CONFIG_NFS_DIRECTIO to be enabled, so + we can't always use it. +*/ + +#include "lib.h" +#include "path-util.h" +#include "nfs-workarounds.h" + +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> + +#if defined (__linux__) || defined(__sun) +# define READ_CACHE_FLUSH_FCNTL +#endif +#if defined(__FreeBSD__) || defined(__sun) +# define ATTRCACHE_FLUSH_CHOWN_UID_1 +#endif + +static void nfs_flush_file_handle_cache_parent_dir(const char *path); + +static int +nfs_safe_do(const char *path, int (*callback)(const char *path, void *context), + void *context) +{ + unsigned int i; + int ret; + + for (i = 1;; i++) { + ret = callback(path, context); + if (ret == 0 || errno != ESTALE || i == NFS_ESTALE_RETRY_COUNT) + break; + + /* ESTALE: Some operating systems may fail with this if they + can't internally revalidate the NFS file handle. Flush the + file handle and try again */ + nfs_flush_file_handle_cache(path); + } + return ret; +} + +struct nfs_safe_open_context { + int flags; + int fd; +}; + +static int nfs_safe_open_callback(const char *path, void *context) +{ + struct nfs_safe_open_context *ctx = context; + + ctx->fd = open(path, ctx->flags); + return ctx->fd == -1 ? -1 : 0; +} + +int nfs_safe_open(const char *path, int flags) +{ + struct nfs_safe_open_context ctx; + + i_assert((flags & O_CREAT) == 0); + + ctx.flags = flags; + if (nfs_safe_do(path, nfs_safe_open_callback, &ctx) < 0) + return -1; + + return ctx.fd; +} + +static int nfs_safe_stat_callback(const char *path, void *context) +{ + struct stat *buf = context; + + return stat(path, buf); +} + +int nfs_safe_stat(const char *path, struct stat *buf) +{ + return nfs_safe_do(path, nfs_safe_stat_callback, buf); +} + +static int nfs_safe_lstat_callback(const char *path, void *context) +{ + struct stat *buf = context; + + return lstat(path, buf); +} + +int nfs_safe_lstat(const char *path, struct stat *buf) +{ + return nfs_safe_do(path, nfs_safe_lstat_callback, buf); +} + +int nfs_safe_link(const char *oldpath, const char *newpath, bool links1) +{ + struct stat st; + nlink_t orig_link_count = 1; + + if (!links1) { + if (stat(oldpath, &st) < 0) + return -1; + orig_link_count = st.st_nlink; + } + + if (link(oldpath, newpath) == 0) { +#ifndef __FreeBSD__ + return 0; +#endif + /* FreeBSD at least up to v6.2 converts EEXIST errors to + success. */ + } else if (errno != EEXIST) + return -1; + + /* We don't know if it succeeded or failed. stat() to make sure. */ + if (stat(oldpath, &st) < 0) + return -1; + if (st.st_nlink == orig_link_count) { + errno = EEXIST; + return -1; + } + return 0; +} + +static void nfs_flush_chown_uid(const char *path) +{ + +#ifdef ATTRCACHE_FLUSH_CHOWN_UID_1 + uid_t uid = (uid_t)-1; + if (chown(path, uid, (gid_t)-1) < 0) { + if (errno == ESTALE || errno == EPERM || errno == ENOENT) { + /* attr cache is flushed */ + return; + } + if (likely(errno == ENOENT)) { + nfs_flush_file_handle_cache_parent_dir(path); + return; + } + i_error("nfs_flush_chown_uid: chown(%s) failed: %m", path); + } +#else + struct stat st; + + if (stat(path, &st) == 0) { + /* do nothing */ + } else { + if (errno == ESTALE) { + /* ESTALE causes the OS to flush the attr cache */ + return; + } + if (likely(errno == ENOENT)) { + nfs_flush_file_handle_cache_parent_dir(path); + return; + } + i_error("nfs_flush_chown_uid: stat(%s) failed: %m", path); + return; + } + /* we use chmod for this operation since chown has been seen to drop S_UID + and S_GID bits from directory inodes in certain conditions */ + if (chmod(path, st.st_mode & 07777) < 0) { + if (errno == EPERM) { + /* attr cache is flushed */ + return; + } + if (likely(errno == ENOENT)) { + nfs_flush_file_handle_cache_parent_dir(path); + return; + } + i_error("nfs_flush_chown_uid: chmod(%s, %04o) failed: %m", + path, st.st_mode & 07777); + } +#endif +} + +#ifdef __FreeBSD__ +static bool nfs_flush_fchown_uid(const char *path, int fd) +{ + uid_t uid; +#ifndef ATTRCACHE_FLUSH_CHOWN_UID_1 + struct stat st; + + if (fstat(fd, &st) < 0) { + if (likely(errno == ESTALE)) + return FALSE; + i_error("nfs_flush_attr_cache_fchown: fstat(%s) failed: %m", + path); + return TRUE; + } + uid = st.st_uid; +#else + uid = (uid_t)-1; +#endif + if (fchown(fd, uid, (gid_t)-1) < 0) { + if (errno == ESTALE) + return FALSE; + if (likely(errno == EACCES || errno == EPERM)) { + /* attr cache is flushed */ + return TRUE; + } + + i_error("nfs_flush_attr_cache_fd_locked: fchown(%s) failed: %m", + path); + } + return TRUE; +} +#endif + +#ifdef READ_CACHE_FLUSH_FCNTL +static bool nfs_flush_fcntl(const char *path, int fd) +{ + static bool locks_disabled = FALSE; + struct flock fl; + int ret; + + if (locks_disabled) + return FALSE; + + /* If the file was already locked, we'll just get the same lock + again. It should succeed just fine. If was was unlocked, we'll + have to get a lock and then unlock it. Linux 2.6 flushes read cache + only when read/write locking succeeded. */ + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + alarm(60); + ret = fcntl(fd, F_SETLKW, &fl); + alarm(0); + + if (unlikely(ret < 0)) { + if (errno == ENOLCK) { + locks_disabled = TRUE; + return FALSE; + } + i_error("nfs_flush_fcntl: fcntl(%s, F_RDLCK) failed: %m", path); + return FALSE; + } + + fl.l_type = F_UNLCK; + (void)fcntl(fd, F_SETLKW, &fl); + return TRUE; +} +#endif + +void nfs_flush_attr_cache_unlocked(const char *path) +{ + int fd; + + /* Try to flush the attribute cache the nice way first. */ + fd = open(path, O_RDONLY); + if (fd != -1) + i_close_fd(&fd); + else if (errno == ESTALE) { + /* this already flushed the cache */ + } else { + /* most likely ENOENT, which means a negative cache hit. + flush the file handles for its parent directory. */ + nfs_flush_file_handle_cache_parent_dir(path); + } +} + +void nfs_flush_attr_cache_maybe_locked(const char *path) +{ + nfs_flush_chown_uid(path); +} + +void nfs_flush_attr_cache_fd_locked(const char *path ATTR_UNUSED, + int fd ATTR_UNUSED) +{ +#ifdef __FreeBSD__ + /* FreeBSD doesn't flush attribute cache with fcntl(), so we have + to do it ourself. */ + (void)nfs_flush_fchown_uid(path, fd); +#else + /* Linux and Solaris are fine. */ +#endif +} + +static bool +nfs_flush_file_handle_cache_dir(const char *path, bool try_parent ATTR_UNUSED) +{ +#ifdef __linux__ + /* chown()ing parent is the safest way to handle this */ + nfs_flush_chown_uid(path); +#else + /* rmdir() is the only choice with FreeBSD and Solaris */ + if (unlikely(rmdir(path) == 0)) { + if (mkdir(path, 0700) == 0) { + i_warning("nfs_flush_file_handle_cache_dir: " + "rmdir(%s) unexpectedly " + "removed the dir. recreated.", path); + } else { + i_warning("nfs_flush_file_handle_cache_dir: " + "rmdir(%s) unexpectedly " + "removed the dir. mkdir() failed: %m", path); + } + } else if (errno == ESTALE || errno == ENOTDIR || + errno == ENOTEMPTY || errno == EEXIST || errno == EACCES) { + /* expected failures */ + } else if (errno == ENOENT) { + return FALSE; + } else if (errno == EINVAL && try_parent) { + /* Solaris gives this if we're trying to rmdir() the current + directory. Work around this by temporarily changing the + current directory to the parent directory. */ + const char *cur_path, *p; + int cur_dir_fd; + bool ret; + + cur_dir_fd = open(".", O_RDONLY); + if (cur_dir_fd == -1) { + i_error("open(.) failed for: %m"); + return TRUE; + } + + const char *error; + if (t_get_working_dir(&cur_path, &error) < 0) { + i_error("nfs_flush_file_handle_cache_dir: %s", error); + i_close_fd(&cur_dir_fd); + return TRUE; + } + p = strrchr(cur_path, '/'); + if (p == NULL) + cur_path = "/"; + else + cur_path = t_strdup_until(cur_path, p); + if (chdir(cur_path) < 0) { + i_error("nfs_flush_file_handle_cache_dir: " + "chdir() failed"); + } + ret = nfs_flush_file_handle_cache_dir(path, FALSE); + if (fchdir(cur_dir_fd) < 0) + i_error("fchdir() failed: %m"); + i_close_fd(&cur_dir_fd); + return ret; + } else { + i_error("nfs_flush_file_handle_cache_dir: " + "rmdir(%s) failed: %m", path); + } +#endif + return TRUE; +} + +static void nfs_flush_file_handle_cache_parent_dir(const char *path) +{ + const char *p; + + p = strrchr(path, '/'); + T_BEGIN { + if (p == NULL) + (void)nfs_flush_file_handle_cache_dir(".", TRUE); + else + (void)nfs_flush_file_handle_cache_dir(t_strdup_until(path, p), + TRUE); + } T_END; +} + +void nfs_flush_file_handle_cache(const char *path) +{ + nfs_flush_file_handle_cache_parent_dir(path); +} + +void nfs_flush_read_cache_locked(const char *path ATTR_UNUSED, + int fd ATTR_UNUSED) +{ +#ifdef READ_CACHE_FLUSH_FCNTL + /* already flushed when fcntl() was called */ +#else + /* we can only hope that underlying filesystem uses micro/nanosecond + resolution so that attribute cache flushing notices mtime changes */ + nfs_flush_attr_cache_fd_locked(path, fd); +#endif +} + +void nfs_flush_read_cache_unlocked(const char *path, int fd) +{ +#ifdef READ_CACHE_FLUSH_FCNTL + if (!nfs_flush_fcntl(path, fd)) + nfs_flush_attr_cache_fd_locked(path, fd); +#else + nfs_flush_read_cache_locked(path, fd); +#endif +} diff --git a/src/lib/nfs-workarounds.h b/src/lib/nfs-workarounds.h new file mode 100644 index 0000000..261f523 --- /dev/null +++ b/src/lib/nfs-workarounds.h @@ -0,0 +1,40 @@ +#ifndef NFS_WORKAROUNDS_H +#define NFS_WORKAROUNDS_H + +/* Note that some systems (Solaris) may use a macro to redefine struct stat */ +#include <sys/stat.h> + +/* When syscall fails with ESTALE error, how many times to try reopening the + file and retrying the operation. */ +#define NFS_ESTALE_RETRY_COUNT 10 + +/* Same as open(), but try to handle ESTALE errors. */ +int nfs_safe_open(const char *path, int flags); +/* Same as stat(), but try to handle ESTALE errors. + Doesn't flush attribute cache. */ +int nfs_safe_stat(const char *path, struct stat *buf); +int nfs_safe_lstat(const char *path, struct stat *buf); +/* Same as link(), but handle problems with link() by verifying the file's + link count changes. If links1=TRUE, assume the original file's link count + is 1, otherwise stat() first to find it out. */ +int nfs_safe_link(const char *oldpath, const char *newpath, bool links1); + +/* Flush attribute cache for given path. The file must not be fcntl locked or + the locks may get dropped. */ +void nfs_flush_attr_cache_unlocked(const char *path); +/* Flush attribute cache for given path. The file may be fcntl locked. */ +void nfs_flush_attr_cache_maybe_locked(const char *path); +/* Flush attribute cache for a fcntl locked file descriptor. If locking flushes + the attribute cache with the running OS, this function does nothing. + The given path is used only for logging. */ +void nfs_flush_attr_cache_fd_locked(const char *path, int fd); +/* Flush file handle cache for given file. */ +void nfs_flush_file_handle_cache(const char *path); + +/* Flush read cache for fd that was just fcntl locked. If the OS flushes + read cache when fcntl locking file, this function does nothing. */ +void nfs_flush_read_cache_locked(const char *path, int fd); +/* Flush read cache for fd that doesn't have fcntl locks. */ +void nfs_flush_read_cache_unlocked(const char *path, int fd); + +#endif diff --git a/src/lib/numpack.c b/src/lib/numpack.c new file mode 100644 index 0000000..8a59f3a --- /dev/null +++ b/src/lib/numpack.c @@ -0,0 +1,57 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "numpack.h" + +void numpack_encode(buffer_t *buf, uint64_t num) +{ + /* number continues as long as the highest bit is set */ + while (num >= 0x80) { + buffer_append_c(buf, (num & 0x7f) | 0x80); + num >>= 7; + } + + buffer_append_c(buf, num); +} + +int numpack_decode(const uint8_t **p, const uint8_t *end, uint64_t *num_r) + ATTR_UNSIGNED_WRAPS +{ + const uint8_t *c = *p; + uint64_t value = 0; + unsigned int bits = 0; + + while (bits < 64) { + if (c == end) + return -1; + + value |= (uint64_t)(*c & 0x7f) << bits; + if (*c < 0x80) + break; + + bits += 7; + c++; + } + + bits += bits_required8(*c); + if (bits > 64) /* overflow */ + return -1; + + *p = c + 1; + *num_r = value; + return 0; +} + +int numpack_decode32(const uint8_t **p, const uint8_t *end, uint32_t *num_r) +{ + uint64_t num; + + if (numpack_decode(p, end, &num) < 0) + return -1; + if (num > 4294967295U) + return -1; + + *num_r = (uint32_t)num; + return 0; +} diff --git a/src/lib/numpack.h b/src/lib/numpack.h new file mode 100644 index 0000000..1ee0737 --- /dev/null +++ b/src/lib/numpack.h @@ -0,0 +1,11 @@ +#ifndef NUMPACK_H +#define NUMPACK_H + +/* Numbers are stored by 7 bits at a time. The highest bit specifies if the + number continues to next byte. */ + +void numpack_encode(buffer_t *buf, uint64_t num); +int numpack_decode(const uint8_t **p, const uint8_t *end, uint64_t *num_r); +int numpack_decode32(const uint8_t **p, const uint8_t *end, uint32_t *num_r); + +#endif diff --git a/src/lib/ostream-buffer.c b/src/lib/ostream-buffer.c new file mode 100644 index 0000000..af3dbef --- /dev/null +++ b/src/lib/ostream-buffer.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "ostream-private.h" + +struct buffer_ostream { + struct ostream_private ostream; + buffer_t *buf; + bool seeked; +}; + +static int o_stream_buffer_seek(struct ostream_private *stream, uoff_t offset) +{ + struct buffer_ostream *bstream = + container_of(stream, struct buffer_ostream, ostream); + + bstream->seeked = TRUE; + stream->ostream.offset = offset; + return 1; +} + +static int +o_stream_buffer_write_at(struct ostream_private *stream, + const void *data, size_t size, uoff_t offset) +{ + struct buffer_ostream *bstream = + container_of(stream, struct buffer_ostream, ostream); + + buffer_write(bstream->buf, offset, data, size); + return 0; +} + +static ssize_t +o_stream_buffer_sendv(struct ostream_private *stream, + const struct const_iovec *iov, unsigned int iov_count) +{ + struct buffer_ostream *bstream = + container_of(stream, struct buffer_ostream, ostream); + size_t left, n, offset; + ssize_t ret = 0; + unsigned int i; + + offset = bstream->seeked ? stream->ostream.offset : bstream->buf->used; + + for (i = 0; i < iov_count; i++) { + left = bstream->ostream.max_buffer_size - + stream->ostream.offset; + n = I_MIN(left, iov[i].iov_len); + buffer_write(bstream->buf, offset, iov[i].iov_base, n); + stream->ostream.offset += n; offset += n; + ret += n; + if (n != iov[i].iov_len) + break; + } + return ret; +} + +static size_t +o_stream_buffer_get_buffer_used_size(const struct ostream_private *stream) +{ + const struct buffer_ostream *bstream = + container_of(stream, const struct buffer_ostream, ostream); + + return bstream->buf->used; +} + +struct ostream *o_stream_create_buffer(buffer_t *buf) +{ + struct buffer_ostream *bstream; + struct ostream *output; + + bstream = i_new(struct buffer_ostream, 1); + /* we don't set buffer as blocking, because if max_buffer_size is + changed it can get truncated. this is used in various places in + unit tests. */ + bstream->ostream.max_buffer_size = SIZE_MAX; + bstream->ostream.seek = o_stream_buffer_seek; + bstream->ostream.sendv = o_stream_buffer_sendv; + bstream->ostream.write_at = o_stream_buffer_write_at; + bstream->ostream.get_buffer_used_size = + o_stream_buffer_get_buffer_used_size; + + bstream->buf = buf; + output = o_stream_create(&bstream->ostream, NULL, -1); + o_stream_set_name(output, "(buffer)"); + return output; +} diff --git a/src/lib/ostream-failure-at.c b/src/lib/ostream-failure-at.c new file mode 100644 index 0000000..87bd7b8 --- /dev/null +++ b/src/lib/ostream-failure-at.c @@ -0,0 +1,123 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "ostream-private.h" +#include "ostream-failure-at.h" + +struct failure_at_ostream { + struct ostream_private ostream; + char *error_string; + uoff_t failure_offset; + bool failed; +}; + +static void o_stream_failure_at_destroy(struct iostream_private *stream) +{ + struct failure_at_ostream *fstream = + container_of(stream, struct failure_at_ostream, + ostream.iostream); + + i_free(fstream->error_string); + o_stream_unref(&fstream->ostream.parent); +} + +static ssize_t +o_stream_failure_at_sendv(struct ostream_private *stream, + const struct const_iovec *iov, unsigned int iov_count) +{ + struct failure_at_ostream *fstream = + container_of(stream, struct failure_at_ostream, ostream); + unsigned int i; + struct const_iovec *iov_dup; + unsigned int iov_dup_count; + uoff_t bytes_until_failure, blocking_bytes_count = 0; + ssize_t ret; + + if (stream->ostream.blocking) { + /* blocking ostream must return either a full success or a + failure. if the current write would go past failure_offset, + return a failure now before writing anything. */ + for (i = 0; i < iov_count; i++) + blocking_bytes_count += iov[i].iov_len; + if (blocking_bytes_count > 0) { + /* if we're exactly at the failure offset after this + write, fail it only on the next write. */ + blocking_bytes_count--; + } + } + + if (fstream->failure_offset <= stream->ostream.offset + blocking_bytes_count) { + io_stream_set_error(&stream->iostream, "%s", + fstream->error_string); + stream->ostream.stream_errno = errno = EIO; + fstream->failed = TRUE; + return -1; + } + bytes_until_failure = fstream->failure_offset - stream->ostream.offset; + + iov_dup = i_new(struct const_iovec, iov_count); + iov_dup_count = iov_count; + for (i = 0; i < iov_count; i++) { + iov_dup[i] = iov[i]; + if (iov_dup[i].iov_len >= bytes_until_failure) { + iov_dup[i].iov_len = bytes_until_failure; + iov_dup_count = i+1; + break; + } + } + ret = o_stream_sendv(stream->parent, iov_dup, iov_dup_count); + i_free(iov_dup); + + if (ret < 0) { + o_stream_copy_error_from_parent(stream); + return -1; + } + stream->ostream.offset += ret; + return ret; +} + +static int +o_stream_failure_at_flush(struct ostream_private *stream) +{ + struct failure_at_ostream *fstream = + container_of(stream, struct failure_at_ostream, ostream); + + if (fstream->failed) { + io_stream_set_error(&stream->iostream, "%s", + fstream->error_string); + stream->ostream.stream_errno = errno = EIO; + return -1; + } + return o_stream_flush_parent(stream); +} + +struct ostream * +o_stream_create_failure_at(struct ostream *output, uoff_t failure_offset, + const char *error_string) +{ + struct failure_at_ostream *fstream; + + fstream = i_new(struct failure_at_ostream, 1); + fstream->ostream.sendv = o_stream_failure_at_sendv; + fstream->ostream.flush = o_stream_failure_at_flush; + fstream->ostream.iostream.destroy = o_stream_failure_at_destroy; + fstream->failure_offset = failure_offset; + fstream->error_string = i_strdup(error_string); + return o_stream_create(&fstream->ostream, output, + o_stream_get_fd(output)); +} + +struct ostream * +o_stream_create_failure_at_flush(struct ostream *output, const char *error_string) +{ + struct failure_at_ostream *fstream; + + fstream = i_new(struct failure_at_ostream, 1); + fstream->ostream.flush = o_stream_failure_at_flush; + fstream->ostream.iostream.destroy = o_stream_failure_at_destroy; + fstream->error_string = i_strdup(error_string); + fstream->failed = TRUE; + return o_stream_create(&fstream->ostream, output, + o_stream_get_fd(output)); +} diff --git a/src/lib/ostream-failure-at.h b/src/lib/ostream-failure-at.h new file mode 100644 index 0000000..61e1d72 --- /dev/null +++ b/src/lib/ostream-failure-at.h @@ -0,0 +1,10 @@ +#ifndef OSTREAM_FAILURE_AT_H +#define OSTREAM_FAILURE_AT_H + +struct ostream * +o_stream_create_failure_at(struct ostream *output, uoff_t failure_offset, + const char *error_string); +struct ostream * +o_stream_create_failure_at_flush(struct ostream *output, const char *error_string); + +#endif diff --git a/src/lib/ostream-file-private.h b/src/lib/ostream-file-private.h new file mode 100644 index 0000000..2d58933 --- /dev/null +++ b/src/lib/ostream-file-private.h @@ -0,0 +1,45 @@ +#ifndef OSTREAM_FILE_PRIVATE_H +#define OSTREAM_FILE_PRIVATE_H + +#include "ostream-private.h" + +struct file_ostream { + struct ostream_private ostream; + + ssize_t (*writev)(struct file_ostream *fstream, + const struct const_iovec *iov, + unsigned int iov_count); + + int fd; + struct io *io; + uoff_t buffer_offset; + uoff_t real_offset; + + unsigned char *buffer; /* ring-buffer */ + size_t buffer_size, optimal_block_size; + size_t head, tail; /* first unsent/unused byte */ + + bool full:1; /* if head == tail, is buffer empty or full? */ + bool file:1; + bool flush_pending:1; + bool socket_cork_set:1; + bool no_socket_cork:1; + bool no_socket_nodelay:1; + bool no_socket_quickack:1; + bool no_sendfile:1; + bool autoclose_fd:1; +}; + +struct ostream * +o_stream_create_file_common(struct file_ostream *fstream, + int fd, size_t max_buffer_size, bool autoclose_fd); +ssize_t o_stream_file_writev(struct file_ostream *fstream, + const struct const_iovec *iov, + unsigned int iov_size); +ssize_t o_stream_file_sendv(struct ostream_private *stream, + const struct const_iovec *iov, + unsigned int iov_count); +void o_stream_file_close(struct iostream_private *stream, + bool close_parent); + +#endif diff --git a/src/lib/ostream-file.c b/src/lib/ostream-file.c new file mode 100644 index 0000000..2be00d2 --- /dev/null +++ b/src/lib/ostream-file.c @@ -0,0 +1,1154 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +/* @UNSAFE: whole file */ + +#include "lib.h" +#include "ioloop.h" +#include "write-full.h" +#include "net.h" +#include "sendfile-util.h" +#include "istream.h" +#include "istream-private.h" +#include "ostream-file-private.h" + +#include <unistd.h> +#include <sys/stat.h> +#ifdef HAVE_SYS_UIO_H +# include <sys/uio.h> +#endif +#include <fcntl.h> + +/* try to keep the buffer size within 4k..128k. ReiserFS may actually return + 128k as optimal size. */ +#define DEFAULT_OPTIMAL_BLOCK_SIZE IO_BLOCK_SIZE +#define MAX_OPTIMAL_BLOCK_SIZE (128*1024) + +#define IS_STREAM_EMPTY(fstream) \ + ((fstream)->head == (fstream)->tail && !(fstream)->full) + +#define MAX_SSIZE_T(size) \ + ((size) < SSIZE_T_MAX ? (size_t)(size) : SSIZE_T_MAX) + +static void stream_send_io(struct file_ostream *fstream); +static struct ostream * o_stream_create_fd_common(int fd, + size_t max_buffer_size, bool autoclose_fd); + +static void stream_closed(struct file_ostream *fstream) +{ + io_remove(&fstream->io); + + if (fstream->autoclose_fd && fstream->fd != -1) { + /* Ignore ECONNRESET because we don't really care about it here, + as we are closing the socket down in any case. There might be + unsent data but nothing we can do about that. */ + if (unlikely(close(fstream->fd) < 0 && errno != ECONNRESET)) { + i_error("file_ostream.close(%s) failed: %m", + o_stream_get_name(&fstream->ostream.ostream)); + } + } + fstream->fd = -1; + + fstream->ostream.ostream.closed = TRUE; +} + +void o_stream_file_close(struct iostream_private *stream, + bool close_parent ATTR_UNUSED) +{ + struct file_ostream *fstream = + container_of(stream, struct file_ostream, ostream.iostream); + + stream_closed(fstream); +} + +static void o_stream_file_destroy(struct iostream_private *stream) +{ + struct file_ostream *fstream = + container_of(stream, struct file_ostream, ostream.iostream); + + i_free(fstream->buffer); +} + +static size_t file_buffer_get_used_size(struct file_ostream *fstream) +{ + if (fstream->head == fstream->tail) + return fstream->full ? fstream->buffer_size : 0; + else if (fstream->head < fstream->tail) { + /* ...HXXXT... */ + return fstream->tail - fstream->head; + } else { + /* XXXT...HXXX */ + return fstream->tail + + (fstream->buffer_size - fstream->head); + } +} + +static void update_buffer(struct file_ostream *fstream, size_t size) +{ + size_t used; + + if (IS_STREAM_EMPTY(fstream) || size == 0) + return; + + if (fstream->head < fstream->tail) { + /* ...HXXXT... */ + used = fstream->tail - fstream->head; + i_assert(size <= used); + fstream->head += size; + } else { + /* XXXT...HXXX */ + used = fstream->buffer_size - fstream->head; + if (size > used) { + size -= used; + i_assert(size <= fstream->tail); + fstream->head = size; + } else { + fstream->head += size; + } + + fstream->full = FALSE; + } + + if (fstream->head == fstream->tail) + fstream->head = fstream->tail = 0; + + if (fstream->head == fstream->buffer_size) + fstream->head = 0; +} + +static void o_stream_socket_cork(struct file_ostream *fstream) +{ + if (fstream->ostream.corked && !fstream->socket_cork_set) { + if (!fstream->no_socket_cork) { + if (net_set_cork(fstream->fd, TRUE) < 0) + fstream->no_socket_cork = TRUE; + else + fstream->socket_cork_set = TRUE; + } + } +} + +static int o_stream_lseek(struct file_ostream *fstream) +{ + off_t ret; + + if (fstream->real_offset == fstream->buffer_offset) + return 0; + + ret = lseek(fstream->fd, (off_t)fstream->buffer_offset, SEEK_SET); + if (ret < 0) { + io_stream_set_error(&fstream->ostream.iostream, + "lseek() failed: %m"); + fstream->ostream.ostream.stream_errno = errno; + return -1; + } + + if (ret != (off_t)fstream->buffer_offset) { + io_stream_set_error(&fstream->ostream.iostream, + "lseek() returned wrong value"); + fstream->ostream.ostream.stream_errno = EINVAL; + return -1; + } + fstream->real_offset = fstream->buffer_offset; + return 0; +} + +ssize_t o_stream_file_writev(struct file_ostream *fstream, + const struct const_iovec *iov, + unsigned int iov_count) +{ + ssize_t ret; + size_t size, sent; + unsigned int i; + + if (iov_count == 1) { + i_assert(iov->iov_len > 0); + + if (!fstream->file || + fstream->real_offset == fstream->buffer_offset) { + ret = write(fstream->fd, iov->iov_base, iov->iov_len); + if (ret > 0) + fstream->real_offset += ret; + } else { + ret = pwrite(fstream->fd, iov->iov_base, iov->iov_len, + fstream->buffer_offset); + } + } else { + if (o_stream_lseek(fstream) < 0) + return -1; + + sent = 0; + while (iov_count > IOV_MAX) { + size = 0; + for (i = 0; i < IOV_MAX; i++) + size += iov[i].iov_len; + + ret = writev(fstream->fd, (const struct iovec *)iov, + IOV_MAX); + if (ret != (ssize_t)size) { + break; + } + + fstream->real_offset += ret; + fstream->buffer_offset += ret; + sent += ret; + iov += IOV_MAX; + iov_count -= IOV_MAX; + } + + if (iov_count <= IOV_MAX) { + size = 0; + for (i = 0; i < iov_count; i++) + size += iov[i].iov_len; + + ret = writev(fstream->fd, (const struct iovec *)iov, + iov_count); + } + if (ret > 0) { + fstream->real_offset += ret; + ret += sent; + } else if (!fstream->file && sent > 0) { + /* return what we managed to get sent */ + ret = sent; + } + } + return ret; +} + +static ssize_t +o_stream_file_writev_full(struct file_ostream *fstream, + const struct const_iovec *iov, + unsigned int iov_count) +{ + ssize_t ret, ret2; + size_t size, total_size; + bool partial; + unsigned int i; + + for (i = 0, total_size = 0; i < iov_count; i++) + total_size += iov[i].iov_len; + + o_stream_socket_cork(fstream); + ret = fstream->writev(fstream, iov, iov_count); + partial = ret != (ssize_t)total_size; + + if (ret < 0) { + if (fstream->file) { + if (errno == EINTR) { + /* automatically retry */ + return o_stream_file_writev_full(fstream, iov, iov_count); + } + } else if (errno == EAGAIN || errno == EINTR) { + /* try again later */ + return 0; + } + fstream->ostream.ostream.stream_errno = errno; + stream_closed(fstream); + return -1; + } + if (unlikely(ret == 0 && fstream->file)) { + /* assume out of disk space */ + fstream->ostream.ostream.stream_errno = ENOSPC; + stream_closed(fstream); + return -1; + } + fstream->buffer_offset += ret; + if (partial && fstream->file) { + /* we failed to write everything to a file. either we ran out + of disk space or we're writing to NFS. try to write the + rest to resolve this. */ + size = ret; + while (iov_count > 0 && size >= iov->iov_len) { + size -= iov->iov_len; + iov++; + iov_count--; + } + i_assert(iov_count > 0); + if (size == 0) + ret2 = o_stream_file_writev_full(fstream, iov, iov_count); + else { + /* write the first iov separately */ + struct const_iovec new_iov; + + new_iov.iov_base = + CONST_PTR_OFFSET(iov->iov_base, size); + new_iov.iov_len = iov->iov_len - size; + ret2 = o_stream_file_writev_full(fstream, &new_iov, 1); + if (ret2 > 0) { + i_assert((size_t)ret2 == new_iov.iov_len); + /* write the rest */ + if (iov_count > 1) { + ret += ret2; + ret2 = o_stream_file_writev_full(fstream, iov + 1, + iov_count - 1); + } + } + } + i_assert(ret2 != 0); + if (ret2 < 0) + ret = ret2; + else + ret += ret2; + } + i_assert(ret < 0 || !fstream->file || + (size_t)ret == total_size); + return ret; +} + +/* returns how much of vector was used */ +static int o_stream_fill_iovec(struct file_ostream *fstream, + struct const_iovec iov[2]) +{ + if (IS_STREAM_EMPTY(fstream)) + return 0; + + if (fstream->head < fstream->tail) { + iov[0].iov_base = fstream->buffer + fstream->head; + iov[0].iov_len = fstream->tail - fstream->head; + return 1; + } else { + iov[0].iov_base = fstream->buffer + fstream->head; + iov[0].iov_len = fstream->buffer_size - fstream->head; + if (fstream->tail == 0) + return 1; + else { + iov[1].iov_base = fstream->buffer; + iov[1].iov_len = fstream->tail; + return 2; + } + } +} + +static int buffer_flush(struct file_ostream *fstream) +{ + struct const_iovec iov[2]; + int iov_len; + ssize_t ret; + + iov_len = o_stream_fill_iovec(fstream, iov); + if (iov_len > 0) { + ret = o_stream_file_writev_full(fstream, iov, iov_len); + if (ret < 0) + return -1; + + update_buffer(fstream, ret); + } + + return IS_STREAM_EMPTY(fstream) ? 1 : 0; +} + +static void o_stream_tcp_flush_via_nodelay(struct file_ostream *fstream) +{ + if (net_set_tcp_nodelay(fstream->fd, TRUE) < 0) { + /* Don't bother logging errors. There are quite a lot of + different errors that need to be ignored, and it differs + between OSes. At least: + Linux: ENOTSUP, ENOTSOCK, ENOPROTOOPT + FreeBSD: EINVAL, ECONNRESET */ + fstream->no_socket_nodelay = TRUE; + } else if (net_set_tcp_nodelay(fstream->fd, FALSE) < 0) { + /* We already successfully enabled TCP_NODELAY, so there + shouldn't really be errors. Except ECONNRESET can possibly + still happen between these two calls, so again don't log + errors. */ + fstream->no_socket_nodelay = TRUE; + } +} + +static void o_stream_file_cork(struct ostream_private *stream, bool set) +{ + struct file_ostream *fstream = + container_of(stream, struct file_ostream, ostream); + struct iostream_private *iostream = &fstream->ostream.iostream; + int ret; + + if (stream->corked != set && !stream->ostream.closed) { + if (set && fstream->io != NULL) + io_remove(&fstream->io); + else if (!set) { + /* buffer flushing might close the stream */ + ret = buffer_flush(fstream); + stream->last_errors_not_checked = TRUE; + if (fstream->io == NULL && + (ret == 0 || fstream->flush_pending) && + !stream->ostream.closed) { + fstream->io = io_add_to( + io_stream_get_ioloop(iostream), + fstream->fd, IO_WRITE, + stream_send_io, fstream); + } + } + if (stream->ostream.closed) { + /* flushing may have closed the stream already */ + return; + } + + if (fstream->socket_cork_set) { + i_assert(!set); + if (net_set_cork(fstream->fd, FALSE) < 0) + fstream->no_socket_cork = TRUE; + fstream->socket_cork_set = FALSE; + } + if (!set && !fstream->no_socket_nodelay) { + /* Uncorking - send all the pending data immediately. + Remove nodelay immediately afterwards, so if any + output is sent outside corking it may get delayed. */ + o_stream_tcp_flush_via_nodelay(fstream); + } + if (!set && !fstream->no_socket_quickack) { + /* Uncorking - disable delayed ACKs to reduce latency. + Note that this needs to be set repeatedly. */ + if (net_set_tcp_quickack(fstream->fd, TRUE) < 0) + fstream->no_socket_quickack = TRUE; + } + stream->corked = set; + } +} + +static int o_stream_file_flush(struct ostream_private *stream) +{ + struct file_ostream *fstream = + container_of(stream, struct file_ostream, ostream); + + return buffer_flush(fstream); +} + +static void +o_stream_file_flush_pending(struct ostream_private *stream, bool set) +{ + struct file_ostream *fstream = + container_of(stream, struct file_ostream, ostream); + struct iostream_private *iostream = &fstream->ostream.iostream; + + fstream->flush_pending = set; + if (set && !stream->corked && fstream->io == NULL) { + fstream->io = io_add_to(io_stream_get_ioloop(iostream), + fstream->fd, IO_WRITE, + stream_send_io, fstream); + } +} + +static size_t get_unused_space(const struct file_ostream *fstream) +{ + if (fstream->head > fstream->tail) { + /* XXXT...HXXX */ + return fstream->head - fstream->tail; + } else if (fstream->head < fstream->tail) { + /* ...HXXXT... */ + return (fstream->buffer_size - fstream->tail) + fstream->head; + } else { + /* either fully unused or fully used */ + return fstream->full ? 0 : fstream->buffer_size; + } +} + +static size_t +o_stream_file_get_buffer_used_size(const struct ostream_private *stream) +{ + const struct file_ostream *fstream = + container_of(stream, const struct file_ostream, ostream); + + return fstream->buffer_size - get_unused_space(fstream); +} + +static int o_stream_file_seek(struct ostream_private *stream, uoff_t offset) +{ + struct file_ostream *fstream = + container_of(stream, struct file_ostream, ostream); + + if (offset > OFF_T_MAX) { + stream->ostream.stream_errno = EINVAL; + return -1; + } + if (!fstream->file) { + stream->ostream.stream_errno = ESPIPE; + return -1; + } + + if (buffer_flush(fstream) < 0) + return -1; + + stream->ostream.offset = offset; + fstream->buffer_offset = offset; + return 1; +} + +static void o_stream_grow_buffer(struct file_ostream *fstream, size_t bytes) +{ + size_t size, new_size, end_size; + + size = nearest_power(fstream->buffer_size + bytes); + if (size > fstream->ostream.max_buffer_size) { + /* limit the size */ + size = fstream->ostream.max_buffer_size; + } else if (fstream->ostream.corked) { + /* try to use optimal buffer size with corking */ + new_size = I_MIN(fstream->optimal_block_size, + fstream->ostream.max_buffer_size); + if (new_size > size) + size = new_size; + } + + if (size <= fstream->buffer_size) + return; + + fstream->buffer = i_realloc(fstream->buffer, + fstream->buffer_size, size); + + if (fstream->tail <= fstream->head && !IS_STREAM_EMPTY(fstream)) { + /* move head forward to end of buffer */ + end_size = fstream->buffer_size - fstream->head; + memmove(fstream->buffer + size - end_size, + fstream->buffer + fstream->head, end_size); + fstream->head = size - end_size; + } + + fstream->full = FALSE; + fstream->buffer_size = size; +} + +static void stream_send_io(struct file_ostream *fstream) +{ + struct ostream *ostream = &fstream->ostream.ostream; + struct iostream_private *iostream = &fstream->ostream.iostream; + bool use_cork = !fstream->ostream.corked; + int ret; + + /* Set flush_pending = FALSE first before calling the flush callback, + and change it to TRUE only if callback returns 0. That way the + callback can call o_stream_set_flush_pending() again and we don't + forget it even if flush callback returns 1. */ + fstream->flush_pending = FALSE; + + o_stream_ref(ostream); + if (use_cork) + o_stream_cork(ostream); + if (fstream->ostream.callback != NULL) + ret = fstream->ostream.callback(fstream->ostream.context); + else + ret = o_stream_file_flush(&fstream->ostream); + if (use_cork) + o_stream_uncork(ostream); + + if (ret == 0) + fstream->flush_pending = TRUE; + + if (!fstream->flush_pending && IS_STREAM_EMPTY(fstream)) { + io_remove(&fstream->io); + } else if (!fstream->ostream.ostream.closed) { + /* Add the IO handler if it's not there already. Callback + might have just returned 0 without there being any data + to be sent. */ + if (fstream->io == NULL) { + fstream->io = io_add_to(io_stream_get_ioloop(iostream), + fstream->fd, IO_WRITE, + stream_send_io, fstream); + } + } + + o_stream_unref(&ostream); +} + +static size_t o_stream_add(struct file_ostream *fstream, + const void *data, size_t size) +{ + struct iostream_private *iostream = &fstream->ostream.iostream; + size_t unused, sent; + int i; + + unused = get_unused_space(fstream); + if (unused < size) + o_stream_grow_buffer(fstream, size-unused); + + sent = 0; + for (i = 0; i < 2 && sent < size && !fstream->full; i++) { + unused = fstream->tail >= fstream->head ? + fstream->buffer_size - fstream->tail : + fstream->head - fstream->tail; + + if (unused > size-sent) + unused = size-sent; + memcpy(fstream->buffer + fstream->tail, + CONST_PTR_OFFSET(data, sent), unused); + sent += unused; + + fstream->tail += unused; + if (fstream->tail == fstream->buffer_size) + fstream->tail = 0; + + if (fstream->head == fstream->tail && + fstream->buffer_size > 0) + fstream->full = TRUE; + } + + if (sent != 0 && fstream->io == NULL && + !fstream->ostream.corked && !fstream->file) { + fstream->io = io_add_to(io_stream_get_ioloop(iostream), + fstream->fd, IO_WRITE, stream_send_io, + fstream); + } + + return sent; +} + +ssize_t o_stream_file_sendv(struct ostream_private *stream, + const struct const_iovec *iov, + unsigned int iov_count) +{ + struct file_ostream *fstream = + container_of(stream, struct file_ostream, ostream); + size_t size, total_size, added, optimal_size; + unsigned int i; + ssize_t ret = 0; + + for (i = 0, size = 0; i < iov_count; i++) + size += iov[i].iov_len; + total_size = size; + + if (size > get_unused_space(fstream) && !IS_STREAM_EMPTY(fstream)) { + if (o_stream_file_flush(stream) < 0) + return -1; + } + + optimal_size = I_MIN(fstream->optimal_block_size, + fstream->ostream.max_buffer_size); + if (IS_STREAM_EMPTY(fstream) && + (!stream->corked || size >= optimal_size)) { + /* send immediately */ + ret = o_stream_file_writev_full(fstream, iov, iov_count); + if (ret < 0) + return -1; + + size = ret; + while (size > 0 && iov_count > 0 && size >= iov[0].iov_len) { + size -= iov[0].iov_len; + iov++; + iov_count--; + } + + if (iov_count == 0) + i_assert(size == 0); + else { + added = o_stream_add(fstream, + CONST_PTR_OFFSET(iov[0].iov_base, size), + iov[0].iov_len - size); + ret += added; + + if (added != iov[0].iov_len - size) { + /* buffer full */ + stream->ostream.offset += ret; + return ret; + } + + iov++; + iov_count--; + } + } + + /* buffer it, at least partly */ + for (i = 0; i < iov_count; i++) { + added = o_stream_add(fstream, iov[i].iov_base, iov[i].iov_len); + ret += added; + if (added != iov[i].iov_len) + break; + } + stream->ostream.offset += ret; + i_assert((size_t)ret <= total_size); + i_assert((size_t)ret == total_size || !fstream->file); + return ret; +} + +static size_t +o_stream_file_update_buffer(struct file_ostream *fstream, + const void *data, size_t size, size_t pos) +{ + size_t avail, copy_size; + + if (fstream->head < fstream->tail) { + /* ...HXXXT... */ + i_assert(pos < fstream->tail); + avail = fstream->tail - pos; + } else { + /* XXXT...HXXX */ + avail = fstream->buffer_size - pos; + } + copy_size = I_MIN(size, avail); + memcpy(fstream->buffer + pos, data, copy_size); + data = CONST_PTR_OFFSET(data, copy_size); + size -= copy_size; + + if (size > 0 && fstream->head >= fstream->tail) { + /* wraps to beginning of the buffer */ + copy_size = I_MIN(size, fstream->tail); + memcpy(fstream->buffer, data, copy_size); + size -= copy_size; + } + return size; +} + +static int +o_stream_file_write_at(struct ostream_private *stream, + const void *data, size_t size, uoff_t offset) +{ + struct file_ostream *fstream = + container_of(stream, struct file_ostream, ostream); + size_t used, pos, skip, left; + + /* update buffer if the write overlaps it */ + used = file_buffer_get_used_size(fstream); + if (used > 0 && + fstream->buffer_offset < offset + size && + fstream->buffer_offset + used > offset) { + if (fstream->buffer_offset <= offset) { + /* updating from the beginning */ + skip = 0; + } else { + skip = fstream->buffer_offset - offset; + } + pos = (fstream->head + offset + skip - fstream->buffer_offset) % + fstream->buffer_size; + left = o_stream_file_update_buffer(fstream, + CONST_PTR_OFFSET(data, skip), size - skip, pos); + if (left > 0) { + /* didn't write all of it */ + if (skip > 0) { + /* we also have to write a prefix. don't + bother with two syscalls, just write all + of it in one pwrite(). */ + } else { + /* write only the suffix */ + size_t update_count = size - left; + + data = CONST_PTR_OFFSET(data, update_count); + size -= update_count; + offset += update_count; + } + } else if (skip == 0) { + /* everything done */ + return 0; + } else { + /* still have to write prefix */ + size = skip; + } + } + + /* we couldn't write everything to the buffer. flush the buffer + and pwrite() the rest. */ + if (o_stream_file_flush(stream) < 0) + return -1; + + if (pwrite_full(fstream->fd, data, size, offset) < 0) { + stream->ostream.stream_errno = errno; + stream_closed(fstream); + return -1; + } + return 0; +} + +static bool +io_stream_sendfile(struct ostream_private *outstream, + struct istream *instream, int in_fd, + enum ostream_send_istream_result *res_r) +{ + struct file_ostream *foutstream = + container_of(outstream, struct file_ostream, ostream); + uoff_t in_size, offset, send_size, v_offset, abs_start_offset; + ssize_t ret; + bool sendfile_not_supported = FALSE; + + if ((ret = i_stream_get_size(instream, TRUE, &in_size)) < 0) { + *res_r = OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT; + return TRUE; + } + if (ret == 0) { + /* size unknown. we can't use sendfile(). */ + return FALSE; + } + + o_stream_socket_cork(foutstream); + + /* flush out any data in buffer */ + if ((ret = buffer_flush(foutstream)) < 0) { + *res_r = OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT; + return TRUE; + } else if (ret == 0) { + *res_r = OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT; + return TRUE; + } + + if (o_stream_lseek(foutstream) < 0) { + *res_r = OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT; + return TRUE; + } + + v_offset = instream->v_offset; + abs_start_offset = i_stream_get_absolute_offset(instream) - v_offset; + while (v_offset < in_size) { + offset = abs_start_offset + v_offset; + send_size = in_size - v_offset; + + ret = safe_sendfile(foutstream->fd, in_fd, &offset, + MAX_SSIZE_T(send_size)); + if (ret <= 0) { + if (ret == 0) { + /* Unexpectedly early EOF at input */ + i_stream_seek(instream, v_offset); + instream->eof = TRUE; + *res_r = OSTREAM_SEND_ISTREAM_RESULT_FINISHED; + return TRUE; + } + if (foutstream->file) { + if (errno == EINTR) { + /* automatically retry */ + continue; + } + } else { + if (errno == EINTR || errno == EAGAIN) { + ret = 0; + break; + } + } + if (errno == EINVAL) + sendfile_not_supported = TRUE; + else { + io_stream_set_error(&outstream->iostream, + "sendfile() failed: %m"); + outstream->ostream.stream_errno = errno; + /* close only if error wasn't because + sendfile() isn't supported */ + stream_closed(foutstream); + } + break; + } + + v_offset += ret; + foutstream->real_offset += ret; + foutstream->buffer_offset += ret; + outstream->ostream.offset += ret; + } + + i_stream_seek(instream, v_offset); + if (v_offset == in_size) { + instream->eof = TRUE; + *res_r = OSTREAM_SEND_ISTREAM_RESULT_FINISHED; + return TRUE; + } + i_assert(ret <= 0); + if (sendfile_not_supported) + return FALSE; + if (ret < 0) + *res_r = OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT; + else + *res_r = OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT; + return TRUE; +} + +static enum ostream_send_istream_result +io_stream_copy_backwards(struct ostream_private *outstream, + struct istream *instream, uoff_t in_size) +{ + struct file_ostream *foutstream = + container_of(outstream, struct file_ostream, ostream); + uoff_t in_start_offset, in_offset, in_limit, out_offset; + const unsigned char *data; + size_t buffer_size, size, read_size; + ssize_t ret; + + i_assert(IS_STREAM_EMPTY(foutstream)); + + /* figure out optimal buffer size */ + buffer_size = instream->real_stream->buffer_size; + if (buffer_size == 0 || buffer_size > foutstream->buffer_size) { + if (foutstream->optimal_block_size > foutstream->buffer_size) { + o_stream_grow_buffer(foutstream, + foutstream->optimal_block_size - + foutstream->buffer_size); + } + + buffer_size = foutstream->buffer_size; + } + + in_start_offset = instream->v_offset; + in_offset = in_limit = in_size; + out_offset = outstream->ostream.offset + (in_offset - in_start_offset); + + while (in_offset > in_start_offset) { + if (in_offset - in_start_offset <= buffer_size) + read_size = in_offset - in_start_offset; + else + read_size = buffer_size; + in_offset -= read_size; + out_offset -= read_size; + + for (;;) { + i_assert(in_offset <= in_limit); + + i_stream_seek(instream, in_offset); + read_size = in_limit - in_offset; + + /* FIXME: something's wrong here */ + if (i_stream_read_bytes(instream, &data, &size, + read_size) == 0) + i_unreached(); + if (size >= read_size) { + size = read_size; + if (instream->mmaped) { + /* we'll have to write it through + buffer or the file gets corrupted */ + i_assert(size <= + foutstream->buffer_size); + memcpy(foutstream->buffer, data, size); + data = foutstream->buffer; + } + break; + } + + /* buffer too large probably, try with smaller */ + read_size -= size; + in_offset += read_size; + out_offset += read_size; + buffer_size -= read_size; + } + in_limit -= size; + + ret = pwrite_full(foutstream->fd, data, size, out_offset); + if (ret < 0) { + /* error */ + outstream->ostream.stream_errno = errno; + return OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT; + } + i_stream_skip(instream, size); + } + /* make it visible that we're at instream's EOF */ + i_stream_seek(instream, in_size); + instream->eof = TRUE; + + outstream->ostream.offset += in_size - in_start_offset; + return OSTREAM_SEND_ISTREAM_RESULT_FINISHED; +} + +static enum ostream_send_istream_result +io_stream_copy_same_stream(struct ostream_private *outstream, + struct istream *instream) +{ + uoff_t in_size; + off_t in_abs_offset, ret = 0; + + /* copying data within same fd. we'll have to be careful with + seeks and overlapping writes. */ + if ((ret = i_stream_get_size(instream, TRUE, &in_size)) < 0) + return OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT; + if (ret == 0) { + /* if we couldn't find out the size, it means that instream + isn't a regular file_istream. we can be reasonably sure that + we can copy it safely the regular way. (there's really no + other possibility, other than failing completely.) */ + return io_stream_copy(&outstream->ostream, instream); + } + i_assert(instream->v_offset <= in_size); + + in_abs_offset = i_stream_get_absolute_offset(instream); + ret = (off_t)outstream->ostream.offset - in_abs_offset; + if (ret == 0) { + /* copying data over itself. we don't really + need to do that, just fake it. */ + return OSTREAM_SEND_ISTREAM_RESULT_FINISHED; + } + if (ret > 0 && in_size > (uoff_t)ret) { + /* overlapping */ + i_assert(instream->seekable); + return io_stream_copy_backwards(outstream, instream, in_size); + } else { + /* non-overlapping */ + return io_stream_copy(&outstream->ostream, instream); + } +} + +static enum ostream_send_istream_result +o_stream_file_send_istream(struct ostream_private *outstream, + struct istream *instream) +{ + struct file_ostream *foutstream = + container_of(outstream, struct file_ostream, ostream); + bool same_stream; + int in_fd; + enum ostream_send_istream_result res; + + in_fd = !instream->readable_fd ? -1 : i_stream_get_fd(instream); + if (!foutstream->no_sendfile && in_fd != -1 && + in_fd != foutstream->fd && instream->seekable) { + if (io_stream_sendfile(outstream, instream, in_fd, &res)) + return res; + + /* sendfile() not supported (with this fd), fallback to + regular sending. */ + foutstream->no_sendfile = TRUE; + } + + same_stream = i_stream_get_fd(instream) == foutstream->fd && + foutstream->fd != -1; + if (!same_stream) + return io_stream_copy(&outstream->ostream, instream); + return io_stream_copy_same_stream(outstream, instream); +} + +static void o_stream_file_switch_ioloop_to(struct ostream_private *stream, + struct ioloop *ioloop) +{ + struct file_ostream *fstream = + container_of(stream, struct file_ostream, ostream); + + if (fstream->io != NULL) + fstream->io = io_loop_move_io_to(ioloop, &fstream->io); +} + +struct ostream * +o_stream_create_file_common(struct file_ostream *fstream, + int fd, size_t max_buffer_size, bool autoclose_fd) +{ + struct ostream *ostream; + + fstream->fd = fd; + fstream->autoclose_fd = autoclose_fd; + fstream->optimal_block_size = DEFAULT_OPTIMAL_BLOCK_SIZE; + + fstream->ostream.iostream.close = o_stream_file_close; + fstream->ostream.iostream.destroy = o_stream_file_destroy; + + fstream->ostream.cork = o_stream_file_cork; + fstream->ostream.flush = o_stream_file_flush; + fstream->ostream.flush_pending = o_stream_file_flush_pending; + fstream->ostream.get_buffer_used_size = + o_stream_file_get_buffer_used_size; + fstream->ostream.seek = o_stream_file_seek; + fstream->ostream.sendv = o_stream_file_sendv; + fstream->ostream.write_at = o_stream_file_write_at; + fstream->ostream.send_istream = o_stream_file_send_istream; + fstream->ostream.switch_ioloop_to = o_stream_file_switch_ioloop_to; + + fstream->writev = o_stream_file_writev; + + fstream->ostream.max_buffer_size = max_buffer_size; + ostream = o_stream_create(&fstream->ostream, NULL, fd); + + if (max_buffer_size == 0) + fstream->ostream.max_buffer_size = fstream->optimal_block_size; + + return ostream; +} + +static void fstream_init_file(struct file_ostream *fstream) +{ + struct stat st; + + fstream->no_sendfile = TRUE; + if (fstat(fstream->fd, &st) < 0) + return; + + if ((uoff_t)st.st_blksize > fstream->optimal_block_size) { + /* use the optimal block size, but with a reasonable limit */ + fstream->optimal_block_size = + I_MIN(st.st_blksize, MAX_OPTIMAL_BLOCK_SIZE); + } + + if (S_ISREG(st.st_mode)) { + fstream->no_socket_cork = TRUE; + fstream->no_socket_nodelay = TRUE; + fstream->no_socket_quickack = TRUE; + fstream->file = TRUE; + } +} + +static +struct ostream * o_stream_create_fd_common(int fd, size_t max_buffer_size, + bool autoclose_fd) +{ + struct file_ostream *fstream; + struct ostream *ostream; + off_t offset; + + fstream = i_new(struct file_ostream, 1); + ostream = o_stream_create_file_common + (fstream, fd, max_buffer_size, autoclose_fd); + + offset = lseek(fd, 0, SEEK_CUR); + if (offset >= 0) { + ostream->offset = offset; + fstream->real_offset = offset; + fstream->buffer_offset = offset; + fstream_init_file(fstream); + } else { + struct ip_addr local_ip; + + if (net_getsockname(fd, &local_ip, NULL) < 0) { + /* not a socket */ + fstream->no_sendfile = TRUE; + fstream->no_socket_cork = TRUE; + fstream->no_socket_nodelay = TRUE; + fstream->no_socket_quickack = TRUE; + } else if (local_ip.family == 0) { + /* UNIX domain socket */ + fstream->no_socket_cork = TRUE; + fstream->no_socket_nodelay = TRUE; + fstream->no_socket_quickack = TRUE; + } + } + + return ostream; +} + +struct ostream * +o_stream_create_fd(int fd, size_t max_buffer_size) +{ + return o_stream_create_fd_common(fd, max_buffer_size, FALSE); +} + +struct ostream * +o_stream_create_fd_autoclose(int *fd, size_t max_buffer_size) +{ + struct ostream *ostream = o_stream_create_fd_common(*fd, + max_buffer_size, TRUE); + *fd = -1; + return ostream; +} + +struct ostream * +o_stream_create_fd_file(int fd, uoff_t offset, bool autoclose_fd) +{ + struct file_ostream *fstream; + struct ostream *ostream; + + if (offset == UOFF_T_MAX) + offset = lseek(fd, 0, SEEK_CUR); + + fstream = i_new(struct file_ostream, 1); + ostream = o_stream_create_file_common(fstream, fd, 0, autoclose_fd); + fstream_init_file(fstream); + fstream->real_offset = offset; + fstream->buffer_offset = offset; + ostream->blocking = fstream->file; + ostream->offset = offset; + return ostream; +} + +struct ostream *o_stream_create_fd_file_autoclose(int *fd, uoff_t offset) +{ + struct ostream *output; + + output = o_stream_create_fd_file(*fd, offset, TRUE); + *fd = -1; + return output; +} + +struct ostream *o_stream_create_file(const char *path, uoff_t offset, mode_t mode, + enum ostream_create_file_flags flags) +{ + int fd; + int open_flags = O_WRONLY|O_CREAT; + if (HAS_ANY_BITS(flags, OSTREAM_CREATE_FILE_FLAG_APPEND)) + open_flags |= O_APPEND; + else + open_flags |= O_TRUNC; + if ((fd = open(path, open_flags, mode)) < 0) + return o_stream_create_error(errno); + return o_stream_create_fd_file_autoclose(&fd, offset); +} diff --git a/src/lib/ostream-hash.c b/src/lib/ostream-hash.c new file mode 100644 index 0000000..c83b43e --- /dev/null +++ b/src/lib/ostream-hash.c @@ -0,0 +1,56 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "hash-method.h" +#include "ostream-private.h" +#include "ostream-hash.h" + +struct hash_ostream { + struct ostream_private ostream; + const struct hash_method *method; + void *hash_context; +}; + +static ssize_t +o_stream_hash_sendv(struct ostream_private *stream, + const struct const_iovec *iov, unsigned int iov_count) +{ + struct hash_ostream *hstream = + container_of(stream, struct hash_ostream, ostream); + unsigned int i; + size_t bytes_left, block_len; + ssize_t ret; + + if ((ret = o_stream_sendv(stream->parent, iov, iov_count)) < 0) { + o_stream_copy_error_from_parent(stream); + return -1; + } + if (ret > 0) { + bytes_left = ret; + for (i = 0; i < iov_count && bytes_left > 0; i++) { + block_len = iov[i].iov_len <= bytes_left ? + iov[i].iov_len : bytes_left; + hstream->method->loop(hstream->hash_context, + iov[i].iov_base, block_len); + bytes_left -= block_len; + } + } + + stream->ostream.offset += ret; + return ret; +} + +struct ostream * +o_stream_create_hash(struct ostream *output, const struct hash_method *method, + void *hash_context) +{ + struct hash_ostream *hstream; + + hstream = i_new(struct hash_ostream, 1); + hstream->ostream.sendv = o_stream_hash_sendv; + hstream->method = method; + hstream->hash_context = hash_context; + + return o_stream_create(&hstream->ostream, output, + o_stream_get_fd(output)); +} diff --git a/src/lib/ostream-hash.h b/src/lib/ostream-hash.h new file mode 100644 index 0000000..7fb4b6d --- /dev/null +++ b/src/lib/ostream-hash.h @@ -0,0 +1,12 @@ +#ifndef OSTREAM_HASH_H +#define OSTREAM_HASH_H + +struct hash_method; + +/* hash_context must be allocated and initialized by caller. This ostream will + simply call method->loop() for all the data going through the ostream. */ +struct ostream * +o_stream_create_hash(struct ostream *output, const struct hash_method *method, + void *hash_context); + +#endif diff --git a/src/lib/ostream-multiplex.c b/src/lib/ostream-multiplex.c new file mode 100644 index 0000000..6c3bf85 --- /dev/null +++ b/src/lib/ostream-multiplex.c @@ -0,0 +1,367 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "array.h" +#include "ostream-private.h" +#include "ostream-multiplex.h" + +/* all multiplex packets are [1 byte cid][4 byte length][data] */ + +struct multiplex_ostream; + +struct multiplex_ochannel { + struct ostream_private ostream; + struct multiplex_ostream *mstream; + uint8_t cid; + buffer_t *buf; + uint64_t last_sent_counter; + bool closed:1; + bool corked:1; +}; + +struct multiplex_ostream { + struct ostream *parent; + + stream_flush_callback_t *old_flush_callback; + void *old_flush_context; + + /* channel 0 is main channel */ + uint8_t cur_channel; + unsigned int remain; + size_t bufsize; + uint64_t send_counter; + ARRAY(struct multiplex_ochannel *) channels; + + bool destroyed:1; +}; + +static struct multiplex_ochannel * +get_channel(struct multiplex_ostream *mstream, uint8_t cid) +{ + struct multiplex_ochannel *channel; + i_assert(mstream != NULL); + array_foreach_elem(&mstream->channels, channel) { + if (channel != NULL && channel->cid == cid) + return channel; + } + return NULL; +} + +static void propagate_error(struct multiplex_ostream *mstream, int stream_errno) +{ + struct multiplex_ochannel *channel; + array_foreach_elem(&mstream->channels, channel) + if (channel != NULL) + channel->ostream.ostream.stream_errno = stream_errno; +} + +static struct multiplex_ochannel *get_next_channel(struct multiplex_ostream *mstream) +{ + struct multiplex_ochannel *oldest_channel = NULL; + struct multiplex_ochannel *channel; + uint64_t last_counter = mstream->send_counter; + + array_foreach_elem(&mstream->channels, channel) { + if (channel != NULL && + channel->last_sent_counter <= last_counter && + channel->buf->used > 0) { + last_counter = channel->last_sent_counter; + oldest_channel = channel; + } + } + return oldest_channel; +} + +static bool +o_stream_multiplex_sendv(struct multiplex_ostream *mstream) +{ + struct multiplex_ochannel *channel; + ssize_t ret = 0; + bool all_sent = TRUE; + + while((channel = get_next_channel(mstream)) != NULL) { + if (channel->buf->used == 0) + continue; + if (o_stream_get_buffer_avail_size(mstream->parent) < 6) { + all_sent = FALSE; + break; + } + /* check parent stream capacity */ + size_t tmp = o_stream_get_buffer_avail_size(mstream->parent) - 5; + /* ensure it fits into 32 bit int */ + size_t amt = I_MIN(UINT_MAX, I_MIN(tmp, channel->buf->used)); + /* ensure amt fits */ + if (tmp == 0) + break; + /* delay corking here now that we are going to send something */ + if (!o_stream_is_corked(mstream->parent)) + o_stream_cork(mstream->parent); + uint32_t len = cpu32_to_be(amt); + const struct const_iovec vec[] = { + { &channel->cid, 1 }, + { &len, 4 }, + { channel->buf->data, amt } + }; + if ((ret = o_stream_sendv(mstream->parent, vec, N_ELEMENTS(vec))) < 0) { + propagate_error(mstream, mstream->parent->stream_errno); + break; + } + i_assert((size_t)ret == 1 + 4 + amt); + buffer_delete(channel->buf, 0, amt); + channel->last_sent_counter = ++mstream->send_counter; + } + if (o_stream_is_corked(mstream->parent)) + o_stream_uncork(mstream->parent); + return all_sent; +} + +static int o_stream_multiplex_flush(struct multiplex_ostream *mstream) +{ + int ret = o_stream_flush(mstream->parent); + if (ret >= 0) { + if (!o_stream_multiplex_sendv(mstream)) + return 0; + } + + /* a) Everything is flushed. See if one of the callbacks' flush + callbacks wants to write more data. + b) ostream failed. Notify the callbacks in case they need to know. */ + struct multiplex_ochannel *channel; + bool unfinished = FALSE; + bool failed = FALSE; + array_foreach_elem(&mstream->channels, channel) { + if (channel != NULL && channel->ostream.callback != NULL) { + ret = channel->ostream.callback(channel->ostream.context); + if (ret < 0) + failed = TRUE; + else if (ret == 0) + unfinished = TRUE; + } + } + return failed ? -1 : + (unfinished ? 0 : 1); +} + +static int o_stream_multiplex_ochannel_flush(struct ostream_private *stream) +{ + ssize_t ret; + struct multiplex_ochannel *channel = + container_of(stream, struct multiplex_ochannel, ostream); + struct multiplex_ostream *mstream = channel->mstream; + + /* flush parent stream always, so there is room for more. */ + if ((ret = o_stream_flush(mstream->parent)) <= 0) { + if (ret == -1) + propagate_error(mstream, mstream->parent->stream_errno); + return ret; + } + + /* send all channels */ + o_stream_multiplex_sendv(mstream); + + if (channel->buf->used > 0) + return 0; + return 1; +} + +static void o_stream_multiplex_ochannel_cork(struct ostream_private *stream, bool set) +{ + struct multiplex_ochannel *channel = + container_of(stream, struct multiplex_ochannel, ostream); + if (channel->corked != set && !set) { + /* flush */ + (void)o_stream_multiplex_ochannel_flush(stream); + } + channel->corked = set; +} + +static ssize_t +o_stream_multiplex_ochannel_sendv(struct ostream_private *stream, + const struct const_iovec *iov, unsigned int iov_count) +{ + struct multiplex_ochannel *channel = + container_of(stream, struct multiplex_ochannel, ostream); + size_t total = 0, avail = o_stream_get_buffer_avail_size(&stream->ostream); + size_t optimal_size = I_MIN(IO_BLOCK_SIZE, avail); + + for (unsigned int i = 0; i < iov_count; i++) + total += iov[i].iov_len; + + if (avail < total) { + o_stream_multiplex_sendv(channel->mstream); + avail = o_stream_get_buffer_avail_size(&stream->ostream); + if (avail == 0) + return 0; + } + + total = 0; + + for (unsigned int i = 0; i < iov_count; i++) { + /* copy data to buffer */ + size_t tmp = avail - total; + if (tmp == 0) + break; + buffer_append(channel->buf, iov[i].iov_base, + I_MIN(tmp, iov[i].iov_len)); + total += I_MIN(tmp, iov[i].iov_len); + } + + stream->ostream.offset += total; + + /* will send later */ + if (channel->corked && channel->buf->used < optimal_size) + return total; + + o_stream_multiplex_sendv(channel->mstream); + return total; +} + +static void +o_stream_multiplex_ochannel_set_flush_callback(struct ostream_private *stream, + stream_flush_callback_t *callback, + void *context) +{ + /* We have overwritten our parent's flush-callback. Don't change it. */ + stream->callback = callback; + stream->context = context; +} + +static size_t +o_stream_multiplex_ochannel_get_buffer_used_size(const struct ostream_private *stream) +{ + const struct multiplex_ochannel *channel = + container_of(stream, const struct multiplex_ochannel, ostream); + + return channel->buf->used + + o_stream_get_buffer_used_size(channel->mstream->parent); +} + +static size_t +o_stream_multiplex_ochannel_get_buffer_avail_size(const struct ostream_private *stream) +{ + const struct multiplex_ochannel *channel = + container_of(stream, const struct multiplex_ochannel, ostream); + size_t max_avail = I_MIN(channel->mstream->bufsize, + o_stream_get_buffer_avail_size(stream->parent)); + + /* There is 5-byte overhead per message, so take that into account */ + return max_avail <= (channel->buf->used + 5) ? 0 : + max_avail - (channel->buf->used + 5); +} + +static void +o_stream_multiplex_ochannel_close(struct iostream_private *stream, bool close_parent) +{ + struct multiplex_ochannel *arr_channel; + struct multiplex_ochannel *channel = + container_of(stream, struct multiplex_ochannel, ostream.iostream); + + channel->closed = TRUE; + if (close_parent) { + array_foreach_elem(&channel->mstream->channels, arr_channel) + if (arr_channel != NULL && !arr_channel->closed) + return; + o_stream_close(channel->mstream->parent); + } +} + +static void o_stream_multiplex_try_destroy(struct multiplex_ostream *mstream) +{ + struct multiplex_ochannel *channel; + /* can't do anything until they are all closed */ + array_foreach_elem(&mstream->channels, channel) + if (channel != NULL) + return; + + i_assert(mstream->parent->real_stream->callback == + (stream_flush_callback_t *)o_stream_multiplex_flush); + o_stream_set_flush_callback(mstream->parent, + *mstream->old_flush_callback, + mstream->old_flush_context); + o_stream_unref(&mstream->parent); + array_free(&mstream->channels); + i_free(mstream); +} + +static void o_stream_multiplex_ochannel_destroy(struct iostream_private *stream) +{ + struct multiplex_ochannel **channelp; + struct multiplex_ochannel *channel = + container_of(stream, struct multiplex_ochannel, ostream.iostream); + o_stream_unref(&channel->ostream.parent); + if (channel->buf != NULL) + buffer_free(&channel->buf); + /* delete the channel */ + array_foreach_modifiable(&channel->mstream->channels, channelp) { + if (*channelp != NULL && (*channelp)->cid == channel->cid) { + *channelp = NULL; + break; + } + } + o_stream_multiplex_try_destroy(channel->mstream); +} + +static struct ostream * +o_stream_add_channel_real(struct multiplex_ostream *mstream, uint8_t cid) +{ + struct multiplex_ochannel *channel = i_new(struct multiplex_ochannel, 1); + channel->cid = cid; + channel->buf = buffer_create_dynamic(default_pool, 256); + channel->mstream = mstream; + channel->ostream.cork = o_stream_multiplex_ochannel_cork; + channel->ostream.flush = o_stream_multiplex_ochannel_flush; + channel->ostream.sendv = o_stream_multiplex_ochannel_sendv; + channel->ostream.set_flush_callback = + o_stream_multiplex_ochannel_set_flush_callback; + channel->ostream.get_buffer_used_size = + o_stream_multiplex_ochannel_get_buffer_used_size; + channel->ostream.get_buffer_avail_size = + o_stream_multiplex_ochannel_get_buffer_avail_size; + channel->ostream.iostream.close = o_stream_multiplex_ochannel_close; + channel->ostream.iostream.destroy = o_stream_multiplex_ochannel_destroy; + channel->ostream.fd = o_stream_get_fd(mstream->parent); + array_push_back(&channel->mstream->channels, &channel); + + (void)o_stream_create(&channel->ostream, mstream->parent, -1); + /* o_stream_create() defaults the flush_callback to parent's callback. + Here it points to o_stream_multiplex_flush(), which just causes + infinite looping. */ + channel->ostream.callback = NULL; + channel->ostream.context = NULL; + return &channel->ostream.ostream; +} + +struct ostream *o_stream_multiplex_add_channel(struct ostream *stream, uint8_t cid) +{ + struct multiplex_ochannel *chan = + container_of(stream->real_stream, struct multiplex_ochannel, + ostream); + i_assert(get_channel(chan->mstream, cid) == NULL); + + return o_stream_add_channel_real(chan->mstream, cid); +} + +struct ostream *o_stream_create_multiplex(struct ostream *parent, size_t bufsize) +{ + struct multiplex_ostream *mstream; + + mstream = i_new(struct multiplex_ostream, 1); + mstream->parent = parent; + mstream->bufsize = bufsize; + mstream->old_flush_callback = parent->real_stream->callback; + mstream->old_flush_context = parent->real_stream->context; + o_stream_set_flush_callback(parent, o_stream_multiplex_flush, mstream); + i_array_init(&mstream->channels, 8); + o_stream_ref(parent); + + return o_stream_add_channel_real(mstream, 0); +} + +uint8_t o_stream_multiplex_get_channel_id(struct ostream *stream) +{ + struct multiplex_ochannel *channel = + container_of(stream->real_stream, struct multiplex_ochannel, + ostream); + return channel->cid; +} diff --git a/src/lib/ostream-multiplex.h b/src/lib/ostream-multiplex.h new file mode 100644 index 0000000..2c7cdcd --- /dev/null +++ b/src/lib/ostream-multiplex.h @@ -0,0 +1,8 @@ +#ifndef OSTREAM_MULTIPLEX +#define OSTREAM_MULTIPLEX 1 + +struct ostream *o_stream_create_multiplex(struct ostream *parent, size_t bufsize); +struct ostream *o_stream_multiplex_add_channel(struct ostream *stream, uint8_t cid); +uint8_t o_stream_multiplex_get_channel_id(struct ostream *stream); + +#endif diff --git a/src/lib/ostream-null.c b/src/lib/ostream-null.c new file mode 100644 index 0000000..0ce858b --- /dev/null +++ b/src/lib/ostream-null.c @@ -0,0 +1,33 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ostream-private.h" +#include "ostream-null.h" + +static ssize_t +o_stream_null_sendv(struct ostream_private *stream, + const struct const_iovec *iov, unsigned int iov_count) +{ + unsigned int i; + size_t ret = 0; + + for (i = 0; i < iov_count; i++) + ret += iov[i].iov_len; + stream->ostream.offset += ret; + return ret; +} + +struct ostream *o_stream_create_null(void) +{ + struct ostream_private *stream; + struct ostream *output; + + stream = i_new(struct ostream_private, 1); + stream->ostream.blocking = TRUE; + stream->sendv = o_stream_null_sendv; + + output = o_stream_create(stream, NULL, -1); + o_stream_set_no_error_handling(output, TRUE); + o_stream_set_name(output, "(/dev/null)"); + return output; +} diff --git a/src/lib/ostream-null.h b/src/lib/ostream-null.h new file mode 100644 index 0000000..7c83c80 --- /dev/null +++ b/src/lib/ostream-null.h @@ -0,0 +1,7 @@ +#ifndef OSTREAM_NULL_H +#define OSTREAM_NULL_H + +/* Create an output stream that ignores all the writes. */ +struct ostream *o_stream_create_null(void); + +#endif diff --git a/src/lib/ostream-private.h b/src/lib/ostream-private.h new file mode 100644 index 0000000..6222aa0 --- /dev/null +++ b/src/lib/ostream-private.h @@ -0,0 +1,73 @@ +#ifndef OSTREAM_PRIVATE_H +#define OSTREAM_PRIVATE_H + +#include "ostream.h" +#include "iostream-private.h" + +struct ostream_private { +/* inheritance: */ + struct iostream_private iostream; + +/* methods: */ + void (*cork)(struct ostream_private *stream, bool set); + int (*flush)(struct ostream_private *stream); + void (*set_flush_callback)(struct ostream_private *stream, + stream_flush_callback_t *callback, + void *context); + void (*flush_pending)(struct ostream_private *stream, bool set); + size_t (*get_buffer_used_size)(const struct ostream_private *stream); + size_t (*get_buffer_avail_size)(const struct ostream_private *stream); + int (*seek)(struct ostream_private *stream, uoff_t offset); + ssize_t (*sendv)(struct ostream_private *stream, + const struct const_iovec *iov, + unsigned int iov_count); + int (*write_at)(struct ostream_private *stream, + const void *data, size_t size, uoff_t offset); + enum ostream_send_istream_result + (*send_istream)(struct ostream_private *outstream, + struct istream *instream); + void (*switch_ioloop_to)(struct ostream_private *stream, + struct ioloop *ioloop); + +/* data: */ + struct ostream ostream; + size_t max_buffer_size; + + struct ostream *parent; /* for filter streams */ + + int fd; + struct timeval last_write_timeval; + + stream_flush_callback_t *callback; + void *context; + + bool corked:1; + bool finished:1; + bool closing:1; + bool last_errors_not_checked:1; + bool error_handling_disabled:1; + bool noverflow:1; + bool finish_also_parent:1; + bool finish_via_child:1; +}; + +struct ostream * +o_stream_create(struct ostream_private *_stream, struct ostream *parent, int fd) + ATTR_NULL(2); + +enum ostream_send_istream_result +io_stream_copy(struct ostream *outstream, struct istream *instream); + +void o_stream_copy_error_from_parent(struct ostream_private *_stream); +/* This should be called before sending data to parent stream. It makes sure + that the parent stream's output buffer doesn't become too large. + Returns 1 if more data can be safely added, 0 if not, -1 if error. */ +int o_stream_flush_parent_if_needed(struct ostream_private *_stream); + +/* Call this in flush() handler to flush the parent stream. It will call + either o_stream_flush() or o_stream_finish() depending on whether this + stream is already finished. If the parent fails, its error will be also + copied to this stream. */ +int o_stream_flush_parent(struct ostream_private *_stream); + +#endif diff --git a/src/lib/ostream-rawlog.c b/src/lib/ostream-rawlog.c new file mode 100644 index 0000000..8066392 --- /dev/null +++ b/src/lib/ostream-rawlog.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "iostream-rawlog-private.h" +#include "ostream-private.h" +#include "ostream-rawlog.h" + +struct rawlog_ostream { + struct ostream_private ostream; + struct rawlog_iostream riostream; +}; + +static void o_stream_rawlog_close(struct iostream_private *stream, + bool close_parent) +{ + struct rawlog_ostream *rstream = + container_of(stream, struct rawlog_ostream, ostream.iostream); + + iostream_rawlog_close(&rstream->riostream); + if (close_parent) + o_stream_close(rstream->ostream.parent); +} + +static ssize_t +o_stream_rawlog_sendv(struct ostream_private *stream, + const struct const_iovec *iov, unsigned int iov_count) +{ + struct rawlog_ostream *rstream = + container_of(stream, struct rawlog_ostream, ostream); + unsigned int i; + ssize_t ret, bytes; + + if ((ret = o_stream_sendv(stream->parent, iov, iov_count)) < 0) { + o_stream_copy_error_from_parent(stream); + return -1; + } + bytes = ret; + for (i = 0; i < iov_count && bytes > 0; i++) { + if (iov[i].iov_len < (size_t)bytes) { + iostream_rawlog_write(&rstream->riostream, + iov[i].iov_base, iov[i].iov_len); + bytes -= iov[i].iov_len; + } else { + iostream_rawlog_write(&rstream->riostream, + iov[i].iov_base, bytes); + break; + } + } + + stream->ostream.offset += ret; + return ret; +} + +struct ostream * +o_stream_create_rawlog(struct ostream *output, const char *rawlog_path, + int rawlog_fd, enum iostream_rawlog_flags flags) +{ + struct ostream *rawlog_output; + bool autoclose_fd = (flags & IOSTREAM_RAWLOG_FLAG_AUTOCLOSE) != 0; + + i_assert(rawlog_path != NULL); + i_assert(rawlog_fd != -1); + + rawlog_output = autoclose_fd ? + o_stream_create_fd_autoclose(&rawlog_fd, 0): + o_stream_create_fd(rawlog_fd, 0); + + o_stream_set_name(rawlog_output, + t_strdup_printf("rawlog(%s)", rawlog_path)); + return o_stream_create_rawlog_from_stream(output, rawlog_output, flags); +} + +struct ostream * +o_stream_create_rawlog_from_stream(struct ostream *output, + struct ostream *rawlog_output, + enum iostream_rawlog_flags flags) +{ + struct rawlog_ostream *rstream; + + rstream = i_new(struct rawlog_ostream, 1); + rstream->ostream.sendv = o_stream_rawlog_sendv; + rstream->ostream.iostream.close = o_stream_rawlog_close; + + rstream->riostream.rawlog_output = rawlog_output; + iostream_rawlog_init(&rstream->riostream, flags, FALSE); + return o_stream_create(&rstream->ostream, output, + o_stream_get_fd(output)); +} diff --git a/src/lib/ostream-rawlog.h b/src/lib/ostream-rawlog.h new file mode 100644 index 0000000..8f3e2b7 --- /dev/null +++ b/src/lib/ostream-rawlog.h @@ -0,0 +1,14 @@ +#ifndef OSTREAM_RAWLOG_H +#define OSTREAM_RAWLOG_H + +#include "iostream-rawlog.h" + +struct ostream * +o_stream_create_rawlog(struct ostream *output, const char *rawlog_path, + int rawlog_fd, enum iostream_rawlog_flags flags); +struct ostream * +o_stream_create_rawlog_from_stream(struct ostream *output, + struct ostream *rawlog_output, + enum iostream_rawlog_flags flags); + +#endif diff --git a/src/lib/ostream-unix.c b/src/lib/ostream-unix.c new file mode 100644 index 0000000..06e918f --- /dev/null +++ b/src/lib/ostream-unix.c @@ -0,0 +1,95 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "fdpass.h" +#include "ostream-file-private.h" +#include "ostream-unix.h" + +struct unix_ostream { + struct file_ostream fstream; + int write_fd; +}; + +static void +o_stream_unix_close(struct iostream_private *stream, bool close_parent) +{ + struct unix_ostream *ustream = + container_of(stream, struct unix_ostream, + fstream.ostream.iostream); + + i_close_fd(&ustream->write_fd); + o_stream_file_close(stream, close_parent); +} + +static ssize_t o_stream_unix_writev(struct file_ostream *fstream, + const struct const_iovec *iov, + unsigned int iov_count) +{ + struct unix_ostream *ustream = + container_of(fstream, struct unix_ostream, fstream); + size_t sent; + ssize_t ret; + + if (ustream->write_fd == -1) { + /* no fd */ + return o_stream_file_writev(fstream, iov, iov_count); + } + + /* send first iovec along with fd */ + if (iov_count == 0) + return 0; + i_assert(iov[0].iov_len > 0); + ret = fd_send(fstream->fd, ustream->write_fd, + iov[0].iov_base, iov[0].iov_len); + if (ret < 0) + return ret; + + /* update stream */ + sent = ret; + fstream->real_offset += sent; + + ustream->write_fd = -1; + + if (sent < iov[0].iov_len || iov_count == 1) { + /* caller will call us again to write the rest */ + return sent; + } + + /* send remaining iovecs */ + ret = o_stream_file_writev(fstream, &iov[1], iov_count-1); + if (ret < 0) + return (errno == EAGAIN || errno == EINTR ? (ssize_t)sent : ret); + sent += ret; + return sent; +} + +struct ostream *o_stream_create_unix(int fd, size_t max_buffer_size) +{ + struct unix_ostream *ustream; + struct ostream *output; + + i_assert(fd != -1); + + ustream = i_new(struct unix_ostream, 1); + ustream->write_fd = -1; + output = o_stream_create_file_common(&ustream->fstream, fd, + max_buffer_size, FALSE); + output->real_stream->iostream.close = o_stream_unix_close; + ustream->fstream.writev = o_stream_unix_writev; + + return output; +} + +bool o_stream_unix_write_fd(struct ostream *output, int fd) +{ + struct unix_ostream *ustream = + container_of(output->real_stream, struct unix_ostream, + fstream.ostream); + + i_assert(fd >= 0); + + if (ustream->write_fd >= 0) + return FALSE; + ustream->write_fd = fd; + return TRUE; +} diff --git a/src/lib/ostream-unix.h b/src/lib/ostream-unix.h new file mode 100644 index 0000000..849669b --- /dev/null +++ b/src/lib/ostream-unix.h @@ -0,0 +1,10 @@ +#ifndef OSTREAM_UNIX_H +#define OSTREAM_UNIX_H + +struct ostream *o_stream_create_unix(int fd, size_t max_buffer_size); +/* Write fd to UNIX socket along with the next outgoing data block. + Returns TRUE if fd is accepted, and FALSE if a previous fd still + needs to be sent. */ +bool o_stream_unix_write_fd(struct ostream *output, int fd); + +#endif diff --git a/src/lib/ostream-wrapper.c b/src/lib/ostream-wrapper.c new file mode 100644 index 0000000..dfd6699 --- /dev/null +++ b/src/lib/ostream-wrapper.c @@ -0,0 +1,1259 @@ +/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "ostream-private.h" + +#include "ostream-wrapper.h" + +static int wrapper_ostream_flush(struct ostream_private *stream); +static void +wrapper_ostream_switch_ioloop_to(struct ostream_private *stream, + struct ioloop *ioloop); + +/* + * Buffer + */ + +/* Determine the optimum buffer size for the wrapper stream itself. */ +static inline size_t +wrapper_ostream_optimal_size(struct wrapper_ostream *wostream) +{ + size_t optimal_size = wostream->ostream.max_buffer_size; + + if (wostream->output != NULL) { + optimal_size = I_MIN( + o_stream_get_max_buffer_size(wostream->output), + optimal_size); + } + if (optimal_size == SIZE_MAX) + optimal_size = IO_BLOCK_SIZE; + + return optimal_size; +} + +/* Return the current size of the wrapper output stream buffer. */ +static inline size_t wrapper_ostream_size(struct wrapper_ostream *wostream) +{ + buffer_t *buffer = wostream->buffer; + + if (buffer == NULL) + return 0; + return buffer->used; +} + +/* Return TRUE when the wrapper stream's internal buffer is empty. */ +static inline bool wrapper_ostream_is_empty(struct wrapper_ostream *wostream) +{ + return (wrapper_ostream_size(wostream) == 0); +} +/* Return TRUE when the wrapper stream's internal buffer is filled to the + maximum. */ +static inline bool wrapper_ostream_is_full(struct wrapper_ostream *wostream) +{ + return (wrapper_ostream_size(wostream) >= + wostream->ostream.max_buffer_size); +} +/* Return TRUE when the wrapper stream's internal buffer is filled at or beyond + the optimum. */ +static inline bool wrapper_ostream_is_filled(struct wrapper_ostream *wostream) +{ + return (wrapper_ostream_size(wostream) >= + wrapper_ostream_optimal_size(wostream)); +} + +/* + * Underlying output + */ + +/* Handle error in the underlying output stream (the parent). */ +static void +wrapper_ostream_copy_parent_error(struct wrapper_ostream *wostream) +{ + i_assert(wostream->output != NULL); + i_assert(wostream->output->stream_errno != 0); + + wostream->ostream.ostream.stream_errno = + wostream->output->stream_errno; + wostream->ostream.ostream.overflow = + wostream->output->overflow; +} + +static void +wrapper_ostream_handle_parent_error(struct wrapper_ostream *wostream) +{ + wrapper_ostream_copy_parent_error(wostream); + + if (wostream->output->closed) + o_stream_close(&wostream->ostream.ostream); + + if (wostream->output_error != NULL) + wostream->output_error(wostream); +} + +static void wrapper_ostream_closed(struct wrapper_ostream *wostream) +{ + wostream->ostream.ostream.closed = TRUE; +} + +/* Drop the underlying output. */ +static void wrapper_ostream_output_close(struct wrapper_ostream *wostream) +{ + o_stream_unref(&wostream->output); + wostream->output_finished = TRUE; + wostream->output_closed = TRUE; + wostream->output_closed_api = TRUE; +} + +/* Method calls */ + +/* Called when the implementation should start making the parent output stream + available, e.g. connect to the server (see output_start() method). + */ +static void wrapper_ostream_output_start(struct wrapper_ostream *wostream) +{ + if (wostream->output_started) + return; + wostream->output_started = TRUE; + if (wostream->output_start != NULL) + wostream->output_start(wostream); +} + +/* Returns TRUE when the output is ready for data (see output_ready() method). + */ +static bool wrapper_ostream_output_ready(struct wrapper_ostream *wostream) +{ + i_assert(wostream->output_ready != NULL); + return wostream->output_ready(wostream); +} + +/* Finish the underlying output (see output_finish() method).*/ +static int wrapper_ostream_output_finish(struct wrapper_ostream *wostream) +{ + i_assert(wostream->output_finish != NULL); + return wostream->output_finish(wostream); +} + +/* Called when the wrapper ostream does not need write to parent output stream. + (see output_halt() method). + */ +static void wrapper_ostream_output_halt(struct wrapper_ostream *wostream) +{ + if (wostream->output_closed) + return; + if (wostream->output_halt != NULL) + wostream->output_halt(wostream); +} + +/* Called when the wrapper ostream has data available for the parent output and + wants wrapper_ostream_continue() to be called when the parent stream is + writeable (see output_resume() method). */ +static void wrapper_ostream_output_resume(struct wrapper_ostream *wostream) +{ + if (wostream->output_closed) + return; + if (wostream->output_resume != NULL) + wostream->output_resume(wostream); +} + +/* Update any timeouts for the underlying (parent) output (see + output_update_timeouts() method). */ +static void +wrapper_ostream_output_update_timeouts(struct wrapper_ostream *wostream) +{ + struct ostream_private *stream = &wostream->ostream; + bool sender_blocking; + + if (wostream->output_closed) + return; + if (wostream->output_update_timeouts == NULL) + return; + + sender_blocking = (!stream->finished && + (wrapper_ostream_is_empty(wostream) || + (stream->corked && + !wrapper_ostream_is_filled(wostream)))); + wostream->output_update_timeouts(wostream, sender_blocking); +} + +/* + * Wrapper + */ + +/* Halt/resume the underlying output based on the state of the wrapper stream. + */ +static void +wrapper_ostream_output_manage(struct wrapper_ostream *wostream, bool sending) +{ + struct ostream_private *stream = &wostream->ostream; + bool must_flush, no_data; + + if (wostream->output_closed) + return; + + must_flush = (sending || stream->finished || wostream->flush_pending); + no_data = (wrapper_ostream_is_empty(wostream) || + (stream->corked && !wrapper_ostream_is_filled(wostream))); + + if (!must_flush && (no_data || stream->ostream.closed)) + wrapper_ostream_output_halt(wostream); + else { + wrapper_ostream_output_resume(wostream); + if (wostream->output != NULL && must_flush) + o_stream_set_flush_pending(wostream->output, TRUE); + } +} + +/* Handle any pending error by making it available to the application through + the output stream API. */ +static int +wrapper_ostream_handle_pending_error(struct wrapper_ostream *wostream) +{ + struct ostream_private *stream = &wostream->ostream; + + if (wostream->pending_errno != 0) { + if (wostream->pending_error != NULL) { + io_stream_set_error(&stream->iostream, + "%s", wostream->pending_error); + } + stream->ostream.stream_errno = wostream->pending_errno; + wostream->pending_errno = 0; + wostream->returned_error = TRUE; + wrapper_ostream_closed(wostream); + i_free_and_null(wostream->pending_error); + return -1; + } + return 0; +} + +/* Called when the wrapper stream is first finished using o_stream_finish(). */ +static int wrapper_ostream_finish(struct wrapper_ostream *wostream) +{ + int ret; + + if (wostream->output_closed) { + if (wrapper_ostream_handle_pending_error(wostream) < 0) + return -1; + return 1; + } + + if (!wrapper_ostream_output_ready(wostream)) { + return 0; + } + + wostream->output_finished = TRUE; + if (wostream->output != NULL) { + if (o_stream_uncork_flush(wostream->output) < 0) { + wrapper_ostream_handle_parent_error(wostream); + o_stream_unref(&wostream->output); + return -1; + } + } + + /* Finished sending payload; now also finish the underlying output. */ + ret = wrapper_ostream_output_finish(wostream); + if (ret == 0) + return ret; + if (ret < 0 && wostream->ostream.ostream.stream_errno != 0) { + wrapper_ostream_copy_parent_error(wostream); + return -1; + } + if (wrapper_ostream_handle_pending_error(wostream) < 0 || ret < 0) { + i_assert(wostream->ostream.ostream.stream_errno != 0); + return -1; + } + wrapper_ostream_output_close(wostream); + return 1; +} + +/* Wait in ioloop until underlying (parent) output can be flushed. This is + called only when the wrapper stream is blocking. */ +static int +wrapper_ostream_flush_wait(struct wrapper_ostream *wostream) +{ + struct ostream_private *stream = &wostream->ostream; + struct ioloop *ioloop, *prev_ioloop; + bool was_corked = FALSE; + + wrapper_ostream_output_manage(wostream, !wostream->flushing); + + /* Cannot be already waiting */ + i_assert(!wostream->flush_waiting); + i_assert(wostream->flush_ioloop == NULL); + + i_assert(wostream->wait_begin != NULL); + i_assert(wostream->wait_end != NULL); + + if (wostream->output != NULL && o_stream_is_corked(wostream->output)) { + /* Make sure parent is uncorked here to make sure output IO is + active. */ + if (o_stream_uncork_flush(wostream->output) < 0) { + wrapper_ostream_handle_parent_error(wostream); + return -1; + } + was_corked = TRUE; + } + + wostream->flush_ioloop = ioloop = io_loop_create(); + prev_ioloop = wostream->wait_begin(wostream, ioloop); + o_stream_switch_ioloop_to(&wostream->ostream.ostream, ioloop); + + /* Either we're waiting for network I/O or we're getting out of a + callback using timeout_add_short(0) */ + i_assert(io_loop_have_ios(ioloop) || + io_loop_have_immediate_timeouts(ioloop)); + + wostream->flush_waiting = TRUE; + do { + e_debug(wostream->event, "Waiting for output flush"); + io_loop_run(ioloop); + } while (wostream->flush_waiting); + + e_debug(wostream->event, "Can now flush output"); + + o_stream_switch_ioloop_to(&wostream->ostream.ostream, prev_ioloop); + wostream->wait_end(wostream, prev_ioloop); + io_loop_destroy(&ioloop); + wostream->flush_ioloop = NULL; + + if (stream->ostream.blocking) + wrapper_ostream_output_halt(wostream); + + if (was_corked && wostream->output != NULL) + o_stream_cork(wostream->output); + + if (wrapper_ostream_handle_pending_error(wostream) < 0) { + /* Stream already hit an error */ + return -1; + } + return 0; +} + +/* Try to flush the underlying (parent) output. */ +static int wrapper_ostream_flush_parent(struct wrapper_ostream *wostream) +{ + struct ostream *parent; + + if (wostream->output_closed) { + /* Output already dropped; nothing to flush */ + return 1; + } + if (!wrapper_ostream_output_ready(wostream)) { + /* There is no parent ostream yet */ + return 1; + } + + parent = wostream->output; + if (parent == NULL) { + /* There is no parent ostream anymore */ + i_assert(wostream->buffer == NULL || + wostream->buffer->used == 0); + return 1; + } + if (o_stream_get_buffer_used_size(parent) >= IO_BLOCK_SIZE) { + /* We already have quite a lot of data in parent stream. + unless we can flush it, don't add any more to it or we + could keep wasting memory by just increasing the buffer + size all the time. */ + if (o_stream_flush(parent) < 0) { + wrapper_ostream_handle_parent_error(wostream); + return -1; + } + if (o_stream_get_buffer_used_size(parent) >= IO_BLOCK_SIZE) + return 0; + } + + return 1; +} + +/* Try to write data to underlying (parent) output. */ +static ssize_t +wrapper_ostream_writev(struct wrapper_ostream *wostream, + const struct const_iovec *iov, unsigned int iov_count) +{ + struct ostream *parent = wostream->output; + ssize_t sent; + + i_assert(!wostream->output_closed); + i_assert(!wostream->output_finished); + + if (!wrapper_ostream_output_ready(wostream)) + return 0; + + /* Send more data to parent ostream */ + i_assert(parent != NULL); + o_stream_set_max_buffer_size(parent, IO_BLOCK_SIZE); + sent = o_stream_sendv(parent, iov, iov_count); + o_stream_set_max_buffer_size(parent, SIZE_MAX); + if (sent < 0) { + wrapper_ostream_handle_parent_error(wostream); + return -1; + } + + return sent; +} + +/* Try to write data to underlying (parent) output and implement blocking + behavior by running an ioloop. */ +static ssize_t +wrapper_ostream_writev_full(struct wrapper_ostream *wostream, + const struct const_iovec *iov, + unsigned int iov_count) +{ + struct ostream_private *stream = &wostream->ostream; + unsigned int i; + ssize_t sent, sent_total; + + if (!stream->ostream.blocking) { + /* Not blocking; send what we can */ + return wrapper_ostream_writev(wostream, iov, iov_count); + } + + /* Blocking; loop and wait until all is sent */ + + sent_total = 0; + for (;;) { + struct const_iovec niov; + size_t iov_pos; + + i_assert(iov_count > 0); + + /* Send iovec with complete entries */ + sent = wrapper_ostream_writev(wostream, iov, iov_count); + if (sent < 0) + return -1; + if (sent == 0) { + if (wrapper_ostream_flush_wait(wostream) < 0) + return -1; + i_assert(!wostream->output_closed); + continue; + } + + /* Determine what was sent */ + sent_total += sent; + iov_pos = (size_t)sent; + for (i = 0; i < iov_count && iov_pos >= iov[i].iov_len; i++) + iov_pos -= iov[i].iov_len; + if (i >= iov_count) { + /* All sent */ + i_assert(iov_pos == 0); + return sent_total; + } + + iov = &iov[i]; + iov_count -= i; + if (iov_pos == 0) { + /* Nicely sent until an iovec boundary */ + continue; + } + + /* Send partial iovec entry */ + i_zero(&niov); + niov = iov[0]; + i_assert(iov_pos < niov.iov_len); + niov.iov_base = CONST_PTR_OFFSET(niov.iov_base, iov_pos); + niov.iov_len -= iov_pos; + + while (niov.iov_len > 0) { + sent = wrapper_ostream_writev(wostream, &niov, 1); + if (sent < 0) + return sent; + if (sent == 0) { + if (wrapper_ostream_flush_wait(wostream) < 0) + return -1; + i_assert(!wostream->output_closed); + continue; + } + i_assert((size_t)sent <= niov.iov_len); + niov.iov_base = CONST_PTR_OFFSET(niov.iov_base, sent); + niov.iov_len -= sent; + sent_total += sent; + } + + if (iov_count == 1) { + i_assert(sent_total != 0); + return sent_total; + } + + /* Now sent until an iovec boundary */ + iov = &iov[1]; + iov_count--; + } + + i_unreached(); +} + +/* Try to flush wrapper stream's buffer content. */ +static int wrapper_ostream_flush_buffer(struct wrapper_ostream *wostream) +{ + struct ostream_private *stream = &wostream->ostream; + buffer_t *buffer = wostream->buffer; + struct const_iovec iov; + ssize_t sent; + + if (wostream->output_closed) { + /* Ostream already finished */ + i_assert(wostream->ostream.finished); + return 1; + } + + if (buffer == NULL || buffer->used == 0) { + /* Buffer already empty */ + return 1; + } + + do { + /* Try to flush whole buffer */ + iov.iov_base = buffer->data; + iov.iov_len = buffer->used; + sent = wrapper_ostream_writev_full(wostream, &iov, 1); + if (sent < 0) + return -1; + + /* Remove sent data from buffer */ + buffer_delete(buffer, 0, sent); + + /* More aggressively flush the buffer when this stream is + finished + */ + } while (wostream->ostream.finished && sent > 0 && buffer->used > 0); + + if (buffer->used == 0 || + (stream->corked && !wrapper_ostream_is_filled(wostream))) + wrapper_ostream_output_halt(wostream); + + return (buffer->used == 0 ? 1 : 0); +} + +static int wrapper_ostream_flush_real(struct wrapper_ostream *wostream) +{ + struct ostream_private *stream = &wostream->ostream; + int ret; + + if (wrapper_ostream_handle_pending_error(wostream) < 0) { + /* Stream already hit an error */ + return -1; + } + wrapper_ostream_output_start(wostream); + + if ((ret = wrapper_ostream_flush_parent(wostream)) <= 0) { + /* Try to flush parent stream first to make room for more + data */ + return ret; + } + if ((ret = wrapper_ostream_flush_buffer(wostream)) <= 0) { + /* Try sending data we already buffered */ + return ret; + } + + if (wostream->output_closed || wostream->output_finished) { + /* Already finished the ostream */ + i_assert(stream->finished); + return 1; + } + + if (!wrapper_ostream_output_ready(wostream)) { + return ((wostream->buffer == NULL || + wostream->buffer->used == 0) ? 1 : 0); + } + + if (wostream->output == NULL) { + i_assert(wrapper_ostream_is_empty(wostream)); + ret = 1; + } else { + ret = o_stream_flush(wostream->output); + if (ret < 0) + wrapper_ostream_handle_parent_error(wostream); + } + + return ret; +} + +static bool +wrapper_ostream_send_prepare(struct wrapper_ostream *wostream, size_t size) +{ + struct ostream_private *stream = &wostream->ostream; + + if (wostream->output_closed || wostream->output_started) + return TRUE; + + if (stream->corked && !stream->finished) { + if (wostream->buffer == NULL) + return FALSE; + if ((wostream->buffer->used + size) < stream->max_buffer_size) + return FALSE; + } + wrapper_ostream_output_start(wostream); + return TRUE; +} + +/* Add data to the wrapper stream's internal buffer. */ +static size_t +wrapper_ostream_add(struct wrapper_ostream *wostream, + const struct const_iovec *iov, + unsigned int iov_count, unsigned int *iov_idx, + size_t *iov_idx_pos) +{ + buffer_t *buffer = wostream->buffer; + unsigned int i; + size_t added = 0; + + /* Create buffer */ + if (buffer == NULL) { + wostream->buffer = buffer = + buffer_create_dynamic(default_pool, IO_BLOCK_SIZE); + } + + for (i = *iov_idx; i < iov_count; i++) { + size_t iov_len, iov_add, space; + const unsigned char *iov_data; + + iov_len = iov[i].iov_len; + iov_data = iov[i].iov_base; + space = wostream->ostream.max_buffer_size - buffer->used; + + i_assert(*iov_idx_pos < iov_len); + if (*iov_idx_pos > 0) { + iov_len -= *iov_idx_pos; + iov_data += *iov_idx_pos; + } + iov_add = I_MIN(space, iov_len); + buffer_append(buffer, iov_data, iov_add); + added += iov_add; + if (iov_add < iov_len) { + /* Buffer is full */ + *iov_idx_pos += iov_add; + break; + } + *iov_idx_pos = 0; + } + + *iov_idx = i; + return added; +} + +static ssize_t +wrapper_ostream_sendv_real(struct wrapper_ostream *wostream, + const struct const_iovec *iov, + unsigned int iov_count) +{ + struct ostream_private *stream = &wostream->ostream; + ssize_t written; + size_t size, iov_pos, sent; + unsigned int i; + int ret; + + if (wrapper_ostream_handle_pending_error(wostream) < 0) { + /* Stream already hit an error */ + return -1; + } + + i_assert(!wostream->output_closed); + i_assert(!wostream->output_finished); + + /* Determine total size of data to send */ + size = 0; + for (i = 0; i < iov_count; i++) + size += iov[i].iov_len; + + /* Flush buffer if required */ + if (!wrapper_ostream_is_empty(wostream) && + (!stream->corked || wrapper_ostream_is_filled(wostream)) && + wrapper_ostream_send_prepare(wostream, size) && + wrapper_ostream_flush_buffer(wostream) < 0) + return -1; + + if (!stream->corked && wrapper_ostream_is_full(wostream)) { + /* No space in buffer for more data */ + i_assert(!stream->ostream.blocking); + return 0; + } + + /* Send data to connection directly if possible */ + i = 0; + sent = iov_pos = 0; + if (wrapper_ostream_is_empty(wostream) && + (!stream->corked || + size >= wrapper_ostream_optimal_size(wostream)) && + wrapper_ostream_send_prepare(wostream, size)) { + written = wrapper_ostream_writev_full(wostream, iov, iov_count); + if (written < 0) + return -1; + sent += written; + if (sent == size) { + /* All sent */ + return (ssize_t)sent; + } + + i_assert(!stream->ostream.blocking); + + /* Determine send position */ + iov_pos = sent; + for (; i < iov_count && iov_pos >= iov[i].iov_len; i++) + iov_pos -= iov[i].iov_len; + i_assert(i < iov_count); + } + + /* Fill buffer with remainder that was not sent directly */ + for (;;) { + sent += wrapper_ostream_add(wostream, iov, iov_count, + &i, &iov_pos); + i_assert(sent <= size); + + if (!stream->corked || !wrapper_ostream_is_filled(wostream)) + break; + + /* Flush corked full buffer */ + wrapper_ostream_output_start(wostream); + if ((ret = wrapper_ostream_flush_buffer(wostream)) < 0) + return -1; + if (ret == 0) + break; + } + + i_assert(!stream->ostream.blocking || sent == size); + return sent; +} + +/* Run the flush callback for the wrapper stream. */ +static int wrapper_ostream_callback(struct wrapper_ostream *wostream) +{ + int ret; + + if (wostream->ostream.callback != NULL) { + if (wostream->callback_pre != NULL) + wostream->callback_pre(wostream); + ret = wostream->ostream.callback(wostream->ostream.context); + if (wostream->callback_post != NULL) + wostream->callback_post(wostream); + } else { + ret = wrapper_ostream_flush(&wostream->ostream); + } + return ret; +} + +/* Handle an event by running wrapper_ostream_continue(). This called from + ioloop on a zero timeout. */ +static void wrapper_ostream_handle_event(struct wrapper_ostream *wostream) +{ + timeout_remove(&wostream->to_event); + (void)wrapper_ostream_continue(wostream); +} + +/* + * iostream methods + */ + +static void +wrapper_ostream_close(struct iostream_private *stream, + bool close_parent ATTR_UNUSED) +{ + struct wrapper_ostream *wostream = + container_of(stream, struct wrapper_ostream, ostream.iostream); + + timeout_remove(&wostream->to_event); + wrapper_ostream_output_close(wostream); + if (wostream->close != NULL) + wostream->close(wostream); +} + +static void wrapper_ostream_destroy(struct iostream_private *stream) +{ + struct wrapper_ostream *wostream = + container_of(stream, struct wrapper_ostream, ostream.iostream); + + timeout_remove(&wostream->to_event); + i_free(wostream->pending_error); + + if (wostream->destroy != NULL) + wostream->destroy(wostream); + buffer_free(&wostream->buffer); + o_stream_unref(&wostream->output); + event_unref(&wostream->event); +} + +/* + * ostream methods + */ + +static void wrapper_ostream_cork(struct ostream_private *stream, bool set) +{ + struct wrapper_ostream *wostream = + container_of(stream, struct wrapper_ostream, ostream); + int ret; + + if (stream->ostream.closed || wostream->pending_errno != 0) + return; + + if (wostream->output_closed) { + i_assert(wostream->ostream.finished); + return; + } + + if (set) { + if (wostream->output != NULL) + o_stream_cork(wostream->output); + } else { + /* Buffer flushing might close the stream */ + ret = wrapper_ostream_flush_buffer(wostream); + stream->last_errors_not_checked = TRUE; + + if (wostream->output != NULL) { + if (o_stream_uncork_flush(wostream->output) < 0) { + wrapper_ostream_handle_parent_error(wostream); + ret = -1; + } + } + if ((ret == 0 || wostream->flush_pending) && + !stream->ostream.closed) + wrapper_ostream_output_resume(wostream); + } + stream->corked = set; + + wrapper_ostream_output_manage(wostream, FALSE); +} + +static ssize_t +wrapper_ostream_sendv(struct ostream_private *stream, + const struct const_iovec *iov, unsigned int iov_count) +{ + struct wrapper_ostream *wostream = + container_of(stream, struct wrapper_ostream, ostream); + bool must_uncork = FALSE; + ssize_t sret; + + if (wrapper_ostream_handle_pending_error(wostream) < 0) { + /* Stream already hit an error */ + return -1; + } + + /* Cork parent ostream if necessary */ + if (!wostream->output_closed && wostream->output != NULL && + !o_stream_is_corked(wostream->output)) { + o_stream_cork(wostream->output); + must_uncork = TRUE; + } + + sret = wrapper_ostream_sendv_real(wostream, iov, iov_count); + if (sret > 0) + stream->ostream.offset += (ssize_t)sret; + + /* Uncork the parent ostream */ + if (must_uncork && !wostream->output_closed && + wostream->output != NULL) { + if (o_stream_uncork_flush(wostream->output) < 0 && + sret >= 0) { + wrapper_ostream_handle_parent_error(wostream); + sret = -1; + } + } + + if (sret >= 0) { + wrapper_ostream_output_update_timeouts(wostream); + if (!stream->ostream.blocking) + wrapper_ostream_output_manage(wostream, FALSE); + } + + return sret; +} + +static int wrapper_ostream_flush(struct ostream_private *stream) +{ + struct wrapper_ostream *wostream = + container_of(stream, struct wrapper_ostream, ostream); + struct ostream *ostream = &stream->ostream; + bool must_uncork = FALSE; + int ret; + + if (wrapper_ostream_handle_pending_error(wostream) < 0) { + /* Stream already hit an error */ + return -1; + } + + if (wostream->output_closed) { + if (!stream->finished || !wrapper_ostream_is_empty(wostream)) { + stream->ostream.stream_errno = EPIPE; + return -1; + } + /* Already finished the ostream */ + return 1; + } + + if (wostream->flushing) { + /* Prevent recursion while finishing output */ + return 1; + } + wostream->flushing = TRUE; + o_stream_ref(ostream); + + /* Cork parent ostream if necessary */ + if (wostream->output != NULL && !o_stream_is_corked(wostream->output)) { + o_stream_cork(wostream->output); + must_uncork = TRUE; + } + + /* If blocking: loop until all is flushed; otherwise try once */ + do { + /* Try to flush */ + if ((ret = wrapper_ostream_flush_real(wostream)) < 0) { + ret = -1; + break; + } + + if (ret == 0 && stream->ostream.blocking) { + /* Block until we can write more */ + if (wrapper_ostream_flush_wait(wostream) < 0) { + ret = -1; + break; + } + } + + if (stream->ostream.closed) { + /* Ostream was closed in the mean time */ + ret = -1; + break; + } + + if (wostream->output_closed) { + /* Already finished the ostream */ + i_assert(stream->finished); + ret = 1; + break; + } + } while (ret == 0 && stream->ostream.blocking); + + if (ret > 0 && stream->finished) { + /* This was an o_stream_finish() call or subsequent flush */ + i_assert(wrapper_ostream_is_empty(wostream)); + while ((ret = wrapper_ostream_finish(wostream)) == 0) { + if (!stream->ostream.blocking) { + /* Not yet finished completely */ + break; + } + /* Block until we can write more */ + if (wrapper_ostream_flush_wait(wostream) < 0) { + ret = -1; + break; + } + } + } + wrapper_ostream_output_update_timeouts(wostream); + wostream->flushing = FALSE; + + if (ret >= 0 && !ostream->blocking) + wrapper_ostream_output_manage(wostream, FALSE); + + if (wostream->output_closed) { + i_assert(ret < 0 || ostream->stream_errno == 0 || + ostream->closed); + i_assert(ret >= 0 || ostream->stream_errno != 0); + o_stream_unref(&ostream); + return (ret >= 0 ? 1 : -1); + } + + if (!must_uncork || wostream->output == NULL) { + /* Nothing */ + } else if (ret >= 0) { + /* Uncork the parent ostream */ + if (o_stream_uncork_flush(wostream->output) < 0) { + wrapper_ostream_handle_parent_error(wostream); + ret = -1; + } + } else { + o_stream_uncork(wostream->output); + } + + i_assert(ret >= 0 || ostream->stream_errno != 0); + o_stream_unref(&ostream); + return ret; +} + +static void +wrapper_ostream_set_flush_callback(struct ostream_private *stream, + stream_flush_callback_t *callback, + void *context) +{ + struct wrapper_ostream *wostream = + container_of(stream, struct wrapper_ostream, ostream); + + stream->callback = callback; + stream->context = context; + + if (!stream->ostream.blocking && stream->callback == NULL) { + /* Application is currently not interested in flush events and + that includes request events like errors. */ + timeout_remove(&wostream->to_event); + } else if (wostream->pending_error != NULL && + wostream->to_event == NULL) { + /* Schedule flush callback to notify application of events */ + wostream->to_event = timeout_add_short( + 0, wrapper_ostream_handle_event, wostream); + } +} + +static void +wrapper_ostream_flush_pending(struct ostream_private *stream, bool set) +{ + struct wrapper_ostream *wostream = + container_of(stream, struct wrapper_ostream, ostream); + + wostream->flush_pending = set; + if (!set) + return; + if (wostream->output_closed) { + i_assert(wostream->ostream.ostream.closed); + return; + } + if (wostream->to_event == NULL) { + wostream->to_event = timeout_add_short( + 0, wrapper_ostream_handle_event, wostream); + } +} + +static size_t +wrapper_ostream_get_buffer_used_size(const struct ostream_private *stream) +{ + const struct wrapper_ostream *wostream = + container_of(stream, const struct wrapper_ostream, ostream); + size_t size = 0; + + if (wostream->buffer != NULL) + size += wostream->buffer->used; + if (wostream->output != NULL) + size += o_stream_get_buffer_used_size(wostream->output); + return size; +} + +static size_t +wrapper_ostream_get_buffer_avail_size(const struct ostream_private *stream) +{ + const struct wrapper_ostream *wostream = + container_of(stream, const struct wrapper_ostream, ostream); + size_t size = 0; + + if (wostream->ostream.max_buffer_size == SIZE_MAX) + return SIZE_MAX; + + if (wostream->buffer == NULL) + size = wostream->ostream.max_buffer_size; + else if (wostream->buffer->used < wostream->ostream.max_buffer_size) { + size = (wostream->ostream.max_buffer_size - + wostream->buffer->used); + } + + if (wostream->output != NULL) + size += o_stream_get_buffer_avail_size(wostream->output); + + return size; +} + +static void +wrapper_ostream_switch_ioloop_to(struct ostream_private *stream, + struct ioloop *ioloop) +{ + struct wrapper_ostream *wostream = + container_of(stream, struct wrapper_ostream, ostream); + + if (wostream->flush_ioloop != ioloop && + wostream->switch_ioloop_to != NULL) + wostream->switch_ioloop_to(wostream, ioloop); + + if (wostream->to_event != NULL) { + wostream->to_event = + io_loop_move_timeout_to(ioloop, &wostream->to_event); + } +} + +/* + * API + */ + +struct ostream * +wrapper_ostream_create(struct wrapper_ostream *wostream, + size_t max_buffer_size, bool blocking, + struct event *event) +{ + wostream->ostream.iostream.close = wrapper_ostream_close; + wostream->ostream.iostream.destroy = wrapper_ostream_destroy; + + wostream->ostream.ostream.blocking = blocking; + wostream->ostream.max_buffer_size = max_buffer_size; + wostream->ostream.cork = wrapper_ostream_cork; + wostream->ostream.sendv = wrapper_ostream_sendv; + wostream->ostream.flush = wrapper_ostream_flush; + wostream->ostream.set_flush_callback = + wrapper_ostream_set_flush_callback; + wostream->ostream.flush_pending = wrapper_ostream_flush_pending; + wostream->ostream.get_buffer_used_size = + wrapper_ostream_get_buffer_used_size; + wostream->ostream.get_buffer_avail_size = + wrapper_ostream_get_buffer_avail_size; + wostream->ostream.switch_ioloop_to = + wrapper_ostream_switch_ioloop_to; + + wostream->event = event_create(event); + + return o_stream_create(&wostream->ostream, NULL, -1); +} + +int wrapper_ostream_continue(struct wrapper_ostream *wostream) +{ + struct ostream_private *stream = &wostream->ostream; + struct ostream *ostream = &stream->ostream; + struct ioloop *ioloop = NULL; + bool use_cork = !stream->corked; + int ret = 1; + + if (wostream->flush_waiting) { + /* Inside wrapper_ostream_flush_wait() */ + ioloop = wostream->flush_ioloop; + } + if (stream->ostream.closed || + (stream->finished && wrapper_ostream_is_empty(wostream) && + wostream->output != NULL && + o_stream_get_buffer_used_size(wostream->output) == 0)) { + /* Already finished */ + ret = wrapper_ostream_finish(wostream); + if (ret == 0) + return 0; + } + if (wostream->flush_waiting) { + i_assert(ioloop != NULL); + io_loop_stop(ioloop); + wostream->flush_waiting = FALSE; + return ret; + } + + /* Set flush_pending = FALSE first before calling the flush callback, + and change it to TRUE only if callback returns 0. That way the + callback can call o_stream_set_flush_pending() again and we don't + forget it even if flush callback returns 1. */ + wostream->flush_pending = FALSE; + + o_stream_ref(ostream); + wostream->continuing = TRUE; + for (;;) { + if (use_cork) + o_stream_cork(ostream); + ret = wrapper_ostream_callback(wostream); + if (use_cork && !wostream->output_closed) { + int fret = o_stream_uncork_flush(ostream); + if (ret == 0 && fret > 0) + continue; + if (fret < 0 && ret >= 0) { + i_assert(ostream->stream_errno != 0); + (void)wrapper_ostream_callback(wostream); + ret = -1; + } + } + break; + } + wostream->continuing = FALSE; + if (wostream->output_closed) + o_stream_close(ostream); + + if (ret == 0) + wostream->flush_pending = TRUE; + + if (!stream->ostream.blocking) + wrapper_ostream_output_manage(wostream, FALSE); + + if (ret < 0 || ostream->stream_errno != 0 || + wostream->pending_errno != 0) + ret = -1; + else if (wostream->output_closed) + ret = 1; + else if (!wrapper_ostream_is_empty(wostream) && + (!stream->corked || wrapper_ostream_is_filled(wostream))) + ret = 0; + else if (wostream->flush_pending) + ret = 0; + + o_stream_unref(&ostream); + + return ret; +} + +void wrapper_ostream_trigger_flush(struct wrapper_ostream *wostream) +{ + struct ostream *ostream = &wostream->ostream.ostream; + + if (ostream->closed) + return; + if (wostream->to_event != NULL) + return; + if (!wostream->flush_waiting && wostream->ostream.callback == NULL) + return; + + wostream->to_event = timeout_add_short( + 0, wrapper_ostream_handle_event, wostream); +} + +bool wrapper_ostream_get_buffered_size(struct wrapper_ostream *wostream, + uoff_t *size_r) +{ + buffer_t *buffer = wostream->buffer; + + if (!wostream->ostream.finished) + return FALSE; + + *size_r = (buffer == NULL ? 0 : (uoff_t)buffer->used); + i_assert(*size_r == wostream->ostream.ostream.offset); + return TRUE; +} + +void wrapper_ostream_output_available(struct wrapper_ostream *wostream, + struct ostream *output) +{ + i_assert(!wostream->output_closed); + i_assert(!wostream->output_finished); + i_assert(wostream->output == NULL); + wostream->output = output; + if (output != NULL) { + if (wostream->ostream.corked) + o_stream_cork(wostream->output); + o_stream_ref(output); + } +} + +void wrapper_ostream_output_destroyed(struct wrapper_ostream *wostream) +{ + struct ostream *ostream = &wostream->ostream.ostream; + + wrapper_ostream_trigger_flush(wostream); + o_stream_set_no_error_handling(ostream, TRUE); + + o_stream_unref(&wostream->output); + wostream->output_closed = TRUE; + wostream->output_finished = TRUE; +} + +void wrapper_ostream_set_error(struct wrapper_ostream *wostream, + int stream_errno, const char *stream_error) +{ + struct ostream *ostream = &wostream->ostream.ostream; + + if (ostream->closed || wostream->pending_errno != 0 || + wostream->returned_error) + return; + + i_assert(wostream->pending_error == NULL); + wostream->pending_errno = stream_errno; + wostream->pending_error = i_strdup(stream_error); + + wrapper_ostream_trigger_flush(wostream); +} + +void wrapper_ostream_notify_error(struct wrapper_ostream *wostream) +{ + struct ostream *ostream = &wostream->ostream.ostream; + + if (ostream->closed || ostream->blocking || + wostream->output_closed_api || wostream->returned_error || + wostream->continuing) + return; + if (wostream->pending_errno == 0) + return; + wostream->returned_error = TRUE; + (void)wrapper_ostream_callback(wostream); +} diff --git a/src/lib/ostream-wrapper.h b/src/lib/ostream-wrapper.h new file mode 100644 index 0000000..3fb6ccc --- /dev/null +++ b/src/lib/ostream-wrapper.h @@ -0,0 +1,165 @@ +#ifndef OSTREAM_WRAPPER_H +#define OSTREAM_WRAPPER_H + +#include "ostream-private.h" + +/* The wrapper output stream allows turning any form* of activity involving data + output into a standard Dovecot output stream. The wrapper output stream can + operate both in blocking and non-blocking mode. When the wrapped activity is + non-blocking, a blocking wrapper output stream will implicitly run its own + ioloop. + + It is possible to have the wrapper output stream object available even before + the data can be written anywhere, even before any form of output object (a + connection) exists. In that case, any data written to the wrapper stream is + buffered until the buffer is full. Once that happens, the stream will block + or refuse writes until the underlying output becomes available. + + The wrapper output stream is not meant to be used directly. Instead, it is + to be used as part of the implementation of an application-specific output + stream. The wrapper output stream serves as the means to prevent code + duplication between similar output stream implementations. It defines several + methods that need to be implemented by the application-specific output + stream. + + * Currently, the wrapper stream still expects an output stream object when + data is to be written somewhere, but that should be easily circumvented + once such behavior is needed (FIXME). + */ + +struct wrapper_ostream { + struct ostream_private ostream; + struct event *event; + + /* Called when the implementation should start making the parent output + stream available, e.g. connect to the server. This happens when data + was written to the wrapper ostream (when it is corked this only + happens when the wrapper ostream buffer is full or the wrapper + ostream is finished). */ + void (*output_start)(struct wrapper_ostream *wostream); + /* Returns TRUE when the output is ready for data. */ + bool (*output_ready)(struct wrapper_ostream *wostream); + /* Called when an error occurred while writing to the output stream. */ + void (*output_error)(struct wrapper_ostream *wostream); + /* Called when the wrapper ostream was finished using o_stream_finish() + and the wrapper ostream buffer is empty. Also, the parent output + was flushed successfully. */ + int (*output_finish)(struct wrapper_ostream *wostream); + /* Called when the wrapper ostream does not need write to parent output + stream. This is will e.g. drop the parent output's flush callback or + equivalent notification mechanism. */ + void (*output_halt)(struct wrapper_ostream *wostream); + /* Called when the wrapper ostream has data available for the parent + output and wants wrapper_ostream_continue() to be called when the + parent stream is writeable. */ + void (*output_resume)(struct wrapper_ostream *wostream); + /* Update the timeouts. The sender_blocking parameter indicates which + side of the data transfer is blocking, so whether a timeout needs to + be set for limiting the time other side is not doing anything. */ + void (*output_update_timeouts)(struct wrapper_ostream *wostream, + bool sender_blocking); + + /* Called before and after running ioloop for performing blocking I/O + wait. Use these vfuncs to switch to and from the temporary ioloop. */ + struct ioloop *(*wait_begin)(struct wrapper_ostream *wostream, + struct ioloop *ioloop); + void (*wait_end)(struct wrapper_ostream *wostream, + struct ioloop *prev_ioloop); + + /* Called before and after running the flush callback for the ostream. + */ + void (*callback_pre)(struct wrapper_ostream *wostream); + void (*callback_post)(struct wrapper_ostream *wostream); + + /* Called when the ostream is switched to a different ioloop. */ + void (*switch_ioloop_to)(struct wrapper_ostream *wostream, + struct ioloop *ioloop); + + /* Called when the wrapper ostream is forcibly closed using + o_stream_close() (or indirectly through e.g. o_stream_destroy()). */ + void (*close)(struct wrapper_ostream *wostream); + /* Called when the ostream is destroyed. */ + void (*destroy)(struct wrapper_ostream *wostream); + + buffer_t *buffer; // FIXME: use a ringbuffer instead (file_ostream) + + /* The (parent) output stream. */ + struct ostream *output; + + /* The ioloop used while flushing/sending output for when the wrapper + ostream is blocking. */ + struct ioloop *flush_ioloop; + + /* Error set using wrapper_ostream_return_error(). This is returned to + the application once it continues using the wrapper ostream. */ + char *pending_error; + int pending_errno; + + /* Timeout for delayed execution of wrapper_ostream_continue(). */ + struct timeout *to_event; + + /* Output was started (output_start() vfunc was called). */ + bool output_started:1; + /* Output was finished (output_finish() vfunc was called). */ + bool output_finished:1; + /* Output was was closed somehow. This means that the output is no + longer available. This is not the same as the ostream close flag. */ + bool output_closed:1; + /* Output was closed directly or indirectly by the application action. + */ + bool output_closed_api:1; + + bool flush_pending:1; + bool flush_waiting:1; + bool flushing:1; + bool continuing:1; + bool returned_error:1; +}; + +/* Create the wrapper output stream. This function calls o_stream_create() + internally. The initial maximum buffer size is set to max_buffer_size. When + blocking is TRUE, a blocking output stream will be created. The provided + event is used internally for debug logging. */ +struct ostream * +wrapper_ostream_create(struct wrapper_ostream *wostream, + size_t max_buffer_size, bool blocking, + struct event *event) ATTR_NULL(4); + +/* Continue sending output. Returns 1 if all buffered data is sent so far, + 0 if not, and -1 if an error occurred. */ +int wrapper_ostream_continue(struct wrapper_ostream *wostream); +/* Trigger an (asynchronous) flush on the output stream. */ +void wrapper_ostream_trigger_flush(struct wrapper_ostream *wostream); + +/* This function returns the size of the data buffered in the wrapper stream, + but only when the output stream is finished using o_stream_finish(). When the + output stream is finished, the data is complete and this function returns + TRUE and size_r is set to the size. If it is not complete, this function + returns FALSE and size_r is not assigned. This function is meant to be called + just before sending the first block of data internally for deciding between + sending the data using a chunked transfer encoding or, when it is already + complete, as a single blob with known size. E.g., for HTTP this is the choice + between sending the message using the Transfer-Encoding: chunked header or + the Content-Length header. */ +bool wrapper_ostream_get_buffered_size(struct wrapper_ostream *wostream, + uoff_t *size_r); + +/* Call this when the underlying output stream first becomes available. */ +void wrapper_ostream_output_available(struct wrapper_ostream *wostream, + struct ostream *output); +/* Call this to notify the wrapper that the underlying output is destroyed and + no more data can be written ever. */ +void wrapper_ostream_output_destroyed(struct wrapper_ostream *wostream); + +/* Call this to notify the wrapper that an error has occurred. It will be + returned as such for the next stream write/flush and subsequent + o_stream_get_error(). */ +void wrapper_ostream_set_error(struct wrapper_ostream *wostream, + int stream_errno, const char *stream_error); +/* Notify the application immediately about any error condition set earlier + using wrapper_ostream_set_error() by calling the ostream flush callback + right now. + */ +void wrapper_ostream_notify_error(struct wrapper_ostream *wostream); + +#endif diff --git a/src/lib/ostream.c b/src/lib/ostream.c new file mode 100644 index 0000000..2f9c49d --- /dev/null +++ b/src/lib/ostream.c @@ -0,0 +1,804 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream.h" +#include "ostream-private.h" + +void o_stream_set_name(struct ostream *stream, const char *name) +{ + i_free(stream->real_stream->iostream.name); + stream->real_stream->iostream.name = i_strdup(name); +} + +const char *o_stream_get_name(struct ostream *stream) +{ + while (stream->real_stream->iostream.name == NULL) { + stream = stream->real_stream->parent; + if (stream == NULL) + return ""; + } + return stream->real_stream->iostream.name; +} + +int o_stream_get_fd(struct ostream *stream) +{ + return stream->real_stream->fd; +} + +const char *o_stream_get_error(struct ostream *stream) +{ + struct ostream *s; + + /* we'll only return errors for streams that have stream_errno set. + we might be returning unintended error otherwise. */ + if (stream->stream_errno == 0) + return "<no error>"; + + for (s = stream; s != NULL; s = s->real_stream->parent) { + if (s->stream_errno == 0) + break; + if (s->real_stream->iostream.error != NULL) + return s->real_stream->iostream.error; + } + return strerror(stream->stream_errno); +} + +const char *o_stream_get_disconnect_reason(struct ostream *stream) +{ + return io_stream_get_disconnect_reason(NULL, stream); +} + +static void o_stream_close_full(struct ostream *stream, bool close_parents) +{ + /* Ideally o_stream_finish() would be called for all non-failed + ostreams, but strictly requiring it would cause unnecessary + complexity for many callers. Just require that at this point + after flushing there isn't anything in the output buffer or that + we're ignoring all errors. */ + if (o_stream_flush(stream) == 0) + i_assert(stream->real_stream->error_handling_disabled); + + if (!stream->closed && !stream->real_stream->closing) { + /* first mark the stream as being closed so the + o_stream_copy_error_from_parent() won't recurse us back + here. but don't immediately mark the stream closed, because + we may still want to write something to it. */ + stream->real_stream->closing = TRUE; + io_stream_close(&stream->real_stream->iostream, close_parents); + stream->closed = TRUE; + } + + if (stream->stream_errno == 0) + stream->stream_errno = EPIPE; +} + +void o_stream_destroy(struct ostream **_stream) +{ + struct ostream *stream = *_stream; + + if (stream == NULL) + return; + + *_stream = NULL; + o_stream_close_full(stream, FALSE); + o_stream_unref(&stream); +} + +void o_stream_ref(struct ostream *stream) +{ + io_stream_ref(&stream->real_stream->iostream); +} + +void o_stream_unref(struct ostream **_stream) +{ + struct ostream *stream; + + if (*_stream == NULL) + return; + + stream = *_stream; + + if (stream->real_stream->last_errors_not_checked && + !stream->real_stream->error_handling_disabled && + stream->real_stream->iostream.refcount == 1) { + i_panic("output stream %s is missing error handling", + o_stream_get_name(stream)); + } + + if (!io_stream_unref(&stream->real_stream->iostream)) + io_stream_free(&stream->real_stream->iostream); + *_stream = NULL; +} + +#undef o_stream_add_destroy_callback +void o_stream_add_destroy_callback(struct ostream *stream, + ostream_callback_t *callback, void *context) +{ + io_stream_add_destroy_callback(&stream->real_stream->iostream, + callback, context); +} + +void o_stream_remove_destroy_callback(struct ostream *stream, + void (*callback)()) +{ + io_stream_remove_destroy_callback(&stream->real_stream->iostream, + callback); +} + +void o_stream_close(struct ostream *stream) +{ + if (stream != NULL) + o_stream_close_full(stream, TRUE); +} + +#undef o_stream_set_flush_callback +void o_stream_set_flush_callback(struct ostream *stream, + stream_flush_callback_t *callback, + void *context) +{ + struct ostream_private *_stream = stream->real_stream; + + _stream->set_flush_callback(_stream, callback, context); +} + +void o_stream_unset_flush_callback(struct ostream *stream) +{ + struct ostream_private *_stream = stream->real_stream; + + _stream->set_flush_callback(_stream, NULL, NULL); +} + +void o_stream_set_max_buffer_size(struct ostream *stream, size_t max_size) +{ + io_stream_set_max_buffer_size(&stream->real_stream->iostream, max_size); +} + +size_t o_stream_get_max_buffer_size(struct ostream *stream) +{ + return stream->real_stream->max_buffer_size; +} + +void o_stream_cork(struct ostream *stream) +{ + struct ostream_private *_stream = stream->real_stream; + + if (unlikely(stream->closed || stream->stream_errno != 0)) + return; + + _stream->cork(_stream, TRUE); +} + +void o_stream_uncork(struct ostream *stream) +{ + struct ostream_private *_stream = stream->real_stream; + + if (unlikely(stream->closed || stream->stream_errno != 0)) + return; + + _stream->cork(_stream, FALSE); +} + +bool o_stream_is_corked(struct ostream *stream) +{ + struct ostream_private *_stream = stream->real_stream; + + return _stream->corked; +} + +int o_stream_flush(struct ostream *stream) +{ + struct ostream_private *_stream = stream->real_stream; + int ret = 1; + + o_stream_ignore_last_errors(stream); + + if (unlikely(stream->closed || stream->stream_errno != 0)) { + errno = stream->stream_errno; + return -1; + } + + if (unlikely(_stream->noverflow)) { + io_stream_set_error(&_stream->iostream, + "Output stream buffer was full (%zu bytes)", + o_stream_get_max_buffer_size(stream)); + errno = stream->stream_errno = ENOBUFS; + return -1; + } + + if (unlikely((ret = _stream->flush(_stream)) < 0)) { + i_assert(stream->stream_errno != 0); + errno = stream->stream_errno; + } + return ret; +} + +void o_stream_set_flush_pending(struct ostream *stream, bool set) +{ + struct ostream_private *_stream = stream->real_stream; + + if (unlikely(stream->closed || stream->stream_errno != 0)) + return; + + _stream->flush_pending(_stream, set); +} + +size_t o_stream_get_buffer_used_size(const struct ostream *stream) +{ + const struct ostream_private *_stream = stream->real_stream; + + return _stream->get_buffer_used_size(_stream); +} + +size_t o_stream_get_buffer_avail_size(const struct ostream *stream) +{ + const struct ostream_private *_stream = stream->real_stream; + + return _stream->get_buffer_avail_size(_stream); +} + +int o_stream_seek(struct ostream *stream, uoff_t offset) +{ + struct ostream_private *_stream = stream->real_stream; + + if (unlikely(stream->closed || stream->stream_errno != 0)) { + errno = stream->stream_errno; + return -1; + } + + if (unlikely(_stream->seek(_stream, offset) < 0)) { + i_assert(stream->stream_errno != 0); + errno = stream->stream_errno; + return -1; + } + return 1; +} + +ssize_t o_stream_send(struct ostream *stream, const void *data, size_t size) +{ + struct const_iovec iov; + + i_zero(&iov); + iov.iov_base = data; + iov.iov_len = size; + + return o_stream_sendv(stream, &iov, 1); +} + +static ssize_t +o_stream_sendv_int(struct ostream *stream, const struct const_iovec *iov, + unsigned int iov_count, bool *overflow_r) +{ + struct ostream_private *_stream = stream->real_stream; + unsigned int i; + size_t total_size; + ssize_t ret; + + *overflow_r = FALSE; + + for (i = 0, total_size = 0; i < iov_count; i++) + total_size += iov[i].iov_len; + if (total_size == 0) + return 0; + + i_assert(!_stream->finished); + ret = _stream->sendv(_stream, iov, iov_count); + if (ret > 0) + stream->real_stream->last_write_timeval = ioloop_timeval; + if (unlikely(ret != (ssize_t)total_size)) { + if (ret < 0) { + i_assert(stream->stream_errno != 0); + errno = stream->stream_errno; + } else { + i_assert(!stream->blocking); + stream->overflow = TRUE; + *overflow_r = TRUE; + } + } + return ret; +} + +ssize_t o_stream_sendv(struct ostream *stream, const struct const_iovec *iov, + unsigned int iov_count) +{ + bool overflow; + + if (unlikely(stream->closed || stream->stream_errno != 0)) { + errno = stream->stream_errno; + return -1; + } + return o_stream_sendv_int(stream, iov, iov_count, &overflow); +} + +ssize_t o_stream_send_str(struct ostream *stream, const char *str) +{ + return o_stream_send(stream, str, strlen(str)); +} + +void o_stream_nsend(struct ostream *stream, const void *data, size_t size) +{ + struct const_iovec iov; + + i_zero(&iov); + iov.iov_base = data; + iov.iov_len = size; + + o_stream_nsendv(stream, &iov, 1); +} + +void o_stream_nsendv(struct ostream *stream, const struct const_iovec *iov, + unsigned int iov_count) +{ + bool overflow; + + if (unlikely(stream->closed || stream->stream_errno != 0 || + stream->real_stream->noverflow)) + return; + (void)o_stream_sendv_int(stream, iov, iov_count, &overflow); + if (overflow) + stream->real_stream->noverflow = TRUE; + stream->real_stream->last_errors_not_checked = TRUE; +} + +void o_stream_nsend_str(struct ostream *stream, const char *str) +{ + o_stream_nsend(stream, str, strlen(str)); +} + +int o_stream_finish(struct ostream *stream) +{ + stream->real_stream->finished = TRUE; + return o_stream_flush(stream); +} + +void o_stream_set_finish_also_parent(struct ostream *stream, bool set) +{ + stream->real_stream->finish_also_parent = set; +} + +void o_stream_set_finish_via_child(struct ostream *stream, bool set) +{ + stream->real_stream->finish_via_child = set; +} + +void o_stream_ignore_last_errors(struct ostream *stream) +{ + while (stream != NULL) { + stream->real_stream->last_errors_not_checked = FALSE; + stream = stream->real_stream->parent; + } +} + +void o_stream_abort(struct ostream *stream) +{ + o_stream_ignore_last_errors(stream); + if (stream->stream_errno != 0) + return; + io_stream_set_error(&stream->real_stream->iostream, "aborted writing"); + stream->stream_errno = EPIPE; +} + +void o_stream_set_no_error_handling(struct ostream *stream, bool set) +{ + stream->real_stream->error_handling_disabled = set; +} + +enum ostream_send_istream_result +o_stream_send_istream(struct ostream *outstream, struct istream *instream) +{ + struct ostream_private *_outstream = outstream->real_stream; + uoff_t old_outstream_offset = outstream->offset; + uoff_t old_instream_offset = instream->v_offset; + enum ostream_send_istream_result res; + + if (unlikely(instream->closed || instream->stream_errno != 0)) { + errno = instream->stream_errno; + return OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT; + } + if (unlikely(outstream->closed || outstream->stream_errno != 0)) { + errno = outstream->stream_errno; + return OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT; + } + + i_assert(!_outstream->finished); + res = _outstream->send_istream(_outstream, instream); + switch (res) { + case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: + i_assert(instream->stream_errno == 0); + i_assert(outstream->stream_errno == 0); + i_assert(!i_stream_have_bytes_left(instream)); + break; + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: + i_assert(!instream->blocking); + break; + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: + i_assert(!outstream->blocking); + o_stream_set_flush_pending(outstream, TRUE); + break; + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: + i_assert(instream->stream_errno != 0); + return res; + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: + i_assert(outstream->stream_errno != 0); + return res; + } + /* non-failure - make sure stream offsets match */ + i_assert((outstream->offset - old_outstream_offset) == + (instream->v_offset - old_instream_offset)); + + if (outstream->offset != old_outstream_offset) + outstream->real_stream->last_write_timeval = ioloop_timeval; + return res; +} + +void o_stream_nsend_istream(struct ostream *outstream, struct istream *instream) +{ + i_assert(instream->blocking); + + switch (o_stream_send_istream(outstream, instream)) { + case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: + break; + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: + i_unreached(); + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: + outstream->real_stream->noverflow = TRUE; + break; + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: + outstream->stream_errno = instream->stream_errno; + io_stream_set_error(&outstream->real_stream->iostream, + "nsend-istream: read(%s) failed: %s", + i_stream_get_name(instream), + i_stream_get_error(instream)); + break; + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: + break; + } + outstream->real_stream->last_errors_not_checked = TRUE; +} + +int o_stream_pwrite(struct ostream *stream, const void *data, size_t size, + uoff_t offset) +{ + int ret; + + if (unlikely(stream->closed || stream->stream_errno != 0)) { + errno = stream->stream_errno; + return -1; + } + + i_assert(!stream->real_stream->finished); + ret = stream->real_stream->write_at(stream->real_stream, + data, size, offset); + if (ret > 0) + stream->real_stream->last_write_timeval = ioloop_timeval; + else if (unlikely(ret < 0)) { + i_assert(stream->stream_errno != 0); + errno = stream->stream_errno; + } + + return ret; +} + +void o_stream_get_last_write_time(struct ostream *stream, struct timeval *tv_r) +{ + *tv_r = stream->real_stream->last_write_timeval; +} + +enum ostream_send_istream_result +io_stream_copy(struct ostream *outstream, struct istream *instream) +{ + struct const_iovec iov; + const unsigned char *data; + ssize_t ret; + + while (i_stream_read_more(instream, &data, &iov.iov_len) > 0) { + iov.iov_base = data; + if ((ret = o_stream_sendv(outstream, &iov, 1)) < 0) + return OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT; + else if (ret == 0) + return OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT; + i_stream_skip(instream, ret); + } + + if (instream->stream_errno != 0) + return OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT; + if (i_stream_have_bytes_left(instream)) + return OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT; + return OSTREAM_SEND_ISTREAM_RESULT_FINISHED; +} + +void o_stream_switch_ioloop_to(struct ostream *stream, struct ioloop *ioloop) +{ + struct ostream_private *_stream = stream->real_stream; + + io_stream_switch_ioloop_to(&_stream->iostream, ioloop); + + _stream->switch_ioloop_to(_stream, ioloop); +} + +void o_stream_switch_ioloop(struct ostream *stream) +{ + o_stream_switch_ioloop_to(stream, current_ioloop); +} + +static void o_stream_default_close(struct iostream_private *stream, + bool close_parent) +{ + struct ostream_private *_stream = + container_of(stream, struct ostream_private, iostream); + + (void)o_stream_flush(&_stream->ostream); + if (close_parent) + o_stream_close(_stream->parent); +} + +static void o_stream_default_destroy(struct iostream_private *stream) +{ + struct ostream_private *_stream = + container_of(stream, struct ostream_private, iostream); + + o_stream_unref(&_stream->parent); +} + +static void +o_stream_default_set_max_buffer_size(struct iostream_private *stream, + size_t max_size) +{ + struct ostream_private *_stream = + container_of(stream, struct ostream_private, iostream); + + if (_stream->parent != NULL) + o_stream_set_max_buffer_size(_stream->parent, max_size); + _stream->max_buffer_size = max_size; +} + +static void o_stream_default_cork(struct ostream_private *_stream, bool set) +{ + _stream->corked = set; + if (set) { + if (_stream->parent != NULL) + o_stream_cork(_stream->parent); + } else { + (void)o_stream_flush(&_stream->ostream); + _stream->last_errors_not_checked = TRUE; + + if (_stream->parent != NULL) + o_stream_uncork(_stream->parent); + } +} + +void o_stream_copy_error_from_parent(struct ostream_private *_stream) +{ + struct ostream *src = _stream->parent; + struct ostream *dest = &_stream->ostream; + + i_assert(src->stream_errno != 0); + + dest->stream_errno = src->stream_errno; + dest->overflow = src->overflow; + if (src->closed) + o_stream_close(dest); +} + +int o_stream_flush_parent_if_needed(struct ostream_private *_stream) +{ + if (o_stream_get_buffer_used_size(_stream->parent) >= IO_BLOCK_SIZE) { + /* we already have quite a lot of data in parent stream. + unless we can flush it, don't add any more to it or we + could keep wasting memory by just increasing the buffer + size all the time. */ + if (o_stream_flush(_stream->parent) < 0) { + o_stream_copy_error_from_parent(_stream); + return -1; + } + if (o_stream_get_buffer_used_size(_stream->parent) >= IO_BLOCK_SIZE) + return 0; + } + return 1; +} + +int o_stream_flush_parent(struct ostream_private *_stream) +{ + int ret; + + i_assert(_stream->parent != NULL); + + if (!_stream->finished || !_stream->finish_also_parent || + !_stream->parent->real_stream->finish_via_child) + ret = o_stream_flush(_stream->parent); + else + ret = o_stream_finish(_stream->parent); + if (ret < 0) + o_stream_copy_error_from_parent(_stream); + return ret; +} + +static int o_stream_default_flush(struct ostream_private *_stream) +{ + if (_stream->parent == NULL) + return 1; + + return o_stream_flush_parent(_stream); +} + +static void +o_stream_default_set_flush_callback(struct ostream_private *_stream, + stream_flush_callback_t *callback, + void *context) +{ + if (_stream->parent != NULL) + o_stream_set_flush_callback(_stream->parent, callback, context); + + _stream->callback = callback; + _stream->context = context; +} + +static void +o_stream_default_set_flush_pending(struct ostream_private *_stream, bool set) +{ + if (_stream->parent != NULL) + o_stream_set_flush_pending(_stream->parent, set); +} + +static size_t +o_stream_default_get_buffer_used_size(const struct ostream_private *_stream) +{ + if (_stream->parent == NULL) + return 0; + else + return o_stream_get_buffer_used_size(_stream->parent); +} + +static size_t +o_stream_default_get_buffer_avail_size(const struct ostream_private *_stream) +{ + /* This default implementation assumes that the returned buffer size is + between 0..max_buffer_size. There's no assert though, in case the + max_buffer_size changes. */ + size_t used = o_stream_get_buffer_used_size(&_stream->ostream); + + return _stream->max_buffer_size <= used ? 0 : + _stream->max_buffer_size - used; +} + +static int +o_stream_default_seek(struct ostream_private *_stream, + uoff_t offset ATTR_UNUSED) +{ + _stream->ostream.stream_errno = ESPIPE; + return -1; +} + +static ssize_t +o_stream_default_sendv(struct ostream_private *stream, + const struct const_iovec *iov, unsigned int iov_count) +{ + ssize_t ret; + + if ((ret = o_stream_sendv(stream->parent, iov, iov_count)) < 0) { + o_stream_copy_error_from_parent(stream); + return -1; + } + stream->ostream.offset += ret; + return ret; +} + +static int +o_stream_default_write_at(struct ostream_private *_stream, + const void *data ATTR_UNUSED, + size_t size ATTR_UNUSED, uoff_t offset ATTR_UNUSED) +{ + _stream->ostream.stream_errno = ESPIPE; + return -1; +} + +static enum ostream_send_istream_result +o_stream_default_send_istream(struct ostream_private *outstream, + struct istream *instream) +{ + return io_stream_copy(&outstream->ostream, instream); +} + +static void +o_stream_default_switch_ioloop_to(struct ostream_private *_stream, + struct ioloop *ioloop) +{ + if (_stream->parent != NULL) + o_stream_switch_ioloop_to(_stream->parent, ioloop); +} + +struct ostream * +o_stream_create(struct ostream_private *_stream, struct ostream *parent, int fd) +{ + _stream->finish_also_parent = TRUE; + _stream->finish_via_child = TRUE; + _stream->fd = fd; + _stream->ostream.real_stream = _stream; + if (parent != NULL) { + _stream->ostream.blocking = parent->blocking; + _stream->parent = parent; + o_stream_ref(parent); + + _stream->callback = parent->real_stream->callback; + _stream->context = parent->real_stream->context; + _stream->max_buffer_size = parent->real_stream->max_buffer_size; + _stream->error_handling_disabled = + parent->real_stream->error_handling_disabled; + } + + if (_stream->iostream.close == NULL) + _stream->iostream.close = o_stream_default_close; + if (_stream->iostream.destroy == NULL) + _stream->iostream.destroy = o_stream_default_destroy; + if (_stream->iostream.set_max_buffer_size == NULL) { + _stream->iostream.set_max_buffer_size = + o_stream_default_set_max_buffer_size; + } + + if (_stream->cork == NULL) + _stream->cork = o_stream_default_cork; + if (_stream->flush == NULL) + _stream->flush = o_stream_default_flush; + if (_stream->set_flush_callback == NULL) { + _stream->set_flush_callback = + o_stream_default_set_flush_callback; + } + if (_stream->flush_pending == NULL) + _stream->flush_pending = o_stream_default_set_flush_pending; + if (_stream->get_buffer_used_size == NULL) + _stream->get_buffer_used_size = + o_stream_default_get_buffer_used_size; + if (_stream->get_buffer_avail_size == NULL) { + _stream->get_buffer_avail_size = + o_stream_default_get_buffer_avail_size; + } + if (_stream->seek == NULL) + _stream->seek = o_stream_default_seek; + if (_stream->sendv == NULL) + _stream->sendv = o_stream_default_sendv; + if (_stream->write_at == NULL) + _stream->write_at = o_stream_default_write_at; + if (_stream->send_istream == NULL) + _stream->send_istream = o_stream_default_send_istream; + if (_stream->switch_ioloop_to == NULL) + _stream->switch_ioloop_to = o_stream_default_switch_ioloop_to; + + io_stream_init(&_stream->iostream); + return &_stream->ostream; +} + +struct ostream *o_stream_create_error(int stream_errno) +{ + struct ostream_private *stream; + struct ostream *output; + + stream = i_new(struct ostream_private, 1); + stream->ostream.blocking = TRUE; + stream->ostream.closed = TRUE; + stream->ostream.stream_errno = stream_errno; + + output = o_stream_create(stream, NULL, -1); + o_stream_set_no_error_handling(output, TRUE); + o_stream_set_name(output, "(error)"); + return output; +} + +struct ostream * +o_stream_create_error_str(int stream_errno, const char *fmt, ...) +{ + struct ostream *output; + va_list args; + + va_start(args, fmt); + output = o_stream_create_error(stream_errno); + io_stream_set_verror(&output->real_stream->iostream, fmt, args); + va_end(args); + return output; +} + +struct ostream *o_stream_create_passthrough(struct ostream *output) +{ + struct ostream_private *stream; + + stream = i_new(struct ostream_private, 1); + return o_stream_create(stream, output, o_stream_get_fd(output)); +} diff --git a/src/lib/ostream.h b/src/lib/ostream.h new file mode 100644 index 0000000..2063847 --- /dev/null +++ b/src/lib/ostream.h @@ -0,0 +1,260 @@ +#ifndef OSTREAM_H +#define OSTREAM_H + +#include "ioloop.h" + +enum ostream_send_istream_result { + /* All of the istream was successfully sent to ostream. */ + OSTREAM_SEND_ISTREAM_RESULT_FINISHED, + /* Caller needs to wait for more input from non-blocking istream. */ + OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT, + /* Caller needs to wait for output to non-blocking ostream. + o_stream_set_flush_pending() is automatically called. */ + OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT, + /* Read from istream failed. See istream->stream_errno. */ + OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT, + /* Write to ostream failed. See ostream->stream_errno. */ + OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT +}; + +enum ostream_create_file_flags { + /* without append, file is truncated */ + OSTREAM_CREATE_FILE_FLAG_APPEND = BIT(0), +}; + +struct ostream { + /* Number of bytes sent via o_stream_send*() and similar functions. + This is counting the input data. For example with a compressed + ostream this is counting the uncompressed bytes. The compressed + bytes could be counted from the parent ostream's offset. + + Seeking to a specified offset only makes sense if there is no + difference between input and output data sizes (e.g. there are no + wrapper ostreams changing the data). */ + uoff_t offset; + + /* errno for the last operation send/seek operation. cleared before + each call. */ + int stream_errno; + + /* overflow is set when some of the data given to send() + functions was neither sent nor buffered. It's never unset inside + ostream code. */ + bool overflow:1; + /* o_stream_send() writes all the data or returns failure */ + bool blocking:1; + bool closed:1; + + struct ostream_private *real_stream; +}; + +/* Returns 1 if all data is sent (not necessarily flushed), 0 if not. + Pretty much the only real reason to return 0 is if you wish to send more + data to client which isn't buffered, eg. o_stream_send_istream(). */ +typedef int stream_flush_callback_t(void *context); +typedef void ostream_callback_t(void *context); + +/* Create new output stream from given file descriptor. + If max_buffer_size is 0, an "optimal" buffer size is used (max 128kB). */ +struct ostream *o_stream_create_fd(int fd, size_t max_buffer_size); +/* The fd is set to -1 immediately to avoid accidentally closing it twice. */ +struct ostream *o_stream_create_fd_autoclose(int *fd, size_t max_buffer_size); +/* Create an output stream from a regular file which begins at given offset. + If offset==UOFF_T_MAX, the current offset isn't known. */ +struct ostream * +o_stream_create_fd_file(int fd, uoff_t offset, bool autoclose_fd); +struct ostream *o_stream_create_fd_file_autoclose(int *fd, uoff_t offset); +/* Create ostream for file. If append flag is not set, file will be truncated. */ +struct ostream *o_stream_create_file(const char *path, uoff_t offset, mode_t mode, + enum ostream_create_file_flags flags); +/* Create an output stream to a buffer. Note that the buffer is treated as the + ostream's internal buffer. This means that o_stream_get_buffer_used_size() + returns buf->used, and _get_buffer_avail_size() returns how many bytes can + be written until the buffer's max size is reached. This behavior may make + ostream-buffer unsuitable for code that assumes that having bytes in the + internal buffer means that ostream isn't finished flushing its internal + buffer. Especially o_stream_flush_parent_if_needed() (used by + lib-compression ostreams) don't work with this. */ +struct ostream *o_stream_create_buffer(buffer_t *buf); +/* Create an output streams that always fails the writes. */ +struct ostream *o_stream_create_error(int stream_errno); +struct ostream * +o_stream_create_error_str(int stream_errno, const char *fmt, ...) + ATTR_FORMAT(2, 3); +/* Create an output stream that simply passes through data. This is mainly + useful as a wrapper when combined with destroy callbacks. */ +struct ostream *o_stream_create_passthrough(struct ostream *output); + +/* Set name (e.g. path) for output stream. */ +void o_stream_set_name(struct ostream *stream, const char *name); +/* Get output stream's name. Returns "" if stream has no name. */ +const char *o_stream_get_name(struct ostream *stream); + +/* Return file descriptor for stream, or -1 if none is available. */ +int o_stream_get_fd(struct ostream *stream); +/* Returns error string for the previous error. */ +const char *o_stream_get_error(struct ostream *stream); +/* Returns human-readable reason for why ostream was disconnected. + The output is either "Connection closed" for clean disconnections or + "Connection closed: <error>" for unclean disconnections. This is an + alternative to o_stream_get_error(), which is preferred to be used when + logging errors about client connections. */ +const char *o_stream_get_disconnect_reason(struct ostream *stream); + +/* Close this stream (but not its parents) and unreference it. */ +void o_stream_destroy(struct ostream **stream); +/* Reference counting. References start from 1, so calling o_stream_unref() + destroys the stream if o_stream_ref() is never used. */ +void o_stream_ref(struct ostream *stream); +/* Unreferences the stream and sets stream pointer to NULL. */ +void o_stream_unref(struct ostream **stream); +/* Call the given callback function when stream is destroyed. */ +void o_stream_add_destroy_callback(struct ostream *stream, + ostream_callback_t *callback, void *context) + ATTR_NULL(3); +#define o_stream_add_destroy_callback(stream, callback, context) \ + o_stream_add_destroy_callback(stream - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (ostream_callback_t *)callback, context) +/* Remove the destroy callback. */ +void o_stream_remove_destroy_callback(struct ostream *stream, + void (*callback)()); + +/* Mark the stream and all of its parent streams closed. Nothing will be + sent after this call. When using ostreams that require writing a trailer, + o_stream_finish() must be used before the stream is closed. When ostream + is destroyed, it's also closed but its parents aren't. + + Closing the ostream (also via destroy) will first flush the ostream, and + afterwards requires one of: a) stream has failed, b) there is no more + buffered data, c) o_stream_set_no_error_handling() has been called. */ +void o_stream_close(struct ostream *stream); + +/* Set IO_WRITE callback. Default will just try to flush the output and + finishes when the buffer is empty. */ +void o_stream_set_flush_callback(struct ostream *stream, + stream_flush_callback_t *callback, + void *context) ATTR_NULL(3); +#define o_stream_set_flush_callback(stream, callback, context) \ + o_stream_set_flush_callback(stream - \ + CALLBACK_TYPECHECK(callback, int (*)(typeof(context))), \ + (stream_flush_callback_t *)callback, context) +void o_stream_unset_flush_callback(struct ostream *stream); +/* Change the maximum size for stream's output buffer to grow. */ +void o_stream_set_max_buffer_size(struct ostream *stream, size_t max_size); +/* Returns the current max. buffer size. */ +size_t o_stream_get_max_buffer_size(struct ostream *stream); + +/* Delays sending as far as possible, writing only full buffers. Also sets + TCP_CORK on if supported. */ +void o_stream_cork(struct ostream *stream); +/* Try to flush the buffer by calling o_stream_flush() and remove TCP_CORK. + Note that after this o_stream_flush() must be called, unless the stream + ignores errors. */ +void o_stream_uncork(struct ostream *stream); +bool o_stream_is_corked(struct ostream *stream); +/* Try to flush the output stream. If o_stream_nsend*() had been used and + the stream had overflown, return error. Returns 1 if all data is sent, + 0 there's still buffered data, -1 if error. */ +int o_stream_flush(struct ostream *stream); +/* Wrapper to easily both uncork and flush. */ +static inline int o_stream_uncork_flush(struct ostream *stream) +{ + o_stream_uncork(stream); + return o_stream_flush(stream); +} + +/* Set "flush pending" state of stream. If set, the flush callback is called + when more data is allowed to be sent, even if the buffer itself is empty. + Note that if the stream is corked, the flush callback won't be called until + the stream is first uncorked. */ +void o_stream_set_flush_pending(struct ostream *stream, bool set); +/* Returns the number of bytes currently in all the pending write buffers of + this ostream, including its parent streams. This function is commonly used + by callers to determine when they've filled up the ostream so they can stop + writing to it. Because of this, the return value shouldn't include buffers + that are expected to be filled up before they send anything to their parent + stream. Otherwise the callers may stop writing to the stream too early and + hang. Such an example could be a compression ostream that won't send + anything to its parent stream before an internal compression buffer is + full. */ +size_t o_stream_get_buffer_used_size(const struct ostream *stream) ATTR_PURE; +/* Returns the (minimum) number of bytes we can still write without failing. + This is commonly used by callers to find out how many bytes they're + guaranteed to be able to send, and then generate that much data and send + it. */ +size_t o_stream_get_buffer_avail_size(const struct ostream *stream) ATTR_PURE; + +/* Seek to specified position from beginning of file. This works only for + files. Returns 1 if successful, -1 if error. */ +int o_stream_seek(struct ostream *stream, uoff_t offset); +/* Returns number of bytes sent, -1 = error */ +ssize_t o_stream_send(struct ostream *stream, const void *data, size_t size) + ATTR_WARN_UNUSED_RESULT; +ssize_t o_stream_sendv(struct ostream *stream, const struct const_iovec *iov, + unsigned int iov_count) ATTR_WARN_UNUSED_RESULT; +ssize_t o_stream_send_str(struct ostream *stream, const char *str) + ATTR_WARN_UNUSED_RESULT; +/* Send with delayed error handling. o_stream_flush() or + o_stream_ignore_last_errors() must be called after these functions before + the stream is destroyed. If any of the data can't be sent due to stream's + buffer getting full, all further nsends are ignores and o_stream_flush() + will fail. */ +void o_stream_nsend(struct ostream *stream, const void *data, size_t size); +void o_stream_nsendv(struct ostream *stream, const struct const_iovec *iov, + unsigned int iov_count); +void o_stream_nsend_str(struct ostream *stream, const char *str); +/* Mark the ostream as finished and flush it. If the ostream has a footer, + it's written here. Any further write attempts to the ostream will + assert-crash. Returns the same as o_stream_flush(). Afterwards any calls to + this function are identical to o_stream_flush(). */ +int o_stream_finish(struct ostream *stream); +/* Specify whether calling o_stream_finish() will cause the parent stream to + be finished as well. The default is yes. */ +void o_stream_set_finish_also_parent(struct ostream *stream, bool set); +/* Specify whether calling o_stream_finish() on a child stream will cause + this stream to be finished as well. The default is yes. */ +void o_stream_set_finish_via_child(struct ostream *stream, bool set); +/* Marks the stream's error handling as completed to avoid i_panic() on + destroy. */ +void o_stream_ignore_last_errors(struct ostream *stream); +/* Abort writing to the ostream, also marking any previous error handling as + completed. If the stream hasn't already failed, sets the stream_errno=EPIPE. + This is necessary when aborting write to streams that require finishing. */ +void o_stream_abort(struct ostream *stream); +/* If error handling is disabled, the i_panic() on destroy is never called. + This function can be called immediately after the stream is created. + When creating wrapper streams, they copy this behavior from the parent + stream. */ +void o_stream_set_no_error_handling(struct ostream *stream, bool set); +/* Send all of the instream to outstream. + + On non-failure instream is skips over all data written to outstream. + This means that the number of bytes written to outstream is always equal to + the number of bytes skipped in instream. + + It's also possible to use this function to copy data within same file + descriptor, even if the source and destination overlaps. If the file must + be grown, you have to do it manually before calling this function. */ +enum ostream_send_istream_result ATTR_WARN_UNUSED_RESULT +o_stream_send_istream(struct ostream *outstream, struct istream *instream); +/* Same as o_stream_send_istream(), but assume that reads and writes will + succeed. If not, o_stream_flush() will fail with the correct error + message (even istream's). */ +void o_stream_nsend_istream(struct ostream *outstream, struct istream *instream); + +/* Write data to specified offset. Returns 0 if successful, -1 if error. */ +int o_stream_pwrite(struct ostream *stream, const void *data, size_t size, + uoff_t offset); + +/* Return the last timestamp when something was successfully sent to the + ostream's internal buffers (no guarantees that anything was sent further). + The timestamp is 0 if nothing has ever been written. */ +void o_stream_get_last_write_time(struct ostream *stream, struct timeval *tv_r); + +/* If there are any I/O loop items associated with the stream, move all of + them to provided/current ioloop. */ +void o_stream_switch_ioloop_to(struct ostream *stream, struct ioloop *ioloop); +void o_stream_switch_ioloop(struct ostream *stream); + +#endif diff --git a/src/lib/path-util.c b/src/lib/path-util.c new file mode 100644 index 0000000..90e1e46 --- /dev/null +++ b/src/lib/path-util.c @@ -0,0 +1,398 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "path-util.h" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +#define PATH_UTIL_MAX_PATH 8*1024 +#define PATH_UTIL_MAX_SYMLINKS 80 + +static int t_getcwd_noalloc(char **dir_r, size_t *asize_r, + const char **error_r) ATTR_NULL(2) +{ + /* @UNSAFE */ + char *dir; + size_t asize = 128; + + dir = t_buffer_get(asize); + while (getcwd(dir, asize) == NULL) { + if (errno != ERANGE) { + *error_r = t_strdup_printf("getcwd() failed: %m"); + return -1; + } + asize = nearest_power(asize+1); + dir = t_buffer_get(asize); + } + if (asize_r != NULL) + *asize_r = asize; + *dir_r = dir; + return 0; +} + +static int path_normalize(const char *path, bool resolve_links, + const char **npath_r, const char **error_r) +{ + /* @UNSAFE */ + unsigned int link_count = 0; + char *npath, *npath_pos; + const char *p; + size_t asize; + + i_assert(path != NULL); + i_assert(npath_r != NULL); + i_assert(error_r != NULL); + + if (path[0] != '/') { + /* relative; initialize npath with current directory */ + if (t_getcwd_noalloc(&npath, &asize, error_r) < 0) + return -1; + npath_pos = npath + strlen(npath); + i_assert(npath[0] == '/'); + } else { + /* absolute; initialize npath with root */ + asize = 128; + npath = t_buffer_get(asize); + npath[0] = '/'; + npath_pos = npath + 1; + } + + p = path; + while (*p != '\0') { + struct stat st; + ptrdiff_t seglen; + const char *segend; + + /* skip duplicate slashes */ + while (*p == '/') + p++; + + /* find end of path segment */ + for (segend = p; *segend != '\0' && *segend != '/'; segend++); + + if (segend == p) + break; /* '\0' */ + seglen = segend - p; + if (seglen == 1 && p[0] == '.') { + /* a reference to this segment; nothing to do */ + } else if (seglen == 2 && p[0] == '.' && p[1] == '.') { + /* a reference to parent segment; back up to previous + * slash */ + i_assert(npath_pos >= npath); + if ((npath_pos - npath) > 1) { + if (*(npath_pos-1) == '/') + npath_pos--; + for (; *(npath_pos-1) != '/'; npath_pos--); + } + } else { + /* allocate space if necessary */ + i_assert(npath_pos >= npath); + if ((size_t)((npath_pos - npath) + seglen + 1) >= asize) { + ptrdiff_t npath_offset = npath_pos - npath; + asize = nearest_power(npath_offset + seglen + 2); + npath = t_buffer_reget(npath, asize); + npath_pos = npath + npath_offset; + } + + /* make sure npath now ends in slash */ + i_assert(npath_pos > npath); + if (*(npath_pos-1) != '/') { + i_assert((size_t)((npath_pos - npath) + 1) < asize); + *(npath_pos++) = '/'; + } + + /* copy segment to normalized path */ + i_assert(npath_pos >= npath); + i_assert((size_t)((npath_pos - npath) + seglen) < asize); + memmove(npath_pos, p, seglen); + npath_pos += seglen; + } + + if (resolve_links) { + /* stat path up to here (segend points to tail) */ + *npath_pos = '\0'; + if (lstat(npath, &st) < 0) { + *error_r = t_strdup_printf("lstat() failed: %m"); + return -1; + } + + if (S_ISLNK (st.st_mode)) { + /* symlink */ + char *npath_link; + size_t lsize = 128, tlen = strlen(segend), espace; + size_t ltlen = (link_count == 0 ? 0 : tlen); + ssize_t ret; + + /* limit link dereferences */ + if (++link_count > PATH_UTIL_MAX_SYMLINKS) { + errno = ELOOP; + *error_r = "Too many symlink dereferences"; + return -1; + } + + /* allocate space for preserving tail of previous symlink and + first attempt at reading symlink with room for the tail + + buffer will look like this: + [npath][0][preserved tail][link buffer][room for tail][0] + */ + espace = ltlen + tlen + 2; + i_assert(npath_pos >= npath); + if ((size_t)((npath_pos - npath) + espace + lsize) >= asize) { + ptrdiff_t npath_offset = npath_pos - npath; + asize = nearest_power((npath_offset + espace + lsize) + 1); + lsize = asize - (npath_offset + espace); + npath = t_buffer_reget(npath, asize); + npath_pos = npath + npath_offset; + } + + if (ltlen > 0) { + /* preserve tail just after end of npath */ + i_assert(npath_pos >= npath); + i_assert((size_t)((npath_pos + 1 - npath) + ltlen) < asize); + memmove(npath_pos + 1, segend, ltlen); + } + + /* read the symlink after the preserved tail */ + for (;;) { + npath_link = (npath_pos + 1) + ltlen; + + i_assert(npath_link >= npath_pos); + i_assert((size_t)((npath_link - npath) + lsize) < asize); + + /* attempt to read the link */ + if ((ret=readlink(npath, npath_link, lsize)) < 0) { + *error_r = t_strdup_printf("readlink() failed: %m"); + return -1; + } + if ((size_t)ret < lsize) { + /* POSIX doesn't guarantee the presence of a NIL */ + npath_link[ret] = '\0'; + break; + } + + /* sum of new symlink content length + * and path tail length may not + exceed maximum */ + if ((size_t)(ret + tlen) >= PATH_UTIL_MAX_PATH) { + errno = ENAMETOOLONG; + *error_r = "Resulting path is too long"; + return -1; + } + + /* try again with bigger buffer, + we need to allocate more space as well if lsize == ret, + because the returned link may have gotten truncated */ + espace = ltlen + tlen + 2; + i_assert(npath_pos >= npath); + if ((size_t)((npath_pos - npath) + espace + lsize) >= asize || + lsize == (size_t)ret) { + ptrdiff_t npath_offset = npath_pos - npath; + asize = nearest_power((npath_offset + espace + lsize) + 1); + lsize = asize - (npath_offset + espace); + npath = t_buffer_reget(npath, asize); + npath_pos = npath + npath_offset; + } + } + + /* add tail of previous path at end of symlink */ + i_assert(npath_link >= npath); + if (ltlen > 0) { + i_assert(npath_pos >= npath); + i_assert((size_t)((npath_pos - npath) + 1 + tlen) < asize); + i_assert((size_t)((npath_link - npath) + ret + tlen) < asize); + memcpy(npath_link + ret, npath_pos + 1, tlen); + } else { + i_assert((size_t)((npath_link - npath) + ret + tlen) < asize); + memcpy(npath_link + ret, segend, tlen); + } + *(npath_link+ret+tlen) = '\0'; + + /* use as new source path */ + path = segend = npath_link; + + if (path[0] == '/') { + /* absolute symlink; start over at root */ + npath_pos = npath + 1; + } else { + /* relative symlink; back up to previous segment */ + i_assert(npath_pos >= npath); + if ((npath_pos - npath) > 1) { + if (*(npath_pos-1) == '/') + npath_pos--; + for (; *(npath_pos-1) != '/'; npath_pos--); + } + } + + } else if (*segend != '\0' && !S_ISDIR (st.st_mode)) { + /* not last segment, but not a directory either */ + errno = ENOTDIR; + *error_r = t_strdup_printf("Not a directory: %s", npath); + return -1; + } + } + + p = segend; + } + + i_assert(npath_pos >= npath); + i_assert((size_t)(npath_pos - npath) < asize); + + /* remove any trailing slash */ + if ((npath_pos - npath) > 1 && *(npath_pos-1) == '/') + npath_pos--; + *npath_pos = '\0'; + + t_buffer_alloc(npath_pos - npath + 1); + *npath_r = npath; + return 0; +} + +int t_normpath(const char *path, const char **npath_r, const char **error_r) +{ + return path_normalize(path, FALSE, npath_r, error_r); +} + +int t_normpath_to(const char *path, const char *root, const char **npath_r, + const char **error_r) +{ + i_assert(path != NULL); + i_assert(root != NULL); + i_assert(npath_r != NULL); + + if (*path == '/') + return t_normpath(path, npath_r, error_r); + + return t_normpath(t_strconcat(root, "/", path, NULL), npath_r, error_r); +} + +int t_realpath(const char *path, const char **npath_r, const char **error_r) +{ + return path_normalize(path, TRUE, npath_r, error_r); +} + +int t_realpath_to(const char *path, const char *root, const char **npath_r, + const char **error_r) +{ + i_assert(path != NULL); + i_assert(root != NULL); + i_assert(npath_r != NULL); + + if (*path == '/') + return t_realpath(path, npath_r, error_r); + + return t_realpath(t_strconcat(root, "/", path, NULL), npath_r, error_r); +} + +int t_abspath(const char *path, const char **abspath_r, const char **error_r) +{ + i_assert(path != NULL); + i_assert(abspath_r != NULL); + i_assert(error_r != NULL); + + if (*path == '/') { + *abspath_r = path; + return 0; + } + + const char *dir, *error; + if (t_get_working_dir(&dir, &error) < 0) { + *error_r = t_strconcat("Failed to get working directory: ", + error, NULL); + return -1; + } + *abspath_r = t_strconcat(dir, "/", path, NULL); + return 0; +} + +const char *t_abspath_to(const char *path, const char *root) +{ + i_assert(path != NULL); + i_assert(root != NULL); + + if (*path == '/') + return path; + + return t_strconcat(root, "/", path, NULL); +} + +int t_get_working_dir(const char **dir_r, const char **error_r) +{ + char *dir; + + i_assert(dir_r != NULL); + i_assert(error_r != NULL); + if (t_getcwd_noalloc(&dir, NULL, error_r) < 0) + return -1; + + t_buffer_alloc(strlen(dir) + 1); + *dir_r = dir; + return 0; +} + +int t_readlink(const char *path, const char **dest_r, const char **error_r) +{ + i_assert(error_r != NULL); + + /* @UNSAFE */ + ssize_t ret; + char *dest; + size_t size = 128; + + dest = t_buffer_get(size); + while ((ret = readlink(path, dest, size)) >= (ssize_t)size) { + size = nearest_power(size+1); + dest = t_buffer_get(size); + } + if (ret < 0) { + *error_r = t_strdup_printf("readlink() failed: %m"); + return -1; + } + + dest[ret] = '\0'; + t_buffer_alloc(ret + 1); + *dest_r = dest; + return 0; +} + +bool t_binary_abspath(const char **binpath, const char **error_r) +{ + const char *path_env, *const *paths; + string_t *path; + + if (**binpath == '/') { + /* already have absolute path */ + return TRUE; + } else if (strchr(*binpath, '/') != NULL) { + /* relative to current directory */ + const char *error; + if (t_abspath(*binpath, binpath, &error) < 0) { + *error_r = t_strdup_printf("t_abspath(%s) failed: %s", + *binpath, error); + return FALSE; + } + return TRUE; + } else if ((path_env = getenv("PATH")) != NULL) { + /* we have to find our executable from path */ + path = t_str_new(256); + paths = t_strsplit(path_env, ":"); + for (; *paths != NULL; paths++) { + str_append(path, *paths); + str_append_c(path, '/'); + str_append(path, *binpath); + if (access(str_c(path), X_OK) == 0) { + *binpath = str_c(path); + return TRUE; + } + str_truncate(path, 0); + } + *error_r = "Could not find the wanted executable from PATH"; + return FALSE; + } else { + *error_r = "PATH environment variable undefined"; + return FALSE; + } +} diff --git a/src/lib/path-util.h b/src/lib/path-util.h new file mode 100644 index 0000000..8492cf3 --- /dev/null +++ b/src/lib/path-util.h @@ -0,0 +1,69 @@ +#ifndef PATH_UTIL_H +#define PATH_UTIL_H + +/* Returns path as the normalized absolute path, which means that './' + * and '../' components are resolved, and that duplicate and trailing + * slashes are removed. If it's not already the absolute path, it's + * assumed to be relative to the current working directory. + * + * NOTE: Be careful with this function. The resolution of '../' components + * with the parent component as if it were a normal directory is not valid + * if the path contains symbolic links. + * + * Returns 0 on success, and -1 on failure. errno and error_r are set on + * failure, and error_r cannot be NULL. + */ +int t_normpath(const char *path, const char **npath_r, const char **error_r); +/* Like t_normpath(), but path is relative to given root. */ +int t_normpath_to(const char *path, const char *root, const char **npath_r, + const char **error_r); + +/* Returns path as the real normalized absolute path, which means that all + * symbolic links in the path are resolved, that './' and '../' components + * are resolved, and that duplicate and trailing slashes are removed. If it's + * not already the absolute path, it's assumed to be relative to the current + * working directory. + * + * NOTE: This function calls stat() for each path component and more when + * there are symbolic links (just like POSIX realpath()). + * + * Returns 0 on success, and -1 on failure. errno and error_r are set on + * failure, and error_r cannot be NULL. + */ +int t_realpath(const char *path, const char **npath_r, const char **error_r); +/* Like t_realpath(), but path is relative to given root. */ +int t_realpath_to(const char *path, const char *root, const char **npath_r, + const char **error_r); + +/* Returns path as absolute path. If it's not already absolute path, + * it's assumed to be relative to current working directory. + * + * In the t_abspath functions, the returned paths are not normalized. This + * means that './' and '../' are not resolved, but they left in the returned + * path as given in the parameters. Symbolic links are not resolved either. + * + * Returns 0 on success, and -1 on failure. error_r is set on failure, and + * cannot be NULL. + */ +int t_abspath(const char *path, const char **abspath_r, const char **error_r); +/* Like t_abspath(), but path is relative to given root. */ +const char *t_abspath_to(const char *path, const char *root); + +/* Get current working directory allocated from data stack. Returns 0 on + * success and 1 on failure. error_r is set on failure and cannot be NULL. */ +int t_get_working_dir(const char **dir_r, const char **error_r); + +/* Get symlink destination allocated from data stack. Returns 0 on success and + * -1 on failure. error_r is set on failure and cannot be NULL. */ +int t_readlink(const char *path, const char **dest_r, const char **error_r); + +/* Update binpath to be absolute: + * a) begins with '/' -> no change + * b) contains '/' -> assume relative to working directory + * c) set to first executable that's found from $PATH + * + * error_r is set on failure, and cannot be NULL. + */ +bool t_binary_abspath(const char **binpath, const char **error_r); + +#endif diff --git a/src/lib/pkcs5.c b/src/lib/pkcs5.c new file mode 100644 index 0000000..e035d47 --- /dev/null +++ b/src/lib/pkcs5.c @@ -0,0 +1,97 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "hash-method.h" +#include "hmac.h" +#include "pkcs5.h" + +#include <stdint.h> +#include <arpa/inet.h> + +static +int pkcs5_pbkdf1(const struct hash_method *hash, + const unsigned char *password, size_t password_len, + const unsigned char *salt, size_t salt_len, + unsigned int iter, uint32_t length, + buffer_t *result) +{ + if (length < 1 || + length > hash->digest_size) return -1; + if (iter < 1) return -1; + + unsigned char dk[hash->digest_size]; + unsigned char ctx[hash->context_size]; + + hash->init(ctx); + hash->loop(ctx, password, password_len); + hash->loop(ctx, salt, salt_len); + hash->result(ctx, dk); + length--; + + for(;length>0;length--) { + hash->init(ctx); + hash->loop(ctx, dk, hash->digest_size); + hash->result(ctx, dk); + } + + buffer_append(result, dk, hash->digest_size); + + return 0; +} + +static +int pkcs5_pbkdf2(const struct hash_method *hash, + const unsigned char *password, size_t password_len, + const unsigned char *salt, size_t salt_len, + unsigned int iter, uint32_t length, + buffer_t *result) +{ + if (length < 1 || iter < 1) return -1; + + size_t l = (length + hash->digest_size - 1)/hash->digest_size; /* same as ceil(length/hash->digest_size) */ + unsigned char dk[l * hash->digest_size]; + unsigned char *block; + struct hmac_context hctx; + unsigned int c,i,t; + unsigned char U_c[hash->digest_size]; + + for(t = 0; t < l; t++) { + block = &(dk[t*hash->digest_size]); + /* U_1 = PRF(Password, Salt|| INT_BE32(Block_Number)) */ + c = htonl(t+1); + hmac_init(&hctx, password, password_len, hash); + hmac_update(&hctx, salt, salt_len); + hmac_update(&hctx, &c, sizeof(c)); + hmac_final(&hctx, U_c); + /* block = U_1 ^ .. ^ U_iter */ + memcpy(block, U_c, hash->digest_size); + /* U_c = PRF(Password, U_c-1) */ + for(c = 1; c < iter; c++) { + hmac_init(&hctx, password, password_len, hash); + hmac_update(&hctx, U_c, hash->digest_size); + hmac_final(&hctx, U_c); + for(i = 0; i < hash->digest_size; i++) + block[i] ^= U_c[i]; + } + } + + buffer_append(result, dk, length); + + return 0; +} + +int pkcs5_pbkdf(enum pkcs5_pbkdf_mode mode, const struct hash_method *hash, + const unsigned char *password, size_t password_len, + const unsigned char *salt, size_t salt_len, + unsigned int iterations, uint32_t dk_len, + buffer_t *result) +{ + if (mode == PKCS5_PBKDF1) + return pkcs5_pbkdf1(hash,password,password_len, + salt,salt_len,iterations,dk_len,result); + else if (mode == PKCS5_PBKDF2) + return pkcs5_pbkdf2(hash,password,password_len, + salt,salt_len,iterations,dk_len,result); + i_unreached(); +} diff --git a/src/lib/pkcs5.h b/src/lib/pkcs5.h new file mode 100644 index 0000000..5cc0650 --- /dev/null +++ b/src/lib/pkcs5.h @@ -0,0 +1,35 @@ +#ifndef PKCS5_H +#define PKCS5_H 1 + +enum pkcs5_pbkdf_mode { + PKCS5_PBKDF1, + PKCS5_PBKDF2 +}; + +/* + + mode - v1.0 or v2.0 + hash - hash_method_lookup return value + password - private password for generation + password_len - length of password in octets + salt - salt for generation + salt_len - length of salt in octets + iterations - number of iterations to hash (use at least 1000, a very large number => very very slow) + dk_len - number of bytes to return from derived key + result - buffer_t to hold the result, either use dynamic or make sure it fits dk_len + + non-zero return value indicates that either iterations was less than 1 or dk_len was too large + + Sample code: + + buffer_t *result = t_buffer_create(256); + if (pkcs5_pbkdf(PKCS5_PBKDF2, hash_method_lookup("sha256"), "password", 8, "salt", 4, 4096, 256, result) != 0) { // error } + +*/ + +int pkcs5_pbkdf(enum pkcs5_pbkdf_mode mode, const struct hash_method *hash, + const unsigned char *password, size_t password_len, + const unsigned char *salt, size_t salt_len, + unsigned int iterations, uint32_t dk_len, + buffer_t *result); +#endif diff --git a/src/lib/primes.c b/src/lib/primes.c new file mode 100644 index 0000000..cb4a5e2 --- /dev/null +++ b/src/lib/primes.c @@ -0,0 +1,48 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "primes.h" + +static const unsigned int primes[] = { +#define PRIME_SKIP_COUNT 3 + 17, + 37, + 67, + 131, + 257, /* next from 2^8 */ + 521, + 1031, + 2053, + 4099, + 8209, + 16411, + 32771, + 65537, /* next from 2^16 */ + 131101, + 262147, + 524309, + 1048583, + 2097169, + 4194319, + 8388617, + 16777259, /* next from 2^24 */ + 33554467, + 67108879, + 134217757, + 268435459, + 536870923, + 1073741827, + 2147483659U, + 4294967291U /* previous from 2^32 */ +}; + +unsigned int primes_closest(unsigned int num) +{ + unsigned int i; + + for (i = 31; i > PRIME_SKIP_COUNT; i--) { + if ((num & (1U << i)) != 0) + return primes[i - PRIME_SKIP_COUNT]; + } + return primes[0]; +} diff --git a/src/lib/primes.h b/src/lib/primes.h new file mode 100644 index 0000000..8153d6d --- /dev/null +++ b/src/lib/primes.h @@ -0,0 +1,8 @@ +#ifndef PRIMES_H +#define PRIMES_H + +/* Returns a prime close to specified number, or the number itself if it's + a prime. Note that the returned value may be smaller than requested! */ +unsigned int primes_closest(unsigned int num) ATTR_CONST; + +#endif diff --git a/src/lib/printf-format-fix.c b/src/lib/printf-format-fix.c new file mode 100644 index 0000000..001e7b0 --- /dev/null +++ b/src/lib/printf-format-fix.c @@ -0,0 +1,192 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "printf-format-fix.h" + +static const char * +fix_format_real(const char *fmt, const char *p, size_t *len_r) +{ + const char *errstr; + char *buf; + size_t len1, len2, len3; + + i_assert((size_t)(p - fmt) < INT_MAX); + i_assert(p[0] == '%' && p[1] == 'm'); + + errstr = strerror(errno); + + /* we'll assume that there's only one %m in the format string. + this simplifies the code and there's really no good reason to have + it multiple times. Callers can trap this case themselves. */ + len1 = p - fmt; + len2 = strlen(errstr); + len3 = strlen(p + 2); + + /* @UNSAFE */ + buf = t_buffer_get(len1 + len2 + len3 + 1); + memcpy(buf, fmt, len1); + memcpy(buf + len1, errstr, len2); + memcpy(buf + len1 + len2, p + 2, len3 + 1); + + *len_r = len1 + len2 + len3; + return buf; +} + +static bool verify_length(const char **p) +{ + if (**p == '*') { + /* We don't bother supporting "*m$" - it's not used + anywhere and seems a bit dangerous. */ + *p += 1; + } else if (**p >= '0' && **p <= '9') { + /* Limit to 4 digits - we'll never want more than that. + Some implementations might not handle long digits + correctly, or maybe even could be used for DoS due + to using too much CPU. If you want to express '99' + as '00099', then you lose in this function. */ + unsigned int i = 0; + do { + *p += 1; + if (++i > 4) + return FALSE; + } while (**p >= '0' && **p <= '9'); + } + return TRUE; +} + +static const char * +printf_format_fix_noalloc(const char *format, size_t *len_r) +{ + /* NOTE: This function is overly strict in what it accepts. Some + format strings that are valid (and safe) in C99 will cause a panic + here. This is because we don't really need to support the weirdest + special cases, and we're also being extra careful not to pass + anything to the underlying libc printf, which might treat the string + differently than us and unexpectedly handling it as %n. For example + "%**%n" with glibc. */ + + /* Allow only the standard C99 flags. There are also <'> and <I> flags, + but we don't really need them. And at worst if they're not supported + by the underlying printf, they could potentially be used to work + around our restrictions. */ + const char printf_flags[] = "#0- +"; + /* As a tiny optimization keep the most commonly used conversion + specifiers first, so strchr() stops early. */ + static const char *printf_specifiers = "sudcixXpoeEfFgGaA"; + const char *ret, *p, *p2; + char *flag; + + p = ret = format; + while ((p2 = strchr(p, '%')) != NULL) { + const unsigned int start_pos = p2 - format; + + p = p2+1; + if (*p == '%') { + /* we'll be strict and allow %% only when there are no + optinal flags or modifiers. */ + p++; + continue; + } + /* 1) zero or more flags. We'll add a further restriction that + each flag can be used only once, since there's no need to + use them more than once, and some implementations might + add their own limits. */ + bool printf_flags_seen[N_ELEMENTS(printf_flags)] = { FALSE, }; + while (*p != '\0' && + (flag = strchr(printf_flags, *p)) != NULL) { + unsigned int flag_idx = flag - printf_flags; + + if (printf_flags_seen[flag_idx]) { + i_panic("Duplicate %% flag '%c' starting at #%u in '%s'", + *p, start_pos, format); + } + printf_flags_seen[flag_idx] = TRUE; + p++; + } + + /* 2) Optional minimum field width */ + if (!verify_length(&p)) { + i_panic("Too large minimum field width starting at #%u in '%s'", + start_pos, format); + } + + /* 3) Optional precision */ + if (*p == '.') { + p++; + if (!verify_length(&p)) { + i_panic("Too large precision starting at #%u in '%s'", + start_pos, format); + } + } + + /* 4) Optional length modifier */ + switch (*p) { + case 'h': + if (*++p == 'h') + p++; + break; + case 'l': + if (*++p == 'l') + p++; + break; + case 'L': + case 'j': + case 'z': + case 't': + p++; + break; + } + + /* 5) conversion specifier */ + if (*p == '\0' || strchr(printf_specifiers, *p) == NULL) { + switch (*p) { + case 'n': + i_panic("%%n modifier used"); + case 'm': + if (ret != format) + i_panic("%%m used twice"); + ret = fix_format_real(format, p-1, len_r); + break; + case '\0': + i_panic("Missing %% specifier starting at #%u in '%s'", + start_pos, format); + default: + i_panic("Unsupported 0x%02x specifier starting at #%u in '%s'", + *p, start_pos, format); + } + } + p++; + } + + if (ret == format) + *len_r = p - format + strlen(p); + return ret; +} + +const char *printf_format_fix_get_len(const char *format, size_t *len_r) +{ + const char *ret; + + ret = printf_format_fix_noalloc(format, len_r); + if (ret != format) + t_buffer_alloc(*len_r + 1); + return ret; +} + +const char *printf_format_fix(const char *format) +{ + const char *ret; + size_t len; + + ret = printf_format_fix_noalloc(format, &len); + if (ret != format) + t_buffer_alloc(len + 1); + return ret; +} + +const char *printf_format_fix_unsafe(const char *format) +{ + size_t len; + + return printf_format_fix_noalloc(format, &len); +} diff --git a/src/lib/printf-format-fix.h b/src/lib/printf-format-fix.h new file mode 100644 index 0000000..5691a07 --- /dev/null +++ b/src/lib/printf-format-fix.h @@ -0,0 +1,15 @@ +#ifndef PRINTF_FORMAT_FIX_H +#define PRINTF_FORMAT_FIX_H + +/* Replaces %m in format with strerror(errno) and panics if %n modifier is + used. If the format string was modified, it's returned from data stack. */ +const char *printf_format_fix(const char *format) ATTR_FORMAT_ARG(1); +/* Like printf_format_fix(), except return also the format string's length. */ +const char *printf_format_fix_get_len(const char *format, size_t *len_r) + ATTR_FORMAT_ARG(1); +/* Like printf_format_fix(), except the format string is written to data + stack without actually allocating it. Data stack must not be used until + format string is no longer needed. */ +const char *printf_format_fix_unsafe(const char *format) ATTR_FORMAT_ARG(1); + +#endif diff --git a/src/lib/priorityq.c b/src/lib/priorityq.c new file mode 100644 index 0000000..e532de8 --- /dev/null +++ b/src/lib/priorityq.c @@ -0,0 +1,171 @@ +/* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "priorityq.h" + +/* Macros for moving inside an array implementation of binary tree where + [0] is the root. */ +#define PARENT_IDX(idx) \ + (((idx) - 1) / 2) +#define LEFT_CHILD_IDX(idx) \ + ((idx) * 2 + 1) +#define RIGHT_CHILD_IDX(idx) \ + ((idx) * 2 + 2) + +struct priorityq { + priorityq_cmp_callback_t *cmp_callback; + + ARRAY(struct priorityq_item *) items; +}; + +struct priorityq * +priorityq_init(priorityq_cmp_callback_t *cmp_callback, unsigned int init_size) +{ + struct priorityq *pq; + + pq = i_new(struct priorityq, 1); + pq->cmp_callback = cmp_callback; + i_array_init(&pq->items, init_size); + return pq; +} + +void priorityq_deinit(struct priorityq **_pq) +{ + struct priorityq *pq = *_pq; + + *_pq = NULL; + array_free(&pq->items); + i_free(pq); +} + +unsigned int priorityq_count(const struct priorityq *pq) +{ + return array_count(&pq->items); +} + +static void heap_items_swap(struct priorityq_item **items, + unsigned int idx1, unsigned int idx2) +{ + struct priorityq_item *tmp; + + /* swap the item indexes */ + i_assert(items[idx1]->idx == idx1); + i_assert(items[idx2]->idx == idx2); + + items[idx1]->idx = idx2; + items[idx2]->idx = idx1; + + /* swap the item pointers */ + tmp = items[idx1]; + items[idx1] = items[idx2]; + items[idx2] = tmp; +} + +static unsigned int +heap_item_bubble_up(struct priorityq *pq, unsigned int idx) +{ + struct priorityq_item **items; + unsigned int parent_idx, count; + + items = array_get_modifiable(&pq->items, &count); + while (idx > 0) { + parent_idx = PARENT_IDX(idx); + + i_assert(idx < count); + if (pq->cmp_callback(items[idx], items[parent_idx]) >= 0) + break; + + /* wrong order - swap */ + heap_items_swap(items, idx, parent_idx); + idx = parent_idx; + } + return idx; +} + +static void heap_item_bubble_down(struct priorityq *pq, unsigned int idx) +{ + struct priorityq_item **items; + unsigned int left_idx, right_idx, min_child_idx, count; + + items = array_get_modifiable(&pq->items, &count); + while ((left_idx = LEFT_CHILD_IDX(idx)) < count) { + right_idx = RIGHT_CHILD_IDX(idx); + if (right_idx >= count || + pq->cmp_callback(items[left_idx], items[right_idx]) < 0) + min_child_idx = left_idx; + else + min_child_idx = right_idx; + + if (pq->cmp_callback(items[min_child_idx], items[idx]) >= 0) + break; + + /* wrong order - swap */ + heap_items_swap(items, idx, min_child_idx); + idx = min_child_idx; + } +} + +void priorityq_add(struct priorityq *pq, struct priorityq_item *item) +{ + item->idx = array_count(&pq->items); + array_push_back(&pq->items, &item); + (void)heap_item_bubble_up(pq, item->idx); +} + +static void priorityq_remove_idx(struct priorityq *pq, unsigned int idx) +{ + struct priorityq_item **items; + unsigned int count; + + items = array_get_modifiable(&pq->items, &count); + i_assert(idx < count); + + /* move last item over the removed one and fix the heap */ + count--; + heap_items_swap(items, idx, count); + array_delete(&pq->items, count, 1); + + if (count > 0 && idx != count) { + if (idx > 0) + idx = heap_item_bubble_up(pq, idx); + heap_item_bubble_down(pq, idx); + } +} + +void priorityq_remove(struct priorityq *pq, struct priorityq_item *item) +{ + priorityq_remove_idx(pq, item->idx); + item->idx = UINT_MAX; +} + +struct priorityq_item *priorityq_peek(struct priorityq *pq) +{ + struct priorityq_item *const *items; + + if (array_count(&pq->items) == 0) + return NULL; + + items = array_front(&pq->items); + return items[0]; +} + +struct priorityq_item *priorityq_pop(struct priorityq *pq) +{ + struct priorityq_item *item; + + item = priorityq_peek(pq); + if (item != NULL) { + priorityq_remove_idx(pq, 0); + item->idx = UINT_MAX; + } + return item; +} + +struct priorityq_item *const *priorityq_items(struct priorityq *pq) +{ + if (array_count(&pq->items) == 0) + return NULL; + + return array_front(&pq->items); +} diff --git a/src/lib/priorityq.h b/src/lib/priorityq.h new file mode 100644 index 0000000..aa38914 --- /dev/null +++ b/src/lib/priorityq.h @@ -0,0 +1,39 @@ +#ifndef PRIORITYQ_H +#define PRIORITYQ_H + +/* Priority queue implementation using heap. The items you add to the queue + must begin with a struct priorityq_item. This is necessary for + priorityq_remove() to work fast. */ + +struct priorityq_item { + /* Current index in the queue array, updated automatically. */ + unsigned int idx; + /* [your own data] */ +}; + +/* Returns <0, 0 or >0 */ +typedef int priorityq_cmp_callback_t(const void *p1, const void *p2); + +/* Create a new priority queue. Callback is used to compare added items. */ +struct priorityq * +priorityq_init(priorityq_cmp_callback_t *cmp_callback, unsigned int init_size); +void priorityq_deinit(struct priorityq **pq); + +/* Return number of items in the queue. */ +unsigned int priorityq_count(const struct priorityq *pq) ATTR_PURE; + +/* Add a new item to the queue. */ +void priorityq_add(struct priorityq *pq, struct priorityq_item *item); +/* Remove the specified item from the queue. */ +void priorityq_remove(struct priorityq *pq, struct priorityq_item *item); + +/* Return the item with the highest priority. Returns NULL if queue is empty. */ +struct priorityq_item *priorityq_peek(struct priorityq *pq); +/* Like priorityq_peek(), but also remove the returned item from the queue. */ +struct priorityq_item *priorityq_pop(struct priorityq *pq); +/* Returns array containing all the priorityq_items. Only the first item is + guaranteed to be the highest priority item, the rest can't be assumed to + be in any order. */ +struct priorityq_item *const *priorityq_items(struct priorityq *pq); + +#endif diff --git a/src/lib/process-stat.c b/src/lib/process-stat.c new file mode 100644 index 0000000..fae57c7 --- /dev/null +++ b/src/lib/process-stat.c @@ -0,0 +1,267 @@ +/* Copyright (c) 2008-2021 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "process-stat.h" +#include "time-util.h" +#include <limits.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/time.h> +#include <time.h> +#include <sys/resource.h> +#include <stdio.h> + +#define PROC_STAT_PATH "/proc/self/stat" +#define PROC_STATUS_PATH "/proc/self/status" +#define PROC_IO_PATH "/proc/self/io" + +static const uint64_t stat_undefined = 0xFFFFFFFFFFFFFFFF; + +struct key_val { + const char *key; + uint64_t *value; + unsigned int idx; +}; + +static int parse_field(const char *line, struct key_val *field) +{ + if (str_begins(line, field->key)) + return str_to_uint64(line + strlen(field->key), field->value); + return -1; +} + +static void buffer_parse(const char *buf, struct key_val *fields) +{ + const char *const *tmp; + tmp = t_strsplit(buf, "\n"); + unsigned int tmp_count = str_array_length(tmp); + for (; fields->key != NULL; fields++) { + if (fields->idx >= tmp_count || + parse_field(tmp[fields->idx], fields) < 0) + *fields->value = stat_undefined; + } +} + +static int open_fd(const char *path, struct event *event) +{ + int fd; + uid_t uid; + + fd = open(path, O_RDONLY); + + if (fd == -1 && errno == EACCES) { + uid = geteuid(); + /* kludge: if we're running with permissions temporarily + dropped, get them temporarily back so we can open + /proc/self/io. */ + if (seteuid(0) == 0) { + fd = open(path, O_RDONLY); + if (seteuid(uid) < 0) + i_fatal("seteuid(%s) failed", dec2str(uid)); + } + errno = EACCES; + } + if (fd == -1) { + if (errno == ENOENT || errno == EACCES) + e_debug(event, "open(%s) failed: %m", path); + else + e_error(event, "open(%s) failed: %m", path); + } + return fd; +} + +static int +read_file(int fd, const char *path, char *buf_r, size_t buf_size, struct event *event) +{ + ssize_t ret; + ret = read(fd, buf_r, buf_size); + if (ret <= 0) { + if (ret == -1) + e_error(event, "read(%s) failed: %m", path); + else + e_error(event, "read(%s) returned EOF", path); + } else if (ret == (ssize_t)buf_size) { + e_error(event, "%s is larger than expected", path); + buf_r[buf_size - 1] = '\0'; + } else { + buf_r[ret] = '\0'; + } + i_close_fd(&fd); + return ret <= 0 ? -1 : 0; +} + +static int parse_key_val_file(const char *path, + struct key_val *fields, + struct event *event) +{ + char buf[2048]; + int fd; + + fd = open_fd(path, event); + if (fd == -1 || read_file(fd, path, buf, sizeof(buf), event) < 0) { + for (; fields->key != NULL; fields++) + *fields->value = stat_undefined; + return -1; + } + buffer_parse(buf, fields); + return 0; +} + +static int parse_proc_io(struct process_stat *stat_r, struct event *event) +{ + struct key_val fields[] = { + { "rchar: ", &stat_r->rchar, 0 }, + { "wchar: ", &stat_r->wchar, 1 }, + { "syscr: ", &stat_r->syscr, 2 }, + { "syscw: ", &stat_r->syscw, 3 }, + { NULL, NULL, 0 }, + }; + if (stat_r->proc_io_failed || + parse_key_val_file(PROC_IO_PATH, fields, event) < 0) { + stat_r->proc_io_failed = TRUE; + return -1; + } + return 0; +} + +static int parse_proc_status(struct process_stat *stat_r, struct event *event) +{ + struct key_val fields [] = { + { "voluntary_ctxt_switches:\t", &stat_r->vol_cs, 53 }, + { "nonvoluntary_ctxt_switches:\t", &stat_r->invol_cs, 54 }, + { NULL, NULL, 0 }, + }; + if (stat_r->proc_status_failed || + parse_key_val_file(PROC_STATUS_PATH, fields, event) < 0) { + stat_r->proc_status_failed = TRUE; + return -1; + } + return 0; +} + +static int stat_get_rusage(struct process_stat *stat_r) +{ + struct rusage usage; + + if (getrusage(RUSAGE_SELF, &usage) < 0) + return -1; + stat_r->utime = timeval_to_usecs(&usage.ru_utime); + stat_r->stime = timeval_to_usecs(&usage.ru_stime); + stat_r->minor_faults = usage.ru_minflt; + stat_r->major_faults = usage.ru_majflt; + stat_r->vol_cs = usage.ru_nvcsw; + stat_r->invol_cs = usage.ru_nivcsw; + return 0; +} + +static int parse_stat_file(struct process_stat *stat_r, struct event *event) +{ + int fd = -1; + char buf[1024]; + unsigned int i; + const char *const *tmp; + struct { + uint64_t *value; + unsigned int idx; + } fields[] = { + { &stat_r->minor_faults, 9 }, + { &stat_r->major_faults, 11 }, + { &stat_r->utime, 13 }, + { &stat_r->stime, 14 }, + { &stat_r->vsz, 22 }, + { &stat_r->rss, 23 }, + }; + if (!stat_r->proc_stat_failed) + fd = open_fd(PROC_STAT_PATH, event); + if (fd == -1) { + stat_r->proc_stat_failed = TRUE; + /* vsz and rss are not provided by getrusage(), setting to undefined */ + stat_r->vsz = stat_undefined; + stat_r->rss = stat_undefined; + if (stat_r->rusage_failed) + return -1; + if (stat_get_rusage(stat_r) < 0) { + e_error(event, "getrusage() failed: %m"); + stat_r->rusage_failed = TRUE; + return -1; + } + return 0; + } + if (read_file(fd, PROC_STAT_PATH, buf, sizeof(buf), event) < 0) { + stat_r->proc_stat_failed = TRUE; + return -1; + } + tmp = t_strsplit(buf, " "); + unsigned int tmp_count = str_array_length(tmp); + + for (i = 0; i < N_ELEMENTS(fields); i++) { + if (fields[i].idx >= tmp_count || + str_to_uint64(tmp[fields[i].idx], fields[i].value) < 0) + *fields[i].value = stat_undefined; + } + /* rss is provided in pages, convert to bytes */ + stat_r->rss *= sysconf(_SC_PAGESIZE); + return 0; +} + +static int parse_all_stats(struct process_stat *stat_r, struct event *event) +{ + bool has_fields = FALSE; + + if (parse_stat_file(stat_r, event) == 0) + has_fields = TRUE; + if (parse_proc_io(stat_r, event) == 0) + has_fields = TRUE; + if ((!stat_r->proc_stat_failed || stat_r->rusage_failed) && + parse_proc_status(stat_r, event) == 0) + has_fields = TRUE; + + if (has_fields) + return 0; + return -1; +} + +void process_stat_read_start(struct process_stat *stat_r, struct event *event) +{ + i_zero(stat_r); + (void)parse_all_stats(stat_r, event); +} + +void process_stat_read_finish(struct process_stat *stat, struct event *event) +{ + unsigned int i; + struct process_stat new_stat; + i_zero(&new_stat); + new_stat.proc_io_failed = stat->proc_io_failed; + new_stat.proc_status_failed = stat->proc_status_failed; + new_stat.proc_stat_failed = stat->proc_stat_failed; + new_stat.rusage_failed = stat->rusage_failed; + if (parse_all_stats(&new_stat, event) < 0) { + i_zero(stat); + return; + } + stat->vsz = new_stat.vsz == stat_undefined ? 0 : new_stat.vsz; + stat->rss = new_stat.rss == stat_undefined ? 0 : new_stat.rss; + + unsigned int cumulative_field_offsets[] = { + offsetof(struct process_stat, utime), + offsetof(struct process_stat, stime), + offsetof(struct process_stat, minor_faults), + offsetof(struct process_stat, major_faults), + offsetof(struct process_stat, vol_cs), + offsetof(struct process_stat, invol_cs), + offsetof(struct process_stat, rchar), + offsetof(struct process_stat, wchar), + offsetof(struct process_stat, syscr), + offsetof(struct process_stat, syscw), + }; + for (i = 0; i < N_ELEMENTS(cumulative_field_offsets); i++) { + uint64_t *old_value = PTR_OFFSET(stat, cumulative_field_offsets[i]); + uint64_t *new_value = PTR_OFFSET(&new_stat, cumulative_field_offsets[i]); + if (*old_value == stat_undefined || *new_value == stat_undefined) + *old_value = 0; + else + *old_value = *new_value > *old_value ? + (*new_value - *old_value) : 0; + } +} diff --git a/src/lib/process-stat.h b/src/lib/process-stat.h new file mode 100644 index 0000000..66fea7d --- /dev/null +++ b/src/lib/process-stat.h @@ -0,0 +1,26 @@ +#ifndef PROCESS_STAT_H +#define PROCESS_STAT_H + +struct process_stat { + uint64_t utime; + uint64_t stime; + uint64_t minor_faults; + uint64_t major_faults; + uint64_t vol_cs; + uint64_t invol_cs; + uint64_t rss; + uint64_t vsz; + uint64_t rchar; + uint64_t wchar; + uint64_t syscr; + uint64_t syscw; + bool proc_io_failed:1; + bool rusage_failed:1; + bool proc_stat_failed:1; + bool proc_status_failed:1; +}; + +void process_stat_read_start(struct process_stat *stat_r, struct event *event); +void process_stat_read_finish(struct process_stat *stat, struct event *event); + +#endif diff --git a/src/lib/process-title.c b/src/lib/process-title.c new file mode 100644 index 0000000..15192ce --- /dev/null +++ b/src/lib/process-title.c @@ -0,0 +1,194 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "env-util.h" +#include "process-title.h" + +#ifdef HAVE_LIBBSD +#include <bsd/unistd.h> +#else +#include <unistd.h> /* FreeBSD */ +#endif + +static char *process_name = NULL; +static char *current_process_title; +static unsigned int process_title_counter = 0; + +#ifdef HAVE_SETPROCTITLE +# undef PROCTITLE_HACK +#endif + +#ifdef PROCTITLE_HACK + +#ifdef DEBUG +/* if there are problems with this approach, try to make sure we notice it */ +# define PROCTITLE_CLEAR_CHAR 0xab +#else +/* There are always race conditions when updating the process title. ps might + read a partially written title. Try to at least minimize this by using NUL + as the fill character, so ps won't show a large number of 0xab chars. */ +# define PROCTITLE_CLEAR_CHAR 0 +#endif + +static char *process_title; +static size_t process_title_len, process_title_clean_pos; +static void *argv_memblock, *environ_memblock; + +static void proctitle_hack_init(char *argv[], char *env[]) +{ + char *last; + unsigned int i; + bool clear_env; + + i_assert(argv[0] != NULL); + + /* find the last argv or environment string. it should always be the + last string in environ, but don't rely on it. this is what openssh + does, so hopefully it's safe enough. */ + last = argv[0] + strlen(argv[0]) + 1; + for (i = 1; argv[i] != NULL; i++) { + if (argv[i] == last) + last = argv[i] + strlen(argv[i]) + 1; + } + if (env[0] == NULL) + clear_env = FALSE; + else { + clear_env = last == env[0]; + for (i = 0; env[i] != NULL; i++) { + if (env[i] == last) + last = env[i] + strlen(env[i]) + 1; + } + } + + process_title = argv[0]; + process_title_len = last - argv[0]; + + if (clear_env) { + memset(env[0], PROCTITLE_CLEAR_CHAR, last - env[0]); + process_title_clean_pos = env[0] - process_title; + } else { + process_title_clean_pos = 0; + } +} + +static char **argv_dup(char *old_argv[], void **memblock_r) +{ + /* @UNSAFE */ + void *memblock, *memblock_end; + char **new_argv; + unsigned int i, count; + size_t len, memblock_len = 0; + + for (count = 0; old_argv[count] != NULL; count++) + memblock_len += strlen(old_argv[count]) + 1; + memblock_len += sizeof(char *) * (count + 1); + + memblock = malloc(memblock_len); + if (memblock == NULL) + i_fatal_status(FATAL_OUTOFMEM, "malloc() failed: %m"); + *memblock_r = memblock; + memblock_end = PTR_OFFSET(memblock, memblock_len); + + new_argv = memblock; + memblock = PTR_OFFSET(memblock, sizeof(char *) * (count + 1)); + + for (i = 0; i < count; i++) { + new_argv[i] = memblock; + len = strlen(old_argv[i]) + 1; + memcpy(memblock, old_argv[i], len); + memblock = PTR_OFFSET(memblock, len); + } + i_assert(memblock == memblock_end); + new_argv[i] = NULL; + return new_argv; +} + +static void proctitle_hack_set(const char *title) +{ + size_t len = strlen(title); + + /* OS X wants two NULs */ + if (len >= process_title_len-1) + len = process_title_len - 2; + + memcpy(process_title, title, len); + process_title[len++] = '\0'; + process_title[len++] = '\0'; + + if (len < process_title_clean_pos) { + memset(process_title + len, PROCTITLE_CLEAR_CHAR, + process_title_clean_pos - len); + process_title_clean_pos = len; + } else if (process_title_clean_pos != 0) { + process_title_clean_pos = len; + } +} + +#endif + +void process_title_init(int argc ATTR_UNUSED, char **argv[]) +{ +#ifdef PROCTITLE_HACK + char ***environ_p = env_get_environ_p(); + char **orig_argv = *argv; + char **orig_environ = *environ_p; + + *argv = argv_dup(orig_argv, &argv_memblock); + *environ_p = argv_dup(orig_environ, &environ_memblock); + proctitle_hack_init(orig_argv, orig_environ); +#endif +#ifdef HAVE_LIBBSD + setproctitle_init(argc, *argv, *env_get_environ_p()); +#endif + process_name = (*argv)[0]; +} + +void process_title_set(const char *title) +{ + i_assert(process_name != NULL); + + process_title_counter++; + i_free(current_process_title); + current_process_title = i_strdup(title); +#ifdef HAVE_SETPROCTITLE + if (title == NULL) + setproctitle(NULL); + else + setproctitle("%s", title); +#elif defined(PROCTITLE_HACK) + T_BEGIN { + proctitle_hack_set(t_strconcat(process_name, " ", title, NULL)); + } T_END; +#endif +} + +const char *process_title_get(void) +{ + return current_process_title; +} + +unsigned int process_title_get_counter(void) +{ + return process_title_counter; +} + +void process_title_deinit(void) +{ +#ifdef PROCTITLE_HACK + char ***environ_p = env_get_environ_p(); + + free(argv_memblock); + free(environ_memblock); + + /* Environment is no longer usable. Make sure we won't crash in case + some library's deinit function still calls getenv(). This code was + mainly added because of GNUTLS where we don't really care about the + getenv() call. + + Alternatively we could remove the free() calls above, but that would + annoy memory leak checking tools. Also we could attempt to restore + the environ_p to its original state, but that's a bit complicated. */ + *environ_p = NULL; +#endif + i_free(current_process_title); +} diff --git a/src/lib/process-title.h b/src/lib/process-title.h new file mode 100644 index 0000000..0f5480f --- /dev/null +++ b/src/lib/process-title.h @@ -0,0 +1,19 @@ +#ifndef PROCESS_TITLE_H +#define PROCESS_TITLE_H + +/* Initialize title changing. */ +void process_title_init(int argc, char **argv[]); + +/* Change the process title if possible. */ +void process_title_set(const char *title); +/* Return the previously set process title. NULL means that it's either not + set, or the title was explicitly set to NULL previously. */ +const char *process_title_get(void); +/* Return the number of times process_title_set() has been called. */ +unsigned int process_title_get_counter(void); + +/* Free all memory used by process title hacks. This should be the last + function called by the process, since it frees argv and environment. */ +void process_title_deinit(void); + +#endif diff --git a/src/lib/rand.c b/src/lib/rand.c new file mode 100644 index 0000000..12c9686 --- /dev/null +++ b/src/lib/rand.c @@ -0,0 +1,101 @@ +/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "randgen.h" + +#ifdef HAVE_ARC4RANDOM +#ifdef HAVE_LIBBSD +#include <bsd/stdlib.h> +#endif + +uint32_t i_rand(void) +{ + return arc4random(); +} + +uint32_t i_rand_limit(uint32_t upper_bound) +{ + i_assert(upper_bound > 0); + + return arc4random_uniform(upper_bound); +} +#else +uint32_t i_rand(void) +{ + uint32_t value; + random_fill(&value, sizeof(value)); + return value; +} + +/* + * The following generates a random number in the range [0, upper_bound) + * with each possible value having equal probability of occurring. + * + * This algorithm is not original, but it is dense enough that a detailed + * explanation is in order. + * + * The big problem is that we want a uniformly random values. If one were + * to do `i_rand() % upper_bound`, the result probability distribution would + * depend on the value of the upper bound. When the upper bound is a power + * of 2, the distribution is uniform. If it is not a power of 2, the + * distribution is skewed. + * + * The naive modulo approach breaks down because the division effectively + * splits the whole range of input values into a number of fixed sized + * "buckets", but with non-power-of-2 bound the last bucket is not the full + * size. + * + * To fix this bias, we reduce the input range such that the remaining + * values can be split exactly into equal sized buckets. + * + * For example, let's assume that i_rand() produces a uint8_t to simplify + * the math, and that we want a random number [0, 9] - in other words, + * upper_bound == 10. + * + * `i_rand() % 10` makes buckets 10 numbers wide, but the last bucket is only + * 6 numbers wide (250..255). Therefore, 0..5 will occur more frequently + * than 6..9. + * + * If we reduce the input range to [0, 250), the result of the mod 10 will + * be uniform. Interestingly, the same can be accomplished if we reduce the + * input range to [6, 255]. + * + * This minimum value can be calculated as: 256 % 10 = 6. + * + * Or more generically: (UINT32_MAX + 1) % upper_bound. + * + * Then, we can pick random numbers until we get one that is >= this + * minimum. Once we have it, we can simply mod it by the limit to get our + * answer. + * + * For our example of modding by 10, we pick random numbers until they are + * greater than or equal to 6. Once we have one, we have a value in the + * range [6, 255] which when modded by 10 yields uniformly distributed + * values [0, 9]. + * + * There are two things to consider while implementing this algorithm: + * + * 1. Division by 0: Getting called with a 0 upper bound doesn't make sense, + * therefore we simply assert that the passed in bound is non-zero. + * + * 2. 32-bit performance: The above expression to calculate the minimum + * value requires 64-bit division. This generally isn't a problem on + * 64-bit systems, but 32-bit systems often end up calling a software + * implementation (e.g., `__umoddi3`). This is undesirable. + * + * Therefore, we rewrite the expression as: + * + * ~(upper_bound - 1) % upper_bound + * + * This is harder to understand, but it is 100% equivalent. + */ +uint32_t i_rand_limit(uint32_t upper_bound) +{ + i_assert(upper_bound > 0); + + uint32_t val; + uint32_t min = UNSIGNED_MINUS(upper_bound) % upper_bound; + while((val = i_rand()) < min); + return val % upper_bound; +} +#endif diff --git a/src/lib/randgen.c b/src/lib/randgen.c new file mode 100644 index 0000000..f6b2da9 --- /dev/null +++ b/src/lib/randgen.c @@ -0,0 +1,222 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "randgen.h" +#include <unistd.h> +#include <fcntl.h> + +#ifdef DEBUG +/* For reproducing tests, fall back onto using a simple deterministic PRNG */ +/* Marsaglia's 1999 KISS, de-macro-ified, and with the fixed KISS11 SHR3, + which is clearly what was intended given the "cycle length 2^123" claim. */ +static bool kiss_in_use; +static unsigned int kiss_seed; +static uint32_t kiss_z, kiss_w, kiss_jsr, kiss_jcong; +static void +kiss_init(unsigned int seed) +{ + i_info("Random numbers are PRNG using kiss, as per DOVECOT_SRAND=%u", seed); + kiss_seed = seed; + kiss_jsr = 0x5eed5eed; /* simply musn't be 0 */ + kiss_z = 1 ^ (kiss_w = kiss_jcong = seed); /* w=z=0 is bad, see Rose */ + kiss_in_use = TRUE; +} +static unsigned int +kiss_rand(void) +{ + kiss_z = 36969 * (kiss_z&65535) + (kiss_z>>16); + kiss_w = 18000 * (kiss_w&65535) + (kiss_w>>16); + kiss_jcong = 69069 * kiss_jcong + 1234567; + kiss_jsr^=(kiss_jsr<<13); /* <<17, >>13 gives cycle length 2^28.2 max */ + kiss_jsr^=(kiss_jsr>>17); /* <<13, >>17 gives maximal cycle length */ + kiss_jsr^=(kiss_jsr<<5); + return (((kiss_z<<16) + kiss_w) ^ kiss_jcong) + kiss_jsr; +} +int rand_get_last_seed(unsigned int *seed_r) +{ + if (!kiss_in_use) + return -1; /* not using a deterministic PRNG, seed is irrelevant */ + *seed_r = kiss_seed; + return 0; +} +#endif + +/* get randomness from either getrandom, arc4random or /dev/urandom */ + +#if defined(HAVE_GETRANDOM) && HAVE_DECL_GETRANDOM != 0 +# include <sys/random.h> +# define USE_GETRANDOM +static bool getrandom_present = TRUE; +#elif defined(HAVE_ARC4RANDOM) +# if defined(HAVE_LIBBSD) +# include <bsd/stdlib.h> +# endif +# define USE_ARC4RANDOM +#else +static bool getrandom_present = FALSE; +# define USE_RANDOM_DEV +#endif + +static int init_refcount = 0; +static int urandom_fd = -1; + +#if defined(USE_GETRANDOM) || defined(USE_RANDOM_DEV) +/* Use a small buffer when reading randomness. This is mainly to make small + random reads more efficient, such as i_rand*(). When reading larger amount + of randomness this buffer is bypassed. + + There doesn't seem to be a big difference in Linux system CPU usage when + buffer size is above 16 bytes. Double it just to be safe. Avoid it being + too large anyway so we don't unnecessarily waste CPU and memory. */ +#define RANDOM_READ_BUFFER_SIZE 32 +static unsigned char random_next[RANDOM_READ_BUFFER_SIZE]; +static size_t random_next_pos = 0; +static size_t random_next_size = 0; + +static void random_open_urandom(void) +{ + urandom_fd = open(DEV_URANDOM_PATH, O_RDONLY); + if (urandom_fd == -1) { + if (errno == ENOENT) { + i_fatal("open("DEV_URANDOM_PATH") failed: doesn't exist," + "currently we require it"); + } else { + i_fatal("open("DEV_URANDOM_PATH") failed: %m"); + } + } + fd_close_on_exec(urandom_fd, TRUE); +} + +static inline int random_read(unsigned char *buf, size_t size) +{ + ssize_t ret = 0; +# if defined(USE_GETRANDOM) + if (getrandom_present) { + ret = getrandom(buf, size, 0); + if (ret < 0 && errno == ENOSYS) { + getrandom_present = FALSE; + /* It gets complicated here... While the libc (and its + headers) indicated that getrandom() was available when + we were compiled, the kernel disagreed just now at + runtime. Fall back to reading /dev/urandom. */ + random_open_urandom(); + } + } + /* this is here to avoid clang complain, + because getrandom_present will be always FALSE + if USE_GETRANDOM is not defined */ + if (!getrandom_present) +# endif + ret = read(urandom_fd, buf, size); + if (unlikely(ret <= 0)) { + if (ret == 0) { + i_fatal("read("DEV_URANDOM_PATH") failed: EOF"); + } else if (errno != EINTR) { + if (getrandom_present) { + i_fatal("getrandom() failed: %m"); + } else { + i_fatal("read("DEV_URANDOM_PATH") failed: %m"); + } + } + } + i_assert(ret > 0 || errno == EINTR); + return ret; +} +#endif + +void random_fill(void *buf, size_t size) +{ + i_assert(init_refcount > 0); + i_assert(size < SSIZE_T_MAX); + +#ifdef DEBUG + if (kiss_in_use) { + for (size_t pos = 0; pos < size; pos++) + ((unsigned char*)buf)[pos] = kiss_rand(); + return; + } +#endif + +#if defined(USE_ARC4RANDOM) + arc4random_buf(buf, size); +#else + size_t pos; + ssize_t ret; + + for (pos = 0; pos < size; ) { + if (size >= sizeof(random_next) && random_next_size == 0) { + /* Asking for lots of randomness. Read directly to the + destination buffer. */ + ret = random_read(PTR_OFFSET(buf, pos), size - pos); + if (ret > -1) + pos += ret; + } else { + /* Asking for a little randomness. Read via a larger + buffer to reduce the number of syscalls. */ + if (random_next_size > random_next_pos) + ret = random_next_size - random_next_pos; + else { + random_next_pos = 0; + ret = random_read(random_next, + sizeof(random_next)); + random_next_size = ret < 0 ? 0 : ret; + } + if (ret > 0) { + size_t used = I_MIN(size - pos, (size_t)ret); + memcpy(PTR_OFFSET(buf, pos), + random_next + random_next_pos, used); + random_next_pos += used; + pos += used; + } + } + } +#endif /* defined(USE_ARC4RANDOM) */ +} + +void random_init(void) +{ + /* static analyzer seems to require this */ + unsigned int seed = 0; + const char *env_seed; + + if (init_refcount++ > 0) + return; + + env_seed = getenv("DOVECOT_SRAND"); +#ifdef DEBUG + if (env_seed != NULL && str_to_uint(env_seed, &seed) >= 0) { + kiss_init(seed); + /* getrandom_present = FALSE; not needed, only used in random_read() */ + goto normal_exit; + } +#else + if (env_seed != NULL && *env_seed != '\0') + i_warning("DOVECOT_SRAND is not available in non-debug builds"); +#endif /* DEBUG */ + +#if defined(USE_RANDOM_DEV) + random_open_urandom(); +#endif + /* DO NOT REMOVE THIS - It is also + needed to make sure getrandom really works. + */ + random_fill(&seed, sizeof(seed)); +#ifdef DEBUG + if (env_seed != NULL) { + if (strcmp(env_seed, "kiss") != 0) + i_fatal("DOVECOT_SRAND not a number or 'kiss'"); + kiss_init(seed); + i_close_fd(&urandom_fd); + } + +normal_exit: +#endif + srand(seed); +} + +void random_deinit(void) +{ + if (--init_refcount > 0) + return; + i_close_fd(&urandom_fd); +} diff --git a/src/lib/randgen.h b/src/lib/randgen.h new file mode 100644 index 0000000..d532880 --- /dev/null +++ b/src/lib/randgen.h @@ -0,0 +1,17 @@ +#ifndef RANDGEN_H +#define RANDGEN_H + +/* Fill given buffer with semi-strong randomness */ +void random_fill(void *buf, size_t size); + +/* may be called multiple times, + and are called by default in lib_init */ +void random_init(void); +void random_deinit(void); + +#ifdef DEBUG +/* Debug helper to make random tests reproduceable. 0=got seed, -1=failure. */ +int rand_get_last_seed(unsigned int *seed_r); +#endif + +#endif diff --git a/src/lib/read-full.c b/src/lib/read-full.c new file mode 100644 index 0000000..733c3df --- /dev/null +++ b/src/lib/read-full.c @@ -0,0 +1,40 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "read-full.h" + +#include <unistd.h> + +int read_full(int fd, void *data, size_t size) +{ + ssize_t ret; + + while (size > 0) { + ret = read(fd, data, size < SSIZE_T_MAX ? size : SSIZE_T_MAX); + if (ret <= 0) + return ret; + + data = PTR_OFFSET(data, ret); + size -= ret; + } + + return 1; +} + +int pread_full(int fd, void *data, size_t size, off_t offset) +{ + ssize_t ret; + + while (size > 0) { + ret = pread(fd, data, size < SSIZE_T_MAX ? + size : SSIZE_T_MAX, offset); + if (ret <= 0) + return ret; + + data = PTR_OFFSET(data, ret); + size -= ret; + offset += ret; + } + + return 1; +} diff --git a/src/lib/read-full.h b/src/lib/read-full.h new file mode 100644 index 0000000..8a6dc7d --- /dev/null +++ b/src/lib/read-full.h @@ -0,0 +1,9 @@ +#ifndef READ_FULL_H +#define READ_FULL_H + +/* Read data from file. Returns -1 if error occurred, or 0 if EOF came before + everything was read, or 1 if all was ok. */ +int read_full(int fd, void *data, size_t size); +int pread_full(int fd, void *data, size_t size, off_t offset); + +#endif diff --git a/src/lib/restrict-access.c b/src/lib/restrict-access.c new file mode 100644 index 0000000..a8fc47d --- /dev/null +++ b/src/lib/restrict-access.c @@ -0,0 +1,531 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#define _GNU_SOURCE /* setresgid() */ +#include <stdio.h> /* for AIX */ +#include <sys/types.h> +#include <unistd.h> + +#include "lib.h" +#include "str.h" +#include "restrict-access.h" +#include "env-util.h" +#include "ipwd.h" + +#include <time.h> +#ifdef HAVE_PR_SET_DUMPABLE +# include <sys/prctl.h> +#endif + +static gid_t process_primary_gid = (gid_t)-1; +static gid_t process_privileged_gid = (gid_t)-1; +static bool process_using_priv_gid = FALSE; +static char *chroot_dir = NULL; + +void restrict_access_init(struct restrict_access_settings *set) +{ + i_zero(set); + + set->uid = (uid_t)-1; + set->gid = (gid_t)-1; + set->privileged_gid = (gid_t)-1; +} + +static const char *get_uid_str(uid_t uid) +{ + struct passwd pw; + const char *ret; + int old_errno = errno; + + if (i_getpwuid(uid, &pw) <= 0) + ret = dec2str(uid); + else + ret = t_strdup_printf("%s(%s)", dec2str(uid), pw.pw_name); + errno = old_errno; + return ret; +} + +static const char *get_gid_str(gid_t gid) +{ + struct group group; + const char *ret; + int old_errno = errno; + + if (i_getgrgid(gid, &group) <= 0) + ret = dec2str(gid); + else + ret = t_strdup_printf("%s(%s)", dec2str(gid), group.gr_name); + errno = old_errno; + return ret; +} + +static void restrict_init_groups(gid_t primary_gid, gid_t privileged_gid, + const char *gid_source) +{ + string_t *str; + + if (privileged_gid == (gid_t)-1) { + if (primary_gid == getgid() && primary_gid == getegid()) { + /* everything is already set */ + return; + } + + if (setgid(primary_gid) == 0) + return; + + str = t_str_new(128); + str_printfa(str, "setgid(%s", get_gid_str(primary_gid)); + if (gid_source != NULL) + str_printfa(str, " from %s", gid_source); + str_printfa(str, ") failed with euid=%s, gid=%s, egid=%s: %m " + "(This binary should probably be called with " + "process group set to %s instead of %s)", + get_uid_str(geteuid()), + get_gid_str(getgid()), get_gid_str(getegid()), + get_gid_str(primary_gid), get_gid_str(getegid())); + i_fatal("%s", str_c(str)); + } + + if (getegid() != 0 && primary_gid == getgid() && + primary_gid == getegid()) { + /* privileged_gid is hopefully in saved ID. if not, + there's nothing we can do about it. */ + return; + } + +#ifdef HAVE_SETRESGID + if (setresgid(primary_gid, primary_gid, privileged_gid) != 0) { + i_fatal("setresgid(%s,%s,%s) failed with euid=%s: %m", + get_gid_str(primary_gid), get_gid_str(primary_gid), + get_gid_str(privileged_gid), get_uid_str(geteuid())); + } +#else + if (geteuid() == 0) { + /* real, effective, saved -> privileged_gid */ + if (setgid(privileged_gid) < 0) { + i_fatal("setgid(%s) failed: %m", + get_gid_str(privileged_gid)); + } + } + /* real, effective -> primary_gid + saved -> keep */ + if (setregid(primary_gid, primary_gid) != 0) { + i_fatal("setregid(%s,%s) failed with euid=%s: %m", + get_gid_str(primary_gid), get_gid_str(privileged_gid), + get_uid_str(geteuid())); + } +#endif +} + +gid_t *restrict_get_groups_list(unsigned int *gid_count_r) +{ + gid_t *gid_list; + int ret, gid_count; + + if ((gid_count = getgroups(0, NULL)) < 0) + i_fatal("getgroups() failed: %m"); + + /* @UNSAFE */ + gid_list = t_new(gid_t, gid_count+1); /* +1 in case gid_count=0 */ + if ((ret = getgroups(gid_count, gid_list)) < 0) + i_fatal("getgroups() failed: %m"); + + *gid_count_r = ret; + return gid_list; +} + +static void drop_restricted_groups(const struct restrict_access_settings *set, + gid_t *gid_list, unsigned int *gid_count, + bool *have_root_group) +{ + /* @UNSAFE */ + unsigned int i, used; + + for (i = 0, used = 0; i < *gid_count; i++) { + if (gid_list[i] >= set->first_valid_gid && + (set->last_valid_gid == 0 || + gid_list[i] <= set->last_valid_gid)) { + if (gid_list[i] == 0) + *have_root_group = TRUE; + gid_list[used++] = gid_list[i]; + } + } + *gid_count = used; +} + +static gid_t get_group_id(const char *name) +{ + struct group group; + gid_t gid; + + if (str_to_gid(name, &gid) == 0) + return gid; + + switch (i_getgrnam(name, &group)) { + case -1: + i_fatal("getgrnam(%s) failed: %m", name); + case 0: + i_fatal("unknown group name in extra_groups: %s", name); + default: + return group.gr_gid; + } +} + +static void fix_groups_list(const struct restrict_access_settings *set, + bool preserve_existing, bool *have_root_group) +{ + gid_t gid, *gid_list, *gid_list2; + const char *const *tmp, *empty = NULL; + unsigned int i, gid_count; + bool add_primary_gid; + + /* if we're using a privileged GID, we can temporarily drop our + effective GID. we still want to be able to use its privileges, + so add it to supplementary groups. */ + add_primary_gid = process_privileged_gid != (gid_t)-1; + + tmp = set->extra_groups == NULL ? &empty : + t_strsplit_spaces(set->extra_groups, ", "); + + if (preserve_existing) { + gid_list = restrict_get_groups_list(&gid_count); + drop_restricted_groups(set, gid_list, &gid_count, + have_root_group); + /* see if the list already contains the primary GID */ + for (i = 0; i < gid_count; i++) { + if (gid_list[i] == process_primary_gid) { + add_primary_gid = FALSE; + break; + } + } + } else { + gid_list = NULL; + gid_count = 0; + } + if (gid_count == 0) { + /* Some OSes don't like an empty groups list, + so use the primary GID as the only one. */ + gid_list = t_new(gid_t, 2); + gid_list[0] = process_primary_gid; + gid_count = 1; + add_primary_gid = FALSE; + } + + if (*tmp != NULL || add_primary_gid) { + /* @UNSAFE: add extra groups and/or primary GID to gids list */ + gid_list2 = t_new(gid_t, gid_count + str_array_length(tmp) + 1); + memcpy(gid_list2, gid_list, gid_count * sizeof(gid_t)); + for (; *tmp != NULL; tmp++) { + gid = get_group_id(*tmp); + if (gid != process_primary_gid) + gid_list2[gid_count++] = gid; + } + if (add_primary_gid) + gid_list2[gid_count++] = process_primary_gid; + gid_list = gid_list2; + } + + if (setgroups(gid_count, gid_list) < 0) { + if (errno == EINVAL) { + i_fatal("setgroups(%s) failed: Too many extra groups", + set->extra_groups == NULL ? "" : + set->extra_groups); + } else { + i_fatal("setgroups() failed: %m"); + } + } +} + +static const char * +get_setuid_error_str(const struct restrict_access_settings *set, uid_t target_uid) +{ + string_t *str = t_str_new(128); + + str_printfa(str, "setuid(%s", get_uid_str(target_uid)); + if (set->uid_source != NULL) + str_printfa(str, " from %s", set->uid_source); + str_printfa(str, ") failed with euid=%s: %m ", + get_uid_str(geteuid())); + if (errno == EAGAIN) { + str_append(str, "(ulimit -u reached)"); + } else { + str_printfa(str, "(This binary should probably be called with " + "process user set to %s instead of %s)", + get_uid_str(target_uid), get_uid_str(geteuid())); + } + return str_c(str); +} + +void restrict_access(const struct restrict_access_settings *set, + enum restrict_access_flags flags, const char *home) +{ + bool is_root, have_root_group, preserve_groups = FALSE; + bool allow_root_gid; + bool allow_root = (flags & RESTRICT_ACCESS_FLAG_ALLOW_ROOT) != 0; + uid_t target_uid = set->uid; + + is_root = geteuid() == 0; + + if (!is_root && + !set->allow_setuid_root && + getuid() == 0) { + /* recover current effective UID */ + if (target_uid == (uid_t)-1) + target_uid = geteuid(); + else + i_assert(target_uid > 0); + /* try to elevate to root */ + if (seteuid(0) < 0) + i_fatal("seteuid(0) failed: %m"); + is_root = TRUE; + } + + /* set the primary/privileged group */ + process_primary_gid = set->gid; + process_privileged_gid = set->privileged_gid; + if (process_privileged_gid == process_primary_gid) { + /* a pointless configuration, ignore it */ + process_privileged_gid = (gid_t)-1; + } + + have_root_group = process_primary_gid == 0; + if (process_primary_gid != (gid_t)-1 || + process_privileged_gid != (gid_t)-1) { + if (process_primary_gid == (gid_t)-1) + process_primary_gid = getegid(); + restrict_init_groups(process_primary_gid, + process_privileged_gid, set->gid_source); + } else { + if (process_primary_gid == (gid_t)-1) + process_primary_gid = getegid(); + } + + /* set system user's groups */ + if (set->system_groups_user != NULL && is_root) { + if (initgroups(set->system_groups_user, + process_primary_gid) < 0) { + i_fatal("initgroups(%s, %s) failed: %m", + set->system_groups_user, + get_gid_str(process_primary_gid)); + } + preserve_groups = TRUE; + } + + /* add extra groups. if we set system user's groups, drop the + restricted groups at the same time. */ + if (is_root) T_BEGIN { + fix_groups_list(set, preserve_groups, + &have_root_group); + } T_END; + + /* chrooting */ + if (set->chroot_dir != NULL) { + /* kludge: localtime() must be called before chroot(), + or the timezone isn't known */ + time_t t = 0; + (void)localtime(&t); + + if (chroot(set->chroot_dir) != 0) + i_fatal("chroot(%s) failed: %m", set->chroot_dir); + /* makes static analyzers happy, and is more secure */ + if (chdir("/") != 0) + i_fatal("chdir(/) failed: %m"); + + chroot_dir = i_strdup(set->chroot_dir); + + if (home != NULL) { + if (chdir(home) < 0) { + i_error("chdir(%s) failed: %m", home); + } + } + } + + /* uid last */ + if (target_uid != (uid_t)-1) { + if (setuid(target_uid) != 0) + i_fatal("%s", get_setuid_error_str(set, target_uid)); + } + + /* verify that we actually dropped the privileges */ + if ((target_uid != (uid_t)-1 && target_uid != 0) || !allow_root) { + if (setuid(0) == 0) { + if (!allow_root && + (target_uid == 0 || target_uid == (uid_t)-1)) + i_fatal("This process must not be run as root"); + + i_fatal("We couldn't drop root privileges"); + } + } + + if (set->first_valid_gid != 0) + allow_root_gid = FALSE; + else if (process_primary_gid == 0 || process_privileged_gid == 0) + allow_root_gid = TRUE; + else + allow_root_gid = FALSE; + + if (!allow_root_gid && target_uid != 0 && + (target_uid != (uid_t)-1 || !is_root)) { + if (getgid() == 0 || getegid() == 0 || setgid(0) == 0) { + if (process_primary_gid == 0) + i_fatal("GID 0 isn't permitted"); + i_fatal("We couldn't drop root group privileges " + "(wanted=%s, gid=%s, egid=%s)", + get_gid_str(process_primary_gid), + get_gid_str(getgid()), get_gid_str(getegid())); + } + } +} + +void restrict_access_set_env(const struct restrict_access_settings *set) +{ + if (set->system_groups_user != NULL && + *set->system_groups_user != '\0') + env_put("RESTRICT_USER", set->system_groups_user); + if (set->chroot_dir != NULL && *set->chroot_dir != '\0') + env_put("RESTRICT_CHROOT", set->chroot_dir); + + if (set->uid != (uid_t)-1) + env_put("RESTRICT_SETUID", dec2str(set->uid)); + if (set->gid != (gid_t)-1) + env_put("RESTRICT_SETGID", dec2str(set->gid)); + if (set->privileged_gid != (gid_t)-1) + env_put("RESTRICT_SETGID_PRIV", dec2str(set->privileged_gid)); + if (set->extra_groups != NULL && *set->extra_groups != '\0') + env_put("RESTRICT_SETEXTRAGROUPS", set->extra_groups); + + if (set->first_valid_gid != 0) + env_put("RESTRICT_GID_FIRST", dec2str(set->first_valid_gid)); + if (set->last_valid_gid != 0) + env_put("RESTRICT_GID_LAST", dec2str(set->last_valid_gid)); +} + +static const char *null_if_empty(const char *str) +{ + return str == NULL || *str == '\0' ? NULL : str; +} + +void restrict_access_get_env(struct restrict_access_settings *set_r) +{ + const char *value; + + restrict_access_init(set_r); + if ((value = getenv("RESTRICT_SETUID")) != NULL) { + if (str_to_uid(value, &set_r->uid) < 0) + i_fatal("Invalid uid: %s", value); + } + if ((value = getenv("RESTRICT_SETGID")) != NULL) { + if (str_to_gid(value, &set_r->gid) < 0) + i_fatal("Invalid gid: %s", value); + } + if ((value = getenv("RESTRICT_SETGID_PRIV")) != NULL) { + if (str_to_gid(value, &set_r->privileged_gid) < 0) + i_fatal("Invalid privileged_gid: %s", value); + } + if ((value = getenv("RESTRICT_GID_FIRST")) != NULL) { + if (str_to_gid(value, &set_r->first_valid_gid) < 0) + i_fatal("Invalid first_valid_gid: %s", value); + } + if ((value = getenv("RESTRICT_GID_LAST")) != NULL) { + if (str_to_gid(value, &set_r->last_valid_gid) < 0) + i_fatal("Invalid last_value_gid: %s", value); + } + + set_r->extra_groups = null_if_empty(getenv("RESTRICT_SETEXTRAGROUPS")); + set_r->system_groups_user = null_if_empty(getenv("RESTRICT_USER")); + set_r->chroot_dir = null_if_empty(getenv("RESTRICT_CHROOT")); +} + +void restrict_access_by_env(enum restrict_access_flags flags, const char *home) +{ + struct restrict_access_settings set; + + restrict_access_get_env(&set); + restrict_access(&set, flags, home); + + /* clear the environment, so we don't fail if we get back here */ + env_remove("RESTRICT_SETUID"); + if (process_privileged_gid == (gid_t)-1) { + /* if we're dropping privileges before executing and + a privileged group is set, the groups must be fixed + after exec */ + env_remove("RESTRICT_SETGID"); + env_remove("RESTRICT_SETGID_PRIV"); + } + env_remove("RESTRICT_GID_FIRST"); + env_remove("RESTRICT_GID_LAST"); + if (getuid() != 0) + env_remove("RESTRICT_SETEXTRAGROUPS"); + else { + /* Preserve RESTRICT_SETEXTRAGROUPS, so if we're again dropping + more privileges we'll still preserve the extra groups. This + mainly means preserving service { extra_groups } for lmtp + and doveadm accesses. */ + } + env_remove("RESTRICT_USER"); + env_remove("RESTRICT_CHROOT"); +} + +const char *restrict_access_get_current_chroot(void) +{ + return chroot_dir; +} + +void restrict_access_set_dumpable(bool allow ATTR_UNUSED) +{ +#ifdef HAVE_PR_SET_DUMPABLE + if (prctl(PR_SET_DUMPABLE, allow ? 1 : 0, 0, 0, 0) < 0) + i_error("prctl(PR_SET_DUMPABLE) failed: %m"); +#endif +} + +bool restrict_access_get_dumpable(void) +{ +#ifdef HAVE_PR_SET_DUMPABLE + bool allow = FALSE; + if (prctl(PR_GET_DUMPABLE, &allow, 0, 0, 0) < 0) + i_error("prctl(PR_GET_DUMPABLE) failed: %m"); + return allow; +#endif + return TRUE; +} + +void restrict_access_allow_coredumps(bool allow) +{ + if (getenv("PR_SET_DUMPABLE") != NULL) { + restrict_access_set_dumpable(allow); + } +} + +int restrict_access_use_priv_gid(void) +{ + i_assert(!process_using_priv_gid); + + if (process_privileged_gid == (gid_t)-1) + return 0; + if (setegid(process_privileged_gid) < 0) { + i_error("setegid(privileged) failed: %m"); + return -1; + } + process_using_priv_gid = TRUE; + return 0; +} + +void restrict_access_drop_priv_gid(void) +{ + if (!process_using_priv_gid) + return; + + if (setegid(process_primary_gid) < 0) + i_fatal("setegid(primary) failed: %m"); + process_using_priv_gid = FALSE; +} + +bool restrict_access_have_priv_gid(void) +{ + return process_privileged_gid != (gid_t)-1; +} + +void restrict_access_deinit(void) +{ + i_free(chroot_dir); +} diff --git a/src/lib/restrict-access.h b/src/lib/restrict-access.h new file mode 100644 index 0000000..ba4d893 --- /dev/null +++ b/src/lib/restrict-access.h @@ -0,0 +1,90 @@ +#ifndef RESTRICT_ACCESS_H +#define RESTRICT_ACCESS_H + +enum restrict_access_flags { + /* If flags given to restrict_access() include + * RESTRICT_ACCESS_FLAG_ALLOW_ROOT, we won't kill + * ourself when we have root privileges. */ + RESTRICT_ACCESS_FLAG_ALLOW_ROOT = 1, +}; + +struct restrict_access_settings { + /* UID to use, or (uid_t)-1 if you don't want to change it */ + uid_t uid; + /* Effective GID to use, or (gid_t)-1 if you don't want to change it */ + gid_t gid; + /* If not (gid_t)-1, the privileged GID can be temporarily + enabled/disabled. */ + gid_t privileged_gid; + + /* Add access to these space or comma -separated extra groups */ + const char *extra_groups; + /* Add access to groups this system user belongs to */ + const char *system_groups_user; + + /* All specified GIDs must be in this range. If extra_groups or system + group user contains other GIDs, they're silently dropped. */ + gid_t first_valid_gid, last_valid_gid; + + /* Human readable "source" of UID and GID values. If non-NULL, + displayed on error messages about failing to change uid/gid. */ + const char *uid_source, *gid_source; + + /* Chroot directory */ + const char *chroot_dir; + + /* Allow running in setuid-root mode, where real UID is root and + * effective UID is non-root. By default the real UID is changed + * to be the same as the effective UID. */ + bool allow_setuid_root; +}; + +/* Initialize settings with values that don't change anything. */ +void restrict_access_init(struct restrict_access_settings *set); +/* Restrict access as specified by the settings. If home is not NULL, + it's chdir()ed after chrooting, otherwise it chdirs to / (the chroot). */ +void restrict_access(const struct restrict_access_settings *set, + enum restrict_access_flags flags, const char *home) + ATTR_NULL(3); +/* Set environment variables so they can be read with + restrict_access_by_env(). */ +void restrict_access_set_env(const struct restrict_access_settings *set); +/* Read restrict_access_set_env() environments back into struct. */ +void restrict_access_get_env(struct restrict_access_settings *set_r); +/* Read restrictions from environment and call restrict_access(). + If flags do not include RESTRICT_ACCESS_FLAG_ALLOW_ROOT, we'll kill ourself + unless the RESTRICT_* environments caused root privileges to be dropped */ +void restrict_access_by_env(enum restrict_access_flags flags, + const char *home) ATTR_NULL(2); + +/* Return the chrooted directory if restrict_access*() chrooted, + otherwise NULL. */ +const char *restrict_access_get_current_chroot(void); + +/* + Checks if PR_SET_DUMPABLE environment variable is set, and if it is, + calls restrict_access_set_dumpable(allow). +*/ +void restrict_access_allow_coredumps(bool allow); + +/* Sets process dumpable true or false. Setting this true allows core dumping, + reading /proc/self/io, attaching with PTRACE_ATTACH, and also changes + ownership of /proc/[pid] directory. */ +void restrict_access_set_dumpable(bool allow); + +/* Gets process dumpability, returns TRUE if not supported, because + we then assume that constraint is not present. */ +bool restrict_access_get_dumpable(void); + +/* If privileged_gid was set, these functions can be used to temporarily + gain access to the group. */ +int restrict_access_use_priv_gid(void); +void restrict_access_drop_priv_gid(void); +/* Returns TRUE if privileged GID exists for this process. */ +bool restrict_access_have_priv_gid(void); + +gid_t *restrict_get_groups_list(unsigned int *gid_count_r); + +void restrict_access_deinit(void); + +#endif diff --git a/src/lib/restrict-process-size.c b/src/lib/restrict-process-size.c new file mode 100644 index 0000000..0181473 --- /dev/null +++ b/src/lib/restrict-process-size.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "restrict-process-size.h" + +#include <unistd.h> + +void restrict_process_size(rlim_t bytes) +{ + struct rlimit rlim; + + rlim.rlim_max = rlim.rlim_cur = bytes; + if (setrlimit(RLIMIT_DATA, &rlim) < 0) { + i_fatal("setrlimit(RLIMIT_DATA, %llu): %m", + (unsigned long long)bytes); + } + +#ifdef HAVE_RLIMIT_AS + if (setrlimit(RLIMIT_AS, &rlim) < 0) { + i_fatal("setrlimit(RLIMIT_AS, %llu): %m", + (unsigned long long)bytes); + } +#endif +} + +void restrict_process_count(rlim_t count ATTR_UNUSED) +{ +#ifdef HAVE_RLIMIT_NPROC + struct rlimit rlim; + + rlim.rlim_max = rlim.rlim_cur = count; + if (setrlimit(RLIMIT_NPROC, &rlim) < 0) { + i_error("setrlimit(RLIMIT_NPROC, %llu): %m", + (unsigned long long)count); + } +#endif +} + +void restrict_fd_limit(rlim_t count) +{ +#ifdef HAVE_SETRLIMIT + struct rlimit rlim; + + rlim.rlim_cur = rlim.rlim_max = count; + if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) { + i_error("setrlimit(RLIMIT_NOFILE, %llu): %m", + (unsigned long long)count); + } +#endif +} + +int restrict_get_process_size(rlim_t *limit_r) +{ + struct rlimit rlim; + +#ifdef HAVE_RLIMIT_AS + if (getrlimit(RLIMIT_AS, &rlim) < 0) { + i_error("getrlimit(RLIMIT_AS): %m"); + return -1; + } +#else + if (getrlimit(RLIMIT_DATA, &rlim) < 0) { + i_error("getrlimit(RLIMIT_DATA): %m"); + return -1; + } +#endif + *limit_r = rlim.rlim_cur; + return 0; +} + +int restrict_get_core_limit(rlim_t *limit_r) +{ +#ifdef HAVE_RLIMIT_CORE + struct rlimit rlim; + + if (getrlimit(RLIMIT_CORE, &rlim) < 0) { + i_error("getrlimit(RLIMIT_CORE) failed: %m"); + return -1; + } + *limit_r = rlim.rlim_cur; + return 0; +#else + return -1; +#endif +} + +int restrict_get_process_limit(rlim_t *limit_r) +{ +#ifdef HAVE_RLIMIT_NPROC + struct rlimit rlim; + + if (getrlimit(RLIMIT_NPROC, &rlim) < 0) { + i_error("getrlimit(RLIMIT_NPROC) failed: %m"); + return -1; + } + *limit_r = rlim.rlim_cur; + return 0; +#else + return -1; +#endif +} + +int restrict_get_fd_limit(rlim_t *limit_r) +{ + struct rlimit rlim; + + if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { + i_error("getrlimit(RLIMIT_NOFILE) failed: %m"); + return -1; + } + *limit_r = rlim.rlim_cur; + return 0; +} diff --git a/src/lib/restrict-process-size.h b/src/lib/restrict-process-size.h new file mode 100644 index 0000000..35c323d --- /dev/null +++ b/src/lib/restrict-process-size.h @@ -0,0 +1,25 @@ +#ifndef RESTRICT_PROCESS_SIZE_H +#define RESTRICT_PROCESS_SIZE_H + +#include <sys/time.h> +#ifdef HAVE_SYS_RESOURCE_H +# include <sys/resource.h> +#endif + +/* Restrict max. process size. */ +void restrict_process_size(rlim_t bytes); +/* Restrict max. number of processes. */ +void restrict_process_count(rlim_t count); +/* Set fd limit to count. */ +void restrict_fd_limit(rlim_t count); + +/* Get the core dump size limit. Returns 0 if ok, -1 if lookup failed. */ +int restrict_get_core_limit(rlim_t *limit_r); +/* Get the process VSZ size limit. Returns 0 if ok, -1 if lookup failed. */ +int restrict_get_process_size(rlim_t *limit_r); +/* Get the process count limit. Returns 0 if ok, -1 if lookup failed. */ +int restrict_get_process_limit(rlim_t *limit_r); +/* Get the fd limit. Returns 0 if ok, -1 if lookup failed. */ +int restrict_get_fd_limit(rlim_t *limit_r); + +#endif diff --git a/src/lib/safe-memset.c b/src/lib/safe-memset.c new file mode 100644 index 0000000..3062a41 --- /dev/null +++ b/src/lib/safe-memset.c @@ -0,0 +1,17 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "safe-memset.h" + +void safe_memset(void *data, int c, size_t size) +{ + volatile unsigned int volatile_zero_idx = 0; + volatile unsigned char *p = data; + + if (size == 0) + return; + + do { + memset(data, c, size); + } while (p[volatile_zero_idx] != c); +} diff --git a/src/lib/safe-memset.h b/src/lib/safe-memset.h new file mode 100644 index 0000000..affd562 --- /dev/null +++ b/src/lib/safe-memset.h @@ -0,0 +1,8 @@ +#ifndef SAFE_MEMSET_H +#define SAFE_MEMSET_H + +/* memset() guaranteed not to get optimized away by compiler. + Should be used instead of memset() when clearing any sensitive data. */ +void safe_memset(void *data, int c, size_t size); + +#endif diff --git a/src/lib/safe-mkdir.c b/src/lib/safe-mkdir.c new file mode 100644 index 0000000..dc695c7 --- /dev/null +++ b/src/lib/safe-mkdir.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "safe-mkdir.h" + +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + +int safe_mkdir(const char *dir, mode_t mode, uid_t uid, gid_t gid) +{ + struct stat st; + int fd, ret = 2, changed_ret = 0; + + if (lstat(dir, &st) < 0) { + if (errno != ENOENT) + i_fatal("lstat() failed for %s: %m", dir); + + if (mkdir(dir, mode) < 0) { + if (errno != EEXIST) + i_fatal("Can't create directory %s: %m", dir); + } else { + /* created it */ + ret = changed_ret = 1; + } + } + + /* use fchown() and fchmod() just to make sure we aren't following + symbolic links. */ + fd = open(dir, O_RDONLY); + if (fd == -1) + i_fatal("open() failed for %s: %m", dir); + + if (fstat(fd, &st) < 0) + i_fatal("fstat() failed for %s: %m", dir); + + if (!S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)) + i_fatal("Not a directory %s", dir); + + /* change the file owner first, since it's the only user one who + can mess up with the file mode. */ + if ((st.st_uid != uid && uid != (uid_t)-1) || + (st.st_gid != gid && gid != (gid_t)-1)) { + if (fchown(fd, uid, gid) < 0) + i_fatal("fchown() failed for %s: %m", dir); + ret = changed_ret; + } + + if ((st.st_mode & 07777) != mode) { + if (fchmod(fd, mode) < 0) + i_fatal("chmod() failed for %s: %m", dir); + ret = changed_ret; + } + + if (close(fd) < 0) + i_fatal("close() failed for %s: %m", dir); + + /* paranoia: make sure we succeeded in everything. */ + if (lstat(dir, &st) < 0) + i_fatal("lstat() check failed for %s: %m", dir); + + if (!S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)) + i_fatal("Not a directory %s", dir); + + if ((st.st_mode & 07777) != mode) { + i_fatal("safe_mkdir() failed: %s (%o) is still not mode %o", + dir, (int)st.st_mode, (int)mode); + } + if ((st.st_uid != uid && uid != (uid_t)-1) || + (st.st_gid != gid && gid != (gid_t)-1)) { + i_fatal("safe_mkdir() failed: %s (%s, %s) " + "is still not owned by %s.%s", + dir, dec2str(st.st_uid), dec2str(st.st_gid), + dec2str(uid), dec2str(gid)); + } + + return ret; +} diff --git a/src/lib/safe-mkdir.h b/src/lib/safe-mkdir.h new file mode 100644 index 0000000..eb75fd1 --- /dev/null +++ b/src/lib/safe-mkdir.h @@ -0,0 +1,10 @@ +#ifndef SAFE_MKDIR_H +#define SAFE_MKDIR_H + +/* Either create a directory or make sure that it already exists with given + permissions. If anything fails, the i_fatal() is called. Returns 1 if + directory was created, 2 if it already existed with correct permissions, + 0 if we changed permissions. */ +int safe_mkdir(const char *dir, mode_t mode, uid_t uid, gid_t gid); + +#endif diff --git a/src/lib/safe-mkstemp.c b/src/lib/safe-mkstemp.c new file mode 100644 index 0000000..27b401e --- /dev/null +++ b/src/lib/safe-mkstemp.c @@ -0,0 +1,106 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "hex-binary.h" +#include "randgen.h" +#include "hostpid.h" +#include "eacces-error.h" +#include "safe-mkstemp.h" + +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> + +static int ATTR_NULL(5) +safe_mkstemp_full(string_t *prefix, mode_t mode, uid_t uid, gid_t gid, + const char *gid_origin) +{ + size_t prefix_len; + struct stat st; + unsigned char randbuf[8]; + mode_t old_umask; + int fd; + + prefix_len = str_len(prefix); + for (;;) { + do { + random_fill(randbuf, sizeof(randbuf)); + str_truncate(prefix, prefix_len); + str_append(prefix, + binary_to_hex(randbuf, sizeof(randbuf))); + } while (lstat(str_c(prefix), &st) == 0); + + if (errno != ENOENT) { + i_error("stat(%s) failed: %m", str_c(prefix)); + str_truncate(prefix, prefix_len); + return -1; + } + + old_umask = umask(0666 ^ mode); + fd = open(str_c(prefix), O_RDWR | O_EXCL | O_CREAT, 0666); + umask(old_umask); + if (fd != -1) + break; + + if (errno != EEXIST) { + if (errno != ENOENT && errno != EACCES) + i_error("open(%s) failed: %m", str_c(prefix)); + str_truncate(prefix, prefix_len); + return -1; + } + } + if (uid == (uid_t)-1 && gid == (gid_t)-1) + return fd; + + if (fchown(fd, uid, gid) < 0) { + if (errno == EPERM) { + i_error("%s", eperm_error_get_chgrp("fchown", + str_c(prefix), gid, gid_origin)); + } else { + i_error("fchown(%s, %ld, %ld) failed: %m", + str_c(prefix), + uid == (uid_t)-1 ? -1L : (long)uid, + gid == (gid_t)-1 ? -1L : (long)gid); + } + i_close_fd(&fd); + i_unlink(str_c(prefix)); + str_truncate(prefix, prefix_len); + return -1; + } + return fd; +} + +int safe_mkstemp(string_t *prefix, mode_t mode, uid_t uid, gid_t gid) +{ + return safe_mkstemp_full(prefix, mode, uid, gid, NULL); +} + +int safe_mkstemp_group(string_t *prefix, mode_t mode, + gid_t gid, const char *gid_origin) +{ + return safe_mkstemp_full(prefix, mode, (uid_t)-1, gid, gid_origin); +} + +int safe_mkstemp_hostpid(string_t *prefix, mode_t mode, uid_t uid, gid_t gid) +{ + size_t orig_prefix_len = str_len(prefix); + int fd; + + str_printfa(prefix, "%s.%s.", my_hostname, my_pid); + if ((fd = safe_mkstemp(prefix, mode, uid, gid)) == -1) + str_truncate(prefix, orig_prefix_len); + return fd; +} + +int safe_mkstemp_hostpid_group(string_t *prefix, mode_t mode, + gid_t gid, const char *gid_origin) +{ + size_t orig_prefix_len = str_len(prefix); + int fd; + + str_printfa(prefix, "%s.%s.", my_hostname, my_pid); + if ((fd = safe_mkstemp_group(prefix, mode, gid, gid_origin)) == -1) + str_truncate(prefix, orig_prefix_len); + return fd; +} diff --git a/src/lib/safe-mkstemp.h b/src/lib/safe-mkstemp.h new file mode 100644 index 0000000..f04d5cf --- /dev/null +++ b/src/lib/safe-mkstemp.h @@ -0,0 +1,15 @@ +#ifndef SAFE_MKSTEMP_H +#define SAFE_MKSTEMP_H + +/* Create a new file with a given prefix. The string is updated to contain the + created filename. uid and gid can be (uid_t)-1 and (gid_t)-1 to use the + defaults. */ +int safe_mkstemp(string_t *prefix, mode_t mode, uid_t uid, gid_t gid); +int safe_mkstemp_group(string_t *prefix, mode_t mode, + gid_t gid, const char *gid_origin); +/* Append host and PID to the prefix. */ +int safe_mkstemp_hostpid(string_t *prefix, mode_t mode, uid_t uid, gid_t gid); +int safe_mkstemp_hostpid_group(string_t *prefix, mode_t mode, + gid_t gid, const char *gid_origin); + +#endif diff --git a/src/lib/sendfile-util.c b/src/lib/sendfile-util.c new file mode 100644 index 0000000..662285b --- /dev/null +++ b/src/lib/sendfile-util.c @@ -0,0 +1,137 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +/* kludge a bit to remove _FILE_OFFSET_BITS definition from config.h. + It's required to be able to include sys/sendfile.h with Linux. */ +#include "config.h" +#undef HAVE_CONFIG_H + +#ifdef HAVE_LINUX_SENDFILE +# undef _FILE_OFFSET_BITS +#endif + +#include "lib.h" +#include "sendfile-util.h" + +#ifdef HAVE_LINUX_SENDFILE + +#include <sys/sendfile.h> + +ssize_t safe_sendfile(int out_fd, int in_fd, uoff_t *offset, size_t count) +{ + /* REMEMBER: uoff_t and off_t may not be of same size. */ + off_t safe_offset; + ssize_t ret; + + i_assert(count > 0); + + /* make sure given offset fits into off_t */ + if (sizeof(off_t) * CHAR_BIT == 32) { + /* 32bit off_t */ + if (*offset >= 2147483647L) { + errno = EINVAL; + return -1; + } + if (count > 2147483647L - *offset) + count = 2147483647L - *offset; + } else { + /* they're most likely the same size. if not, fix this + code later */ + i_assert(sizeof(off_t) == sizeof(uoff_t)); + + if (*offset >= OFF_T_MAX) { + errno = EINVAL; + return -1; + } + if (count > OFF_T_MAX - *offset) + count = OFF_T_MAX - *offset; + } + + safe_offset = (off_t)*offset; + ret = sendfile(out_fd, in_fd, &safe_offset, count); + /* ret=0 : trying to read past EOF */ + *offset = (uoff_t)safe_offset; + return ret; +} + +#elif defined(HAVE_FREEBSD_SENDFILE) + +#include <sys/socket.h> +#include <sys/uio.h> + +ssize_t safe_sendfile(int out_fd, int in_fd, uoff_t *offset, size_t count) +{ + struct sf_hdtr hdtr; + off_t sbytes; + int ret; + + /* if count=0 is passed to sendfile(), it sends everything + from in_fd until EOF. We don't want that. */ + i_assert(count > 0); + i_assert(count <= SSIZE_T_MAX); + + i_zero(&hdtr); + ret = sendfile(in_fd, out_fd, *offset, count, &hdtr, &sbytes, 0); + + *offset += sbytes; + + if (ret == 0 || (ret < 0 && errno == EAGAIN && sbytes > 0)) + return (ssize_t)sbytes; + else { + if (errno == ENOTSOCK) { + /* out_fd wasn't a socket. behave as if sendfile() + wasn't supported at all. */ + errno = EINVAL; + } + return -1; + } +} + +#elif defined (HAVE_SOLARIS_SENDFILE) + +#include <sys/sendfile.h> +#include "net.h" + +ssize_t safe_sendfile(int out_fd, int in_fd, uoff_t *offset, size_t count) +{ + ssize_t ret; + off_t s_offset; + + i_assert(count > 0); + i_assert(count <= SSIZE_T_MAX); + + /* NOTE: if outfd is not a socket, some Solaris versions will + kernel panic */ + + s_offset = (off_t)*offset; + ret = sendfile(out_fd, in_fd, &s_offset, count); + + if (ret < 0) { + /* if remote is gone, EPIPE is returned */ + if (errno == EINVAL) { + /* most likely trying to read past EOF */ + ret = 0; + } else if (errno == EAFNOSUPPORT || errno == EOPNOTSUPP) { + /* not supported, return Linux-like EINVAL so caller + sees only consistent errnos. */ + errno = EINVAL; + } else if (s_offset != (off_t)*offset) { + /* some data was sent, return it */ + i_assert(s_offset > (off_t)*offset); + ret = s_offset - (off_t)*offset; + } + } + *offset = (uoff_t)s_offset; + i_assert(ret < 0 || (size_t)ret <= count); + return ret; +} + +#else +ssize_t safe_sendfile(int out_fd ATTR_UNUSED, int in_fd ATTR_UNUSED, + uoff_t *offset ATTR_UNUSED, + size_t count ATTR_UNUSED) +{ + errno = EINVAL; + return -1; +} + +#endif diff --git a/src/lib/sendfile-util.h b/src/lib/sendfile-util.h new file mode 100644 index 0000000..3d7f14a --- /dev/null +++ b/src/lib/sendfile-util.h @@ -0,0 +1,16 @@ +#ifndef SENDFILE_UTIL_H +#define SENDFILE_UTIL_H + +/* Wrapper for various sendfile()-like calls. Read a maximum of count bytes + from the given offset in in_fd and write them to out_fd. The offset is + updated after the call. Note the call assert-crashes if count is 0. + + Returns: + >0 number of bytes successfully written (maybe less than count) + 0 if offset points to the input's EOF or past it + -1, errno=EINVAL if it isn't supported for some reason (out_fd isn't a + socket or there simply is no sendfile()). + -1, errno=EAGAIN if non-blocking write couldn't send anything */ +ssize_t safe_sendfile(int out_fd, int in_fd, uoff_t *offset, size_t count); + +#endif diff --git a/src/lib/seq-range-array.c b/src/lib/seq-range-array.c new file mode 100644 index 0000000..2d8dbfc --- /dev/null +++ b/src/lib/seq-range-array.c @@ -0,0 +1,569 @@ +/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "seq-range-array.h" + +static bool seq_range_is_overflowed(const ARRAY_TYPE(seq_range) *array) +{ + const struct seq_range *range; + unsigned int count; + + range = array_get(array, &count); + return count == 1 && range[0].seq1 == 0 && + range[0].seq2 == (uint32_t)-1; +} + +static bool ATTR_NOWARN_UNUSED_RESULT +seq_range_lookup(const ARRAY_TYPE(seq_range) *array, + uint32_t seq, unsigned int *idx_r) +{ + const struct seq_range *data; + unsigned int idx, left_idx, right_idx, count; + + data = array_get(array, &count); + i_assert(count < INT_MAX); + + idx = 0; left_idx = 0; right_idx = count; + while (left_idx < right_idx) { + idx = (left_idx + right_idx) / 2; + + if (data[idx].seq1 <= seq) { + if (data[idx].seq2 >= seq) { + /* it's already in the range */ + *idx_r = idx; + return TRUE; + } + left_idx = idx+1; + } else { + right_idx = idx; + } + } + if (left_idx > idx) + idx++; + *idx_r = idx; + return FALSE; +} + +static bool +seq_range_array_add_slow_path(ARRAY_TYPE(seq_range) *array, uint32_t seq) +{ + struct seq_range *data, value; + unsigned int idx, count; + + value.seq1 = value.seq2 = seq; + data = array_get_modifiable(array, &count); + + /* somewhere in the middle, array is sorted so find it with + binary search */ + if (seq_range_lookup(array, seq, &idx)) + return TRUE; + + /* idx == count couldn't happen because we already handle it above */ + i_assert(idx < count && data[idx].seq1 >= seq); + i_assert(data[idx].seq1 > seq || data[idx].seq2 < seq); + + if (data[idx].seq1 == seq+1) { + data[idx].seq1 = seq; + if (idx > 0 && data[idx-1].seq2 == seq-1) { + /* merge */ + data[idx-1].seq2 = data[idx].seq2; + array_delete(array, idx, 1); + } + } else { + if (idx > 0 && data[idx-1].seq2 == seq-1) + idx--; + if (data[idx].seq2 == seq-1) { + i_assert(idx+1 < count); /* already handled above */ + data[idx].seq2 = seq; + if (data[idx+1].seq1 == seq+1) { + /* merge */ + data[idx+1].seq1 = data[idx].seq1; + array_delete(array, idx, 1); + } + } else { + array_insert(array, idx, &value, 1); + } + } + return FALSE; +} + +bool seq_range_array_add(ARRAY_TYPE(seq_range) *array, uint32_t seq) +{ + struct seq_range *data, value; + unsigned int count; + bool exists = FALSE; + + value.seq1 = value.seq2 = seq; + + data = array_get_modifiable(array, &count); + /* quick checks */ + if (count == 0) + array_push_back(array, &value); + else if (data[count-1].seq2 < seq) { + if (data[count-1].seq2 == seq-1) { + /* grow last range */ + data[count-1].seq2 = seq; + } else { + array_push_back(array, &value); + } + } else if (data[0].seq1 > seq) { + if (data[0].seq1-1 == seq) { + /* grow down first range */ + data[0].seq1 = seq; + } else { + array_push_front(array, &value); + } + } else { + exists = seq_range_array_add_slow_path(array, seq); + } + i_assert(!seq_range_is_overflowed(array)); + return exists; +} + +void seq_range_array_add_with_init(ARRAY_TYPE(seq_range) *array, + unsigned int init_count, uint32_t seq) +{ + if (!array_is_created(array)) + i_array_init(array, init_count); + seq_range_array_add(array, seq); +} + +static void +seq_range_array_add_range_internal(ARRAY_TYPE(seq_range) *array, + uint32_t seq1, uint32_t seq2, + unsigned int *r_count) +{ + struct seq_range *data, value; + unsigned int idx1, idx2, count; + + seq_range_lookup(array, seq1, &idx1); + seq_range_lookup(array, seq2, &idx2); + + data = array_get_modifiable(array, &count); + if (r_count != NULL) { + /* Find number we're adding by counting the number we're + not adding, and subtracting that from the nominal range. */ + unsigned int added = seq2+1 - seq1; + unsigned int countidx2 = idx2; + unsigned int overcounted = 0u, notadded = 0u; + unsigned int i; + + if (idx1 == count) { + /* not in a range as too far right */ + } else if (seq1 < data[idx1].seq1) { + /* not in a range, to the left of a real range */ + } else { + /* count the whole of this range, which is an overcount */ + overcounted += seq1 - data[idx1].seq1; + /* fencepost check: equality means the whole range is valid, + therefore there's no overcounting. Result = 0 overcount */ + } + if (idx2 == count) { + /* not in a range as too far right */ + } else if (seq2 < data[idx2].seq1) { + /* not in a range, to the left of a real range */ + } else { + /* count the whole of this range, which is an overcount */ + overcounted += data[idx2].seq2 - seq2; + countidx2++; /* may become == count i.e. past the end */ + /* fencepost check: equality means the whole range is valid, + therefore there's no overcounting. Result = 0 overcount. */ + } + /* Now count how many we're not adding */ + for (i = idx1; i < countidx2; i++) + notadded += data[i].seq2+1 - data[i].seq1; + /* Maybe the not added tally included some over-counting too */ + added -= (notadded - overcounted); + *r_count = added; + } + + if (idx1 > 0 && data[idx1-1].seq2+1 == seq1) + idx1--; + + if (idx1 == idx2 && + (idx2 == count || (seq2 < (uint32_t)-1 && data[idx2].seq1 > seq2+1)) && + (idx1 == 0 || data[idx1-1].seq2 < seq1-1)) { + /* no overlapping */ + value.seq1 = seq1; + value.seq2 = seq2; + array_insert(array, idx1, &value, 1); + } else { + i_assert(idx1 < count); + if (seq1 < data[idx1].seq1) + data[idx1].seq1 = seq1; + if (seq2 > data[idx1].seq2) { + /* merge */ + if (idx2 == count || + data[idx2].seq1 > seq2+1) + idx2--; + if (seq2 >= data[idx2].seq2) { + data[idx1].seq2 = seq2; + } else { + data[idx1].seq2 = data[idx2].seq2; + } + array_delete(array, idx1 + 1, idx2 - idx1); + } + } + i_assert(!seq_range_is_overflowed(array)); +} + +void seq_range_array_add_range(ARRAY_TYPE(seq_range) *array, + uint32_t seq1, uint32_t seq2) +{ + seq_range_array_add_range_internal(array, seq1, seq2, NULL); +} +unsigned int seq_range_array_add_range_count(ARRAY_TYPE(seq_range) *array, + uint32_t seq1, uint32_t seq2) +{ + unsigned int count; + seq_range_array_add_range_internal(array, seq1, seq2, &count); + return count; +} + +void seq_range_array_merge(ARRAY_TYPE(seq_range) *dest, + const ARRAY_TYPE(seq_range) *src) +{ + const struct seq_range *range; + + if (array_count(dest) == 0) { + array_append_array(dest, src); + return; + } + + array_foreach(src, range) + seq_range_array_add_range(dest, range->seq1, range->seq2); +} + +void seq_range_array_merge_n(ARRAY_TYPE(seq_range) *dest, + const ARRAY_TYPE(seq_range) *src, + unsigned int count) +{ + const struct seq_range *src_range; + unsigned int src_idx, src_count; + unsigned int merge_count = count; + + src_range = array_get(src, &src_count); + for (src_idx = 0; src_idx < src_count && merge_count > 0; src_idx++) { + uint32_t first_seq = src_range[src_idx].seq1; + uint32_t last_seq = src_range[src_idx].seq2; + unsigned int idx_count = last_seq - first_seq + 1; + + if (idx_count > merge_count) { + last_seq = first_seq + merge_count - 1; + merge_count = 0; + } else { + merge_count -= idx_count; + } + seq_range_array_add_range(dest, first_seq, last_seq); + } +} + +bool seq_range_array_remove(ARRAY_TYPE(seq_range) *array, uint32_t seq) +{ + struct seq_range *data, value; + unsigned int idx, left_idx, right_idx, count; + + if (!array_is_created(array)) + return FALSE; + + data = array_get_modifiable(array, &count); + if (count == 0) + return FALSE; + + /* quick checks */ + if (seq > data[count-1].seq2 || seq < data[0].seq1) { + /* outside the range */ + return FALSE; + } + if (data[count-1].seq2 == seq) { + /* shrink last range */ + if (data[count-1].seq1 != data[count-1].seq2) + data[count-1].seq2--; + else + array_delete(array, count-1, 1); + return TRUE; + } + if (data[0].seq1 == seq) { + /* shrink up first range */ + if (data[0].seq1 != data[0].seq2) + data[0].seq1++; + else + array_pop_front(array); + return TRUE; + } + + /* somewhere in the middle, array is sorted so find it with + binary search */ + i_assert(count < INT_MAX); + left_idx = 0; right_idx = count; + while (left_idx < right_idx) { + idx = (left_idx + right_idx) / 2; + + if (data[idx].seq1 > seq) + right_idx = idx; + else if (data[idx].seq2 < seq) + left_idx = idx+1; + else { + /* found it */ + if (data[idx].seq1 == seq) { + if (data[idx].seq1 == data[idx].seq2) { + /* a single sequence range. + remove it entirely */ + array_delete(array, idx, 1); + } else { + /* shrink the range */ + data[idx].seq1++; + } + } else if (data[idx].seq2 == seq) { + /* shrink the range */ + data[idx].seq2--; + } else { + /* split the sequence range */ + value.seq1 = seq + 1; + value.seq2 = data[idx].seq2; + data[idx].seq2 = seq - 1; + + array_insert(array, idx + 1, &value, 1); + } + return TRUE; + } + } + return FALSE; +} + +unsigned int seq_range_array_remove_range(ARRAY_TYPE(seq_range) *array, + uint32_t seq1, uint32_t seq2) +{ + const struct seq_range *data; + unsigned int idx, idx2, count, remove_count = 0; + + /* remove first and last. this makes sure that everything between + can simply be deleted with array_delete(). + + FIXME: it would be faster if we did only one binary lookup here + and handled the splitting ourself.. */ + if (seq_range_array_remove(array, seq1)) + remove_count++; + if (seq1 == seq2) + return remove_count; + seq1++; + + if (seq_range_array_remove(array, seq2--)) + remove_count++; + if (seq1 > seq2) + return remove_count; + + /* find the beginning */ + data = array_get(array, &count); + seq_range_lookup(array, seq1, &idx); + + if (idx == count) + return remove_count; + + i_assert(data[idx].seq1 >= seq1); + for (idx2 = idx; idx2 < count; idx2++) { + if (data[idx2].seq1 > seq2) + break; + i_assert(UINT_MAX - remove_count >= seq_range_length(&data[idx2])); + remove_count += seq_range_length(&data[idx2]); + } + array_delete(array, idx, idx2-idx); + return remove_count; +} + +unsigned int seq_range_array_remove_seq_range(ARRAY_TYPE(seq_range) *dest, + const ARRAY_TYPE(seq_range) *src) +{ + unsigned int count, full_count = 0; + const struct seq_range *src_range; + + array_foreach(src, src_range) { + count = seq_range_array_remove_range(dest, src_range->seq1, + src_range->seq2); + i_assert(UINT_MAX - full_count >= count); + full_count += count; + } + return full_count; +} + +void seq_range_array_remove_nth(ARRAY_TYPE(seq_range) *array, + uint32_t n, uint32_t count) +{ + struct seq_range_iter iter; + uint32_t seq1, seq2; + + if (count == 0) + return; + + seq_range_array_iter_init(&iter, array); + if (!seq_range_array_iter_nth(&iter, n, &seq1)) { + /* n points beyond array */ + return; + } + if (count-1 >= (uint32_t)-1 - n || + !seq_range_array_iter_nth(&iter, n + (count-1), &seq2)) { + /* count points beyond array */ + seq2 = (uint32_t)-1; + } + seq_range_array_remove_range(array, seq1, seq2); +} + +unsigned int seq_range_array_intersect(ARRAY_TYPE(seq_range) *dest, + const ARRAY_TYPE(seq_range) *src) +{ + const struct seq_range *src_range; + unsigned int i, count, remove_count, full_count = 0; + uint32_t last_seq = 0; + + src_range = array_get(src, &count); + for (i = 0; i < count; i++) { + if (last_seq + 1 < src_range[i].seq1) { + remove_count = seq_range_array_remove_range(dest, + last_seq + 1, src_range[i].seq1 - 1); + i_assert(UINT_MAX - full_count >= remove_count); + full_count += remove_count; + } + last_seq = src_range[i].seq2; + } + if (last_seq != (uint32_t)-1) { + remove_count = seq_range_array_remove_range(dest, last_seq + 1, + (uint32_t)-1); + i_assert(UINT_MAX - full_count >= remove_count); + full_count += remove_count; + } + return full_count; +} + +bool seq_range_exists(const ARRAY_TYPE(seq_range) *array, uint32_t seq) +{ + unsigned int idx; + + return seq_range_lookup(array, seq, &idx); +} + +bool seq_range_array_have_common(const ARRAY_TYPE(seq_range) *array1, + const ARRAY_TYPE(seq_range) *array2) +{ + const struct seq_range *range1, *range2; + unsigned int i1, i2, count1, count2; + + range1 = array_get(array1, &count1); + range2 = array_get(array2, &count2); + for (i1 = i2 = 0; i1 < count1 && i2 < count2; ) { + if (range1[i1].seq1 <= range2[i2].seq2 && + range1[i1].seq2 >= range2[i2].seq1) + return TRUE; + + if (range1[i1].seq1 < range2[i2].seq1) + i1++; + else + i2++; + } + return FALSE; +} + +unsigned int seq_range_count(const ARRAY_TYPE(seq_range) *array) +{ + const struct seq_range *range; + unsigned int seq_count = 0; + + array_foreach(array, range) { + i_assert(UINT_MAX - seq_count >= seq_range_length(range)); + seq_count += seq_range_length(range); + } + return seq_count; +} + +void seq_range_array_invert(ARRAY_TYPE(seq_range) *array, + uint32_t min_seq, uint32_t max_seq) +{ + struct seq_range *range, value; + unsigned int i, count; + uint32_t prev_min_seq; + + if (array_is_created(array)) + range = array_get_modifiable(array, &count); + else { + range = NULL; + count = 0; + } + if (count == 0) { + /* empty -> full */ + if (!array_is_created(array)) + i_array_init(array, 4); + value.seq1 = min_seq; + value.seq2 = max_seq; + array_push_back(array, &value); + return; + } + i_assert(range[0].seq1 >= min_seq); + i_assert(range[count-1].seq2 <= max_seq); + + if (range[0].seq1 == min_seq && range[0].seq2 == max_seq) { + /* full -> empty */ + array_clear(array); + return; + } + + for (i = 0; i < count; ) { + prev_min_seq = min_seq; + min_seq = range[i].seq2; + if (range[i].seq1 == prev_min_seq) { + array_delete(array, i, 1); + range = array_get_modifiable(array, &count); + } else { + range[i].seq2 = range[i].seq1 - 1; + range[i].seq1 = prev_min_seq; + i++; + } + if (min_seq >= max_seq) { + /* max_seq is reached. the rest of the array should be + empty. we'll return here, because min_seq++ may + wrap to 0. */ + i_assert(min_seq == max_seq); + i_assert(i == count); + return; + } + min_seq++; + } + if (min_seq <= max_seq) { + value.seq1 = min_seq; + value.seq2 = max_seq; + array_push_back(array, &value); + } +} + +void seq_range_array_iter_init(struct seq_range_iter *iter_r, + const ARRAY_TYPE(seq_range) *array) +{ + i_zero(iter_r); + iter_r->array = array; +} + +bool seq_range_array_iter_nth(struct seq_range_iter *iter, unsigned int n, + uint32_t *seq_r) +{ + const struct seq_range *range; + unsigned int i, count, diff; + + if (n < iter->prev_n) { + /* iterating backwards, don't bother optimizing */ + iter->prev_n = 0; + iter->prev_idx = 0; + } + + range = array_get(iter->array, &count); + for (i = iter->prev_idx; i < count; i++) { + diff = range[i].seq2 - range[i].seq1; + if (n <= iter->prev_n + diff) { + i_assert(n >= iter->prev_n); + *seq_r = range[i].seq1 + (n - iter->prev_n); + iter->prev_idx = i; + return TRUE; + } + iter->prev_n += diff + 1; + } + iter->prev_idx = i; + return FALSE; +} diff --git a/src/lib/seq-range-array.h b/src/lib/seq-range-array.h new file mode 100644 index 0000000..e323ead --- /dev/null +++ b/src/lib/seq-range-array.h @@ -0,0 +1,82 @@ +#ifndef SEQ_RANGE_ARRAY_H +#define SEQ_RANGE_ARRAY_H + +/* NOTE: A full 0..UINT_MAX sequence range isn't valid to use here, because its + size would become UINT_MAX+1, which can't be returned by e.g. + seq_range_count() and similar functions. Attempting to use such sequence + ranges will result in assert-crash. */ + +struct seq_range { + uint32_t seq1, seq2; +}; +ARRAY_DEFINE_TYPE(seq_range, struct seq_range); + +struct seq_range_iter { + const ARRAY_TYPE(seq_range) *array; + unsigned int prev_n, prev_idx; +}; + +static inline uint32_t ATTR_PURE seq_range_length(const struct seq_range *range) +{ + i_assert(range->seq2 >= range->seq1); + i_assert(range->seq1 > 0 || range->seq2 < (uint32_t)-1); + return range->seq2 - range->seq1 + 1; +} + +/* Add sequence to range. If the array isn't created yet, create it with + initial size of init_count. */ +bool ATTR_NOWARN_UNUSED_RESULT +seq_range_array_add(ARRAY_TYPE(seq_range) *array, uint32_t seq); +/* Like seq_range_array_add(), but if the array isn't already initialized do + it with i_array_init(). */ +void seq_range_array_add_with_init(ARRAY_TYPE(seq_range) *array, + unsigned int init_count, uint32_t seq); +void seq_range_array_add_range(ARRAY_TYPE(seq_range) *array, + uint32_t seq1, uint32_t seq2); +unsigned int seq_range_array_add_range_count(ARRAY_TYPE(seq_range) *array, + uint32_t seq1, uint32_t seq2); +void seq_range_array_merge(ARRAY_TYPE(seq_range) *dest, + const ARRAY_TYPE(seq_range) *src); +/* Merge the first n sequences from src into dest. */ +void seq_range_array_merge_n(ARRAY_TYPE(seq_range) *dest, + const ARRAY_TYPE(seq_range) *src, + unsigned int count); +/* Remove the given sequence from range. Returns TRUE if it was found. */ +bool ATTR_NOWARN_UNUSED_RESULT +seq_range_array_remove(ARRAY_TYPE(seq_range) *array, uint32_t seq); +/* Remove a sequence range. Returns number of sequences actually removed. */ +unsigned int ATTR_NOWARN_UNUSED_RESULT +seq_range_array_remove_range(ARRAY_TYPE(seq_range) *array, + uint32_t seq1, uint32_t seq2); +unsigned int ATTR_NOWARN_UNUSED_RESULT +seq_range_array_remove_seq_range(ARRAY_TYPE(seq_range) *dest, + const ARRAY_TYPE(seq_range) *src); +/* Remove count number of sequences from the nth sequence (0 = first). */ +void seq_range_array_remove_nth(ARRAY_TYPE(seq_range) *array, + uint32_t n, uint32_t count); +/* Remove sequences from dest that don't exist in src. */ +unsigned int ATTR_NOWARN_UNUSED_RESULT +seq_range_array_intersect(ARRAY_TYPE(seq_range) *dest, + const ARRAY_TYPE(seq_range) *src); +/* Returns TRUE if sequence exists in the range. */ +bool seq_range_exists(const ARRAY_TYPE(seq_range) *array, + uint32_t seq) ATTR_PURE; +/* Returns TRUE if arrays have common sequences. */ +bool seq_range_array_have_common(const ARRAY_TYPE(seq_range) *array1, + const ARRAY_TYPE(seq_range) *array2) ATTR_PURE; +/* Return number of sequences in the range. */ +unsigned int seq_range_count(const ARRAY_TYPE(seq_range) *array) ATTR_PURE; + +/* Invert the sequence range. For example 5:6 -> min_seq:4,7:max_seq. + The array must not have any sequences outside min_seq..max_seq or this + function will assert-crash. */ +void seq_range_array_invert(ARRAY_TYPE(seq_range) *array, + uint32_t min_seq, uint32_t max_seq); + +void seq_range_array_iter_init(struct seq_range_iter *iter_r, + const ARRAY_TYPE(seq_range) *array); +/* Get the nth sequence (0 = first). Returns FALSE if idx is too large. */ +bool seq_range_array_iter_nth(struct seq_range_iter *iter, unsigned int n, + uint32_t *seq_r); + +#endif diff --git a/src/lib/seq-set-builder.c b/src/lib/seq-set-builder.c new file mode 100644 index 0000000..b5649b7 --- /dev/null +++ b/src/lib/seq-set-builder.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "seq-set-builder.h" + +struct seqset_builder { + string_t *str; + uint32_t last_seq; + size_t last_seq_pos; + size_t prefix_length; +}; + +struct seqset_builder *seqset_builder_init(string_t *str) +{ + struct seqset_builder *builder; + builder = i_new(struct seqset_builder, 1); + builder->str = str; + builder->last_seq = 0; + builder->prefix_length = str_len(str); + builder->last_seq_pos = 0; + return builder; +} + +static void +seqset_builder_append_one(struct seqset_builder *builder, uint32_t seq) +{ + builder->last_seq_pos = str_len(builder->str)+1; + str_printfa(builder->str, "%u,", seq); +} + +static void +seqset_builder_create_or_merge_range(struct seqset_builder *builder, + uint32_t seq) +{ + char delimiter = '\0'; + + i_assert(builder->last_seq_pos > builder->prefix_length); + + str_truncate(builder->str, builder->last_seq_pos-1); + + /* Get the delimiter from the builder string */ + if (str_len(builder->str) > 0 && + str_len(builder->str) - 1 > builder->prefix_length) + delimiter = str_data(builder->str)[str_len(builder->str) - 1]; + + if (delimiter == ':') { + seqset_builder_append_one(builder, seq); + } else if (delimiter == ',' || delimiter == '\0') { + str_printfa(builder->str, "%u:", builder->last_seq); + builder->last_seq_pos = str_len(builder->str) + 1; + str_printfa(builder->str, "%u,", seq); + } else + i_unreached(); + return; +} + +void seqset_builder_add(struct seqset_builder *builder, uint32_t seq) +{ + if (builder->last_seq == 0) { + /* No seq was yet appened so just append this one */ + seqset_builder_append_one(builder, seq); + } else if (builder->last_seq + 1 == seq) { + /* This seq is following directly on the previous one + try to create a range of seqs */ + seqset_builder_create_or_merge_range(builder, seq); + } else { + /* Append this seq without creating a range */ + seqset_builder_append_one(builder, seq); + } + builder->last_seq = seq; +} + +bool seqset_builder_try_add(struct seqset_builder *builder, size_t max_len, + uint32_t seq) +{ + /* Length of this sequence to be appended */ + unsigned int seq_len = 0; + /* Buffer to use when calculating seq length as string */ + char seq_str[MAX_INT_STRLEN]; + /* Current length of the seq string */ + unsigned int builder_str_len = str_len(builder->str); + + if (builder->last_seq + 1 == seq && builder_str_len + 1 <= max_len) { + /* Following sequence: This seq can't grow the overall length + by more than one. */ + seqset_builder_add(builder, seq); + return TRUE; + } + + if (builder_str_len + MAX_INT_STRLEN + 1 <= max_len) { + /* Appending the maximum length of a sequence number and ',' + still fits into max_len. There is no need to check the + actual length. */ + seqset_builder_add(builder, seq); + return TRUE; + } + + seq_len = strlen(dec2str_buf(seq_str, seq)) + 1; + if (seq_len + builder_str_len > max_len) + return FALSE; + + seqset_builder_add(builder, seq); + return TRUE; +} + +void seqset_builder_deinit(struct seqset_builder **builder) +{ + /* If anything was appened to the string remove the trailing ',' */ + if ((*builder)->last_seq != 0) + str_truncate((*builder)->str, str_len((*builder)->str) - 1); + i_free(*builder); +} diff --git a/src/lib/seq-set-builder.h b/src/lib/seq-set-builder.h new file mode 100644 index 0000000..37d9cbc --- /dev/null +++ b/src/lib/seq-set-builder.h @@ -0,0 +1,15 @@ +#ifndef SEQ_SET_BUILDER_H +#define SEQ_SET_BUILDER_H + +/* Append a seqset to the given string. */ +struct seqset_builder *seqset_builder_init(string_t *str); +/* Add seq to the string. The string must not have been modified before the previous + seqset_builder_add() call, since the last sequence in it may be rewritten. */ +void seqset_builder_add(struct seqset_builder *builder, uint32_t seq); +/* Add the seq to the string, but only if the string length stays below max_len. + Returns TRUE if added, FALSE if not. */ +bool seqset_builder_try_add(struct seqset_builder *builder, size_t max_len, uint32_t seq); +/* Deinitialize the builder */ +void seqset_builder_deinit(struct seqset_builder **builder); + +#endif diff --git a/src/lib/sha-common.h b/src/lib/sha-common.h new file mode 100644 index 0000000..abc63ae --- /dev/null +++ b/src/lib/sha-common.h @@ -0,0 +1,12 @@ +#ifndef SHA_COMMON + +#define SHA256_RESULTLEN (256 / 8) +#define SHA256_BLOCK_SIZE (512 / 8) + +#define SHA384_RESULTLEN (384 / 8) +#define SHA384_BLOCK_SIZE (1024 / 8) + +#define SHA512_RESULTLEN (512 / 8) +#define SHA512_BLOCK_SIZE (1024 / 8) + +#endif diff --git a/src/lib/sha1.c b/src/lib/sha1.c new file mode 100644 index 0000000..b647a91 --- /dev/null +++ b/src/lib/sha1.c @@ -0,0 +1,288 @@ +/* $KAME: sha1.c,v 1.5 2000/11/08 06:13:08 itojun Exp $ */ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT 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. + */ + +/* + * FIPS pub 180-1: Secure Hash Algorithm (SHA-1) + * based on: http://csrc.nist.gov/fips/fip180-1.txt + * implemented by Jun-ichiro itojun Itoh <itojun@itojun.org> + */ + +#include "lib.h" +#include "sha1.h" +#include "safe-memset.h" + +/* constant table */ +static uint32_t SHA1_K[] = { 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 }; +#define K(t) SHA1_K[(t) / 20] + +#define F0(b, c, d) (((b) & (c)) | ((~(b)) & (d))) +#define F1(b, c, d) (((b) ^ (c)) ^ (d)) +#define F2(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d))) +#define F3(b, c, d) (((b) ^ (c)) ^ (d)) + +#define S(n, x) (((x) << (n)) | ((x) >> (32 - n))) + +#define H(n) (ctxt->h.b32[(n)]) +#define COUNT (ctxt->count) +#define BCOUNT (ctxt->c.b64[0] / 8) +#define W(n) (ctxt->m.b32[(n)]) + +#define PUTBYTE(x) { \ + ctxt->m.b8[(COUNT % 64)] = (x); \ + COUNT++; \ + COUNT %= 64; \ + ctxt->c.b64[0] += 8; \ + if (COUNT % 64 == 0) \ + sha1_step(ctxt); \ + } + +#define PUTPAD(x) { \ + ctxt->m.b8[(COUNT % 64)] = (x); \ + COUNT++; \ + COUNT %= 64; \ + if (COUNT % 64 == 0) \ + sha1_step(ctxt); \ + } + +static void sha1_step(struct sha1_ctxt *); + +static void ATTR_UNSIGNED_WRAPS +sha1_step(struct sha1_ctxt *ctxt) +{ + uint32_t a, b, c, d, e; + size_t t, s; + uint32_t tmp; + +#ifndef WORDS_BIGENDIAN + struct sha1_ctxt tctxt; + memmove(&tctxt.m.b8[0], &ctxt->m.b8[0], 64); + ctxt->m.b8[0] = tctxt.m.b8[3]; ctxt->m.b8[1] = tctxt.m.b8[2]; + ctxt->m.b8[2] = tctxt.m.b8[1]; ctxt->m.b8[3] = tctxt.m.b8[0]; + ctxt->m.b8[4] = tctxt.m.b8[7]; ctxt->m.b8[5] = tctxt.m.b8[6]; + ctxt->m.b8[6] = tctxt.m.b8[5]; ctxt->m.b8[7] = tctxt.m.b8[4]; + ctxt->m.b8[8] = tctxt.m.b8[11]; ctxt->m.b8[9] = tctxt.m.b8[10]; + ctxt->m.b8[10] = tctxt.m.b8[9]; ctxt->m.b8[11] = tctxt.m.b8[8]; + ctxt->m.b8[12] = tctxt.m.b8[15]; ctxt->m.b8[13] = tctxt.m.b8[14]; + ctxt->m.b8[14] = tctxt.m.b8[13]; ctxt->m.b8[15] = tctxt.m.b8[12]; + ctxt->m.b8[16] = tctxt.m.b8[19]; ctxt->m.b8[17] = tctxt.m.b8[18]; + ctxt->m.b8[18] = tctxt.m.b8[17]; ctxt->m.b8[19] = tctxt.m.b8[16]; + ctxt->m.b8[20] = tctxt.m.b8[23]; ctxt->m.b8[21] = tctxt.m.b8[22]; + ctxt->m.b8[22] = tctxt.m.b8[21]; ctxt->m.b8[23] = tctxt.m.b8[20]; + ctxt->m.b8[24] = tctxt.m.b8[27]; ctxt->m.b8[25] = tctxt.m.b8[26]; + ctxt->m.b8[26] = tctxt.m.b8[25]; ctxt->m.b8[27] = tctxt.m.b8[24]; + ctxt->m.b8[28] = tctxt.m.b8[31]; ctxt->m.b8[29] = tctxt.m.b8[30]; + ctxt->m.b8[30] = tctxt.m.b8[29]; ctxt->m.b8[31] = tctxt.m.b8[28]; + ctxt->m.b8[32] = tctxt.m.b8[35]; ctxt->m.b8[33] = tctxt.m.b8[34]; + ctxt->m.b8[34] = tctxt.m.b8[33]; ctxt->m.b8[35] = tctxt.m.b8[32]; + ctxt->m.b8[36] = tctxt.m.b8[39]; ctxt->m.b8[37] = tctxt.m.b8[38]; + ctxt->m.b8[38] = tctxt.m.b8[37]; ctxt->m.b8[39] = tctxt.m.b8[36]; + ctxt->m.b8[40] = tctxt.m.b8[43]; ctxt->m.b8[41] = tctxt.m.b8[42]; + ctxt->m.b8[42] = tctxt.m.b8[41]; ctxt->m.b8[43] = tctxt.m.b8[40]; + ctxt->m.b8[44] = tctxt.m.b8[47]; ctxt->m.b8[45] = tctxt.m.b8[46]; + ctxt->m.b8[46] = tctxt.m.b8[45]; ctxt->m.b8[47] = tctxt.m.b8[44]; + ctxt->m.b8[48] = tctxt.m.b8[51]; ctxt->m.b8[49] = tctxt.m.b8[50]; + ctxt->m.b8[50] = tctxt.m.b8[49]; ctxt->m.b8[51] = tctxt.m.b8[48]; + ctxt->m.b8[52] = tctxt.m.b8[55]; ctxt->m.b8[53] = tctxt.m.b8[54]; + ctxt->m.b8[54] = tctxt.m.b8[53]; ctxt->m.b8[55] = tctxt.m.b8[52]; + ctxt->m.b8[56] = tctxt.m.b8[59]; ctxt->m.b8[57] = tctxt.m.b8[58]; + ctxt->m.b8[58] = tctxt.m.b8[57]; ctxt->m.b8[59] = tctxt.m.b8[56]; + ctxt->m.b8[60] = tctxt.m.b8[63]; ctxt->m.b8[61] = tctxt.m.b8[62]; + ctxt->m.b8[62] = tctxt.m.b8[61]; ctxt->m.b8[63] = tctxt.m.b8[60]; +#endif + + a = H(0); b = H(1); c = H(2); d = H(3); e = H(4); + + for (t = 0; t < 20; t++) { + s = t & 0x0f; + if (t >= 16) { + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); + } + tmp = S(5, a) + F0(b, c, d) + e + W(s) + K(t); + e = d; d = c; c = S(30, b); b = a; a = tmp; + } + for (t = 20; t < 40; t++) { + s = t & 0x0f; + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); + tmp = S(5, a) + F1(b, c, d) + e + W(s) + K(t); + e = d; d = c; c = S(30, b); b = a; a = tmp; + } + for (t = 40; t < 60; t++) { + s = t & 0x0f; + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); + tmp = S(5, a) + F2(b, c, d) + e + W(s) + K(t); + e = d; d = c; c = S(30, b); b = a; a = tmp; + } + for (t = 60; t < 80; t++) { + s = t & 0x0f; + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); + tmp = S(5, a) + F3(b, c, d) + e + W(s) + K(t); + e = d; d = c; c = S(30, b); b = a; a = tmp; + } + + H(0) = H(0) + a; + H(1) = H(1) + b; + H(2) = H(2) + c; + H(3) = H(3) + d; + H(4) = H(4) + e; + + memset(&ctxt->m.b8[0], 0, 64); +} + +/*------------------------------------------------------------*/ + +void +sha1_init(struct sha1_ctxt *ctxt) +{ + memset(ctxt, 0, sizeof(struct sha1_ctxt)); + H(0) = 0x67452301; + H(1) = 0xefcdab89; + H(2) = 0x98badcfe; + H(3) = 0x10325476; + H(4) = 0xc3d2e1f0; +} + +void +sha1_pad(struct sha1_ctxt *ctxt) +{ + size_t padlen; /*pad length in bytes*/ + size_t padstart; + + PUTPAD(0x80); + + padstart = COUNT % 64; + padlen = 64 - padstart; + if (padlen < 8) { + memset(&ctxt->m.b8[padstart], 0, padlen); + COUNT += padlen; + COUNT %= 64; + sha1_step(ctxt); + padstart = COUNT % 64; /* should be 0 */ + padlen = 64 - padstart; /* should be 64 */ + } + memset(&ctxt->m.b8[padstart], 0, padlen - 8); + COUNT += (padlen - 8); + COUNT %= 64; +#ifdef WORDS_BIGENDIAN + PUTPAD(ctxt->c.b8[0]); PUTPAD(ctxt->c.b8[1]); + PUTPAD(ctxt->c.b8[2]); PUTPAD(ctxt->c.b8[3]); + PUTPAD(ctxt->c.b8[4]); PUTPAD(ctxt->c.b8[5]); + PUTPAD(ctxt->c.b8[6]); PUTPAD(ctxt->c.b8[7]); +#else + PUTPAD(ctxt->c.b8[7]); PUTPAD(ctxt->c.b8[6]); + PUTPAD(ctxt->c.b8[5]); PUTPAD(ctxt->c.b8[4]); + PUTPAD(ctxt->c.b8[3]); PUTPAD(ctxt->c.b8[2]); + PUTPAD(ctxt->c.b8[1]); PUTPAD(ctxt->c.b8[0]); +#endif +} + +void +sha1_loop(struct sha1_ctxt *ctxt, const void *input, size_t len) +{ + const unsigned char *input_c = input; + size_t gaplen; + size_t gapstart; + size_t off; + size_t copysiz; + + off = 0; + + while (off < len) { + gapstart = COUNT % 64; + gaplen = 64 - gapstart; + + copysiz = (gaplen < len - off) ? gaplen : len - off; + memmove(&ctxt->m.b8[gapstart], &input_c[off], copysiz); + COUNT += copysiz; + COUNT %= 64; + ctxt->c.b64[0] += copysiz * 8; + if (COUNT % 64 == 0) + sha1_step(ctxt); + off += copysiz; + } +} + +void +sha1_result(struct sha1_ctxt *ctxt, void *digest0) +{ + uint8_t *digest; + + digest = (uint8_t *)digest0; + sha1_pad(ctxt); +#ifdef WORDS_BIGENDIAN + memmove(digest, &ctxt->h.b8[0], 20); +#else + digest[0] = ctxt->h.b8[3]; digest[1] = ctxt->h.b8[2]; + digest[2] = ctxt->h.b8[1]; digest[3] = ctxt->h.b8[0]; + digest[4] = ctxt->h.b8[7]; digest[5] = ctxt->h.b8[6]; + digest[6] = ctxt->h.b8[5]; digest[7] = ctxt->h.b8[4]; + digest[8] = ctxt->h.b8[11]; digest[9] = ctxt->h.b8[10]; + digest[10] = ctxt->h.b8[9]; digest[11] = ctxt->h.b8[8]; + digest[12] = ctxt->h.b8[15]; digest[13] = ctxt->h.b8[14]; + digest[14] = ctxt->h.b8[13]; digest[15] = ctxt->h.b8[12]; + digest[16] = ctxt->h.b8[19]; digest[17] = ctxt->h.b8[18]; + digest[18] = ctxt->h.b8[17]; digest[19] = ctxt->h.b8[16]; +#endif + safe_memset(ctxt, 0, sizeof(struct sha1_ctxt)); +} + +void sha1_get_digest(const void *data, size_t size, + unsigned char result[STATIC_ARRAY SHA1_RESULTLEN]) +{ + struct sha1_ctxt ctx; + + sha1_init(&ctx); + sha1_loop(&ctx, data, size); + sha1_result(&ctx, result); +} + +static void hash_method_init_sha1(void *context) +{ + sha1_init(context); +} +static void hash_method_loop_sha1(void *context, const void *data, size_t size) +{ + sha1_loop(context, data, size); +} + +static void hash_method_result_sha1(void *context, unsigned char *result_r) +{ + sha1_result(context, result_r); +} + +const struct hash_method hash_method_sha1 = { + .name = "sha1", + .block_size = 64, /* block size is 512 bits */ + .context_size = sizeof(struct sha1_ctxt), + .digest_size = SHA1_RESULTLEN, + + .init = hash_method_init_sha1, + .loop = hash_method_loop_sha1, + .result = hash_method_result_sha1, +}; diff --git a/src/lib/sha1.h b/src/lib/sha1.h new file mode 100644 index 0000000..c8e1e67 --- /dev/null +++ b/src/lib/sha1.h @@ -0,0 +1,84 @@ +/* $FreeBSD: src/sys/crypto/sha1.h,v 1.8 2002/03/20 05:13:50 alfred Exp $ */ +/* $KAME: sha1.h,v 1.5 2000/03/27 04:36:23 sumikawa Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT 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. + */ +/* + * FIPS pub 180-1: Secure Hash Algorithm (SHA-1) + * based on: http://csrc.nist.gov/fips/fip180-1.txt + * implemented by Jun-ichiro itojun Itoh <itojun@itojun.org> + */ + +#ifndef SHA1_H +#define SHA1_H + +#include "hash-method.h" + +/* libmysqlclient really should try to keep its internal stuff internal so + they won't conflict with the actual programs that are trying to use it. + This particular instance has been fixed in 4.1.18 and 5.0.19, but there + are others. */ +#define sha1_result sha1_result_libmysqlclient_craps_all_over + +struct sha1_ctxt { + union { + uint8_t b8[20]; + uint32_t b32[5]; + } h; + union { + uint8_t b8[8]; + uint64_t b64[1]; + } c; + union { + uint8_t b8[64]; + uint32_t b32[16]; + } m; + uint8_t count; +}; + +extern void sha1_init(struct sha1_ctxt *); +extern void sha1_pad(struct sha1_ctxt *); +extern void sha1_loop(struct sha1_ctxt *, const void *, size_t); +extern void sha1_result(struct sha1_ctxt *, void *); + + +/* compatibility with other SHA1 source codes */ +typedef struct sha1_ctxt SHA1_CTX; +#define SHA1Init(x) sha1_init((x)) +#define SHA1Update(x, y, z) sha1_loop((x), (y), (z)) +#define SHA1Final(x, y) sha1_result((y), (x)) + +#define SHA1_RESULTLEN (160/8) + +extern void sha1_get_digest(const void *data, size_t size, + unsigned char result[STATIC_ARRAY SHA1_RESULTLEN]); + +extern const struct hash_method hash_method_sha1; + +#endif diff --git a/src/lib/sha2.c b/src/lib/sha2.c new file mode 100644 index 0000000..93dddfb --- /dev/null +++ b/src/lib/sha2.c @@ -0,0 +1,647 @@ +/* + * FIPS 180-2 SHA-224/256/384/512 implementation + * Last update: 02/02/2007 + * Issue date: 04/30/2005 + * + * Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch> + * 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. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "lib.h" +#include "sha2.h" + +#define SHFR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) +#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) +#define CH(x, y, z) ((x & y) ^ (~x & z)) +#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) + +#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3)) +#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10)) + +#define SHA384_F1(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)) +#define SHA384_F2(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)) +#define SHA384_F3(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHFR(x, 7)) +#define SHA384_F4(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHFR(x, 6)) + +#define SHA512_F1(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)) +#define SHA512_F2(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)) +#define SHA512_F3(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHFR(x, 7)) +#define SHA512_F4(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHFR(x, 6)) + +#define UNPACK32(x, str) \ +{ \ + *((str) + 3) = (uint8_t) ((x) ); \ + *((str) + 2) = (uint8_t) ((x) >> 8); \ + *((str) + 1) = (uint8_t) ((x) >> 16); \ + *((str) + 0) = (uint8_t) ((x) >> 24); \ +} + +#define PACK32(str, x) \ +{ \ + *(x) = ((uint32_t) *((str) + 3) ) \ + | ((uint32_t) *((str) + 2) << 8) \ + | ((uint32_t) *((str) + 1) << 16) \ + | ((uint32_t) *((str) + 0) << 24); \ +} + +#define UNPACK64(x, str) \ +{ \ + *((str) + 7) = (uint8_t) ((x) ); \ + *((str) + 6) = (uint8_t) ((x) >> 8); \ + *((str) + 5) = (uint8_t) ((x) >> 16); \ + *((str) + 4) = (uint8_t) ((x) >> 24); \ + *((str) + 3) = (uint8_t) ((x) >> 32); \ + *((str) + 2) = (uint8_t) ((x) >> 40); \ + *((str) + 1) = (uint8_t) ((x) >> 48); \ + *((str) + 0) = (uint8_t) ((x) >> 56); \ +} + +#define PACK64(str, x) \ +{ \ + *(x) = ((uint64_t) *((str) + 7) ) \ + | ((uint64_t) *((str) + 6) << 8) \ + | ((uint64_t) *((str) + 5) << 16) \ + | ((uint64_t) *((str) + 4) << 24) \ + | ((uint64_t) *((str) + 3) << 32) \ + | ((uint64_t) *((str) + 2) << 40) \ + | ((uint64_t) *((str) + 1) << 48) \ + | ((uint64_t) *((str) + 0) << 56); \ +} + +#define SHA256_SCR(i) \ +{ \ + w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \ + + SHA256_F3(w[i - 15]) + w[i - 16]; \ +} + +#define SHA384_SCR(i) \ +{ \ + w[i] = SHA512_F4(w[i - 2]) + w[i - 7] \ + + SHA512_F3(w[i - 15]) + w[i - 16]; \ +} + +#define SHA512_SCR(i) \ +{ \ + w[i] = SHA512_F4(w[i - 2]) + w[i - 7] \ + + SHA512_F3(w[i - 15]) + w[i - 16]; \ +} + +static const uint32_t sha256_h0[8] = + {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + +static const uint64_t sha384_h0[8] = + {0xcbbb9d5dc1059ed8ULL, 0x629a292a367cd507ULL, + 0x9159015a3070dd17ULL, 0x152fecd8f70e5939ULL, + 0x67332667ffc00b31ULL, 0x8eb44a8768581511ULL, + 0xdb0c2e0d64f98fa7ULL, 0x47b5481dbefa4fa4ULL}; + +static const uint64_t sha512_h0[8] = + {0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL}; + +static const uint32_t sha256_k[64] = + {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + +static const uint64_t sha512_k[80] = + {0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL}; + + +/* SHA-256 functions */ + +static void ATTR_UNSIGNED_WRAPS +sha256_transf(struct sha256_ctx *ctx, const unsigned char *data, + size_t block_nb) +{ + uint32_t w[64]; + uint32_t wv[8]; + uint32_t t1, t2; + const unsigned char *sub_block; + int i,j; + + + for (i = 0; i < (int) block_nb; i++) { + sub_block = data + (i << 6); + + for (j = 0; j < 16; j++) { + PACK32(&sub_block[j << 2], &w[j]); + } + + for (j = 16; j < 64; j++) { + SHA256_SCR(j); + } + + for (j = 0; j < 8; j++) { + wv[j] = ctx->h[j]; + } + + for (j = 0; j < 64; j++) { + t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + + sha256_k[j] + w[j]; + t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + + for (j = 0; j < 8; j++) { + ctx->h[j] += wv[j]; + } + } +} + +void sha256_init(struct sha256_ctx *ctx) +{ + int i; + + for (i = 0; i < 8; i++) { + ctx->h[i] = sha256_h0[i]; + } + + ctx->len = 0; + ctx->tot_len = 0; +} + +void sha256_loop(struct sha256_ctx *ctx, const void *data, + size_t len) +{ + const unsigned char *shifted_message; + size_t block_nb; + size_t new_len, rem_len, tmp_len; + + tmp_len = SHA256_BLOCK_SIZE - ctx->len; + rem_len = len < tmp_len ? len : tmp_len; + + memcpy(&ctx->block[ctx->len], data, rem_len); + + if (ctx->len + len < SHA256_BLOCK_SIZE) { + ctx->len += len; + return; + } + + new_len = len - rem_len; + block_nb = new_len / SHA256_BLOCK_SIZE; + + shifted_message = CONST_PTR_OFFSET(data, rem_len); + + sha256_transf(ctx, ctx->block, 1); + sha256_transf(ctx, shifted_message, block_nb); + + rem_len = new_len % SHA256_BLOCK_SIZE; + memcpy(ctx->block, &shifted_message[block_nb << 6], rem_len); + + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 6; +} + +void sha256_result(struct sha256_ctx *ctx, + unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]) +{ + size_t block_nb; + size_t pm_len; + size_t len_b; + int i; + + block_nb = (1 + ((SHA256_BLOCK_SIZE - 9) + < (ctx->len % SHA256_BLOCK_SIZE))); + + len_b = (ctx->tot_len + ctx->len) << 3; + pm_len = block_nb << 6; + + memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK32(len_b, ctx->block + pm_len - 4); + + sha256_transf(ctx, ctx->block, block_nb); + + for (i = 0 ; i < 8; i++) { + UNPACK32(ctx->h[i], &digest[i << 2]); + } +} + +void sha256_get_digest(const void *data, size_t size, + unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]) +{ + struct sha256_ctx ctx; + + sha256_init(&ctx); + sha256_loop(&ctx, data, size); + sha256_result(&ctx, digest); +} + +/* SHA-384 functions */ + +static void ATTR_UNSIGNED_WRAPS +sha384_transf(struct sha384_ctx *ctx, const unsigned char *data, + size_t block_nb) +{ + uint64_t w[80]; + uint64_t wv[8]; + uint64_t t1, t2; + const unsigned char *sub_block; + int i, j; + + for (i = 0; i < (int) block_nb; i++) { + sub_block = data + (i << 7); + + for (j = 0; j < 16; j++) { + PACK64(&sub_block[j << 3], &w[j]); + } + + for (j = 16; j < 80; j++) { + SHA384_SCR(j); + } + + for (j = 0; j < 8; j++) { + wv[j] = ctx->h[j]; + } + + for (j = 0; j < 80; j++) { + /* sha384_k is same as sha512_k */ + t1 = wv[7] + SHA384_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + + sha512_k[j] + w[j]; + t2 = SHA384_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + + for (j = 0; j < 8; j++) { + ctx->h[j] += wv[j]; + } + } +} + +void sha384_init(struct sha384_ctx *ctx) +{ + int i; + + for (i = 0; i < 8; i++) { + ctx->h[i] = sha384_h0[i]; + } + + ctx->len = 0; + ctx->tot_len = 0; +} + +void sha384_loop(struct sha384_ctx *ctx, const void *data, + size_t len) +{ + const unsigned char *shifted_message; + size_t block_nb; + size_t new_len, rem_len, tmp_len; + + tmp_len = SHA384_BLOCK_SIZE - ctx->len; + rem_len = len < tmp_len ? len : tmp_len; + + memcpy(&ctx->block[ctx->len], data, rem_len); + + if (ctx->len + len < SHA384_BLOCK_SIZE) { + ctx->len += len; + return; + } + + new_len = len - rem_len; + block_nb = new_len / SHA384_BLOCK_SIZE; + + shifted_message = CONST_PTR_OFFSET(data, rem_len); + + sha384_transf(ctx, ctx->block, 1); + sha384_transf(ctx, shifted_message, block_nb); + + rem_len = new_len % SHA384_BLOCK_SIZE; + memcpy(ctx->block, &shifted_message[block_nb << 7], rem_len); + + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 7; +} + +void sha384_result(struct sha384_ctx *ctx, + unsigned char digest[STATIC_ARRAY SHA384_RESULTLEN]) +{ + unsigned int block_nb; + unsigned int pm_len; + size_t len_b; + int i; + + block_nb = 1 + ((SHA384_BLOCK_SIZE - 17) + < (ctx->len % SHA384_BLOCK_SIZE)); + + len_b = (ctx->tot_len + ctx->len) << 3; + pm_len = block_nb << 7; + + memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK32(len_b, ctx->block + pm_len - 4); + + sha384_transf(ctx, ctx->block, block_nb); + + for (i = 0 ; i < 6; i++) { + UNPACK64(ctx->h[i], &digest[i << 3]); + } +} + +void sha384_get_digest(const void *data, size_t size, + unsigned char digest[STATIC_ARRAY SHA384_RESULTLEN]) +{ + struct sha384_ctx ctx; + + sha384_init(&ctx); + sha384_loop(&ctx, data, size); + sha384_result(&ctx, digest); +} + + +/* SHA-512 functions */ + +static void ATTR_UNSIGNED_WRAPS +sha512_transf(struct sha512_ctx *ctx, const unsigned char *data, + size_t block_nb) +{ + uint64_t w[80]; + uint64_t wv[8]; + uint64_t t1, t2; + const unsigned char *sub_block; + int i, j; + + for (i = 0; i < (int) block_nb; i++) { + sub_block = data + (i << 7); + + for (j = 0; j < 16; j++) { + PACK64(&sub_block[j << 3], &w[j]); + } + + for (j = 16; j < 80; j++) { + SHA512_SCR(j); + } + + for (j = 0; j < 8; j++) { + wv[j] = ctx->h[j]; + } + + for (j = 0; j < 80; j++) { + t1 = wv[7] + SHA512_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + + sha512_k[j] + w[j]; + t2 = SHA512_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + + for (j = 0; j < 8; j++) { + ctx->h[j] += wv[j]; + } + } +} + +void sha512_init(struct sha512_ctx *ctx) +{ + int i; + + for (i = 0; i < 8; i++) { + ctx->h[i] = sha512_h0[i]; + } + + ctx->len = 0; + ctx->tot_len = 0; +} + +void sha512_loop(struct sha512_ctx *ctx, const void *data, + size_t len) +{ + const unsigned char *shifted_message; + size_t block_nb; + size_t new_len, rem_len, tmp_len; + + tmp_len = SHA512_BLOCK_SIZE - ctx->len; + rem_len = len < tmp_len ? len : tmp_len; + + memcpy(&ctx->block[ctx->len], data, rem_len); + + if (ctx->len + len < SHA512_BLOCK_SIZE) { + ctx->len += len; + return; + } + + new_len = len - rem_len; + block_nb = new_len / SHA512_BLOCK_SIZE; + + shifted_message = CONST_PTR_OFFSET(data, rem_len); + + sha512_transf(ctx, ctx->block, 1); + sha512_transf(ctx, shifted_message, block_nb); + + rem_len = new_len % SHA512_BLOCK_SIZE; + memcpy(ctx->block, &shifted_message[block_nb << 7], rem_len); + + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 7; +} + +void sha512_result(struct sha512_ctx *ctx, + unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]) +{ + unsigned int block_nb; + unsigned int pm_len; + size_t len_b; + int i; + + block_nb = 1 + ((SHA512_BLOCK_SIZE - 17) + < (ctx->len % SHA512_BLOCK_SIZE)); + + len_b = (ctx->tot_len + ctx->len) << 3; + pm_len = block_nb << 7; + + memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK32(len_b, ctx->block + pm_len - 4); + + sha512_transf(ctx, ctx->block, block_nb); + + for (i = 0 ; i < 8; i++) { + UNPACK64(ctx->h[i], &digest[i << 3]); + } +} + +void sha512_get_digest(const void *data, size_t size, + unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]) +{ + struct sha512_ctx ctx; + + sha512_init(&ctx); + sha512_loop(&ctx, data, size); + sha512_result(&ctx, digest); +} + +static void hash_method_init_sha256(void *context) +{ + sha256_init(context); +} +static void hash_method_loop_sha256(void *context, const void *data, size_t size) +{ + sha256_loop(context, data, size); +} + +static void hash_method_result_sha256(void *context, unsigned char *result_r) +{ + sha256_result(context, result_r); +} + +const struct hash_method hash_method_sha256 = { + .name = "sha256", + .block_size = SHA256_BLOCK_SIZE, + .context_size = sizeof(struct sha256_ctx), + .digest_size = SHA256_RESULTLEN, + + .init = hash_method_init_sha256, + .loop = hash_method_loop_sha256, + .result = hash_method_result_sha256, +}; + +static void hash_method_init_sha384(void *context) +{ + sha384_init(context); +} +static void hash_method_loop_sha384(void *context, const void *data, size_t size) +{ + sha384_loop(context, data, size); +} + +static void hash_method_result_sha384(void *context, unsigned char *result_r) +{ + sha384_result(context, result_r); +} + +const struct hash_method hash_method_sha384 = { + .name = "sha384", + .block_size = SHA384_BLOCK_SIZE, + .context_size = sizeof(struct sha384_ctx), + .digest_size = SHA384_RESULTLEN, + + .init = hash_method_init_sha384, + .loop = hash_method_loop_sha384, + .result = hash_method_result_sha384, +}; + +static void hash_method_init_sha512(void *context) +{ + sha512_init(context); +} +static void hash_method_loop_sha512(void *context, const void *data, size_t size) +{ + sha512_loop(context, data, size); +} + +static void hash_method_result_sha512(void *context, unsigned char *result_r) +{ + sha512_result(context, result_r); +} + +const struct hash_method hash_method_sha512 = { + .name = "sha512", + .block_size = SHA512_BLOCK_SIZE, + .context_size = sizeof(struct sha512_ctx), + .digest_size = SHA512_RESULTLEN, + + .init = hash_method_init_sha512, + .loop = hash_method_loop_sha512, + .result = hash_method_result_sha512, +}; diff --git a/src/lib/sha2.h b/src/lib/sha2.h new file mode 100644 index 0000000..8c893eb --- /dev/null +++ b/src/lib/sha2.h @@ -0,0 +1,89 @@ +/* + * FIPS 180-2 SHA-224/256/384/512 implementation + * Last update: 02/02/2007 + * Issue date: 04/30/2005 + * + * Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch> + * 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. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef SHA2_H +#define SHA2_H + +#include "hash-method.h" +#include "sha-common.h" + +struct sha256_ctx { + size_t tot_len; + size_t len; + unsigned char block[2 * SHA256_BLOCK_SIZE]; + uint32_t h[8]; +}; + +struct sha384_ctx { + size_t tot_len; + size_t len; + unsigned char block[2 * SHA384_BLOCK_SIZE]; + uint64_t h[8]; +}; + +struct sha512_ctx { + size_t tot_len; + size_t len; + unsigned char block[2 * SHA512_BLOCK_SIZE]; + uint64_t h[8]; +}; + +void sha256_init(struct sha256_ctx *ctx); +void sha256_loop(struct sha256_ctx *ctx, const void *data, size_t len); +void sha256_result(struct sha256_ctx *ctx, + unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]); + +void sha256_get_digest(const void *data, size_t size, + unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]); + +void sha384_init(struct sha384_ctx *ctx); +void sha384_loop(struct sha384_ctx *ctx, const void *data, size_t len); +void sha384_result(struct sha384_ctx *ctx, + unsigned char digest[STATIC_ARRAY SHA384_RESULTLEN]); + +void sha384_get_digest(const void *data, size_t size, + unsigned char digest[STATIC_ARRAY SHA384_RESULTLEN]); + +void sha512_init(struct sha512_ctx *ctx); +void sha512_loop(struct sha512_ctx *ctx, const void *data, size_t len); +void sha512_result(struct sha512_ctx *ctx, + unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]); + +void sha512_get_digest(const void *data, size_t size, + unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]); + +extern const struct hash_method hash_method_sha256; +extern const struct hash_method hash_method_sha384; +extern const struct hash_method hash_method_sha512; + +#endif /* !SHA2_H */ diff --git a/src/lib/sha3.c b/src/lib/sha3.c new file mode 100644 index 0000000..3b40ddd --- /dev/null +++ b/src/lib/sha3.c @@ -0,0 +1,335 @@ +/* ------------------------------------------------------------------------- + * Works when compiled for either 32-bit or 64-bit targets, optimized for + * 64 bit. + * + * Canonical implementation of Init/Update/Finalize for SHA-3 byte input. + * + * SHA3-256, SHA3-384, SHA-512 are implemented. SHA-224 can easily be added. + * + * Based on code from http://keccak.noekeon.org/ . + * + * I place the code that I wrote into public domain, free to use. + * + * I would appreciate if you give credits to this work if you used it to + * write or test * your code. + * + * Aug 2015. Andrey Jivsov. crypto@brainhub.org + * + * Modified for Dovecot oy use + * Oct 2016. Aki Tuomi <aki.tuomi@dovecot.fi> + + * ---------------------------------------------------------------------- */ +#include "lib.h" +#include "sha3.h" + +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#if defined(_MSC_VER) +#define SHA3_CONST(x) x +#else +#define SHA3_CONST(x) x##L +#endif + +/* The following state definition should normally be in a separate + * header file + */ + +#ifndef SHA3_ROTL64 +#define SHA3_ROTL64(x, y) \ + (((x) << (y)) | ((x) >> ((sizeof(uint64_t)*8) - (y)))) +#endif + +static const uint64_t keccakf_rndc[24] = { + SHA3_CONST(0x0000000000000001UL), SHA3_CONST(0x0000000000008082UL), + SHA3_CONST(0x800000000000808aUL), SHA3_CONST(0x8000000080008000UL), + SHA3_CONST(0x000000000000808bUL), SHA3_CONST(0x0000000080000001UL), + SHA3_CONST(0x8000000080008081UL), SHA3_CONST(0x8000000000008009UL), + SHA3_CONST(0x000000000000008aUL), SHA3_CONST(0x0000000000000088UL), + SHA3_CONST(0x0000000080008009UL), SHA3_CONST(0x000000008000000aUL), + SHA3_CONST(0x000000008000808bUL), SHA3_CONST(0x800000000000008bUL), + SHA3_CONST(0x8000000000008089UL), SHA3_CONST(0x8000000000008003UL), + SHA3_CONST(0x8000000000008002UL), SHA3_CONST(0x8000000000000080UL), + SHA3_CONST(0x000000000000800aUL), SHA3_CONST(0x800000008000000aUL), + SHA3_CONST(0x8000000080008081UL), SHA3_CONST(0x8000000000008080UL), + SHA3_CONST(0x0000000080000001UL), SHA3_CONST(0x8000000080008008UL) +}; + +static const unsigned keccakf_rotc[24] = { + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, + 18, 39, 61, 20, 44 +}; + +static const unsigned keccakf_piln[24] = { + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, + 14, 22, 9, 6, 1 +}; + +/* generally called after SHA3_KECCAK_SPONGE_WORDS-ctx->capacityWords words + * are XORed into the state s + */ +static void ATTR_UNSIGNED_WRAPS +keccakf(uint64_t s[25]) +{ + int i, j, round; + uint64_t t, bc[5]; +#define KECCAK_ROUNDS 24 + + for(round = 0; round < KECCAK_ROUNDS; round++) { + + /* Theta */ + for(i = 0; i < 5; i++) + bc[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20]; + + for(i = 0; i < 5; i++) { + t = bc[(i + 4) % 5] ^ SHA3_ROTL64(bc[(i + 1) % 5], 1); + for(j = 0; j < 25; j += 5) + s[j + i] ^= t; + } + + /* Rho Pi */ + t = s[1]; + for(i = 0; i < 24; i++) { + j = keccakf_piln[i]; + bc[0] = s[j]; + s[j] = SHA3_ROTL64(t, keccakf_rotc[i]); + t = bc[0]; + } + + /* Chi */ + for(j = 0; j < 25; j += 5) { + for(i = 0; i < 5; i++) + bc[i] = s[j + i]; + for(i = 0; i < 5; i++) + s[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5]; + } + + /* Iota */ + s[0] ^= keccakf_rndc[round]; + } +} + +/* *************************** Public Interface ************************ */ + +void sha3_256_init(void *context) +{ + struct sha3_ctx *ctx = context; + i_zero(ctx); + ctx->capacityWords = 2 * 256 / (8 * sizeof(uint64_t)); +} + +void sha3_512_init(void *context) +{ + struct sha3_ctx *ctx = context; + i_zero(ctx); + ctx->capacityWords = 2 * 512 / (8 * sizeof(uint64_t)); +} + +void sha3_loop(void *context, const void *data, size_t len) +{ + struct sha3_ctx *ctx = context; + /* 0...7 -- how much is needed to have a word */ + unsigned old_tail = (8 - ctx->byteIndex) & 7; + + size_t words; + unsigned tail; + size_t i; + + const uint8_t *buf = data; + + i_assert(ctx->byteIndex < 8); + i_assert(ctx->wordIndex < sizeof(ctx->s) / sizeof(ctx->s[0])); + + if(len < old_tail) { /* have no complete word or haven't started + * the word yet */ + /* endian-independent code follows: */ + while (len > 0) { + len--; + ctx->saved |= (uint64_t) (*(buf++)) << + ((ctx->byteIndex++) * 8); + } + i_assert(ctx->byteIndex < 8); + return; + } + + if(old_tail != 0) { /* will have one word to process */ + /* endian-independent code follows: */ + len -= old_tail; + while (old_tail > 0) { + old_tail--; + ctx->saved |= (uint64_t) (*(buf++)) << + ((ctx->byteIndex++) * 8); + } + + /* now ready to add saved to the sponge */ + ctx->s[ctx->wordIndex] ^= ctx->saved; + i_assert(ctx->byteIndex == 8); + ctx->byteIndex = 0; + ctx->saved = 0; + if(++ctx->wordIndex == + (SHA3_KECCAK_SPONGE_WORDS - + ctx->capacityWords)) { + keccakf(ctx->s); + ctx->wordIndex = 0; + } + } + + /* now work in full words directly from input */ + + i_assert(ctx->byteIndex == 0); + + words = len / sizeof(uint64_t); + tail = len - words * sizeof(uint64_t); + + for(i = 0; i < words; i++, buf += sizeof(uint64_t)) { + const uint64_t t = (uint64_t) (buf[0]) | + ((uint64_t) (buf[1]) << 8 * 1) | + ((uint64_t) (buf[2]) << 8 * 2) | + ((uint64_t) (buf[3]) << 8 * 3) | + ((uint64_t) (buf[4]) << 8 * 4) | + ((uint64_t) (buf[5]) << 8 * 5) | + ((uint64_t) (buf[6]) << 8 * 6) | + ((uint64_t) (buf[7]) << 8 * 7); +#if defined(__x86_64__ ) || defined(__i386__) + i_assert(memcmp(&t, buf, 8) == 0); +#endif + ctx->s[ctx->wordIndex] ^= t; + if(++ctx->wordIndex == + (SHA3_KECCAK_SPONGE_WORDS - ctx->capacityWords)) { + keccakf(ctx->s); + ctx->wordIndex = 0; + } + } + + /* finally, save the partial word */ + i_assert(ctx->byteIndex == 0 && tail < 8); + while (tail > 0) { + tail--; + ctx->saved |= (uint64_t) (*(buf++)) << ((ctx->byteIndex++) * 8); + } + i_assert(ctx->byteIndex < 8); +} + +/* This is simply the 'update' with the padding block. + * The padding block is 0x01 || 0x00* || 0x80. First 0x01 and last 0x80 + * bytes are always present, but they can be the same byte. + */ +static void +sha3_finalize(struct sha3_ctx *ctx) +{ + /* Append 2-bit suffix 01, per SHA-3 spec. Instead of 1 for padding we + * use 1<<2 below. The 0x02 below corresponds to the suffix 01. + * Overall, we feed 0, then 1, and finally 1 to start padding. Without + * M || 01, we would simply use 1 to start padding. */ + + /* SHA3 version */ + ctx->s[ctx->wordIndex] ^= + (ctx->saved ^ ((uint64_t) ((uint64_t) (0x02 | (1 << 2)) << + ((ctx->byteIndex) * 8)))); + + ctx->s[SHA3_KECCAK_SPONGE_WORDS - ctx->capacityWords - 1] ^= + SHA3_CONST(0x8000000000000000UL); + keccakf(ctx->s); + +#ifdef WORDS_BIGENDIAN + { + unsigned i; + for(i = 0; i < SHA3_KECCAK_SPONGE_WORDS; i++) { + const unsigned t1 = (uint32_t) ctx->s[i]; + const unsigned t2 = (uint32_t) ((ctx->s[i] >> 16) >> 16); + ctx->sb[i * 8 + 0] = (uint8_t) (t1); + ctx->sb[i * 8 + 1] = (uint8_t) (t1 >> 8); + ctx->sb[i * 8 + 2] = (uint8_t) (t1 >> 16); + ctx->sb[i * 8 + 3] = (uint8_t) (t1 >> 24); + ctx->sb[i * 8 + 4] = (uint8_t) (t2); + ctx->sb[i * 8 + 5] = (uint8_t) (t2 >> 8); + ctx->sb[i * 8 + 6] = (uint8_t) (t2 >> 16); + ctx->sb[i * 8 + 7] = (uint8_t) (t2 >> 24); + } + } +#endif +} + +void sha3_256_result(void *context, + unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]) +{ + struct sha3_ctx *ctx = context; + sha3_finalize(ctx); + memcpy(digest, ctx->sb, SHA256_RESULTLEN); +} + + +void sha3_512_result(void *context, + unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]) +{ + struct sha3_ctx *ctx = context; + sha3_finalize(ctx); + memcpy(digest, ctx->sb, SHA512_RESULTLEN); +} + + +void sha3_256_get_digest(const void *data, size_t size, + unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]) +{ + struct sha3_ctx ctx; + sha3_256_init(&ctx); + sha3_loop(&ctx, data, size); + sha3_256_result(&ctx, digest); +} + +void sha3_512_get_digest(const void *data, size_t size, + unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]) +{ + struct sha3_ctx ctx; + sha3_512_init(&ctx); + sha3_loop(&ctx, data, size); + sha3_512_result(&ctx, digest); +} + +static void hash_method_init_sha3_256(void *context) +{ + sha3_256_init(context); +} + +static void hash_method_loop_sha3(void *context, const void *data, size_t size) +{ + sha3_loop(context, data, size); +} + +static void hash_method_result_sha3_256(void *context, unsigned char *result_r) +{ + sha3_256_result(context, result_r); +} + +const struct hash_method hash_method_sha3_256 = { + .name = "sha3-256", + .block_size = SHA256_BLOCK_SIZE, + .context_size = sizeof(struct sha3_ctx), + .digest_size = SHA256_RESULTLEN, + + .init = hash_method_init_sha3_256, + .loop = hash_method_loop_sha3, + .result = hash_method_result_sha3_256, +}; + +static void hash_method_init_sha3_512(void *context) +{ + sha3_512_init(context); +} + +static void hash_method_result_sha3_512(void *context, unsigned char *result_r) +{ + sha3_512_result(context, result_r); +} + +const struct hash_method hash_method_sha3_512 = { + .name = "sha3-512", + .block_size = SHA512_BLOCK_SIZE, + .context_size = sizeof(struct sha3_ctx), + .digest_size = SHA512_RESULTLEN, + + .init = hash_method_init_sha3_512, + .loop = hash_method_loop_sha3, + .result = hash_method_result_sha3_512, +}; diff --git a/src/lib/sha3.h b/src/lib/sha3.h new file mode 100644 index 0000000..69babc8 --- /dev/null +++ b/src/lib/sha3.h @@ -0,0 +1,75 @@ +/* + * FIPS 180-2 SHA-224/256/384/512 implementation + * Last update: 02/02/2007 + * Issue date: 04/30/2005 + * + * Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch> + * 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. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef SHA3_H +#define SHA3_H + +#include "hash-method.h" +#include "sha-common.h" + +#define SHA3_KECCAK_SPONGE_WORDS \ + (((1600)/8/*bits to byte*/)/sizeof(uint64_t)) + +struct sha3_ctx { + uint64_t saved; /* the portion of the input message that we + * didn't consume yet */ + union { /* Keccak's state */ + uint64_t s[SHA3_KECCAK_SPONGE_WORDS]; + uint8_t sb[SHA3_KECCAK_SPONGE_WORDS * 8]; + }; + unsigned byteIndex; /* 0..7--the next byte after the set one + * (starts from 0; 0--none are buffered) */ + unsigned wordIndex; /* 0..24--the next word to integrate input + * (starts from 0) */ + unsigned capacityWords; /* the double size of the hash output in + * words (e.g. 16 for Keccak 512) */ +}; + +void sha3_256_init(void *context); +void sha3_256_result(void *context, + unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]); +void sha3_256_get_digest(const void *data, size_t size, + unsigned char digest[STATIC_ARRAY SHA256_RESULTLEN]); + +void sha3_512_init(void *context); +void sha3_512_result(void *context, + unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]); +void sha3_512_get_digest(const void *data, size_t size, + unsigned char digest[STATIC_ARRAY SHA512_RESULTLEN]); + +void sha3_loop(void *context, const void *data, size_t len); + +extern const struct hash_method hash_method_sha3_256; +extern const struct hash_method hash_method_sha3_512; + +#endif diff --git a/src/lib/sleep.c b/src/lib/sleep.c new file mode 100644 index 0000000..7b3b6fa --- /dev/null +++ b/src/lib/sleep.c @@ -0,0 +1,74 @@ +/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "sleep.h" + +#include <time.h> + +static bool ATTR_NOWARN_UNUSED_RESULT +sleep_timespec(const struct timespec *ts_sleep, bool interruptible) +{ + struct timespec ts_remain = *ts_sleep; + + while (nanosleep(&ts_remain, &ts_remain) < 0) { + if (errno != EINTR) + i_fatal("nanosleep(): %m"); + if (interruptible) + return FALSE; + } + return TRUE; +} + +void i_sleep_usecs(unsigned long long usecs) +{ + struct timespec ts_sleep; + + ts_sleep.tv_sec = (time_t)(usecs / 1000000); + ts_sleep.tv_nsec = (long)(usecs % 1000000) * 1000; + sleep_timespec(&ts_sleep, FALSE); +} + +void i_sleep_msecs(unsigned int msecs) +{ + struct timespec ts_sleep; + + ts_sleep.tv_sec = (time_t)(msecs / 1000); + ts_sleep.tv_nsec = (long)(msecs % 1000) * 1000000; + sleep_timespec(&ts_sleep, FALSE); +} + +void i_sleep_secs(time_t secs) +{ + struct timespec ts_sleep; + + ts_sleep.tv_sec = secs; + ts_sleep.tv_nsec = 0; + sleep_timespec(&ts_sleep, FALSE); +} + +bool i_sleep_intr_usecs(unsigned long long usecs) +{ + struct timespec ts_sleep; + + ts_sleep.tv_sec = (time_t)(usecs / 1000000); + ts_sleep.tv_nsec = (long)(usecs % 1000000) * 1000; + return sleep_timespec(&ts_sleep, TRUE); +} + +bool i_sleep_intr_msecs(unsigned int msecs) +{ + struct timespec ts_sleep; + + ts_sleep.tv_sec = (time_t)(msecs / 1000); + ts_sleep.tv_nsec = (long)(msecs % 1000) * 1000000; + return sleep_timespec(&ts_sleep, TRUE); +} + +bool i_sleep_intr_secs(time_t secs) +{ + struct timespec ts_sleep; + + ts_sleep.tv_sec = secs; + ts_sleep.tv_nsec = 0; + return sleep_timespec(&ts_sleep, TRUE); +} diff --git a/src/lib/sleep.h b/src/lib/sleep.h new file mode 100644 index 0000000..0f8d7c3 --- /dev/null +++ b/src/lib/sleep.h @@ -0,0 +1,30 @@ +#ifndef SLEEP_H +#define SLEEP_H + +/* Sleep for the indicated number of microseconds. Signal interruptions are + handled and ignored internally. */ +void i_sleep_usecs(unsigned long long usecs); +/* Sleep for the indicated number of milliseconds. Signal interruptions are + handled and ignored internally. */ +void i_sleep_msecs(unsigned int msecs); +/* Sleep for the indicated number of seconds. Signal interruptions are + handled and ignored internally. */ +void i_sleep_secs(time_t secs); + +/* Sleep for the indicated number of microseconds while allowing signal + interruptions. This function returns FALSE when it is interrupted by a + signal. Otherwise, this function always returns TRUE. */ +bool ATTR_NOWARN_UNUSED_RESULT +i_sleep_intr_usecs(unsigned long long usecs); +/* Sleep for the indicated number of milliseconds while allowing signal + interruptions. This function returns FALSE when it is interrupted by a + signal. Otherwise, this function always returns TRUE. */ +bool ATTR_NOWARN_UNUSED_RESULT +i_sleep_intr_msecs(unsigned int msecs); +/* Sleep for the indicated number of seconds while allowing signal + interruptions. This function returns FALSE when it is interrupted by a + signal. Otherwise, this function always returns TRUE. */ +bool ATTR_NOWARN_UNUSED_RESULT +i_sleep_intr_secs(time_t secs); + +#endif diff --git a/src/lib/sort.c b/src/lib/sort.c new file mode 100644 index 0000000..897bffb --- /dev/null +++ b/src/lib/sort.c @@ -0,0 +1,17 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "sort.h" + +#include <string.h> +#include <strings.h> + +int bsearch_strcmp(const char *key, const char *const *member) +{ + return strcmp(key, *member); +} + +int bsearch_strcasecmp(const char *key, const char *const *member) +{ + return strcasecmp(key, *member); +} diff --git a/src/lib/sort.h b/src/lib/sort.h new file mode 100644 index 0000000..94cab81 --- /dev/null +++ b/src/lib/sort.h @@ -0,0 +1,33 @@ +#ifndef SORT_H +#define SORT_H + +#define INTEGER_CMP(name, type) \ + static inline int name(const type *i1, const type *i2) \ + { \ + if (*i1 < *i2) \ + return -1; \ + else if (*i1 > *i2) \ + return 1; \ + else \ + return 0; \ + } + +INTEGER_CMP(uint64_cmp, uint64_t) +INTEGER_CMP(uint32_cmp, uint32_t) + +#define i_qsort(base, nmemb, size, cmp) \ + qsort(base, nmemb, size - \ + CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*base) *), \ + typeof(const typeof(*base) *))), \ + (int (*)(const void *, const void *))cmp) + +#define i_bsearch(key, base, nmemb, size, cmp) \ + bsearch(key, base, nmemb, size - \ + CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*key) *), \ + typeof(const typeof(*base) *))), \ + (int (*)(const void *, const void *))cmp) + +int bsearch_strcmp(const char *key, const char *const *member) ATTR_PURE; +int bsearch_strcasecmp(const char *key, const char *const *member) ATTR_PURE; + +#endif diff --git a/src/lib/stats-dist.c b/src/lib/stats-dist.c new file mode 100644 index 0000000..882bde1 --- /dev/null +++ b/src/lib/stats-dist.c @@ -0,0 +1,183 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "stats-dist.h" +#include "sort.h" + +/* In order to have a vaguely accurate 95th percentile, you need way + more than 20 in your subsample. */ +#define TIMING_DEFAULT_SUBSAMPLING_BUFFER (20*24) /* 20*24 fits in a page */ + +struct stats_dist { + unsigned int sample_count; + unsigned int count; + bool sorted; + uint64_t min; + uint64_t max; + uint64_t sum; + uint64_t samples[]; +}; + +struct stats_dist *stats_dist_init(void) +{ + return stats_dist_init_with_size(TIMING_DEFAULT_SUBSAMPLING_BUFFER); +} + +struct stats_dist *stats_dist_init_with_size(unsigned int sample_count) +{ + i_assert(sample_count > 0); + + struct stats_dist *stats = + i_malloc(sizeof(struct stats_dist) + + sizeof(uint64_t) * sample_count); + stats->sample_count = sample_count; + return stats; +} + +void stats_dist_deinit(struct stats_dist **_stats) +{ + i_free_and_null(*_stats); +} + +void stats_dist_reset(struct stats_dist *stats) +{ + unsigned int sample_count = stats->sample_count; + i_zero(stats); + stats->sample_count = sample_count; +} + +void stats_dist_add(struct stats_dist *stats, uint64_t value) +{ + if (stats->count < stats->sample_count) { + stats->samples[stats->count] = value; + if (stats->count == 0) + stats->min = stats->max = value; + } else { + unsigned int idx = i_rand_limit(stats->count); + if (idx < stats->sample_count) + stats->samples[idx] = value; + } + + stats->count++; + stats->sum += value; + if (stats->max < value) + stats->max = value; + if (stats->min > value) + stats->min = value; + stats->sorted = FALSE; +} + +unsigned int stats_dist_get_count(const struct stats_dist *stats) +{ + return stats->count; +} + +uint64_t stats_dist_get_sum(const struct stats_dist *stats) +{ + return stats->sum; +} + +uint64_t stats_dist_get_min(const struct stats_dist *stats) +{ + return stats->min; +} + +uint64_t stats_dist_get_max(const struct stats_dist *stats) +{ + return stats->max; +} + +double stats_dist_get_avg(const struct stats_dist *stats) +{ + if (stats->count == 0) + return 0; + + return (double)stats->sum / stats->count; +} + +static void stats_dist_ensure_sorted(struct stats_dist *stats) +{ + if (stats->sorted) + return; + + unsigned int count = (stats->count < stats->sample_count) + ? stats->count + : stats->sample_count; + i_qsort(stats->samples, count, sizeof(*stats->samples), + uint64_cmp); + stats->sorted = TRUE; +} + +uint64_t stats_dist_get_median(struct stats_dist *stats) +{ + if (stats->count == 0) + return 0; + /* cast-away const - reading requires sorting */ + stats_dist_ensure_sorted(stats); + unsigned int count = (stats->count < stats->sample_count) + ? stats->count + : stats->sample_count; + unsigned int idx1 = (count-1)/2, idx2 = count/2; + return (stats->samples[idx1] + stats->samples[idx2]) / 2; +} + +double stats_dist_get_variance(const struct stats_dist *stats) +{ + double sum = 0; + if (stats->count == 0) + return 0; + + double avg = stats_dist_get_avg(stats); + double count = (stats->count < stats->sample_count) + ? stats->count + : stats->sample_count; + + for(unsigned int i = 0; i < count; i++) { + sum += (stats->samples[i] - avg)*(stats->samples[i] - avg); + } + + return sum / count; +} + +/* This is independent of the stats framework, useful for any selection task */ +static unsigned int stats_dist_get_index(unsigned int range, double fraction) +{ + /* With out of range fractions, we can give the caller what + they probably want rather than just crashing. */ + if (fraction >= 1.) + return range - 1; + if (fraction <= 0.) + return 0; + + double idx_float = range * fraction; + unsigned int idx = idx_float; /* C defaults to rounding down */ + idx_float -= idx; + /* Exact boundaries belong to the open range below them. + As FP isn't exact, and ratios may be specified inexactly, + include a small amount of fuzz around the exact boundary. */ + if (idx_float < 1e-8*range) + idx--; + + return idx; +} + +uint64_t stats_dist_get_percentile(struct stats_dist *stats, double fraction) +{ + if (stats->count == 0) + return 0; + stats_dist_ensure_sorted(stats); + unsigned int count = (stats->count < stats->sample_count) + ? stats->count + : stats->sample_count; + unsigned int idx = stats_dist_get_index(count, fraction); + return stats->samples[idx]; +} + +const uint64_t *stats_dist_get_samples(const struct stats_dist *stats, + unsigned int *count_r) +{ + *count_r = (stats->count < stats->sample_count) + ? stats->count + : stats->sample_count; + return stats->samples; +} diff --git a/src/lib/stats-dist.h b/src/lib/stats-dist.h new file mode 100644 index 0000000..2d49f72 --- /dev/null +++ b/src/lib/stats-dist.h @@ -0,0 +1,40 @@ +#ifndef STATS_DIST_H +#define STATS_DIST_H + +struct stats_dist *stats_dist_init(void); +struct stats_dist *stats_dist_init_with_size(unsigned int sample_count); +void stats_dist_deinit(struct stats_dist **stats); + +/* Reset all events. */ +void stats_dist_reset(struct stats_dist *stats); + +/* Add a new event. */ +void stats_dist_add(struct stats_dist *stats, uint64_t value); + +/* Returns number of events added. */ +unsigned int stats_dist_get_count(const struct stats_dist *stats); +/* Returns the sum of all events. */ +uint64_t stats_dist_get_sum(const struct stats_dist *stats); + +/* Returns events' minimum. */ +uint64_t stats_dist_get_min(const struct stats_dist *stats); +/* Returns events' maximum. */ +uint64_t stats_dist_get_max(const struct stats_dist *stats); +/* Returns events' average. */ +double stats_dist_get_avg(const struct stats_dist *stats); +/* Returns events' approximate (through random subsampling) median. */ +uint64_t stats_dist_get_median(struct stats_dist *stats); +/* Returns events' variance */ +double stats_dist_get_variance(const struct stats_dist *stats); +/* Returns events' approximate (through random subsampling) percentile. + fraction parameter is in the range (0., 1.], so 95th %-ile is 0.95. */ +uint64_t stats_dist_get_percentile(struct stats_dist *stats, double fraction); +/* Returns events' approximate (through random subsampling) 95th percentile. */ +static inline uint64_t stats_dist_get_95th(struct stats_dist *stats) +{ + return stats_dist_get_percentile(stats, 0.95); +} +/* Returns the sample array */ +const uint64_t *stats_dist_get_samples(const struct stats_dist *stats, + unsigned int *count_r); +#endif diff --git a/src/lib/str-find.c b/src/lib/str-find.c new file mode 100644 index 0000000..56d8069 --- /dev/null +++ b/src/lib/str-find.c @@ -0,0 +1,183 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +/* @UNSAFE: whole file */ + +#include "lib.h" +#include "str-find.h" + +struct str_find_context { + pool_t pool; + unsigned char *key; + unsigned int key_len; + + unsigned int *matches; + unsigned int match_count; + + size_t match_end_pos; + + int badtab[UCHAR_MAX+1]; + int goodtab[FLEXIBLE_ARRAY_MEMBER]; +}; + +static void init_badtab(struct str_find_context *ctx) +{ + unsigned int i, len_1 = ctx->key_len - 1; + + for (i = 0; i <= UCHAR_MAX; i++) + ctx->badtab[i] = ctx->key_len; + + for (i = 0; i < len_1; i++) + ctx->badtab[ctx->key[i]] = len_1 - i; +} + +static void init_suffixes(struct str_find_context *ctx, unsigned int *suffixes) +{ + unsigned int len_1 = ctx->key_len - 1; + int f = 0, g, i; + + suffixes[len_1] = ctx->key_len; + g = len_1; + for (i = (int)ctx->key_len - 2; i >= 0; i--) { + if (i > g && (int)suffixes[i + len_1 - f] < i - g) + suffixes[i] = suffixes[i + len_1 - f]; + else { + if (i < g) + g = i; + f = i; + while (g >= 0 && ctx->key[g] == ctx->key[g + len_1 - f]) + g--; + suffixes[i] = f - g; + } + } +} + +static void init_goodtab(struct str_find_context *ctx) +{ + unsigned int *suffixes; + int j, i, len_1 = ctx->key_len - 1; + + suffixes = t_buffer_get(sizeof(*suffixes) * ctx->key_len); + init_suffixes(ctx, suffixes); + + for (i = 0; i < (int)ctx->key_len; i++) + ctx->goodtab[i] = ctx->key_len; + + j = 0; + for (i = len_1; i >= -1; i--) { + if (i < 0 || suffixes[i] == (unsigned int)i + 1) { + for (; j < len_1 - i; j++) { + if (ctx->goodtab[j] == (int)ctx->key_len) + ctx->goodtab[j] = len_1 - i; + } + } + } + for (i = 0; i <= (int)ctx->key_len - 2; i++) + ctx->goodtab[len_1 - suffixes[i]] = len_1 - i; +} + +struct str_find_context *str_find_init(pool_t pool, const char *key) +{ + struct str_find_context *ctx; + size_t key_len = strlen(key); + + i_assert(key_len > 0); + i_assert(key_len < INT_MAX); + + ctx = p_malloc(pool, MALLOC_ADD(sizeof(struct str_find_context), + MALLOC_MULTIPLY(sizeof(ctx->goodtab[0]), key_len))); + ctx->pool = pool; + ctx->matches = p_new(pool, unsigned int, key_len); + ctx->key_len = key_len; + ctx->key = p_malloc(pool, key_len); + memcpy(ctx->key, key, key_len); + + init_goodtab(ctx); + init_badtab(ctx); + return ctx; +} + +void str_find_deinit(struct str_find_context **_ctx) +{ + struct str_find_context *ctx = *_ctx; + + *_ctx = NULL; + p_free(ctx->pool, ctx->matches); + p_free(ctx->pool, ctx->key); + p_free(ctx->pool, ctx); +} + +bool str_find_more(struct str_find_context *ctx, + const unsigned char *data, size_t size) +{ + unsigned int key_len = ctx->key_len; + unsigned int i, j, a, b; + int bad_value; + + for (i = j = 0; i < ctx->match_count; i++) { + a = ctx->matches[i]; + if (ctx->matches[i] + size >= key_len) { + /* we can fully determine this match now */ + for (; a < key_len; a++) { + if (ctx->key[a] != data[a - ctx->matches[i]]) + break; + } + + if (a == key_len) { + ctx->match_end_pos = key_len - ctx->matches[i]; + return TRUE; + } + } else { + for (b = 0; b < size; b++) { + if (ctx->key[a+b] != data[b]) + break; + } + + if (b == size) + ctx->matches[j++] = a + size; + } + } + if (j > 0) { + i_assert(j + size < key_len); + ctx->match_count = j; + j = 0; + } else { + /* Boyer-Moore searching */ + j = 0; + while (j + key_len <= size) { + i = key_len - 1; + while (ctx->key[i] == data[i + j]) { + if (i == 0) { + ctx->match_end_pos = j + key_len; + return TRUE; + } + i--; + } + + bad_value = (int)(ctx->badtab[data[i + j]] + i + 1) - + (int)key_len; + j += I_MAX(ctx->goodtab[i], bad_value); + } + i_assert(j <= size); + ctx->match_count = 0; + } + + for (; j < size; j++) { + for (i = j; i < size; i++) { + if (ctx->key[i-j] != data[i]) + break; + } + if (i == size) + ctx->matches[ctx->match_count++] = size - j; + } + return FALSE; +} + +size_t str_find_get_match_end_pos(struct str_find_context *ctx) +{ + return ctx->match_end_pos; +} + +void str_find_reset(struct str_find_context *ctx) +{ + ctx->match_count = 0; +} diff --git a/src/lib/str-find.h b/src/lib/str-find.h new file mode 100644 index 0000000..9ab8f37 --- /dev/null +++ b/src/lib/str-find.h @@ -0,0 +1,20 @@ +#ifndef STR_FIND_H +#define STR_FIND_H + +struct str_find_context; + +struct str_find_context *str_find_init(pool_t pool, const char *key); +void str_find_deinit(struct str_find_context **ctx); + +/* Returns TRUE if key is found. It's possible to send the data in arbitrary + blocks and have the key still match. */ +bool str_find_more(struct str_find_context *ctx, + const unsigned char *data, size_t size); +/* After str_find_more() has returned TRUE, this function returns the end + position in the previous data block where the key had matched. */ +size_t str_find_get_match_end_pos(struct str_find_context *ctx); +/* Reset input data. The next str_find_more() call won't try to match the key + to earlier data. */ +void str_find_reset(struct str_find_context *ctx); + +#endif diff --git a/src/lib/str-sanitize.c b/src/lib/str-sanitize.c new file mode 100644 index 0000000..859640a --- /dev/null +++ b/src/lib/str-sanitize.c @@ -0,0 +1,165 @@ +/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "unichar.h" +#include "str.h" +#include "str-sanitize.h" + +static size_t str_sanitize_skip_start(const char *src, size_t max_bytes) +{ + unichar_t chr; + size_t i; + + for (i = 0; i < max_bytes && src[i] != '\0'; ) { + int len = uni_utf8_get_char_n(src+i, max_bytes-i, &chr); + if (len <= 0) + break; + if ((unsigned char)src[i] < 32) + break; + i += len; + } + i_assert(i <= max_bytes); + return i; +} + + +static size_t +str_sanitize_skip_start_utf8(const char *src, uintmax_t max_chars) +{ + unichar_t chr; + uintmax_t c; + size_t i; + + for (i = 0, c = 0; c < max_chars && src[i] != '\0'; ) { + int len = uni_utf8_get_char(src+i, &chr); + if (len <= 0) + break; + if ((unsigned char)src[i] < 32) + break; + c++; + i += len; + } + i_assert(c <= max_chars); + return i; +} + +static void str_sanitize_truncate_char(string_t *dest, unsigned int initial_pos) +{ + const unsigned char *data = str_data(dest); + size_t len = str_len(dest); + + i_assert(len >= initial_pos); + if (len == initial_pos) + return; + + data += initial_pos; + len -= initial_pos; + str_truncate(dest, initial_pos + + uni_utf8_data_truncate(data, len, len-1)); +} + +void str_sanitize_append(string_t *dest, const char *src, size_t max_bytes) +{ + size_t initial_pos = str_len(dest); + unichar_t chr; + size_t i; + + for (i = 0; i < max_bytes && src[i] != '\0'; ) { + int len = uni_utf8_get_char_n(src+i, max_bytes-i, &chr); + if (len == 0) + break; /* input ended too early */ + + if (len < 0) { + /* invalid UTF-8 */ + str_append_c(dest, '?'); + i++; + continue; + } + if ((unsigned char)src[i] < 32) + str_append_c(dest, '?'); + else + str_append_data(dest, src+i, len); + i += len; + } + + if (src[i] != '\0') { + if (max_bytes < 3) + str_truncate(dest, initial_pos); + else { + while (str_len(dest) - initial_pos > max_bytes-3) + str_sanitize_truncate_char(dest, initial_pos); + } + str_append(dest, "..."); + } +} + +void str_sanitize_append_utf8(string_t *dest, const char *src, + uintmax_t max_cps) +{ + size_t last_pos = 0; + unichar_t chr; + uintmax_t c; + size_t i; + + i_assert(max_cps > 0); + + for (i = 0, c = 0; c < max_cps && src[i] != '\0'; ) { + int len = uni_utf8_get_char(src+i, &chr); + if (len == 0) + break; /* input ended too early */ + + last_pos = str_len(dest); + if (len < 0) { + /* invalid UTF-8 */ + str_append(dest, UNICODE_REPLACEMENT_CHAR_UTF8); + i++; + continue; + } + if ((unsigned char)src[i] < 32) + str_append(dest, UNICODE_REPLACEMENT_CHAR_UTF8); + else + str_append_data(dest, src+i, len); + i += len; + c++; + } + + if (src[i] != '\0') { + str_truncate(dest, last_pos); + str_append(dest, UNICODE_HORIZONTAL_ELLIPSIS_CHAR_UTF8); + } +} + +const char *str_sanitize(const char *src, size_t max_bytes) +{ + string_t *str; + size_t i; + + if (src == NULL) + return NULL; + + i = str_sanitize_skip_start(src, max_bytes); + if (src[i] == '\0') + return src; + + str = t_str_new(I_MIN(max_bytes, 256)); + str_sanitize_append(str, src, max_bytes); + return str_c(str); +} + +const char *str_sanitize_utf8(const char *src, uintmax_t max_cps) +{ + string_t *str; + size_t i; + + if (src == NULL) + return NULL; + + i = str_sanitize_skip_start_utf8(src, max_cps); + if (src[i] == '\0') + return src; + + str = t_str_new(I_MIN(max_cps, 256)); + str_sanitize_append_utf8(str, src, max_cps); + return str_c(str); +} + diff --git a/src/lib/str-sanitize.h b/src/lib/str-sanitize.h new file mode 100644 index 0000000..c1b2fce --- /dev/null +++ b/src/lib/str-sanitize.h @@ -0,0 +1,22 @@ +#ifndef STR_SANITIZE_H +#define STR_SANITIZE_H + +/* All control characters in src will be appended as '?'. If src is longer + than max_bytes, it's truncated with "..." appended to the end. Note that + src is treated as UTF-8 input, but max_bytes is in bytes instead of + UTF-8 characters. */ +void str_sanitize_append(string_t *dest, const char *src, size_t max_bytes); +/* All control characters in src will be appended as the unicode replacement + character (U+FFFD). If src has more than max_cps unicode code points, it's + truncated with a horizontal ellipsis character (U+2026) appended to the end. + */ +void str_sanitize_append_utf8(string_t *dest, const char *src, + uintmax_t max_cps); +/* Return src sanitized. If there are no changes, src pointer is returned. + If src is NULL, returns NULL. */ +const char *str_sanitize(const char *src, size_t max_bytes); +/* The unicode version of str_sanitize() using str_sanitize_append_utf8() + internally. */ +const char *str_sanitize_utf8(const char *src, uintmax_t max_cps); + +#endif diff --git a/src/lib/str-table.c b/src/lib/str-table.c new file mode 100644 index 0000000..10eddd6 --- /dev/null +++ b/src/lib/str-table.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "hash.h" +#include "str-table.h" + +struct str_table { + HASH_TABLE(char *, void *) hash; +}; + +struct str_table *str_table_init(void) +{ + struct str_table *table; + + table = i_new(struct str_table, 1); + hash_table_create(&table->hash, default_pool, 0, str_hash, strcmp); + return table; +} + +void str_table_deinit(struct str_table **_table) +{ + struct str_table *table = *_table; + struct hash_iterate_context *iter; + char *key; + void *value; + + *_table = NULL; + + iter = hash_table_iterate_init(table->hash); + while (hash_table_iterate(iter, table->hash, &key, &value)) + i_free(key); + hash_table_iterate_deinit(&iter); + hash_table_destroy(&table->hash); + i_free(table); +} + +bool str_table_is_empty(struct str_table *table) +{ + return hash_table_count(table->hash) == 0; +} + +const char *str_table_ref(struct str_table *table, const char *str) +{ + char *key; + void *value; + unsigned int ref; + + if (!hash_table_lookup_full(table->hash, str, &key, &value)) { + key = i_strdup(str); + ref = 1; + } else { + ref = POINTER_CAST_TO(value, unsigned int); + i_assert(ref > 0); + ref++; + } + hash_table_update(table->hash, key, POINTER_CAST(ref)); + return key; +} + +void str_table_unref(struct str_table *table, const char **str) +{ + char *key; + void *value; + unsigned int ref; + + if (!hash_table_lookup_full(table->hash, *str, &key, &value)) + i_unreached(); + + ref = POINTER_CAST_TO(value, unsigned int); + i_assert(ref > 0); + if (--ref > 0) + hash_table_update(table->hash, key, POINTER_CAST(ref)); + else { + hash_table_remove(table->hash, key); + i_free(key); + } + *str = NULL; +} diff --git a/src/lib/str-table.h b/src/lib/str-table.h new file mode 100644 index 0000000..55111a6 --- /dev/null +++ b/src/lib/str-table.h @@ -0,0 +1,19 @@ +#ifndef STR_TABLE_H +#define STR_TABLE_H + +/* Hash table containing string -> refcount. */ + +struct str_table *str_table_init(void); +void str_table_deinit(struct str_table **table); + +/* Returns TRUE if there are no referenced strings in the table. */ +bool str_table_is_empty(struct str_table *table); + +/* Return string allocated from the strtable and increase its reference + count. */ +const char *str_table_ref(struct str_table *table, const char *str); +/* Decrease string's reference count, freeing it if it reaches zero. + The str pointer must have been returned by the str_table_ref(). */ +void str_table_unref(struct str_table *table, const char **str); + +#endif diff --git a/src/lib/str.c b/src/lib/str.c new file mode 100644 index 0000000..2ec5970 --- /dev/null +++ b/src/lib/str.c @@ -0,0 +1,158 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "printf-format-fix.h" +#include "unichar.h" +#include "str.h" + +#include <stdio.h> + +string_t *str_new(pool_t pool, size_t initial_size) +{ + /* never allocate a 0 byte size buffer. this is especially important + when str_c() is called on an empty string from a different stack + frame (see the comment in buffer.c about this). */ + return buffer_create_dynamic(pool, I_MAX(initial_size, 1)); +} + +string_t *str_new_const(pool_t pool, const char *str, size_t len) +{ + string_t *ret; + + i_assert(str[len] == '\0'); + + ret = p_new(pool, buffer_t, 1); + buffer_create_from_const_data(ret, str, len + 1); + str_truncate(ret, len); + return ret; +} + +string_t *t_str_new(size_t initial_size) +{ + return str_new(pool_datastack_create(), initial_size); +} + +string_t *t_str_new_const(const char *str, size_t len) +{ + return str_new_const(pool_datastack_create(), str, len); +} + +void str_free(string_t **str) +{ + if (str == NULL || *str == NULL) + return; + + buffer_free(str); +} + +static void str_add_nul(string_t *str) +{ + const unsigned char *data = str_data(str); + size_t len = str_len(str); + size_t alloc = buffer_get_size(str); + + if (len == alloc || data[len] != '\0') { + buffer_write(str, len, "", 1); + /* remove the \0 - we don't want to keep it */ + buffer_set_used_size(str, len); + } +} + +char *str_free_without_data(string_t **str) +{ + str_add_nul(*str); + return buffer_free_without_data(str); +} + +const char *str_c(string_t *str) +{ + str_add_nul(str); + return str->data; +} + +char *str_c_modifiable(string_t *str) +{ + str_add_nul(str); + return buffer_get_modifiable_data(str, NULL); +} + +bool str_equals(const string_t *str1, const string_t *str2) +{ + if (str1->used != str2->used) + return FALSE; + + return memcmp(str1->data, str2->data, str1->used) == 0; +} + +void str_append_max(string_t *str, const char *cstr, size_t max_len) +{ + const char *p; + size_t len; + + p = memchr(cstr, '\0', max_len); + if (p == NULL) + len = max_len; + else + len = p - (const char *)cstr; + buffer_append(str, cstr, len); +} + +void str_printfa(string_t *str, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + str_vprintfa(str, fmt, args); + va_end(args); +} + +void str_vprintfa(string_t *str, const char *fmt, va_list args) +{ +#define SNPRINTF_INITIAL_EXTRA_SIZE 128 + va_list args2; + char *tmp; + size_t init_size; + size_t pos = str->used; + int ret, ret2; + + VA_COPY(args2, args); + + /* the format string is modified only if %m exists in it. it happens + only in error conditions, so don't try to t_push() here since it'll + just slow down the normal code path. */ + fmt = printf_format_fix_get_len(fmt, &init_size); + init_size += SNPRINTF_INITIAL_EXTRA_SIZE; + + /* @UNSAFE */ + if (pos+init_size > buffer_get_writable_size(str) && + pos < buffer_get_writable_size(str)) { + /* avoid growing buffer larger if possible. this is also + required if buffer isn't dynamically growing. */ + init_size = buffer_get_writable_size(str)-pos; + } + tmp = buffer_get_space_unsafe(str, pos, init_size); + ret = vsnprintf(tmp, init_size, fmt, args); + i_assert(ret >= 0); + + if ((unsigned int)ret >= init_size) { + /* didn't fit with the first guess. now we know the size, + so try again. */ + tmp = buffer_get_space_unsafe(str, pos, ret + 1); + ret2 = vsnprintf(tmp, ret + 1, fmt, args2); + i_assert(ret2 == ret); + } + va_end(args2); + + /* drop the unused data, including terminating NUL */ + buffer_set_used_size(str, pos + ret); +} + +void str_truncate_utf8(string_t *str, size_t len) +{ + size_t size = str_len(str); + + if (size <= len) + return; + str_truncate(str, uni_utf8_data_truncate(str_data(str), size, len)); +} diff --git a/src/lib/str.h b/src/lib/str.h new file mode 100644 index 0000000..f0ec8f1 --- /dev/null +++ b/src/lib/str.h @@ -0,0 +1,100 @@ +#ifndef STR_H +#define STR_H + +#include "buffer.h" + +string_t *str_new(pool_t pool, size_t initial_size); +string_t *t_str_new(size_t initial_size); +/* Allocate a constant string using the given str as the input data. + str pointer is saved directly, so it must not be freed until the returned + string is no longer used. len must contain strlen(str). */ +string_t *str_new_const(pool_t pool, const char *str, size_t len); +string_t *t_str_new_const(const char *str, size_t len); +void str_free(string_t **str); +char *str_free_without_data(string_t **str); + +const char *str_c(string_t *str); +char *str_c_modifiable(string_t *str); +bool str_equals(const string_t *str1, const string_t *str2) ATTR_PURE; + +static inline const unsigned char *str_data(const string_t *str) +{ + return (const unsigned char*)str->data; +} +static inline size_t str_len(const string_t *str) +{ + return str->used; +} + +/* Append NUL-terminated string. If the trailing NUL isn't found earlier, + append a maximum of max_len characters. */ +void str_append_max(string_t *str, const char *cstr, size_t max_len); + +static inline void str_append(string_t *str, const char *cstr) +{ + buffer_append(str, cstr, strlen(cstr)); +} +static inline void str_append_data(string_t *str, const void *data, size_t len) +{ + buffer_append(str, data, len); +} + +static inline void str_append_c(string_t *str, unsigned char chr) +{ + buffer_append_c(str, chr); +} +/* This macro ensures we add unsigned char to str to avoid + implicit casts which cause errors with clang's implicit integer truncation + sanitizier. Issues caught by these sanitizers are not undefined behavior, + but are often unintentional. + We also need to check that the type we are adding is compatible with char, + so that we don't end up doing a narrowing cast. */ +#ifdef HAVE_TYPE_CHECKS +# define str_append_c(str, chr) \ + str_append_c((str), __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof((chr)), char), \ + (unsigned char)(chr), (chr))) +#endif + +static inline void str_append_str(string_t *dest, const string_t *src) +{ + buffer_append(dest, src->data, src->used); +} + +/* Append printf()-like data */ +void str_printfa(string_t *str, const char *fmt, ...) + ATTR_FORMAT(2, 3); +void str_vprintfa(string_t *str, const char *fmt, va_list args) + ATTR_FORMAT(2, 0); + +static inline void str_insert(string_t *str, size_t pos, const char *cstr) +{ + buffer_insert(str, pos, cstr, strlen(cstr)); +} + +static inline void str_delete(string_t *str, size_t pos, size_t len) +{ + buffer_delete(str, pos, len); +} + +static inline void str_replace(string_t *str, size_t pos, size_t len, + const char *cstr) +{ + buffer_replace(str, pos, len, cstr, strlen(cstr)); +} + +/* Truncate the string to specified length. If it's already smaller, + do nothing. */ +static inline void str_truncate(string_t *str, size_t len) +{ + if (str_len(str) > len) + buffer_set_used_size(str, len); +} + +/* Truncate the string to specified length, but also make sure the truncation + doesn't happen in the middle of an UTF-8 character sequence. In that case, + the string will end up being up to a few bytes smaller than len. If it's + already smaller to begin with, do nothing. */ +void str_truncate_utf8(string_t *str, size_t len); + +#endif diff --git a/src/lib/strescape.c b/src/lib/strescape.c new file mode 100644 index 0000000..bfa4473 --- /dev/null +++ b/src/lib/strescape.c @@ -0,0 +1,358 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "strescape.h" + +const char *str_nescape(const void *str, size_t len) +{ + string_t *dest = t_str_new(len*2); + str_append_escaped(dest, str, len); + return str_c(dest); +} + +void str_append_escaped(string_t *dest, const void *src, size_t src_size) +{ + const unsigned char *pstart = src, *p = src, *pend = pstart + src_size; + /* see if we need to quote it */ + for (; p < pend; p++) { + if (IS_ESCAPED_CHAR(*p)) + break; + } + + /* quote */ + str_append_data(dest, pstart, (size_t)(p - pstart)); + + for (; p < pend; p++) { + if (IS_ESCAPED_CHAR(*p)) + str_append_c(dest, '\\'); + str_append_data(dest, p, 1); + } +} + +void str_append_unescaped(string_t *dest, const void *src, size_t src_size) +{ + const unsigned char *src_c = src; + size_t start = 0, i = 0; + + while (i < src_size) { + for (; i < src_size; i++) { + if (src_c[i] == '\\') + break; + } + + str_append_data(dest, src_c + start, i-start); + + if (i < src_size) { + if (++i == src_size) + break; + str_append_c(dest, src_c[i++]); + } + start = i; + } +} + +char *str_unescape(char *str) +{ + /* @UNSAFE */ + char *dest, *start = str; + + while (*str != '\\') { + if (*str == '\0') + return start; + str++; + } + + for (dest = str; *str != '\0'; str++) { + if (*str == '\\') { + str++; + if (*str == '\0') + break; + } + + *dest++ = *str; + } + + *dest = '\0'; + return start; +} + +int str_unescape_next(const char **str, const char **unescaped_r) +{ + const char *p; + char *escaped; + bool esc_found = FALSE; + + for (p = *str; *p != '\0'; p++) { + if (*p == '"') + break; + else if (*p == '\\') { + if (p[1] == '\0') + return -1; + esc_found = TRUE; + p++; + } + } + if (*p != '"') + return -1; + escaped = p_strdup_until(unsafe_data_stack_pool, *str, p); + *str = p+1; + *unescaped_r = !esc_found ? escaped : str_unescape(escaped); + return 0; +} + +void str_append_tabescaped_n(string_t *dest, const unsigned char *src, size_t src_size) +{ + size_t prev_pos = 0; + char esc[2] = { '\001', '\0' }; + + for (size_t i = 0; i < src_size; i++) { + switch (src[i]) { + case '\000': + esc[1] = '0'; + break; + case '\001': + esc[1] = '1'; + break; + case '\t': + esc[1] = 't'; + break; + case '\r': + esc[1] = 'r'; + break; + case '\n': + esc[1] = 'n'; + break; + default: + continue; + } + str_append_data(dest, src + prev_pos, i - prev_pos); + str_append_data(dest, esc, 2); + prev_pos = i + 1; + } + str_append_data(dest, src + prev_pos, src_size - prev_pos); +} + +void str_append_tabescaped(string_t *dest, const char *src) +{ + size_t pos, prev_pos = 0; + char esc[2] = { '\001', '\0' }; + + for (;;) { + pos = prev_pos + strcspn(src + prev_pos, "\001\t\r\n"); + str_append_data(dest, src + prev_pos, pos - prev_pos); + prev_pos = pos + 1; + + switch (src[pos]) { + case '\000': + /* end of src string reached */ + return; + case '\001': + esc[1] = '1'; + break; + case '\t': + esc[1] = 't'; + break; + case '\r': + esc[1] = 'r'; + break; + case '\n': + esc[1] = 'n'; + break; + default: + i_unreached(); + } + str_append_data(dest, esc, 2); + } +} + + +const char *str_tabescape(const char *str) +{ + string_t *tmp; + const char *p; + + if ((p = strpbrk(str, "\001\t\r\n")) != NULL) { + tmp = t_str_new(128); + str_append_data(tmp, str, p-str); + str_append_tabescaped(tmp, p); + return str_c(tmp); + } + return str; +} + +void str_append_tabunescaped(string_t *dest, const void *src, size_t src_size) +{ + const unsigned char *src_c = src; + size_t start = 0, i = 0; + + while (i < src_size) { + for (; i < src_size; i++) { + if (src_c[i] == '\001') + break; + } + + str_append_data(dest, src_c + start, i-start); + + if (i < src_size) { + i++; + if (i < src_size) { + switch (src_c[i]) { + case '0': + str_append_c(dest, '\000'); + break; + case '1': + str_append_c(dest, '\001'); + break; + case 't': + str_append_c(dest, '\t'); + break; + case 'r': + str_append_c(dest, '\r'); + break; + case 'n': + str_append_c(dest, '\n'); + break; + default: + str_append_c(dest, src_c[i]); + break; + } + i++; + } + } + start = i; + } +} + +static char *str_tabunescape_from(char *str, char *src) +{ + /* @UNSAFE */ + char *dest, *p; + + dest = src; + for (;;) { + switch (src[1]) { + case '\0': + /* truncated input */ + *dest = '\0'; + return str; + case '0': + *dest++ = '\000'; + break; + case '1': + *dest++ = '\001'; + break; + case 't': + *dest++ = '\t'; + break; + case 'r': + *dest++ = '\r'; + break; + case 'n': + *dest++ = '\n'; + break; + default: + *dest++ = src[1]; + break; + } + src += 2; + + p = strchr(src, '\001'); + if (p == NULL) { + memmove(dest, src, strlen(src)+1); + break; + } + + size_t copy_len = p - src; + memmove(dest, src, copy_len); + dest += copy_len; + src = p; + } + return str; +} + +char *str_tabunescape(char *str) +{ + char *src = strchr(str, '\001'); + if (src == NULL) { + /* no unescaping needed */ + return str; + } + return str_tabunescape_from(str, src); +} + +const char *t_str_tabunescape(const char *str) +{ + const char *p; + + p = strchr(str, '\001'); + if (p == NULL) + return str; + + char *dest = t_strdup_noconst(str); + return str_tabunescape_from(dest, dest + (p - str)); +} + +static char **p_strsplit_tabescaped_inplace(pool_t pool, char *data) +{ + /* @UNSAFE */ + char **array; + unsigned int count, new_alloc_count, alloc_count; + + if (*data == '\0') + return p_new(pool, char *, 1); + + alloc_count = 32; + array = pool == unsafe_data_stack_pool ? + t_malloc_no0(sizeof(char *) * alloc_count) : + p_malloc(pool, sizeof(char *) * alloc_count); + + array[0] = data; count = 1; + char *need_unescape = NULL; + while ((data = strpbrk(data, "\t\001")) != NULL) { + /* separator or escape char found */ + if (*data == '\001') { + if (need_unescape == NULL) + need_unescape = data; + data++; + continue; + } + if (count+1 >= alloc_count) { + new_alloc_count = nearest_power(alloc_count+1); + array = p_realloc(pool, array, + sizeof(char *) * alloc_count, + sizeof(char *) * + new_alloc_count); + alloc_count = new_alloc_count; + } + *data++ = '\0'; + if (need_unescape != NULL) { + str_tabunescape_from(array[count-1], need_unescape); + need_unescape = NULL; + } + array[count++] = data; + } + if (need_unescape != NULL) + str_tabunescape_from(array[count-1], need_unescape); + i_assert(count < alloc_count); + array[count] = NULL; + + return array; +} + +const char *const *t_strsplit_tabescaped_inplace(char *data) +{ + char *const *escaped = + p_strsplit_tabescaped_inplace(unsafe_data_stack_pool, data); + return (const char *const *)escaped; +} + +char **p_strsplit_tabescaped(pool_t pool, const char *str) +{ + return p_strsplit_tabescaped_inplace(pool, p_strdup(pool, str)); +} + +const char *const *t_strsplit_tabescaped(const char *str) +{ + return t_strsplit_tabescaped_inplace(t_strdup_noconst(str)); +} diff --git a/src/lib/strescape.h b/src/lib/strescape.h new file mode 100644 index 0000000..1381650 --- /dev/null +++ b/src/lib/strescape.h @@ -0,0 +1,43 @@ +#ifndef STRESCAPE_H +#define STRESCAPE_H + +#define IS_ESCAPED_CHAR(c) ((c) == '"' || (c) == '\\' || (c) == '\'') + +/* escape all '\', '"' and "'" characters, + this is nul safe */ +const char *str_nescape(const void *str, size_t len); + +/* escape string */ +static inline const char *str_escape(const char *str) +{ + return str_nescape(str, strlen(str)); +} + +void str_append_escaped(string_t *dest, const void *src, size_t src_size); +/* remove all '\' characters, append to given string */ +void str_append_unescaped(string_t *dest, const void *src, size_t src_size); + +/* remove all '\' characters */ +char *str_unescape(char *str); + +/* Remove all '\' chars from str until '"' is reached and return the unescaped + string. *str is updated to point to the character after the '"'. Returns 0 + if ok, -1 if '"' wasn't found. */ +int str_unescape_next(const char **str, const char **unescaped_r); + +/* For Dovecot's internal protocols: Escape \001, \t, \r and \n characters + using \001. */ +const char *str_tabescape(const char *str); +void str_append_tabescaped(string_t *dest, const char *src); +void str_append_tabescaped_n(string_t *dest, const unsigned char *src, size_t src_size); +void str_append_tabunescaped(string_t *dest, const void *src, size_t src_size); +char *str_tabunescape(char *str); +const char *t_str_tabunescape(const char *str); + +char **p_strsplit_tabescaped(pool_t pool, const char *str); +const char *const *t_strsplit_tabescaped(const char *str); +/* Same as t_strsplit_tabescaped(), but the input string is modified and the + returned pointers inside the array point to the original string. */ +const char *const *t_strsplit_tabescaped_inplace(char *str); + +#endif diff --git a/src/lib/strfuncs.c b/src/lib/strfuncs.c new file mode 100644 index 0000000..56e4576 --- /dev/null +++ b/src/lib/strfuncs.c @@ -0,0 +1,945 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +/* @UNSAFE: whole file */ + +#include "lib.h" +#include "str.h" +#include "printf-format-fix.h" +#include "strfuncs.h" +#include "array.h" + +#include <stdio.h> +#include <limits.h> +#include <ctype.h> + +#define STRCONCAT_BUFSIZE 512 + +enum _str_trim_sides { + STR_TRIM_LEFT = BIT(0), + STR_TRIM_RIGHT = BIT(1), +}; + +const unsigned char uchar_nul = '\0'; +const unsigned char *uchar_empty_ptr = &uchar_nul; + +volatile int timing_safety_unoptimization; + +int i_snprintf(char *dest, size_t max_chars, const char *format, ...) +{ + va_list args; + int ret; + + i_assert(max_chars < INT_MAX); + + va_start(args, format); + ret = vsnprintf(dest, max_chars, printf_format_fix_unsafe(format), + args); + va_end(args); + + i_assert(ret >= 0); + return (unsigned int)ret < max_chars ? 0 : -1; +} + +char *p_strdup(pool_t pool, const char *str) +{ + void *mem; + size_t len; + + if (str == NULL) + return NULL; + + len = strlen(str) + 1; + mem = p_malloc(pool, len); + memcpy(mem, str, len); + return mem; +} + +void *p_memdup(pool_t pool, const void *data, size_t size) +{ + void *mem; + + mem = p_malloc(pool, size); + memcpy(mem, data, size); + return mem; +} + +char *p_strdup_empty(pool_t pool, const char *str) +{ + if (str == NULL || *str == '\0') + return NULL; + + return p_strdup(pool, str); +} + +char *p_strdup_until(pool_t pool, const void *start, const void *end) +{ + size_t size; + char *mem; + + i_assert((const char *) start <= (const char *) end); + + size = (size_t) ((const char *) end - (const char *) start); + + mem = p_malloc(pool, size + 1); + memcpy(mem, start, size); + return mem; +} + +char *p_strndup(pool_t pool, const void *str, size_t max_chars) +{ + const char *p; + char *mem; + size_t len; + + i_assert(str != NULL); + i_assert(max_chars != SIZE_MAX); + + p = memchr(str, '\0', max_chars); + if (p == NULL) + len = max_chars; + else + len = p - (const char *)str; + + mem = p_malloc(pool, len+1); + memcpy(mem, str, len); + return mem; +} + +char *p_strdup_printf(pool_t pool, const char *format, ...) +{ + va_list args; + char *ret; + + va_start(args, format); + ret = p_strdup_vprintf(pool, format, args); + va_end(args); + + return ret; +} + +char *t_noalloc_strdup_vprintf(const char *format, va_list args, + unsigned int *size_r) +{ +#define SNPRINTF_INITIAL_EXTRA_SIZE 256 + va_list args2; + char *tmp; + size_t init_size; + int ret; +#ifdef DEBUG + int old_errno = errno; +#endif + + VA_COPY(args2, args); + + /* the format string is modified only if %m exists in it. it happens + only in error conditions, so don't try to t_push() here since it'll + just slow down the normal code path. */ + format = printf_format_fix_get_len(format, &init_size); + init_size += SNPRINTF_INITIAL_EXTRA_SIZE; + + tmp = t_buffer_get(init_size); + ret = vsnprintf(tmp, init_size, format, args); + i_assert(ret >= 0); + + *size_r = ret + 1; + if ((unsigned int)ret >= init_size) { + /* didn't fit with the first guess. now we know the size, + so try again. */ + tmp = t_buffer_get(*size_r); + ret = vsnprintf(tmp, *size_r, format, args2); + i_assert((unsigned int)ret == *size_r-1); + } +#ifdef DEBUG + /* we rely on errno not changing. it shouldn't. */ + i_assert(errno == old_errno); +#endif + va_end(args2); + return tmp; +} + +char *p_strdup_vprintf(pool_t pool, const char *format, va_list args) +{ + char *tmp, *buf; + unsigned int size; + + tmp = t_noalloc_strdup_vprintf(format, args, &size); + if (pool->datastack_pool) { + t_buffer_alloc(size); + return tmp; + } else { + buf = p_malloc(pool, size); + memcpy(buf, tmp, size - 1); + return buf; + } +} + +char *vstrconcat(const char *str1, va_list args, size_t *ret_len) +{ + const char *str; + char *temp; + size_t bufsize, i, len; + + i_assert(str1 != NULL); + + str = str1; + bufsize = STRCONCAT_BUFSIZE; + temp = t_buffer_get(bufsize); + + i = 0; + do { + len = strlen(str); + + if (i + len >= bufsize) { + /* need more memory */ + bufsize = nearest_power(i + len + 1); + temp = t_buffer_reget(temp, bufsize); + } + + memcpy(temp + i, str, len); i += len; + + /* next string */ + str = va_arg(args, const char *); + } while (str != NULL); + + i_assert(i < bufsize); + + temp[i++] = '\0'; + *ret_len = i; + return temp; +} + +char *p_strconcat(pool_t pool, const char *str1, ...) +{ + va_list args; + char *temp, *ret; + size_t len; + + i_assert(str1 != NULL); + + va_start(args, str1); + + if (pool->datastack_pool) { + ret = vstrconcat(str1, args, &len); + t_buffer_alloc(len); + } else { + temp = vstrconcat(str1, args, &len); + ret = p_malloc(pool, len); + memcpy(ret, temp, len); + } + + va_end(args); + return ret; +} + +static void *t_memdup(const void *data, size_t size) +{ + void *mem = t_malloc_no0(size); + memcpy(mem, data, size); + return mem; +} + +const char *t_strdup(const char *str) +{ + return t_strdup_noconst(str); +} + +char *t_strdup_noconst(const char *str) +{ + if (str == NULL) + return NULL; + return t_memdup(str, strlen(str) + 1); +} + +const char *t_strdup_empty(const char *str) +{ + if (str == NULL || *str == '\0') + return NULL; + + return t_strdup(str); +} + +const char *t_strdup_until(const void *start, const void *end) +{ + char *mem; + size_t size; + + i_assert((const char *) start <= (const char *) end); + + size = (size_t)((const char *)end - (const char *)start); + + mem = t_malloc_no0(size + 1); + memcpy(mem, start, size); + mem[size] = '\0'; + return mem; +} + +const char *t_strndup(const void *str, size_t max_chars) +{ + i_assert(str != NULL); + return p_strndup(unsafe_data_stack_pool, str, max_chars); +} + +const char *t_strdup_printf(const char *format, ...) +{ + va_list args; + const char *ret; + + va_start(args, format); + ret = p_strdup_vprintf(unsafe_data_stack_pool, format, args); + va_end(args); + + return ret; +} + +const char *t_strdup_vprintf(const char *format, va_list args) +{ + return p_strdup_vprintf(unsafe_data_stack_pool, format, args); +} + +const char *t_strconcat(const char *str1, ...) +{ + va_list args; + const char *ret; + size_t len; + + i_assert(str1 != NULL); + + va_start(args, str1); + + ret = vstrconcat(str1, args, &len); + t_buffer_alloc(len); + + va_end(args); + return ret; +} + +const char *t_strcut(const char *str, char cutchar) +{ + const char *p; + + for (p = str; *p != '\0'; p++) { + if (*p == cutchar) + return t_strdup_until(str, p); + } + + return str; +} + +const char *t_str_replace(const char *str, char from, char to) +{ + char *out; + size_t i, len; + + if (strchr(str, from) == NULL) + return str; + + len = strlen(str); + out = t_malloc_no0(len + 1); + for (i = 0; i < len; i++) { + if (str[i] == from) + out[i] = to; + else + out[i] = str[i]; + } + out[i] = '\0'; + return out; +} + +const char *t_str_oneline(const char *str) +{ + string_t *out; + size_t len; + const char *p, *pend, *poff; + bool new_line; + + if (strpbrk(str, "\r\n") == NULL) + return str; + + len = strlen(str); + out = t_str_new(len + 1); + new_line = TRUE; + p = poff = str; + pend = str + len; + while (p < pend) { + switch (*p) { + case '\r': + if (p > poff) + str_append_data(out, poff, p - poff); + /* just drop \r */ + poff = p + 1; + break; + case '\n': + if (p > poff) + str_append_data(out, poff, p - poff); + if (new_line) { + /* coalesce multiple \n into a single space */ + } else { + /* first \n after text */ + str_append_c(out, ' '); + new_line = TRUE; + } + poff = p + 1; + break; + default: + new_line = FALSE; + break; + } + p++; + } + + if (new_line && str_len(out) > 0) + str_truncate(out, str_len(out) - 1); + else if (p > poff) + str_append_data(out, poff, p - poff); + return str_c(out); +} + +int i_strocpy(char *dest, const char *src, size_t dstsize) +{ + if (dstsize == 0) + return -1; + + while (*src != '\0' && dstsize > 1) { + *dest++ = *src++; + dstsize--; + } + + *dest = '\0'; + return *src == '\0' ? 0 : -1; +} + +char *str_ucase(char *str) +{ + char *p; + + for (p = str; *p != '\0'; p++) + *p = i_toupper(*p); + return str; +} + +char *str_lcase(char *str) +{ + char *p; + + for (p = str; *p != '\0'; p++) + *p = i_tolower(*p); + return str; +} + +const char *t_str_lcase(const char *str) +{ + i_assert(str != NULL); + + return str_lcase(t_strdup_noconst(str)); +} + +const char *t_str_ucase(const char *str) +{ + i_assert(str != NULL); + + return str_ucase(t_strdup_noconst(str)); +} + +const char *i_strstr_arr(const char *haystack, const char *const *needles) +{ + const char *ptr; + for(; *needles != NULL; needles++) + if ((ptr = strstr(haystack, *needles)) != NULL) + return ptr; + return NULL; +} + +static void str_trim_parse(const char *str, + const char *chars, enum _str_trim_sides sides, + const char **begin_r, const char **end_r) +{ + const char *p, *pend, *begin, *end; + + *begin_r = *end_r = NULL; + + pend = str + strlen(str); + if (pend == str) + return; + + p = str; + if ((sides & STR_TRIM_LEFT) != 0) { + while (p < pend && strchr(chars, *p) != NULL) + p++; + if (p == pend) + return; + } + begin = p; + + p = pend; + if ((sides & STR_TRIM_RIGHT) != 0) { + while (p > begin && strchr(chars, *(p-1)) != NULL) + p--; + if (p == begin) + return; + } + end = p; + + *begin_r = begin; + *end_r = end; +} + +const char *t_str_trim(const char *str, const char *chars) +{ + const char *begin, *end; + + str_trim_parse(str, chars, + STR_TRIM_LEFT | STR_TRIM_RIGHT, &begin, &end); + if (begin == NULL) + return ""; + return t_strdup_until(begin, end); +} + +const char *p_str_trim(pool_t pool, const char *str, const char *chars) +{ + const char *begin, *end; + + str_trim_parse(str, chars, + STR_TRIM_LEFT | STR_TRIM_RIGHT, &begin, &end); + if (begin == NULL) + return ""; + return p_strdup_until(pool, begin, end); +} + +const char *str_ltrim(const char *str, const char *chars) +{ + const char *begin, *end; + + str_trim_parse(str, chars, STR_TRIM_LEFT, &begin, &end); + if (begin == NULL) + return ""; + return begin; +} + +const char *t_str_ltrim(const char *str, const char *chars) +{ + return t_strdup(str_ltrim(str, chars)); +} + +const char *p_str_ltrim(pool_t pool, const char *str, const char *chars) +{ + return p_strdup(pool, str_ltrim(str, chars)); +} + +const char *t_str_rtrim(const char *str, const char *chars) +{ + const char *begin, *end; + + str_trim_parse(str, chars, STR_TRIM_RIGHT, &begin, &end); + if (begin == NULL) + return ""; + return t_strdup_until(begin, end); +} + +const char *p_str_rtrim(pool_t pool, const char *str, const char *chars) +{ + const char *begin, *end; + + str_trim_parse(str, chars, STR_TRIM_RIGHT, &begin, &end); + if (begin == NULL) + return ""; + return p_strdup_until(pool, begin, end); +} + +int null_strcmp(const char *s1, const char *s2) +{ + if (s1 == NULL) + return s2 == NULL ? 0 : -1; + if (s2 == NULL) + return 1; + + return strcmp(s1, s2); +} + +int null_strcasecmp(const char *s1, const char *s2) +{ + if (s1 == NULL) + return s2 == NULL ? 0 : -1; + if (s2 == NULL) + return 1; + + return strcasecmp(s1, s2); +} + +int i_memcasecmp(const void *p1, const void *p2, size_t size) +{ + const unsigned char *s1 = p1; + const unsigned char *s2 = p2; + int ret; + + while (size > 0) { + ret = i_toupper(*s1) - i_toupper(*s2); + if (ret != 0) + return ret; + + s1++; s2++; size--; + } + + return 0; +} + +int i_strcmp_p(const char *const *p1, const char *const *p2) +{ + return strcmp(*p1, *p2); +} + +int i_strcasecmp_p(const char *const *p1, const char *const *p2) +{ + return strcasecmp(*p1, *p2); +} + +bool mem_equals_timing_safe(const void *p1, const void *p2, size_t size) +{ + const unsigned char *s1 = p1, *s2 = p2; + size_t i; + int ret = 0; + + for (i = 0; i < size; i++) + ret |= s1[i] ^ s2[i]; + + /* make sure the compiler optimizer doesn't try to break out of the + above loop early. */ + timing_safety_unoptimization = ret; + return ret == 0; +} + +bool str_equals_timing_almost_safe(const char *s1, const char *s2) +{ + size_t i; + int ret = 0; + + for (i = 0; s1[i] != '\0' && s2[i] != '\0'; i++) + ret |= s1[i] ^ s2[i]; + ret |= s1[i] ^ s2[i]; + + /* make sure the compiler optimizer doesn't try to break out of the + above loop early. */ + timing_safety_unoptimization = ret; + return ret == 0; +} + +size_t +str_match(const char *p1, const char *p2) +{ + size_t i = 0; + + while(p1[i] != '\0' && p1[i] == p2[i]) + i++; + + return i; +} + +size_t i_memspn(const void *data, size_t data_len, + const void *accept, size_t accept_len) +{ + const unsigned char *start = data; + i_assert(data != NULL || data_len == 0); + i_assert(accept != NULL || accept_len == 0); + size_t pos = 0; + /* nothing to accept */ + if (accept_len == 0) + return 0; + for (; pos < data_len; pos++) { + if (memchr(accept, start[pos], accept_len) == NULL) + break; + } + return pos; +} + +size_t i_memcspn(const void *data, size_t data_len, + const void *reject, size_t reject_len) +{ + const unsigned char *start = data; + const unsigned char *r = reject; + const unsigned char *ptr = CONST_PTR_OFFSET(data, data_len); + i_assert(data != NULL || data_len == 0); + i_assert(reject != NULL || reject_len == 0); + /* nothing to reject */ + if (reject_len == 0 || data_len == 0) + return data_len; + /* Doing repeated memchr's over the data is faster than + going over it once byte by byte, as long as reject + is reasonably short. */ + for (size_t i = 0; i < reject_len; i++) { + const unsigned char *kand = + memchr(start, r[i], data_len); + if (kand != NULL && kand < ptr) + ptr = kand; + } + return ptr - start; +} + +static char ** +split_str_slow(pool_t pool, const char *data, const char *separators, bool spaces) +{ + char **array; + char *str; + unsigned int count, alloc_count, new_alloc_count; + + if (spaces) { + /* skip leading separators */ + while (*data != '\0' && strchr(separators, *data) != NULL) + data++; + } + if (*data == '\0') + return p_new(pool, char *, 1); + + str = p_strdup(pool, data); + + alloc_count = 32; + array = p_new(pool, char *, alloc_count); + + array[0] = str; count = 1; + while (*str != '\0') { + if (strchr(separators, *str) != NULL) { + /* separator found */ + if (count+1 >= alloc_count) { + new_alloc_count = nearest_power(alloc_count+1); + array = p_realloc(pool, array, + sizeof(char *) * alloc_count, + sizeof(char *) * + new_alloc_count); + alloc_count = new_alloc_count; + } + + *str = '\0'; + if (spaces) { + while (str[1] != '\0' && + strchr(separators, str[1]) != NULL) + str++; + + /* ignore trailing separators */ + if (str[1] == '\0') + break; + } + + array[count++] = str+1; + } + + str++; + } + + i_assert(count < alloc_count); + array[count] = NULL; + + return array; +} + +static char ** +split_str_fast(pool_t pool, const char *data, char sep) +{ + char **array, *str; + unsigned int count, alloc_count, new_alloc_count; + + if (*data == '\0') + return p_new(pool, char *, 1); + + str = p_strdup(pool, data); + + alloc_count = 32; + array = p_new(pool, char *, alloc_count); + + array[0] = str; count = 1; + while ((str = strchr(str, sep)) != NULL) { + /* separator found */ + if (count+1 >= alloc_count) { + new_alloc_count = nearest_power(alloc_count+1); + array = p_realloc(pool, array, + sizeof(char *) * alloc_count, + sizeof(char *) * + new_alloc_count); + alloc_count = new_alloc_count; + } + *str++ = '\0'; + array[count++] = str; + } + i_assert(count < alloc_count); + i_assert(array[count] == NULL); + + return array; +} + +static char ** +split_str(pool_t pool, const char *data, const char *separators, bool spaces) +{ + i_assert(*separators != '\0'); + + if (separators[1] == '\0' && !spaces) + return split_str_fast(pool, data, separators[0]); + else + return split_str_slow(pool, data, separators, spaces); +} + +const char **t_strsplit(const char *data, const char *separators) +{ + return (const char **)split_str(unsafe_data_stack_pool, data, + separators, FALSE); +} + +const char **t_strsplit_spaces(const char *data, const char *separators) +{ + return (const char **)split_str(unsafe_data_stack_pool, data, + separators, TRUE); +} + +char **p_strsplit(pool_t pool, const char *data, const char *separators) +{ + return split_str(pool, data, separators, FALSE); +} + +char **p_strsplit_spaces(pool_t pool, const char *data, + const char *separators) +{ + return split_str(pool, data, separators, TRUE); +} + +void p_strsplit_free(pool_t pool, char **arr) +{ + p_free(pool, arr[0]); + p_free(pool, arr); +} + +unsigned int str_array_length(const char *const *arr) +{ + unsigned int count; + + if (arr == NULL) + return 0; + + for (count = 0; *arr != NULL; arr++) + count++; + + return count; +} + +static char * +p_strarray_join_n(pool_t pool, const char *const *arr, unsigned int arr_len, + const char *separator) +{ + size_t alloc_len, sep_len, len, pos, needed_space; + unsigned int i; + char *str; + + sep_len = strlen(separator); + alloc_len = 64; + str = t_buffer_get(alloc_len); + pos = 0; + + for (i = 0; i < arr_len; i++) { + len = strlen(arr[i]); + needed_space = pos + len + sep_len + 1; + if (needed_space > alloc_len) { + alloc_len = nearest_power(needed_space); + str = t_buffer_reget(str, alloc_len); + } + + if (i != 0) { + memcpy(str + pos, separator, sep_len); + pos += sep_len; + } + + memcpy(str + pos, arr[i], len); + pos += len; + } + str[pos] = '\0'; + if (!pool->datastack_pool) + return p_memdup(pool, str, pos + 1); + t_buffer_alloc(pos + 1); + return str; +} + +const char *t_strarray_join(const char *const *arr, const char *separator) +{ + return p_strarray_join_n(unsafe_data_stack_pool, arr, + str_array_length(arr), separator); +} + +bool str_array_remove(const char **arr, const char *value) +{ + const char **dest; + + for (; *arr != NULL; arr++) { + if (strcmp(*arr, value) == 0) { + /* found it. now move the rest. */ + for (dest = arr, arr++; *arr != NULL; arr++, dest++) + *dest = *arr; + *dest = NULL; + return TRUE; + } + } + return FALSE; +} + +bool str_array_find(const char *const *arr, const char *value) +{ + for (; *arr != NULL; arr++) { + if (strcmp(*arr, value) == 0) + return TRUE; + } + return FALSE; +} + +bool str_array_icase_find(const char *const *arr, const char *value) +{ + for (; *arr != NULL; arr++) { + if (strcasecmp(*arr, value) == 0) + return TRUE; + } + return FALSE; +} + +const char **p_strarray_dup(pool_t pool, const char *const *arr) +{ + unsigned int i; + const char **ret; + char *p; + size_t len, size = sizeof(const char *); + + /* @UNSAFE: integer overflow checks are missing */ + for (i = 0; arr[i] != NULL; i++) + size += sizeof(const char *) + strlen(arr[i]) + 1; + + ret = p_malloc(pool, size); + p = PTR_OFFSET(ret, sizeof(const char *) * (i + 1)); + for (i = 0; arr[i] != NULL; i++) { + len = strlen(arr[i]) + 1; + memcpy(p, arr[i], len); + ret[i] = p; + p += len; + } + i_assert(PTR_OFFSET(ret, size) == (void *)p); + return ret; +} + +const char *dec2str(uintmax_t number) +{ + return dec2str_buf(t_malloc_no0(MAX_INT_STRLEN), number); +} + +char *dec2str_buf(char buffer[STATIC_ARRAY MAX_INT_STRLEN], uintmax_t number) +{ + int pos; + + pos = MAX_INT_STRLEN; + buffer[--pos] = '\0'; + do { + buffer[--pos] = (number % 10) + '0'; + number /= 10; + } while (number != 0 && pos >= 0); + + i_assert(pos >= 0); + return buffer + pos; +} + +char *p_array_const_string_join(pool_t pool, const ARRAY_TYPE(const_string) *arr, + const char *separator) +{ + if (array_count(arr) == 0) + return ""; + return p_strarray_join_n(pool, array_front(arr), array_count(arr), + separator); +} diff --git a/src/lib/strfuncs.h b/src/lib/strfuncs.h new file mode 100644 index 0000000..1ddb82f --- /dev/null +++ b/src/lib/strfuncs.h @@ -0,0 +1,170 @@ +#ifndef STRFUNC_H +#define STRFUNC_H + +/* Maximum number of bytes needed for the largest uintmax_t or the lowest + intmax_t number in base 10. This value includes the trailing \0. */ +#define MAX_INT_STRLEN ((sizeof(uintmax_t) * CHAR_BIT + 2) / 3 + 1) + +extern const unsigned char uchar_nul; /* (const unsigned char *)"" */ +extern const unsigned char *uchar_empty_ptr; /* non-NULL pointer that shouldn't be dereferenced. */ + +/* Returns -1 if dest wasn't large enough, 0 if not. */ +int i_snprintf(char *dest, size_t max_chars, const char *format, ...) + ATTR_FORMAT(3, 4); + +char *p_strdup(pool_t pool, const char *str) ATTR_MALLOC; +void *p_memdup(pool_t pool, const void *data, size_t size) ATTR_MALLOC; +/* return NULL if str = "" */ +char *p_strdup_empty(pool_t pool, const char *str) ATTR_MALLOC; +/* *end isn't included */ +char *p_strdup_until(pool_t pool, const void *start, const void *end) + ATTR_MALLOC ATTR_RETURNS_NONNULL; +char *p_strndup(pool_t pool, const void *str, size_t max_chars) ATTR_MALLOC; +char *p_strdup_printf(pool_t pool, const char *format, ...) + ATTR_FORMAT(2, 3) ATTR_MALLOC ATTR_RETURNS_NONNULL; +char *p_strdup_vprintf(pool_t pool, const char *format, va_list args) + ATTR_FORMAT(2, 0) ATTR_MALLOC ATTR_RETURNS_NONNULL; +char *p_strconcat(pool_t pool, const char *str1, ...) + ATTR_SENTINEL ATTR_MALLOC; + +/* same with temporary memory allocations: */ +const char *t_strdup(const char *str) ATTR_MALLOC; +char *t_strdup_noconst(const char *str) ATTR_MALLOC; +/* return NULL if str = "" */ +const char *t_strdup_empty(const char *str) ATTR_MALLOC; +/* *end isn't included */ +const char *t_strdup_until(const void *start, const void *end) + ATTR_MALLOC ATTR_RETURNS_NONNULL; +const char *t_strndup(const void *str, size_t max_chars) ATTR_MALLOC; +const char *t_strdup_printf(const char *format, ...) + ATTR_FORMAT(1, 2) ATTR_MALLOC ATTR_RETURNS_NONNULL; +const char *t_strdup_vprintf(const char *format, va_list args) + ATTR_FORMAT(1, 0) ATTR_MALLOC ATTR_RETURNS_NONNULL; +const char *t_strconcat(const char *str1, ...) + ATTR_SENTINEL ATTR_MALLOC; + +/* Like t_strdup(), but stop at cutchar. */ +const char *t_strcut(const char *str, char cutchar); +/* Replace all from->to chars in the string. */ +const char *t_str_replace(const char *str, char from, char to); +/* Put the string on a single line by replacing all newlines with spaces and + dropping any carriage returns. Sequences of several newlines are merged into + one space and newlines at the beginning and end of the string are dropped. */ +const char *t_str_oneline(const char *str); + +/* Like strlcpy(), but return -1 if buffer was overflown, 0 if not. */ +int i_strocpy(char *dest, const char *src, size_t dstsize); + +char *str_ucase(char *str); +char *str_lcase(char *str); +const char *t_str_lcase(const char *str); +const char *t_str_ucase(const char *str); + +/* Return pointer to first matching needle */ +const char *i_strstr_arr(const char *haystack, const char *const *needles); + +/* Trim matching chars from either side of the string */ +const char *t_str_trim(const char *str, const char *chars); +const char *p_str_trim(pool_t pool, const char *str, const char *chars); +const char *str_ltrim(const char *str, const char *chars); +const char *t_str_ltrim(const char *str, const char *chars); +const char *p_str_ltrim(pool_t pool, const char *str, const char *chars); +const char *t_str_rtrim(const char *str, const char *chars); +const char *p_str_rtrim(pool_t pool, const char *str, const char *chars); + +int null_strcmp(const char *s1, const char *s2) ATTR_PURE; +int null_strcasecmp(const char *s1, const char *s2) ATTR_PURE; +int i_memcasecmp(const void *p1, const void *p2, size_t size) ATTR_PURE; +int i_strcmp_p(const char *const *p1, const char *const *p2) ATTR_PURE; +int i_strcasecmp_p(const char *const *p1, const char *const *p2) ATTR_PURE; +/* Returns TRUE if the two memory areas are equal. This function is safe + against timing attacks, so it compares all the bytes every time. */ +bool mem_equals_timing_safe(const void *p1, const void *p2, size_t size); +/* Returns TRUE if the two strings are equal. Similar to + mem_equals_timing_safe() this function is safe against timing attacks when + the string lengths are the same. If not, the length of the secret string may + be leaked, but otherwise the contents won't be. */ +bool str_equals_timing_almost_safe(const char *s1, const char *s2); + +size_t str_match(const char *p1, const char *p2) ATTR_PURE; +static inline ATTR_PURE bool str_begins(const char *haystack, const char *needle) +{ + return needle[str_match(haystack, needle)] == '\0'; +} +#if defined(__GNUC__) && (__GNUC__ >= 2) +/* GCC (and Clang) are known to have a compile-time strlen("literal") shortcut, and + an optimised strncmp(), so use that by default. Macro is multi-evaluation safe. */ +# define str_begins(h, n) (__builtin_constant_p(n) ? strncmp((h), (n), strlen(n))==0 : (str_begins)((h), (n))) +#endif + +/* Get length of a prefix segment. + + Calculates the length (in bytes) of the initial segment of s which consists + entirely of bytes in accept. +*/ +size_t i_memspn(const void *data, size_t data_len, + const void *accept, size_t accept_len); +/* Get length of a prefix segment. + + Calculates the length of the initial segment of s which consists entirely of + bytes not in reject. +*/ +size_t i_memcspn(const void *data, size_t data_len, + const void *reject, size_t reject_len); + +static inline char *i_strchr_to_next(const char *str, char chr) +{ + char *tmp = (char *)strchr(str, chr); + return tmp == NULL ? NULL : tmp+1; +} + +/* separators is an array of separator characters, not a separator string. + an empty data string results in an array containing only NULL. */ +char **p_strsplit(pool_t pool, const char *data, const char *separators) + ATTR_MALLOC ATTR_RETURNS_NONNULL; +const char **t_strsplit(const char *data, const char *separators) + ATTR_MALLOC ATTR_RETURNS_NONNULL; +/* like p_strsplit(), but treats multiple adjacent separators as a single + separator. separators at the beginning or at the end of the string are also + ignored, so it's not possible for the result to have any empty strings. */ +char **p_strsplit_spaces(pool_t pool, const char *data, const char *separators) + ATTR_MALLOC ATTR_RETURNS_NONNULL; +const char **t_strsplit_spaces(const char *data, const char *separators) + ATTR_MALLOC ATTR_RETURNS_NONNULL; +void p_strsplit_free(pool_t pool, char **arr); + +const char *dec2str(uintmax_t number); +/* Use the given buffer to write out the number. Returns pointer to the + written number in the buffer. Note that this isn't the same as the beginning + of the buffer. */ +char *dec2str_buf(char buffer[STATIC_ARRAY MAX_INT_STRLEN], uintmax_t number); + +/* Return length of NULL-terminated list string array */ +unsigned int str_array_length(const char *const *arr) ATTR_PURE; +/* Return all strings from array joined into one string. */ +const char *t_strarray_join(const char *const *arr, const char *separator) + ATTR_MALLOC ATTR_RETURNS_NONNULL; +/* Removes a value from NULL-terminated string array. Returns TRUE if found. */ +bool str_array_remove(const char **arr, const char *value); +/* Returns TRUE if value exists in NULL-terminated string array. */ +bool str_array_find(const char *const *arr, const char *value); +/* Like str_array_find(), but use strcasecmp(). */ +bool str_array_icase_find(const char *const *arr, const char *value); +/* Duplicate array of strings. The memory can be freed by freeing the + return value. */ +const char **p_strarray_dup(pool_t pool, const char *const *arr) + ATTR_MALLOC ATTR_RETURNS_NONNULL; + +/* Join ARRAY_TYPE(const_string) to a string, similar to t_strarray_join() */ +char *p_array_const_string_join(pool_t pool, const ARRAY_TYPE(const_string) *arr, + const char *separator); +#define t_array_const_string_join(arr, separator) \ + ((const char *)p_array_const_string_join(unsafe_data_stack_pool, arr, separator)) + +/* INTERNAL */ +char *t_noalloc_strdup_vprintf(const char *format, va_list args, + unsigned int *size_r) + ATTR_FORMAT(1, 0) ATTR_RETURNS_NONNULL; +char *vstrconcat(const char *str1, va_list args, size_t *ret_len) ATTR_MALLOC; + +#endif diff --git a/src/lib/strnum.c b/src/lib/strnum.c new file mode 100644 index 0000000..4b3da4a --- /dev/null +++ b/src/lib/strnum.c @@ -0,0 +1,464 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "strnum.h" + +bool str_is_numeric(const char *str, char end_char) +{ + if (*str == '\0' || *str == end_char) + return FALSE; + + while (*str != '\0' && *str != end_char) { + if (*str < '0' || *str > '9') + return FALSE; + str++; + } + + return TRUE; +} + +bool str_is_float(const char *str, char end_char) +{ + bool dot_seen = FALSE; + bool num_seen = FALSE; + + if (*str == '\0' || *str == end_char) + return FALSE; + + while (*str != '\0' && *str != end_char) { + if (*str == '.') { + if (dot_seen || !num_seen) return FALSE; + dot_seen = TRUE; + num_seen = FALSE; + str++; + /* enforce that number follows dot */ + continue; + } + if (*str < '0' || *str > '9') + return FALSE; + num_seen = TRUE; + str++; + } + + return num_seen; +} + +/* + * Unsigned decimal + */ + +#define STR_PARSE_U__TEMPLATE(name, type) \ +int name(const char *str, type *num_r, const char **endp_r) \ +{ \ + uintmax_t l; \ + if (str_parse_uintmax(str, &l, endp_r) < 0 || l > (type)-1) \ + return -1; \ + *num_r = (type)l; \ + return 0; \ +} + +STR_PARSE_U__TEMPLATE(str_parse_uint, unsigned int) +STR_PARSE_U__TEMPLATE(str_parse_ulong, unsigned long) +STR_PARSE_U__TEMPLATE(str_parse_ullong, unsigned long long) +STR_PARSE_U__TEMPLATE(str_parse_uint32, uint32_t) +STR_PARSE_U__TEMPLATE(str_parse_uint64, uint64_t) + +#define STR_TO_U__TEMPLATE(name, type) \ +int name(const char *str, type *num_r) \ +{ \ + uintmax_t l; \ + if (str_to_uintmax(str, &l) < 0 || l > (type)-1) \ + return -1; \ + *num_r = (type)l; \ + return 0; \ +} + +STR_TO_U__TEMPLATE(str_to_uint, unsigned int) +STR_TO_U__TEMPLATE(str_to_ulong, unsigned long) +STR_TO_U__TEMPLATE(str_to_ullong, unsigned long long) +STR_TO_U__TEMPLATE(str_to_uint32, uint32_t) +STR_TO_U__TEMPLATE(str_to_uint64, uint64_t) + +int str_parse_uintmax(const char *str, uintmax_t *num_r, + const char **endp_r) +{ + uintmax_t n = 0; + + if (*str < '0' || *str > '9') + return -1; + + do { + if (n >= ((uintmax_t)-1 / 10)) { + if (n > (uintmax_t)-1 / 10) + return -1; + if ((uintmax_t)(*str - '0') > ((uintmax_t)-1 % 10)) + return -1; + } + n = n * 10 + (*str - '0'); + str++; + } while (*str >= '0' && *str <= '9'); + + if (endp_r != NULL) + *endp_r = str; + *num_r = n; + return 0; +} +int str_to_uintmax(const char *str, uintmax_t *num_r) +{ + const char *endp; + uintmax_t n; + int ret = str_parse_uintmax(str, &n, &endp); + if ((ret != 0) || (*endp != '\0')) + return -1; + *num_r = n; + return 0; +} + +bool str_uint_equals(const char *str, uintmax_t num) +{ + uintmax_t l; + + if (str_to_uintmax(str, &l) < 0) + return FALSE; + return l == num; +} + +/* + * Unsigned hexadecimal + */ + +#define STR_PARSE_UHEX__TEMPLATE(name, type) \ +int name(const char *str, type *num_r, const char **endp_r) \ +{ \ + uintmax_t l; \ + if (str_parse_uintmax_hex(str, &l, endp_r) < 0 || l > (type)-1) \ + return -1; \ + *num_r = (type)l; \ + return 0; \ +} + +STR_PARSE_UHEX__TEMPLATE(str_parse_uint_hex, unsigned int) +STR_PARSE_UHEX__TEMPLATE(str_parse_ulong_hex, unsigned long) +STR_PARSE_UHEX__TEMPLATE(str_parse_ullong_hex, unsigned long long) +STR_PARSE_UHEX__TEMPLATE(str_parse_uint32_hex, uint32_t) +STR_PARSE_UHEX__TEMPLATE(str_parse_uint64_hex, uint64_t) + +#define STR_TO_UHEX__TEMPLATE(name, type) \ +int name(const char *str, type *num_r) \ +{ \ + uintmax_t l; \ + if (str_to_uintmax_hex(str, &l) < 0 || l > (type)-1) \ + return -1; \ + *num_r = (type)l; \ + return 0; \ +} + +STR_TO_UHEX__TEMPLATE(str_to_uint_hex, unsigned int) +STR_TO_UHEX__TEMPLATE(str_to_ulong_hex, unsigned long) +STR_TO_UHEX__TEMPLATE(str_to_ullong_hex, unsigned long long) +STR_TO_UHEX__TEMPLATE(str_to_uint32_hex, uint32_t) +STR_TO_UHEX__TEMPLATE(str_to_uint64_hex, uint64_t) + +static inline int _str_parse_hex(const char ch, + unsigned int *hex_r) +{ + switch (ch) { + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + *hex_r = (unsigned int)(ch - 'a' + 10); + return 0; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + *hex_r = (unsigned int)(ch - 'A' + 10); + return 0; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + *hex_r = (unsigned int)(ch - '0'); + return 0; + default: + break; + } + return -1; +} +int str_parse_uintmax_hex(const char *str, uintmax_t *num_r, + const char **endp_r) +{ + unsigned int hex; + uintmax_t n = 0; + + if (_str_parse_hex(*str, &hex) < 0) + return -1; + + do { + if (n > (uintmax_t)-1 >> 4) + return -1; + n = (n << 4) + hex; + str++; + } while (_str_parse_hex(*str, &hex) >= 0); + if (endp_r != NULL) + *endp_r = str; + *num_r = n; + return 0; +} +int str_to_uintmax_hex(const char *str, uintmax_t *num_r) +{ + const char *endp; + uintmax_t n; + int ret = str_parse_uintmax_hex(str, &n, &endp); + if ((ret != 0) || (*endp != '\0')) + return -1; + *num_r = n; + return 0; +} + +/* + * Unsigned octal + */ + +#define STR_PARSE_UOCT__TEMPLATE(name, type) \ +int name(const char *str, type *num_r, const char **endp_r) \ +{ \ + uintmax_t l; \ + if (str_parse_uintmax_oct(str, &l, endp_r) < 0 || l > (type)-1) \ + return -1; \ + *num_r = (type)l; \ + return 0; \ +} + +STR_PARSE_UOCT__TEMPLATE(str_parse_uint_oct, unsigned int) +STR_PARSE_UOCT__TEMPLATE(str_parse_ulong_oct, unsigned long) +STR_PARSE_UOCT__TEMPLATE(str_parse_ullong_oct, unsigned long long) +STR_PARSE_UOCT__TEMPLATE(str_parse_uint32_oct, uint32_t) +STR_PARSE_UOCT__TEMPLATE(str_parse_uint64_oct, uint64_t) + +#define STR_TO_UOCT__TEMPLATE(name, type) \ +int name(const char *str, type *num_r) \ +{ \ + uintmax_t l; \ + if (str_to_uintmax_oct(str, &l) < 0 || l > (type)-1) \ + return -1; \ + *num_r = (type)l; \ + return 0; \ +} + +STR_TO_UOCT__TEMPLATE(str_to_uint_oct, unsigned int) +STR_TO_UOCT__TEMPLATE(str_to_ulong_oct, unsigned long) +STR_TO_UOCT__TEMPLATE(str_to_ullong_oct, unsigned long long) +STR_TO_UOCT__TEMPLATE(str_to_uint32_oct, uint32_t) +STR_TO_UOCT__TEMPLATE(str_to_uint64_oct, uint64_t) + +int str_parse_uintmax_oct(const char *str, uintmax_t *num_r, + const char **endp_r) +{ + uintmax_t n = 0; + + if (*str < '0' || *str > '7') + return -1; + + for (; *str >= '0' && *str <= '7'; str++) { + if (n > (uintmax_t)-1 >> 3) + return -1; + n = (n << 3) + (*str - '0'); + } + if (endp_r != NULL) + *endp_r = str; + *num_r = n; + return 0; +} +int str_to_uintmax_oct(const char *str, uintmax_t *num_r) +{ + const char *endp; + uintmax_t n; + int ret = str_parse_uintmax_oct(str, &n, &endp); + if ((ret != 0) || (*endp != '\0')) + return -1; + *num_r = n; + return 0; +} + +/* + * Signed + */ + +#define STR_PARSE_S__TEMPLATE(name, type, int_min, int_max) \ +int name(const char *str, type *num_r, const char **endp_r) \ +{ \ + intmax_t l; \ + if (str_parse_intmax(str, &l, endp_r) < 0) \ + return -1; \ + if (l < int_min || l > int_max) \ + return -1; \ + *num_r = (type)l; \ + return 0; \ +} + +STR_PARSE_S__TEMPLATE(str_parse_int, int, INT_MIN, INT_MAX) +STR_PARSE_S__TEMPLATE(str_parse_long, long, LONG_MIN, LONG_MAX) +STR_PARSE_S__TEMPLATE(str_parse_llong, long long, LLONG_MIN, LLONG_MAX) +STR_PARSE_S__TEMPLATE(str_parse_int32, int32_t, INT32_MIN, INT32_MAX) +STR_PARSE_S__TEMPLATE(str_parse_int64, int64_t, INT64_MIN, INT64_MAX) + +#define STR_TO_S__TEMPLATE(name, type, int_min, int_max) \ +int name(const char *str, type *num_r) \ +{ \ + intmax_t l; \ + if (str_to_intmax(str, &l) < 0) \ + return -1; \ + if (l < int_min || l > int_max) \ + return -1; \ + *num_r = (type)l; \ + return 0; \ +} + +STR_TO_S__TEMPLATE(str_to_int, int, INT_MIN, INT_MAX) +STR_TO_S__TEMPLATE(str_to_long, long, LONG_MIN, LONG_MAX) +STR_TO_S__TEMPLATE(str_to_llong, long long, LLONG_MIN, LLONG_MAX) +STR_TO_S__TEMPLATE(str_to_int32, int32_t, INT32_MIN, INT32_MAX) +STR_TO_S__TEMPLATE(str_to_int64, int64_t, INT64_MIN, INT64_MAX) + +int ATTR_NO_SANITIZE_IMPLICIT_CONVERSION ATTR_NO_SANITIZE_INTEGER +str_parse_intmax(const char *str, intmax_t *num_r, const char **endp_r) +{ + bool neg = FALSE; + uintmax_t l; + + if (*str == '-') { + neg = TRUE; + str++; + } + if (str_parse_uintmax(str, &l, endp_r) < 0) + return -1; + + if (!neg) { + if (l > INTMAX_MAX) + return -1; + *num_r = (intmax_t)l; + } else { + if (l > UINTMAX_MAX - (UINTMAX_MAX + INTMAX_MIN)) + return -1; + *num_r = (intmax_t) UNSIGNED_MINUS(l); + } + return 0; +} +int str_to_intmax(const char *str, intmax_t *num_r) +{ + const char *endp; + intmax_t n; + int ret = str_parse_intmax(str, &n, &endp); + if ((ret != 0) || (*endp != '\0')) + return -1; + *num_r = n; + return 0; +} + +/* + * Special numeric types + */ + +static int verify_xid(uintmax_t l, unsigned int result_size) +{ + unsigned int result_bits; + + /* we assume that result is a signed type, + but that it can never be negative */ + result_bits = result_size*CHAR_BIT - 1; + if ((l >> result_bits) != 0) + return -1; + return 0; +} + +int str_to_uid(const char *str, uid_t *num_r) +{ + uintmax_t l; + + if (str_to_uintmax(str, &l) < 0) + return -1; + + if (verify_xid(l, sizeof(*num_r)) < 0) + return -1; + *num_r = (uid_t)l; + return 0; +} + +int str_to_gid(const char *str, gid_t *num_r) +{ + uintmax_t l; + + if (str_to_uintmax(str, &l) < 0) + return -1; + + /* OS X uses negative GIDs */ +#ifndef __APPLE__ + if (verify_xid(l, sizeof(*num_r)) < 0) + return -1; +#endif + *num_r = (gid_t)l; + return 0; +} + +int str_to_pid(const char *str, pid_t *num_r) +{ + uintmax_t l; + + if (str_to_uintmax(str, &l) < 0) + return -1; + + if (verify_xid(l, sizeof(*num_r)) < 0) + return -1; + *num_r = (pid_t)l; + return 0; +} + +int str_to_ino(const char *str, ino_t *num_r) +{ + uintmax_t l; + + if (str_to_uintmax(str, &l) < 0) + return -1; + + if (verify_xid(l, sizeof(*num_r)) < 0) + return -1; + *num_r = (ino_t)l; + return 0; +} + +int str_to_uoff(const char *str, uoff_t *num_r) +{ + uintmax_t l; + + if (str_to_uintmax(str, &l) < 0) + return -1; + + if (l > UOFF_T_MAX) + return -1; + *num_r = (uoff_t)l; + return 0; +} + +int str_to_time(const char *str, time_t *num_r) +{ + intmax_t l; + + if (str_to_intmax(str, &l) < 0) + return -1; + + *num_r = (time_t)l; + return 0; +} + +STR_PARSE_U__TEMPLATE(str_parse_uoff, uoff_t) + +/* + * Error handling + */ + +const char *str_num_error(const char *str) +{ + if (*str == '-') { + if (!str_is_numeric(str + 1, '\0')) + return "Not a valid number"; + return "Number too small"; + } else { + if (!str_is_numeric(str, '\0')) + return "Not a valid number"; + return "Number too large"; + } +} diff --git a/src/lib/strnum.h b/src/lib/strnum.h new file mode 100644 index 0000000..471d4aa --- /dev/null +++ b/src/lib/strnum.h @@ -0,0 +1,192 @@ +#ifndef STRNUM_H +#define STRNUM_H + +/* str_to_*() functions return 0 if string is nothing more than a valid number + in valid range. Otherwise -1 is returned and num_r is left untouched + + str_parse_*() helpers do not require the number to be the entire string + and pass back the pointer just past a valid parsed integer in endp_r if + it is non-NULL. What is written to endp_r in error cases is undefined. +*/ + +/* + * Unsigned decimal + */ + +int str_to_uint(const char *str, unsigned int *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_uint(const char *str, unsigned int *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_ulong(const char *str, unsigned long *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_ulong(const char *str, unsigned long *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_ullong(const char *str, unsigned long long *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_ullong(const char *str, unsigned long long *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_uint32(const char *str, uint32_t *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_uint32(const char *str, uint32_t *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_uint64(const char *str, uint64_t *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_uint64(const char *str, uint64_t *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_uintmax(const char *str, uintmax_t *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_uintmax(const char *str, uintmax_t *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +/* Returns TRUE if str is a valid unsigned number that equals to num. */ +bool str_uint_equals(const char *str, uintmax_t num); + +/* + * Unsigned hexadecimal + */ + +int str_to_uint_hex(const char *str, unsigned int *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_uint_hex(const char *str, unsigned int *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_ulong_hex(const char *str, unsigned long *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_ulong_hex(const char *str, unsigned long *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_ullong_hex(const char *str, unsigned long long *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_ullong_hex(const char *str, unsigned long long *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_uint32_hex(const char *str, uint32_t *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_uint32_hex(const char *str, uint32_t *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_uint64_hex(const char *str, uint64_t *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_uint64_hex(const char *str, uint64_t *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_uintmax_hex(const char *str, uintmax_t *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_uintmax_hex(const char *str, uintmax_t *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +/* + * Unsigned octal + */ + +int str_to_uint_oct(const char *str, unsigned int *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_uint_oct(const char *str, unsigned int *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_ulong_oct(const char *str, unsigned long *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_ulong_oct(const char *str, unsigned long *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_ullong_oct(const char *str, unsigned long long *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_ullong_oct(const char *str, unsigned long long *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_uint32_oct(const char *str, uint32_t *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_uint32_oct(const char *str, uint32_t *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_uint64_oct(const char *str, uint64_t *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_uint64_oct(const char *str, uint64_t *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_uintmax_oct(const char *str, uintmax_t *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_uintmax_oct(const char *str, uintmax_t *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +/* + * Signed + */ + +int str_to_int(const char *str, int *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_int(const char *str, int *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_long(const char *str, long *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_long(const char *str, long *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_llong(const char *str, long long *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_llong(const char *str, long long *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_int32(const char *str, int32_t *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_int32(const char *str, int32_t *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_int64(const char *str, int64_t *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_int64(const char *str, int64_t *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_intmax(const char *str, intmax_t *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_intmax(const char *str, intmax_t *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +/* + * Special numeric types + */ + +int str_to_uid(const char *str, uid_t *num_r) + ATTR_WARN_UNUSED_RESULT; + +int str_to_gid(const char *str, gid_t *num_r) + ATTR_WARN_UNUSED_RESULT; + +int str_to_pid(const char *str, pid_t *num_r) + ATTR_WARN_UNUSED_RESULT; + +int str_to_ino(const char *str, ino_t *num_r) + ATTR_WARN_UNUSED_RESULT; + +int str_to_uoff(const char *str, uoff_t *num_r) + ATTR_WARN_UNUSED_RESULT; +int str_parse_uoff(const char *str, uoff_t *num_r, + const char **endp_r) ATTR_WARN_UNUSED_RESULT ATTR_NULL(3); + +int str_to_time(const char *str, time_t *num_r) + ATTR_WARN_UNUSED_RESULT; + +/* + * Utility + */ + +/* Return TRUE if all characters in string are numbers. + Stop when `end_char' is found from string. */ +bool str_is_numeric(const char *str, char end_char) ATTR_PURE; + +/* Return TRUE when string has one or more numbers, followed + with zero or one dot, followed with at least one number. */ +bool str_is_float(const char *str, char end_char) ATTR_PURE; + +/* Returns human readable string about what is wrong with the string. + This function assumes that str_to_*() had already returned -1 for the + string. */ +const char *str_num_error(const char *str); + +#endif diff --git a/src/lib/test-aqueue.c b/src/lib/test-aqueue.c new file mode 100644 index 0000000..0ff03ac --- /dev/null +++ b/src/lib/test-aqueue.c @@ -0,0 +1,73 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "array.h" +#include "aqueue.h" + +static bool aqueue_is_ok(struct aqueue *aqueue, unsigned int deleted_n) +{ + const unsigned int *p; + unsigned int n, i, count; + + count = aqueue_count(aqueue); + for (i = 0, n = 1; i < count; i++, n++) { + p = array_idx_i(aqueue->arr, aqueue_idx(aqueue, i)); + if (i == deleted_n) + n++; + if (*p != n) + return FALSE; + } + return TRUE; +} + +static const unsigned int aqueue_input[] = { 1, 2, 3, 4, 5, 6 }; +static const char *test_aqueue2(unsigned int initial_size) +{ + ARRAY(unsigned int) aqueue_array; + unsigned int i, j, k; + + for (i = 0; i < N_ELEMENTS(aqueue_input); i++) { + for (k = 0; k < N_ELEMENTS(aqueue_input); k++) { + struct aqueue *aqueue; + + t_array_init(&aqueue_array, initial_size); + aqueue = aqueue_init(&aqueue_array.arr); + aqueue->head = aqueue->tail = initial_size - 1; + for (j = 0; j < k; j++) { + aqueue_append(aqueue, &aqueue_input[j]); + if (aqueue_count(aqueue) != j + 1) { + return t_strdup_printf("Wrong count after append %u vs %u)", + aqueue_count(aqueue), j + 1); + } + if (!aqueue_is_ok(aqueue, UINT_MAX)) + return "Invalid data after append"; + } + + if (k != 0 && i < k) { + aqueue_delete(aqueue, i); + if (aqueue_count(aqueue) != k - 1) + return "Wrong count after delete"; + if (!aqueue_is_ok(aqueue, i)) + return "Invalid data after delete"; + } + aqueue_clear(aqueue); + if (aqueue_count(aqueue) != 0) + return "aqueue_clear() broken"; + aqueue_deinit(&aqueue); + } + } + return NULL; +} + +void test_aqueue(void) +{ + unsigned int i; + const char *reason = NULL; + + for (i = 1; i <= N_ELEMENTS(aqueue_input) + 1 && reason == NULL; i++) { + T_BEGIN { + reason = test_aqueue2(i); + } T_END; + } + test_out_reason("aqueue", reason == NULL, reason); +} diff --git a/src/lib/test-array.c b/src/lib/test-array.c new file mode 100644 index 0000000..e6aade1 --- /dev/null +++ b/src/lib/test-array.c @@ -0,0 +1,441 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "array.h" + + +struct foo { + unsigned int a, b, c; +}; + +static void test_array_elem(void) +{ + ARRAY(struct foo *) foos; + struct foo *nfoo; + struct foo *foo; + struct foo local_foo; + unsigned int i; + + test_begin("array elem"); + t_array_init(&foos, 32); + + foo = &local_foo; + array_foreach_elem(&foos, foo) + test_assert(FALSE); + test_assert(foo == &local_foo); + + for (i = 1; i <= 3; i++) { + nfoo = t_new(struct foo, 1); + nfoo->a = i; + array_push_back(&foos, &nfoo); + } + + struct foo *const *foo_p = array_idx(&foos, 1); + unsigned int idx = 1; + foo = array_idx_elem(&foos, idx++); + /* make sure idx isn't expanded multiple times in the macro */ + test_assert(idx == 2); + test_assert(*foo_p == foo); + + i = 1; + array_foreach_elem(&foos, foo) { + test_assert(foo->a == i); + i++; + } + test_assert(foo->a == i-1); + test_end(); +} + +static void test_array_count(void) +{ + ARRAY(struct foo) foos; + struct foo nfoo; + + test_begin("array count/empty"); + t_array_init(&foos, 32); + + test_assert(array_count(&foos) == 0); + test_assert(array_is_empty(&foos)); + test_assert(!array_not_empty(&foos)); + nfoo.a = nfoo.b = nfoo.c = 9; + array_push_back(&foos, &nfoo); + test_assert(array_count(&foos) == 1); + test_assert(!array_is_empty(&foos)); + test_assert(array_not_empty(&foos)); + + test_end(); +} +static void test_array_foreach(void) +{ + ARRAY(struct foo) foos; + const struct foo *foo; + struct foo nfoo; + unsigned int i; + + test_begin("array foreach"); + t_array_init(&foos, 32); + for (i = 0; i < 10; i++) { + nfoo.a = nfoo.b = nfoo.c = i; + array_push_back(&foos, &nfoo); + } + + array_foreach(&foos, foo) { + i = array_foreach_idx(&foos, foo); + test_assert(foo->a == i); + test_assert(foo->b == i); + test_assert(foo->c == i); + } + /* points past the last element */ + test_assert(foo == array_idx(&foos, i)+1); + test_end(); +} + +static void test_array_foreach_reverse(void) +{ + ARRAY(unsigned int) arr; + const unsigned int *i_p; + unsigned int i, i2, *imod_p; + + test_begin("array foreach reverse"); + t_array_init(&arr, 32); + + /* first test that array_foreach() + array_delete() doesn't really + work as we might hope.. */ + for (i = 1; i <= 5; i++) + array_push_back(&arr, &i); + array_foreach(&arr, i_p) { + i = array_foreach_idx(&arr, i_p); + array_delete(&arr, i, 1); + } + test_assert(array_count(&arr) == 2); + + /* but using array_foreach_reverse() + array_delete() does work: */ + array_clear(&arr); + i2 = 5; + for (i = 1; i <= i2; i++) + array_push_back(&arr, &i); + array_foreach_reverse(&arr, i_p) { + i = array_foreach_idx(&arr, i_p); + test_assert(*i_p == i2); + test_assert(*i_p == i + 1); + array_delete(&arr, i, 1); + i2--; + } + test_assert(array_count(&arr) == 0); + + /* also array_foreach_reverse_modifiable() + array_delete() works: */ + i2 = 5; + for (i = 1; i <= i2; i++) + array_push_back(&arr, &i); + array_foreach_reverse_modifiable(&arr, imod_p) { + i = array_foreach_idx(&arr, imod_p); + test_assert(*imod_p == i2); + test_assert(*imod_p == i + 1); + array_delete(&arr, i, 1); + i2--; + } + test_assert(array_count(&arr) == 0); + + test_end(); +} + +static void test_array_foreach_elem_string(void) +{ + ARRAY(char *) blurbs; + ARRAY(const char *) cblurbs; + char *string; + const char *cstring; + int i; + + test_begin("array foreach_elem ro/rw strings"); + t_array_init(&blurbs, 32); + t_array_init(&cblurbs, 32); + for (i = 0; i < 10; i++) { + cstring = t_strdup_printf("x%iy", i); + string = t_strdup_noconst(cstring); + array_push_back(&blurbs, &string); + array_push_back(&cblurbs, &cstring); + } + + i = 0; + array_foreach_elem(&blurbs, string) { + test_assert_idx(string[0] == 'x' && string[1]-'0' == i && string[2] == 'y', i); + i++; + } + i = 0; + array_foreach_elem(&cblurbs, cstring) { + test_assert_idx(cstring[0] == 'x' && cstring[1]-'0' == i && cstring[2] == 'y', i); + i++; + } + test_end(); +} + +static int test_int_compare(const int *key, const int *elem) +{ + return (*key < *elem) ? -1 : + (*key > *elem) ? 1 : + 0; +} +static void test_array_reverse(void) +{ + ARRAY(int) intarr; + int input[] = { -1234567890, -272585721, 272485922, 824725652 }; + const int tmpi = 999, *output; + unsigned int i, j; + + test_begin("array reverse"); + t_array_init(&intarr, 5); + for (i = 0; i <= N_ELEMENTS(input); i++) { + array_clear(&intarr); + array_append(&intarr, input, i); + array_reverse(&intarr); + + output = i == 0 ? NULL : array_front(&intarr); + for (j = 0; j < i; j++) + test_assert(input[i-j-1] == output[j]); + } + test_end(); + + test_begin("array_lsearch"); + for (i = 0; i < N_ELEMENTS(input); i++) { + output = array_lsearch(&intarr, &input[i], test_int_compare); + test_assert(output != NULL); + j = array_ptr_to_idx(&intarr, output); + test_assert_idx(j == N_ELEMENTS(input) - 1 - i, i); + } + output = array_lsearch(&intarr, &tmpi, test_int_compare); + test_assert(output == NULL); + test_end(); +} +static int test_compare_ushort(const unsigned short *c1, const unsigned short *c2) +{ + return *c1 > *c2 ? 1 + : *c1 < *c2 ? -1 + : 0; +} +static int test_compare_ushort_fuzz(const unsigned short *c1, const unsigned short *c2, const int *pfuzz) +{ + int d = (int)*c1 - (int)*c2; + if (d <= *pfuzz && -d <= *pfuzz) + return 0; + return d; +} +static void test_array_cmp(void) +{ + static const unsigned short deltas[] = { + 0x8000, 0xc000, 0xfe00, 0xff00, 0xff80, 0xffc0, 0xfffe, 0xffff, + 0, 1, 2, 64, 128, 256, 512, 16384, 32768 + }; + +#define NELEMS 5u + ARRAY(unsigned short) arr1, arr2; + unsigned short elems[NELEMS+1]; + unsigned int i; + int fuzz; + + test_begin("array compare (ushort)"); + t_array_init(&arr1, NELEMS); + t_array_init(&arr2, NELEMS); + for (i = 0; i < NELEMS; i++) { + elems[i] = i_rand_ushort(); + array_push_back(&arr2, &elems[i]); + } + array_append(&arr1, elems, NELEMS); + test_assert(array_cmp(&arr1, &arr2) == TRUE); + test_assert(array_equal_fn(&arr1, &arr2, test_compare_ushort) == TRUE); + fuzz = 0; + test_assert(array_equal_fn_ctx(&arr1, &arr2, test_compare_ushort_fuzz, &fuzz) == TRUE); + + for (i = 0; i < 256; i++) { + unsigned int j = i_rand_limit(NELEMS); + const unsigned short *ptmp = array_idx(&arr2, j); + unsigned short tmp = *ptmp; + unsigned short repl = ((unsigned int)tmp + + deltas[i_rand_limit(N_ELEMENTS(deltas))]) & 0xffff; + + array_idx_set(&arr2, j, &repl); + test_assert_idx(array_cmp(&arr1, &arr2) == (tmp == repl), i); + test_assert_idx(array_equal_fn(&arr1, &arr2, test_compare_ushort) == (tmp == repl), i); + fuzz = (int)tmp - (int)repl; + if (fuzz < 0) + fuzz = -fuzz; + test_assert_idx(array_equal_fn_ctx(&arr1, &arr2, test_compare_ushort_fuzz, &fuzz) == TRUE, i); + if (fuzz > 0) { + fuzz--; + test_assert_idx(array_equal_fn_ctx(&arr1, &arr2, test_compare_ushort_fuzz, &fuzz) == FALSE, i); + } + array_idx_set(&arr2, j, &tmp); + test_assert_idx(array_cmp(&arr1, &arr2) == TRUE, i); + test_assert_idx(array_equal_fn(&arr1, &arr2, test_compare_ushort) == TRUE, i); + fuzz = 0; + test_assert_idx(array_equal_fn_ctx(&arr1, &arr2, test_compare_ushort_fuzz, &fuzz) == TRUE, i); + } + elems[NELEMS] = 0; + array_push_back(&arr2, &elems[NELEMS]); + test_assert(array_cmp(&arr1, &arr2) == FALSE); + test_assert(array_equal_fn(&arr1, &arr2, test_compare_ushort) == FALSE); + test_assert_idx(array_equal_fn_ctx(&arr1, &arr2, test_compare_ushort_fuzz, &fuzz) == FALSE, i); + + test_end(); +} + +static void test_array_cmp_str(void) +{ +#define NELEMS 5u + ARRAY(const char *) arr1, arr2; + const char *elemstrs[NELEMS+1]; + unsigned int i; + + test_begin("array compare (char*)"); + t_array_init(&arr1, NELEMS); + t_array_init(&arr2, NELEMS); + for (i = 0; i < NELEMS; i++) { + elemstrs[i] = t_strdup_printf("%x", i_rand()); /* never 0-length */ + array_push_back(&arr2, &elemstrs[i]); + } + array_append(&arr1, elemstrs, NELEMS); + test_assert(array_cmp(&arr1, &arr2) == TRUE); /* pointers shared, so identical */ + test_assert(array_equal_fn(&arr1, &arr2, i_strcmp_p) == TRUE); /* therefore value same */ + for (i = 0; i < 2560; i++) { + unsigned int j = i_rand_limit(NELEMS); + const char *const *ostr_p = array_idx(&arr2, j); + const char *ostr = *ostr_p; + unsigned int olen = strlen(ostr); + unsigned int rc = i_rand_limit(olen + 1); + char ochar = ostr[rc]; + char buf[12]; + const char *bufp = buf; + memcpy(buf, ostr, olen+1); + buf[rc] = (int32_t)i_rand_limit(CHAR_MAX + 1 - CHAR_MIN) + CHAR_MIN; + if(rc == olen) + buf[rc+1] = '\0'; + array_idx_set(&arr2, j, &bufp); + test_assert(array_cmp(&arr1, &arr2) == FALSE); /* pointers now differ */ + test_assert_idx(array_equal_fn(&arr1, &arr2, i_strcmp_p) + == (strcmp(ostr, buf) == 0), i); /* sometimes still the same */ + test_assert_idx(array_equal_fn(&arr1, &arr2, i_strcmp_p) + == (ochar == buf[rc]), i); /* ditto */ + array_idx_set(&arr2, j, &ostr); + test_assert(array_cmp(&arr1, &arr2) == TRUE); /* pointers now same again */ + test_assert_idx(array_equal_fn(&arr1, &arr2, i_strcmp_p) == TRUE, i); /* duh! */ + } + /* length differences being detected are tested in other tests */ + test_end(); +} + +static void +test_array_free_case(bool keep) +{ + pool_t pool = pool_allocfree_create("array test"); + ARRAY(int) r; + int *p; + + test_begin(keep ? "array_free" : "array_free_without_data"); + + p_array_init(&r, pool, 100); + array_append_zero(&r); + if (keep) { + p = array_free_without_data(&r); + test_assert(pool_allocfree_get_total_used_size(pool)>=400); + p_free(pool, p); + } else { + array_free(&r); + test_assert(pool_allocfree_get_total_used_size(pool)==0); + } + pool_unref(&pool); + test_end(); +} +static void +test_array_free(void) +{ + test_array_free_case(FALSE); + test_array_free_case(TRUE); +} + +void test_array(void) +{ + test_array_elem(); + test_array_count(); + test_array_foreach(); + test_array_foreach_reverse(); + test_array_foreach_elem_string(); + test_array_reverse(); + test_array_cmp(); + test_array_cmp_str(); + test_array_free(); +} + +enum fatal_test_state fatal_array(unsigned int stage) +{ + double tmpd[2] = { 42., -42. }; + short tmps[8] = {1,2,3,4,5,6,7,8}; + static const void *useless_ptr; /* persuade gcc to not optimise the tests */ + + switch(stage) { + case 0: { + ARRAY(double) ad; + test_begin("fatal_array"); + t_array_init(&ad, 3); + /* allocation big enough, but memory not initialised */ + test_expect_fatal_string("(array_idx_i): assertion failed: (idx < array->buffer->used / array->element_size)"); + useless_ptr = array_front(&ad); + return FATAL_TEST_FAILURE; + } + + case 1: { + ARRAY(double) ad; + t_array_init(&ad, 2); + array_append(&ad, tmpd, 2); + /* actual out of range address requested */ + test_expect_fatal_string("(array_idx_i): assertion failed: (idx < array->buffer->used / array->element_size)"); + useless_ptr = array_idx(&ad, 2); + return FATAL_TEST_FAILURE; + } + + case 2: { + ARRAY(double) ad; + ARRAY(short) as; + t_array_init(&ad, 2); + t_array_init(&as, 8); + array_append(&as, tmps, 2); + /* can't copy different array sizes */ + test_expect_fatal_string("(array_copy): assertion failed: (dest->element_size == src->element_size)"); + array_copy(&ad.arr, 1, &as.arr, 0, 4); + return FATAL_TEST_FAILURE; + } + case 3: { + ARRAY(uint8_t) arr; + /* Allocate value dynamically, so compiler won't know the + allocated memory size and output a warning that it's too + small for array_append(). */ + uint8_t *value = t_malloc0(1); + + t_array_init(&arr, 2); + array_push_back(&arr, value); + test_expect_fatal_string("Buffer write out of range"); + /* this is supposed to assert-crash before it even attempts to + access value */ + array_append(&arr, value, UINT_MAX); + return FATAL_TEST_FAILURE; + } + case 4: { + ARRAY(uint32_t) arr; + /* Allocate value dynamically (see above for reasoning). */ + uint32_t *value = t_malloc0(1); + + t_array_init(&arr, 2); + array_push_back(&arr, value); + test_expect_fatal_string("Buffer write out of range"); + /* this is supposed to assert-crash before it even attempts to + access value */ + array_append(&arr, value, UINT_MAX); + return FATAL_TEST_FAILURE; + } + } + test_end(); + /* Forces the compiler to check the value of useless_ptr, so that it + must call array_idx (which is marked as pure, and gcc was desperate + to optimise out. Of course, gcc is unaware stage is never UINT_MAX.*/ + return (useless_ptr != NULL && stage == UINT_MAX) + ? FATAL_TEST_FAILURE : FATAL_TEST_FINISHED; +} diff --git a/src/lib/test-backtrace.c b/src/lib/test-backtrace.c new file mode 100644 index 0000000..fdebe0f --- /dev/null +++ b/src/lib/test-backtrace.c @@ -0,0 +1,57 @@ +#include "test-lib.h" +#include "str.h" +#include "backtrace-string.h" + +static void test_backtrace_append(void) +{ + test_begin("backtrace_append"); + string_t *bt = t_str_new(128); +#if (defined(HAVE_LIBUNWIND)) + test_assert(backtrace_append(bt) == 0); + /* Check that there's a usable function in the backtrace. + Note that this function may be inlined, so don't check for + test_backtrace_get() */ + test_assert(strstr(str_c(bt), "test_backtrace") != NULL); + /* make sure the backtrace_append is not */ + test_assert(strstr(str_c(bt), " backtrace_append") == NULL); +#elif (defined(HAVE_BACKTRACE_SYMBOLS) && defined(HAVE_EXECINFO_H)) || \ + (defined(HAVE_WALKCONTEXT) && defined(HAVE_UCONTEXT_H)) + test_assert(backtrace_append(bt) == 0); + /* it should have some kind of main in it */ + test_assert(strstr(str_c(bt), "main") != NULL); +#else + /* should not work in this context */ + test_assert(backtrace_append(bt) == -1); +#endif + test_end(); +} + +static void test_backtrace_get(void) +{ + test_begin("backtrace_get"); + const char *bt = NULL; +#if (defined(HAVE_LIBUNWIND)) + test_assert(backtrace_get(&bt) == 0); + /* Check that there's a usable function in the backtrace. + Note that this function may be inlined, so don't check for + test_backtrace_get() */ + test_assert(strstr(bt, "test_backtrace") != NULL); + /* make sure the backtrace_get is not */ + test_assert(strstr(bt, " backtrace_get") == NULL); +#elif (defined(HAVE_BACKTRACE_SYMBOLS) && defined(HAVE_EXECINFO_H)) || \ + (defined(HAVE_WALKCONTEXT) && defined(HAVE_UCONTEXT_H)) + test_assert(backtrace_get(&bt) == 0); + /* it should have some kind of main in it */ + test_assert(strstr(bt, "main") != NULL); +#else + /* should not work in this context */ + test_assert(backtrace_get(&bt) == -1); +#endif + test_end(); +} + +void test_backtrace(void) +{ + test_backtrace_append(); + test_backtrace_get(); +} diff --git a/src/lib/test-base32.c b/src/lib/test-base32.c new file mode 100644 index 0000000..072dae8 --- /dev/null +++ b/src/lib/test-base32.c @@ -0,0 +1,189 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "base32.h" + + +static void test_base32_encode(void) +{ + static const char *input[] = { + "toedeledokie!!", + "bye bye world", + "hoeveel onzin kun je testen?????", + "c'est pas vrai! ", + "dit is het einde van deze test" + }; + static const char *output[] = { + "ORXWKZDFNRSWI33LNFSSCII=", + "MJ4WKIDCPFSSA53POJWGI===", + "NBXWK5TFMVWCA33OPJUW4IDLOVXCA2TFEB2GK43UMVXD6PZ7H47Q====", + "MMTWK43UEBYGC4ZAOZZGC2JBEA======", + "MRUXIIDJOMQGQZLUEBSWS3TEMUQHMYLOEBSGK6TFEB2GK43U" + }; + string_t *str; + unsigned int i; + + test_begin("base32_encode() with padding"); + str = t_str_new(256); + for (i = 0; i < N_ELEMENTS(input); i++) { + str_truncate(str, 0); + base32_encode(TRUE, input[i], strlen(input[i]), str); + test_assert(strcmp(output[i], str_c(str)) == 0); + } + test_end(); + + test_begin("base32_encode() no padding"); + str = t_str_new(256); + for (i = 0; i < N_ELEMENTS(input); i++) { + const char *p = strchr(output[i], '='); + size_t len; + + if (p == NULL) + len = strlen(output[i]); + else + len = (size_t)(p - output[i]); + str_truncate(str, 0); + base32_encode(FALSE, input[i], strlen(input[i]), str); + test_assert(strncmp(output[i], str_c(str), len) == 0); + } + test_end(); +} + +static void test_base32hex_encode(void) +{ + static const char *input[] = { + "toedeledokie!!", + "bye bye world", + "hoeveel onzin kun je testen?????", + "c'est pas vrai! ", + "dit is het einde van deze test" + }; + static const char *output[] = { + "EHNMAP35DHIM8RRBD5II288=", + "C9SMA832F5II0TRFE9M68===", + "D1NMATJ5CLM20RREF9KMS83BELN20QJ541Q6ASRKCLN3UFPV7SVG====", + "CCJMASRK41O62SP0EPP62Q9140======", + "CHKN8839ECG6GPBK41IMIRJ4CKG7COBE41I6AUJ541Q6ASRK" + }; + string_t *str; + unsigned int i; + + test_begin("base32hex_encode() with padding"); + str = t_str_new(256); + for (i = 0; i < N_ELEMENTS(input); i++) { + str_truncate(str, 0); + base32hex_encode(TRUE, input[i], strlen(input[i]), str); + test_assert(strcmp(output[i], str_c(str)) == 0); + } + test_end(); + + test_begin("base32hex_encode() no padding"); + str = t_str_new(256); + for (i = 0; i < N_ELEMENTS(input); i++) { + const char *p = strchr(output[i], '='); + size_t len; + + if (p == NULL) + len = strlen(output[i]); + else + len = (size_t)(p - output[i]); + str_truncate(str, 0); + base32hex_encode(FALSE, input[i], strlen(input[i]), str); + test_assert(strncmp(output[i], str_c(str), len) == 0); + } + test_end(); + +} + +struct test_base32_decode_output { + const char *text; + int ret; + unsigned int src_pos; +}; + +static void test_base32_decode(void) +{ + static const char *input[] = { + "ORXWKZDFNRSWI33LNFSSCII=", + "MJ4WKIDCPFSSA53POJWGI===", + "NBXWK5TFMVWCA33OPJUW4IDLOVXCA2TFEB2GK43UMVXD6PZ7H47Q====", + "MMTWK43UEBYGC4ZAOZZGC2JBEA======", + "MRUXIIDJOMQGQZLUEBSWS3TEMUQHMYLOEBSGK6TFEB2GK43U" + }; + static const struct test_base32_decode_output output[] = { + { "toedeledokie!!", 0, 24 }, + { "bye bye world", 0, 24 }, + { "hoeveel onzin kun je testen?????", 0, 56 }, + { "c'est pas vrai! ", 0, 32 }, + { "dit is het einde van deze test", 1, 48 }, + }; + string_t *str; + unsigned int i; + size_t src_pos; + int ret; + + test_begin("base32_decode()"); + str = t_str_new(256); + for (i = 0; i < N_ELEMENTS(input); i++) { + str_truncate(str, 0); + + src_pos = 0; + ret = base32_decode(input[i], strlen(input[i]), &src_pos, str); + + test_assert(output[i].ret == ret && + strcmp(output[i].text, str_c(str)) == 0 && + (src_pos == output[i].src_pos || + (output[i].src_pos == UINT_MAX && + src_pos == strlen(input[i])))); + } + test_end(); +} + +static void test_base32_random(void) +{ + string_t *str, *dest; + unsigned char buf[10]; + unsigned int i, j, max; + + str = t_str_new(256); + dest = t_str_new(256); + + test_begin("padded base32 encode/decode with random input"); + for (i = 0; i < 1000; i++) { + max = i_rand_limit(sizeof(buf)); + for (j = 0; j < max; j++) + buf[j] = i_rand_uchar(); + + str_truncate(str, 0); + str_truncate(dest, 0); + base32_encode(TRUE, buf, max, str); + test_assert(base32_decode(str_data(str), str_len(str), NULL, dest) >= 0); + test_assert(str_len(dest) == max && + memcmp(buf, str_data(dest), max) == 0); + } + test_end(); + + test_begin("padded base32hex encode/decode with random input"); + for (i = 0; i < 1000; i++) { + max = i_rand_limit(sizeof(buf)); + for (j = 0; j < max; j++) + buf[j] = i_rand_uchar(); + + str_truncate(str, 0); + str_truncate(dest, 0); + base32hex_encode(TRUE, buf, max, str); + test_assert(base32hex_decode(str_data(str), str_len(str), NULL, dest) >= 0); + test_assert(str_len(dest) == max && + memcmp(buf, str_data(dest), max) == 0); + } + test_end(); +} + +void test_base32(void) +{ + test_base32_encode(); + test_base32hex_encode(); + test_base32_decode(); + test_base32_random(); +} diff --git a/src/lib/test-base64.c b/src/lib/test-base64.c new file mode 100644 index 0000000..d024890 --- /dev/null +++ b/src/lib/test-base64.c @@ -0,0 +1,1317 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "base64.h" + +static void test_base64_encode(void) +{ + const struct { + const char *input; + const char *output; + } tests[] = { + { "hello world", "aGVsbG8gd29ybGQ=" }, + { "foo barits", "Zm9vIGJhcml0cw==" }, + { "just niin", "anVzdCBuaWlu" }, + { "\xe7\x8c\xbf\xe3\x82\x82\xe6\x9c\xa8\xe3\x81\x8b" + "\xe3\x82\x89\xe8\x90\xbd\xe3\x81\xa1\xe3\x82\x8b", + "54y/44KC5pyo44GL44KJ6JC944Gh44KL" }, + { "\xe8\xa7\x92\xe3\x82\x92\xe7\x9f\xaf\xe3\x82\x81\xe3\x81" + "\xa6\xe7\x89\x9b\xe3\x82\x92\xe6\xae\xba\xe3\x81\x99", + "6KeS44KS55+v44KB44Gm54mb44KS5q6644GZ" }, + }; + string_t *str; + unsigned int i; + + test_begin("base64_encode()"); + str = t_str_new(256); + for (i = 0; i < N_ELEMENTS(tests); i++) { + str_truncate(str, 0); + base64_encode(tests[i].input, strlen(tests[i].input), str); + test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i); + test_assert_idx( + str_len(str) == MAX_BASE64_ENCODED_SIZE( + strlen(tests[i].input)), i); + } + test_end(); +} + +struct test_base64_decode { + const char *input; + const char *output; + int ret; +}; + +static void test_base64_decode(void) +{ + static const struct test_base64_decode tests[] = { + { "", "", 0 }, + { "\taGVsbG8gd29ybGQ=", + "hello world", 0 }, + { "\nZm9v\n \tIGJh \t\ncml0cw==", + "foo barits", 0 }, + { " anVzdCBuaWlu \n", + "just niin", 0 }, + { "aGVsb", + "hel", -1 }, + { "aGVsb!!!!!", + "hel", -1 }, + { "aGVs!!!!!", + "hel", -1 }, + { "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt" + "C+INC60YPRgCDQtNC+0Y/MgdGCLg==", + "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc" + "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0" + "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc" + "\x81\xd1\x82\x2e", 0 }, + }; + string_t *str; + buffer_t buf; + unsigned int i; + int ret; + + test_begin("base64_decode()"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + /* Some of the base64_decode() callers use fixed size buffers. + Use a fixed size buffer here as well to test that + base64_decode() can't allocate any extra space even + temporarily. */ + size_t max_decoded_size = + MAX_BASE64_DECODED_SIZE(strlen(tests[i].input)); + + buffer_create_from_data(&buf, + (max_decoded_size == 0 ? "" : + t_malloc0(max_decoded_size)), + max_decoded_size); + str = &buf; + ret = base64_decode(tests[i].input, strlen(tests[i].input), + NULL, str); + + test_assert_idx(tests[i].ret == ret, i); + test_assert_idx(strlen(tests[i].output) == str_len(str) && + memcmp(tests[i].output, str_data(str), + str_len(str)) == 0, i); + if (ret >= 0) { + test_assert_idx( + str_len(str) <= MAX_BASE64_DECODED_SIZE( + strlen(tests[i].input)), i); + } + } + test_end(); +} + +static void test_base64_random(void) +{ + string_t *str, *dest; + unsigned char buf[10]; + unsigned int i, j, max; + + str = t_str_new(256); + dest = t_str_new(256); + + test_begin("base64 encode/decode with random input"); + for (i = 0; i < 1000; i++) { + max = i_rand_limit(sizeof(buf)); + for (j = 0; j < max; j++) + buf[j] = i_rand_uchar(); + + str_truncate(str, 0); + str_truncate(dest, 0); + base64_encode(buf, max, str); + test_assert_idx(base64_decode(str_data(str), str_len(str), + NULL, dest) >= 0, i); + test_assert_idx(str_len(dest) == max && + memcmp(buf, str_data(dest), max) == 0, i); + } + test_end(); +} + +static void test_base64url_encode(void) +{ + const struct { + const char *input; + const char *output; + } tests[] = { + { "", "" }, + { "hello world", "aGVsbG8gd29ybGQ=" }, + { "foo barits", "Zm9vIGJhcml0cw==" }, + { "just niin", "anVzdCBuaWlu" }, + { "\xe7\x8c\xbf\xe3\x82\x82\xe6\x9c\xa8\xe3\x81\x8b" + "\xe3\x82\x89\xe8\x90\xbd\xe3\x81\xa1\xe3\x82\x8b", + "54y_44KC5pyo44GL44KJ6JC944Gh44KL" }, + { "\xe8\xa7\x92\xe3\x82\x92\xe7\x9f\xaf\xe3\x82\x81\xe3\x81" + "\xa6\xe7\x89\x9b\xe3\x82\x92\xe6\xae\xba\xe3\x81\x99", + "6KeS44KS55-v44KB44Gm54mb44KS5q6644GZ" }, + }; + string_t *str; + unsigned int i; + + test_begin("base64url_encode()"); + str = t_str_new(256); + for (i = 0; i < N_ELEMENTS(tests); i++) { + str_truncate(str, 0); + base64url_encode(0, 0, tests[i].input, strlen(tests[i].input), + str); + test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i); + test_assert_idx( + str_len(str) == MAX_BASE64_ENCODED_SIZE( + strlen(tests[i].input)), i); + } + test_end(); +} + +struct test_base64url_decode { + const char *input; + const char *output; + int ret; +}; + +static void test_base64url_decode(void) +{ + static const struct test_base64url_decode tests[] = { + { "", "", 0 }, + { "\taGVsbG8gd29ybGQ=", + "hello world", 0 }, + { "\nZm9v\n \tIGJh \t\ncml0cw==", + "foo barits", 0 }, + { " anVzdCBuaWlu \n", + "just niin", 0 }, + { "aGVsb", + "hel", -1 }, + { "aGVsb!!!!!", + "hel", -1 }, + { "aGVs!!!!!", + "hel", -1 }, + { "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt" + "C-INC60YPRgCDQtNC-0Y_MgdGCLg==", + "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc" + "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0" + "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc" + "\x81\xd1\x82\x2e", 0 }, + }; + string_t *str; + buffer_t buf; + unsigned int i; + int ret; + + test_begin("base64url_decode()"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + /* Some of the base64_decode() callers use fixed size buffers. + Use a fixed size buffer here as well to test that + base64_decode() can't allocate any extra space even + temporarily. */ + size_t max_decoded_size = + MAX_BASE64_DECODED_SIZE(strlen(tests[i].input)); + + buffer_create_from_data(&buf, + (max_decoded_size == 0 ? "" : + t_malloc0(max_decoded_size)), + max_decoded_size); + str = &buf; + ret = base64url_decode(0, tests[i].input, + strlen(tests[i].input), str); + + test_assert_idx(tests[i].ret == ret, i); + test_assert_idx(strlen(tests[i].output) == str_len(str) && + memcmp(tests[i].output, str_data(str), + str_len(str)) == 0, i); + if (ret >= 0) { + test_assert_idx( + str_len(str) <= MAX_BASE64_DECODED_SIZE( + strlen(tests[i].input)), i); + } + } + test_end(); +} + +static void test_base64url_random(void) +{ + string_t *str, *dest; + unsigned char buf[10]; + unsigned int i, j, max; + + str = t_str_new(256); + dest = t_str_new(256); + + test_begin("base64url encode/decode with random input"); + for (i = 0; i < 1000; i++) { + max = i_rand_limit(sizeof(buf)); + for (j = 0; j < max; j++) + buf[j] = i_rand_uchar(); + + str_truncate(str, 0); + str_truncate(dest, 0); + base64url_encode(0, 0, buf, max, str); + test_assert_idx(base64url_decode(0, str_data(str), str_len(str), + dest) >= 0, i); + test_assert_idx(str_len(dest) == max && + memcmp(buf, str_data(dest), max) == 0, i); + } + test_end(); +} + +struct test_base64_encode_lowlevel { + const struct base64_scheme *scheme; + enum base64_encode_flags flags; + size_t max_line_len; + + const char *input; + const char *output; +}; + +static const struct test_base64_encode_lowlevel +tests_base64_encode_lowlevel[] = { + { + .scheme = &base64_scheme, + .input = "", + .output = "", + }, + { + .scheme = &base64_scheme, + .max_line_len = 2, + .input = "", + .output = "", + }, + { + .scheme = &base64_scheme, + .flags = BASE64_ENCODE_FLAG_CRLF, + .max_line_len = 2, + .input = "", + .output = "", + }, + { + .scheme = &base64_scheme, + .flags = BASE64_ENCODE_FLAG_NO_PADDING, + .input = "", + .output = "", + }, + { + .scheme = &base64_scheme, + .input = "hello world", + .output = "aGVsbG8gd29ybGQ=", + }, + { + .scheme = &base64url_scheme, + .input = "hello world", + .output = "aGVsbG8gd29ybGQ=", + }, + { + .scheme = &base64_scheme, + .flags = BASE64_ENCODE_FLAG_NO_PADDING, + .input = "hello world", + .output = "aGVsbG8gd29ybGQ", + }, + { + .scheme = &base64_scheme, + .input = "foo barits", + .output = "Zm9vIGJhcml0cw==", + }, + { + .scheme = &base64url_scheme, + .input = "foo barits", + .output = "Zm9vIGJhcml0cw==", + }, + { + .scheme = &base64_scheme, + .flags = BASE64_ENCODE_FLAG_NO_PADDING, + .input = "foo barits", + .output = "Zm9vIGJhcml0cw", + }, + { + .scheme = &base64_scheme, + .input = "just niin", + .output = "anVzdCBuaWlu", + }, + { + .scheme = &base64url_scheme, + .input = "just niin", + .output = "anVzdCBuaWlu", + }, + { + .scheme = &base64_scheme, + .flags = BASE64_ENCODE_FLAG_NO_PADDING, + .input = "just niin", + .output = "anVzdCBuaWlu", + }, + { + .scheme = &base64_scheme, + .input = + "\xe7\x8c\xbf\xe3\x82\x82\xe6\x9c\xa8\xe3\x81\x8b" + "\xe3\x82\x89\xe8\x90\xbd\xe3\x81\xa1\xe3\x82\x8b", + .output = "54y/44KC5pyo44GL44KJ6JC944Gh44KL", + }, + { + .scheme = &base64url_scheme, + .input = + "\xe7\x8c\xbf\xe3\x82\x82\xe6\x9c\xa8\xe3\x81\x8b" + "\xe3\x82\x89\xe8\x90\xbd\xe3\x81\xa1\xe3\x82\x8b", + .output = "54y_44KC5pyo44GL44KJ6JC944Gh44KL", + }, + { + .scheme = &base64_scheme, + .input = + "\xe8\xa7\x92\xe3\x82\x92\xe7\x9f\xaf\xe3\x82\x81\xe3" + "\x81\xa6\xe7\x89\x9b\xe3\x82\x92\xe6\xae\xba\xe3\x81" + "\x99", + .output = "6KeS44KS55+v44KB44Gm54mb44KS5q6644GZ", + }, + { + .scheme = &base64url_scheme, + .input = + "\xe8\xa7\x92\xe3\x82\x92\xe7\x9f\xaf\xe3\x82\x81\xe3" + "\x81\xa6\xe7\x89\x9b\xe3\x82\x92\xe6\xae\xba\xe3\x81" + "\x99", + .output = "6KeS44KS55-v44KB44Gm54mb44KS5q6644GZ", + }, + { + .scheme = &base64_scheme, + .flags = BASE64_ENCODE_FLAG_CRLF, + .input = "just niin", + .output = "anVzdCBuaWlu", + }, + { + .scheme = &base64_scheme, + .flags = BASE64_ENCODE_FLAG_CRLF, + .max_line_len = 80, + .input = "just niin", + .output = "anVzdCBuaWlu", + }, + { + .scheme = &base64_scheme, + .flags = BASE64_ENCODE_FLAG_CRLF, + .max_line_len = 48, + .input = + "Passer, deliciae meae puellae,\n" + "quicum ludere, quem in sinu tenere,\n" + "cui primum digitum dare appetenti\n" + "et acris solet incitare morsus,\n" + "cum desiderio meo nitenti\n" + "carum nescio quid lubet iocari,\n" + "credo ut, cum gravis acquiescet ardor,\n" + "sit solaciolum sui doloris,\n" + "tecum ludere sicut ipsa possem\n" + "et tristis animi levare curas!\n", + .output = + "UGFzc2VyLCBkZWxpY2lhZSBtZWFlIHB1ZWxsYWUsCnF1aWN1\r\n" + "bSBsdWRlcmUsIHF1ZW0gaW4gc2ludSB0ZW5lcmUsCmN1aSBw\r\n" + "cmltdW0gZGlnaXR1bSBkYXJlIGFwcGV0ZW50aQpldCBhY3Jp\r\n" + "cyBzb2xldCBpbmNpdGFyZSBtb3JzdXMsCmN1bSBkZXNpZGVy\r\n" + "aW8gbWVvIG5pdGVudGkKY2FydW0gbmVzY2lvIHF1aWQgbHVi\r\n" + "ZXQgaW9jYXJpLApjcmVkbyB1dCwgY3VtIGdyYXZpcyBhY3F1\r\n" + "aWVzY2V0IGFyZG9yLApzaXQgc29sYWNpb2x1bSBzdWkgZG9s\r\n" + "b3JpcywKdGVjdW0gbHVkZXJlIHNpY3V0IGlwc2EgcG9zc2Vt\r\n" + "CmV0IHRyaXN0aXMgYW5pbWkgbGV2YXJlIGN1cmFzIQo=", + }, + { + .scheme = &base64_scheme, + .max_line_len = 48, + .input = + "Lugete, o Veneres Cupidinesque,\n" + "et quantum est hominum venustiorum:\n" + "passer mortuus est meae puellae, \n" + "passer, deliciae meae puellae\n" + "quem plus amat illa oculis suis amabat.\n" + "Nam mellitus erat suamque norat\n" + "ipsam tam bene quam puella matrem,\n" + "nec sese a gremio illius movebat,\n" + "sed circumsiliens modo huc modo illuc\n" + "ad solam dominam usque pipiabat;\n" + "qui nunc it per iter tenebricosum\n" + "illuc, unde negant redire quemquam.\n" + "At vobis male sint, malae tenebrae\n" + "Orci, quae omnia bella devoratis:\n" + "tam bellum mihi passerem abstulistis.\n" + "O factum male! O miselle passer!\n" + "Tua nunc opera meae puellae\n" + "flendo turgiduli rubent ocelli.\n", + .output = + "THVnZXRlLCBvIFZlbmVyZXMgQ3VwaWRpbmVzcXVlLApldCBx\n" + "dWFudHVtIGVzdCBob21pbnVtIHZlbnVzdGlvcnVtOgpwYXNz\n" + "ZXIgbW9ydHV1cyBlc3QgbWVhZSBwdWVsbGFlLCAKcGFzc2Vy\n" + "LCBkZWxpY2lhZSBtZWFlIHB1ZWxsYWUKcXVlbSBwbHVzIGFt\n" + "YXQgaWxsYSBvY3VsaXMgc3VpcyBhbWFiYXQuCk5hbSBtZWxs\n" + "aXR1cyBlcmF0IHN1YW1xdWUgbm9yYXQKaXBzYW0gdGFtIGJl\n" + "bmUgcXVhbSBwdWVsbGEgbWF0cmVtLApuZWMgc2VzZSBhIGdy\n" + "ZW1pbyBpbGxpdXMgbW92ZWJhdCwKc2VkIGNpcmN1bXNpbGll\n" + "bnMgbW9kbyBodWMgbW9kbyBpbGx1YwphZCBzb2xhbSBkb21p\n" + "bmFtIHVzcXVlIHBpcGlhYmF0OwpxdWkgbnVuYyBpdCBwZXIg\n" + "aXRlciB0ZW5lYnJpY29zdW0KaWxsdWMsIHVuZGUgbmVnYW50\n" + "IHJlZGlyZSBxdWVtcXVhbS4KQXQgdm9iaXMgbWFsZSBzaW50\n" + "LCBtYWxhZSB0ZW5lYnJhZQpPcmNpLCBxdWFlIG9tbmlhIGJl\n" + "bGxhIGRldm9yYXRpczoKdGFtIGJlbGx1bSBtaWhpIHBhc3Nl\n" + "cmVtIGFic3R1bGlzdGlzLgpPIGZhY3R1bSBtYWxlISBPIG1p\n" + "c2VsbGUgcGFzc2VyIQpUdWEgbnVuYyBvcGVyYSBtZWFlIHB1\n" + "ZWxsYWUKZmxlbmRvIHR1cmdpZHVsaSBydWJlbnQgb2NlbGxp\n" + "Lgo=", + }, +}; + +static void test_base64_encode_lowlevel(void) +{ + string_t *str; + unsigned int i; + + test_begin("base64 encode low-level"); + str = t_str_new(256); + for (i = 0; i < N_ELEMENTS(tests_base64_encode_lowlevel); i++) { + const struct test_base64_encode_lowlevel *test = + &tests_base64_encode_lowlevel[i]; + struct base64_encoder enc; + uoff_t out_size; + + str_truncate(str, 0); + + base64_encode_init(&enc, test->scheme, test->flags, + test->max_line_len); + out_size = base64_get_full_encoded_size( + &enc, strlen(test->input)); + test_assert_idx(base64_encode_more(&enc, test->input, + strlen(test->input), + NULL, str), i); + test_assert_idx(base64_encode_finish(&enc, str), i); + + test_assert_idx(strcmp(test->output, str_c(str)) == 0, i); + test_assert_idx(test->flags != 0 || test->max_line_len != 0 || + str_len(str) == MAX_BASE64_ENCODED_SIZE( + strlen(test->input)), i); + test_assert_idx(str_len(str) == out_size, i); + } + test_end(); +} + +struct test_base64_decode_lowlevel { + const struct base64_scheme *scheme; + enum base64_decode_flags flags; + + const char *input; + const char *output; + int ret; + unsigned int src_pos; +}; + +static const struct test_base64_decode_lowlevel +tests_base64_decode_lowlevel[] = { + { + .scheme = &base64_scheme, + .input = "", + .output = "", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .input = " ", + .output = "", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .input = "", + .output = "", + .ret = 0, + .src_pos = UINT_MAX, + .flags = BASE64_DECODE_FLAG_EXPECT_BOUNDARY, + }, + { + .scheme = &base64_scheme, + .input = "", + .output = "", + .ret = 0, + .src_pos = UINT_MAX, + .flags = BASE64_DECODE_FLAG_NO_WHITESPACE, + }, + { + .scheme = &base64_scheme, + .input = " ", + .output = "", + .ret = -1, + .src_pos = 0, + .flags = BASE64_DECODE_FLAG_NO_WHITESPACE, + }, + { + .scheme = &base64_scheme, + .input = "", + .output = "", + .ret = 0, + .src_pos = UINT_MAX, + .flags = BASE64_DECODE_FLAG_NO_PADDING, + }, + { + .scheme = &base64_scheme, + .input = "", + .output = "", + .ret = 0, + .src_pos = UINT_MAX, + .flags = BASE64_DECODE_FLAG_IGNORE_PADDING, + }, + { + .scheme = &base64_scheme, + .input = "\taGVsbG8gd29ybGQ=", + .output = "hello world", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64url_scheme, + .input = "\taGVsbG8gd29ybGQ=", + .output = "hello world", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .input = "aGVsbG8gd29ybGQ=\t", + .output = "hello world", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .input = "\taGVsbG8gd29ybGQ=\t", + .output = "hello world", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .input = "aGVsbG8gd29ybGQ=:frop", + .output = "hello world", + .ret = 0, + .src_pos = 16, + .flags = BASE64_DECODE_FLAG_EXPECT_BOUNDARY, + }, + { + .scheme = &base64_scheme, + .input = "\taGVsbG8gd29ybGQ=\t:frop", + .output = "hello world", + .ret = 0, + .src_pos = 18, + .flags = BASE64_DECODE_FLAG_EXPECT_BOUNDARY, + }, + { + .scheme = &base64_scheme, + .input = "aGVsbG8gd29ybGQ=\t", + .output = "hello world", + .ret = -1, + .src_pos = 16, + .flags = BASE64_DECODE_FLAG_NO_WHITESPACE, + }, + { + .scheme = &base64_scheme, + .input = "\taGVsbG8gd29ybGQ=\t", + .output = "", + .ret = -1, + .src_pos = 0, + .flags = BASE64_DECODE_FLAG_NO_WHITESPACE, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_NO_PADDING, + .input = "\taGVsbG8gd29ybGQ=", + .output = "hello world", + .ret = -1, + .src_pos = 16, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_NO_PADDING, + .input = "\taGVsbG8gd29ybGQ", + .output = "hello world", + .ret = 0, + .src_pos = 16, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_IGNORE_PADDING, + .input = "\taGVsbG8gd29ybGQ=", + .output = "hello world", + .ret = 0, + .src_pos = 17, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_IGNORE_PADDING, + .input = "\taGVsbG8gd29ybGQ", + .output = "hello world", + .ret = 0, + .src_pos = 16, + }, + { + .scheme = &base64_scheme, + .input = "\nZm9v\n \tIGJh \t\ncml0cw==", + .output = "foo barits", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64url_scheme, + .input = "\nZm9v\n \tIGJh \t\ncml0cw==", + .output = "foo barits", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .input = "\nZm9v\n \tIGJh \t\ncml0cw==\n ", + .output = "foo barits", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .input = "\nZm9v\n \tIGJh \t\ncml0cw= =\n ", + .output = "foo barits", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .input = "\nZm9v\n \tIGJh \t\ncml0cw\n= =\n ", + .output = "foo barits", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_NO_PADDING, + .input = "\nZm9v\n \tIGJh \t\ncml0cw==", + .output = "foo barits", + .ret = -1, + .src_pos = 22, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_NO_PADDING, + .input = "\nZm9v\n \tIGJh \t\ncml0cw", + .output = "foo barits", + .ret = 0, + .src_pos = 22, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_IGNORE_PADDING, + .input = "\nZm9v\n \tIGJh \t\ncml0cw==", + .output = "foo barits", + .ret = 0, + .src_pos = 24, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_IGNORE_PADDING, + .input = "\nZm9v\n \tIGJh \t\ncml0cw", + .output = "foo barits", + .ret = 0, + .src_pos = 22, + }, + { + .scheme = &base64_scheme, + .input = " anVzdCBuaWlu \n", + .output = "just niin", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64url_scheme, + .input = " anVzdCBuaWlu \n", + .output = "just niin", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_NO_PADDING, + .input = " anVzdCBuaWlu \n", + .output = "just niin", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_IGNORE_PADDING, + .input = " anVzdCBuaWlu \n", + .output = "just niin", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .input = "aGVsb", + .output = "hel", + .ret = -1, + .src_pos = 5, + }, + { + .scheme = &base64url_scheme, + .input = "aGVsb", + .output = "hel", + .ret = -1, + .src_pos = 5, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_NO_PADDING, + .input = "aGVsb", + .output = "hel", + .ret = 0, + .src_pos = 5, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_IGNORE_PADDING, + .input = "aGVsb", + .output = "hel", + .ret = 0, + .src_pos = 5, + }, + { + .scheme = &base64_scheme, + .input = "aGVsb!!!!!", + .output = "hel", + .ret = -1, + .src_pos = 5, + }, + { + .scheme = &base64url_scheme, + .input = "aGVsb!!!!!", + .output = "hel", + .ret = -1, + .src_pos = 5, + }, + { + .scheme = &base64_scheme, + .input = "aGVs!!!!!", + .output = "hel", + .ret = -1, + .src_pos = 4, + }, + { + .scheme = &base64url_scheme, + .input = "aGVs!!!!!", + .output = "hel", + .ret = -1, + .src_pos = 4, + }, + { + .scheme = &base64_scheme, + .input = + "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt" + "C+INC60YPRgCDQtNC+0Y/MgdGCLg==", + .output = + "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc" + "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0" + "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc" + "\x81\xd1\x82\x2e", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64url_scheme, + .input = + "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt" + "C-INC60YPRgCDQtNC-0Y_MgdGCLg==", + .output = + "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc" + "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0" + "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc" + "\x81\xd1\x82\x2e", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_NO_PADDING, + .input = + "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt" + "C+INC60YPRgCDQtNC+0Y/MgdGCLg", + .output = + "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc" + "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0" + "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc" + "\x81\xd1\x82\x2e", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_IGNORE_PADDING, + .input = + "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt" + "C+INC60YPRgCDQtNC+0Y/MgdGCLg", + .output = + "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc" + "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0" + "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc" + "\x81\xd1\x82\x2e", + .ret = 0, + .src_pos = UINT_MAX, + }, + { + .scheme = &base64_scheme, + .flags = BASE64_DECODE_FLAG_IGNORE_PADDING, + .input = + "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt" + "C+INC60YPRgCDQtNC+0Y/MgdGCLg==", + .output = + "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc" + "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0" + "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc" + "\x81\xd1\x82\x2e", + .ret = 0, + .src_pos = UINT_MAX, + }, +}; + +static void test_base64_decode_lowlevel(void) +{ + string_t *str; + buffer_t buf; + unsigned int i; + + test_begin("base64 decode low-level"); + for (i = 0; i < N_ELEMENTS(tests_base64_decode_lowlevel); i++) { + const struct test_base64_decode_lowlevel *test = + &tests_base64_decode_lowlevel[i]; + struct base64_decoder dec; + size_t src_pos; + int ret; + + /* Some of the base64_decode() callers use fixed size buffers. + Use a fixed size buffer here as well to test that + base64_decode() can't allocate any extra space even + temporarily. */ + size_t max_decoded_size = + MAX_BASE64_DECODED_SIZE(strlen(test->input)); + + buffer_create_from_data(&buf, + (max_decoded_size == 0 ? "" : + t_malloc0(max_decoded_size)), + max_decoded_size); + str = &buf; + base64_decode_init(&dec, test->scheme, test->flags); + ret = base64_decode_more(&dec, test->input, strlen(test->input), + &src_pos, str); + if (ret >= 0) + ret = base64_decode_finish(&dec); + + test_assert_idx(ret == test->ret, i); + test_assert_idx(strlen(test->output) == str_len(str) && + memcmp(test->output, str_data(str), + str_len(str)) == 0, i); + test_assert_idx(src_pos == test->src_pos || + (test->src_pos == UINT_MAX && + src_pos == strlen(test->input)), i); + if (ret >= 0) { + test_assert_idx( + str_len(str) <= MAX_BASE64_DECODED_SIZE( + strlen(test->input)), i); + } + } + test_end(); +} + +static void +test_base64_random_lowlevel_one_block(const struct base64_scheme *b64, + enum base64_encode_flags enc_flags, + enum base64_decode_flags dec_flags, + size_t max_line_len, + unsigned int test_idx, + const unsigned char *in_buf, + size_t in_buf_size, + buffer_t *buf1, buffer_t *buf2) +{ + struct base64_encoder enc; + struct base64_decoder dec; + void *space; + size_t enc_size; + buffer_t buf; + int ret; + + buffer_set_used_size(buf1, 0); + buffer_set_used_size(buf2, 0); + + base64_encode_init(&enc, b64, enc_flags, max_line_len); + enc_size = base64_get_full_encoded_size(&enc, in_buf_size); + space = buffer_append_space_unsafe(buf1, enc_size); + buffer_create_from_data(&buf, space, enc_size); + + if (!base64_encode_more(&enc, in_buf, in_buf_size, NULL, &buf)) + test_assert_idx(FALSE, test_idx); + if (!base64_encode_finish(&enc, &buf)) + test_assert_idx(FALSE, test_idx); + + test_assert(base64_get_full_encoded_size(&enc, in_buf_size) == + buf1->used); + + base64_decode_init(&dec, b64, dec_flags); + space = buffer_append_space_unsafe(buf2, in_buf_size); + buffer_create_from_data(&buf, space, in_buf_size); + + ret = base64_decode_more(&dec, buf1->data, buf1->used, NULL, &buf); + if (ret >= 0) + ret = base64_decode_finish(&dec); + + test_assert_idx(ret >= 0, test_idx); + test_assert_idx(buf2->used == in_buf_size && + memcmp(in_buf, buf2->data, in_buf_size) == 0, test_idx); +} + +static void +test_base64_random_lowlevel_stream(const struct base64_scheme *b64, + enum base64_encode_flags enc_flags, + enum base64_decode_flags dec_flags, + size_t max_line_len, unsigned int test_idx, + const unsigned char *in_buf, + size_t in_buf_size, + buffer_t *buf1, buffer_t *buf2, + size_t chunk_size) +{ + struct base64_encoder enc; + struct base64_decoder dec; + const unsigned char *buf_p, *buf_begin, *buf_end; + int ret; + size_t out_space, out_full_size; + void *out_data; + buffer_t out; + + /* Encode */ + + buffer_set_used_size(buf1, 0); + + buf_begin = in_buf; + buf_end = buf_begin + in_buf_size; + + base64_encode_init(&enc, b64, enc_flags, max_line_len); + out_full_size = base64_get_full_encoded_size(&enc, in_buf_size); + out_space = 0; + for (buf_p = buf_begin; buf_p < buf_end; ) { + size_t buf_ch, out_ch; + size_t left = (buf_end - buf_p); + size_t used = buf1->used; + size_t src_pos, out_size, src_full_space; + bool eres; + + if (chunk_size == 0) { + buf_ch = i_rand_limit(32); + out_ch = i_rand_limit(32); + } else { + buf_ch = chunk_size; + out_ch = chunk_size; + } + + out_space += out_ch; + out_data = buffer_append_space_unsafe(buf1, out_space); + buffer_create_from_data(&out, out_data, out_space); + + if (buf_ch > left) + buf_ch = left; + + src_full_space = base64_encode_get_full_space( + &enc, out_full_size - used); + test_assert_idx(src_full_space >= (size_t)(buf_end - buf_p), + test_idx); + + out_size = base64_encode_get_size(&enc, buf_ch); + + eres = base64_encode_more(&enc, buf_p, buf_ch, &src_pos, &out); + test_assert_idx((eres && src_pos == buf_ch) || + (!eres && src_pos < buf_ch), test_idx); + test_assert_idx(out.used <= out_size, test_idx); + buf_p += src_pos; + i_assert(out_space >= out.used); + out_space -= out.used; + buffer_set_used_size(buf1, used + out.used); + } + test_assert_idx(base64_encode_finish(&enc, buf1), test_idx); + + /* Verify encode */ + + test_assert(out_full_size == buf1->used); + + buffer_set_used_size(buf2, 0); + base64_encode_init(&enc, b64, enc_flags, max_line_len); + test_assert_idx(base64_encode_more(&enc, in_buf, in_buf_size, + NULL, buf2), test_idx); + test_assert_idx(base64_encode_finish(&enc, buf2), test_idx); + test_assert_idx(buffer_cmp(buf1, buf2), test_idx); + + /* Decode */ + + buffer_set_used_size(buf2, 0); + + buf_begin = buf1->data; + buf_end = buf_begin + buf1->used; + + base64_decode_init(&dec, b64, dec_flags); + ret = 1; + out_space = 0; + for (buf_p = buf_begin; buf_p < buf_end; ) { + size_t buf_ch, out_ch; + size_t left = (buf_end - buf_p); + size_t used = buf2->used; + size_t src_pos; + + if (chunk_size == 0) { + buf_ch = i_rand_limit(32); + out_ch = i_rand_limit(32); + } else { + buf_ch = chunk_size; + out_ch = chunk_size; + } + + out_space += out_ch; + out_data = buffer_append_space_unsafe(buf2, out_space); + buffer_create_from_data(&out, out_data, out_space); + + if (buf_ch > left) + buf_ch = left; + ret = base64_decode_more(&dec, buf_p, buf_ch, + &src_pos, &out); + test_assert_idx(ret >= 0, test_idx); + if (ret < 0) { + break; + } + buf_p += src_pos; + i_assert(out_space >= out.used); + out_space -= out.used; + buffer_set_used_size(buf2, used + out.used); + } + test_assert_idx(ret >= 0, test_idx); + + /* Verify decode */ + if (ret > 0) { + ret = base64_decode_finish(&dec); + test_assert_idx(ret == 0, test_idx); + test_assert_idx(buf2->used == in_buf_size && + memcmp(in_buf, buf2->data, in_buf_size) == 0, + test_idx); + } +} + +static void +test_base64_random_lowlevel_case(const struct base64_scheme *b64, + enum base64_encode_flags enc_flags, + enum base64_decode_flags dec_flags, + size_t max_line_len) +{ + unsigned char in_buf[512]; + size_t in_buf_size; + buffer_t *buf1, *buf2; + unsigned int i, j; + + if (test_has_failed()) + return; + + buf1 = t_buffer_create(MAX_BASE64_ENCODED_SIZE(sizeof(in_buf))); + buf2 = t_buffer_create(MAX_BASE64_ENCODED_SIZE(sizeof(in_buf))); + + /* one block */ + for (i = 0; i < 1000; i++) { + in_buf_size = i_rand_limit(sizeof(in_buf)); + for (j = 0; j < in_buf_size; j++) + in_buf[j] = i_rand_uchar(); + + test_base64_random_lowlevel_one_block(b64, enc_flags, dec_flags, + max_line_len, i, + in_buf, in_buf_size, + buf1, buf2); + + if (test_has_failed()) { + i_info("One block test failed (" + "enc_flags=%02x dec_flags=%02x " + "max_line_len=%zu size=%zu)", + enc_flags, dec_flags, max_line_len, + in_buf_size); + return; + } + } + + /* streaming; single-byte trickle */ + for (i = 0; i < 1000; i++) { + in_buf_size = i_rand_limit(sizeof(in_buf)); + for (j = 0; j < in_buf_size; j++) + in_buf[j] = i_rand_uchar(); + + test_base64_random_lowlevel_stream(b64, enc_flags, dec_flags, + max_line_len, i, + in_buf, in_buf_size, + buf1, buf2, 1); + + if (test_has_failed()) { + i_info("Streaming single-byte trickle test failed (" + "enc_flags=%02x dec_flags=%02x " + "max_line_len=%zu size=%zu)", + enc_flags, dec_flags, max_line_len, + in_buf_size); + return; + } + } + + /* streaming; random chunks */ + for (i = 0; i < 1000; i++) { + in_buf_size = i_rand_limit(sizeof(in_buf)); + for (j = 0; j < in_buf_size; j++) + in_buf[j] = i_rand_uchar(); + + test_base64_random_lowlevel_stream(b64, enc_flags, dec_flags, + max_line_len, i, + in_buf, in_buf_size, + buf1, buf2, 0); + if (test_has_failed()) { + i_info("Streaming random chunks test failed (" + "enc_flags=%02x dec_flags=%02x " + "max_line_len=%zu size=%zu)", + enc_flags, dec_flags, max_line_len, + in_buf_size); + return; + } + } +} + +static void +test_base64_random_lowlevel(void) +{ + test_begin("base64 encode/decode low-level with random input"); + test_base64_random_lowlevel_case(&base64_scheme, 0, 0, 0); + test_base64_random_lowlevel_case(&base64url_scheme, 0, 0, 0); + test_base64_random_lowlevel_case(&base64_scheme, 0, + BASE64_DECODE_FLAG_EXPECT_BOUNDARY, 0); + test_base64_random_lowlevel_case(&base64url_scheme, 0, + BASE64_DECODE_FLAG_EXPECT_BOUNDARY, 0); + test_base64_random_lowlevel_case(&base64_scheme, 0, + BASE64_DECODE_FLAG_NO_WHITESPACE, 0); + test_base64_random_lowlevel_case(&base64url_scheme, 0, + BASE64_DECODE_FLAG_NO_WHITESPACE, 0); + test_base64_random_lowlevel_case(&base64_scheme, 0, 0, 10); + test_base64_random_lowlevel_case(&base64url_scheme, 0, 0, 10); + test_base64_random_lowlevel_case(&base64_scheme, + BASE64_ENCODE_FLAG_CRLF, 0, 10); + test_base64_random_lowlevel_case(&base64url_scheme, + BASE64_ENCODE_FLAG_CRLF, 0, 10); + test_base64_random_lowlevel_case(&base64_scheme, + BASE64_ENCODE_FLAG_NO_PADDING, + BASE64_DECODE_FLAG_NO_PADDING, 0); + test_base64_random_lowlevel_case(&base64url_scheme, + BASE64_ENCODE_FLAG_NO_PADDING, + BASE64_DECODE_FLAG_NO_PADDING, 0); + test_base64_random_lowlevel_case(&base64_scheme, + BASE64_ENCODE_FLAG_NO_PADDING | + BASE64_ENCODE_FLAG_CRLF, + BASE64_DECODE_FLAG_NO_PADDING, 15); + test_base64_random_lowlevel_case(&base64url_scheme, + BASE64_ENCODE_FLAG_NO_PADDING | + BASE64_ENCODE_FLAG_CRLF, + BASE64_DECODE_FLAG_NO_PADDING, 15); + test_base64_random_lowlevel_case(&base64_scheme, + BASE64_ENCODE_FLAG_NO_PADDING | + BASE64_ENCODE_FLAG_CRLF, + BASE64_DECODE_FLAG_NO_PADDING, 1); + test_end(); +} + +static void +_add_lines(const char *in, size_t max_line_len, bool crlf, string_t *out) +{ + size_t in_len = strlen(in); + + while (max_line_len > 0 && in_len > max_line_len) { + str_append_data(out, in, max_line_len); + if (crlf) + str_append(out, "\r\n"); + else + str_append_c(out, '\n'); + in += max_line_len; + in_len -= max_line_len; + } + + str_append_data(out, in, in_len); +} + +static void test_base64_encode_lines(void) +{ + static const char *input[] = { + "Passer, deliciae meae puellae,\n" + "quicum ludere, quem in sinu tenere,\n" + "cui primum digitum dare appetenti\n" + "et acris solet incitare morsus,\n" + "cum desiderio meo nitenti\n" + "carum nescio quid lubet iocari,\n" + "credo ut, cum gravis acquiescet ardor,\n" + "sit solaciolum sui doloris,\n" + "tecum ludere sicut ipsa possem\n" + "et tristis animi levare curas!\n" + }; + static const char *output[] = { + "UGFzc2VyLCBkZWxpY2lhZSBtZWFlIHB1ZWxsYWUsCnF1aWN1" + "bSBsdWRlcmUsIHF1ZW0gaW4gc2ludSB0ZW5lcmUsCmN1aSBw" + "cmltdW0gZGlnaXR1bSBkYXJlIGFwcGV0ZW50aQpldCBhY3Jp" + "cyBzb2xldCBpbmNpdGFyZSBtb3JzdXMsCmN1bSBkZXNpZGVy" + "aW8gbWVvIG5pdGVudGkKY2FydW0gbmVzY2lvIHF1aWQgbHVi" + "ZXQgaW9jYXJpLApjcmVkbyB1dCwgY3VtIGdyYXZpcyBhY3F1" + "aWVzY2V0IGFyZG9yLApzaXQgc29sYWNpb2x1bSBzdWkgZG9s" + "b3JpcywKdGVjdW0gbHVkZXJlIHNpY3V0IGlwc2EgcG9zc2Vt" + "CmV0IHRyaXN0aXMgYW5pbWkgbGV2YXJlIGN1cmFzIQo=", + }; + string_t *out_test, *out_ref; + unsigned int i, n; + + out_test = t_str_new(256); + out_ref = t_str_new(256); + + test_begin("base64 encode lines (LF)"); + for (i = 0; i < N_ELEMENTS(input); i++) { + struct base64_encoder b64enc; + + for (n = 0; n <= 80; n++) { + str_truncate(out_test, 0); + base64_encode_init(&b64enc, &base64_scheme, 0, n); + base64_encode_more(&b64enc, input[i], strlen(input[i]), + NULL, out_test); + base64_encode_finish(&b64enc, out_test); + + str_truncate(out_ref, 0); + _add_lines(output[i], n, FALSE, out_ref); + + test_assert(strcmp(str_c(out_ref), + str_c(out_test)) == 0); + + } + } + test_end(); + + test_begin("base64 encode lines (CRLF)"); + for (i = 0; i < N_ELEMENTS(input); i++) { + struct base64_encoder b64enc; + + for (n = 0; n <= 80; n++) { + str_truncate(out_test, 0); + base64_encode_init(&b64enc, &base64_scheme, + BASE64_ENCODE_FLAG_CRLF, n); + base64_encode_more(&b64enc, input[i], strlen(input[i]), + NULL, out_test); + base64_encode_finish(&b64enc, out_test); + + str_truncate(out_ref, 0); + _add_lines(output[i], n, TRUE, out_ref); + + test_assert(strcmp(str_c(out_ref), + str_c(out_test)) == 0); + + } + } + test_end(); +} + +void test_base64(void) +{ + test_base64_encode(); + test_base64_decode(); + test_base64_random(); + test_base64url_encode(); + test_base64url_decode(); + test_base64url_random(); + test_base64_encode_lowlevel(); + test_base64_decode_lowlevel(); + test_base64_random_lowlevel(); + test_base64_encode_lines(); +} diff --git a/src/lib/test-bits.c b/src/lib/test-bits.c new file mode 100644 index 0000000..d091dc5 --- /dev/null +++ b/src/lib/test-bits.c @@ -0,0 +1,281 @@ +/* Copyright (c) 2001-2018 Dovecot authors, see the included COPYING file */ + +/* Unit tests for bit twiddles library */ + +#include "test-lib.h" + +#include <stdio.h> + +static void test_bits_unsigned_minus(void) +{ + test_begin("bits_unsigned_minus()"); + + // 32 bit + test_assert(UNSIGNED_MINUS(0x00000000U) == 0x00000000U); + + test_assert(UNSIGNED_MINUS(0x00000001U) == 0xffffffffU); + test_assert(UNSIGNED_MINUS(0x00000002U) == 0xfffffffeU); + test_assert(UNSIGNED_MINUS(0x00000003U) == 0xfffffffdU); + //.. + test_assert(UNSIGNED_MINUS(0x7fffffffU) == 0x80000001U); + test_assert(UNSIGNED_MINUS(0x80000000U) == 0x80000000U); + test_assert(UNSIGNED_MINUS(0x80000001U) == 0x7fffffffU); + //.. + test_assert(UNSIGNED_MINUS(0xffffffffU) == 0x00000001U); + test_assert(UNSIGNED_MINUS(0xfffffffeU) == 0x00000002U); + test_assert(UNSIGNED_MINUS(0xfffffffdU) == 0x00000003U); + + // 64 bit + test_assert(UNSIGNED_MINUS(0x0000000000000000ULL) == 0x0000000000000000ULL); + + test_assert(UNSIGNED_MINUS(0x0000000000000001ULL) == 0xffffffffffffffffULL); + test_assert(UNSIGNED_MINUS(0x0000000000000002ULL) == 0xfffffffffffffffeULL); + test_assert(UNSIGNED_MINUS(0x0000000000000003ULL) == 0xfffffffffffffffdULL); + //.. + test_assert(UNSIGNED_MINUS(0x7fffffffffffffffULL) == 0x8000000000000001ULL); + test_assert(UNSIGNED_MINUS(0x8000000000000000ULL) == 0x8000000000000000ULL); + test_assert(UNSIGNED_MINUS(0x8000000000000001ULL) == 0x7fffffffffffffffULL); + //.. + test_assert(UNSIGNED_MINUS(0xffffffffffffffffULL) == 0x0000000000000001ULL); + test_assert(UNSIGNED_MINUS(0xfffffffffffffffeULL) == 0x0000000000000002ULL); + test_assert(UNSIGNED_MINUS(0xfffffffffffffffdULL) == 0x0000000000000003ULL); + + test_end(); +} + + +/* nearest_power(0) = error bits_requiredXX(0) = 0 + nearest_power(1) = 1 = 1<<0 bits_requiredXX(1) = 1 + nearest_power(2) = 2 = 1<<1 bits_requiredXX(2) = 2 + nearest_power(3) = 4 = 1<<2 bits_requiredXX(3) = 2 + nearest_power(4) = 4 = 1<<2 bits_requiredXX(4) = 3 + nearest_power(5) = 8 = 1<<3 bits_requiredXX(5) = 3 + nearest_power(7) = 8 = 1<<3 bits_requiredXX(7) = 3 + nearest_power(8) = 8 = 1<<3 bits_requiredXX(8) = 4 +*/ + +/* nearest_power(num) == 1ULL << bits_required64(num-1) */ +static void test_nearest_power(void) +{ + unsigned int b; + size_t num; + test_begin("nearest_power()"); + test_assert(nearest_power(1)==1); + test_assert(nearest_power(2)==2); + for (b = 2; b < CHAR_BIT*sizeof(size_t) - 1; ++b) { + /* b=2 tests 3,4,5; b=3 tests 7,8,9; ... b=30 tests ~1G */ + num = (size_t)1 << b; + test_assert_idx(nearest_power(num-1) == num, b); + test_assert_idx(nearest_power(num ) == num, b); + test_assert_idx(nearest_power(num+1) == num<<1, b); + } + /* With 32-bit size_t, now: b=31 tests 2G-1, 2G, not 2G+1. */ + num = (size_t)1 << b; + test_assert_idx(nearest_power(num-1) == num, b); + test_assert_idx(nearest_power(num ) == num, b); + /* i_assert()s: test_assert_idx(nearest_power(num+1) == num<<1, b); */ + test_end(); +} + +static void test_bits_is_power_of_two(void) +{ + test_begin("bits_is_power_of_two()"); + for (unsigned int i = 0; i < 64; i++) + test_assert_idx(bits_is_power_of_two(1ULL << i), i); + for (unsigned int i = 2; i < 64; i++) { + test_assert_idx(!bits_is_power_of_two((1ULL << i) - 1), i); + test_assert_idx(!bits_is_power_of_two((1ULL << i) + 1), i); + } + test_assert(!bits_is_power_of_two(0)); + test_assert(!bits_is_power_of_two(0xffffffffffffffffULL)); + test_assert( bits_is_power_of_two(0x8000000000000000ULL)); + test_end(); +} + +static void test_bits_requiredXX(void) +{ + /* As ..64 depends on ..32 and tests it twice, + * and ..32 depends on ..16 and tests it twice, + * etc., we only test ..64 + */ + unsigned int b; + test_begin("bits_requiredXX()"); + test_assert(bits_required64(0) == 0); + test_assert(bits_required64(1) == 1); + test_assert(bits_required64(2) == 2); + for (b = 2; b < 64; ++b) { + /* b=2 tests 3,4,5; b=3 tests 7,8,9; ... */ + uint64_t num = 1ULL << b; + test_assert_idx(bits_required64(num-1) == b, b); + test_assert_idx(bits_required64(num ) == b+1, b); + test_assert_idx(bits_required64(num+1) == b+1, b); + } + test_end(); +} + +static void test_sum_overflows(void) +{ +#define MAX64 (uint64_t)-1 + static const struct { + uint64_t a, b; + bool overflows; + } tests[] = { + { MAX64-1, 1, FALSE }, + { MAX64, 1, TRUE }, + { MAX64-1, 1, FALSE }, + { MAX64-1, 2, TRUE }, + { MAX64-1, MAX64-1, TRUE }, + { MAX64-1, MAX64, TRUE }, + { MAX64, MAX64, TRUE } + }; + unsigned int i; + + test_begin("UINT64_SUM_OVERFLOWS"); + for (i = 0; i < N_ELEMENTS(tests); i++) + test_assert(UINT64_SUM_OVERFLOWS(tests[i].a, tests[i].b) == tests[i].overflows); + test_end(); +} + +static void ATTR_NO_SANITIZE_INTEGER ATTR_NO_SANITIZE_IMPLICIT_CONVERSION +test_bits_fraclog(void) +{ + unsigned int fracbits; + for (fracbits = 0; fracbits < 6; fracbits++) { + static char name[] = "fraclog x-bit"; + name[8] = '0'+ fracbits; + test_begin(name); + + unsigned int i; + unsigned int last_end = ~0u; + for (i = 0; i < BITS_FRACLOG_BUCKETS(fracbits); i++) { + unsigned int start = bits_fraclog_bucket_start(i, fracbits); + unsigned int end = bits_fraclog_bucket_end(i, fracbits); + test_assert_idx(start == last_end + 1, i); + last_end = end; + test_assert_idx(bits_fraclog(start, fracbits) == i, i); + test_assert_idx(bits_fraclog(end, fracbits) == i, i); + } + test_assert_idx(last_end == ~0u, fracbits); + + test_end(); + } +} + +/* The compiler *should* generate different code when the fracbits parameter + is a compile-time constant, so we also need to check that's the case. +*/ +static void ATTR_NO_SANITIZE_INTEGER ATTR_NO_SANITIZE_IMPLICIT_CONVERSION +test_bits_fraclog_const(void) +{ +#define FRACBITS 2 +#define STR2(s) #s +#define STR(s) STR2(s) + test_begin("fraclog constant " STR(FRACBITS) " bit"); + + unsigned int i; + unsigned int last_end = ~0u; + for (i = 0; i < BITS_FRACLOG_BUCKETS(FRACBITS); i++) { + unsigned int start = bits_fraclog_bucket_start(i, FRACBITS); + unsigned int end = bits_fraclog_bucket_end(i, FRACBITS); + test_assert_idx(start == last_end + 1, i); + last_end = end; + test_assert_idx(bits_fraclog(start, FRACBITS) == i, i); + test_assert_idx(bits_fraclog(end, FRACBITS) == i, i); + } + test_assert(last_end == ~0u); + + test_end(); +} + +static void test_bits_rotl32(void) +{ + test_begin("bits_rotl32"); + + test_assert(bits_rotl32(0x1c00000eU, 3) == 0xe0000070U); + test_assert(bits_rotl32(0xe0000070U, 5) == 0x00000e1cU); + test_assert(bits_rotl32(0x00000e1cU, 0) == 0x00000e1cU); + test_assert(bits_rotl32(0x1c00000eU, 3 + 32) == 0xe0000070U); + + test_end(); +} + +static void test_bits_rotl64(void) +{ + test_begin("bits_rotl64"); + + test_assert(bits_rotl64(0x1c0000000000000eUL, 3) == 0xe000000000000070UL); + test_assert(bits_rotl64(0xe000000000000070UL, 5) == 0x0000000000000e1cUL); + test_assert(bits_rotl64(0x0000000000000e1cUL, 0) == 0x0000000000000e1cUL); + test_assert(bits_rotl64(0x1c0000000000000eUL, 3 + 64) == 0xe000000000000070UL); + + test_end(); +} + +static void test_bits_rotr32(void) +{ + test_begin("bits_rotr32"); + + test_assert(bits_rotr32(0x1c00000eU, 3) == 0xc3800001U); + test_assert(bits_rotr32(0xc3800001U, 5) == 0x0e1c0000U); + test_assert(bits_rotr32(0x00000e1cU, 0) == 0x00000e1cU); + test_assert(bits_rotr32(0x1c00000eU, 3 + 32) == 0xc3800001U); + + test_end(); +} + +static void test_bits_rotr64(void) +{ + test_begin("bits_rotr64"); + + test_assert(bits_rotr64(0x1c0000000000000eUL, 3) == 0xc380000000000001UL); + test_assert(bits_rotr64(0xc380000000000001UL, 5) == 0x0e1c000000000000UL); + test_assert(bits_rotr64(0x0000000000000e1cUL, 0) == 0x0000000000000e1cUL); + test_assert(bits_rotr64(0x1c0000000000000eUL, 3 + 64) == 0xc380000000000001UL); + + test_end(); +} + +static void test_bit_tests(void) +{ + test_begin("HAS_..._BITS() macro tests"); + + test_assert(HAS_NO_BITS(1,0)); + test_assert(HAS_NO_BITS(2,~2U)); + test_assert(!HAS_NO_BITS(2,2)); + + /* OUCH - this vacuously true expression fails. However, if you are + dumb enough to use 0 as bits, then it will also fail in the verbose + case that this macro replaces, it's not a regression. */ + /* test_assert(HAS_ANY_BITS(6,0)); */ + test_assert(HAS_ANY_BITS(3,1)); + test_assert(HAS_ANY_BITS(2,3)); + test_assert(!HAS_ANY_BITS(7,~(7U|128U))); + + test_assert(HAS_ALL_BITS(0,0)); + test_assert(HAS_ALL_BITS(30,14)); + test_assert(!HAS_ALL_BITS(~1U,~0U)); + + /* Trap double-evaluation */ + unsigned int v=10,b=2; + test_assert(!HAS_NO_BITS(v++, b++) && v==11 && b==3); + test_assert(HAS_ANY_BITS(v++, b++) && v==12 && b==4); + test_assert(HAS_ALL_BITS(v++, b++) && v==13 && b==5); + + test_end(); +} + +void test_bits(void) +{ + test_bits_unsigned_minus(); + test_nearest_power(); + test_bits_is_power_of_two(); + test_bits_requiredXX(); + test_bits_fraclog(); + test_bits_fraclog_const(); + test_bits_rotl32(); + test_bits_rotr32(); + test_bits_rotl64(); + test_bits_rotr64(); + test_sum_overflows(); + test_bit_tests(); +} diff --git a/src/lib/test-bsearch-insert-pos.c b/src/lib/test-bsearch-insert-pos.c new file mode 100644 index 0000000..5b2454e --- /dev/null +++ b/src/lib/test-bsearch-insert-pos.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "bsearch-insert-pos.h" + +static int cmp_uint(const unsigned int *i1, const unsigned int *i2) +{ + return (int)*i1 - (int)*i2; +} + +void test_bsearch_insert_pos(void) +{ + static const unsigned int input[] = { + 1, 5, 9, 15, 16, UINT_MAX, + 1, 5, 9, 15, 16, 17, UINT_MAX, + UINT_MAX + }; + static const unsigned int max_key = 18; + const unsigned int *cur; + unsigned int key, len, i, idx; + bool success; + + cur = input; + for (i = 0; cur[0] != UINT_MAX; i++) { + for (len = 0; cur[len] != UINT_MAX; len++) ; + for (key = 0; key < max_key; key++) { + if (bsearch_insert_pos(&key, cur, len, sizeof(*cur), + cmp_uint, &idx)) + success = cur[idx] == key; + else if (idx == 0) + success = cur[0] > key; + else if (idx == len) + success = cur[len-1] < key; + else { + success = cur[idx-1] < key && + cur[idx+1] > key; + } + if (!success) + break; + } + cur += len + 1; + + test_out(t_strdup_printf("bsearch_insert_pos(%d,%d)", i, key), + success); + } +} diff --git a/src/lib/test-buffer-istream.c b/src/lib/test-buffer-istream.c new file mode 100644 index 0000000..056c859 --- /dev/null +++ b/src/lib/test-buffer-istream.c @@ -0,0 +1,101 @@ +/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "istream.h" +#include "str.h" +#include "write-full.h" + +#include <unistd.h> +#include <fcntl.h> + +#define TEST_FILENAME ".test_buffer_append_full_file" +static void test_buffer_append_full_file(void) +{ + const char *test_string = "this is a test string\n"; + test_begin("buffer_append_full_file"); + buffer_t *result = t_buffer_create(32); + const char *error; + int fd = open(TEST_FILENAME, O_WRONLY|O_CREAT, 0600); + i_assert(fd > -1); + test_assert(write_full(fd, test_string, strlen(test_string)) == 0); + i_close_fd(&fd); + + test_assert(buffer_append_full_file(result, TEST_FILENAME, SIZE_MAX, + &error) == BUFFER_APPEND_OK); + test_assert_strcmp(str_c(result), test_string); + + /* test max_read_size */ + for (size_t max = 0; max < strlen(test_string)-1; max++) { + buffer_set_used_size(result, 0); + test_assert(buffer_append_full_file(result, TEST_FILENAME, + max, &error) == BUFFER_APPEND_READ_MAX_SIZE); + test_assert(result->used == max && + memcmp(result->data, test_string, max) == 0); + } + + fd = open(TEST_FILENAME, O_WRONLY|O_TRUNC); + i_assert(fd > -1); + /* write it enough many times */ + for (size_t i = 0; i < IO_BLOCK_SIZE; i += strlen(test_string)) { + test_assert(write_full(fd, test_string, strlen(test_string)) == 0); + } + i_close_fd(&fd); + buffer_set_used_size(result, 0); + test_assert(buffer_append_full_file(result, TEST_FILENAME, + SIZE_MAX, &error) == BUFFER_APPEND_OK); + for (size_t i = 0; i < result->used; i += strlen(test_string)) { + const char *data = result->data; + data += i; + test_assert(memcmp(data, test_string, strlen(test_string)) == 0); + } + buffer_set_used_size(result, 0); + test_assert(chmod(TEST_FILENAME, 0) == 0); + error = NULL; + test_assert(buffer_append_full_file(result, TEST_FILENAME, SIZE_MAX, + &error) == BUFFER_APPEND_READ_ERROR); + test_assert(error != NULL && *error != '\0'); + buffer_set_used_size(result, 0); + test_assert(chmod(TEST_FILENAME, 0700) == 0); + /* test permission problems */ + i_unlink(TEST_FILENAME); + test_assert(buffer_append_full_file(result, TEST_FILENAME, SIZE_MAX, + &error) == BUFFER_APPEND_READ_ERROR); + test_assert_strcmp(str_c(result), ""); + test_end(); +} + +static void test_buffer_append_full_istream(void) +{ + int fds[2]; + const char *error; + test_begin("buffer_append_full_istream"); + buffer_t *result = t_buffer_create(32); + test_assert(pipe(fds) == 0); + fd_set_nonblock(fds[0], TRUE); + fd_set_nonblock(fds[1], TRUE); + + struct istream *is = i_stream_create_fd(fds[0], SIZE_MAX); + /* test just the READ_MORE stuff */ + + test_assert(write_full(fds[1], "some data ", 10) == 0); + + test_assert(buffer_append_full_istream(result, is, SIZE_MAX, &error) == + BUFFER_APPEND_READ_MORE); + test_assert(write_full(fds[1], "final read", 10) == 0); + i_close_fd(&fds[1]); + + test_assert(buffer_append_full_istream(result, is, SIZE_MAX, &error) == + BUFFER_APPEND_OK); + test_assert_strcmp(str_c(result), "some data final read"); + i_stream_unref(&is); + i_close_fd(&fds[0]); + + test_end(); +} + +void test_buffer_append_full(void) +{ + test_buffer_append_full_file(); + test_buffer_append_full_istream(); +} + diff --git a/src/lib/test-buffer.c b/src/lib/test-buffer.c new file mode 100644 index 0000000..50a2e92 --- /dev/null +++ b/src/lib/test-buffer.c @@ -0,0 +1,367 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "buffer.h" + +static void test_buffer_random(void) +{ +#define BUF_TEST_SIZE (1024*2) +#define BUF_TEST_COUNT 1000 + buffer_t *buf; + unsigned char *p, testdata[BUF_TEST_SIZE], shadowbuf[BUF_TEST_SIZE]; + unsigned int i, shadowbuf_size; + size_t pos, pos2, size, size2; + int test = -1; + bool zero; + + buf = buffer_create_dynamic(default_pool, 1); + for (i = 0; i < BUF_TEST_SIZE; i++) + testdata[i] = i_rand_uchar(); + memset(shadowbuf, 0, sizeof(shadowbuf)); + + shadowbuf_size = 0; + for (i = 0; i < BUF_TEST_COUNT; i++) { + if (buf->used == BUF_TEST_SIZE) { + size = shadowbuf_size = i_rand_limit(buf->used - 1); + buffer_set_used_size(buf, size); + memset(shadowbuf + shadowbuf_size, 0, + BUF_TEST_SIZE - shadowbuf_size); + i_assert(buf->used < BUF_TEST_SIZE); + } + + test = i_rand_limit(7); + zero = i_rand_limit(10) == 0; + switch (test) { + case 0: + pos = i_rand_limit(BUF_TEST_SIZE - 1); + size = i_rand_limit(BUF_TEST_SIZE - pos); + if (!zero) { + buffer_write(buf, pos, testdata, size); + memcpy(shadowbuf + pos, testdata, size); + } else { + buffer_write_zero(buf, pos, size); + memset(shadowbuf + pos, 0, size); + } + if (pos + size > shadowbuf_size) + shadowbuf_size = pos + size; + break; + case 1: + size = i_rand_limit(BUF_TEST_SIZE - buf->used); + if (!zero) { + buffer_append(buf, testdata, size); + memcpy(shadowbuf + shadowbuf_size, + testdata, size); + } else { + buffer_append_zero(buf, size); + memset(shadowbuf + shadowbuf_size, 0, size); + } + shadowbuf_size += size; + break; + case 2: + pos = i_rand_limit(BUF_TEST_SIZE - 1); + size = i_rand_limit(BUF_TEST_SIZE - I_MAX(buf->used, pos)); + if (!zero) { + buffer_insert(buf, pos, testdata, size); + memmove(shadowbuf + pos + size, + shadowbuf + pos, + BUF_TEST_SIZE - (pos + size)); + memcpy(shadowbuf + pos, testdata, size); + } else { + buffer_insert_zero(buf, pos, size); + memmove(shadowbuf + pos + size, + shadowbuf + pos, + BUF_TEST_SIZE - (pos + size)); + memset(shadowbuf + pos, 0, size); + } + if (pos < shadowbuf_size) + shadowbuf_size += size; + else + shadowbuf_size = pos + size; + break; + case 3: + pos = i_rand_limit(BUF_TEST_SIZE - 1); + size = i_rand_limit(BUF_TEST_SIZE - pos); + buffer_delete(buf, pos, size); + if (pos < shadowbuf_size) { + if (pos + size > shadowbuf_size) + size = shadowbuf_size - pos; + memmove(shadowbuf + pos, + shadowbuf + pos + size, + BUF_TEST_SIZE - (pos + size)); + + shadowbuf_size -= size; + memset(shadowbuf + shadowbuf_size, 0, + BUF_TEST_SIZE - shadowbuf_size); + } + break; + case 4: + pos = i_rand_limit(BUF_TEST_SIZE - 1); + size = i_rand_limit(BUF_TEST_SIZE - pos); + size2 = i_rand_limit(BUF_TEST_SIZE - + I_MAX(buf->used, pos)); + buffer_replace(buf, pos, size, testdata, size2); + if (pos < shadowbuf_size) { + if (pos + size > shadowbuf_size) + size = shadowbuf_size - pos; + memmove(shadowbuf + pos, + shadowbuf + pos + size, + BUF_TEST_SIZE - (pos + size)); + + shadowbuf_size -= size; + memset(shadowbuf + shadowbuf_size, 0, + BUF_TEST_SIZE - shadowbuf_size); + } + memmove(shadowbuf + pos + size2, + shadowbuf + pos, + BUF_TEST_SIZE - (pos + size2)); + memcpy(shadowbuf + pos, testdata, size2); + if (pos < shadowbuf_size) + shadowbuf_size += size2; + else + shadowbuf_size = pos + size2; + break; + case 5: + if (shadowbuf_size <= 1) + break; + pos = i_rand_limit(shadowbuf_size - 1); /* dest */ + pos2 = i_rand_limit(shadowbuf_size - 1); /* source */ + size = i_rand_limit(shadowbuf_size - I_MAX(pos, pos2)); + buffer_copy(buf, pos, buf, pos2, size); + memmove(shadowbuf + pos, + shadowbuf + pos2, size); + if (pos > pos2 && pos + size > shadowbuf_size) + shadowbuf_size = pos + size; + break; + case 6: + pos = i_rand_limit(BUF_TEST_SIZE - 1); + size = i_rand_limit(BUF_TEST_SIZE - pos); + p = buffer_get_space_unsafe(buf, pos, size); + memcpy(p, testdata, size); + memcpy(shadowbuf + pos, testdata, size); + if (pos + size > shadowbuf_size) + shadowbuf_size = pos + size; + break; + } + i_assert(shadowbuf_size <= BUF_TEST_SIZE); + + if (buf->used != shadowbuf_size || + memcmp(buf->data, shadowbuf, buf->used) != 0) + break; + } + if (i == BUF_TEST_COUNT) + test_out("buffer", TRUE); + else { + test_out_reason("buffer", FALSE, + t_strdup_printf("round %u test %d failed", i, test)); + } + buffer_free(&buf); +} + +static void test_buffer_write(void) +{ + buffer_t *buf; + + test_begin("buffer_write"); + buf = t_buffer_create(8); + buffer_write(buf, 5, buf, 0); + test_assert(buf->used == 5); + test_end(); +} + +static void test_buffer_set_used_size(void) +{ + buffer_t *buf; + + test_begin("buffer_set_used_size"); + buf = t_buffer_create(8); + memset(buffer_append_space_unsafe(buf, 7), 'a', 7); + buffer_set_used_size(buf, 4); + test_assert(memcmp(buffer_get_space_unsafe(buf, 0, 7), "aaaa\0\0\0", 7) == 0); + memset(buffer_get_space_unsafe(buf, 4, 7), 'b', 7); + buffer_set_used_size(buf, 10); + test_assert(memcmp(buffer_append_space_unsafe(buf, 1), "\0", 1) == 0); + buffer_set_used_size(buf, 11); + test_assert(memcmp(buffer_get_space_unsafe(buf, 0, 11), "aaaabbbbbb\0", 11) == 0); + test_end(); +} + + +#if 0 + +/* this code is left here to produce the output found in + * buffer.h should it be needed for debugging purposes */ +#include "str.h" +#include "hex-binary.h" +static const char *binary_to_10(const unsigned char *data, size_t size) +{ + string_t *str = t_str_new(size*8); + + for (size_t i = 0; i < size; i++) { + for (int j = 0; j < 8; j++) { + if ((data[i] & (1 << (7-j))) != 0) + str_append_c(str, '1'); + else + str_append_c(str, '0'); + } + } + return str_c(str); +} + +static void test_foo(void) +{ + buffer_t *buf = buffer_create_dynamic(default_pool, 100); + + for (int i = 1; i <= 24; i++) { + buffer_set_used_size(buf, 0); + buffer_append_c(buf, 0xff); + buffer_append_c(buf, 0xff); + buffer_append_c(buf, 0xff); + buffer_truncate_rshift_bits(buf, i); + printf("%2d bits: %24s %s\n", i, + binary_to_hex(buf->data, buf->used), + binary_to_10(buf->data, buf->used)); + } +} + +#endif + +static void test_buffer_truncate_bits(void) +{ + buffer_t *buf; + test_begin("buffer_test_truncate_bits"); + + struct { + buffer_t input; + size_t bits; + buffer_t output; + } test_cases[] = { + { { { { "\xff\xff\xff", 3 } } }, 0, { { { "", 0 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 1, { { { "\x01", 1 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 2, { { { "\x03", 1 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 3, { { { "\x07", 1 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 4, { { { "\x0f", 1 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 5, { { { "\x1f", 1 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 6, { { { "\x3f", 1 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 7, { { { "\x7f", 1 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 8, { { { "\xff", 1 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 9, { { { "\x01\xff", 2 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 10, { { { "\x03\xff", 2 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 11, { { { "\x07\xff", 2 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 12, { { { "\x0f\xff", 2 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 13, { { { "\x1f\xff", 2 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 14, { { { "\x3f\xff", 2 } } } }, + { { { { "\xff\xff\xff", 3 } } }, 15, { { { "\x7f\xff", 2 } } } }, + { { { { "0123456789", 10 } } }, 16, { { { "01", 2 } } } }, + { { { { "0123456789", 10 } } }, 24, { { { "012", 3 } } } }, + { { { { "0123456789", 10 } } }, 32, { { { "0123", 4 } } } }, + { { { { "0123456789", 10 } } }, 40, { { { "01234", 5 } } } }, + { { { { "0123456789", 10 } } }, 48, { { { "012345", 6 } } } }, + { { { { "0123456789", 10 } } }, 56, { { { "0123456", 7 } } } }, + { { { { "0123456789", 10 } } }, 64, { { { "01234567", 8 } } } }, + { { { { "0123456789", 10 } } }, 72, { { { "012345678", 9 } } } }, + { { { { "0123456789", 10 } } }, 80, { { { "0123456789", 10 } } } }, + + { { { { "\x58\x11\xed\x02\x4d\x87\x4a\xe2\x5c\xb2\xfa\x69\xf0\xa9\x46\x2e\x04\xca\x5d\x82", 20 } } }, + 13, + { { { "\x0b\x02", 2 } } } + }, + + /* special test cases for auth policy */ + + { { { { "\x34\x40\xc8\xc9\x3a\xb6\xe7\xc4\x3f\xc1\xc3\x4d\xd5\x56\xa3\xea\xfb\x5a\x33\x57\xac\x11\x39\x2c\x71\xcb\xee\xbb\xc8\x66\x2f\x64", 32 } } }, + 12, + { { { "\x03\x44", 2 } } } + }, + + { { { { "\x49\xe5\x8a\x88\x76\xd3\x25\x68\xc9\x89\x4a\xe0\x64\xe4\x04\xf4\xf9\x13\xec\x88\x97\x47\x30\x7f\x3f\xcd\x8f\x74\x4f\x40\xd1\x25", 32 } } }, + 12, + { { { "\x04\x9e", 2 } } } + }, + + { { { { "\x08\x3c\xdc\x14\x61\x80\x1c\xe8\x43\x81\x98\xfa\xc0\x64\x04\x7a\xa2\x73\x25\x6e\xe6\x4b\x85\x42\xd0\xe2\x78\xd7\x91\xb4\x89\x3f", 32 } } }, + 12, + { { { "\x00\x83", 2 } } } + }, + + }; + + buf = t_buffer_create(10); + + for(size_t i = 0; i < N_ELEMENTS(test_cases); i++) { + buffer_set_used_size(buf, 0); + buffer_copy(buf, 0, &test_cases[i].input, 0, SIZE_MAX); + buffer_truncate_rshift_bits(buf, test_cases[i].bits); + test_assert_idx(buffer_cmp(buf, &test_cases[i].output) == TRUE, i); + } + + test_end(); +} + +static void test_buffer_replace(void) +{ + const char orig_input[] = "123456789"; + const char data[] = "abcdefghij"; + buffer_t *buf, *buf2; + unsigned int init_size, pos, size, data_size; + + test_begin("buffer_replace()"); + for (init_size = 0; init_size <= sizeof(orig_input)-1; init_size++) { + for (pos = 0; pos < sizeof(orig_input)+1; pos++) { + for (size = 0; size < sizeof(orig_input)+1; size++) { + for (data_size = 0; data_size <= sizeof(data)-1; data_size++) T_BEGIN { + buf = buffer_create_dynamic(pool_datastack_create(), 4); + buf2 = buffer_create_dynamic(pool_datastack_create(), 4); + buffer_append(buf, orig_input, init_size); + buffer_append(buf2, orig_input, init_size); + + buffer_replace(buf, pos, size, data, data_size); + buffer_delete(buf2, pos, size); + buffer_insert(buf2, pos, data, data_size); + test_assert(buf->used == buf2->used && + memcmp(buf->data, buf2->data, buf->used) == 0); + } T_END; + } + } + } + + test_end(); +} + +void test_buffer(void) +{ + test_buffer_random(); + test_buffer_write(); + test_buffer_set_used_size(); + test_buffer_truncate_bits(); + test_buffer_replace(); +} + +static void fatal_buffer_free(buffer_t *buf) +{ + buffer_free(&buf); +} + +enum fatal_test_state fatal_buffer(unsigned int stage) +{ + buffer_t *buf; + + switch (stage) { + case 0: + test_begin("fatal buffer_create_dynamic_max()"); + buf = buffer_create_dynamic_max(default_pool, 1, 5); + buffer_append(buf, "12345", 5); + test_expect_fatal_string("Buffer write out of range"); + test_fatal_set_callback(fatal_buffer_free, buf); + buffer_append_c(buf, 'x'); + return FATAL_TEST_FAILURE; + case 1: + buf = buffer_create_dynamic_max(default_pool, 1, 5); + test_expect_fatal_string("Buffer write out of range"); + test_fatal_set_callback(fatal_buffer_free, buf); + buffer_append(buf, "123456", 6); + return FATAL_TEST_FAILURE; + default: + test_end(); + return FATAL_TEST_FINISHED; + } +} diff --git a/src/lib/test-byteorder.c b/src/lib/test-byteorder.c new file mode 100644 index 0000000..9e350ce --- /dev/null +++ b/src/lib/test-byteorder.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2016-2017 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> + * + * 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 "test-lib.h" +#include "byteorder.h" + +struct bswap_run { + uint64_t in; + uint8_t out8; + uint16_t out16; + uint32_t out32; + uint64_t out64; +}; + +static const struct bswap_run runs[] = { + { + .in = 0, + .out8 = 0, + .out16 = 0, + .out32 = 0, + .out64 = 0, + }, + { + .in = 0xffffffffffffffff, + .out8 = 0xff, + .out16 = 0xffff, + .out32 = 0xffffffff, + .out64 = 0xffffffffffffffff, + }, + { + .in = 0x123456789abcdef0, + .out8 = 0xf0, + .out16 = 0xf0de, + .out32 = 0xf0debc9a, + .out64 = 0xf0debc9a78563412, + }, + { + .in = 0x8080808080808080, + .out8 = 0x80, + .out16 = 0x8080, + .out32 = 0x80808080, + .out64 = 0x8080808080808080, + }, +}; + +#define CHECK(iter, size, in, exp) \ + do { \ + uint##size##_t got = i_bswap_##size(in); \ + \ + test_begin(t_strdup_printf("byteorder - bswap " \ + "(size:%-2u iter:%u)", \ + size, iter)); \ + test_assert(got == exp); \ + test_end(); \ + } while (0) + +static void __test(int iter, const struct bswap_run *run) +{ + CHECK(iter, 8, run->in & 0xff, run->out8); + CHECK(iter, 16, run->in & 0xffff, run->out16); + CHECK(iter, 32, run->in & 0xffffffff, run->out32); + CHECK(iter, 64, run->in, run->out64); +} + +static void test_bswap(void) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(runs) ; i++) + __test(i, &runs[i]); +} + +struct unaligned_run { + uint8_t in[8]; + + /* outputs */ + uint8_t be8; + uint16_t be16; + uint32_t be32; + uint64_t be64; + + uint8_t le8; + uint16_t le16; + uint32_t le32; + uint64_t le64; + +#ifdef WORDS_BIGENDIAN +#define cpu8 be8 +#define cpu16 be16 +#define cpu32 be32 +#define cpu64 be64 +#else +#define cpu8 le8 +#define cpu16 le16 +#define cpu32 le32 +#define cpu64 le64 +#endif +}; + +static const struct unaligned_run uruns[] = { + { + .in = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }, + .be8 = 0, + .be16 = 0, + .be32 = 0, + .be64 = 0, + .le8 = 0, + .le16 = 0, + .le32 = 0, + .le64 = 0, + }, + { + .in = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + }, + .be8 = 0xff, + .be16 = 0xffff, + .be32 = 0xffffffff, + .be64 = 0xffffffffffffffff, + .le8 = 0xff, + .le16 = 0xffff, + .le32 = 0xffffffff, + .le64 = 0xffffffffffffffff, + }, + { + .in = { + 0x12, 0x34, 0x56, 0x78, + 0x9a, 0xbc, 0xde, 0xf0, + }, + .be8 = 0x12, + .be16 = 0x1234, + .be32 = 0x12345678, + .be64 = 0x123456789abcdef0, + .le8 = 0x12, + .le16 = 0x3412, + .le32 = 0x78563412, + .le64 = 0xf0debc9a78563412, + }, + { + .in = { + 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, + }, + .be8 = 0x80, + .be16 = 0x8080, + .be32 = 0x80808080, + .be64 = 0x8080808080808080, + .le8 = 0x80, + .le16 = 0x8080, + .le32 = 0x80808080, + .le64 = 0x8080808080808080, + }, +}; + +#define __CHECK_READ(iter, size, pfx, in, fxn, exp) \ + do { \ + uint##size##_t got = fxn(in); \ + \ + test_begin(t_strdup_printf("byteorder - unaligned read "\ + "(%-3s size:%-2u iter:%u)", \ + pfx, size, iter)); \ + test_assert(got == exp); \ + test_end(); \ + } while (0) + +#define CHECK_READ(iter, size, in, be_exp, le_exp, cpu_exp) \ + do { \ + __CHECK_READ(iter, size, "BE", in, \ + be##size##_to_cpu_unaligned, be_exp); \ + __CHECK_READ(iter, size, "LE", in, \ + le##size##_to_cpu_unaligned, le_exp); \ + __CHECK_READ(iter, size, "CPU", in, \ + cpu##size##_to_cpu_unaligned, cpu_exp); \ + } while (0) + +static void __test_read(int iter, const struct unaligned_run *run) +{ + CHECK_READ(iter, 8, run->in, run->be8, run->le8, run->cpu8); + CHECK_READ(iter, 16, run->in, run->be16, run->le16, run->cpu16); + CHECK_READ(iter, 32, run->in, run->be32, run->le32, run->cpu32); + CHECK_READ(iter, 64, run->in, run->be64, run->le64, run->cpu64); +} + +#define __CHECK_WRITE(iter, size, pfx, in, fxn, exp) \ + do { \ + uint8_t got[size / 8]; \ + \ + fxn(in, got); \ + \ + test_begin(t_strdup_printf("byteorder - unaligned write "\ + "(%-3s size:%-2u iter:%u)", \ + pfx, size, iter)); \ + test_assert(memcmp(got, exp, sizeof(got)) == 0); \ + test_end(); \ + } while (0) + +#define CHECK_WRITE(iter, size, out, be_in, le_in) \ + do { \ + __CHECK_WRITE(iter, size, "BE", be_in, \ + cpu##size##_to_be_unaligned, out); \ + __CHECK_WRITE(iter, size, "LE", le_in, \ + cpu##size##_to_le_unaligned, out); \ + } while (0) + +static void __test_write(int iter, const struct unaligned_run *run) +{ + CHECK_WRITE(iter, 8, run->in, run->be8, run->le8); + CHECK_WRITE(iter, 16, run->in, run->be16, run->le16); + CHECK_WRITE(iter, 32, run->in, run->be32, run->le32); + CHECK_WRITE(iter, 64, run->in, run->be64, run->le64); +} + +static void test_unaligned(void) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(uruns) ; i++) + __test_read(i, &uruns[i]); + + for (i = 0; i < N_ELEMENTS(uruns) ; i++) + __test_write(i, &uruns[i]); +} + +void test_byteorder(void) +{ + test_bswap(); + test_unaligned(); +} diff --git a/src/lib/test-connection.c b/src/lib/test-connection.c new file mode 100644 index 0000000..22677c3 --- /dev/null +++ b/src/lib/test-connection.c @@ -0,0 +1,744 @@ +/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "ioloop.h" +#include "connection.h" +#include "istream.h" +#include "ostream.h" +#include "strnum.h" +#include "strescape.h" + +#include <unistd.h> + +static const struct connection_settings client_set = +{ + .service_name_in = "TEST-S", + .service_name_out = "TEST-C", + .major_version = 1, + .minor_version = 0, + .client = TRUE, + .input_max_size = SIZE_MAX, + .output_max_size = SIZE_MAX, +}; + +static const struct connection_settings server_set = +{ + .service_name_in = "TEST-C", + .service_name_out = "TEST-S", + .major_version = 1, + .minor_version = 0, + .client = FALSE, + .input_max_size = SIZE_MAX, + .output_max_size = SIZE_MAX, +}; + +static bool received_quit = FALSE; +static bool was_resumed = FALSE; +static bool was_idle_killed = FALSE; +static int received_count = 0; + +static void test_connection_run(const struct connection_settings *set_s, + const struct connection_settings *set_c, + const struct connection_vfuncs *v_s, + const struct connection_vfuncs *v_c, + unsigned int iter_count) +{ + int fds[2]; + + struct ioloop *loop = io_loop_create(); + struct connection_list *clients = connection_list_init(set_c, v_c); + struct connection_list *servers = connection_list_init(set_s, v_s); + struct connection *conn_c = i_new(struct connection, 1); + struct connection *conn_s = i_new(struct connection, 1); + + conn_s->ioloop = loop; + conn_c->ioloop = loop; + + for(unsigned int iters = 0; iters < iter_count; iters++) { + test_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); + + fd_set_nonblock(fds[0], TRUE); + fd_set_nonblock(fds[1], TRUE); + connection_init_server(servers, conn_s, "client", fds[1], fds[1]); + connection_init_client_fd(clients, conn_c, "server", fds[0], fds[0]); + + io_loop_run(loop); + + connection_deinit(conn_c); + connection_deinit(conn_s); + } + + i_free(conn_c); + i_free(conn_s); + + connection_list_deinit(&clients); + connection_list_deinit(&servers); + + io_loop_destroy(&loop); +} + +/* BEGIN SIMPLE TEST */ + +static void test_connection_simple_client_connected(struct connection *conn, bool success) +{ + if (conn->list->set.client) + o_stream_nsend_str(conn->output, "QUIT\n"); + test_assert(success); +}; + +static int +test_connection_simple_input_args(struct connection *conn, const char *const *args) +{ + if (strcmp(args[0], "QUIT") == 0) { + received_quit = TRUE; + connection_disconnect(conn); + return 0; + } + i_error("invalid input"); + return -1; +} + +static void test_connection_simple_destroy(struct connection *conn) +{ + io_loop_stop(conn->ioloop); + connection_disconnect(conn); +} + +static const struct connection_vfuncs simple_v = +{ + .client_connected = test_connection_simple_client_connected, + .input_args = test_connection_simple_input_args, + .destroy = test_connection_simple_destroy, +}; + +static void test_connection_simple(void) +{ + test_begin("connection simple"); + + test_connection_run(&server_set, &client_set, &simple_v, &simple_v, 10); + + test_assert(received_quit); + received_quit = FALSE; + + test_end(); +} + +/* BEGIN NO INPUT TEST */ + +static const struct connection_settings no_input_client_set = +{ + .service_name_in = "TEST-S", + .service_name_out = "TEST-C", + .major_version = 1, + .minor_version = 0, + .client = TRUE, + .input_max_size = 0, + .output_max_size = SIZE_MAX, +}; + +static const struct connection_settings no_input_server_set = +{ + .service_name_in = "TEST-C", + .service_name_out = "TEST-S", + .major_version = 1, + .minor_version = 0, + .client = FALSE, + .input_max_size = 0, + .output_max_size = SIZE_MAX, +}; + +static void +test_connection_no_input_input(struct connection *conn) +{ + const char *input; + struct istream *is = i_stream_create_fd(conn->fd_in, SIZE_MAX); + i_stream_set_blocking(is, FALSE); + while ((input = i_stream_read_next_line(is)) != NULL) { + const char *const *args = t_strsplit_tabescaped(input); + if (!conn->handshake_received) { + if (connection_handshake_args_default(conn, args) > -1) + conn->handshake_received = TRUE; + continue; + } + if (strcmp(args[0], "QUIT") == 0) { + received_quit = TRUE; + io_loop_stop(conn->ioloop); + break; + } + } + i_stream_unref(&is); +} + +static const struct connection_vfuncs no_input_v = +{ + .client_connected = test_connection_simple_client_connected, + .input = test_connection_no_input_input, + .destroy = test_connection_simple_destroy, +}; + +static void test_connection_no_input(void) +{ + test_begin("connection no input stream"); + + test_connection_run(&no_input_server_set, &no_input_client_set, + &no_input_v, &no_input_v, 1); + + test_assert(received_quit); + received_quit = FALSE; + + test_end(); +} + +/* BEGIN HANDSHAKE TEST */ +static void test_connection_custom_handshake_client_connected(struct connection *conn, bool success) +{ + if (conn->list->set.client) + o_stream_nsend_str(conn->output, "HANDSHAKE\tFRIEND\n"); + test_assert(success); +}; + +static int test_connection_custom_handshake_args(struct connection *conn, + const char *const *args) +{ + if (!conn->version_received) { + if (connection_handshake_args_default(conn, args) < 0) + return -1; + return 0; + } + if (!conn->handshake_received) { + if (strcmp(args[0], "HANDSHAKE") == 0 && + strcmp(args[1], "FRIEND") == 0) { + if (!conn->list->set.client) + o_stream_nsend_str(conn->output, "HANDSHAKE\tFRIEND\n"); + else + o_stream_nsend_str(conn->output, "QUIT\n"); + return 1; + } + return -1; + } + return 1; +} + +static const struct connection_vfuncs custom_handshake_v = +{ + .client_connected = test_connection_custom_handshake_client_connected, + .input_args = test_connection_simple_input_args, + .handshake_args = test_connection_custom_handshake_args, + .destroy = test_connection_simple_destroy, +}; + +static void test_connection_custom_handshake(void) +{ + test_begin("connection custom handshake"); + + test_connection_run(&server_set, &client_set, &custom_handshake_v, + &custom_handshake_v, 10); + + test_assert(received_quit); + received_quit = FALSE; + + test_end(); +} + +/* BEGIN PING PONG TEST */ + +static int test_connection_ping_pong_input_args(struct connection *conn, const char *const *args) +{ + unsigned int n; + test_assert(args[0] != NULL && args[1] != NULL); + if (args[0] == NULL || args[1] == NULL) + return -1; + if (str_to_uint(args[1], &n) < 0) + return -1; + if (n > 10) + o_stream_nsend_str(conn->output, "QUIT\t0\n"); + else if (strcmp(args[0], "QUIT") == 0) + connection_disconnect(conn); + else if (strcmp(args[0], "PING") == 0) { + received_count++; + o_stream_nsend_str(conn->output, t_strdup_printf("PONG\t%u\n", n+1)); + } else if (strcmp(args[0], "PONG") == 0) + o_stream_nsend_str(conn->output, t_strdup_printf("PING\t%u\n", n)); + else + return -1; + return 1; +} + +static void test_connection_ping_pong_client_connected(struct connection *conn, bool success) +{ + o_stream_nsend_str(conn->output, "PING\t1\n"); + test_assert(success); +}; + +static const struct connection_vfuncs ping_pong_v = +{ + .client_connected = test_connection_ping_pong_client_connected, + .input_args = test_connection_ping_pong_input_args, + .destroy = test_connection_simple_destroy, +}; + +static void test_connection_ping_pong(void) +{ + test_begin("connection ping pong"); + + test_connection_run(&server_set, &client_set, &ping_pong_v, + &ping_pong_v, 10); + + test_assert(received_count == 100); + + test_end(); +} + +/* BEGIN INPUT FULL TEST */ + +static const struct connection_settings input_full_client_set = +{ + .service_name_in = "TEST-S", + .service_name_out = "TEST-C", + .major_version = 1, + .minor_version = 0, + .client = TRUE, + .input_max_size = 100, + .output_max_size = SIZE_MAX, +}; + +static int test_connection_input_full_input_args(struct connection *conn, + const char *const *args ATTR_UNUSED) +{ + /* send a long line */ + for (unsigned int i = 0; i < 200; i++) + o_stream_nsend(conn->output, "c", 1); + return 1; +} + +static void test_connection_input_full_destroy(struct connection *conn) +{ + test_assert(conn->disconnect_reason == CONNECTION_DISCONNECT_BUFFER_FULL || + conn->list->set.client == FALSE); + test_connection_simple_destroy(conn); +} + +static const struct connection_vfuncs input_full_v = +{ + .client_connected = test_connection_simple_client_connected, + .input_args = test_connection_input_full_input_args, + .destroy = test_connection_input_full_destroy, +}; + +static void test_connection_input_full(void) +{ + test_begin("connection input full"); + + test_connection_run(&server_set, &input_full_client_set, &input_full_v, + &simple_v, 10); + test_end(); +} + +/* BEGIN RESUME TEST */ +static struct timeout *to_send_quit = NULL; +static struct timeout *to_resume = NULL; + +static void test_connection_resume_client_connected(struct connection *conn, bool success) +{ + test_assert(success); + o_stream_nsend_str(conn->output, "BEGIN\n"); +} + +static void test_connection_resume_continue(struct connection *conn) +{ + timeout_remove(&to_resume); + /* ensure QUIT wasn't received early */ + was_resumed = !received_quit; + connection_input_resume(conn); +} + +static void test_connection_resume_send_quit(struct connection *conn) +{ + timeout_remove(&to_send_quit); + o_stream_nsend_str(conn->output, "QUIT\n"); +} + +static int test_connection_resume_input_args(struct connection *conn, + const char *const *args) +{ + test_assert(args[0] != NULL); + if (args[0] == NULL) + return -1; + + if (strcmp(args[0], "BEGIN") == 0) { + o_stream_nsend_str(conn->output, "HALT\n"); + to_send_quit = timeout_add_short(10, test_connection_resume_send_quit, conn); + } else if (strcmp(args[0], "HALT") == 0) { + connection_input_halt(conn); + to_resume = timeout_add_short(100, test_connection_resume_continue, conn); + } else if (strcmp(args[0], "QUIT") == 0) { + received_quit = TRUE; + connection_disconnect(conn); + } + + return 1; +} + +static const struct connection_vfuncs resume_v = +{ + .client_connected = test_connection_resume_client_connected, + .input_args = test_connection_resume_input_args, + .destroy = test_connection_simple_destroy, +}; + +static void test_connection_resume(void) +{ + test_begin("connection resume"); + + was_resumed = received_quit = FALSE; + test_connection_run(&server_set, &client_set, &resume_v, &resume_v, 1); + + test_assert(was_resumed); + test_assert(received_quit); + was_resumed = received_quit = FALSE; + + test_end(); +} + +/* BEGIN RESUME PIPELINED TEST */ +static int test_connection_resume_pipelined_input_args(struct connection *conn, + const char *const *args) +{ + test_assert(args[0] != NULL); + if (args[0] == NULL) + return -1; + + if (strcmp(args[0], "BEGIN") == 0) { + o_stream_nsend_str(conn->output, "HALT\nQUIT\n"); + } else if (strcmp(args[0], "HALT") == 0) { + connection_input_halt(conn); + to_resume = timeout_add_short(100, test_connection_resume_continue, conn); + return 0; + } else if (strcmp(args[0], "QUIT") == 0) { + received_quit = TRUE; + connection_disconnect(conn); + } + + return 1; +} + +static const struct connection_vfuncs resume_pipelined_v = +{ + .client_connected = test_connection_resume_client_connected, + .input_args = test_connection_resume_pipelined_input_args, + .destroy = test_connection_simple_destroy, +}; + +static void test_connection_resume_pipelined(void) +{ + test_begin("connection resume pipelined"); + + was_resumed = received_quit = FALSE; + test_connection_run(&server_set, &client_set, + &resume_pipelined_v, &resume_pipelined_v, 1); + + test_assert(was_resumed); + test_assert(received_quit); + was_resumed = received_quit = FALSE; + + test_end(); +} + +/* BEGIN IDLE KILL TEST */ + +static void +test_connection_idle_kill_client_connected(struct connection *conn ATTR_UNUSED, + bool success) +{ + test_assert(success); +}; + +static const struct connection_settings idle_kill_server_set = +{ + .service_name_in = "TEST-C", + .service_name_out = "TEST-S", + .major_version = 1, + .minor_version = 0, + .client = FALSE, + .input_max_size = SIZE_MAX, + .output_max_size = SIZE_MAX, + .input_idle_timeout_secs = 1, +}; + +static void test_connection_idle_kill_timeout(struct connection *conn) +{ + was_idle_killed = TRUE; + o_stream_nsend_str(conn->output, "QUIT\n"); +} + +static const struct connection_vfuncs idle_kill_v = +{ + .client_connected = test_connection_idle_kill_client_connected, + .input_args = test_connection_simple_input_args, + .destroy = test_connection_simple_destroy, + .idle_timeout = test_connection_idle_kill_timeout, +}; + +static void test_connection_idle_kill(void) +{ + test_begin("connection idle kill"); + + was_idle_killed = received_quit = FALSE; + test_connection_run(&idle_kill_server_set, &client_set, &idle_kill_v, + &idle_kill_v, 1); + + test_assert(received_quit); + test_assert(was_idle_killed); + was_idle_killed = received_quit = FALSE; + + test_end(); +} + +/* BEGIN HANDSHAKE FAILED TEST (version) */ + +static void test_connection_handshake_failed_destroy(struct connection *conn) +{ + test_assert(conn->disconnect_reason == CONNECTION_DISCONNECT_HANDSHAKE_FAILED); + test_connection_simple_destroy(conn); +} + +static const struct connection_vfuncs handshake_failed_version_v = +{ + .client_connected = test_connection_simple_client_connected, + .input_args = test_connection_simple_input_args, + .destroy = test_connection_handshake_failed_destroy, +}; + +static void test_connection_handshake_failed_version(void) +{ + static const struct connection_settings client_sets[] = { + { + .service_name_in = "TEST-S", + .service_name_out = "TEST-S", + .major_version = 1, + .minor_version = 0, + .client = TRUE, + .input_max_size = SIZE_MAX, + .output_max_size = SIZE_MAX, + }, + { + .service_name_in = "TEST-C", + .service_name_out = "TEST-C", + .major_version = 1, + .minor_version = 0, + .client = TRUE, + .input_max_size = SIZE_MAX, + .output_max_size = SIZE_MAX, + }, + { + .service_name_in = "TEST-S", + .service_name_out = "TEST-C", + .major_version = 2, + .minor_version = 0, + .client = TRUE, + .input_max_size = SIZE_MAX, + .output_max_size = SIZE_MAX, + } + }; + + static const struct connection_settings client_set_minor = { + .service_name_in = "TEST-S", + .service_name_out = "TEST-C", + .major_version = 1, + .minor_version = 2, + .client = TRUE, + .input_max_size = SIZE_MAX, + .output_max_size = SIZE_MAX, + }; + + test_begin("connection handshake failed (version)"); + + test_expect_errors(N_ELEMENTS(client_sets)); + + /* this should stay FALSE during the version mismatch sets */ + received_quit = FALSE; + for (size_t i = 0; i < N_ELEMENTS(client_sets); i++) { + test_connection_run(&server_set, &client_sets[i], &simple_v, + &handshake_failed_version_v, 1); + test_assert(!received_quit); + } + + received_quit = FALSE; + test_connection_run(&server_set, &client_set_minor, &simple_v, + &simple_v, 1); + test_assert(received_quit); + received_quit = FALSE; + + test_end(); +} + +/* BEGIN HANDSHAKE FAILED TEST (args) */ + +static int test_connection_handshake_failed_1_args(struct connection *conn ATTR_UNUSED, + const char *const *args ATTR_UNUSED) +{ + /* just fail */ + return -1; +} + +static const struct connection_vfuncs handshake_failed_1_v = +{ + .client_connected = test_connection_simple_client_connected, + .input_args = test_connection_simple_input_args, + .handshake_args = test_connection_handshake_failed_1_args, + .destroy = test_connection_handshake_failed_destroy, +}; + +static void test_connection_handshake_failed_args(void) +{ + test_begin("connection handshake failed (handshake_args)"); + + test_connection_run(&server_set, &client_set, &simple_v, + &handshake_failed_1_v, 10); + + test_end(); +} + +/* BEGIN HANDSHAKE FAILED TEST (handshake_line) */ + +static int test_connection_handshake_failed_2_line(struct connection *conn ATTR_UNUSED, + const char *line ATTR_UNUSED) +{ + return -1; +} + +static const struct connection_vfuncs handshake_failed_2_v = +{ + .client_connected = test_connection_simple_client_connected, + .input_args = test_connection_simple_input_args, + .handshake_line = test_connection_handshake_failed_2_line, + .destroy = test_connection_handshake_failed_destroy, +}; + +static void test_connection_handshake_failed_line(void) +{ + test_begin("connection handshake failed (handshake_line)"); + + test_connection_run(&server_set, &client_set, &simple_v, + &handshake_failed_2_v, 10); + + test_end(); +} + +/* BEGIN HANDSHAKE FAILED TEST (handshake) */ + +static int test_connection_handshake_failed_3(struct connection *conn ATTR_UNUSED) +{ + return -1; +} + +static const struct connection_vfuncs handshake_failed_3_v = +{ + .client_connected = test_connection_simple_client_connected, + .input_args = test_connection_simple_input_args, + .handshake = test_connection_handshake_failed_3, + .destroy = test_connection_handshake_failed_destroy, +}; + +static void test_connection_handshake_failed_input(void) +{ + test_begin("connection handshake failed (handshake)"); + + test_connection_run(&server_set, &client_set, &simple_v, + &handshake_failed_3_v, 10); + + test_end(); +} + +/* BEGIN CONNECTION ERRORED TEST (ensure correct error) */ + +static void test_connection_errored_client_connected(struct connection *conn, + bool success) +{ + test_assert(success); + o_stream_nsend_str(conn->output, "HELLO\n"); +} + +static void test_connection_errored_destroy(struct connection *conn) +{ + test_assert(conn->disconnect_reason == CONNECTION_DISCONNECT_DEINIT); + test_connection_simple_destroy(conn); +} + +static int test_connection_errored_input_line(struct connection *conn ATTR_UNUSED, + const char *line) +{ + if (str_begins(line, "VERSION")) + return 1; + return -1; +} + +static const struct connection_vfuncs test_connection_errored_1_v = +{ + .client_connected = test_connection_errored_client_connected, + .input_line = test_connection_errored_input_line, + .destroy = test_connection_errored_destroy, +}; + +static void test_connection_input_error_reason(void) +{ + test_begin("connection input error (correct disconnect reason)"); + + test_connection_run(&server_set, &client_set, &test_connection_errored_1_v, + &test_connection_errored_1_v, 10); + + test_end(); +} + +/* END CONNECTION ERRORED TEST */ + +/* BEGIN NO VERSION TEST */ + +static const struct connection_settings no_version_client_set = +{ + .major_version = 0, + .minor_version = 0, + .client = TRUE, + .input_max_size = SIZE_MAX, + .output_max_size = SIZE_MAX, + .dont_send_version = TRUE, +}; + +static const struct connection_settings no_version_server_set = +{ + .major_version = 0, + .minor_version = 0, + .input_max_size = SIZE_MAX, + .output_max_size = SIZE_MAX, + .dont_send_version = TRUE, +}; + +static void test_connection_no_version(void) +{ + test_begin("connection no version sent"); + + test_connection_run(&no_version_server_set, &no_version_client_set, + &simple_v, &simple_v, 10); + + test_end(); +} + +/* END NO VERSION TEST */ + +void test_connection(void) +{ + test_connection_simple(); + test_connection_no_input(); + test_connection_custom_handshake(); + test_connection_ping_pong(); + test_connection_input_full(); + test_connection_resume(); + test_connection_resume_pipelined(); + test_connection_idle_kill(); + test_connection_handshake_failed_version(); + test_connection_handshake_failed_args(); + test_connection_handshake_failed_line(); + test_connection_handshake_failed_input(); + test_connection_input_error_reason(); + test_connection_no_version(); +} diff --git a/src/lib/test-cpu-limit.c b/src/lib/test-cpu-limit.c new file mode 100644 index 0000000..c4c1090 --- /dev/null +++ b/src/lib/test-cpu-limit.c @@ -0,0 +1,145 @@ +#include "test-lib.h" +#include "lib-signals.h" +#include "guid.h" +#include "time-util.h" +#include "cpu-limit.h" + +#include <unistd.h> +#include <fcntl.h> +#include <sys/time.h> +#include <sys/resource.h> + +/* The CPU limits aren't exact. Allow this much leniency in the time + comparisons. Note that system CPU usage can grow very large on loaded + systems, so we're not checking its upper limit at all. */ +#define ALLOW_MSECS_BELOW 500 +#define ALLOW_MSECS_ABOVE 3000 + +static const char *const test_path = ".test.cpulimit"; + +static struct timeval get_cpu_time(enum cpu_limit_type type) +{ + struct rusage rusage; + struct timeval cpu_usage = { 0, 0 }; + + /* Query cpu usage so far */ + if (getrusage(RUSAGE_SELF, &rusage) < 0) + i_fatal("getrusage() failed: %m"); + if ((type & CPU_LIMIT_TYPE_USER) != 0) + timeval_add(&cpu_usage, &rusage.ru_utime); + if ((type & CPU_LIMIT_TYPE_SYSTEM) != 0) + timeval_add(&cpu_usage, &rusage.ru_stime); + return cpu_usage; +} + +static void test_cpu_loop_once(void) +{ + guid_128_t guid; + + /* consume some user CPU */ + for (unsigned int i = 0; i < 10000; i++) + guid_128_generate(guid); + /* consume some system CPU */ + int fd = creat(test_path, 0600); + if (fd == -1) + i_fatal("creat(%s) failed: %m", test_path); + if (write(fd, guid, sizeof(guid)) < 0) + i_fatal("write(%s) failed: %m", test_path); + i_close_fd(&fd); +} + +static void +test_cpu_limit_simple(enum cpu_limit_type type, const char *type_str) +{ + struct cpu_limit *climit; + struct timeval usage, cpu; + int diff_msecs; + + test_begin(t_strdup_printf("cpu limit - simple (%s)", type_str)); + + lib_signals_init(); + climit = cpu_limit_init(2, type); + usage = get_cpu_time(type); + + while (!cpu_limit_exceeded(climit)) + test_cpu_loop_once(); + + cpu_limit_deinit(&climit); + cpu = get_cpu_time(type); + diff_msecs = timeval_diff_msecs(&cpu, &usage); + test_assert_cmp(diff_msecs, >=, 2000 - ALLOW_MSECS_BELOW); + + lib_signals_deinit(); + test_end(); +} + +static void test_cpu_limit_nested(enum cpu_limit_type type, const char *type_str) +{ + struct cpu_limit *climit1, *climit2; + struct timeval usage1, cpu; + unsigned int n; + int diff_msecs; + + test_begin(t_strdup_printf("cpu limit - nested (%s)", type_str)); + + lib_signals_init(); + climit1 = cpu_limit_init(3, type); + usage1 = get_cpu_time(type); + + while (!cpu_limit_exceeded(climit1) && !test_has_failed()) { + climit2 = cpu_limit_init(1, type); + + while (!cpu_limit_exceeded(climit2) && !test_has_failed()) + test_cpu_loop_once(); + + cpu_limit_deinit(&climit2); + } + + cpu_limit_deinit(&climit1); + cpu = get_cpu_time(type); + diff_msecs = timeval_diff_msecs(&cpu, &usage1); + test_assert_cmp(diff_msecs, >=, 3000 - ALLOW_MSECS_BELOW); + + lib_signals_deinit(); + test_end(); + + test_begin(t_strdup_printf("cpu limit - nested2 (%s)", type_str)); + + lib_signals_init(); + climit1 = cpu_limit_init(3, type); + usage1 = get_cpu_time(type); + + n = 0; + while (!cpu_limit_exceeded(climit1) && !test_has_failed()) { + if (++n >= 3) { + /* Consume last second in top cpu limit */ + test_cpu_loop_once(); + continue; + } + climit2 = cpu_limit_init(1, type); + + while (!cpu_limit_exceeded(climit2) && !test_has_failed()) + test_cpu_loop_once(); + + cpu_limit_deinit(&climit2); + } + + cpu_limit_deinit(&climit1); + cpu = get_cpu_time(type); + diff_msecs = timeval_diff_msecs(&cpu, &usage1); + test_assert_cmp(diff_msecs, >=, 3000 - ALLOW_MSECS_BELOW); + + i_unlink_if_exists(test_path); + lib_signals_deinit(); + test_end(); +} + +void test_cpu_limit(void) +{ + test_cpu_limit_simple(CPU_LIMIT_TYPE_USER, "user"); + test_cpu_limit_simple(CPU_LIMIT_TYPE_SYSTEM, "system"); + test_cpu_limit_simple(CPU_LIMIT_TYPE_ALL, "all"); + test_cpu_limit_nested(CPU_LIMIT_TYPE_USER, "user"); + test_cpu_limit_nested(CPU_LIMIT_TYPE_SYSTEM, "system"); + test_cpu_limit_nested(CPU_LIMIT_TYPE_ALL, "all"); +} diff --git a/src/lib/test-crc32.c b/src/lib/test-crc32.c new file mode 100644 index 0000000..3292d1c --- /dev/null +++ b/src/lib/test-crc32.c @@ -0,0 +1,14 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "crc32.h" + +void test_crc32(void) +{ + const char str[] = "foo\0bar"; + + test_begin("crc32"); + test_assert(crc32_str(str) == 0x8c736521); + test_assert(crc32_data(str, sizeof(str)) == 0x32c9723d); + test_end(); +} diff --git a/src/lib/test-data-stack.c b/src/lib/test-data-stack.c new file mode 100644 index 0000000..c38da60 --- /dev/null +++ b/src/lib/test-data-stack.c @@ -0,0 +1,455 @@ +/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "lib-event-private.h" +#include "event-filter.h" +#include "data-stack.h" + +static int ds_grow_event_count = 0; + +static bool +test_ds_grow_event_callback(struct event *event, + enum event_callback_type type, + struct failure_context *ctx, + const char *fmt ATTR_UNUSED, + va_list args ATTR_UNUSED) +{ + const struct event_field *field; + + if (type != EVENT_CALLBACK_TYPE_SEND) + return TRUE; + + ds_grow_event_count++; + test_assert(ctx->type == LOG_TYPE_DEBUG); + + field = event_find_field_nonrecursive(event, "alloc_size"); + test_assert(field != NULL && + field->value_type == EVENT_FIELD_VALUE_TYPE_INTMAX && + field->value.intmax >= 1024 * (5 + 100)); + field = event_find_field_nonrecursive(event, "used_size"); + test_assert(field != NULL && + field->value_type == EVENT_FIELD_VALUE_TYPE_INTMAX && + field->value.intmax >= 1024 * (5 + 100)); + field = event_find_field_nonrecursive(event, "last_alloc_size"); + test_assert(field != NULL && + field->value_type == EVENT_FIELD_VALUE_TYPE_INTMAX && + field->value.intmax >= 1024 * 100); + field = event_find_field_nonrecursive(event, "frame_marker"); + test_assert(field != NULL && + field->value_type == EVENT_FIELD_VALUE_TYPE_STR && + strstr(field->value.str, "data-stack.c") != NULL); + return TRUE; +} + +static void test_ds_grow_event(void) +{ + const char *error; + + test_begin("data-stack grow event"); + event_register_callback(test_ds_grow_event_callback); + + i_assert(event_get_global_debug_log_filter() == NULL); + struct event_filter *filter = event_filter_create(); + test_assert(event_filter_parse("event=data_stack_grow", filter, &error) == 0); + event_set_global_debug_log_filter(filter); + event_filter_unref(&filter); + + /* make sure the test won't fail due to earlier data stack + allocations. */ + data_stack_free_unused(); + T_BEGIN { + (void)t_malloc0(1024*5); + test_assert(ds_grow_event_count == 0); + (void)t_malloc0(1024*100); + test_assert(ds_grow_event_count == 1); + } T_END; + event_unset_global_debug_log_filter(); + event_unregister_callback(test_ds_grow_event_callback); + test_end(); +} + +static void test_ds_get_used_size(void) +{ + test_begin("data-stack data_stack_get_used_size()"); + size_t size1 = data_stack_get_used_size(); + (void)t_malloc0(500); + size_t size2 = data_stack_get_used_size(); + test_assert(size1 + 500 <= size2); + + T_BEGIN { + (void)t_malloc0(300); + size_t sub_size1 = data_stack_get_used_size(); + T_BEGIN { + (void)t_malloc0(300); + } T_END; + test_assert_cmp(sub_size1, ==, data_stack_get_used_size()); + } T_END; + test_assert_cmp(size2, ==, data_stack_get_used_size()); + test_end(); +} + +static void test_ds_get_bytes_available(void) +{ + test_begin("data-stack t_get_bytes_available()"); + for (unsigned int i = 0; i < 32; i++) { + size_t orig_avail = t_get_bytes_available(); + size_t avail1; + T_BEGIN { + if (i > 0) + t_malloc_no0(i); + avail1 = t_get_bytes_available(); + t_malloc_no0(avail1); + test_assert_idx(t_get_bytes_available() == 0, i); + t_malloc_no0(1); + test_assert_idx(t_get_bytes_available() > 0, i); + } T_END; + T_BEGIN { + if (i > 0) + t_malloc_no0(i); + size_t avail2 = t_get_bytes_available(); + test_assert_idx(avail1 == avail2, i); + t_malloc_no0(avail2 + 1); + test_assert_idx(t_get_bytes_available() > 0, i); + } T_END; + test_assert_idx(t_get_bytes_available() == orig_avail, i); + } + test_end(); +} + +static void ATTR_FORMAT(2, 0) +test_ds_growing_debug(const struct failure_context *ctx ATTR_UNUSED, + const char *format, va_list args) +{ + ds_grow_event_count++; + (void)t_strdup_vprintf(format, args); +} + +static void test_ds_grow_in_event(void) +{ + size_t i, alloc1 = 8096; + unsigned char *buf; + const char *error; + + test_begin("data-stack grow in event"); + + struct event_filter *filter = event_filter_create(); + event_set_global_debug_log_filter(filter); + test_assert(event_filter_parse("event=data_stack_grow", filter, &error) == 0); + event_filter_unref(&filter); + + i_set_debug_handler(test_ds_growing_debug); + buf = t_buffer_get(alloc1); + for (i = 0; i < alloc1; i++) + buf[i] = i & 0xff; + + test_assert(ds_grow_event_count == 0); + buf = t_buffer_reget(buf, 65536); + test_assert(ds_grow_event_count == 1); + for (i = 0; i < alloc1; i++) { + if (buf[i] != (unsigned char)i) + break; + } + test_assert(i == alloc1); + + i_set_debug_handler(default_error_handler); + event_unset_global_debug_log_filter(); + test_end(); +} + +static void test_ds_buffers(void) +{ + test_begin("data-stack buffer growth"); + T_BEGIN { + size_t i; + unsigned char *p; + size_t left = t_get_bytes_available(); + while (left < 10000) { + t_malloc_no0(left+1); /* force a new block */ + left = t_get_bytes_available(); + } + left -= 64; /* make room for the sentry if DEBUG */ + p = t_buffer_get(1); + p[0] = 1; + for (i = 2; i <= left; i++) { + /* grow it */ + unsigned char *p2 = t_buffer_get(i); + test_assert_idx(p == p2, i); + p[i-1] = i & 0xff; + test_assert_idx(p[i-2] == (unsigned char)(i-1), i); + } + /* now fix it permanently */ + t_buffer_alloc_last_full(); + test_assert(t_get_bytes_available() < 64 + MEM_ALIGN(1)); + } T_END; + test_end(); + + test_begin("data-stack buffer interruption"); + T_BEGIN { + void *b = t_buffer_get(1000); + void *a = t_malloc_no0(1); + void *b2 = t_buffer_get(1001); + test_assert(a == b); /* expected, not guaranteed */ + test_assert(b2 != b); + } T_END; + test_end(); + + test_begin("data-stack buffer with reallocs"); + T_BEGIN { + size_t bigleft = t_get_bytes_available(); + size_t i; + /* with DEBUG: the stack frame allocation takes 96 bytes + and malloc takes extra 40 bytes + alignment, so don't let + "i" be too high. */ + for (i = 1; i < bigleft-96-40-16; i += i_rand_limit(32)) T_BEGIN { + unsigned char *p, *p2; + size_t left; + t_malloc_no0(i); + left = t_get_bytes_available(); + /* The most useful idx for the assert is 'left' */ + test_assert_idx(left <= bigleft-i, left); + p = t_buffer_get(left/2); + p[0] = 'Z'; p[left/2 - 1] = 'Z'; + p2 = t_buffer_get(left + left/2); + test_assert_idx(p != p2, left); + test_assert_idx(p[0] == 'Z', left); + test_assert_idx(p[left/2 -1] == 'Z', left); + } T_END; + } T_END; + test_end(); +} + +static void test_ds_realloc() +{ + test_begin("data-stack realloc"); + T_BEGIN { + size_t i; + unsigned char *p; + size_t left = t_get_bytes_available(); + while (left < 10000) { + t_malloc_no0(left+1); /* force a new block */ + left = t_get_bytes_available(); + } + left -= 64; /* make room for the sentry if DEBUG */ + p = t_malloc_no0(1); + p[0] = 1; + for (i = 2; i <= left; i++) { + /* grow it */ + test_assert_idx(t_try_realloc(p, i), i); + p[i-1] = i & 0xff; + test_assert_idx(p[i-2] == (unsigned char)(i-1), i); + } + test_assert(t_get_bytes_available() < 64 + MEM_ALIGN(1)); + } T_END; + test_end(); +} + +static void test_ds_recurse(int depth, int number, size_t size) +{ + int i; + char **ps; + char tag[2] = { depth+1, '\0' }; + int try_fails = 0; + data_stack_frame_t t_id = t_push_named("test_ds_recurse[%i]", depth); + ps = t_buffer_get(sizeof(char *) * number); + i_assert(ps != NULL); + t_buffer_alloc(sizeof(char *) * number); + + for (i = 0; i < number; i++) { + ps[i] = t_malloc_no0(size/2); + bool re = t_try_realloc(ps[i], size); + i_assert(ps[i] != NULL); + if (!re) { + try_fails++; + ps[i] = t_malloc_no0(size); + } + /* drop our own canaries */ + memset(ps[i], tag[0], size); + ps[i][size-2] = 0; + } + /* Do not expect a high failure rate from t_try_realloc */ + test_assert_idx(try_fails <= number / 20, depth); + + /* Now recurse... */ + if(depth>0) + test_ds_recurse(depth-1, number, size); + + /* Test our canaries are still intact */ + for (i = 0; i < number; i++) { + test_assert_idx(strspn(ps[i], tag) == size - 2, i); + test_assert_idx(ps[i][size-1] == tag[0], i); + } + test_assert_idx(t_pop(&t_id), depth); +} + +static void test_ds_recursive(void) +{ + int count = 20, depth = 80; + int i; + + test_begin("data-stack recursive"); + size_t init_size = data_stack_get_used_size(); + for(i = 0; i < count; i++) T_BEGIN { + int number=i_rand_limit(100)+50; + int size=i_rand_limit(100)+50; + test_ds_recurse(depth, number, size); + } T_END; + test_assert_cmp(init_size, ==, data_stack_get_used_size()); + test_end(); +} + +static void test_ds_pass_str(void) +{ + data_stack_frame_t frames[32*2 + 1]; /* BLOCK_FRAME_COUNT*2 + 1 */ + const char *strings[N_ELEMENTS(frames)]; + + test_begin("data-stack pass string"); + for (unsigned int frame = 0; frame < N_ELEMENTS(frames); frame++) { + frames[frame] = t_push("test"); + if (frame % 10 == 5) { + /* increase block counts */ + (void)t_malloc_no0(1024*30); + (void)t_malloc_no0(1024*30); + } + strings[frame] = t_strdup_printf("frame %d", frame); + for (unsigned int i = 0; i <= frame; i++) { + test_assert_idx(data_stack_frame_contains(&frames[frame], strings[i]) == (i == frame), + frame * 100 + i); + } + } + + const char *last_str = strings[N_ELEMENTS(frames)-1]; + for (unsigned int frame = N_ELEMENTS(frames); frame > 0; ) { + frame--; + test_assert(t_pop_pass_str(&frames[frame], &last_str)); + } + test_assert_strcmp(last_str, "frame 64"); + + /* make sure the pass_condition works properly */ + const char *error, *orig_error, *orig2_error; + T_BEGIN { + (void)t_strdup("qwertyuiop"); + error = orig_error = t_strdup("123456"); + } T_END_PASS_STR_IF(TRUE, &error); + + orig2_error = orig_error; + T_BEGIN { + (void)t_strdup("abcdefghijklmnopqrstuvwxyz"); + } T_END_PASS_STR_IF(FALSE, &orig2_error); + /* orig_error and orig2_error both point to freed data stack frame */ + test_assert(orig_error == orig2_error); + /* the passed error is still valid though */ + test_assert_strcmp(error, "123456"); + + test_end(); +} + +void test_data_stack(void) +{ + void (*tests[])(void) = { + test_ds_grow_event, + test_ds_get_used_size, + test_ds_get_bytes_available, + test_ds_grow_in_event, + test_ds_buffers, + test_ds_realloc, + test_ds_recursive, + test_ds_pass_str, + }; + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { + ds_grow_event_count = 0; + data_stack_free_unused(); + T_BEGIN { + tests[i](); + } T_END; + } +} + +enum fatal_test_state fatal_data_stack(unsigned int stage) +{ +#ifdef DEBUG +#define NONEXISTENT_STACK_FRAME_ID (data_stack_frame_t)999999999 + /* If we abort, then we'll be left with a dangling t_push() + keep a record of our temporary stack id, so we can clean up. */ + static data_stack_frame_t t_id = NONEXISTENT_STACK_FRAME_ID; + static unsigned char *undo_ptr = NULL; + static unsigned char undo_data; + static bool things_are_messed_up = FALSE; + if (stage != 0) { + /* Presume that we need to clean up from the prior test: + undo the evil write, then we will be able to t_pop cleanly, + and finally we can end the test stanza. */ + if (things_are_messed_up || undo_ptr == NULL) + return FATAL_TEST_ABORT; /* abort, things are messed up with t_pop */ + *undo_ptr = undo_data; + undo_ptr = NULL; + /* t_pop mustn't abort, that would cause recursion */ + things_are_messed_up = TRUE; + if (t_id != NONEXISTENT_STACK_FRAME_ID && !t_pop(&t_id)) + return FATAL_TEST_ABORT; /* abort, things are messed up with us */ + things_are_messed_up = FALSE; + t_id = NONEXISTENT_STACK_FRAME_ID; + test_end(); + } + + switch(stage) { + case 0: { + unsigned char *p; + test_begin("fatal data-stack underrun"); + t_id = t_push_named("fatal_data_stack underrun"); + size_t left = t_get_bytes_available(); + p = t_malloc_no0(left-80); /* will fit */ + p = t_malloc_no0(100); /* won't fit, will get new block */ + int seek = 0; + /* Seek back for the canary, don't assume endianness */ + while(seek > -60 && + ((p[seek+1] != 0xDB) || + ((p[seek] != 0xBA || p[seek+2] != 0xAD) && + (p[seek+2] != 0xBA || p[seek] != 0xAD)))) + seek--; + if (seek <= -60) + return FATAL_TEST_ABORT; /* abort, couldn't find header */ + undo_ptr = p + seek; + undo_data = *undo_ptr; + *undo_ptr = '*'; + /* t_malloc_no0 will panic block header corruption */ + test_expect_fatal_string("Corrupted data stack canary"); + (void)t_malloc_no0(10); + return FATAL_TEST_FAILURE; + } + + case 1: case 2: { + test_begin(stage == 1 ? "fatal t_malloc_no0 overrun near" : "fatal t_malloc_no0 overrun far"); + t_id = t_push_named(stage == 1 ? "fatal t_malloc_no0 overrun first" : "fatal t_malloc_no0 overrun far"); + unsigned char *p = t_malloc_no0(10); + undo_ptr = p + 10 + (stage == 1 ? 0 : 8*4-1); /* presumes sentry size */ + undo_data = *undo_ptr; + *undo_ptr = '*'; + /* t_pop will now fail */ + test_expect_fatal_string("buffer overflow"); + (void)t_pop(&t_id); + t_id = NONEXISTENT_STACK_FRAME_ID; /* We're FUBAR, mustn't pop next entry */ + return FATAL_TEST_FAILURE; + } + + case 3: case 4: { + test_begin(stage == 3 ? "fatal t_buffer_get overrun near" : "fatal t_buffer_get overrun far"); + t_id = t_push_named(stage == 3 ? "fatal t_buffer overrun near" : "fatal t_buffer_get overrun far"); + unsigned char *p = t_buffer_get(10); + undo_ptr = p + 10 + (stage == 3 ? 0 : 8*4-1); + undo_data = *undo_ptr; + *undo_ptr = '*'; + /* t_pop will now fail */ + test_expect_fatal_string("buffer overflow"); + (void)t_pop(&t_id); + t_id = NONEXISTENT_STACK_FRAME_ID; /* We're FUBAR, mustn't pop next entry */ + return FATAL_TEST_FAILURE; + } + + default: + things_are_messed_up = TRUE; + return FATAL_TEST_FINISHED; + } +#else + return stage == 0 ? FATAL_TEST_FINISHED : FATAL_TEST_ABORT; +#endif +} diff --git a/src/lib/test-env-util.c b/src/lib/test-env-util.c new file mode 100644 index 0000000..7a24615 --- /dev/null +++ b/src/lib/test-env-util.c @@ -0,0 +1,92 @@ +/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "env-util.h" + +void test_env_util(void) +{ + test_begin("env util"); + + env_put("ENVUTIL_BACKUP", "saved"); + struct env_backup *backup = env_backup_save(); + + /* test env_clean() */ + env_clean(); + char ***env = env_get_environ_p(); + test_assert(*env == NULL || **env == NULL); + test_assert(getenv("ENVUTIL_BACKUP") == NULL); + + /* test env_put_array() */ + const char *add_env[] = { "a=1", "b=1", "c=1", "d=1", NULL }; + env_put_array(add_env); + test_assert_strcmp(getenv("a"), "1"); + test_assert_strcmp(getenv("b"), "1"); + test_assert_strcmp(getenv("c"), "1"); + test_assert_strcmp(getenv("d"), "1"); + test_assert(getenv("e") == NULL); + const char *add_env2[] = { "b=", "e=2", NULL }; + env_put_array(add_env2); + test_assert_strcmp(getenv("a"), "1"); + test_assert_strcmp(getenv("b"), ""); + test_assert_strcmp(getenv("c"), "1"); + test_assert_strcmp(getenv("d"), "1"); + test_assert_strcmp(getenv("e"), "2"); + + /* test env_clean_except() */ + const char *preserve_env[] = { "a", "c", NULL }; + env_clean_except(preserve_env); + test_assert_strcmp(getenv("a"), "1"); + test_assert(getenv("b") == NULL); + test_assert_strcmp(getenv("c"), "1"); + test_assert(getenv("d") == NULL); + test_assert(*env != NULL && + (null_strcmp((*env)[0], "a=1") == 0 || + null_strcmp((*env)[0], "c=1") == 0)); + test_assert(*env != NULL && + (null_strcmp((*env)[1], "a=1") == 0 || + null_strcmp((*env)[1], "c=1") == 0)); + + /* test env_remove() */ + env_remove("a"); + test_assert(getenv("a") == NULL); + test_assert(getenv("c") != NULL); + env_remove("a"); + test_assert(getenv("a") == NULL); + test_assert(getenv("c") != NULL); + env_remove("c"); + test_assert(getenv("c") == NULL); + test_assert(*env == NULL || **env == NULL); + + /* test restoring */ + env_backup_restore(backup); + test_assert_strcmp(getenv("ENVUTIL_BACKUP"), "saved"); + env_put("ENVUTIL_BACKUP", "overwrite"); + test_assert_strcmp(getenv("ENVUTIL_BACKUP"), "overwrite"); + + /* test restoring again */ + env_backup_restore(backup); + test_assert_strcmp(getenv("ENVUTIL_BACKUP"), "saved"); + env_backup_free(&backup); + + test_end(); +} + +enum fatal_test_state fatal_env_util(unsigned int stage) +{ + switch (stage) { + case 0: + test_begin("env util fatals"); + + test_expect_fatal_string("strchr(name, '=') == NULL"); + env_put("key=bad", "value"); + return FATAL_TEST_FAILURE; + case 1: + test_expect_fatal_string("value != NULL"); + const char *const envs[] = { "key", NULL }; + env_put_array(envs); + return FATAL_TEST_FAILURE; + default: + test_end(); + return FATAL_TEST_FINISHED; + } +} diff --git a/src/lib/test-event-category-register.c b/src/lib/test-event-category-register.c new file mode 100644 index 0000000..50cc038 --- /dev/null +++ b/src/lib/test-event-category-register.c @@ -0,0 +1,320 @@ +/* Copyright (c) 2019 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "ioloop.h" +#include "time-util.h" +#include "lib-event-private.h" +#include "failures-private.h" + +/* we call a generic "unregister category function; to tell it what exact + * behavior it should expect from lib-lib, we pass in one of the following + * values + */ +enum unreg_expectation { + UNREG_NOT_LAST, + UNREG_LAST, + UNREG_NOP, +}; + +#define CAT_NAME_PREFIX "test-category" + +/* pointer to a category we expect to be registered/unregistered */ +static struct event_category *expected_callback_cat; +static bool callback_called; + +static struct event *dummy_event; + +static void check_category(struct event_category *cat) +{ + callback_called = TRUE; + + /* lib-lib called a callback with a NULL? (useless and a bug) */ + test_assert(cat != NULL); + + /* callback called, but didn't expect to be called? */ + test_assert(expected_callback_cat != NULL); + + /* test_assert() doesn't terminate, so avoid NULL ptr derefs later on */ + if ((cat == NULL) || (expected_callback_cat == NULL)) + return; + + /* check that the categories have the same values */ + test_assert(strcmp(cat->name, expected_callback_cat->name) == 0); + test_assert(cat->internal == expected_callback_cat->internal); +} + +static void check_cat_registered(const char *name, bool should_exist) +{ + struct event_category *cat; + + callback_called = FALSE; + cat = event_category_find_registered(name); + test_assert(callback_called == FALSE); + + test_assert((cat != NULL) == should_exist); +} + +static void register_cat(struct event_category *newcat, + struct event_category *expcat) +{ + /* start with a known state - no regs expected */ + expected_callback_cat = NULL; + callback_called = FALSE; + + dummy_event = event_create(NULL); + test_assert(callback_called == FALSE); + + /* we expect a registration only when adding a cat */ + expected_callback_cat = (expcat); + event_add_category(dummy_event, (newcat)); + expected_callback_cat = NULL; + + /* check that all went well */ + test_assert(callback_called == (expcat != NULL)); + test_assert((newcat)->internal != NULL); + test_assert(event_category_find_registered((newcat)->name) != NULL); + + /* clean up */ + event_unref(&dummy_event); +} + +static void unregister_cat(struct event_category *cat, + enum unreg_expectation expectation) +{ + /* sanity check that cat is set up as expected */ + switch (expectation) { + case UNREG_NOT_LAST: + /* must be registered to unregister */ + test_assert(event_category_find_registered((cat)->name) != NULL); + expected_callback_cat = NULL; + break; + + case UNREG_LAST: + /* must be registered to unregister */ + test_assert(event_category_find_registered((cat)->name) != NULL); + expected_callback_cat = cat; + break; + + case UNREG_NOP: + /* must not be registered for no-op */ + /* event_category_find_registered(cat->name) should return + NULL, but since we don't actually unregister this lookup + would fail. Therefore, we skip it. */ + expected_callback_cat = NULL; + break; + } + + /* Note: We don't actually have a way to unregister categories. We + keep the above checks and the calls to this function as a form of + documentation of how unregistering should work. */ +} + +static void test_event_category_1ptr_null(void) +{ +#define CAT_NAME_0 CAT_NAME_PREFIX "-1ptr-null" + static struct event_category cat = { .name = CAT_NAME_0 }; + + test_begin("event category rereg: same ptr, NULL parent"); + + check_cat_registered(CAT_NAME_0, FALSE); + register_cat(&cat, &cat); + register_cat(&cat, NULL); + check_cat_registered(CAT_NAME_0, TRUE); + + unregister_cat(&cat, UNREG_LAST); + unregister_cat(&cat, UNREG_NOP); + + test_end(); +#undef CAT_NAME_0 +} + +static void test_event_category_1ptr_nonnull(void) +{ +#define CAT_NAME_0 CAT_NAME_PREFIX "-1ptr-nonnull-0" +#define CAT_NAME_1 CAT_NAME_PREFIX "-1ptr-nonnull-1" + static struct event_category cat = { .name = CAT_NAME_0 }; + static struct event_category cat_with_parent = { .name = CAT_NAME_1, .parent = &cat }; + + test_begin("event category rereg: same ptr, non-NULL parent"); + + check_cat_registered(CAT_NAME_0, FALSE); + check_cat_registered(CAT_NAME_1, FALSE); + register_cat(&cat, &cat); + register_cat(&cat_with_parent, &cat_with_parent); + register_cat(&cat_with_parent, NULL); + check_cat_registered(CAT_NAME_0, TRUE); + check_cat_registered(CAT_NAME_1, TRUE); + + unregister_cat(&cat_with_parent, UNREG_LAST); + unregister_cat(&cat_with_parent, UNREG_NOP); + /* NOTE: we must unreg children before parent cats */ + unregister_cat(&cat, UNREG_LAST); + unregister_cat(&cat, UNREG_NOP); + + test_end(); +#undef CAT_NAME_0 +#undef CAT_NAME_1 +} + +static void test_event_category_2ptr_null(void) +{ +#define CAT_NAME_0 CAT_NAME_PREFIX "-2ptr-null" + static struct event_category cat0 = { .name = CAT_NAME_0 }; + static struct event_category cat1 = { .name = CAT_NAME_0 }; + + test_begin("event category rereg: different ptr, NULL parent"); + + check_cat_registered(CAT_NAME_0, FALSE); + register_cat(&cat0, &cat0); + register_cat(&cat1, NULL); + check_cat_registered(CAT_NAME_0, TRUE); + + unregister_cat(&cat0, UNREG_NOT_LAST); + unregister_cat(&cat1, UNREG_LAST); + unregister_cat(&cat0, UNREG_NOP); + unregister_cat(&cat1, UNREG_NOP); + + test_end(); +#undef CAT_NAME_0 +} + +static void test_event_category_2ptr_nonnull_same(void) +{ +#define CAT_NAME_0 CAT_NAME_PREFIX "-2ptr-nonnull-same-0" +#define CAT_NAME_1 CAT_NAME_PREFIX "-2ptr-nonnull-same-1" + static struct event_category cat = { .name = CAT_NAME_0 }; + static struct event_category cat_with_parent0 = { .name = CAT_NAME_1, .parent = &cat }; + static struct event_category cat_with_parent1 = { .name = CAT_NAME_1, .parent = &cat }; + + test_begin("event category rereg: different ptr, same non-NULL parent"); + + check_cat_registered(CAT_NAME_0, FALSE); + check_cat_registered(CAT_NAME_1, FALSE); + register_cat(&cat, &cat); + register_cat(&cat_with_parent0, &cat_with_parent0); + register_cat(&cat_with_parent1, NULL); + check_cat_registered(CAT_NAME_0, TRUE); + check_cat_registered(CAT_NAME_1, TRUE); + + unregister_cat(&cat_with_parent0, UNREG_NOT_LAST); + unregister_cat(&cat_with_parent1, UNREG_LAST); + unregister_cat(&cat_with_parent0, UNREG_NOP); + unregister_cat(&cat_with_parent1, UNREG_NOP); + /* NOTE: we must unreg children before parent cats */ + unregister_cat(&cat, UNREG_LAST); + unregister_cat(&cat, UNREG_NOP); + + test_end(); +#undef CAT_NAME_0 +#undef CAT_NAME_1 +} + +static void test_event_category_2ptr_nonnull_similar(void) +{ +#define CAT_NAME_0 CAT_NAME_PREFIX "-2ptr-nonnull-similar-0" +#define CAT_NAME_1 CAT_NAME_PREFIX "-2ptr-nonnull-similar-1" + static struct event_category cat0 = { .name = CAT_NAME_0 }; + static struct event_category cat1 = { .name = CAT_NAME_0 }; + static struct event_category cat_with_parent0 = { .name = CAT_NAME_1, .parent = &cat0 }; + static struct event_category cat_with_parent1 = { .name = CAT_NAME_1, .parent = &cat1 }; + + test_begin("event category rereg: different ptr, similar non-NULL parent"); + + check_cat_registered(CAT_NAME_0, FALSE); + check_cat_registered(CAT_NAME_1, FALSE); + register_cat(&cat0, &cat0); + register_cat(&cat1, NULL); + register_cat(&cat_with_parent0, &cat_with_parent0); + register_cat(&cat_with_parent1, NULL); + check_cat_registered(CAT_NAME_0, TRUE); + check_cat_registered(CAT_NAME_1, TRUE); + + unregister_cat(&cat_with_parent0, UNREG_NOT_LAST); + unregister_cat(&cat_with_parent1, UNREG_LAST); + unregister_cat(&cat_with_parent0, UNREG_NOP); + unregister_cat(&cat_with_parent1, UNREG_NOP); + /* NOTE: we must unreg children before parent cats */ + unregister_cat(&cat0, UNREG_NOT_LAST); + unregister_cat(&cat1, UNREG_LAST); + unregister_cat(&cat0, UNREG_NOP); + unregister_cat(&cat1, UNREG_NOP); + + test_end(); +#undef CAT_NAME_0 +#undef CAT_NAME_1 +} + +void test_event_category_register(void) +{ + event_category_register_callback(check_category); + + /* + * registering/unregistering the same exact category struct (i.e., + * the pointer is the same) is a no-op after the first call + */ + test_event_category_1ptr_null(); + test_event_category_1ptr_nonnull(); + + /* + * registering/unregistering two different category structs (i.e., + * the pointers are different) is a almost a no-op + */ + test_event_category_2ptr_null(); + test_event_category_2ptr_nonnull_same(); + test_event_category_2ptr_nonnull_similar(); + + event_category_unregister_callback(check_category); +} + +enum fatal_test_state fatal_event_category_register(unsigned int stage) +{ +#define CAT_NAME_0 CAT_NAME_PREFIX "-2ptr-nonnull-different-0" +#define CAT_NAME_1 CAT_NAME_PREFIX "-2ptr-nonnull-different-1" +#define CAT_NAME_2 CAT_NAME_PREFIX "-2ptr-nonnull-different-2" + static struct event_category cat_no_parent0 = { .name = CAT_NAME_0 }; + static struct event_category cat_parent0 = { .name = CAT_NAME_1, .parent = &cat_no_parent0 }; + static struct event_category cat_other = { .name = CAT_NAME_2 }; + static struct event_category cat_other_parent = { .name = CAT_NAME_1, .parent = &cat_other }; + + /* we have only one fatal stage at this point */ + switch (stage) { + case 0: + event_category_register_callback(check_category); + + test_begin("event category rereg: different ptr, different non-NULL parent"); + + check_cat_registered(CAT_NAME_0, FALSE); + check_cat_registered(CAT_NAME_1, FALSE); + check_cat_registered(CAT_NAME_2, FALSE); + register_cat(&cat_no_parent0, &cat_no_parent0); + register_cat(&cat_other, &cat_other); + register_cat(&cat_parent0, &cat_parent0); + + test_expect_fatal_string("event category parent mismatch detected"); + register_cat(&cat_other_parent, NULL); /* expected panic */ + + return FATAL_TEST_FAILURE; + case 1: + event_unref(&dummy_event); + + unregister_cat(&cat_parent0, UNREG_LAST); + unregister_cat(&cat_parent0, UNREG_NOP); + unregister_cat(&cat_other, UNREG_LAST); + unregister_cat(&cat_other, UNREG_NOP); + /* NOTE: we must unreg children before parent cats */ + unregister_cat(&cat_no_parent0, UNREG_LAST); + unregister_cat(&cat_no_parent0, UNREG_NOP); + + test_end(); + + event_category_unregister_callback(check_category); + + return FATAL_TEST_FINISHED; + + default: + return FATAL_TEST_ABORT; + } +#undef CAT_NAME_0 +#undef CAT_NAME_1 +#undef CAT_NAME_2 +} diff --git a/src/lib/test-event-filter-expr.c b/src/lib/test-event-filter-expr.c new file mode 100644 index 0000000..444f16f --- /dev/null +++ b/src/lib/test-event-filter-expr.c @@ -0,0 +1,250 @@ +/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "strescape.h" +#include "event-filter.h" +#include "event-filter-private.h" + +#define STRING1 "X" +#define STRING2 "Y" + +/* dummy values, at least for now */ +#define SOURCE_FILENAME "blah.c" +#define SOURCE_LINE 123 + +static void check_expr(const char *test_name, + struct event *event, + struct event_filter *filter, + enum event_filter_log_type log_type, + bool expected) +{ + struct event_filter_node *expr; + unsigned int num_queries; + bool got; + + /* get at the expr inside the filter */ + expr = event_filter_get_expr_for_testing(filter, &num_queries); + test_out_quiet(t_strdup_printf("%s:num_queries==1", test_name), + num_queries == 1); /* should have only one query */ + + got = event_filter_query_match_eval(expr, event, + SOURCE_FILENAME, SOURCE_LINE, + log_type); + test_out_quiet(t_strdup_printf("%s:got=expected", test_name), + got == expected); +} + +static void do_test_expr(const char *filter_string, struct event *event, + enum event_filter_log_type log_type, + bool expected) +{ + const char *test_name, *error; + + test_name = t_strdup_printf( + "%.*s log type + event {a=%s, b=%s} + filter '%s' (exp %s)", + 3, /* truncate the type name to avoid CI seeing 'warning' messages */ + event_filter_category_from_log_type(log_type), + event_find_field_recursive_str(event, "a"), + event_find_field_recursive_str(event, "b"), + filter_string, + expected ? "true" : "false"); + + /* set up the filter expression */ + struct event_filter *filter = event_filter_create(); + test_out_quiet(t_strdup_printf("%s:event_filter_parse()", test_name), + event_filter_parse(filter_string, filter, &error) == 0); + + check_expr(test_name, event, filter, log_type, expected); + + event_filter_unref(&filter); +} + +static void test_unary_expr(struct event *event, + const char *expr, bool truth, + enum event_filter_log_type log_type) +{ + /* + * The UNARY() macro checks: + * + * 1. expr + * 2. NOT expr + * 3. NOT (expr) + * + * Note that numbers 2 and 3 are equivalent. + * + * The truth argument specifies the expected truth-iness of the + * passed in expression. + */ +#define UNARY() \ + T_BEGIN { \ + do_test_expr(expr, \ + event, log_type, truth); \ + do_test_expr(t_strdup_printf("NOT %s", expr), \ + event, log_type, !truth); \ + do_test_expr(t_strdup_printf("NOT (%s)", expr), \ + event, log_type, !truth); \ + } T_END + + UNARY(); +} + +static void test_binary_expr(struct event *event, + const char *expr1, const char *expr2, + bool truth1, bool truth2, + enum event_filter_log_type log_type) +{ + /* + * The BINARY() macro checks: + * + * 1. expr1 op expr2 + * 2. NOT expr1 op expr2 + * 3. NOT (expr1) op expr2 + * 4. (NOT expr1) op expr2 + * 5. expr1 op NOT expr2 + * 6. expr1 op NOT (expr2) + * 7. expr1 op (NOT expr2) + * 8. NOT (expr1 op expr2) + * 9. NOT expr1 op NOT expr2 + * 10. NOT (expr1) op NOT (expr2) + * 11. (NOT expr1) op (NOT expr2) + * + * Where op is OR or AND. + * + * Note that: + * - numbers 2, 3, and 4 are equivalent + * - numbers 5, 6, and 7 are equivalent + * - numbers 9, 10, and 11 are equivalent + * + * The truth arugments specify the expected truth-iness of the + * passed in expressions. + */ +#define BINARY(opstr, op) \ + T_BEGIN { \ + do_test_expr(t_strdup_printf("%s %s %s", expr1, opstr, expr2),\ + event, log_type, \ + (truth1) op (truth2)); \ + do_test_expr(t_strdup_printf("NOT %s %s %s", expr1, opstr, expr2),\ + event, log_type, \ + !(truth1) op (truth2)); \ + do_test_expr(t_strdup_printf("NOT (%s) %s %s", expr1, opstr, expr2),\ + event, log_type, \ + !(truth1) op (truth2)); \ + do_test_expr(t_strdup_printf("(NOT %s) %s %s", expr1, opstr, expr2),\ + event, log_type, \ + !(truth1) op (truth2)); \ + do_test_expr(t_strdup_printf("%s %s NOT %s", expr1, opstr, expr2),\ + event, log_type, \ + (truth1) op !(truth2)); \ + do_test_expr(t_strdup_printf("%s %s NOT (%s)", expr1, opstr, expr2),\ + event, log_type, \ + (truth1) op !(truth2)); \ + do_test_expr(t_strdup_printf("%s %s (NOT %s)", expr1, opstr, expr2),\ + event, log_type, \ + (truth1) op !(truth2)); \ + do_test_expr(t_strdup_printf("NOT (%s %s %s)", expr1, opstr, expr2),\ + event, log_type, \ + !((truth1) op (truth2))); \ + do_test_expr(t_strdup_printf("NOT %s %s NOT %s", expr1, opstr, expr2),\ + event, log_type, \ + !(truth1) op !(truth2)); \ + do_test_expr(t_strdup_printf("NOT (%s) %s NOT (%s)", expr1, opstr, expr2),\ + event, log_type, \ + !(truth1) op !(truth2)); \ + do_test_expr(t_strdup_printf("(NOT %s) %s (NOT %s)", expr1, opstr, expr2),\ + event, log_type, \ + !(truth1) op !(truth2)); \ + } T_END + + BINARY("OR", ||); + BINARY("AND", &&); +} + +static void test_event_filter_expr_fields(enum event_filter_log_type log_type) +{ + static const char *values[] = { + NULL, + "", + STRING1, + STRING2, + }; + unsigned int a, b; + +#define STR_IS_EMPTY(v) \ + (((v) == NULL) || (strcmp("", (v)) == 0)) +#define STR_MATCHES(v, c) \ + (((v) != NULL) && (strcmp((c), (v)) == 0)) + + /* unary */ + for (a = 0; a < N_ELEMENTS(values); a++) { + /* set up the event to match against */ + struct event *event = event_create(NULL); + event_add_str(event, "a", values[a]); + + test_unary_expr(event, + "a=\"\"", + STR_IS_EMPTY(values[a]), + log_type); + test_unary_expr(event, + "a=" STRING1, + STR_MATCHES(values[a], STRING1), + log_type); + + event_unref(&event); + } + + /* binary */ + for (a = 0; a < N_ELEMENTS(values); a++) { + for (b = 0; b < N_ELEMENTS(values); b++) { + /* set up the event to match against */ + struct event *event = event_create(NULL); + event_add_str(event, "a", values[a]); + event_add_str(event, "b", values[b]); + + test_binary_expr(event, + "a=\"\"", + "b=\"\"", + STR_IS_EMPTY(values[a]), + STR_IS_EMPTY(values[b]), + log_type); + test_binary_expr(event, + "a=" STRING1, + "b=\"\"", + STR_MATCHES(values[a], STRING1), + STR_IS_EMPTY(values[b]), + log_type); + test_binary_expr(event, + "a=\"\"", + "b=" STRING2, + STR_IS_EMPTY(values[a]), + STR_MATCHES(values[b], STRING2), + log_type); + test_binary_expr(event, + "a=" STRING1, + "b=" STRING2, + STR_MATCHES(values[a], STRING1), + STR_MATCHES(values[b], STRING2), + log_type); + + event_unref(&event); + } + } +} + +void test_event_filter_expr(void) +{ + static const enum event_filter_log_type log_types[] = { + EVENT_FILTER_LOG_TYPE_DEBUG, + EVENT_FILTER_LOG_TYPE_INFO, + EVENT_FILTER_LOG_TYPE_WARNING, + EVENT_FILTER_LOG_TYPE_ERROR, + EVENT_FILTER_LOG_TYPE_FATAL, + EVENT_FILTER_LOG_TYPE_PANIC, + }; + unsigned int i; + + test_begin("event filter expressions"); + for (i = 0; i < N_ELEMENTS(log_types); i++) + test_event_filter_expr_fields(log_types[i]); + test_end(); +} diff --git a/src/lib/test-event-filter-merge.c b/src/lib/test-event-filter-merge.c new file mode 100644 index 0000000..b520cf9 --- /dev/null +++ b/src/lib/test-event-filter-merge.c @@ -0,0 +1,67 @@ +/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "event-filter.h" + +static void filter_merge(const char *parent_str, const char *child_str) +{ + struct event_filter *parent, *child; + const char *test_name, *error; + string_t *out = t_str_new(128); + + test_name = t_strdup_printf("parent %s, child %s", + (parent_str == NULL) ? "NULL" : parent_str, + (child_str == NULL) ? "NULL" : child_str); + + parent = event_filter_create(); + child = event_filter_create(); + + /* prime the filters with an expression */ + if (parent_str != NULL) { + test_out_quiet(t_strdup_printf("%s:parent", test_name), + event_filter_parse(parent_str, parent, &error) == 0); + } + if (child_str != NULL) { + test_out_quiet(t_strdup_printf("%s:child", test_name), + event_filter_parse(child_str, child, &error) == 0); + } + /* merge */ + event_filter_merge(parent, child); + + /* export - to visit/deref everything in the filter */ + event_filter_export(parent, out); + event_filter_export(child, out); + + event_filter_unref(&parent); + event_filter_unref(&child); +} + +void test_event_filter_merge(void) +{ + static const char *inputs[] = { + NULL, + /* event name */ + "event=\"bar\"", + "event=\"\"", + /* category */ + "category=\"bar\"", + "category=\"\"", + /* source location */ + "source_location=\"bar:123\"", + "source_location=\"bar\"", + "source_location=\"\"", + /* field */ + "foo=\"bar\"", + "foo=\"\"", + }; + unsigned int i, j; + + test_begin("event filter merge"); + for (i = 0; i < N_ELEMENTS(inputs); i++) { + for (j = 0; j < N_ELEMENTS(inputs); j++) T_BEGIN { + filter_merge(inputs[i], inputs[j]); + } T_END; + } + test_end(); +} diff --git a/src/lib/test-event-filter-parser.c b/src/lib/test-event-filter-parser.c new file mode 100644 index 0000000..3dd2658 --- /dev/null +++ b/src/lib/test-event-filter-parser.c @@ -0,0 +1,543 @@ +/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "strescape.h" +#include "event-filter.h" + +#define GOOD(i, o) \ + { \ + .input = (i), \ + .output = (o), \ + .fails = FALSE, \ + } + +#define BAD(i, o) \ + { \ + .input = (i), \ + .output = (o), \ + .fails = TRUE, \ + } + +enum quoting { + QUOTE_MUST, + QUOTE_MAY, + QUOTE_MUST_NOT, +}; + +static const char *what_special[] = { + "event", + "category", + "source_location", +}; + +/* some sample field names */ +static const char *what_fields_single[] = { + "foo", + "foo_bar", + "foo-bar", +}; + +static const char *comparators[] = { + "=", + "<", + "<=", + ">", + ">=", +}; + +/* values that may be quoted or not quoted */ +static const char *values_single[] = { + "foo", + "foo.c", + "foo.c:123", + + /* wildcards */ + "*foo", + "f*o", + "foo*", + "*", + "?foo", + "f?o", + "foo?", + "?", +}; + +/* values that need to be quoted */ +static const char *values_multi[] = { + "foo bar", + "foo\tbar", + "foo\nbar", + "foo\rbar", + "foo\"bar", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ac " + "vestibulum magna. Maecenas erat mi, finibus et tellus id, suscipit " + "varius arcu. Morbi faucibus diam in ligula suscipit, non bibendum " + "orci venenatis. Vestibulum mattis luctus dictum. Vivamus ultrices " + "tincidunt vehicula. Aliquam nec ante vitae libero dignissim finibus " + "non ac massa. Proin sit amet semper ligula. Curabitur eleifend massa " + "et arcu euismod lacinia. Phasellus sapien mauris, dignissim vitae " + "commodo at, consequat eget augue. Integer posuere non enim eu " + "laoreet. Nulla eget lectus at enim sodales rutrum. Donec tincidunt " + "nibh ac convallis pulvinar. Nunc facilisis tempus ligula. Nullam at " + "ultrices enim, eu faucibus ipsum." + /* utf-8: >= U+128 only */ + "\xc3\xa4\xc3\xa1\xc4\x8d\xc4\x8f\xc4\x9b\xc5\x88\xc3\xb6\xc5\x99\xc3\xbc\xc3\xba\xc5\xaf", + /* utf-8: ascii + combining char */ + "r\xcc\x8c", + + /* wildcards */ + "foo * bar", + "foo ? bar", +}; + +/* boolean operators used as values get lowercased unless they are quoted */ +static const struct values_oper { + const char *in; + const char *out_unquoted; + const char *out_quoted; +} values_oper[] = { + { "AND", "and", "AND" }, + { "ANd", "and", "ANd" }, + { "AnD", "and", "AnD" }, + { "And", "and", "And" }, + { "aND", "and", "aND" }, + { "aNd", "and", "aNd" }, + { "anD", "and", "anD" }, + { "and", "and", "and" }, + + { "OR", "or", "OR" }, + { "Or", "or", "Or" }, + { "oR", "or", "oR" }, + { "or", "or", "or" }, + + { "NOT", "not", "NOT" }, + { "NOt", "not", "NOt" }, + { "NoT", "not", "NoT" }, + { "Not", "not", "Not" }, + { "nOT", "not", "nOT" }, + { "nOt", "not", "nOt" }, + { "noT", "not", "noT" }, + { "not", "not", "not" }, +}; + +static struct test { + const char *input; + const char *output; + bool fails; +} tests[] = { + GOOD("", ""), + + /* unquoted tokens can be only [a-zA-Z0-9:.*?_-]+ */ + BAD("abc=r\xcc\x8c", "event filter: syntax error"), + + /* check that spaces and extra parens don't break anything */ +#define CHECK_REAL(sp1, key, sp2, sp3, value, sp4) \ + GOOD(sp1 key sp2 "=" sp3 value sp4, \ + "(" key "=\"" value "\")") +#define CHECK_SPACES(key, value, sp, op, cp) \ + CHECK_REAL(sp op, key, "", "", value, "" cp), \ + CHECK_REAL(op sp, key, "", "", value, "" cp), \ + CHECK_REAL(op "", key, sp, "", value, "" cp), \ + CHECK_REAL(op "", key, "", sp, value, "" cp), \ + CHECK_REAL(op "", key, "", "", value, sp cp), \ + CHECK_REAL(op "", key, "", "", value, cp sp) +#define CHECK_PARENS(key, value, sp) \ + CHECK_SPACES(key, value, sp, "", ""), \ + CHECK_SPACES(key, value, sp, "(", ")"), \ + CHECK_SPACES(key, value, sp, "((", "))"), \ + CHECK_SPACES(key, value, sp, "(((", ")))") + + CHECK_PARENS("event", "abc", " "), + CHECK_PARENS("event", "abc", "\t"), + CHECK_PARENS("event", "abc", "\n"), + CHECK_PARENS("event", "abc", "\r"), + CHECK_PARENS("event", "abc", " "), +#undef CHECK_PARENS +#undef CHECK_SPACES +#undef CHECK_REAL + + /* check empty parens */ + BAD("()", "event filter: syntax error"), + + /* check name only / name+comparator (!negated & negated) */ +#define CHECK_CMP_REAL(not, name, cmp, err) \ + BAD(not name cmp, err), \ + BAD(not "\"" name "\"" cmp, err) +#define CHECK_CMP(name, cmp, err) \ + CHECK_CMP_REAL("", name, cmp, err), \ + CHECK_CMP_REAL("NOT ", name, cmp, err) +#define CHECK(name) \ + CHECK_CMP(name, "", \ + "event filter: syntax error"), \ + CHECK_CMP(name, "=", \ + "event filter: syntax error"), \ + CHECK_CMP(name, "<", \ + "event filter: syntax error"), \ + CHECK_CMP(name, "<=", \ + "event filter: syntax error"), \ + CHECK_CMP(name, ">", \ + "event filter: syntax error"), \ + CHECK_CMP(name, ">=", \ + "event filter: syntax error") + + CHECK("event"), + CHECK("source_location"), + CHECK("category"), + CHECK("foo-field-name"), +#undef CHECK +#undef CHECK_CMP +#undef CHECK_CMP_REAL + + /* check simple nesting */ +#define CHECK(binop1, binop2) \ + GOOD("(event=abc " binop1 " event=def) " binop2 " event=ghi", \ + "(((event=\"abc\" " binop1 " event=\"def\") " binop2 " event=\"ghi\"))"), \ + GOOD("event=abc " binop1 " (event=def " binop2 " event=ghi)", \ + "((event=\"abc\" " binop1 " (event=\"def\" " binop2 " event=\"ghi\")))") + + CHECK("AND", "AND"), + CHECK("AND", "OR"), + CHECK("OR", "AND"), + CHECK("OR", "OR"), +#undef CHECK + + /* check operator precedence */ +#define CMP(x) "event=\"" #x "\"" +#define CHECK(binop1, binop2) \ + GOOD(CMP(1) " " binop1 " " CMP(2) " " binop2 " " CMP(3), \ + "(((" CMP(1) " " binop1 " " CMP(2) ") " binop2 " " CMP(3) "))") + + CHECK("AND", "AND"), + CHECK("AND", "OR"), + CHECK("OR", "AND"), + CHECK("OR", "OR"), +#undef CHECK +#undef CMP +}; + +static void testcase(const char *name, const char *input, const char *exp, + bool fails) +{ + struct event_filter *filter; + const char *error; + int ret; + + filter = event_filter_create(); + ret = event_filter_parse(input, filter, &error); + + test_out_quiet(name != NULL ? name : "filter parser", + (ret != 0) == fails); + + if (ret == 0) { + string_t *tmp = t_str_new(128); + + event_filter_export(filter, tmp); + + test_out_quiet(t_strdup_printf("input: %s", input), + strcmp(exp, str_c(tmp)) == 0); + } else { + test_out_quiet(t_strdup_printf("input: %s", input), + str_begins(error, exp)); + } + + event_filter_unref(&filter); +} + +static void test_event_filter_parser_table(void) +{ + unsigned int i; + + test_begin("event filter parser: table"); + for (i = 0; i < N_ELEMENTS(tests); i++) T_BEGIN { + testcase(NULL, tests[i].input, + tests[i].output, + tests[i].fails); + } T_END; + test_end(); +} + +static void test_event_filter_parser_categories(void) +{ + static const char *cat_names[] = { + "debug", "info", "warning", "error", "fatal", "panic", + }; + unsigned int i; + + test_begin("event filter parser: log type category"); + for (i = 0; i < N_ELEMENTS(cat_names); i++) T_BEGIN { + string_t *str = t_str_new(128); + + str_append(str, "(category="); + str_append(str, cat_names[i]); + str_append(str, ")"); + + testcase(NULL, str_c(str), str_c(str), FALSE); + } T_END; + test_end(); +} + +static void +test_event_filter_parser_simple_nesting_helper(bool not1, bool not2, + bool and, const char *sp, + bool sp1, bool sp2, + bool sp3, bool sp4) +{ + const char *op = and ? "AND" : "OR"; + const char *expr1 = "event=\"abc\""; + const char *expr2 = "event=\"def\""; + const char *in; + const char *exp; + + in = t_strdup_printf("%s(%s%s)%s%s%s(%s%s)%s", + sp1 ? sp : "", + not1 ? "NOT " : "", + expr1, + sp2 ? sp : "", + op, + sp3 ? sp : "", + not2 ? "NOT " : "", + expr2, + sp4 ? sp : ""); + + exp = t_strdup_printf("((%s%s%s %s %s%s%s))", + not1 ? "(NOT " : "", + expr1, + not1 ? ")" : "", + op, + not2 ? "(NOT " : "", + expr2, + not2 ? ")" : ""); + + testcase(NULL, in, exp, FALSE); +} + +static void test_event_filter_parser_simple_nesting(void) +{ + const char *whitespace[] = { + "", + "\t", + "\n", + "\r", + " ", + }; + unsigned int i; + unsigned int loc; + unsigned int not; + + test_begin("event filter parser: simple nesting"); + for (i = 0; i < N_ELEMENTS(whitespace); i++) { + for (not = 0; not < 4; not++) { + const bool not1 = (not & 0x2) != 0; + const bool not2 = (not & 0x1) != 0; + + for (loc = 0; loc < 16; loc++) T_BEGIN { + const bool sp1 = (loc & 0x8) != 0; + const bool sp2 = (loc & 0x4) != 0; + const bool sp3 = (loc & 0x2) != 0; + const bool sp4 = (loc & 0x1) != 0; + + test_event_filter_parser_simple_nesting_helper(not1, not2, + TRUE, + whitespace[i], + sp1, sp2, + sp3, sp4); + test_event_filter_parser_simple_nesting_helper(not1, not2, + FALSE, + whitespace[i], + sp1, sp2, + sp3, sp4); + } T_END; + } + } + test_end(); +} + +/* + * Test '<key><op><value>' with each possible operator and each possible + * quoting of <key> and <value>. Some quotings are not allowed. The keyq + * and valueq arguments specify whether the <key> and <value> strings + * should be quoted. key_special indicates that the key is *not* a field + * and therefore only the = operator should parse successfully. + */ +static void generated_single_comparison(const char *name, + bool parens, + const char *key, + enum quoting keyq, + bool key_special, + const char *value_in, + const char *value_exp, + enum quoting valueq) +{ + unsigned int c, q; + bool should_fail; + + for (c = 0; c < N_ELEMENTS(comparators); c++) { + string_t *output = t_str_new(128); + + if (key_special && (strcmp(comparators[c], "=") != 0)) { + /* the key is a not a field, only = is allowed */ + str_append(output, "event filter: Only fields support inequality comparisons"); + should_fail = TRUE; + } else { + /* the key is a field, all comparators are allowed */ + str_append_c(output, '('); + if (keyq != QUOTE_MUST_NOT) + str_append_c(output, '"'); + str_append(output, key); + if (keyq != QUOTE_MUST_NOT) + str_append_c(output, '"'); + str_append(output, comparators[c]); + str_append_c(output, '"'); + str_append_escaped(output, value_exp, strlen(value_exp)); + str_append_c(output, '"'); + str_append_c(output, ')'); + should_fail = FALSE; + } + + for (q = 0; q < 4; q++) { + const bool qkey = (q & 1) == 1; + const bool qval = (q & 2) == 2; + string_t *input = t_str_new(128); + + if ((!qkey && (keyq == QUOTE_MUST)) || + (qkey && (keyq == QUOTE_MUST_NOT))) + continue; + if ((!qval && (valueq == QUOTE_MUST)) || + (qval && (valueq == QUOTE_MUST_NOT))) + continue; + + if (parens) + str_append_c(input, '('); + if (qkey) + str_append_c(input, '"'); + str_append(input, key); + if (qkey) + str_append_c(input, '"'); + str_append(input, comparators[c]); + if (qval) { + str_append_c(input, '"'); + str_append_escaped(input, value_in, strlen(value_in)); + str_append_c(input, '"'); + } else { + str_append(input, value_in); + } + if (parens) + str_append_c(input, ')'); + + testcase(name, + str_c(input), + str_c(output), + should_fail); + } + } +} + +static void test_event_filter_parser_generated(bool parens) +{ + unsigned int w, v; + + test_begin(t_strdup_printf("event filter parser: parser generated parens=%s", + parens ? "yes" : "no")); + /* check that non-field keys work */ + for (w = 0; w < N_ELEMENTS(what_special); w++) { + for (v = 0; v < N_ELEMENTS(values_single); v++) + generated_single_comparison("non-field/single", + parens, + what_special[w], + QUOTE_MUST_NOT, + TRUE, + values_single[v], + values_single[v], + QUOTE_MAY); + + for (v = 0; v < N_ELEMENTS(values_multi); v++) + generated_single_comparison("non-field/multi", + parens, + what_special[w], + QUOTE_MUST_NOT, + TRUE, + values_multi[v], + values_multi[v], + QUOTE_MUST); + + for (v = 0; v < N_ELEMENTS(values_oper); v++) { + generated_single_comparison("non-field/bool-op", + parens, + what_special[w], + QUOTE_MUST_NOT, + TRUE, + values_oper[v].in, + values_oper[v].out_unquoted, + QUOTE_MUST_NOT); + generated_single_comparison("non-field/bool-op", + parens, + what_special[w], + QUOTE_MUST_NOT, + TRUE, + values_oper[v].in, + values_oper[v].out_quoted, + QUOTE_MUST); + } + } + + /* check that field keys work */ + for (w = 0; w < N_ELEMENTS(what_fields_single); w++) { + for (v = 0; v < N_ELEMENTS(values_single); v++) + generated_single_comparison("field/single", + parens, + what_fields_single[w], + QUOTE_MAY, + FALSE, + values_single[v], + values_single[v], + QUOTE_MAY); + + for (v = 0; v < N_ELEMENTS(values_multi); v++) + generated_single_comparison("field/multi", + parens, + what_fields_single[w], + QUOTE_MAY, + FALSE, + values_multi[v], + values_multi[v], + QUOTE_MUST); + + for (v = 0; v < N_ELEMENTS(values_oper); v++) { + generated_single_comparison("field/bool-op", + parens, + what_fields_single[w], + QUOTE_MAY, + FALSE, + values_oper[v].in, + values_oper[v].out_unquoted, + QUOTE_MUST_NOT); + generated_single_comparison("field/bool-op", + parens, + what_fields_single[w], + QUOTE_MAY, + FALSE, + values_oper[v].in, + values_oper[v].out_quoted, + QUOTE_MUST); + } + } + test_end(); +} + +static void test_event_filter_parser_simple_invalid(void) +{ + test_begin("event filter parser: simple invalid"); + testcase(NULL, "a=b=c", "", TRUE); + test_end(); +} + +void test_event_filter_parser(void) +{ + test_event_filter_parser_table(); + test_event_filter_parser_categories(); + test_event_filter_parser_simple_nesting(); + test_event_filter_parser_generated(FALSE); + test_event_filter_parser_generated(TRUE); + test_event_filter_parser_simple_invalid(); +} diff --git a/src/lib/test-event-filter.c b/src/lib/test-event-filter.c new file mode 100644 index 0000000..8e1e130 --- /dev/null +++ b/src/lib/test-event-filter.c @@ -0,0 +1,598 @@ +/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "ioloop.h" +#include "event-filter-private.h" + +static void test_event_filter_override_parent_fields(void) +{ + struct event_filter *filter; + const char *error; + const struct failure_context failure_ctx = { + .type = LOG_TYPE_DEBUG + }; + + test_begin("event filter: override parent fields"); + + struct event *parent = event_create(NULL); + event_add_str(parent, "str", "parent_str"); + event_add_str(parent, "parent_str", "parent_str"); + event_add_int(parent, "int1", 0); + event_add_int(parent, "int2", 5); + event_add_int(parent, "parent_int", 6); + + struct event *child = event_create(parent); + event_add_str(child, "str", "child_str"); + event_add_str(child, "child_str", "child_str"); + event_add_int(child, "int1", 6); + event_add_int(child, "int2", 0); + event_add_int(child, "child_int", 8); + + /* parent matches: test a mix of parent/child fields */ + filter = event_filter_create(); + test_assert(event_filter_parse("str=parent_str AND int1=0 AND int2=5", filter, &error) == 0); + test_assert(event_filter_match(filter, parent, &failure_ctx)); + test_assert(!event_filter_match(filter, child, &failure_ctx)); + event_filter_unref(&filter); + + /* parent matches: test fields that exist only in parent */ + filter = event_filter_create(); + test_assert(event_filter_parse("parent_str=parent_str AND parent_int=6", filter, &error) == 0); + test_assert(event_filter_match(filter, parent, &failure_ctx)); + test_assert(event_filter_match(filter, child, &failure_ctx)); + event_filter_unref(&filter); + + /* child matches: test a mix of parent/child fields */ + filter = event_filter_create(); + test_assert(event_filter_parse("str=child_str AND int1=6 AND int2=0", filter, &error) == 0); + test_assert(event_filter_match(filter, child, &failure_ctx)); + test_assert(!event_filter_match(filter, parent, &failure_ctx)); + event_filter_unref(&filter); + + /* child matches: test fields that exist only in child */ + filter = event_filter_create(); + test_assert(event_filter_parse("child_str=child_str AND child_int=8", filter, &error) == 0); + test_assert(event_filter_match(filter, child, &failure_ctx)); + test_assert(!event_filter_match(filter, parent, &failure_ctx)); + event_filter_unref(&filter); + + event_unref(&parent); + event_unref(&child); + test_end(); +} + +static void test_event_filter_override_global_fields(void) +{ + struct event_filter *filter; + const char *error; + const struct failure_context failure_ctx = { + .type = LOG_TYPE_DEBUG + }; + + test_begin("event filter: override global fields"); + + struct event *global = event_create(NULL); + event_add_str(global, "str", "global_str"); + event_add_str(global, "global_str", "global_str"); + event_add_int(global, "int1", 0); + event_add_int(global, "int2", 5); + event_add_int(global, "global_int", 6); + event_push_global(global); + + struct event *local = event_create(NULL); + event_add_str(local, "str", "local_str"); + event_add_str(local, "local_str", "local_str"); + event_add_int(local, "int1", 6); + event_add_int(local, "int2", 0); + event_add_int(local, "local_int", 8); + + /* global matches: test a mix of global/local fields */ + filter = event_filter_create(); + test_assert(event_filter_parse("str=global_str AND int1=0 AND int2=5", filter, &error) == 0); + test_assert(event_filter_match(filter, global, &failure_ctx)); + test_assert(!event_filter_match(filter, local, &failure_ctx)); + event_filter_unref(&filter); + + /* global matches: test fields that exist only in global */ + filter = event_filter_create(); + test_assert(event_filter_parse("global_str=global_str AND global_int=6", filter, &error) == 0); + test_assert(event_filter_match(filter, global, &failure_ctx)); + test_assert(event_filter_match(filter, local, &failure_ctx)); + event_filter_unref(&filter); + + /* local matches: test a mix of global/local fields */ + filter = event_filter_create(); + test_assert(event_filter_parse("str=local_str AND int1=6 AND int2=0", filter, &error) == 0); + test_assert(event_filter_match(filter, local, &failure_ctx)); + test_assert(!event_filter_match(filter, global, &failure_ctx)); + event_filter_unref(&filter); + + /* local matches: test fields that exist only in local */ + filter = event_filter_create(); + test_assert(event_filter_parse("local_str=local_str AND local_int=8", filter, &error) == 0); + test_assert(event_filter_match(filter, local, &failure_ctx)); + test_assert(!event_filter_match(filter, global, &failure_ctx)); + event_filter_unref(&filter); + + event_pop_global(global); + event_unref(&global); + event_unref(&local); + test_end(); +} + +static void test_event_filter_clear_parent_fields(void) +{ + struct event_filter *filter; + const char *error; + const struct failure_context failure_ctx = { + .type = LOG_TYPE_DEBUG + }; + const char *keys[] = { "str", "int" }; + + test_begin("event filter: clear parent fields"); + + struct event *parent = event_create(NULL); + event_add_str(parent, "str", "parent_str"); + event_add_int(parent, "int", 0); + + struct event *child = event_create(parent); + event_field_clear(child, "str"); + event_field_clear(child, "int"); + + for (unsigned int i = 0; i < N_ELEMENTS(keys); i++) { + /* match any value */ + const char *query = t_strdup_printf("%s=*", keys[i]); + filter = event_filter_create(); + test_assert(event_filter_parse(query, filter, &error) == 0); + + test_assert_idx(event_filter_match(filter, parent, &failure_ctx), i); + test_assert_idx(!event_filter_match(filter, child, &failure_ctx), i); + event_filter_unref(&filter); + } + + /* match empty field */ + filter = event_filter_create(); + test_assert(event_filter_parse("str=\"\"", filter, &error) == 0); + test_assert(!event_filter_match(filter, parent, &failure_ctx)); + test_assert(event_filter_match(filter, child, &failure_ctx)); + event_filter_unref(&filter); + + /* match nonexistent field */ + filter = event_filter_create(); + test_assert(event_filter_parse("nonexistent=\"\"", filter, &error) == 0); + test_assert(event_filter_match(filter, parent, &failure_ctx)); + test_assert(event_filter_match(filter, child, &failure_ctx)); + event_filter_unref(&filter); + + event_unref(&parent); + event_unref(&child); + test_end(); +} + +static void test_event_filter_clear_global_fields(void) +{ + struct event_filter *filter; + const char *error; + const struct failure_context failure_ctx = { + .type = LOG_TYPE_DEBUG + }; + const char *keys[] = { "str", "int" }; + + test_begin("event filter: clear global fields"); + + struct event *global = event_create(NULL); + event_add_str(global, "str", "global_str"); + event_add_int(global, "int", 0); + event_push_global(global); + + struct event *local = event_create(NULL); + event_field_clear(local, "str"); + event_field_clear(local, "int"); + + for (unsigned int i = 0; i < N_ELEMENTS(keys); i++) { + /* match any value */ + const char *query = t_strdup_printf("%s=*", keys[i]); + filter = event_filter_create(); + test_assert(event_filter_parse(query, filter, &error) == 0); + + test_assert_idx(event_filter_match(filter, global, &failure_ctx), i); + test_assert_idx(!event_filter_match(filter, local, &failure_ctx), i); + event_filter_unref(&filter); + } + + /* match empty field */ + filter = event_filter_create(); + test_assert(event_filter_parse("str=\"\"", filter, &error) == 0); + test_assert(!event_filter_match(filter, global, &failure_ctx)); + test_assert(event_filter_match(filter, local, &failure_ctx)); + event_filter_unref(&filter); + + /* match nonexistent field */ + filter = event_filter_create(); + test_assert(event_filter_parse("nonexistent=\"\"", filter, &error) == 0); + test_assert(event_filter_match(filter, global, &failure_ctx)); + test_assert(event_filter_match(filter, local, &failure_ctx)); + event_filter_unref(&filter); + + event_pop_global(global); + event_unref(&global); + event_unref(&local); + test_end(); +} + +static void test_event_filter_inc_int(void) +{ + struct event_filter *filter; + const char *error; + const struct failure_context failure_ctx = { + .type = LOG_TYPE_DEBUG + }; + + test_begin("event filter: create and update keys with event_inc_int"); + + struct event *root = event_create(NULL); + + filter = event_filter_create(); + test_assert(event_filter_parse("int=14", filter, &error) == 0); + + const struct event_field *f = event_find_field_recursive(root, "int"); + i_assert(f == NULL); + test_assert(!event_filter_match(filter, root, &failure_ctx)); + + event_inc_int(root, "int", 7); + test_assert(!event_filter_match(filter, root, &failure_ctx)); + f = event_find_field_recursive(root, "int"); + i_assert(f != NULL); + test_assert_strcmp(f->key, "int"); + test_assert(f->value_type == EVENT_FIELD_VALUE_TYPE_INTMAX); + test_assert(f->value.intmax == 7); + + event_inc_int(root, "int", 7); + test_assert(event_filter_match(filter, root, &failure_ctx)); + f = event_find_field_recursive(root, "int"); + i_assert(f != NULL); + test_assert_strcmp(f->key, "int"); + test_assert(f->value_type == EVENT_FIELD_VALUE_TYPE_INTMAX); + test_assert(f->value.intmax == 14); + + event_filter_unref(&filter); + event_unref(&root); + test_end(); +} + +static void test_event_filter_parent_category_match(void) +{ + static struct event_category parent_category = { + .name = "parent", + }; + static struct event_category child_category = { + .parent = &parent_category, + .name = "child", + }; + struct event_filter *filter; + const char *error; + const struct failure_context failure_ctx = { + .type = LOG_TYPE_DEBUG + }; + + test_begin("event filter: parent category match"); + + struct event *e = event_create(NULL); + event_add_category(e, &child_category); + + filter = event_filter_create(); + test_assert(event_filter_parse("category=parent", filter, &error) == 0); + + test_assert(event_filter_match(filter, e, &failure_ctx)); + + event_filter_unref(&filter); + event_unref(&e); + test_end(); +} + +static void test_event_filter_strlist(void) +{ + struct event_filter *filter; + const struct failure_context failure_ctx = { + .type = LOG_TYPE_DEBUG + }; + + test_begin("event filter: match string list"); + + struct event *e = event_create(NULL); + + filter = event_filter_create(); + /* should match empty list */ + event_filter_parse("abc=\"\"", filter, NULL); + test_assert(event_filter_match(filter, e, &failure_ctx)); + /* should still be empty */ + event_strlist_append(e, "abc", NULL); + test_assert(event_filter_match(filter, e, &failure_ctx)); + + /* should not match non-empty list */ + event_strlist_append(e, "abc", "one"); + test_assert(!event_filter_match(filter, e, &failure_ctx)); + event_filter_unref(&filter); + + /* should match non-empty list that has value 'one' */ + filter = event_filter_create(); + event_strlist_append(e, "abc", "two"); + event_filter_parse("abc=one", filter, NULL); + test_assert(event_filter_match(filter, e, &failure_ctx)); + event_filter_unref(&filter); + + /* should match non-empty list that has no value 'three' */ + filter = event_filter_create(); + event_filter_parse("abc=one AND NOT abc=three", filter, NULL); + test_assert(event_filter_match(filter, e, &failure_ctx)); + event_filter_unref(&filter); + + event_unref(&e); + test_end(); +} + +static void test_event_filter_strlist_recursive(void) +{ + struct event_filter *filter; + const struct failure_context failure_ctx = { + .type = LOG_TYPE_DEBUG + }; + + test_begin("event filter: match string list - recursive"); + + struct event *parent = event_create(NULL); + struct event *e = event_create(parent); + + /* empty filter: parent is non-empty */ + filter = event_filter_create(); + event_filter_parse("list1=\"\"", filter, NULL); + test_assert(event_filter_match(filter, e, &failure_ctx)); + event_strlist_append(parent, "list1", "foo"); + test_assert(!event_filter_match(filter, e, &failure_ctx)); + event_filter_unref(&filter); + + /* matching filter: matches parent */ + filter = event_filter_create(); + event_filter_parse("list2=parent", filter, NULL); + /* empty: */ + test_assert(!event_filter_match(filter, e, &failure_ctx)); + /* set parent but no child: */ + event_strlist_append(parent, "list2", "parent"); + test_assert(event_filter_match(filter, e, &failure_ctx)); + /* set child to non-matching: */ + event_strlist_append(e, "list2", "child"); + test_assert(event_filter_match(filter, e, &failure_ctx)); + event_filter_unref(&filter); + + /* matching filter: matches child */ + filter = event_filter_create(); + event_filter_parse("list3=child", filter, NULL); + /* empty: */ + test_assert(!event_filter_match(filter, e, &failure_ctx)); + /* set child but no parent: */ + event_strlist_append(e, "list3", "child"); + test_assert(event_filter_match(filter, e, &failure_ctx)); + /* set parent to non-matching: */ + event_strlist_append(e, "list3", "parent"); + test_assert(event_filter_match(filter, e, &failure_ctx)); + event_filter_unref(&filter); + + event_unref(&e); + event_unref(&parent); + test_end(); +} + +static void test_event_filter_strlist_global_events(void) +{ + struct event_filter *filter; + const struct failure_context failure_ctx = { + .type = LOG_TYPE_DEBUG + }; + + test_begin("event filter: match string list - global events"); + + struct event *global = event_create(NULL); + event_push_global(global); + + struct event *e = event_create(NULL); + + /* empty filter: global is non-empty */ + filter = event_filter_create(); + event_filter_parse("list1=\"\"", filter, NULL); + test_assert(event_filter_match(filter, e, &failure_ctx)); + event_strlist_append(global, "list1", "foo"); + test_assert(!event_filter_match(filter, e, &failure_ctx)); + event_filter_unref(&filter); + + /* matching filter: matches global */ + filter = event_filter_create(); + event_filter_parse("list2=global", filter, NULL); + /* empty: */ + test_assert(!event_filter_match(filter, e, &failure_ctx)); + /* set global but no local: */ + event_strlist_append(global, "list2", "global"); + test_assert(event_filter_match(filter, e, &failure_ctx)); + /* set local to non-matching: */ + event_strlist_append(e, "list2", "local"); + test_assert(event_filter_match(filter, e, &failure_ctx)); + event_filter_unref(&filter); + + /* matching filter: matches local */ + filter = event_filter_create(); + event_filter_parse("list3=local", filter, NULL); + /* empty: */ + test_assert(!event_filter_match(filter, e, &failure_ctx)); + /* set local but no global: */ + event_strlist_append(e, "list3", "local"); + test_assert(event_filter_match(filter, e, &failure_ctx)); + /* set global to non-matching: */ + event_strlist_append(e, "list3", "global"); + test_assert(event_filter_match(filter, e, &failure_ctx)); + event_filter_unref(&filter); + + event_unref(&e); + event_pop_global(global); + event_unref(&global); + test_end(); +} + +static void test_event_filter_named_and_str(void) +{ + struct event_filter *filter; + const char *error; + const struct failure_context failure_ctx = { + .type = LOG_TYPE_DEBUG + }; + + test_begin("event filter: event name and str"); + + filter = event_filter_create(); + struct event *e_noname_nostr = event_create(NULL); + struct event *e_noname_str = event_create(NULL); + event_add_str(e_noname_str, "str", "str"); + struct event *e_noname_wrongstr = event_create(NULL); + event_add_str(e_noname_wrongstr, "str", "wrong"); + struct event *e_named_nostr = event_create(NULL); + event_set_name(e_named_nostr, "named"); + struct event *e_named_str = event_create(NULL); + event_set_name(e_named_str, "named"); + event_add_str(e_named_str, "str", "str"); + struct event *e_named_wrongstr = event_create(NULL); + event_set_name(e_named_wrongstr, "named"); + event_add_str(e_named_wrongstr, "str", "wrong"); + struct event *e_wrongname_nostr = event_create(NULL); + event_set_name(e_wrongname_nostr, "wrong"); + struct event *e_wrongname_str = event_create(NULL); + event_set_name(e_wrongname_str, "wrong"); + event_add_str(e_wrongname_str, "str", "str"); + struct event *e_wrongname_wrongstr = event_create(NULL); + event_set_name(e_wrongname_wrongstr, "wrong"); + event_add_str(e_wrongname_wrongstr, "str", "wrong"); + + test_assert(event_filter_parse("event=named AND str=str", filter, &error) == 0); + test_assert(filter->named_queries_only); + test_assert(!event_filter_match(filter, e_noname_nostr, &failure_ctx)); + test_assert(!event_filter_match(filter, e_noname_str, &failure_ctx)); + test_assert(!event_filter_match(filter, e_noname_wrongstr, &failure_ctx)); + test_assert(!event_filter_match(filter, e_named_nostr, &failure_ctx)); + test_assert(event_filter_match(filter, e_named_str, &failure_ctx)); + test_assert(!event_filter_match(filter, e_named_wrongstr, &failure_ctx)); + test_assert(!event_filter_match(filter, e_wrongname_nostr, &failure_ctx)); + test_assert(!event_filter_match(filter, e_wrongname_str, &failure_ctx)); + test_assert(!event_filter_match(filter, e_wrongname_wrongstr, &failure_ctx)); + + event_filter_unref(&filter); + event_unref(&e_noname_nostr); + event_unref(&e_noname_str); + event_unref(&e_noname_wrongstr); + event_unref(&e_named_nostr); + event_unref(&e_named_str); + event_unref(&e_named_wrongstr); + event_unref(&e_wrongname_nostr); + event_unref(&e_wrongname_str); + event_unref(&e_wrongname_wrongstr); + test_end(); +} + +static void test_event_filter_named_or_str(void) +{ + struct event_filter *filter; + const char *error; + const struct failure_context failure_ctx = { + .type = LOG_TYPE_DEBUG + }; + + test_begin("event filter: event name or str"); + + filter = event_filter_create(); + struct event *e_noname_nostr = event_create(NULL); + struct event *e_noname_str = event_create(NULL); + event_add_str(e_noname_str, "str", "str"); + struct event *e_noname_wrongstr = event_create(NULL); + event_add_str(e_noname_wrongstr, "str", "wrong"); + struct event *e_named_nostr = event_create(NULL); + event_set_name(e_named_nostr, "named"); + struct event *e_named_str = event_create(NULL); + event_set_name(e_named_str, "named"); + event_add_str(e_named_str, "str", "str"); + struct event *e_named_wrongstr = event_create(NULL); + event_set_name(e_named_wrongstr, "named"); + event_add_str(e_named_wrongstr, "str", "wrong"); + struct event *e_wrongname_nostr = event_create(NULL); + event_set_name(e_wrongname_nostr, "wrong"); + struct event *e_wrongname_str = event_create(NULL); + event_set_name(e_wrongname_str, "wrong"); + event_add_str(e_wrongname_str, "str", "str"); + struct event *e_wrongname_wrongstr = event_create(NULL); + event_set_name(e_wrongname_wrongstr, "wrong"); + event_add_str(e_wrongname_wrongstr, "str", "wrong"); + + test_assert(event_filter_parse("event=named OR str=str", filter, &error) == 0); + test_assert(!filter->named_queries_only); + + test_assert(!event_filter_match(filter, e_noname_nostr, &failure_ctx)); + test_assert(event_filter_match(filter, e_noname_str, &failure_ctx)); + test_assert(!event_filter_match(filter, e_noname_wrongstr, &failure_ctx)); + test_assert(event_filter_match(filter, e_named_nostr, &failure_ctx)); + test_assert(event_filter_match(filter, e_named_str, &failure_ctx)); + test_assert(event_filter_match(filter, e_named_wrongstr, &failure_ctx)); + test_assert(!event_filter_match(filter, e_wrongname_nostr, &failure_ctx)); + test_assert(event_filter_match(filter, e_wrongname_str, &failure_ctx)); + test_assert(!event_filter_match(filter, e_wrongname_wrongstr, &failure_ctx)); + + event_filter_unref(&filter); + event_unref(&e_noname_nostr); + event_unref(&e_noname_str); + event_unref(&e_noname_wrongstr); + event_unref(&e_named_nostr); + event_unref(&e_named_str); + event_unref(&e_named_wrongstr); + event_unref(&e_wrongname_nostr); + event_unref(&e_wrongname_str); + event_unref(&e_wrongname_wrongstr); + test_end(); +} + +static void test_event_filter_named_separate_from_str(void) +{ + struct event_filter *filter; + const char *error; + const struct failure_context failure_ctx = { + .type = LOG_TYPE_DEBUG + }; + + test_begin("event filter: event name separate from str"); + + filter = event_filter_create(); + struct event *e_named = event_create(NULL); + event_set_name(e_named, "named"); + struct event *e_noname = event_create(NULL); + event_add_str(e_noname, "str", "str"); + + test_assert(event_filter_parse("event=named", filter, &error) == 0); + test_assert(event_filter_parse("str=str", filter, &error) == 0); + test_assert(!filter->named_queries_only); + test_assert(event_filter_match(filter, e_named, &failure_ctx)); + test_assert(event_filter_match(filter, e_noname, &failure_ctx)); + + event_filter_unref(&filter); + event_unref(&e_named); + event_unref(&e_noname); + test_end(); +} + +void test_event_filter(void) +{ + test_event_filter_override_parent_fields(); + test_event_filter_override_global_fields(); + test_event_filter_clear_parent_fields(); + test_event_filter_clear_global_fields(); + test_event_filter_inc_int(); + test_event_filter_parent_category_match(); + test_event_filter_strlist(); + test_event_filter_strlist_recursive(); + test_event_filter_strlist_global_events(); + test_event_filter_named_and_str(); + test_event_filter_named_or_str(); + test_event_filter_named_separate_from_str(); +} diff --git a/src/lib/test-event-flatten.c b/src/lib/test-event-flatten.c new file mode 100644 index 0000000..526366c --- /dev/null +++ b/src/lib/test-event-flatten.c @@ -0,0 +1,391 @@ +/* Copyright (c) 2019 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "ioloop.h" +#include "time-util.h" +#include "lib-event-private.h" +#include "failures-private.h" +#include "array.h" +#include "str.h" + +#define CHECK_FLATTEN_SAME(e) \ + check_event_same(event_flatten(e), (e)) + +#define CHECK_FLATTEN_DIFF(e, c, nc, f, nf) \ + check_event_diff(event_flatten(e), (e), \ + (c), (nc), \ + (f), (nf)) + +static struct event_category cats[] = { + { .name = "cat0", }, + { .name = "cat1", }, +}; + +static void check_event_diff_cats(struct event_category *const *got, + unsigned int ngot, const char **exp, + unsigned int nexp) +{ + unsigned int i; + + test_assert(ngot == nexp); + + for (i = 0; i < nexp; i++) + test_assert(strcmp(got[i]->name, exp[i]) == 0); +} + +static void check_event_diff_fields(const struct event_field *got, unsigned int ngot, + const struct event_field *exp, unsigned int nexp) +{ + unsigned int i; + const char *got_str; + + test_assert(ngot == nexp); + + for (i = 0; i < nexp; i++) { + if (got[i].value_type != exp[i].value_type) { + test_assert(FALSE); + continue; + } + + switch (exp[i].value_type) { + case EVENT_FIELD_VALUE_TYPE_STR: + test_assert(strcmp(exp[i].value.str, + got[i].value.str) == 0); + break; + case EVENT_FIELD_VALUE_TYPE_INTMAX: + test_assert(exp[i].value.intmax == got[i].value.intmax); + break; + case EVENT_FIELD_VALUE_TYPE_TIMEVAL: + test_assert(timeval_cmp(&exp[i].value.timeval, + &got[i].value.timeval) == 0); + break; + case EVENT_FIELD_VALUE_TYPE_STRLIST: + got_str = t_array_const_string_join(&got[i].value.strlist, ","); + test_assert_strcmp(exp[i].value.str, got_str); + break; + } + } +} + +static void check_event_diff(struct event *e, struct event *orig, + const char **expected_cats, + unsigned int num_expected_cats, + const struct event_field *expected_fields, + unsigned int num_expected_fields) +{ + struct event_category *const *cats; + const struct event_field *fields; + unsigned int num_cats; + unsigned int num_fields; + + test_assert(e != orig); + test_assert(e->parent == NULL); + + /* different pointers implies different ids */ + test_assert(e->id != orig->id); /* TODO: does this make sense? */ + + test_assert(timeval_cmp(&e->tv_created_ioloop, &orig->tv_created_ioloop) == 0); + test_assert(timeval_cmp(&e->tv_created, &orig->tv_created) == 0); + test_assert(timeval_cmp(&e->tv_last_sent, &orig->tv_last_sent) == 0); + + test_assert(strcmp(e->source_filename, orig->source_filename) == 0); + test_assert(e->source_linenum == orig->source_linenum); + + /* FIXME: check sending name? */ + + cats = event_get_categories(e, &num_cats); + check_event_diff_cats(cats, num_cats, + expected_cats, num_expected_cats); + + fields = event_get_fields(e, &num_fields); + check_event_diff_fields(fields, num_fields, + expected_fields, num_expected_fields); + + event_unref(&e); +} + +static void check_event_same(struct event *e, struct event *orig) +{ + test_assert(e == orig); + + /* the pointers are the same; nothing can possibly differ */ + + event_unref(&e); +} + +static void test_event_flatten_no_parent(void) +{ + struct event *e; + + test_begin("event flatten: no parent"); + + e = event_create(NULL); + + CHECK_FLATTEN_SAME(e); + + event_add_int(e, "abc", 4); + CHECK_FLATTEN_SAME(e); + + event_add_int(e, "def", 2); + CHECK_FLATTEN_SAME(e); + + event_add_str(e, "abc", "foo"); + CHECK_FLATTEN_SAME(e); + + event_add_category(e, &cats[0]); + CHECK_FLATTEN_SAME(e); + + event_unref(&e); + + test_end(); +} + +static void test_event_flatten_one_parent(void) +{ + static const char *exp_1cat[] = { + "cat0", + }; + static const char *exp_2cat[] = { + "cat1", + "cat0", + }; + static struct event_field exp_int = { + .key = "abc", + .value_type = EVENT_FIELD_VALUE_TYPE_INTMAX, + .value = { + .str = NULL, + .intmax = 42, + .timeval = {0,0}, + } + }; + static struct event_field exp_2int[2] = { + { + .key = "abc", + .value_type = EVENT_FIELD_VALUE_TYPE_INTMAX, + .value = { + .intmax = 42, + .str = NULL, + .timeval = {0,0}, + } + }, + { + .key = "def", + .value_type = EVENT_FIELD_VALUE_TYPE_INTMAX, + .value = { + .intmax = 49, + .str = NULL, + .timeval = {0,0}, + } + }, + }; + static struct event_field exp_1str1int[2] = { + { + .key = "abc", + .value_type = EVENT_FIELD_VALUE_TYPE_STR, + .value = { + .str = "foo", + .intmax = 0, + .timeval = {0,0}, + } + }, + { + .key = "def", + .value_type = EVENT_FIELD_VALUE_TYPE_INTMAX, + .value = { + .intmax = 49, + .str = NULL, + .timeval = {0,0}, + } + }, + }; + static struct event_field exp_1str1int1strlist[3] = { + { + .key = "abc", + .value_type = EVENT_FIELD_VALUE_TYPE_STR, + .value = { + .str = "foo", + .intmax = 0, + .timeval = {0,0}, + } + }, + { + .key = "def", + .value_type = EVENT_FIELD_VALUE_TYPE_INTMAX, + .value = { + .intmax = 49, + .str = NULL, + .timeval = {0,0}, + } + }, + { + .key = "cba", + .value_type = EVENT_FIELD_VALUE_TYPE_STRLIST, + .value = { + .str = "one,two,three", + }, + }, + }; + + struct event *parent; + struct event *e; + + test_begin("event flatten: one parent"); + + t_array_init(&exp_1str1int1strlist[0].value.strlist, 3); + const char *str = "one"; + array_push_back(&exp_1str1int1strlist[0].value.strlist, &str); + str = "two"; + array_push_back(&exp_1str1int1strlist[0].value.strlist, &str); + str = "three"; + array_push_back(&exp_1str1int1strlist[0].value.strlist, &str); + + parent = event_create(NULL); + + e = event_create(parent); + + CHECK_FLATTEN_DIFF(e, NULL, 0, NULL, 0); + + event_add_int(e, "abc", 42); + CHECK_FLATTEN_DIFF(e, NULL, 0, &exp_int, 1); + + event_add_int(e, "def", 49); + CHECK_FLATTEN_DIFF(e, NULL, 0, exp_2int, 2); + + event_add_str(e, "abc", "foo"); + CHECK_FLATTEN_DIFF(e, NULL, 0, exp_1str1int, 2); + + event_add_category(e, &cats[0]); + CHECK_FLATTEN_DIFF(e, exp_1cat, 1, exp_1str1int, 2); + + event_add_category(e, &cats[1]); + CHECK_FLATTEN_DIFF(e, exp_2cat, 2, exp_1str1int, 2); + + event_strlist_append(e, "cba", "one"); + event_strlist_append(e, "cba", "two"); + event_strlist_append(e, "cba", "three"); + CHECK_FLATTEN_DIFF(e, exp_2cat, 2, exp_1str1int1strlist, 3); + + event_unref(&e); + event_unref(&parent); + + test_end(); +} + +static void test_event_flatten_override_parent_field(void) +{ + static struct event_field exp_int = { + .key = "abc", + .value_type = EVENT_FIELD_VALUE_TYPE_INTMAX, + .value = { + .intmax = 42, + .str = NULL, + .timeval = {0,0}, + } + }; + static struct event_field exp_str = { + .key = "abc", + .value_type = EVENT_FIELD_VALUE_TYPE_STR, + .value = { + .str = "def", + .intmax = 0, + .timeval = {0,0}, + } + }; + static struct event_field exp_2str[2] = { + { + .key = "abc", + .value_type = EVENT_FIELD_VALUE_TYPE_STR, + .value = { + .str = "def", + .intmax = 0, + .timeval = {0,0}, + } + }, + { + .key = "foo", + .value_type = EVENT_FIELD_VALUE_TYPE_STR, + .value = { + .str = "bar", + .intmax = 0, + .timeval = {0,0}, + } + }, + }; + struct event *parent; + struct event *e; + + test_begin("event flatten: override parent field"); + + parent = event_create(NULL); + + event_add_int(parent, "abc", 5); + + e = event_create(parent); + + event_add_int(e, "abc", 42); + + CHECK_FLATTEN_DIFF(e, NULL, 0, &exp_int, 1); + + event_add_str(e, "abc", "def"); + CHECK_FLATTEN_DIFF(e, NULL, 0, &exp_str, 1); + + event_add_str(parent, "foo", "bar"); + CHECK_FLATTEN_DIFF(e, NULL, 0, exp_2str, 2); + + event_unref(&e); + event_unref(&parent); + + test_end(); +} + +static void test_event_strlist_flatten(void) +{ + test_begin("event flatten: strlist"); + struct event *l1 = event_create(NULL); + event_strlist_append(l1, "test", "l3"); + struct event *l2 = event_create(l1); + event_strlist_append(l2, "test", "l1"); + struct event *l3 = event_create(l2); + unsigned int line = __LINE__ - 1; + event_strlist_append(l3, "test", "l2"); + + string_t *dest = t_str_new(32); + struct event *event = event_flatten(l3); + + event_export(event, dest); + /* see if it matches .. */ + const char *reference = t_strdup_printf("%"PRIdTIME_T"\t%u" + "\ts"__FILE__ + "\t%u\tLtest\t3\tl3\tl1\tl2", + event->tv_created.tv_sec, + (unsigned int)event->tv_created.tv_usec, + line); + test_assert_strcmp(str_c(dest), reference); + + /* these should not end up duplicated */ + event_strlist_append(event, "test", "l1"); + event_strlist_append(event, "test", "l2"); + event_strlist_append(event, "test", "l3"); + + /* and export should look the same */ + str_truncate(dest, 0); + event_export(event, dest); + test_assert_strcmp(str_c(dest), reference); + + event_unref(&event); + + /* export event */ + event_unref(&l3); + event_unref(&l2); + event_unref(&l1); + + test_end(); +} + +void test_event_flatten(void) +{ + test_event_flatten_no_parent(); + test_event_flatten_one_parent(); + test_event_flatten_override_parent_field(); + test_event_strlist_flatten(); +} diff --git a/src/lib/test-event-log.c b/src/lib/test-event-log.c new file mode 100644 index 0000000..286a981 --- /dev/null +++ b/src/lib/test-event-log.c @@ -0,0 +1,2528 @@ +/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "ioloop.h" +#include "str.h" +#include "failures-private.h" + +#include <unistd.h> + +enum test_log_event_type { + TYPE_END, + TYPE_PREFIX_APPEND, + TYPE_PREFIX_REPLACE, + TYPE_PREFIX_APPEND_CB, + TYPE_PREFIX_REPLACE_CB, + TYPE_MESSAGE_AMEND, + TYPE_SKIP, +}; + +enum test_log_event_flag { + FLAG_BASE_EVENT = BIT(0), + FLAG_DROP_PREFIXES_1 = BIT(1), + FLAG_DROP_PREFIXES_2 = BIT(2), + FLAG_DROP_PREFIXES_4 = BIT(3), +}; + +enum test_log_flag { + FLAG_NO_SEND = BIT(0), +}; + +struct test_log_event { + enum test_log_event_type type; + const char *str; + enum test_log_event_flag flags; +}; + +struct test_log { + const struct test_log_event *prefixes; + const char *global_log_prefix; + const char *base_send_prefix; + const char *base_str_prefix; + const char *result; + const char *result_str_out; + enum test_log_flag flags; +}; + +static char *test_output = NULL; + +static void ATTR_FORMAT(2, 0) +info_handler(const struct failure_context *ctx, + const char *format, va_list args) +{ + size_t prefix_len; + + i_assert(ctx->type == LOG_TYPE_INFO); + + i_free(test_output); + T_BEGIN { + string_t *str = failure_handler.v->format(ctx, &prefix_len, + format, args); + test_output = i_strdup(str_c(str)); + } T_END; +} + +static void ATTR_FORMAT(2, 0) +error_handler(const struct failure_context *ctx, + const char *format, va_list args) +{ + size_t prefix_len; + + i_assert(ctx->type == LOG_TYPE_WARNING || + ctx->type == LOG_TYPE_ERROR); + + i_free(test_output); + T_BEGIN { + string_t *str = failure_handler.v->format(ctx, &prefix_len, + format, args); + test_output = i_strdup(str_c(str)); + } T_END; +} + +static const char * +test_event_log_prefix_cb(char *prefix) +{ + return t_strdup_printf("callback(%s)", prefix); +} + +static const char * +test_event_log_message_cb(char *prefix, + enum log_type log_type ATTR_UNUSED, + const char *message) +{ + return t_strdup_printf("[%s%s]", prefix, message); +} + +static void test_event_log_message(void) +{ + struct test_log tests[] = { + { + .prefixes = (const struct test_log_event []) { + { .type = TYPE_END } + }, + .global_log_prefix = "global1.", + .result = "global1.Info: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2.Info: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: appended2.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global2.", + .result = "global2.Info: appended1,TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: appended1,appended2.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: " + "appended1,appended2.appended3.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2.Info: appended3#TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { .type = TYPE_END } + }, + .result = "replaced4;Info: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { TYPE_PREFIX_APPEND, "appended5-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced4;Info: appended5-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND_CB, "appended1-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: callback(appended1-)TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced1.", 0 }, + { TYPE_PREFIX_REPLACE_CB, "replaced2-", 0 }, + { .type = TYPE_END } + }, + .result = "callback(replaced2-)Info: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE_CB, "replaced1.", 0 }, + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { .type = TYPE_END } + }, + .result = "callback(replaced1.)Info: appended1,TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE_CB, "replaced1.", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2-Info: TEXT", + }, + /* Tests involving event_set_log_message_callback() */ + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-" , 0}, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-[amended2-TEXT]]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-appended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: appended1-[amended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: " + "appended1-[amended1-appended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-appended1-" + "[amended2-appended2-TEXT]]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: [amended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2,", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2,Info: [amended2-TEXT]", + }, + /* Tests with params->base_str_out != NULL */ + { + .prefixes = (const struct test_log_event []) { + { .type = TYPE_END } + }, + .global_log_prefix = "global1.", + .result = "global1.Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2.Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2.Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .result = "replaced2.Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: appended2.TEXT", + .result_str_out = "appended2.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: appended2.TEXT", + .result_str_out = "appended2.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: appended2.TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced1,", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global2.", + .result = "global2.Info: appended1,TEXT", + .result_str_out = "appended1,TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global2.", + .result = "global2.Info: appended1,TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: appended1,appended2.TEXT", + .result_str_out = "appended1,appended2.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: appended1,appended2.TEXT", + .result_str_out = "appended2.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: appended1,appended2.TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: " + "appended1,appended2.appended3.TEXT", + .result_str_out = "appended1,appended2.appended3.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: " + "appended1,appended2.appended3.TEXT", + .result_str_out = "appended2.appended3.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: " + "appended1,appended2.appended3.TEXT", + .result_str_out = "appended3.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: " + "appended1,appended2.appended3.TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2.Info: appended3#TEXT", + .result_str_out = "appended3#TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2.Info: appended3#TEXT", + .result_str_out = "appended3#TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2.Info: appended3#TEXT", + .result_str_out = "appended3#TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .result = "replaced2.Info: appended3#TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { .type = TYPE_END } + }, + .result = "replaced4;Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { .type = TYPE_END } + }, + .result = "replaced4;Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { .type = TYPE_END } + }, + .result = "replaced4;Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { .type = TYPE_END } + }, + .result = "replaced4;Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .result = "replaced4;Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { TYPE_PREFIX_APPEND, "appended5-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced4;Info: appended5-TEXT", + .result_str_out = "appended5-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { TYPE_PREFIX_APPEND, "appended5-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced4;Info: appended5-TEXT", + .result_str_out = "appended5-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { TYPE_PREFIX_APPEND, "appended5-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced4;Info: appended5-TEXT", + .result_str_out = "appended5-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { TYPE_PREFIX_APPEND, "appended5-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced4;Info: appended5-TEXT", + .result_str_out = "appended5-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended5-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced4;Info: appended5-TEXT", + .result_str_out = "appended5-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { TYPE_PREFIX_APPEND, "appended5-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .result = "replaced4;Info: appended5-TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND_CB, "appended1-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: callback(appended1-)TEXT", + .result_str_out = "callback(appended1-)TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND_CB, "appended1-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: callback(appended1-)TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced1.", 0 }, + { TYPE_PREFIX_REPLACE_CB, "replaced2-", 0 }, + { .type = TYPE_END } + }, + .result = "callback(replaced2-)Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced1.", 0 }, + { TYPE_PREFIX_REPLACE_CB, "replaced2-", 0 }, + { .type = TYPE_END } + }, + .result = "callback(replaced2-)Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced1.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE_CB, "replaced2-", 0 }, + { .type = TYPE_END } + }, + .result = "callback(replaced2-)Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced1.", 0 }, + { TYPE_PREFIX_REPLACE_CB, "replaced2-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .result = "callback(replaced2-)Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE_CB, "replaced1.", 0 }, + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { .type = TYPE_END } + }, + .result = "callback(replaced1.)Info: appended1,TEXT", + .result_str_out = "appended1,TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE_CB, "replaced1.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { .type = TYPE_END } + }, + .result = "callback(replaced1.)Info: appended1,TEXT", + .result_str_out = "appended1,TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE_CB, "replaced1.", 0 }, + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .result = "callback(replaced1.)Info: appended1,TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE_CB, "replaced1.", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2-Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE_CB, "replaced1.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced2-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2-Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE_CB, "replaced1.", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .result = "replaced2-Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-" , 0}, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-TEXT]", + .result_str_out = "[amended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-" , + FLAG_BASE_EVENT}, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-TEXT]", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-[amended2-TEXT]]", + .result_str_out = "[amended1-[amended2-TEXT]]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-[amended2-TEXT]]", + .result_str_out = "[amended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-[amended2-TEXT]]", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-appended1-TEXT]", + .result_str_out = "[amended1-appended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-appended1-TEXT]", + .result_str_out = "appended1-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-appended1-TEXT]", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: appended1-[amended1-TEXT]", + .result_str_out = "appended1-[amended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: appended1-[amended1-TEXT]", + .result_str_out = "[amended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: appended1-[amended1-TEXT]", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: " + "appended1-[amended1-appended2-TEXT]", + .result_str_out = "appended1-[amended1-appended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: " + "appended1-[amended1-appended2-TEXT]", + .result_str_out = "[amended1-appended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: " + "appended1-[amended1-appended2-TEXT]", + .result_str_out = "appended2-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: " + "appended1-[amended1-appended2-TEXT]", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-appended1-" + "[amended2-appended2-TEXT]]", + .result_str_out = "[amended1-appended1-" + "[amended2-appended2-TEXT]]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-appended1-" + "[amended2-appended2-TEXT]]", + .result_str_out = "appended1-[amended2-appended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-appended1-" + "[amended2-appended2-TEXT]]", + .result_str_out = "[amended2-appended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-appended1-" + "[amended2-appended2-TEXT]]", + .result_str_out = "appended2-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = "global4.Info: [amended1-appended1-" + "[amended2-appended2-TEXT]]", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: [amended1-TEXT]", + .result_str_out = "[amended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: [amended1-TEXT]", + .result_str_out = "[amended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .result = "replaced1,Info: [amended1-TEXT]", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2,", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2,Info: [amended2-TEXT]", + .result_str_out = "[amended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2,", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2,Info: [amended2-TEXT]", + .result_str_out = "[amended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced2,", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2,Info: [amended2-TEXT]", + .result_str_out = "[amended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2,", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { .type = TYPE_END } + }, + .result = "replaced2,Info: [amended2-TEXT]", + .result_str_out = "[amended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2,", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .result = "replaced2,Info: [amended2-TEXT]", + .result_str_out = "TEXT", + }, + /* Tests involving params->no_send */ + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = NULL, + .result_str_out = "appended3.TEXT", + .flags = FLAG_NO_SEND, + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .result = NULL, + .result_str_out = "[amended2-appended2-TEXT]", + .flags = FLAG_NO_SEND, + }, + /* Tests with params->base_*_prefix assigned */ + { + .prefixes = (const struct test_log_event []) { + { .type = TYPE_END } + }, + .global_log_prefix = "global1.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global1.Info: PREFIX: TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + + .result = "replaced1,Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced1,Info: PREFIX: TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2.Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2.Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2.Info: PREFIX: TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced1,Info: appended2.TEXT", + .result_str_out = "appended2.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced1,Info: PREFIX: appended2.TEXT", + .result_str_out = "STR_PREFIX: appended2.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced1,Info: appended2.PREFIX: TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced1,Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced1,Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced1,", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced1,Info: PREFIX: TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global2.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global2.Info: PREFIX: appended1,TEXT", + .result_str_out = "STR_PREFIX: appended1,TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global2.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global2.Info: appended1,PREFIX: TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global3.Info: PREFIX: " + "appended1,appended2.TEXT", + .result_str_out = "STR_PREFIX: " + "appended1,appended2.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global3.Info: appended1,PREFIX: " + "appended2.TEXT", + .result_str_out = "STR_PREFIX: appended2.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global3.Info: appended1,appended2." + "PREFIX: TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global3.Info: PREFIX: " + "appended1,appended2.appended3.TEXT", + .result_str_out = "STR_PREFIX: " + "appended1,appended2.appended3.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global3.Info: appended1,PREFIX: " + "appended2.appended3.TEXT", + .result_str_out = "STR_PREFIX: " + "appended2.appended3.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global3.Info: appended1,appended2.PREFIX: " + "appended3.TEXT", + .result_str_out = "STR_PREFIX: appended3.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global3.Info: " + "appended1,appended2.appended3.PREFIX: TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2.Info: appended3#TEXT", + .result_str_out = "appended3#TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2.Info: appended3#TEXT", + .result_str_out = "appended3#TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2.Info: PREFIX: appended3#TEXT", + .result_str_out = "STR_PREFIX: appended3#TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2.Info: appended3#PREFIX: TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced4;Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced4;Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced4;Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced4;Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced4;Info: PREFIX: TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { TYPE_PREFIX_APPEND, "appended5-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced4;Info: appended5-TEXT", + .result_str_out = "appended5-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { TYPE_PREFIX_APPEND, "appended5-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced4;Info: appended5-TEXT", + .result_str_out = "appended5-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { TYPE_PREFIX_APPEND, "appended5-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced4;Info: appended5-TEXT", + .result_str_out = "appended5-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { TYPE_PREFIX_APPEND, "appended5-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced4;Info: appended5-TEXT", + .result_str_out = "appended5-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended5-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced4;Info: PREFIX: appended5-TEXT", + .result_str_out = "STR_PREFIX: appended5-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3#", 0 }, + { TYPE_PREFIX_REPLACE, "replaced4;", 0 }, + { TYPE_PREFIX_APPEND, "appended5-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced4;Info: appended5-PREFIX: TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND_CB, "appended1-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global3.Info: PREFIX: " + "callback(appended1-)TEXT", + .result_str_out = "STR_PREFIX: " + "callback(appended1-)TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND_CB, "appended1-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global3.Info: callback(appended1-)PREFIX: " + "TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced1.", 0 }, + { TYPE_PREFIX_REPLACE_CB, "replaced2-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "callback(replaced2-)Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced1.", 0 }, + { TYPE_PREFIX_REPLACE_CB, "replaced2-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "callback(replaced2-)Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced1.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE_CB, "replaced2-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "callback(replaced2-)Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_REPLACE, "replaced1.", 0 }, + { TYPE_PREFIX_REPLACE_CB, "replaced2-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "callback(replaced2-)Info: PREFIX: TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE_CB, "replaced1.", 0 }, + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "callback(replaced1.)Info: appended1,TEXT", + .result_str_out = "appended1,TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE_CB, "replaced1.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "callback(replaced1.)Info: PREFIX: " + "appended1,TEXT", + .result_str_out = "STR_PREFIX: appended1,TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE_CB, "replaced1.", 0 }, + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "callback(replaced1.)Info: appended1,PREFIX: " + "TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE_CB, "replaced1.", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2-Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE_CB, "replaced1.", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced2-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2-Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE_CB, "replaced1.", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2-Info: PREFIX: TEXT", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-" , 0}, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: PREFIX: [amended1-TEXT]", + .result_str_out = "STR_PREFIX: [amended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-" , + FLAG_BASE_EVENT}, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: [amended1-PREFIX: TEXT]", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: PREFIX: " + "[amended1-[amended2-TEXT]]", + .result_str_out = "STR_PREFIX: " + "[amended1-[amended2-TEXT]]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: [amended1-PREFIX: " + "[amended2-TEXT]]", + .result_str_out = "STR_PREFIX: [amended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: [amended1-[amended2-PREFIX: " + "TEXT]]", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: PREFIX: " + "[amended1-appended1-TEXT]", + .result_str_out = "STR_PREFIX: " + "[amended1-appended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: [amended1-PREFIX: " + "appended1-TEXT]", + .result_str_out = "STR_PREFIX: appended1-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: [amended1-appended1-PREFIX: " + "TEXT]", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: PREFIX: " + "appended1-[amended1-TEXT]", + .result_str_out = "STR_PREFIX: " + "appended1-[amended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: appended1-PREFIX: " + "[amended1-TEXT]", + .result_str_out = "STR_PREFIX: [amended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: appended1-[amended1-PREFIX: " + "TEXT]", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: PREFIX: " + "appended1-[amended1-appended2-TEXT]", + .result_str_out = "STR_PREFIX: " + "appended1-[amended1-appended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: appended1-PREFIX: " + "[amended1-appended2-TEXT]", + .result_str_out = "STR_PREFIX: " + "[amended1-appended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: appended1-[amended1-PREFIX: " + "appended2-TEXT]", + .result_str_out = "STR_PREFIX: appended2-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: " + "appended1-[amended1-appended2-PREFIX: TEXT]", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: PREFIX: [amended1-appended1-" + "[amended2-appended2-TEXT]]", + .result_str_out = "STR_PREFIX: [amended1-appended1-" + "[amended2-appended2-TEXT]]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: [amended1-PREFIX: appended1-" + "[amended2-appended2-TEXT]]", + .result_str_out = "STR_PREFIX: " + "appended1-[amended2-appended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: [amended1-appended1-PREFIX: " + "[amended2-appended2-TEXT]]", + .result_str_out = "STR_PREFIX: " + "[amended2-appended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_APPEND, "appended2-", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: [amended1-appended1-" + "[amended2-PREFIX: appended2-TEXT]]", + .result_str_out = "STR_PREFIX: appended2-TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_APPEND, "appended1-", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { TYPE_PREFIX_APPEND, "appended2-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .global_log_prefix = "global4.", + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "global4.Info: [amended1-appended1-" + "[amended2-appended2-PREFIX: TEXT]]", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced1,Info: [amended1-TEXT]", + .result_str_out = "[amended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced1,Info: PREFIX: [amended1-TEXT]", + .result_str_out = "STR_PREFIX: [amended1-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced1,Info: [amended1-PREFIX: TEXT]", + .result_str_out = "STR_PREFIX: TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2,", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2,Info: [amended2-TEXT]", + .result_str_out = "[amended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2,", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2,Info: [amended2-TEXT]", + .result_str_out = "[amended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", + FLAG_BASE_EVENT }, + { TYPE_PREFIX_REPLACE, "replaced2,", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2,Info: [amended2-TEXT]", + .result_str_out = "[amended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2,", + FLAG_BASE_EVENT }, + { TYPE_MESSAGE_AMEND, "amended2-", 0 }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2,Info: PREFIX: [amended2-TEXT]", + .result_str_out = "STR_PREFIX: [amended2-TEXT]", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_REPLACE, "replaced1,", 0 }, + { TYPE_MESSAGE_AMEND, "amended1-", 0 }, + { TYPE_PREFIX_REPLACE, "replaced2,", 0 }, + { TYPE_MESSAGE_AMEND, "amended2-", + FLAG_BASE_EVENT }, + { .type = TYPE_END } + }, + .base_send_prefix = "PREFIX: ", + .base_str_prefix = "STR_PREFIX: ", + .result = "replaced2,Info: [amended2-PREFIX: TEXT]", + .result_str_out = "STR_PREFIX: TEXT", + }, + /* Tests in which parent log prefixes are dropped by an event + lower in the hierarchy. */ + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { TYPE_PREFIX_APPEND, "appended4.", 0 }, + { TYPE_PREFIX_APPEND, "appended5.", 0 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: " + "appended1,appended2.appended3." + "appended4.appended5.TEXT", + .result_str_out = "appended1,appended2.appended3." + "appended4.appended5.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { TYPE_PREFIX_APPEND, "appended4.", 0 }, + { TYPE_PREFIX_APPEND, "appended5.", + FLAG_DROP_PREFIXES_1 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: " + "appended1,appended2.appended3." + "appended5.TEXT", + .result_str_out = "appended1,appended2.appended3." + "appended5.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { TYPE_PREFIX_APPEND, "appended4.", 0 }, + { TYPE_PREFIX_APPEND, "appended5.", 0 }, + { TYPE_SKIP, NULL, FLAG_DROP_PREFIXES_1 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: " + "appended1,appended2.appended3." + "appended4.TEXT", + .result_str_out = "appended1,appended2.appended3." + "appended4.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { TYPE_PREFIX_APPEND, "appended4.", 0 }, + { TYPE_PREFIX_APPEND, "appended5.", + FLAG_DROP_PREFIXES_2 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: " + "appended1,appended2.appended5.TEXT", + .result_str_out = "appended1,appended2.appended5.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { TYPE_PREFIX_APPEND, "appended4.", 0 }, + { TYPE_PREFIX_APPEND, "appended5.", + (FLAG_DROP_PREFIXES_1 | + FLAG_DROP_PREFIXES_2) }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: appended1,appended5.TEXT", + .result_str_out = "appended1,appended5.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { TYPE_PREFIX_APPEND, "appended4.", 0 }, + { TYPE_PREFIX_APPEND, "appended5.", + FLAG_DROP_PREFIXES_4 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: appended5.TEXT", + .result_str_out = "appended5.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { TYPE_PREFIX_APPEND, "appended4.", 0 }, + { TYPE_PREFIX_APPEND, "appended5.", + (FLAG_DROP_PREFIXES_1 | + FLAG_DROP_PREFIXES_2 | + FLAG_DROP_PREFIXES_4) }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: appended5.TEXT", + .result_str_out = "appended5.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { TYPE_PREFIX_APPEND, "appended4.", 0 }, + { TYPE_PREFIX_APPEND, "appended5.", 0 }, + { TYPE_SKIP, NULL, (FLAG_DROP_PREFIXES_1 | + FLAG_DROP_PREFIXES_2 | + FLAG_DROP_PREFIXES_4) }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: TEXT", + .result_str_out = "TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", + FLAG_DROP_PREFIXES_1 }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { TYPE_PREFIX_APPEND, "appended4.", 0 }, + { TYPE_PREFIX_APPEND, "appended5.", + FLAG_DROP_PREFIXES_1 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: " + "appended2.appended3.appended5.TEXT", + .result_str_out = "appended2.appended3.appended5.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", + FLAG_DROP_PREFIXES_1 }, + { TYPE_PREFIX_APPEND, "appended2.", + FLAG_DROP_PREFIXES_1 }, + { TYPE_PREFIX_APPEND, "appended3.", + FLAG_DROP_PREFIXES_1 }, + { TYPE_PREFIX_APPEND, "appended4.", + FLAG_DROP_PREFIXES_1 }, + { TYPE_PREFIX_APPEND, "appended5.", + FLAG_DROP_PREFIXES_1 }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: appended5.TEXT", + .result_str_out = "appended5.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { .type = TYPE_SKIP }, + { TYPE_PREFIX_APPEND, "appended2.", + FLAG_DROP_PREFIXES_1 }, + { .type = TYPE_SKIP }, + { TYPE_PREFIX_APPEND, "appended3.", 0 }, + { .type = TYPE_SKIP }, + { TYPE_PREFIX_APPEND, "appended4.", 0 }, + { .type = TYPE_SKIP }, + { TYPE_PREFIX_APPEND, "appended5.", + FLAG_DROP_PREFIXES_1 }, + { .type = TYPE_SKIP }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: " + "appended2.appended3.appended5.TEXT", + .result_str_out = "appended2.appended3.appended5.TEXT", + }, + { + .prefixes = (const struct test_log_event []) { + { TYPE_PREFIX_APPEND, "appended1,", 0 }, + { TYPE_PREFIX_APPEND, "appended2.", 0 }, + { TYPE_PREFIX_APPEND, "appended3.", + FLAG_DROP_PREFIXES_1 }, + { TYPE_PREFIX_APPEND, "appended4.", 0 }, + { TYPE_PREFIX_APPEND, "appended5.", + (FLAG_DROP_PREFIXES_1 | + FLAG_DROP_PREFIXES_2) }, + { .type = TYPE_END } + }, + .global_log_prefix = "global3.", + .result = "global3.Info: appended5.TEXT", + .result_str_out = "appended5.TEXT", + }, + }; + + test_begin("event log message"); + + failure_callback_t *orig_fatal, *orig_error, *orig_info, *orig_debug; + i_get_failure_handlers(&orig_fatal, &orig_error, &orig_info, &orig_debug); + i_set_info_handler(info_handler); + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) T_BEGIN { + const struct test_log *test = &tests[i]; + struct event_log_params params = { + .log_type = LOG_TYPE_INFO, + .base_send_prefix = test->base_send_prefix, + .base_str_prefix = test->base_str_prefix, + .no_send = ((test->flags & FLAG_NO_SEND) != 0), + }; + + i_free(test_output); + if (test->global_log_prefix != NULL) + i_set_failure_prefix("%s", test->global_log_prefix); + else + i_set_failure_prefix("UNEXPECTED GLOBAL PREFIX"); + + struct event *event, *parent; + event = parent = event_create(NULL); + for (unsigned int j = 0; test->prefixes[j].type != TYPE_END; j++) { + unsigned int drop_prefixes = 0; + + if (event == NULL) { + struct event *child = event_create(parent); + event_unref(&parent); + event = parent = child; + } + if ((test->prefixes[j].flags & FLAG_BASE_EVENT) != 0) { + i_assert(params.base_event == NULL); + params.base_event = event; + } + if ((test->prefixes[j].flags & + FLAG_DROP_PREFIXES_1) != 0) + drop_prefixes += 1; + if ((test->prefixes[j].flags & + FLAG_DROP_PREFIXES_2) != 0) + drop_prefixes += 2; + if ((test->prefixes[j].flags & + FLAG_DROP_PREFIXES_4) != 0) + drop_prefixes += 4; + event_drop_parent_log_prefixes(event, drop_prefixes); + + switch (test->prefixes[j].type) { + case TYPE_END: + i_unreached(); + case TYPE_PREFIX_APPEND: + event_set_append_log_prefix(event, test->prefixes[j].str); + break; + case TYPE_PREFIX_REPLACE: + event_replace_log_prefix(event, test->prefixes[j].str); + break; + case TYPE_PREFIX_APPEND_CB: + event_set_log_prefix_callback(event, FALSE, + test_event_log_prefix_cb, + (char*)test->prefixes[j].str); + break; + case TYPE_PREFIX_REPLACE_CB: + event_set_log_prefix_callback(event, TRUE, + test_event_log_prefix_cb, + (char*)test->prefixes[j].str); + break; + case TYPE_MESSAGE_AMEND: + event_set_log_message_callback(event, + test_event_log_message_cb, + (char*)test->prefixes[j].str); + break; + case TYPE_SKIP: + break; + } + event = NULL; + } + event = parent; + + if (test->result_str_out != NULL) { + /* Use small value so buffer size grows. This way the + unit test fails if anyone attempts to add data-stack + frame to event_log(). */ + params.base_str_out = t_str_new(1); + } + event_log(event, ¶ms, "TEXT"); + + test_assert_strcmp(test->result, test_output); + if (test->result_str_out != NULL) { + test_assert_strcmp(test->result_str_out, + str_c(params.base_str_out)); + } + event_unref(&event); + } T_END; + i_set_info_handler(orig_info); + i_unset_failure_prefix(); + i_free(test_output); + test_end(); +} + +static void test_event_duration() +{ + uintmax_t duration; + test_begin("event duration"); + struct event *e = event_create(NULL); + usleep(10); + e_info(e, "Submit event"); + event_get_last_duration(e, &duration); + test_assert(duration > 0); + event_unref(&e); + test_end(); +} + +static void test_event_log_level(void) +{ + test_begin("event log level"); + failure_callback_t *orig_fatal, *orig_error, *orig_info, *orig_debug; + i_get_failure_handlers(&orig_fatal, &orig_error, &orig_info, &orig_debug); + i_set_info_handler(info_handler); + i_set_error_handler(error_handler); + + struct event *event = event_create(NULL); + event_set_min_log_level(event, LOG_TYPE_WARNING); + errno = EACCES; + e_info(event, "Info event"); + test_assert(test_output == NULL); + e_warning(event, "Warning event"); + test_assert_strcmp(test_output, "Warning: Warning event"); + event_unref(&event); + i_set_info_handler(orig_info); + i_set_error_handler(orig_error); + i_free(test_output); + test_assert(errno == EACCES); + test_end(); +} + +void test_event_log(void) +{ + test_event_log_message(); + test_event_duration(); + test_event_log_level(); +} diff --git a/src/lib/test-failures.c b/src/lib/test-failures.c new file mode 100644 index 0000000..c76d5b8 --- /dev/null +++ b/src/lib/test-failures.c @@ -0,0 +1,176 @@ +/* Copyright (c) 2001-2018 Dovecot authors, see the included COPYING file */ + +/* Unit tests for failure helpers */ + +#include "test-lib.h" +#include "hostpid.h" +#include "istream.h" +#include "failures.h" + +#include <unistd.h> + +static int handlers_set_me; + +static void test_failures_handler(const struct failure_context *ctx, + const char *format ATTR_UNUSED, + va_list args ATTR_UNUSED) +{ + handlers_set_me = ctx->type; +} +static void test_get_set_handlers(void) +{ + failure_callback_t *handlers[4]; + test_begin("get_handlers"); + i_get_failure_handlers(handlers, handlers+1, handlers+2, handlers+3); + test_end(); + + test_begin("set_handlers"); + + i_set_debug_handler(&test_failures_handler); + i_debug("If you see this debug, something's gone wrong"); + test_assert(handlers_set_me == LOG_TYPE_DEBUG); + i_set_debug_handler(handlers[3]); + + i_set_info_handler(&test_failures_handler); + i_info("If you see this info, something's gone wrong"); + test_assert(handlers_set_me == LOG_TYPE_INFO); + i_set_info_handler(handlers[2]); + + i_set_error_handler(&test_failures_handler); + i_warning("If you see this warning, something's gone wrong"); + test_assert(handlers_set_me == LOG_TYPE_WARNING); + i_error("If you see this error, something's gone wrong"); + test_assert(handlers_set_me == LOG_TYPE_ERROR); + i_set_error_handler(handlers[1]); + + //i_set_fatal_handler(&test_failures_handler); + //i_fatal("If you see this fatal, something's gone wrong"); + //test_assert(handlers_set_me == LOG_TYPE_FATAL); + //i_set_fatal_handler(handlers[0]); + + test_end(); +} +static void test_expected(void) +{ + test_begin("expected messages"); + test_expect_errors(1); + i_warning("deliberate warning - not suppressed"); + test_expect_no_more_errors(); + test_end(); +} +static void test_expected_str(void) +{ + test_begin("expected strings in messages"); + test_expect_error_string("be unhappy"); + i_error("deliberate error - suppressed - be unhappy if you see this"); + test_expect_no_more_errors(); + test_end(); +} + +static bool +internal_line_match(const char *line, const char *prefix, const char *text) +{ + if (line == NULL) + return FALSE; + + if (line[0] != '\001') + return FALSE; + uint8_t type = (uint8_t)line[1]; + if (type != ((LOG_TYPE_DEBUG+1) | 0x80)) + return FALSE; + line += 2; + + if (!str_begins(line, "123 ")) + return FALSE; + line += 4; + + if (!str_begins(line, prefix)) + return FALSE; + line += strlen(prefix); + + return strcmp(line, text) == 0; +} + +static void test_internal_split(void) +{ + int fd[2]; + + test_begin("splitting long internal log lines"); + + char long_log_prefix[PIPE_BUF+1]; + memset(long_log_prefix, 'X', sizeof(long_log_prefix)-1); + long_log_prefix[sizeof(long_log_prefix)-1] = '\0'; + +#define TEXT10 "tttttttttt" +#define TEXT128 TEXT10 TEXT10 TEXT10 TEXT10 TEXT10 TEXT10 TEXT10 TEXT10 \ + TEXT10 TEXT10 TEXT10 TEXT10 "tttttttt" + + char long_lext[PIPE_BUF*2+1]; + memset(long_lext, 'T', sizeof(long_lext)-1); + long_lext[sizeof(long_lext)-1] = '\0'; + + if (pipe(fd) < 0) + i_fatal("pipe() failed: %m"); + switch (fork()) { + case (pid_t)-1: + i_fatal("fork() failed: %m"); + case 0: + /* child - log writer */ + if (dup2(fd[1], STDERR_FILENO) < 0) + i_fatal("dup2() failed: %m"); + i_close_fd(&fd[0]); + i_close_fd(&fd[1]); + + struct failure_context ctx = { + .type = LOG_TYPE_DEBUG, + .log_prefix = long_log_prefix, + }; + + i_set_failure_internal(); + my_pid = "123"; + i_log_type(&ctx, "little text"); + i_log_type(&ctx, TEXT128 TEXT128 TEXT128); + ctx.log_prefix = ""; + i_log_type(&ctx, "%s", long_lext); + test_exit(0); + case 1: + /* parent - log reader */ + i_close_fd(&fd[1]); + break; + } + + alarm(10); + struct istream *input = i_stream_create_fd(fd[0], SIZE_MAX); + + /* long prefix, little text */ + const char *line = i_stream_read_next_line(input); + test_assert(internal_line_match(line, long_log_prefix, "little text")); + + /* long prefix, text split to multiple lines */ + for (unsigned int i = 0; i < 3; i++) { + line = i_stream_read_next_line(input); + test_assert(internal_line_match(line, long_log_prefix, TEXT128)); + } + + /* no prefix, just lots of text */ + line = i_stream_read_next_line(input); + long_lext[PIPE_BUF-7] = '\0'; + test_assert(internal_line_match(line, "", long_lext)); + line = i_stream_read_next_line(input); + test_assert(internal_line_match(line, "", long_lext)); + line = i_stream_read_next_line(input); + test_assert(internal_line_match(line, "", "TTTTTTTTTTTTTT")); + + i_stream_unref(&input); + alarm(0); + + test_end(); +} + +void test_failures(void) +{ + test_get_set_handlers(); + test_expected(); + test_expected_str(); + test_internal_split(); +} diff --git a/src/lib/test-fd-util.c b/src/lib/test-fd-util.c new file mode 100644 index 0000000..1058eca --- /dev/null +++ b/src/lib/test-fd-util.c @@ -0,0 +1,24 @@ +/* Copyright (c) 2019 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "fd-util.h" + +enum fatal_test_state fatal_i_close(unsigned int stage) +{ + if (stage == 0) { + test_begin("fatal i_close"); + } else { + test_end(); + return FATAL_TEST_FINISHED; + } + + int fd = 0; + const char *fatal_string = t_strdup_printf( + "%s: close((&fd)) @ %s:%d attempted with fd=%d", + __func__, __FILE__, __LINE__ + 2, fd); + test_expect_fatal_string(fatal_string); + i_close_fd(&fd); + + /* This cannot be reached. */ + return FATAL_TEST_ABORT; +} diff --git a/src/lib/test-file-cache.c b/src/lib/test-file-cache.c new file mode 100644 index 0000000..6bac9ab --- /dev/null +++ b/src/lib/test-file-cache.c @@ -0,0 +1,293 @@ +/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "istream.h" +#include "ostream.h" +#include "file-cache.h" + +#include <sys/resource.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> + +#define TEST_FILENAME ".test_file_cache" + +static void test_file_cache_read(void) +{ + test_begin("file_cache_read"); + + /* create a file */ + struct ostream *os = o_stream_create_file(TEST_FILENAME, 0, 0600, 0); + o_stream_nsend_str(os, "initial data\n"); + test_assert(o_stream_finish(os) == 1); + o_stream_destroy(&os); + + int fd = open(TEST_FILENAME, O_RDONLY); + i_assert(fd > -1); + struct file_cache *cache = file_cache_new_path(fd, TEST_FILENAME); + + /* this should be 0 before read */ + size_t size; + const unsigned char *map = file_cache_get_map(cache, &size); + test_assert(map == NULL); + test_assert(size == 0); + test_assert(map == NULL); + + test_assert(file_cache_read(cache, 0, 13) == 13); + map = file_cache_get_map(cache, &size); + test_assert(map != NULL && size == 13 && memcmp(map, "initial data\n", 13) == 0); + + file_cache_free(&cache); + i_close_fd(&fd); + i_unlink(TEST_FILENAME); + + test_end(); +} + +static void test_file_cache_write_read(void) +{ + test_begin("file_cache_write_read"); + + /* create a file */ + struct ostream *os = o_stream_create_file(TEST_FILENAME, 0, 0600, 0); + o_stream_nsend_str(os, "initial data\n"); + test_assert(o_stream_finish(os) == 1); + o_stream_destroy(&os); + + int fd = open(TEST_FILENAME, O_RDONLY); + i_assert(fd > -1); + struct file_cache *cache = file_cache_new_path(fd, TEST_FILENAME); + + /* this should be 0 before read */ + size_t size; + const unsigned char *map = file_cache_get_map(cache, &size); + test_assert(map == NULL); + test_assert(size == 0); + test_assert(map == NULL); + test_assert(file_cache_read(cache, 0, 13) == 13); + file_cache_write(cache, "updated data\n", 13, 0); + map = file_cache_get_map(cache, &size); + test_assert(map != NULL && size == 13 && memcmp(map, "updated data\n", 13) == 0); + file_cache_free(&cache); + i_close_fd(&fd); + + struct istream *is = i_stream_create_file(TEST_FILENAME, SIZE_MAX); + const unsigned char *data; + test_assert(i_stream_read_more(is, &data, &size) > 0 && size == 13); + test_assert(map != NULL && size == 13 && memcmp(data, "initial data\n", 13) == 0); + i_stream_destroy(&is); + i_unlink(TEST_FILENAME); + + test_end(); +} + +static void test_file_cache_read_invalidate(void) +{ + test_begin("file_cache_read_invalidate"); + + /* create a file */ + struct ostream *os = o_stream_create_file(TEST_FILENAME, 0, 0600, 0); + o_stream_nsend_str(os, "initial data\n"); + test_assert(o_stream_finish(os) == 1); + o_stream_destroy(&os); + + int fd = open(TEST_FILENAME, O_RDONLY); + i_assert(fd > -1); + struct file_cache *cache = file_cache_new_path(fd, TEST_FILENAME); + + /* this should be 0 before read */ + size_t size; + test_assert(file_cache_read(cache, 0, 13) == 13); + const unsigned char *map = file_cache_get_map(cache, &size); + test_assert(map != NULL && size == 13 && memcmp(map, "initial data\n", 13) == 0); + + /* update file */ + os = o_stream_create_file(TEST_FILENAME, 0, 0600, 0); + o_stream_nsend_str(os, "updated data\n"); + test_assert(o_stream_finish(os) == 1); + o_stream_destroy(&os); + + map = file_cache_get_map(cache, &size); + test_assert(map != NULL && size == 13 && memcmp(map, "initial data\n", 13) == 0); + + /* invalidate cache */ + file_cache_invalidate(cache, 0, size); + test_assert(file_cache_read(cache, 0, 13) == 13); + map = file_cache_get_map(cache, &size); + test_assert(size == 13); + test_assert(map != NULL && size == 13 && memcmp(map, "updated data\n", 13) == 0); + file_cache_free(&cache); + i_close_fd(&fd); + i_unlink(TEST_FILENAME); + + test_end(); +} + +static void test_file_cache_multipage(void) +{ + test_begin("file_cache_multipage"); + + size_t page_size = getpagesize(); + struct ostream *os = o_stream_create_file(TEST_FILENAME, 0, 0600, 0); + size_t total_size = 0; + for (size_t i = 0; i < page_size * 3 + 100; i += 12) { + o_stream_nsend_str(os, "initial data"); + total_size += 12; + } + test_assert(o_stream_finish(os) == 1); + o_stream_destroy(&os); + + int fd = open(TEST_FILENAME, O_RDONLY); + i_assert(fd > -1); + struct file_cache *cache = file_cache_new_path(fd, TEST_FILENAME); + + /* read everything to memory page at a time */ + test_assert(file_cache_read(cache, 0, page_size) == (ssize_t)page_size); + test_assert(file_cache_read(cache, page_size, page_size) == + (ssize_t)page_size); + test_assert(file_cache_read(cache, page_size*2, page_size) == + (ssize_t)page_size); + test_assert(file_cache_read(cache, page_size*3, page_size) == + (ssize_t)total_size-(ssize_t)page_size*3); + + size_t size; + const unsigned char *map = file_cache_get_map(cache, &size); + test_assert(size == total_size); + test_assert(map != NULL); + + /* write-read-invalidate-read */ + for(size_t i = 0; i < page_size * 3; i+= page_size / 3) { + char orig[13]; + const char *ptr = CONST_PTR_OFFSET(map, i); + memcpy(orig, ptr, 12); + orig[12] = '\0'; + file_cache_write(cache, "updated data", 12, i); + map = file_cache_get_map(cache, &size); + ptr = CONST_PTR_OFFSET(map, i); + test_assert(strncmp(ptr, "updated data", 12) == 0); + /* invalidate cache */ + file_cache_invalidate(cache, i, 12); + /* check that it's back what it was */ + test_assert(file_cache_read(cache, i, 12) == 12); + map = file_cache_get_map(cache, &size); + ptr = CONST_PTR_OFFSET(map, i); + test_assert(strncmp(ptr, orig, 12) == 0); + } + + file_cache_free(&cache); + i_close_fd(&fd); + i_unlink(TEST_FILENAME); + test_end(); +} + +static void test_file_cache_anon(void) +{ + /* file-cache should work as anonymous cache for small files */ + test_begin("file_cache_anon"); + test_assert(access(TEST_FILENAME, F_OK) == -1 && errno == ENOENT); + struct file_cache *cache = file_cache_new_path(-1, TEST_FILENAME); + + test_assert(file_cache_set_size(cache, 1024) == 0); + file_cache_write(cache, "initial data", 12, 0); + + size_t size; + const unsigned char *map = file_cache_get_map(cache, &size); + test_assert(map != NULL && size == 12 && memcmp(map, "initial data", 12) == 0); + + file_cache_free(&cache); + i_unlink_if_exists(TEST_FILENAME); + test_end(); +} + +static void test_file_cache_switch_fd(void) +{ + test_begin("file_cache_switch_fd"); + test_assert(access(TEST_FILENAME, F_OK) == -1 && errno == ENOENT); + struct file_cache *cache = file_cache_new_path(-1, TEST_FILENAME); + + test_assert(file_cache_set_size(cache, 13) == 0); + file_cache_write(cache, "initial data\n", 13, 0); + + /* create a file */ + struct ostream *os = o_stream_create_file(TEST_FILENAME, 0, 0600, 0); + o_stream_nsend_str(os, "updated data\n"); + test_assert(o_stream_finish(os) == 1); + o_stream_destroy(&os); + + int fd = open(TEST_FILENAME, O_RDONLY); + i_assert(fd > -1); + /* map should be invalidated and updated data read + from given file */ + file_cache_set_fd(cache, fd); + test_assert(file_cache_read(cache, 0, 13) == 13); + size_t size; + const unsigned char *map = file_cache_get_map(cache, &size); + test_assert(map != NULL && size == 13 && memcmp(map, "updated data\n", 13) == 0); + + file_cache_free(&cache); + i_close_fd(&fd); + i_unlink(TEST_FILENAME); + test_end(); +} + +static void test_file_cache_errors(void) +{ + test_begin("file_cache_errors"); + + size_t page_size = getpagesize(); + + test_assert(access(TEST_FILENAME, F_OK) == -1 && errno == ENOENT); + int fd = open(TEST_FILENAME, O_RDONLY); + struct file_cache *cache = file_cache_new_path(fd, TEST_FILENAME); + size_t size; + + /* file does not exist and we try large enough mapping */ + test_expect_error_string("fstat(.test_file_cache) failed: " + "Bad file descriptor"); + test_assert(file_cache_read(cache, 0, 2*1024*1024) == -1); + const unsigned char *map = file_cache_get_map(cache, &size); + test_assert(size == 0); + test_assert(map == NULL); + + /* temporarily set a small memory limit to make mmap attempt fail */ + struct rlimit rl_cur; + test_assert(getrlimit(RLIMIT_AS, &rl_cur) == 0); + struct rlimit rl_new = { + .rlim_cur = 1, + .rlim_max = rl_cur.rlim_max + }; + const char *errstr = + t_strdup_printf("mmap_anon(.test_file_cache, %zu) failed: " + "Cannot allocate memory", page_size); + test_assert(setrlimit(RLIMIT_AS, &rl_new) == 0); + test_expect_error_string(errstr); + test_assert(file_cache_set_size(cache, 1024) == -1); + test_assert(setrlimit(RLIMIT_AS, &rl_cur) == 0); + + /* same for mremap */ + errstr = t_strdup_printf("mremap_anon(.test_file_cache, %zu) failed: " + "Cannot allocate memory", page_size*2); + test_assert(file_cache_set_size(cache, 1) == 0); + test_assert(setrlimit(RLIMIT_AS, &rl_new) == 0); + test_expect_error_string(errstr); + test_assert(file_cache_set_size(cache, page_size*2) == -1); + test_assert(setrlimit(RLIMIT_AS, &rl_cur) == 0); + + file_cache_free(&cache); + i_close_fd(&fd); + i_unlink_if_exists(TEST_FILENAME); + test_end(); +} + +void test_file_cache(void) +{ + test_file_cache_read(); + test_file_cache_write_read(); + test_file_cache_read_invalidate(); + test_file_cache_multipage(); + test_file_cache_anon(); + test_file_cache_switch_fd(); + test_file_cache_errors(); +} diff --git a/src/lib/test-file-create-locked.c b/src/lib/test-file-create-locked.c new file mode 100644 index 0000000..f5b4f6a --- /dev/null +++ b/src/lib/test-file-create-locked.c @@ -0,0 +1,142 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "unlink-directory.h" +#include "file-create-locked.h" +#include "sleep.h" + +#include <fcntl.h> +#include <signal.h> +#include <sys/stat.h> + +static void create_file(const char *path) +{ + int fd; + + fd = creat(path, 0600); + if (fd == -1) + i_fatal("creat(%s) failed: %m", path); + i_close_fd(&fd); +} + +static bool wait_for_file(pid_t pid, const char *path) +{ + struct stat st; + + for (unsigned int i = 0; i < 1000; i++) { + if (stat(path, &st) == 0) + return TRUE; + if (errno != ENOENT) + i_fatal("stat(%s) failed: %m", path); + if (kill(pid, 0) < 0) { + if (errno == ESRCH) + return FALSE; + i_fatal("kill(SIGSRCH) failed: %m"); + } + i_sleep_msecs(10); + } + i_error("%s isn't being created", path); + return FALSE; +} + +static void test_file_create_locked_basic(void) +{ + struct file_create_settings set = { + .lock_timeout_secs = 0, + .lock_settings = { + .lock_method = FILE_LOCK_METHOD_FCNTL, + }, + }; + const char *path = ".test-file-create-locked"; + struct file_lock *lock; + const char *error; + bool created; + pid_t pid; + int fd; + + test_begin("file_create_locked()"); + + i_unlink_if_exists(path); + i_unlink_if_exists(".test-temp-file-create-locked-child"); + pid = fork(); + switch (pid) { + case (pid_t)-1: + i_error("fork() failed: %m"); + break; + case 0: + /* child */ + fd = file_create_locked(path, &set, &lock, &created, &error); + test_assert(fd > 0); + test_assert(created); + if (test_has_failed()) + lib_exit(1); + create_file(".test-temp-file-create-locked-child"); + sleep(60); + i_close_fd(&fd); + lib_exit(0); + default: + /* parent */ + test_assert(wait_for_file(pid, ".test-temp-file-create-locked-child")); + if (test_has_failed()) + break; + test_assert(file_create_locked(path, &set, &lock, &created, &error) == -1); + test_assert(errno == EAGAIN); + if (kill(pid, SIGKILL) < 0) + i_error("kill(SIGKILL) failed: %m"); + break; + } + i_unlink_if_exists(".test-temp-file-create-locked-child"); + i_unlink_if_exists(path); + test_end(); +} + +static void test_file_create_locked_mkdir(void) +{ + struct file_create_settings set = { + .lock_timeout_secs = 0, + .lock_settings = { + .lock_method = FILE_LOCK_METHOD_FCNTL, + }, + }; + const char *path; + struct file_lock *lock; + const char *error, *dir; + bool created; + int fd; + + test_begin("file_create_locked() with mkdir"); + + dir = ".test-temp-file-create-locked-dir"; + if (unlink_directory(dir, UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0) + i_fatal("unlink_directory(%s) failed: %s", dir, error); + path = t_strconcat(dir, "/lockfile", NULL); + + /* try without mkdir enabled */ + test_assert(file_create_locked(path, &set, &lock, &created, &error) == -1); + test_assert(errno == ENOENT); + + /* try with mkdir enabled */ + set.mkdir_mode = 0700; + fd = file_create_locked(path, &set, &lock, &created, &error); + test_assert(fd > 0); + test_assert(created); + i_close_fd(&fd); + + struct stat st; + if (stat(dir, &st) < 0) + i_error("stat(%s) failed: %m", dir); + test_assert((st.st_mode & 0777) == 0700); + i_unlink(path); + file_lock_free(&lock); + + if (unlink_directory(dir, UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0) + i_fatal("unlink_directory(%s) failed: %s", dir, error); + + test_end(); +} + +void test_file_create_locked(void) +{ + test_file_create_locked_basic(); + test_file_create_locked_mkdir(); +} diff --git a/src/lib/test-guid.c b/src/lib/test-guid.c new file mode 100644 index 0000000..0a13cf1 --- /dev/null +++ b/src/lib/test-guid.c @@ -0,0 +1,259 @@ +/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "guid.h" +#include "ioloop.h" + +/* + * We want earlier timestamps to compare as < with later timestamps, but + * guid_128_cmp() doesn't do that because the timestamps in the guid are + * stored in little-endian byte order. + */ +static int reverse_guid_128_cmp(const guid_128_t a, const guid_128_t b) +{ + int i; + + for (i = GUID_128_SIZE - 1; i >= 0; i--) + if (a[i] != b[i]) + return (int)a[i] - (int)b[i]; + + return 0; +} + +static bool guid_128_has_sane_nsecs(const guid_128_t g) +{ + unsigned long nsecs; + + nsecs = le32_to_cpu_unaligned(g); + + return nsecs < 1000000000UL; +} + +static inline void set_fake_time(time_t sec, long usec) +{ + ioloop_timeval.tv_sec = sec; + ioloop_timeval.tv_usec = usec; +} + +/* + * We muck with the ioloop_timeval in various ways and make sure that the + * guids that get generated make sense. To make sure that the guid + * generation code takes up our faked timestamp, we use a far-away time (Jan + * 1 2038) as the base time. We don't want to go beyond 32-bit signed + * time_t for the base time to avoid issues on systems with 32-bit signed + * time_t. + * + * While guids really only need to be unique, here we actually enforce that + * they are increasing (as defined by reverse_guid_128_cmp()). If guids are + * always increasing, they will always be unique. + */ +static void test_ioloop_guid_128_generate(void) +{ + const time_t basetime = 2145909600; /* Jan 1 2038 */ + struct timeval saved_ioloop_timeval; + guid_128_t guids[2]; + int i; + + /* save the ioloop_timeval before we start messing with it */ + saved_ioloop_timeval = ioloop_timeval; + + /* + * Generating multiple guids within a microsecond should keep + * incrementing them. + */ + test_begin("guid_128_generate() increasing guid within a usec"); + set_fake_time(basetime, 0); + guid_128_generate(guids[1]); + for (i = 0; i < 10; i++) { + const int this = i % 2; + const int prev = 1 - this; + + guid_128_generate(guids[this]); + + test_assert(reverse_guid_128_cmp(guids[prev], guids[this]) < 0); + test_assert(guid_128_has_sane_nsecs(guids[this])); + } + test_end(); + + /* + * If the current time changes by +1 usec, so should the guids. + */ + test_begin("guid_128_generate() increasing guid with usec fast-forward"); + for (i = 0; i < 10; i++) { + const int this = i % 2; + const int prev = 1 - this; + + set_fake_time(basetime, 1 + i); + guid_128_generate(guids[this]); + + test_assert(reverse_guid_128_cmp(guids[prev], guids[this]) < 0); + test_assert(guid_128_has_sane_nsecs(guids[this])); + } + test_end(); + + /* + * If the current time changes by +1 sec, so should the guids. + */ + test_begin("guid_128_generate() increasing guid with sec fast-forward"); + for (i = 0; i < 10; i++) { + const int this = i % 2; + const int prev = 1 - this; + + set_fake_time(basetime + 1 + i, 0); + guid_128_generate(guids[this]); + + test_assert(reverse_guid_128_cmp(guids[prev], guids[this]) < 0); + test_assert(guid_128_has_sane_nsecs(guids[this])); + } + test_end(); + + /* + * Requesting enough guids should increment the seconds but always + * produce valid nsecs. + * + * (Set a time that leaves us 1000 guids before seconds overflow and + * then ask for 2500 guids.) + */ + test_begin("guid_128_generate() proper guid nsec overflow"); + set_fake_time(basetime + 11, 999999L); + for (i = 0; i < 2500; i++) { + const int this = i % 2; + const int prev = 1 - this; + + guid_128_generate(guids[this]); + test_assert(reverse_guid_128_cmp(guids[prev], guids[this]) < 0); + test_assert(guid_128_has_sane_nsecs(guids[this])); + } + test_end(); + + /* + * When ahead by 1500 guids (see previous test), +1 usec shouldn't + * have any effect. + */ + test_begin("guid_128_generate() no effect with increasing time when ahead"); + set_fake_time(basetime + 12, 0); + guid_128_generate(guids[0]); + test_assert(reverse_guid_128_cmp(guids[1], guids[0]) < 0); + test_assert(guid_128_has_sane_nsecs(guids[0])); + test_end(); + + /* not a test - just set a more convenient time */ + set_fake_time(basetime + 15, 500); + guid_128_generate(guids[1]); + test_assert(reverse_guid_128_cmp(guids[0], guids[1]) < 0); + test_assert(guid_128_has_sane_nsecs(guids[1])); + + /* + * Time going backwards by 1 usec should have no effect on guids. + */ + test_begin("guid_128_generate() usec time-travel still increasing"); + set_fake_time(basetime + 15, 499); + guid_128_generate(guids[0]); + test_assert(reverse_guid_128_cmp(guids[1], guids[0]) < 0); + test_assert(guid_128_has_sane_nsecs(guids[0])); + test_end(); + + /* + * Time going backwards by 1 sec should have no effect on guids. + */ + test_begin("guid_128_generate() sec time-travel still increasing"); + set_fake_time(basetime + 14, 499); + guid_128_generate(guids[1]); + test_assert(reverse_guid_128_cmp(guids[0], guids[1]) < 0); + test_assert(guid_128_has_sane_nsecs(guids[1])); + test_end(); + + /* restore the previously saved value just in case */ + ioloop_timeval = saved_ioloop_timeval; +} + +void test_guid(void) +{ + static const guid_128_t test_guid = + { 0x01, 0x23, 0x45, 0x67, 0x89, + 0xab, 0xcd, 0xef, + 0xAB, 0xCD, 0xEF, + 0x00, 0x00, 0x00, 0x00, 0x00 }; + guid_128_t guid1, guid2, guid3; + const char *str; + char guidbuf[GUID_128_SIZE*2 + 2]; + unsigned int i; + + test_begin("guid_128_generate()"); + guid_128_generate(guid1); + guid_128_generate(guid2); + test_assert(!guid_128_equals(guid1, guid2)); + test_assert(guid_128_cmp(guid1, guid2) != 0); + test_end(); + + test_begin("guid_128_is_empty()"); + test_assert(!guid_128_is_empty(guid1)); + test_assert(!guid_128_is_empty(guid2)); + guid_128_generate(guid3); + guid_128_empty(guid3); + test_assert(guid_128_is_empty(guid3)); + test_end(); + + test_begin("guid_128_copy()"); + guid_128_copy(guid3, guid1); + test_assert(guid_128_equals(guid3, guid1)); + test_assert(!guid_128_equals(guid3, guid2)); + guid_128_copy(guid3, guid2); + test_assert(!guid_128_equals(guid3, guid1)); + test_assert(guid_128_equals(guid3, guid2)); + test_end(); + + test_begin("guid_128_to_string()"); + str = guid_128_to_string(guid1); + test_assert(guid_128_from_string(str, guid3) == 0); + test_assert(guid_128_equals(guid3, guid1)); + test_end(); + + test_begin("guid_128_from_string()"); + /* empty */ + memset(guidbuf, '0', GUID_128_SIZE*2); + guidbuf[GUID_128_SIZE*2] = '\0'; + guidbuf[GUID_128_SIZE*2+1] = '\0'; + test_assert(guid_128_from_string(guidbuf, guid3) == 0); + test_assert(guid_128_is_empty(guid3)); + /* too large */ + guidbuf[GUID_128_SIZE*2] = '0'; + test_assert(guid_128_from_string(guidbuf, guid3) < 0); + /* too small */ + guidbuf[GUID_128_SIZE*2-1] = '\0'; + test_assert(guid_128_from_string(guidbuf, guid3) < 0); + /* reset to normal */ + guidbuf[GUID_128_SIZE*2-1] = '0'; + guidbuf[GUID_128_SIZE*2] = '\0'; + test_assert(guid_128_from_string(guidbuf, guid3) == 0); + /* upper + lowercase hex chars */ + i_assert(GUID_128_SIZE*2 > 16 + 6); + for (i = 0; i < 10; i++) + guidbuf[i] = '0' + i; + for (i = 0; i < 6; i++) + guidbuf[10 + i] = 'a' + i; + for (i = 0; i < 6; i++) + guidbuf[16 + i] = 'A' + i; + test_assert(guid_128_from_string(guidbuf, guid3) == 0); + test_assert(guid_128_equals(guid3, test_guid)); + /* non-hex chars */ + guidbuf[0] = 'g'; + test_assert(guid_128_from_string(guidbuf, guid3) < 0); + guidbuf[0] = ' '; + test_assert(guid_128_from_string(guidbuf, guid3) < 0); + + test_assert(guid_128_from_uuid_string("fee0ceac-0327-11e7-ad39-52540078f374", guid3) == 0); + test_assert(guid_128_from_uuid_string("fee0ceac032711e7ad3952540078f374", guid2) == 0); + test_assert(guid_128_cmp(guid3, guid2) == 0); + test_assert(guid_128_from_uuid_string("{fee0ceac-0327-11e7-ad39-52540078f374}", guid2) == 0); + test_assert(guid_128_cmp(guid3, guid2) == 0); + test_assert(strcmp(guid_128_to_uuid_string(guid3, FORMAT_RECORD), "fee0ceac-0327-11e7-ad39-52540078f374")==0); + test_assert(strcmp(guid_128_to_uuid_string(guid3, FORMAT_COMPACT), "fee0ceac032711e7ad3952540078f374")==0); + test_assert(strcmp(guid_128_to_uuid_string(guid3, FORMAT_MICROSOFT), "{fee0ceac-0327-11e7-ad39-52540078f374}")==0); + /* failure test */ + test_assert(guid_128_from_uuid_string("fe-e0ceac-0327-11e7-ad39-52540078f374", guid3) < 0); + + test_end(); + + test_ioloop_guid_128_generate(); +} diff --git a/src/lib/test-hash-format.c b/src/lib/test-hash-format.c new file mode 100644 index 0000000..447e797 --- /dev/null +++ b/src/lib/test-hash-format.c @@ -0,0 +1,54 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "hash-format.h" + +struct hash_format_test { + const char *input; + const char *output; +}; + +void test_hash_format(void) +{ + static const char *fail_input[] = { + "%", + "%A{sha1}", + "%{sha1", + "%{sha1:8", + "%{sha1:8a}", + "%{sha1:0}", + "%{sha1:168}", + NULL + }; + static const struct hash_format_test tests[] = { + { "%{sha1}", "8843d7f92416211de9ebb963ff4ce28125932878" }, + { "*%{sha1}*", "*8843d7f92416211de9ebb963ff4ce28125932878*" }, + { "*%{sha1:8}*", "*88*" }, + { "%{sha1:152}", "8843d7f92416211de9ebb963ff4ce281259328" }, + { "%X{size}", "6" }, + { "%{sha256:80}", "c3ab8ff13720e8ad9047" }, + { "%{sha512:80}", "0a50261ebd1a390fed2b" }, + { "%{md4}", "547aefd231dcbaac398625718336f143" }, + { "%{md5}", "3858f62230ac3c915f300c664312c63f" }, + { "%{sha256:80}-%X{size}", "c3ab8ff13720e8ad9047-6" } + }; + struct hash_format *format; + string_t *str = t_str_new(128); + const char *error; + unsigned int i; + + test_begin("hash_format"); + for (i = 0; fail_input[i] != NULL; i++) + test_assert(hash_format_init(fail_input[i], &format, &error) < 0); + + for (i = 0; i < N_ELEMENTS(tests); i++) { + test_assert(hash_format_init(tests[i].input, &format, &error) == 0); + hash_format_loop(format, "foo", 3); + hash_format_loop(format, "bar", 3); + str_truncate(str, 0); + hash_format_deinit(&format, str); + test_assert(strcmp(str_c(str), tests[i].output) == 0); + } + test_end(); +} diff --git a/src/lib/test-hash-method.c b/src/lib/test-hash-method.c new file mode 100644 index 0000000..0fd41e0 --- /dev/null +++ b/src/lib/test-hash-method.c @@ -0,0 +1,460 @@ +/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "mmap-util.h" +#include "hash-method.h" + +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +# define MAP_ANONYMOUS MAP_ANON +#endif + +static unsigned char *buf; +static unsigned int buf_size; + +static void test_hash_method_one(const struct hash_method *method) +{ + unsigned char *ctx, *digest; + unsigned int i; + + test_begin(t_strdup_printf("hash method %s", method->name)); + + ctx = i_malloc(method->context_size); + digest = i_malloc(method->digest_size); + method->init(ctx); + + /* make sure the code doesn't try to access data past boundaries */ + for (i = 0; i < buf_size; i++) + method->loop(ctx, buf + buf_size - i, i); + method->result(ctx, digest); + + i_free(ctx); + i_free(digest); + test_end(); +} + +static void test_hash_method_boundary(void) +{ + unsigned int i; + + buf_size = mmap_get_page_size(); +#ifdef MAP_ANONYMOUS + buf = mmap(NULL, buf_size*2, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + mprotect(buf + buf_size, buf_size, PROT_NONE); +#else + buf = i_malloc(buf_size); +#endif + memset(buf, 0, buf_size); + + for (i = 0; hash_methods[i] != NULL; i++) + test_hash_method_one(hash_methods[i]); +} + +static void test_hash_methods_fips() { + const char *last_method = NULL; + + struct { + const char *method; + const void *input; + size_t ilen; + size_t rounds; + const void *output; + size_t olen; + } test_vectors[] = + { + { "md4", + "", + 0, + 1, + "\x31\xd6\xcf\xe0\xd1\x6a\xe9\x31" + "\xb7\x3c\x59\xd7\xe0\xc0\x89\xc0", + 128 / 8 + }, + { "md4", + "abc", + 3, + 1, + "\xa4\x48\x01\x7a\xaf\x21\xd8\x52" + "\x5f\xc1\x0a\xe8\x7a\xa6\x72\x9d", + 128 / 8 + }, + { "md4", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef" + "ghijklmnopqrstuvwxyz0123456789", + 62, + 1, + "\x04\x3f\x85\x82\xf2\x41\xdb\x35" + "\x1c\xe6\x27\xe1\x53\xe7\xf0\xe4", + 128 / 8 + }, + { "md5", + "", + 0, + 1, + "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04" + "\xe9\x80\x09\x98\xec\xf8\x42\x7e", + 128 / 8 + }, + { "md5", + "abc", + 3, + 1, + "\x90\x01\x50\x98\x3c\xd2\x4f\xb0" + "\xd6\x96\x3f\x7d\x28\xe1\x7f\x72", + 128 / 8 + }, + { "md5", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef" + "ghijklmnopqrstuvwxyz0123456789", + 62, + 1, + "\xd1\x74\xab\x98\xd2\x77\xd9\xf5" + "\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f", + 128 / 8 + }, + { "sha1", + "", + 0, + 1, + "\xda\x39\xa3\xee\x5e\x6b\x4b\x0d" + "\x32\x55\xbf\xef\x95\x60\x18\x90" + "\xaf\xd8\x07\x09", + 160 / 8 + }, + { "sha1", + "abc", + 3, + 1, + "\xa9\x99\x3e\x36\x47\x06\x81\x6a" + "\xba\x3e\x25\x71\x78\x50\xc2\x6c" + "\x9c\xd0\xd8\x9d", + 160 / 8 + }, + { "sha1", + "abcdbcdecdefdefgefghfghighijhijk" + "ijkljklmklmnlmnomnopnopq", + 56, + 1, + "\x84\x98\x3e\x44\x1c\x3b\xd2\x6e" + "\xba\xae\x4a\xa1\xf9\x51\x29\xe5" + "\xe5\x46\x70\xf1", + 160 / 8 + }, + { "sha256", + "", + 0, + 1, + "\xe3\xb0\xc4\x42\x98\xfc\x1c\x14" + "\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24" + "\x27\xae\x41\xe4\x64\x9b\x93\x4c" + "\xa4\x95\x99\x1b\x78\x52\xb8\x55", + 256 / 8 + }, + { "sha256", + "abc", + 3, + 1, + "\xba\x78\x16\xbf\x8f\x01\xcf\xea" + "\x41\x41\x40\xde\x5d\xae\x22\x23" + "\xb0\x03\x61\xa3\x96\x17\x7a\x9c" + "\xb4\x10\xff\x61\xf2\x00\x15\xad", + 256 / 8 + }, + { "sha256", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + 56, + 1, + "\x24\x8d\x6a\x61\xd2\x06\x38\xb8" + "\xe5\xc0\x26\x93\x0c\x3e\x60\x39" + "\xa3\x3c\xe4\x59\x64\xff\x21\x67" + "\xf6\xec\xed\xd4\x19\xdb\x06\xc1", + 256 / 8 + }, + { "sha384", + "", + 0, + 1, + "\x38\xb0\x60\xa7\x51\xac\x96\x38" + "\x4c\xd9\x32\x7e\xb1\xb1\xe3\x6a" + "\x21\xfd\xb7\x11\x14\xbe\x07\x43" + "\x4c\x0c\xc7\xbf\x63\xf6\xe1\xda" + "\x27\x4e\xde\xbf\xe7\x6f\x65\xfb" + "\xd5\x1a\xd2\xf1\x48\x98\xb9\x5b", + 384 / 8 + }, + { "sha384", + "abc", + 3, + 1, + "\xcb\x00\x75\x3f\x45\xa3\x5e\x8b" + "\xb5\xa0\x3d\x69\x9a\xc6\x50\x07" + "\x27\x2c\x32\xab\x0e\xde\xd1\x63" + "\x1a\x8b\x60\x5a\x43\xff\x5b\xed" + "\x80\x86\x07\x2b\xa1\xe7\xcc\x23" + "\x58\xba\xec\xa1\x34\xc8\x25\xa7", + 384 / 8 + }, + { "sha384", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + 56, + 1, + "\x33\x91\xfd\xdd\xfc\x8d\xc7\x39" + "\x37\x07\xa6\x5b\x1b\x47\x09\x39" + "\x7c\xf8\xb1\xd1\x62\xaf\x05\xab" + "\xfe\x8f\x45\x0d\xe5\xf3\x6b\xc6" + "\xb0\x45\x5a\x85\x20\xbc\x4e\x6f" + "\x5f\xe9\x5b\x1f\xe3\xc8\x45\x2b", + 384 / 8 + }, + { "sha512", + "", + 0, + 1, + "\xcf\x83\xe1\x35\x7e\xef\xb8\xbd" + "\xf1\x54\x28\x50\xd6\x6d\x80\x07" + "\xd6\x20\xe4\x05\x0b\x57\x15\xdc" + "\x83\xf4\xa9\x21\xd3\x6c\xe9\xce" + "\x47\xd0\xd1\x3c\x5d\x85\xf2\xb0" + "\xff\x83\x18\xd2\x87\x7e\xec\x2f" + "\x63\xb9\x31\xbd\x47\x41\x7a\x81" + "\xa5\x38\x32\x7a\xf9\x27\xda\x3e", + 512 / 8 + }, + { "sha512", + "abc", + 3, + 1, + "\xdd\xaf\x35\xa1\x93\x61\x7a\xba" + "\xcc\x41\x73\x49\xae\x20\x41\x31" + "\x12\xe6\xfa\x4e\x89\xa9\x7e\xa2" + "\x0a\x9e\xee\xe6\x4b\x55\xd3\x9a" + "\x21\x92\x99\x2a\x27\x4f\xc1\xa8" + "\x36\xba\x3c\x23\xa3\xfe\xeb\xbd" + "\x45\x4d\x44\x23\x64\x3c\xe8\x0e" + "\x2a\x9a\xc9\x4f\xa5\x4c\xa4\x9f", + 512 / 8 + }, + { "sha512", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + 56, + 1, + "\x20\x4a\x8f\xc6\xdd\xa8\x2f\x0a" + "\x0c\xed\x7b\xeb\x8e\x08\xa4\x16" + "\x57\xc1\x6e\xf4\x68\xb2\x28\xa8" + "\x27\x9b\xe3\x31\xa7\x03\xc3\x35" + "\x96\xfd\x15\xc1\x3b\x1b\x07\xf9" + "\xaa\x1d\x3b\xea\x57\x78\x9c\xa0" + "\x31\xad\x85\xc7\xa7\x1d\xd7\x03" + "\x54\xec\x63\x12\x38\xca\x34\x45", + 512 / 8 + }, + { "sha3-256", + "", + 0, + 1, + "\xa7\xff\xc6\xf8\xbf\x1e\xd7\x66" + "\x51\xc1\x47\x56\xa0\x61\xd6\x62" + "\xf5\x80\xff\x4d\xe4\x3b\x49\xfa" + "\x82\xd8\x0a\x4b\x80\xf8\x43\x4a", + 256 / 8 + }, + { "sha3-256", + "\xb7\x71\xd5\xce\xf5\xd1\xa4\x1a" + "\x93\xd1\x56\x43\xd7\x18\x1d\x2a" + "\x2e\xf0\xa8\xe8\x4d\x91\x81\x2f" + "\x20\xed\x21\xf1\x47\xbe\xf7\x32" + "\xbf\x3a\x60\xef\x40\x67\xc3\x73" + "\x4b\x85\xbc\x8c\xd4\x71\x78\x0f" + "\x10\xdc\x9e\x82\x91\xb5\x83\x39" + "\xa6\x77\xb9\x60\x21\x8f\x71\xe7" + "\x93\xf2\x79\x7a\xea\x34\x94\x06" + "\x51\x28\x29\x06\x5d\x37\xbb\x55" + "\xea\x79\x6f\xa4\xf5\x6f\xd8\x89" + "\x6b\x49\xb2\xcd\x19\xb4\x32\x15" + "\xad\x96\x7c\x71\x2b\x24\xe5\x03" + "\x2d\x06\x52\x32\xe0\x2c\x12\x74" + "\x09\xd2\xed\x41\x46\xb9\xd7\x5d" + "\x76\x3d\x52\xdb\x98\xd9\x49\xd3" + "\xb0\xfe\xd6\xa8\x05\x2f\xbb", + 1080 / 8, + 1, + "\xa1\x9e\xee\x92\xbb\x20\x97\xb6" + "\x4e\x82\x3d\x59\x77\x98\xaa\x18" + "\xbe\x9b\x7c\x73\x6b\x80\x59\xab" + "\xfd\x67\x79\xac\x35\xac\x81\xb5", + 256 / 8 + }, + { "sha3-256", + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3", + 200, + 1, + "\x79\xf3\x8a\xde\xc5\xc2\x03\x07" + "\xa9\x8e\xf7\x6e\x83\x24\xaf\xbf" + "\xd4\x6c\xfd\x81\xb2\x2e\x39\x73" + "\xc6\x5f\xa1\xbd\x9d\xe3\x17\x87", + 256 / 8, + }, + { "sha3-256", + "\xa3", + 1, + 200, + "\x79\xf3\x8a\xde\xc5\xc2\x03\x07" + "\xa9\x8e\xf7\x6e\x83\x24\xaf\xbf" + "\xd4\x6c\xfd\x81\xb2\x2e\x39\x73" + "\xc6\x5f\xa1\xbd\x9d\xe3\x17\x87", + 256 / 8, + }, + { "sha3-512", + "", + 0, + 1, + "\xa6\x9f\x73\xcc\xa2\x3a\x9a\xc5" + "\xc8\xb5\x67\xdc\x18\x5a\x75\x6e" + "\x97\xc9\x82\x16\x4f\xe2\x58\x59" + "\xe0\xd1\xdc\xc1\x47\x5c\x80\xa6" + "\x15\xb2\x12\x3a\xf1\xf5\xf9\x4c" + "\x11\xe3\xe9\x40\x2c\x3a\xc5\x58" + "\xf5\x00\x19\x9d\x95\xb6\xd3\xe3" + "\x01\x75\x85\x86\x28\x1d\xcd\x26", + 512 / 8 + }, + { "sha3-512", + "\xb7\x71\xd5\xce\xf5\xd1\xa4\x1a" + "\x93\xd1\x56\x43\xd7\x18\x1d\x2a" + "\x2e\xf0\xa8\xe8\x4d\x91\x81\x2f" + "\x20\xed\x21\xf1\x47\xbe\xf7\x32" + "\xbf\x3a\x60\xef\x40\x67\xc3\x73" + "\x4b\x85\xbc\x8c\xd4\x71\x78\x0f" + "\x10\xdc\x9e\x82\x91\xb5\x83\x39" + "\xa6\x77\xb9\x60\x21\x8f\x71\xe7" + "\x93\xf2\x79\x7a\xea\x34\x94\x06" + "\x51\x28\x29\x06\x5d\x37\xbb\x55" + "\xea\x79\x6f\xa4\xf5\x6f\xd8\x89" + "\x6b\x49\xb2\xcd\x19\xb4\x32\x15" + "\xad\x96\x7c\x71\x2b\x24\xe5\x03" + "\x2d\x06\x52\x32\xe0\x2c\x12\x74" + "\x09\xd2\xed\x41\x46\xb9\xd7\x5d" + "\x76\x3d\x52\xdb\x98\xd9\x49\xd3" + "\xb0\xfe\xd6\xa8\x05\x2f\xbb", + 1080 / 8, + 1, + "\x75\x75\xa1\xfb\x4f\xc9\xa8\xf9" + "\xc0\x46\x6b\xd5\xfc\xa4\x96\xd1" + "\xcb\x78\x69\x67\x73\xa2\x12\xa5" + "\xf6\x2d\x02\xd1\x4e\x32\x59\xd1" + "\x92\xa8\x7e\xba\x44\x07\xdd\x83" + "\x89\x35\x27\x33\x14\x07\xb6\xda" + "\xda\xad\x92\x0d\xbc\x46\x48\x9b" + "\x67\x74\x93\xce\x5f\x20\xb5\x95", + 512 / 8 + }, + { "sha3-512", + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3", + 200, + 1, + "\xe7\x6d\xfa\xd2\x20\x84\xa8\xb1" + "\x46\x7f\xcf\x2f\xfa\x58\x36\x1b" + "\xec\x76\x28\xed\xf5\xf3\xfd\xc0" + "\xe4\x80\x5d\xc4\x8c\xae\xec\xa8" + "\x1b\x7c\x13\xc3\x0a\xdf\x52\xa3" + "\x65\x95\x84\x73\x9a\x2d\xf4\x6b" + "\xe5\x89\xc5\x1c\xa1\xa4\xa8\x41" + "\x6d\xf6\x54\x5a\x1c\xe8\xba\x00", + 512 / 8, + }, + { "sha3-512", + "\xa3", + 1, + 200, + "\xe7\x6d\xfa\xd2\x20\x84\xa8\xb1" + "\x46\x7f\xcf\x2f\xfa\x58\x36\x1b" + "\xec\x76\x28\xed\xf5\xf3\xfd\xc0" + "\xe4\x80\x5d\xc4\x8c\xae\xec\xa8" + "\x1b\x7c\x13\xc3\x0a\xdf\x52\xa3" + "\x65\x95\x84\x73\x9a\x2d\xf4\x6b" + "\xe5\x89\xc5\x1c\xa1\xa4\xa8\x41" + "\x6d\xf6\x54\x5a\x1c\xe8\xba\x00", + 512 / 8, + }, + }; + + for(size_t i = 0; i < N_ELEMENTS(test_vectors); i++) { + + if (last_method == NULL || + strcmp(last_method, test_vectors[i].method) != 0) { + if (last_method != NULL) + test_end(); + last_method = test_vectors[i].method; + test_begin(t_strdup_printf("hash method %s (test vectors)", + last_method)); + } + const struct hash_method *method = + hash_method_lookup(test_vectors[i].method); + unsigned char context[method->context_size]; + unsigned char result[method->digest_size]; + test_assert_idx(method->digest_size == test_vectors[i].olen, i); + method->init(context); + for(size_t n = 0; n < test_vectors[i].rounds; n++) + method->loop(context, test_vectors[i].input, + test_vectors[i].ilen); + method->result(context, result); + test_assert_idx(memcmp(result, test_vectors[i].output, + test_vectors[i].olen) == 0, i); + } + + test_end(); +} + +void test_hash_method(void) +{ + test_hash_method_boundary(); + test_hash_methods_fips(); +} diff --git a/src/lib/test-hash.c b/src/lib/test-hash.c new file mode 100644 index 0000000..8fe0e53 --- /dev/null +++ b/src/lib/test-hash.c @@ -0,0 +1,47 @@ +/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "hash.h" + + +static void test_hash_random_pool(pool_t pool) +{ +#define KEYMAX 100000 + HASH_TABLE(void *, void *) hash; + unsigned int *keys; + unsigned int i, key, keyidx, delidx; + + keys = i_new(unsigned int, KEYMAX); keyidx = 0; + hash_table_create_direct(&hash, pool, 0); + for (i = 0; i < KEYMAX; i++) { + key = (i_rand_limit(KEYMAX)) + 1; + if (i_rand_limit(5) > 0) { + if (hash_table_lookup(hash, POINTER_CAST(key)) == NULL) { + hash_table_insert(hash, POINTER_CAST(key), + POINTER_CAST(1)); + keys[keyidx++] = key; + } + } else if (keyidx > 0) { + delidx = i_rand_limit(keyidx); + hash_table_remove(hash, POINTER_CAST(keys[delidx])); + memmove(&keys[delidx], &keys[delidx+1], + (keyidx-delidx-1) * sizeof(*keys)); + keyidx--; + } + } + for (i = 0; i < keyidx; i++) + hash_table_remove(hash, POINTER_CAST(keys[i])); + hash_table_destroy(&hash); + i_free(keys); +} + +void test_hash(void) +{ + pool_t pool; + + test_hash_random_pool(default_pool); + + pool = pool_alloconly_create("test hash", 1024); + test_hash_random_pool(pool); + pool_unref(&pool); +} diff --git a/src/lib/test-hex-binary.c b/src/lib/test-hex-binary.c new file mode 100644 index 0000000..cc248d4 --- /dev/null +++ b/src/lib/test-hex-binary.c @@ -0,0 +1,61 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "buffer.h" +#include "str.h" +#include "hex-binary.h" + +static void test_binary_to_hex(void) +{ + static unsigned char input[] = { 0xff, 0x00, 0x01, 0xb3 }; + static char *output_lcase = "ff0001b3"; + static char *output_ucase = "FF0001B3"; + string_t *str; + + test_begin("binary to hex"); + test_assert(strcmp(binary_to_hex(input, sizeof(input)), output_lcase) == 0); + test_end(); + + test_begin("binary to hex ucase"); + test_assert(strcmp(binary_to_hex_ucase(input, sizeof(input)), output_ucase) == 0); + test_end(); + + test_begin("binary to hex ucase"); + str = t_str_new(32); + str_append_c(str, '<'); + binary_to_hex_append(str, input, sizeof(input)); + str_append_c(str, '>'); + test_assert(strcmp(str_c(str), t_strconcat("<", output_lcase, ">", NULL)) == 0); + test_end(); +} + +static void test_hex_to_binary(void) +{ + static const char *ok_input = "0001fEFf"; + static unsigned char ok_output[] = { 0x00, 0x01, 0xfe, 0xff }; + static const char *error_input[] = { + "00 01", + "0x01", + "0g" + }; + buffer_t *buf = t_buffer_create(10); + unsigned int i; + + test_begin("hex to binary"); + test_assert(hex_to_binary("", buf) == 0); + test_assert(buf->used == 0); + + test_assert(hex_to_binary(ok_input, buf) == 0); + test_assert(buf->used == N_ELEMENTS(ok_output)); + test_assert(memcmp(buf->data, ok_output, buf->used) == 0); + + for (i = 0; i < N_ELEMENTS(error_input); i++) + test_assert(hex_to_binary(error_input[i], buf) == -1); + test_end(); +} + +void test_hex_binary(void) +{ + test_binary_to_hex(); + test_hex_to_binary(); +} diff --git a/src/lib/test-hmac.c b/src/lib/test-hmac.c new file mode 100644 index 0000000..cf3d6d6 --- /dev/null +++ b/src/lib/test-hmac.c @@ -0,0 +1,302 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "hash-method.h" +#include "hmac.h" +#include "sha-common.h" +#include "buffer.h" + +#include "hex-binary.h" + +struct test_vector { + const char *prf; + const unsigned char *key; + size_t key_len; + const unsigned char *data; + size_t data_len; + const unsigned char *res; + size_t res_len; +}; + +#define TEST_BUF(x) (const unsigned char*)x, sizeof(x)-1 + +/* RFC 4231 test vectors */ +static const struct test_vector test_vectors[] = { + /* Test Case 1 */ + { "sha256", + TEST_BUF("\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"), + TEST_BUF("Hi There"), + TEST_BUF("\xb0\x34\x4c\x61\xd8\xdb\x38\x53\x5c\xa8\xaf\xce\xaf\x0b\xf1\x2b\x88\x1d\xc2\x00\xc9\x83\x3d\xa7\x26\xe9\x37\x6c\x2e\x32\xcf\xf7") + }, + /* Test Case 2 */ + { "sha256", + TEST_BUF("\x4a\x65\x66\x65"), /* "Jefe" */ + TEST_BUF("what do ya want for nothing?"), + TEST_BUF("\x5b\xdc\xc1\x46\xbf\x60\x75\x4e\x6a\x04\x24\x26\x08\x95\x75\xc7\x5a\x00\x3f\x08\x9d\x27\x39\x83\x9d\xec\x58\xb9\x64\xec\x38\x43") + }, + /* Test Case 3 */ + { "sha256", + TEST_BUF("\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"), + TEST_BUF("\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"), + TEST_BUF("\x77\x3e\xa9\x1e\x36\x80\x0e\x46\x85\x4d\xb8\xeb\xd0\x91\x81\xa7\x29\x59\x09\x8b\x3e\xf8\xc1\x22\xd9\x63\x55\x14\xce\xd5\x65\xfe") + }, + /* Test Case 4 */ + { "sha256", + TEST_BUF("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19"), + TEST_BUF("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"), + TEST_BUF("\x82\x55\x8a\x38\x9a\x44\x3c\x0e\xa4\xcc\x81\x98\x99\xf2\x08\x3a\x85\xf0\xfa\xa3\xe5\x78\xf8\x07\x7a\x2e\x3f\xf4\x67\x29\x66\x5b") + }, + /* Test Case 5 */ + { "sha256", + TEST_BUF("\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"), + TEST_BUF("\x54\x65\x73\x74\x20\x57\x69\x74\x68\x20\x54\x72\x75\x6e\x63\x61\x74\x69\x6f\x6e"), /* "Test With Truncation" */ + TEST_BUF("\xa3\xb6\x16\x74\x73\x10\x0e\xe0\x6e\x0c\x79\x6c\x29\x55\x55\x2b") + }, + /* Test Case 6 */ + { "sha256", + TEST_BUF("\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"), + TEST_BUF("\x54\x65\x73\x74\x20\x55\x73\x69\x6e\x67\x20\x4c\x61\x72\x67\x65\x72\x20\x54\x68\x61\x6e\x20\x42\x6c\x6f\x63\x6b\x2d\x53\x69\x7a\x65\x20\x4b\x65\x79\x20\x2d\x20\x48\x61\x73\x68\x20\x4b\x65\x79\x20\x46\x69\x72\x73\x74"), /* "Test Using Larger Than Block-Size Key - Hash Key First" */ + TEST_BUF("\x60\xe4\x31\x59\x1e\xe0\xb6\x7f\x0d\x8a\x26\xaa\xcb\xf5\xb7\x7f\x8e\x0b\xc6\x21\x37\x28\xc5\x14\x05\x46\x04\x0f\x0e\xe3\x7f\x54") + }, + /* Test Case 7 */ + { "sha256", + TEST_BUF("\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"), + TEST_BUF("\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74\x20\x75\x73\x69\x6e\x67\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20\x61\x6e\x64\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x64\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b\x65\x79\x20\x6e\x65\x65\x64\x73\x20\x74\x6f\x20\x62\x65\x20\x68\x61\x73\x68\x65\x64\x20\x62\x65\x66\x6f\x72\x65\x20\x62\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41\x43\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2e"), + /* "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm." */ + TEST_BUF("\x9b\x09\xff\xa7\x1b\x94\x2f\xcb\x27\x63\x5f\xbc\xd5\xb0\xe9\x44\xbf\xdc\x63\x64\x4f\x07\x13\x93\x8a\x7f\x51\x53\x5c\x3a\x35\xe2") + } + +}; + +static const struct test_vector test_vectors_hmac384[] = { + /* Test Case 1 */ + { "sha384", + TEST_BUF("\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"), + TEST_BUF("Hi There"), + TEST_BUF("\xaf\xd0\x39\x44\xd8\x48\x95\x62\x6b\x08\x25\xf4\xab\x46\x90\x7f\x15\xf9\xda\xdb\xe4\x10\x1e\xc6\x82\xaa\x03\x4c\x7c\xeb\xc5\x9c\xfa\xea\x9e\xa9\x07\x6e\xde\x7f\x4a\xf1\x52\xe8\xb2\xfa\x9c\xb6"), + }, + /* Test Case 2 */ + { "sha384", + TEST_BUF("\x4a\x65\x66\x65"), /* "Jefe" */ + TEST_BUF("what do ya want for nothing?"), + TEST_BUF("\xaf\x45\xd2\xe3\x76\x48\x40\x31\x61\x7f\x78\xd2\xb5\x8a\x6b\x1b\x9c\x7e\xf4\x64\xf5\xa0\x1b\x47\xe4\x2e\xc3\x73\x63\x22\x44\x5e\x8e\x22\x40\xca\x5e\x69\xe2\xc7\x8b\x32\x39\xec\xfa\xb2\x16\x49"), + }, + /* Test Case 3 */ + { "sha384", + TEST_BUF("\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"), + TEST_BUF("\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"), + TEST_BUF("\x88\x06\x26\x08\xd3\xe6\xad\x8a\x0a\xa2\xac\xe0\x14\xc8\xa8\x6f\x0a\xa6\x35\xd9\x47\xac\x9f\xeb\xe8\x3e\xf4\xe5\x59\x66\x14\x4b\x2a\x5a\xb3\x9d\xc1\x38\x14\xb9\x4e\x3a\xb6\xe1\x01\xa3\x4f\x27"), + }, + /* Test Case 4 */ + { "sha384", + TEST_BUF("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19"), + TEST_BUF("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"), + TEST_BUF("\x3e\x8a\x69\xb7\x78\x3c\x25\x85\x19\x33\xab\x62\x90\xaf\x6c\xa7\x7a\x99\x81\x48\x08\x50\x00\x9c\xc5\x57\x7c\x6e\x1f\x57\x3b\x4e\x68\x01\xdd\x23\xc4\xa7\xd6\x79\xcc\xf8\xa3\x86\xc6\x74\xcf\xfb"), + }, + /* Test Case 5 */ + { "sha384", + TEST_BUF("\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"), + TEST_BUF("\x54\x65\x73\x74\x20\x57\x69\x74\x68\x20\x54\x72\x75\x6e\x63\x61\x74\x69\x6f\x6e"), /* "Test With Truncation" */ + TEST_BUF("\x3a\xbf\x34\xc3\x50\x3b\x2a\x23\xa4\x6e\xfc\x61\x9b\xae\xf8\x97"), + }, + /* Test Case 6 */ + { "sha384", + TEST_BUF("\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"), + TEST_BUF("\x54\x65\x73\x74\x20\x55\x73\x69\x6e\x67\x20\x4c\x61\x72\x67\x65\x72\x20\x54\x68\x61\x6e\x20\x42\x6c\x6f\x63\x6b\x2d\x53\x69\x7a\x65\x20\x4b\x65\x79\x20\x2d\x20\x48\x61\x73\x68\x20\x4b\x65\x79\x20\x46\x69\x72\x73\x74"), /* "Test Using Larger Than Block-Size Key - Hash Key First" */ + TEST_BUF("\x4e\xce\x08\x44\x85\x81\x3e\x90\x88\xd2\xc6\x3a\x04\x1b\xc5\xb4\x4f\x9e\xf1\x01\x2a\x2b\x58\x8f\x3c\xd1\x1f\x05\x03\x3a\xc4\xc6\x0c\x2e\xf6\xab\x40\x30\xfe\x82\x96\x24\x8d\xf1\x63\xf4\x49\x52"), + }, + /* Test Case 7 */ + { "sha384", + TEST_BUF("\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"), + TEST_BUF("\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74\x20\x75\x73\x69\x6e\x67\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20\x61\x6e\x64\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x64\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b\x65\x79\x20\x6e\x65\x65\x64\x73\x20\x74\x6f\x20\x62\x65\x20\x68\x61\x73\x68\x65\x64\x20\x62\x65\x66\x6f\x72\x65\x20\x62\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41\x43\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2e"), + /* "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm." */ + TEST_BUF("\x66\x17\x17\x8e\x94\x1f\x02\x0d\x35\x1e\x2f\x25\x4e\x8f\xd3\x2c\x60\x24\x20\xfe\xb0\xb8\xfb\x9a\xdc\xce\xbb\x82\x46\x1e\x99\xc5\xa6\x78\xcc\x31\xe7\x99\x17\x6d\x38\x60\xe6\x11\x0c\x46\x52\x3e"), + } +}; + +static const struct test_vector test_vectors_hmac512[] = { + /* Test Case 1 */ + { "sha512", + TEST_BUF("\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"), + TEST_BUF("Hi There"), + TEST_BUF("\x87\xaa\x7c\xde\xa5\xef\x61\x9d\x4f\xf0\xb4\x24\x1a\x1d\x6c\xb0\x23\x79\xf4\xe2\xce\x4e\xc2\x78\x7a\xd0\xb3\x05\x45\xe1\x7c\xde\xda\xa8\x33\xb7\xd6\xb8\xa7\x02\x03\x8b\x27\x4e\xae\xa3\xf4\xe4\xbe\x9d\x91\x4e\xeb\x61\xf1\x70\x2e\x69\x6c\x20\x3a\x12\x68\x54") + }, + /* Test Case 2 */ + { "sha512", + TEST_BUF("\x4a\x65\x66\x65"), /* "Jefe" */ + TEST_BUF("what do ya want for nothing?"), + TEST_BUF("\x16\x4b\x7a\x7b\xfc\xf8\x19\xe2\xe3\x95\xfb\xe7\x3b\x56\xe0\xa3\x87\xbd\x64\x22\x2e\x83\x1f\xd6\x10\x27\x0c\xd7\xea\x25\x05\x54\x97\x58\xbf\x75\xc0\x5a\x99\x4a\x6d\x03\x4f\x65\xf8\xf0\xe6\xfd\xca\xea\xb1\xa3\x4d\x4a\x6b\x4b\x63\x6e\x07\x0a\x38\xbc\xe7\x37") + }, + /* Test Case 3 */ + { "sha512", + TEST_BUF("\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"), + TEST_BUF("\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"), + TEST_BUF("\xfa\x73\xb0\x08\x9d\x56\xa2\x84\xef\xb0\xf0\x75\x6c\x89\x0b\xe9\xb1\xb5\xdb\xdd\x8e\xe8\x1a\x36\x55\xf8\x3e\x33\xb2\x27\x9d\x39\xbf\x3e\x84\x82\x79\xa7\x22\xc8\x06\xb4\x85\xa4\x7e\x67\xc8\x07\xb9\x46\xa3\x37\xbe\xe8\x94\x26\x74\x27\x88\x59\xe1\x32\x92\xfb") + }, + /* Test Case 4 */ + { "sha512", + TEST_BUF("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19"), + TEST_BUF("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"), + TEST_BUF("\xb0\xba\x46\x56\x37\x45\x8c\x69\x90\xe5\xa8\xc5\xf6\x1d\x4a\xf7\xe5\x76\xd9\x7f\xf9\x4b\x87\x2d\xe7\x6f\x80\x50\x36\x1e\xe3\xdb\xa9\x1c\xa5\xc1\x1a\xa2\x5e\xb4\xd6\x79\x27\x5c\xc5\x78\x80\x63\xa5\xf1\x97\x41\x12\x0c\x4f\x2d\xe2\xad\xeb\xeb\x10\xa2\x98\xdd") + }, + /* Test Case 5 */ + { "sha512", + TEST_BUF("\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"), + TEST_BUF("\x54\x65\x73\x74\x20\x57\x69\x74\x68\x20\x54\x72\x75\x6e\x63\x61\x74\x69\x6f\x6e"), /* "Test With Truncation" */ + TEST_BUF("\x41\x5f\xad\x62\x71\x58\x0a\x53\x1d\x41\x79\xbc\x89\x1d\x87\xa6") + }, + /* Test Case 6 */ + { "sha512", + TEST_BUF("\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"), + TEST_BUF("\x54\x65\x73\x74\x20\x55\x73\x69\x6e\x67\x20\x4c\x61\x72\x67\x65\x72\x20\x54\x68\x61\x6e\x20\x42\x6c\x6f\x63\x6b\x2d\x53\x69\x7a\x65\x20\x4b\x65\x79\x20\x2d\x20\x48\x61\x73\x68\x20\x4b\x65\x79\x20\x46\x69\x72\x73\x74"), /* "Test Using Larger Than Block-Size Key - Hash Key First" */ + TEST_BUF("\x80\xb2\x42\x63\xc7\xc1\xa3\xeb\xb7\x14\x93\xc1\xdd\x7b\xe8\xb4\x9b\x46\xd1\xf4\x1b\x4a\xee\xc1\x12\x1b\x01\x37\x83\xf8\xf3\x52\x6b\x56\xd0\x37\xe0\x5f\x25\x98\xbd\x0f\xd2\x21\x5d\x6a\x1e\x52\x95\xe6\x4f\x73\xf6\x3f\x0a\xec\x8b\x91\x5a\x98\x5d\x78\x65\x98") + }, + /* Test Case 7 */ + { "sha512", + TEST_BUF("\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"), + TEST_BUF("\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74\x20\x75\x73\x69\x6e\x67\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20\x61\x6e\x64\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x64\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b\x65\x79\x20\x6e\x65\x65\x64\x73\x20\x74\x6f\x20\x62\x65\x20\x68\x61\x73\x68\x65\x64\x20\x62\x65\x66\x6f\x72\x65\x20\x62\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41\x43\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2e"), + /* "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm." */ + TEST_BUF("\xe3\x7b\x6a\x77\x5d\xc8\x7d\xba\xa4\xdf\xa9\xf9\x6e\x5e\x3f\xfd\xde\xbd\x71\xf8\x86\x72\x89\x86\x5d\xf5\xa3\x2d\x20\xcd\xc9\x44\xb6\x02\x2c\xac\x3c\x49\x82\xb1\x0d\x5e\xeb\x55\xc3\xe4\xde\x15\x13\x46\x76\xfb\x6d\xe0\x44\x60\x65\xc9\x74\x40\xfa\x8c\x6a\x58") + } + +}; + +/* RFC 5869 test vectors */ + +static const struct test_vector_5869 { + const char *prf; + const unsigned char *ikm; + size_t ikm_len; + const unsigned char *salt; + size_t salt_len; + const unsigned char *info; + size_t info_len; + const unsigned char *okm; + size_t okm_len; +} test_vectors_5869[] = { + { "sha256", + TEST_BUF("\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"), + TEST_BUF("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c"), + TEST_BUF("\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9"), + TEST_BUF("\x3c\xb2\x5f\x25\xfa\xac\xd5\x7a\x90\x43\x4f\x64\xd0\x36\x2f\x2a\x2d\x2d\x0a\x90\xcf\x1a\x5a\x4c\x5d\xb0\x2d\x56\xec\xc4\xc5\xbf\x34\x00\x72\x08\xd5\xb8\x87\x18\x58\x65") + }, + { "sha256", + TEST_BUF("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"), + TEST_BUF("\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"), + TEST_BUF("\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"), + TEST_BUF("\xb1\x1e\x39\x8d\xc8\x03\x27\xa1\xc8\xe7\xf7\x8c\x59\x6a\x49\x34\x4f\x01\x2e\xda\x2d\x4e\xfa\xd8\xa0\x50\xcc\x4c\x19\xaf\xa9\x7c\x59\x04\x5a\x99\xca\xc7\x82\x72\x71\xcb\x41\xc6\x5e\x59\x0e\x09\xda\x32\x75\x60\x0c\x2f\x09\xb8\x36\x77\x93\xa9\xac\xa3\xdb\x71\xcc\x30\xc5\x81\x79\xec\x3e\x87\xc1\x4c\x01\xd5\xc1\xf3\x43\x4f\x1d\x87") + }, + { "sha256", + TEST_BUF("\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"), + TEST_BUF(""), + TEST_BUF(""), + TEST_BUF("\x8d\xa4\xe7\x75\xa5\x63\xc1\x8f\x71\x5f\x80\x2a\x06\x3c\x5a\x31\xb8\xa1\x1f\x5c\x5e\xe1\x87\x9e\xc3\x45\x4e\x5f\x3c\x73\x8d\x2d\x9d\x20\x13\x95\xfa\xa4\xb6\x1a\x96\xc8") + }, + /* should be equal to above */ + { "sha256", + TEST_BUF("\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"), + NULL, 0, + NULL, 0, + TEST_BUF("\x8d\xa4\xe7\x75\xa5\x63\xc1\x8f\x71\x5f\x80\x2a\x06\x3c\x5a\x31\xb8\xa1\x1f\x5c\x5e\xe1\x87\x9e\xc3\x45\x4e\x5f\x3c\x73\x8d\x2d\x9d\x20\x13\x95\xfa\xa4\xb6\x1a\x96\xc8") + }, +}; + +static void test_hmac_rfc(void) +{ + test_begin("hmac sha256 rfc4231 vectors"); + for(size_t i = 0; i < N_ELEMENTS(test_vectors); i++) { + const struct test_vector *vec = &(test_vectors[i]); + struct hmac_context ctx; + hmac_init(&ctx, vec->key, vec->key_len, hash_method_lookup(vec->prf)); + hmac_update(&ctx, vec->data, vec->data_len); + unsigned char res[SHA256_RESULTLEN]; + hmac_final(&ctx, res); + test_assert_idx(memcmp(res, vec->res, vec->res_len) == 0, i); + } + test_end(); +} + +static void test_hmac384_rfc(void) +{ + test_begin("hmac sha384 rfc4231 vectors"); + for (size_t i = 0; i < N_ELEMENTS(test_vectors_hmac384); i++) { + const struct test_vector *vec = &(test_vectors_hmac384[i]); + struct hmac_context ctx; + hmac_init(&ctx, vec->key, vec->key_len, hash_method_lookup(vec->prf)); + hmac_update(&ctx, vec->data, vec->data_len); + unsigned char res[SHA384_RESULTLEN]; + hmac_final(&ctx, res); + test_assert_idx(memcmp(res, vec->res, vec->res_len) == 0, i); + } + test_end(); +} + +static void test_hmac512_rfc(void) +{ + test_begin("hmac sha512 rfc4231 vectors"); + for (size_t i = 0; i < N_ELEMENTS(test_vectors_hmac512); i++) { + const struct test_vector *vec = &(test_vectors_hmac512[i]); + struct hmac_context ctx; + hmac_init(&ctx, vec->key, vec->key_len, hash_method_lookup(vec->prf)); + hmac_update(&ctx, vec->data, vec->data_len); + unsigned char res[SHA512_RESULTLEN]; + hmac_final(&ctx, res); + test_assert_idx(memcmp(res, vec->res, vec->res_len) == 0, i); + } + test_end(); +} + +static void test_hmac_buffer(void) +{ + const struct test_vector *vec = &(test_vectors[0]); + test_begin("hmac temporary buffer"); + + buffer_t *tmp; + + tmp = t_hmac_data(hash_method_lookup(vec->prf), vec->key, vec->key_len, + vec->data, vec->data_len); + + test_assert(tmp->used == vec->res_len && + memcmp(tmp->data, vec->res, vec->res_len) == 0); + + test_end(); +} + +static void test_hkdf_rfc(void) +{ + test_begin("hkdf sha256 rfc5869 vectors"); + buffer_t *res = t_buffer_create(82); + for(size_t i = 0; i < N_ELEMENTS(test_vectors_5869); i++) { + buffer_set_used_size(res, 0); + const struct test_vector_5869 *vec = &(test_vectors_5869[i]); + const struct hash_method *m = hash_method_lookup(vec->prf); + hmac_hkdf(m, vec->salt, vec->salt_len, vec->ikm, vec->ikm_len, + vec->info, vec->info_len, res, vec->okm_len); + test_assert_idx(memcmp(res->data, vec->okm, vec->okm_len) == 0, i); + } + + test_end(); +} + +static void test_hkdf_buffer(void) +{ + test_begin("hkdf temporary buffer"); + const struct test_vector_5869 *vec = &(test_vectors_5869[0]); + const struct hash_method *m = hash_method_lookup(vec->prf); + buffer_t *tmp = t_hmac_hkdf(m, vec->salt, vec->salt_len, vec->ikm, + vec->ikm_len, vec->info, vec->info_len, + vec->okm_len); + test_assert(tmp->used == vec->okm_len && + memcmp(tmp->data, vec->okm, vec->okm_len) == 0); + test_end(); +} + +void test_hmac(void) +{ + test_hmac_rfc(); + test_hmac384_rfc(); + test_hmac512_rfc(); + test_hmac_buffer(); + test_hkdf_rfc(); + test_hkdf_buffer(); +} diff --git a/src/lib/test-imem.c b/src/lib/test-imem.c new file mode 100644 index 0000000..c18c4aa --- /dev/null +++ b/src/lib/test-imem.c @@ -0,0 +1,61 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" + +struct test_struct { + uint32_t num[10]; +}; + +static void test_imem_alloc(void) +{ + struct test_struct ab, bc, cd, de; + + test_begin("imem allocs"); + + memset(ab.num, 0xab, sizeof(ab.num)); + memset(bc.num, 0xbc, sizeof(bc.num)); + memset(cd.num, 0xcd, sizeof(cd.num)); + memset(de.num, 0xde, sizeof(de.num)); + + /* regular alloc */ + struct test_struct *s1 = i_new(struct test_struct, 2); + struct test_struct *s2 = i_malloc(sizeof(struct test_struct) * 2); + s1[0] = ab; s2[0] = ab; + s1[1] = bc; s2[1] = bc; + test_assert(memcmp(s1, s2, sizeof(struct test_struct) * 2) == 0); + + /* realloc */ + s1 = i_realloc_type(s1, struct test_struct, 2, 4); + s2 = i_realloc(s2, sizeof(struct test_struct) * 2, + sizeof(struct test_struct) * 4); + s1[2] = cd; s2[2] = cd; + s1[3] = de; s2[3] = de; + test_assert(memcmp(&s1[0], &ab, sizeof(ab)) == 0); + test_assert(memcmp(&s1[1], &bc, sizeof(bc)) == 0); + test_assert(memcmp(&s1[2], &cd, sizeof(cd)) == 0); + test_assert(memcmp(&s1[3], &de, sizeof(de)) == 0); + test_assert(memcmp(s1, s2, sizeof(struct test_struct) * 4) == 0); + + /* freeing realloced memory */ + i_free(s1); + i_free(s2); + test_assert(s1 == NULL); + test_assert(s2 == NULL); + + /* allcating new memory with realloc */ + s1 = i_realloc_type(NULL, struct test_struct, 0, 2); + s2 = i_realloc(NULL, 0, sizeof(struct test_struct) * 2); + s1[0] = ab; s2[0] = ab; + s1[1] = bc; s2[1] = bc; + test_assert(memcmp(s1, s2, sizeof(struct test_struct) * 2) == 0); + + i_free(s1); + i_free(s2); + + test_end(); +} + +void test_imem(void) +{ + test_imem_alloc(); +} diff --git a/src/lib/test-ioloop.c b/src/lib/test-ioloop.c new file mode 100644 index 0000000..89f6888 --- /dev/null +++ b/src/lib/test-ioloop.c @@ -0,0 +1,404 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "net.h" +#include "time-util.h" +#include "ioloop.h" +#include "istream.h" + +#include <unistd.h> + +struct test_ctx { + bool got_left; + bool got_right; + bool got_to; +}; + +static void timeout_callback(struct timeval *tv) +{ + i_gettimeofday(tv); + io_loop_stop(current_ioloop); +} + +static void test_ioloop_fd_cb_left(struct test_ctx *ctx) +{ + ctx->got_left = TRUE; + if (ctx->got_left && ctx->got_right) + io_loop_stop(current_ioloop); +} + +static void test_ioloop_fd_cb_right(struct test_ctx *ctx) +{ + ctx->got_right = TRUE; + if (ctx->got_left && ctx->got_right) + io_loop_stop(current_ioloop); +} + +static void test_ioloop_fd_to(struct test_ctx *ctx) +{ + ctx->got_to = TRUE; + io_loop_stop(current_ioloop); +} + +static void test_ioloop_fd(void) +{ + test_begin("ioloop fd"); + + struct test_ctx test_ctx; + int fds[2]; + int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + + test_assert(ret == 0); + if (ret < 0) { + i_error("socketpair() failed: %m"); + test_end(); + return; + } + + i_zero(&test_ctx); + + struct ioloop *ioloop = io_loop_create(); + + struct io *io_left = + io_add(fds[0], IO_READ, + test_ioloop_fd_cb_left, &test_ctx); + struct io *io_right = + io_add(fds[1], IO_READ, + test_ioloop_fd_cb_right, &test_ctx); + + struct timeout *to = timeout_add(2000, test_ioloop_fd_to, &test_ctx); + + if (write(fds[0], "ltr", 3) != 3 || + write(fds[1], "rtl", 3) != 3) + i_fatal("write() failed: %m"); + + io_loop_run(ioloop); + + timeout_remove(&to); + io_remove(&io_left); + io_remove(&io_right); + + test_assert(test_ctx.got_to == FALSE); + test_assert(test_ctx.got_left == TRUE); + test_assert(test_ctx.got_right == TRUE); + + io_loop_destroy(&ioloop); + i_close_fd(&fds[0]); + i_close_fd(&fds[1]); + + test_end(); +} + +static void test_ioloop_timeout(void) +{ + struct ioloop *ioloop, *ioloop2; + struct timeout *to, *to2; + struct timeval tv_start, tv_callback; + + test_begin("ioloop timeout"); + + ioloop = io_loop_create(); + + /* add a timeout by moving it from another ioloop */ + ioloop2 = io_loop_create(); + test_assert(io_loop_is_empty(ioloop)); + test_assert(io_loop_is_empty(ioloop2)); + to2 = timeout_add(1000, timeout_callback, &tv_callback); + test_assert(io_loop_is_empty(ioloop)); + test_assert(!io_loop_is_empty(ioloop2)); + io_loop_set_current(ioloop); + to2 = io_loop_move_timeout(&to2); + test_assert(!io_loop_is_empty(ioloop)); + test_assert(io_loop_is_empty(ioloop2)); + io_loop_set_current(ioloop2); + io_loop_destroy(&ioloop2); + + sleep(1); + + /* add & remove immediately */ + to = timeout_add(1000, timeout_callback, &tv_callback); + timeout_remove(&to); + + /* add the timeout we're actually testing below */ + to = timeout_add(1000, timeout_callback, &tv_callback); + i_gettimeofday(&tv_start); + io_loop_run(ioloop); + test_assert(timeval_diff_msecs(&tv_callback, &tv_start) >= 500); + timeout_remove(&to); + timeout_remove(&to2); + test_assert(io_loop_is_empty(ioloop)); + io_loop_destroy(&ioloop); + + test_end(); +} + +static void zero_timeout_callback(unsigned int *counter) +{ + *counter += 1; +} + +static void test_ioloop_zero_timeout(void) +{ + struct ioloop *ioloop; + struct timeout *to; + struct io *io; + unsigned int counter = 0; + int fd[2]; + + test_begin("ioloop zero timeout"); + + if (pipe(fd) < 0) + i_fatal("pipe() failed: %m"); + switch (fork()) { + case (pid_t)-1: + i_fatal("fork() failed: %m"); + case 0: + sleep(1); + char c = 0; + if (write(fd[1], &c, 1) < 0) + i_fatal("write(pipe) failed: %m"); + test_exit(0); + default: + break; + } + + ioloop = io_loop_create(); + to = timeout_add_short(0, zero_timeout_callback, &counter); + io = io_add(fd[0], IO_READ, io_loop_stop, ioloop); + + io_loop_run(ioloop); + test_assert_ucmp(counter, >, 1000); + + timeout_remove(&to); + io_remove(&io); + io_loop_destroy(&ioloop); + test_end(); +} + +struct zero_timeout_recreate_ctx { + struct timeout *to; + unsigned int counter; +}; + +static void +zero_timeout_recreate_callback(struct zero_timeout_recreate_ctx *ctx) +{ + timeout_remove(&ctx->to); + ctx->to = timeout_add_short(0, zero_timeout_recreate_callback, ctx); + ctx->counter++; +} + +static void test_ioloop_zero_timeout_recreate(void) +{ + struct ioloop *ioloop; + struct io *io; + struct zero_timeout_recreate_ctx ctx = { .counter = 0 }; + int fd[2]; + + test_begin("ioloop zero timeout recreate"); + + if (pipe(fd) < 0) + i_fatal("pipe() failed: %m"); + switch (fork()) { + case (pid_t)-1: + i_fatal("fork() failed: %m"); + case 0: + sleep(1); + char c = 0; + if (write(fd[1], &c, 1) < 0) + i_fatal("write(pipe) failed: %m"); + test_exit(0); + default: + break; + } + + ioloop = io_loop_create(); + ctx.to = timeout_add_short(0, zero_timeout_recreate_callback, &ctx); + io = io_add(fd[0], IO_READ, io_loop_stop, ioloop); + + io_loop_run(ioloop); + test_assert_ucmp(ctx.counter, >, 1000); + + timeout_remove(&ctx.to); + io_remove(&io); + io_loop_destroy(&ioloop); + test_end(); +} + +static void io_callback(void *context ATTR_UNUSED) +{ +} + +static void test_ioloop_find_fd_conditions(void) +{ + static struct { + enum io_condition condition; + int fd[2]; + struct io *io; + } tests[] = { + { IO_ERROR, { -1, -1 }, NULL }, + { IO_READ, { -1, -1 }, NULL }, + { IO_WRITE, { -1, -1 }, NULL }, + { IO_READ | IO_WRITE, { -1, -1 }, NULL }, + { IO_READ, { -1, -1 }, NULL } /* read+write as separate ios */ + }; + struct ioloop *ioloop; + struct io *io; + unsigned int i; + + test_begin("ioloop find fd conditions"); + + ioloop = io_loop_create(); + + for (i = 0; i < N_ELEMENTS(tests); i++) { + if (socketpair(AF_UNIX, SOCK_STREAM, 0, tests[i].fd) < 0) + i_fatal("socketpair() failed: %m"); + tests[i].io = io_add(tests[i].fd[0], tests[i].condition, io_callback, NULL); + } + io = io_add(tests[i-1].fd[0], IO_WRITE, io_callback, NULL); + tests[i-1].condition |= IO_WRITE; + + for (i = 0; i < N_ELEMENTS(tests); i++) + test_assert_idx(io_loop_find_fd_conditions(ioloop, tests[i].fd[0]) == tests[i].condition, i); + + io_remove(&io); + for (i = 0; i < N_ELEMENTS(tests); i++) { + io_remove(&tests[i].io); + i_close_fd(&tests[i].fd[0]); + i_close_fd(&tests[i].fd[1]); + } + io_loop_destroy(&ioloop); + + test_end(); +} + +static void io_callback_pending_io(void *context ATTR_UNUSED) +{ + io_loop_stop(current_ioloop); +} + +static void test_ioloop_pending_io(void) +{ + test_begin("ioloop pending io"); + + struct istream *is = i_stream_create_from_data("data", 4); + struct ioloop *ioloop = io_loop_create(); + test_assert(io_loop_is_empty(ioloop)); + struct io *io = io_add_istream(is, io_callback_pending_io, NULL); + test_assert(!io_loop_is_empty(ioloop)); + io_loop_set_current(ioloop); + io_set_pending(io); + io_loop_run(ioloop); + io_remove(&io); + i_stream_unref(&is); + io_loop_destroy(&ioloop); + + test_end(); +} + +static void test_ioloop_context_callback(struct ioloop_context *ctx) +{ + test_assert(io_loop_get_current_context(current_ioloop) == ctx); + io_loop_stop(current_ioloop); +} + +static void test_ioloop_context(void) +{ + test_begin("ioloop context"); + struct ioloop *ioloop = io_loop_create(); + struct ioloop_context *ctx = io_loop_context_new(ioloop); + + test_assert(io_loop_get_current_context(current_ioloop) == NULL); + io_loop_context_activate(ctx); + test_assert(io_loop_get_current_context(current_ioloop) == ctx); + struct timeout *to = timeout_add(0, test_ioloop_context_callback, ctx); + + io_loop_run(ioloop); + test_assert(io_loop_get_current_context(current_ioloop) == NULL); + /* test that we don't crash at deinit if we leave the context active */ + io_loop_context_activate(ctx); + test_assert(io_loop_get_current_context(current_ioloop) == ctx); + + timeout_remove(&to); + io_loop_context_unref(&ctx); + io_loop_destroy(&ioloop); + test_end(); +} + +static void test_ioloop_context_events_run(struct event *root_event) +{ + struct ioloop *ioloop = io_loop_create(); + struct ioloop_context *ctx1, *ctx2; + + /* create context 1 */ + ctx1 = io_loop_context_new(ioloop); + io_loop_context_switch(ctx1); + struct event *ctx1_event1 = event_create(NULL); + event_push_global(ctx1_event1); + struct event *ctx1_event2 = event_create(NULL); + event_push_global(ctx1_event2); + io_loop_context_deactivate(ctx1); + + test_assert(event_get_global() == root_event); + + /* create context 2 */ + ctx2 = io_loop_context_new(ioloop); + io_loop_context_switch(ctx2); + struct event *ctx2_event1 = event_create(NULL); + event_push_global(ctx2_event1); + io_loop_context_deactivate(ctx2); + + test_assert(event_get_global() == root_event); + + /* test switching contexts */ + io_loop_context_switch(ctx1); + test_assert(event_get_global() == ctx1_event2); + io_loop_context_switch(ctx2); + test_assert(event_get_global() == ctx2_event1); + + /* test popping away events */ + io_loop_context_switch(ctx1); + event_pop_global(ctx1_event2); + io_loop_context_switch(ctx2); + event_pop_global(ctx2_event1); + io_loop_context_switch(ctx1); + test_assert(event_get_global() == ctx1_event1); + io_loop_context_switch(ctx2); + test_assert(event_get_global() == root_event); + + io_loop_context_deactivate(ctx2); + io_loop_context_unref(&ctx1); + io_loop_context_unref(&ctx2); + io_loop_destroy(&ioloop); + + event_unref(&ctx1_event1); + event_unref(&ctx1_event2); + event_unref(&ctx2_event1); +} + +static void test_ioloop_context_events(void) +{ + test_begin("ioloop context - no root event"); + test_ioloop_context_events_run(NULL); + test_end(); + + test_begin("ioloop context - with root event"); + struct event *root_event = event_create(NULL); + event_push_global(root_event); + test_ioloop_context_events_run(root_event); + event_pop_global(root_event); + event_unref(&root_event); + test_end(); +} + +void test_ioloop(void) +{ + test_ioloop_timeout(); + test_ioloop_zero_timeout(); + test_ioloop_zero_timeout_recreate(); + test_ioloop_find_fd_conditions(); + test_ioloop_pending_io(); + test_ioloop_fd(); + test_ioloop_context(); + test_ioloop_context_events(); +} diff --git a/src/lib/test-iostream-proxy.c b/src/lib/test-iostream-proxy.c new file mode 100644 index 0000000..9086c5d --- /dev/null +++ b/src/lib/test-iostream-proxy.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "istream.h" +#include "ostream.h" +#include "buffer.h" +#include "ioloop.h" +#include "iostream-proxy.h" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> + +static +void completed(enum iostream_proxy_side side ATTR_UNUSED, + enum iostream_proxy_status status ATTR_UNUSED, int *u0) +{ + i_assert(*u0 > 0); + if (--*u0 == 0) + io_loop_stop(current_ioloop); +} + +static +void test_iostream_proxy_simple(void) +{ + size_t bytes; + + test_begin("iostream_proxy"); + int sfdl[2]; + int sfdr[2]; + + int counter; + + test_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sfdl) == 0); + test_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sfdr) == 0); + + fd_set_nonblock(sfdl[0], TRUE); + fd_set_nonblock(sfdl[1], TRUE); + fd_set_nonblock(sfdr[0], TRUE); + fd_set_nonblock(sfdr[1], TRUE); + + struct ioloop *ioloop = io_loop_create(); + + struct istream *left_in = i_stream_create_fd(sfdl[1], IO_BLOCK_SIZE); + struct ostream *left_out = o_stream_create_fd(sfdl[1], IO_BLOCK_SIZE); + + struct istream *right_in = i_stream_create_fd(sfdr[1], IO_BLOCK_SIZE); + struct ostream *right_out = o_stream_create_fd(sfdr[1], IO_BLOCK_SIZE); + + struct iostream_proxy *proxy; + + proxy = iostream_proxy_create(left_in, left_out, right_in, right_out); + i_stream_unref(&left_in); + o_stream_unref(&left_out); + i_stream_unref(&right_in); + o_stream_unref(&right_out); + + iostream_proxy_set_completion_callback(proxy, completed, &counter); + iostream_proxy_start(proxy); + + left_in = i_stream_create_fd(sfdl[0], IO_BLOCK_SIZE); + left_out = o_stream_create_fd(sfdl[0], IO_BLOCK_SIZE); + + right_in = i_stream_create_fd(sfdr[0], IO_BLOCK_SIZE); + right_out = o_stream_create_fd(sfdr[0], IO_BLOCK_SIZE); + + test_assert(proxy != NULL); + test_assert(o_stream_send_str(left_out, "hello, world") > 0); + test_assert(o_stream_flush(left_out) > 0); + o_stream_unref(&left_out); + test_assert(shutdown(sfdl[0], SHUT_WR) == 0); + + counter = 1; + io_loop_run(ioloop); + + test_assert(i_stream_read(right_in) > 0); + test_assert(strcmp((const char*)i_stream_get_data(right_in, &bytes), "hello, world") == 0); + i_stream_skip(right_in, bytes); + + test_assert(o_stream_send_str(right_out, "hello, world") > 0); + test_assert(o_stream_flush(right_out) > 0); + o_stream_unref(&right_out); + test_assert(shutdown(sfdr[0], SHUT_WR) == 0); + + counter = 1; + io_loop_run(ioloop); + + test_assert(i_stream_read(left_in) > 0); + test_assert(strcmp((const char*)i_stream_get_data(left_in, &bytes), "hello, world") == 0); + i_stream_skip(left_in, bytes); + + iostream_proxy_unref(&proxy); + + io_loop_destroy(&ioloop); + + i_stream_unref(&left_in); + i_stream_unref(&right_in); + + /* close fd */ + i_close_fd(&sfdl[0]); + i_close_fd(&sfdl[1]); + i_close_fd(&sfdr[0]); + i_close_fd(&sfdr[1]); + + test_end(); +} + +void test_iostream_proxy(void) +{ + T_BEGIN { + test_iostream_proxy_simple(); + } T_END; +} diff --git a/src/lib/test-iostream-pump.c b/src/lib/test-iostream-pump.c new file mode 100644 index 0000000..f970fba --- /dev/null +++ b/src/lib/test-iostream-pump.c @@ -0,0 +1,325 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "istream.h" +#include "ostream.h" +#include "buffer.h" +#include "str.h" +#include "ioloop.h" +#include "iostream-pump.h" +#include "istream-failure-at.h" +#include "ostream-failure-at.h" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> + +struct nonblock_ctx { + struct istream *in; + struct ostream *out; + uoff_t pos, max_size; +}; + +static unsigned char data[] = "hello, world"; + +static void completed(enum iostream_pump_status status, int *u0) +{ + /* to somehow discern between error and success .. */ + (*u0) -= (status == IOSTREAM_PUMP_STATUS_INPUT_EOF ? 1 : 2); + io_loop_stop(current_ioloop); +} + +static void failed(int *u0) +{ + *u0 = -1; /* ensure failure */ + io_loop_stop(current_ioloop); +} + +static void pump_nonblocking_timeout(struct nonblock_ctx *ctx) +{ + switch (ctx->pos % 4) { + case 0: + break; + case 1: + /* allow more input */ + if (ctx->in->blocking) + break; + if (ctx->pos/4 == ctx->max_size+1) + test_istream_set_allow_eof(ctx->in, TRUE); + else + test_istream_set_size(ctx->in, ctx->pos/4); + i_stream_set_input_pending(ctx->in, TRUE); + break; + case 2: + break; + case 3: { + /* allow more output. give always one byte less than the + input size so there's something in internal buffer. */ + if (ctx->out->blocking) + break; + size_t size = ctx->pos/4; + if (size > 0) + test_ostream_set_max_output_size(ctx->out, size-1); + break; + } + } + ctx->pos++; +} + +static const char * +run_pump(struct istream *in, struct ostream *out, int *counter, + buffer_t *out_buffer) +{ + struct iostream_pump *pump; + struct ioloop *ioloop = io_loop_create(); + io_loop_set_current(ioloop); + struct nonblock_ctx ctx = { in, out, 0, 0 }; + struct timeout *to2 = NULL; + + if (!in->blocking) { + test_assert(i_stream_get_size(in, TRUE, &ctx.max_size) > 0); + test_istream_set_size(in, 0); + test_istream_set_allow_eof(in, FALSE); + } + if (!out->blocking) { + test_ostream_set_max_output_size(out, 0); + } + if (!in->blocking || !out->blocking) { + to2 = timeout_add_short(0, pump_nonblocking_timeout, &ctx); + } + + pump = iostream_pump_create(in, out); + i_stream_unref(&in); + o_stream_unref(&out); + + iostream_pump_set_completion_callback(pump, completed, counter); + iostream_pump_start(pump); + + alarm(5); + struct timeout *to = timeout_add(3000, failed, counter); + + io_loop_run(current_ioloop); + + timeout_remove(&to); + timeout_remove(&to2); + alarm(0); + + test_assert(*counter == 0); + + if (!ctx.out->blocking && ctx.in->stream_errno != 0 && + ctx.out->stream_errno == 0) { + /* input failed, finish flushing output */ + test_ostream_set_max_output_size(ctx.out, SIZE_MAX); + test_assert(o_stream_flush(ctx.out) > 0); + } else { + test_assert(o_stream_flush(ctx.out) != 0); + } + + const char *ret = t_strdup(str_c(out_buffer)); + + iostream_pump_unref(&pump); + io_loop_destroy(&ioloop); + return ret; +} + +static void +test_iostream_setup(bool in_block, bool out_block, + struct istream **in_r, struct ostream **out_r, + buffer_t **out_buffer_r) +{ + *out_buffer_r = t_buffer_create(128); + + *in_r = test_istream_create_data(data, sizeof(data)); + (*in_r)->blocking = in_block; + + if (out_block) + *out_r = test_ostream_create(*out_buffer_r); + else + *out_r = test_ostream_create_nonblocking(*out_buffer_r, 1); +} + +static void +test_iostream_pump_simple(bool in_block, bool out_block) +{ + int counter; + struct istream *in; + struct ostream *out; + buffer_t *buffer; + + test_begin(t_strdup_printf("iostream_pump " + "(in=%sblocking, out=%sblocking)", + (in_block ? "" : "non-"), + (out_block ? "" : "non-"))); + + test_iostream_setup(in_block, out_block, &in, &out, &buffer); + counter = 1; + + test_assert(strcmp(run_pump(in, out, &counter, buffer), + "hello, world") == 0); + + test_end(); +} + +static void +test_iostream_pump_failure_start_read(bool in_block, bool out_block) +{ + int counter; + struct istream *in, *in_2; + struct ostream *out; + buffer_t *buffer; + + test_begin(t_strdup_printf("iostream_pump failure start-read " + "(in=%sblocking, out=%sblocking)", + (in_block ? "" : "non-"), + (out_block ? "" : "non-"))); + + test_iostream_setup(in_block, out_block, &in_2, &out, &buffer); + in = i_stream_create_failure_at(in_2, 0, EIO, "test pump fail"); + i_stream_unref(&in_2); + counter = 2; + test_assert(strcmp(run_pump(in, out, &counter, buffer), "") == 0); + + test_end(); +} + +static void +test_iostream_pump_failure_mid_read(bool in_block, bool out_block) +{ + int counter; + struct istream *in, *in_2; + struct ostream *out; + buffer_t *buffer; + + test_begin(t_strdup_printf("iostream_pump failure mid-read " + "(in=%sblocking, out=%sblocking)", + (in_block ? "" : "non-"), + (out_block ? "" : "non-"))); + + test_iostream_setup(in_block, out_block, &in_2, &out, &buffer); + in = i_stream_create_failure_at(in_2, 4, EIO, "test pump fail"); + i_stream_unref(&in_2); + counter = 2; + test_assert(strcmp(run_pump(in, out, &counter, buffer), "hell") == 0); + + test_end(); +} + +static void +test_iostream_pump_failure_end_read(bool in_block, bool out_block) +{ + int counter; + struct istream *in, *in_2; + struct ostream *out; + buffer_t *buffer; + + test_begin(t_strdup_printf("iostream_pump failure mid-read " + "(in=%sblocking, out=%sblocking)", + (in_block ? "" : "non-"), + (out_block ? "" : "non-"))); + + test_iostream_setup(in_block, out_block, &in_2, &out, &buffer); + in = i_stream_create_failure_at_eof(in_2, EIO, "test pump fail"); + i_stream_unref(&in_2); + counter = 2; + test_assert(strcmp(run_pump(in, out, &counter, buffer), + "hello, world") == 0); + + test_end(); +} + +static void +test_iostream_pump_failure_start_write(bool in_block, bool out_block) +{ + int counter; + struct istream *in; + struct ostream *out, *out_2; + buffer_t *buffer; + + test_begin(t_strdup_printf("iostream_pump failure start-write " + "(in=%sblocking, out=%sblocking)", + (in_block ? "" : "non-"), + (out_block ? "" : "non-"))); + + test_iostream_setup(in_block, out_block, &in, &out_2, &buffer); + out = o_stream_create_failure_at(out_2, 0, "test pump fail"); + o_stream_unref(&out_2); + counter = 2; + test_assert(strcmp(run_pump(in, out, &counter, buffer), "") == 0); + + test_end(); +} + +static void +test_iostream_pump_failure_mid_write(bool in_block, bool out_block) +{ + int counter; + struct istream *in; + struct ostream *out, *out_2; + buffer_t *buffer; + + test_begin(t_strdup_printf("iostream_pump failure mid-write " + "(in=%sblocking, out=%sblocking)", + (in_block ? "" : "non-"), + (out_block ? "" : "non-"))); + + test_iostream_setup(in_block, out_block, &in, &out_2, &buffer); + out = o_stream_create_failure_at(out_2, 4, "test pump fail"); + o_stream_unref(&out_2); + counter = 2; + + /* "hel" because the last byte is only in internal buffer */ + test_assert(strcmp(run_pump(in, out, &counter, buffer), + (out_block ? (in_block ? "" : "hell") : + "hel")) == 0); + + test_end(); +} + +static void +test_iostream_pump_failure_end_write(bool in_block, bool out_block) +{ + int counter; + struct istream *in; + struct ostream *out, *out_2; + buffer_t *buffer; + + if (!out_block || !in_block) { + /* we'll get flushes constantly */ + return; + } + + test_begin("iostream_pump failure end-write (blocking)"); + + test_iostream_setup(in_block, out_block, &in, &out_2, &buffer); + out = o_stream_create_failure_at_flush(out_2, "test pump fail"); + o_stream_unref(&out_2); + counter = 2; + test_assert(strcmp(run_pump(in, out, &counter, buffer), + "hello, world") == 0); + + test_end(); +} + +static void +test_iostream_pump_real(void) +{ + for(int i = 0; i < 3; i++) { + bool in_block = ((i & BIT(0)) != 0); + bool out_block = ((i & BIT(1)) != 0); + + test_iostream_pump_simple(in_block, out_block); + test_iostream_pump_failure_start_read(in_block, out_block); + test_iostream_pump_failure_mid_read(in_block, out_block); + test_iostream_pump_failure_end_read(in_block, out_block); + test_iostream_pump_failure_start_write(in_block, out_block); + test_iostream_pump_failure_mid_write(in_block, out_block); + test_iostream_pump_failure_end_write(in_block, out_block); + } +} + +void test_iostream_pump(void) +{ + T_BEGIN { + test_iostream_pump_real(); + } T_END; +} diff --git a/src/lib/test-iostream-temp.c b/src/lib/test-iostream-temp.c new file mode 100644 index 0000000..cfb8658 --- /dev/null +++ b/src/lib/test-iostream-temp.c @@ -0,0 +1,150 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "istream.h" +#include "ostream.h" +#include "iostream-temp.h" + +#include <unistd.h> +#include <fcntl.h> + +static void test_iostream_temp_create_sized_memory(void) +{ + struct ostream *output; + + test_begin("iostream_temp_create_sized() memory"); + output = iostream_temp_create_sized(".intentional-nonexistent-error/", 0, "test", 4); + test_assert(o_stream_send(output, "123", 3) == 3); + test_assert(output->offset == 3); + test_assert(o_stream_send(output, "4", 1) == 1); + test_assert(output->offset == 4); + test_assert(o_stream_get_fd(output) == -1); + + /* now we'll try to switch to writing to a file, but it'll fail */ + test_expect_error_string("safe_mkstemp"); + test_assert(o_stream_send(output, "5", 1) == 1); + test_expect_no_more_errors(); + + test_assert(o_stream_get_fd(output) == -1); + o_stream_destroy(&output); + test_end(); +} + +static void test_iostream_temp_create_sized_disk(void) +{ + struct ostream *output; + + test_begin("iostream_temp_create_sized() disk"); + output = iostream_temp_create_sized(".", 0, "test", 4); + test_assert(o_stream_send(output, "123", 3) == 3); + test_assert(output->offset == 3); + test_assert(o_stream_send(output, "4", 1) == 1); + test_assert(output->offset == 4); + test_assert(o_stream_get_fd(output) == -1); + test_assert(o_stream_send(output, "5", 1) == 1); + test_assert(output->offset == 5); + test_assert(o_stream_get_fd(output) != -1); + o_stream_destroy(&output); + test_end(); +} + +static void test_iostream_temp_create_write_error(void) +{ + struct ostream *output; + + test_begin("iostream_temp_create_sized() write error"); + output = iostream_temp_create_sized(".", 0, "test", 1); + + test_assert(o_stream_send(output, "123", 3) == 3); + test_assert(o_stream_get_fd(output) != -1); + test_assert(output->offset == 3); + test_assert(o_stream_temp_move_to_memory(output) == 0); + test_assert(o_stream_get_fd(output) == -1); + test_assert(o_stream_send(output, "45", 2) == 2); + test_assert(output->offset == 5); + + const unsigned char *data; + size_t size; + struct istream *input = iostream_temp_finish(&output, 128); + test_assert(i_stream_read_bytes(input, &data, &size, 5) == 1 && + memcmp(data, "12345", 5) == 0); + i_stream_destroy(&input); + + test_end(); +} + +static void test_iostream_temp_istream(void) +{ + struct istream *input, *input2, *temp_input; + struct ostream *output; + int fd; + + test_begin("iostream_temp istream"); + + fd = open(".temp.istream", O_RDWR | O_CREAT | O_TRUNC, 0600); + if (fd == -1) + i_fatal("create(.temp.istream) failed: %m"); + test_assert(write(fd, "foobar", 6) == 6); + test_assert(lseek(fd, 0, SEEK_SET) == 0); + + input = i_stream_create_fd_autoclose(&fd, 1024); + /* a working fd-dup */ + output = iostream_temp_create_sized(".nonexistent/", + IOSTREAM_TEMP_FLAG_TRY_FD_DUP, "test", 1); + test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); + test_assert(output->offset == 6); + temp_input = iostream_temp_finish(&output, 128); + test_assert(i_stream_read(temp_input) == 6); + i_stream_destroy(&temp_input); + + /* non-working fd-dup: write data before sending istream */ + i_stream_seek(input, 0); + output = iostream_temp_create_sized(".intentional-nonexistent-error/", + IOSTREAM_TEMP_FLAG_TRY_FD_DUP, "test", 4); + test_assert(o_stream_send(output, "1234", 4) == 4); + test_assert(output->offset == 4); + test_expect_error_string("safe_mkstemp"); + test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); + test_assert(output->offset == 10); + test_expect_no_more_errors(); + o_stream_destroy(&output); + + /* non-working fd-dup: write data after sending istream */ + i_stream_seek(input, 0); + output = iostream_temp_create_sized(".intentional-nonexistent-error/", + IOSTREAM_TEMP_FLAG_TRY_FD_DUP, "test", 4); + test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); + test_assert(output->offset == 6); + test_expect_error_string("safe_mkstemp"); + test_assert(o_stream_send(output, "1", 1) == 1); + test_assert(output->offset == 7); + test_expect_no_more_errors(); + o_stream_destroy(&output); + + /* non-working fd-dup: send two istreams */ + i_stream_seek(input, 0); + input2 = i_stream_create_limit(input, UOFF_T_MAX); + output = iostream_temp_create_sized(".intentional-nonexistent-error/", + IOSTREAM_TEMP_FLAG_TRY_FD_DUP, "test", 4); + test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); + test_assert(output->offset == 6); + test_expect_error_string("safe_mkstemp"); + test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); + test_assert(output->offset == 12); + test_expect_no_more_errors(); + o_stream_destroy(&output); + i_stream_unref(&input2); + + i_stream_destroy(&input); + + i_unlink(".temp.istream"); + test_end(); +} + +void test_iostream_temp(void) +{ + test_iostream_temp_create_sized_memory(); + test_iostream_temp_create_sized_disk(); + test_iostream_temp_create_write_error(); + test_iostream_temp_istream(); +} diff --git a/src/lib/test-iso8601-date.c b/src/lib/test-iso8601-date.c new file mode 100644 index 0000000..0b584dd --- /dev/null +++ b/src/lib/test-iso8601-date.c @@ -0,0 +1,147 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "test-common.h" +#include "iso8601-date.h" + +#include <time.h> + +struct iso8601_date_test { + const char *date_in; + const char *date_out; + + struct tm tm; + int zone_offset; +}; + +/* Valid date tests */ +struct iso8601_date_test valid_date_tests[] = { + { + .date_in = "2007-11-07T23:05:34+00:00", + .tm = { + .tm_year = 107, .tm_mon = 10, .tm_mday = 7, + .tm_hour = 23, .tm_min = 5, .tm_sec = 34 }, + },{ + .date_in = "2011-01-07T21:03:31+00:30", + .tm = { + .tm_year = 111, .tm_mon = 0, .tm_mday = 7, + .tm_hour = 21, .tm_min = 3, .tm_sec = 31 }, + .zone_offset = 30 + },{ + .date_in = "2006-05-09T18:04:12+05:30", + .tm = { + .tm_year = 106, .tm_mon = 4, .tm_mday = 9, + .tm_hour = 18, .tm_min = 4, .tm_sec = 12 }, + .zone_offset = 5*60+30 + },{ + .date_in = "1975-10-30T06:33:29Z", + .date_out = "1975-10-30T06:33:29+00:00", + .tm = { + .tm_year = 75, .tm_mon = 9, .tm_mday = 30, + .tm_hour = 6, .tm_min = 33, .tm_sec = 29 }, + },{ + .date_in = "1988-04-24t15:02:12z", + .date_out = "1988-04-24T15:02:12+00:00", + .tm = { + .tm_year = 88, .tm_mon = 3, .tm_mday = 24, + .tm_hour = 15, .tm_min = 2, .tm_sec = 12 }, + },{ + .date_in = "2012-02-29T08:12:34.23198Z", + .date_out = "2012-02-29T08:12:34+00:00", + .tm = { + .tm_year = 112, .tm_mon = 1, .tm_mday = 29, + .tm_hour = 8, .tm_min = 12, .tm_sec = 34 }, + } +}; + +unsigned int valid_date_test_count = N_ELEMENTS(valid_date_tests); + +static void test_iso8601_date_valid(void) +{ + unsigned int i; + + for (i = 0; i < valid_date_test_count; i++) T_BEGIN { + const char *date_in, *date_out, *pdate_out; + struct tm *tm = &valid_date_tests[i].tm, ptm; + int zone_offset = valid_date_tests[i].zone_offset, pzone_offset; + bool result; + + date_in = valid_date_tests[i].date_in; + date_out = valid_date_tests[i].date_out == NULL ? + date_in : valid_date_tests[i].date_out; + + test_begin(t_strdup_printf("iso8601 date valid [%d]", i)); + + result = iso8601_date_parse_tm + ((const unsigned char *)date_in, strlen(date_in), &ptm, &pzone_offset); + test_out(t_strdup_printf("parse %s", date_in), result); + if (result) { + bool equal = tm->tm_year == ptm.tm_year && tm->tm_mon == ptm.tm_mon && + tm->tm_mday == ptm.tm_mday && tm->tm_hour == ptm.tm_hour && + tm->tm_min == ptm.tm_min && tm->tm_sec == ptm.tm_sec; + + test_out("valid timestamp", equal); + test_out_reason("valid timezone", zone_offset == pzone_offset, + t_strdup_printf("%d", pzone_offset)); + + pdate_out = iso8601_date_create_tm(tm, zone_offset); + test_out_reason("valid create", strcmp(date_out, pdate_out) == 0, + pdate_out); + } + + test_end(); + } T_END; +} + +/* Invalid date tests */ +const char *invalid_date_tests[] = { + "200-11-17T23:05:34+00:00", + "2007:11-17T23:05:34+00:00", + "2007-11?17T23:05:34+00:00", + "2007-49-17T23:05:34+00:00", + "2007-11-77T23:05:34+00:00", + "2007-11-17K23:05:34+00:00", + "2007-11-13T59:05:34+00:00", + "2007-112-13T12:15:34+00:00", + "2007-11-133T12:15:34+00:00", + "2007-11-13T12J15:34+00:00", + "2007-11-13T12:15*34+00:00", + "2007-11-13T12:15:34/00:00", + "2007-11-13T12:15:34+00-00", + "2007-11-13T123:15:34+00:00", + "2007-11-13T12:157:34+00:00", + "2007-11-13T12:15:342+00:00", + "2007-11-13T12:15:34+001:00", + "2007-11-13T12:15:32+00:006", + "2007-02-29T15:13:21Z" +}; + +unsigned int invalid_date_test_count = N_ELEMENTS(invalid_date_tests); + +static void test_iso8601_date_invalid(void) +{ + unsigned int i; + + for (i = 0; i < invalid_date_test_count; i++) T_BEGIN { + const char *date_in; + struct tm tm; + int tz; + bool result; + + date_in = invalid_date_tests[i]; + + test_begin(t_strdup_printf("iso8601 date invalid [%d]", i)); + + result = iso8601_date_parse_tm + ((const unsigned char *)date_in, strlen(date_in), &tm, &tz); + test_out(t_strdup_printf("parse %s", date_in), !result); + + test_end(); + } T_END; +} + +void test_iso8601_date(void) +{ + test_iso8601_date_valid(); + test_iso8601_date_invalid(); +} diff --git a/src/lib/test-istream-base64-decoder.c b/src/lib/test-istream-base64-decoder.c new file mode 100644 index 0000000..ea45f6d --- /dev/null +++ b/src/lib/test-istream-base64-decoder.c @@ -0,0 +1,337 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "istream-private.h" +#include "istream-base64.h" +#include "istream-sized.h" +#include "base64.h" + +struct base64_istream_test { + const char *input; + const char *output; + int stream_errno; +}; + +static const struct base64_istream_test base64_tests[] = { + { "", "", 0 }, + { "aGVsbG8gd29ybGQ=", "hello world", 0 }, + { "\naGVs\nbG8g\nd29y\nbGQ=\n", "hello world", 0 }, + { " aGVs \r\n bG8g \r\n d29y \t \r\n bGQ= \r\n\r\n", + "hello world", 0 }, + { "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgtC+INC60YPRgCDQtNC+0Y/MgdGCLg==", + "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc" + "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0" + "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc" + "\x81\xd1\x82\x2e", 0 }, + { "\r", "", 0 }, + { "\n", "", 0 }, + { "\r\n", "", 0 }, + { " ", "", 0 }, + { "foo", "\x7e\x8a", EPIPE }, + { "foo ","\x7e\x8a", EPIPE }, + { "Zm9vC", "foo", EPIPE }, + { "Zm9v!", "foo", EINVAL }, + { "Zm9!v", "fo", EINVAL }, + { "Zm9 v", "foo", 0 }, + { "Zm 9v", "foo", 0 }, + { "Z m9v", "foo", 0 }, +}; + +static const struct base64_istream_test base64url_tests[] = { + { "", "", 0 }, + { "aGVsbG8gd29ybGQ=", "hello world", 0 }, + { "\naGVs\nbG8g\nd29y\nbGQ=\n", "hello world", 0 }, + { " aGVs \r\n bG8g \r\n d29y \t \r\n bGQ= \r\n\r\n", + "hello world", 0 }, + { "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgtC-INC60YPRgCDQtNC-0Y_MgdGCLg==", + "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc" + "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0" + "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc" + "\x81\xd1\x82\x2e", 0 }, + { "\r", "", 0 }, + { "\n", "", 0 }, + { "\r\n", "", 0 }, + { " ", "", 0 }, + { "foo", "\x7e\x8a", EPIPE }, + { "foo ","\x7e\x8a", EPIPE }, + { "Zm9vC", "foo", EPIPE }, + { "Zm9v!", "foo", EINVAL }, + { "Zm9!v", "fo", EINVAL }, + { "Zm9 v", "foo", 0 }, + { "Zm 9v", "foo", 0 }, + { "Z m9v", "foo", 0 }, +}; + +static void +decode_test(unsigned int base64_input_len, + struct istream *input_data, struct istream *input, + const char *output, int stream_errno) +{ + const unsigned char *data; + size_t i, size; + int ret = 0; + + for (i = 1; i <= base64_input_len; i++) { + test_istream_set_size(input_data, i); + while ((ret = i_stream_read(input)) > 0) ; + if (ret == -1 && stream_errno != 0) + break; + test_assert(ret == 0); + } + if (ret == 0) { + test_istream_set_allow_eof(input_data, TRUE); + while ((ret = i_stream_read(input)) > 0) ; + } + test_assert(ret == -1); + test_assert(input->stream_errno == stream_errno); + + data = i_stream_get_data(input, &size); + test_assert(size == strlen(output)); + if (size > 0) + test_assert(memcmp(data, output, size) == 0); +} + +static void +decode_base64_test(const char *base64_input, const char *output, + int stream_errno) +{ + unsigned int base64_input_len = strlen(base64_input); + struct istream *input_data, *input; + + input_data = test_istream_create_data(base64_input, base64_input_len); + test_istream_set_allow_eof(input_data, FALSE); + input = i_stream_create_base64_decoder(input_data); + + decode_test(base64_input_len, input_data, input, output, stream_errno); + + i_stream_unref(&input); + i_stream_unref(&input_data); +} + +static void +decode_base64url_test(const char *base64_input, const char *output, + int stream_errno) +{ + unsigned int base64_input_len = strlen(base64_input); + struct istream *input_data, *input; + + input_data = test_istream_create_data(base64_input, base64_input_len); + test_istream_set_allow_eof(input_data, FALSE); + input = i_stream_create_base64url_decoder(input_data); + + decode_test(base64_input_len, input_data, input, output, stream_errno); + + i_stream_unref(&input); + i_stream_unref(&input_data); +} + +static void +test_istream_base64_io_random(void) +{ + unsigned char in_buf[2048]; + size_t in_buf_size; + buffer_t *out_buf; + unsigned int i, j; + int ret; + + out_buf = t_buffer_create(sizeof(in_buf)); + + test_begin("istream base64 random I/O"); + + for (i = 0; !test_has_failed() && i < 4000; i++) { + struct istream *input1, *input2, *input3, *input4, *input5; + struct istream *sinput1, *sinput2, *sinput3, *sinput4; + struct istream *top_input; + const unsigned char *data; + unsigned int chpl1, chpl2; + unsigned int sized_streams; + unsigned int crlf_encode; + size_t size; + struct base64_encoder b64enc; + + /* Initialize test data */ + in_buf_size = i_rand_limit(sizeof(in_buf)); + for (j = 0; j < in_buf_size; j++) + in_buf[j] = i_rand_uchar(); + + /* Reset final output buffer */ + buffer_set_used_size(out_buf, 0); + + /* Determine line lengths */ + chpl1 = i_rand_limit(30)*4; + chpl2 = i_rand_limit(30)*4; + + /* Create stream for test data */ + input1 = i_stream_create_from_data(in_buf, in_buf_size); + i_stream_set_name(input1, "[data]"); + + /* Determine which stages have sized streams */ + sized_streams = i_rand_minmax(0x00, 0x0f); + /* Determine which stages use CRLF */ + crlf_encode = i_rand_minmax(0x00, 0x03); + + /* Create first encoder stream */ + input2 = i_stream_create_base64_encoder( + input1, chpl1, HAS_ALL_BITS(crlf_encode, BIT(0))); + i_stream_set_name(input2, "[base64_encoder #1]"); + + if (HAS_ALL_BITS(sized_streams, BIT(0))) { + /* Wrap the first encoder stream in a sized stream to + check size and trigger any buffer overflow problems + */ + base64_encode_init(&b64enc, &base64_scheme, + (HAS_ALL_BITS(crlf_encode, BIT(0)) ? + BASE64_ENCODE_FLAG_CRLF : 0), + chpl1); + sinput1 = i_stream_create_sized(input2, + base64_get_full_encoded_size(&b64enc, + in_buf_size)); + i_stream_set_name(sinput1, "[sized #1]"); + } else { + sinput1 = input2; + i_stream_ref(sinput1); + } + + /* Create first decoder stream */ + input3 = i_stream_create_base64_decoder(sinput1); + i_stream_set_name(input3, "[base64_decoder #1]"); + + if (HAS_ALL_BITS(sized_streams, BIT(1))) { + /* Wrap the first decoder stream in a sized stream to + check size and trigger any buffer overflow problems + */ + sinput2 = i_stream_create_sized(input3, in_buf_size); + i_stream_set_name(sinput2, "[sized #2]"); + } else { + sinput2 = input3; + i_stream_ref(sinput2); + } + + /* Create second encoder stream */ + input4 = i_stream_create_base64_encoder( + sinput2, chpl2, HAS_ALL_BITS(crlf_encode, BIT(1))); + i_stream_set_name(input4, "[base64_encoder #2]"); + + if (HAS_ALL_BITS(sized_streams, BIT(2))) { + /* Wrap the second encoder stream in a sized stream to + check size and trigger any buffer overflow problems + */ + base64_encode_init(&b64enc, &base64_scheme, + (HAS_ALL_BITS(crlf_encode, BIT(1)) ? + BASE64_ENCODE_FLAG_CRLF : 0), + chpl2); + sinput3 = i_stream_create_sized(input4, + base64_get_full_encoded_size(&b64enc, + in_buf_size)); + i_stream_set_name(sinput3, "[sized #3]"); + } else { + sinput3 = input4; + i_stream_ref(sinput3); + } + + /* Create second deoder stream */ + input5 = i_stream_create_base64_decoder(sinput3); + i_stream_set_name(input5, "[base64_decoder #2]"); + + if (HAS_ALL_BITS(sized_streams, BIT(3))) { + /* Wrap the second decoder stream in a sized stream to + check size and trigger any buffer overflow problems + */ + sinput4 = i_stream_create_sized(input5, in_buf_size); + i_stream_set_name(sinput4, "[sized #4]"); + } else { + sinput4 = input5; + i_stream_ref(sinput4); + } + + + /* Assign random buffer sizes */ + i_stream_set_max_buffer_size(input5, i_rand_minmax(1, 512)); + i_stream_set_max_buffer_size(input4, i_rand_minmax(1, 512)); + i_stream_set_max_buffer_size(input3, i_rand_minmax(1, 512)); + i_stream_set_max_buffer_size(input2, i_rand_minmax(1, 512)); + + /* Read the outer stream in full with random increments. */ + top_input = sinput4; + while ((ret = i_stream_read_more( + top_input, &data, &size)) > 0) { + size_t ch = i_rand_limit(512); + + size = I_MIN(size, ch); + buffer_append(out_buf, data, size); + i_stream_skip(top_input, size); + } + if (ret < 0 && top_input->stream_errno == 0) { + data = i_stream_get_data(top_input, &size); + if (size > 0) { + buffer_append(out_buf, data, size); + i_stream_skip(top_input, size); + } + } + + /* Assert stream status */ + test_assert_idx(ret < 0 && top_input->stream_errno == 0, i); + /* Assert input/output equality */ + test_assert_idx(out_buf->used == in_buf_size && + memcmp(in_buf, out_buf->data, in_buf_size) == 0, + i); + + if (top_input->stream_errno != 0) { + i_error("%s: %s", i_stream_get_name(input1), + i_stream_get_error(input1)); + i_error("%s: %s", i_stream_get_name(input2), + i_stream_get_error(input2)); + i_error("%s: %s", i_stream_get_name(input3), + i_stream_get_error(input3)); + i_error("%s: %s", i_stream_get_name(input4), + i_stream_get_error(input4)); + i_error("%s: %s", i_stream_get_name(input5), + i_stream_get_error(input5)); + } + + if (test_has_failed()) { + i_info("Test parameters: size=%zu " + "line_length_1=%u line_length_2=%u", + in_buf_size, chpl1, chpl2); + } + + /* Clean up */ + i_stream_unref(&input1); + i_stream_unref(&input2); + i_stream_unref(&input3); + i_stream_unref(&input4); + i_stream_unref(&input5); + i_stream_unref(&sinput1); + i_stream_unref(&sinput2); + i_stream_unref(&sinput3); + i_stream_unref(&sinput4); + } + test_end(); +} + +void test_istream_base64_decoder(void) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(base64_tests); i++) { + const struct base64_istream_test *test = &base64_tests[i]; + + test_begin(t_strdup_printf("istream base64 decoder %u", i+1)); + decode_base64_test(test->input, test->output, + test->stream_errno); + test_end(); + } + + for (i = 0; i < N_ELEMENTS(base64url_tests); i++) { + const struct base64_istream_test *test = &base64url_tests[i]; + + test_begin(t_strdup_printf("istream base64url decoder %u", + i+1)); + decode_base64url_test(test->input, test->output, + test->stream_errno); + test_end(); + } + + test_istream_base64_io_random(); +} diff --git a/src/lib/test-istream-base64-encoder.c b/src/lib/test-istream-base64-encoder.c new file mode 100644 index 0000000..9fc0608 --- /dev/null +++ b/src/lib/test-istream-base64-encoder.c @@ -0,0 +1,222 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "istream-private.h" +#include "istream-base64.h" + +struct base64_istream_test { + const char *input; + unsigned int chars_per_line; + bool crlf; + const char *output; +}; + +static const struct base64_istream_test base64_tests[] = { + { "", 80, FALSE, "" }, + { "1", 80, FALSE, "MQ==" }, + { "12", 80, FALSE, "MTI=" }, + { "123", 80, FALSE, "MTIz" }, + { "1234", 80, FALSE, "MTIzNA==" }, + { "12345", 80, FALSE, "MTIzNDU=" }, + { "hello world", 80, FALSE, "aGVsbG8gd29ybGQ=" }, + { "hello world", 4, FALSE, "aGVs\nbG8g\nd29y\nbGQ=" }, + { "hello world", 4, TRUE, "aGVs\r\nbG8g\r\nd29y\r\nbGQ=" }, + { "hello worlds", 80, FALSE, "aGVsbG8gd29ybGRz" }, + { "hello worlds", 4, FALSE, "aGVs\nbG8g\nd29y\nbGRz" }, + { "hello worlds", 4, TRUE, "aGVs\r\nbG8g\r\nd29y\r\nbGRz" }, + { "hell world", 80, FALSE, "aGVsbCB3b3JsZA==" }, + { "hell world", 4, FALSE, "aGVs\nbCB3\nb3Js\nZA==" }, + { "hell world", 4, TRUE, "aGVs\r\nbCB3\r\nb3Js\r\nZA==" }, + { "hello to the world!!", 80, FALSE, + "aGVsbG8gdG8gdGhlIHdvcmxkISE=" }, + { "hello to the world!!", 8, FALSE, + "aGVsbG8g\ndG8gdGhl\nIHdvcmxk\nISE=" }, + { "hello to the world!!", 8, TRUE, + "aGVsbG8g\r\ndG8gdGhl\r\nIHdvcmxk\r\nISE=" }, + { "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc" + "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0" + "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc" + "\x81\xd1\x82\x2e", 80, FALSE, + "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgtC+INC60YPRgCDQtNC+0Y/MgdGCLg==" }, +}; + +static const struct base64_istream_test base64url_tests[] = { + { "", 80, FALSE, "" }, + { "1", 80, FALSE, "MQ==" }, + { "12", 80, FALSE, "MTI=" }, + { "123", 80, FALSE, "MTIz" }, + { "1234", 80, FALSE, "MTIzNA==" }, + { "12345", 80, FALSE, "MTIzNDU=" }, + { "hello world", 80, FALSE, "aGVsbG8gd29ybGQ=" }, + { "hello world", 4, FALSE, "aGVs\nbG8g\nd29y\nbGQ=" }, + { "hello world", 4, TRUE, "aGVs\r\nbG8g\r\nd29y\r\nbGQ=" }, + { "hello worlds", 80, FALSE, "aGVsbG8gd29ybGRz" }, + { "hello worlds", 4, FALSE, "aGVs\nbG8g\nd29y\nbGRz" }, + { "hello worlds", 4, TRUE, "aGVs\r\nbG8g\r\nd29y\r\nbGRz" }, + { "hell world", 80, FALSE, "aGVsbCB3b3JsZA==" }, + { "hell world", 4, FALSE, "aGVs\nbCB3\nb3Js\nZA==" }, + { "hell world", 4, TRUE, "aGVs\r\nbCB3\r\nb3Js\r\nZA==" }, + { "hello to the world!!", 80, FALSE, + "aGVsbG8gdG8gdGhlIHdvcmxkISE=" }, + { "hello to the world!!", 8, FALSE, + "aGVsbG8g\ndG8gdGhl\nIHdvcmxk\nISE=" }, + { "hello to the world!!", 8, TRUE, + "aGVsbG8g\r\ndG8gdGhl\r\nIHdvcmxk\r\nISE=" }, + { "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc" + "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0" + "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc" + "\x81\xd1\x82\x2e", 80, FALSE, + "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgtC-INC60YPRgCDQtNC-0Y_MgdGCLg==" }, +}; + +static const char *hello = "hello world"; + +static void encode_test(unsigned int text_len, + struct istream *input, struct istream *input_data, + const char *output) +{ + unsigned int i; + const unsigned char *data; + uoff_t stream_size; + size_t size; + ssize_t ret; + + for (i = 1; i <= text_len; i++) { + test_istream_set_size(input_data, i); + while ((ret = i_stream_read(input)) > 0) ; + test_assert(ret == 0); + } + test_istream_set_allow_eof(input_data, TRUE); + while ((ret = i_stream_read(input)) > 0) ; + test_assert(ret == -1); + + data = i_stream_get_data(input, &size); + test_assert(size == strlen(output) && memcmp(data, output, size) == 0); + + ret = i_stream_get_size(input, TRUE, &stream_size); + test_assert(ret > 0); + test_assert(size == stream_size); +} + +static void +encode_base64_test(const char *text, unsigned int chars_per_line, + bool crlf, const char *output) +{ + unsigned int text_len = strlen(text); + struct istream *input, *input_data; + + input_data = test_istream_create_data(text, text_len); + test_istream_set_allow_eof(input_data, FALSE); + input = i_stream_create_base64_encoder(input_data, chars_per_line, + crlf); + + encode_test(text_len, input, input_data, output); + + i_stream_unref(&input); + i_stream_unref(&input_data); +} + +static void +encode_base64url_test(const char *text, unsigned int chars_per_line, + bool crlf, const char *output) +{ + unsigned int text_len = strlen(text); + struct istream *input, *input_data; + + input_data = test_istream_create_data(text, text_len); + test_istream_set_allow_eof(input_data, FALSE); + input = i_stream_create_base64url_encoder(input_data, chars_per_line, + crlf); + + encode_test(text_len, input, input_data, output); + + i_stream_unref(&input); + i_stream_unref(&input_data); +} + +static void +test_encoder_seek(struct istream *input, const char *textout) +{ + unsigned int offset, len = strlen(textout); + const unsigned char *data; + size_t size; + ssize_t ret; + + while (i_stream_read(input) > 0) ; + i_stream_skip(input, i_stream_get_data_size(input)); + + for (offset = 0; offset < len; offset++) { + i_stream_seek(input, offset); + while ((ret = i_stream_read(input)) > 0) ; + test_assert(ret == -1); + + data = i_stream_get_data(input, &size); + test_assert(size == len-offset); + test_assert(memcmp(data, textout+offset, size) == 0); + i_stream_skip(input, size); + } +} + +static void +test_istream_base64_encoder_seek(const char *textin, const char *textout) +{ + struct istream *input, *input_data; + + input_data = i_stream_create_from_data(textin, strlen(textin)); + input = i_stream_create_base64_encoder(input_data, 4, TRUE); + + test_encoder_seek(input, textout); + + i_stream_unref(&input); + i_stream_unref(&input_data); +} + +static void +test_istream_base64url_encoder_seek(const char *textin, const char *textout) +{ + struct istream *input, *input_data; + + input_data = i_stream_create_from_data(textin, strlen(textin)); + input = i_stream_create_base64url_encoder(input_data, 4, TRUE); + + test_encoder_seek(input, textout); + + i_stream_unref(&input); + i_stream_unref(&input_data); +} + +void test_istream_base64_encoder(void) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(base64_tests); i++) { + const struct base64_istream_test *test = &base64_tests[i]; + + test_begin(t_strdup_printf( + "istream base64 encoder %u", i+1)); + encode_base64_test(test->input, test->chars_per_line, + test->crlf, test->output); + test_end(); + } + + for (i = 0; i < N_ELEMENTS(base64url_tests); i++) { + const struct base64_istream_test *test = &base64url_tests[i]; + + test_begin(t_strdup_printf( + "istream base64url encoder %u", i+1)); + encode_base64url_test(test->input, test->chars_per_line, + test->crlf, test->output); + test_end(); + } + + test_begin("istream base64 encoder seek"); + test_istream_base64_encoder_seek( + hello, "aGVs\r\nbG8g\r\nd29y\r\nbGQ="); + test_end(); + + test_begin("istream base64url encoder seek"); + test_istream_base64url_encoder_seek( + hello, "aGVs\r\nbG8g\r\nd29y\r\nbGQ="); + test_end(); +} diff --git a/src/lib/test-istream-chain.c b/src/lib/test-istream-chain.c new file mode 100644 index 0000000..cb72c64 --- /dev/null +++ b/src/lib/test-istream-chain.c @@ -0,0 +1,199 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "istream-private.h" +#include "istream-chain.h" + +static void test_istream_chain_basic(void) +{ + struct istream *input, *test_input, *test_input2; + struct istream_chain *chain; + const unsigned char *data; + size_t size; + + test_begin("istream chain"); + + test_input = test_istream_create("stream1"); + test_input2 = test_istream_create("STREAM2"); + + input = i_stream_create_chain(&chain, IO_BLOCK_SIZE); + /* no input */ + test_assert(i_stream_read(input) == 0); + /* stream1 input */ + i_stream_chain_append(chain, test_input); + test_assert(i_stream_read(input) == 7); + data = i_stream_get_data(input, &size); + test_assert(size == 7 && memcmp(data, "stream1", 7) == 0); + test_assert(i_stream_read(input) == 0); + data = i_stream_get_data(input, &size); + test_assert(size == 7 && memcmp(data, "stream1", 7) == 0); + /* STREAM2 input */ + i_stream_chain_append(chain, test_input2); + test_assert(i_stream_read(input) == 7); + data = i_stream_get_data(input, &size); + test_assert(size == 14 && memcmp(data, "stream1STREAM2", 14) == 0); + test_assert(i_stream_read(input) == 0); + data = i_stream_get_data(input, &size); + test_assert(size == 14 && memcmp(data, "stream1STREAM2", 14) == 0); + /* EOF */ + i_stream_chain_append_eof(chain); + test_assert(i_stream_read(input) == -1 && + input->eof && input->stream_errno == 0); + data = i_stream_get_data(input, &size); + test_assert(size == 14 && memcmp(data, "stream1STREAM2", 14) == 0); + + i_stream_unref(&input); + + test_assert(test_input->eof && test_input->stream_errno == 0); + test_assert(test_input2->eof && test_input2->stream_errno == 0); + + i_stream_unref(&test_input); + i_stream_unref(&test_input2); + test_end(); +} + +static void test_istream_chain_early_end(void) +{ + struct istream *input, *test_input; + struct istream_chain *chain; + + test_begin("istream chain early end"); + + test_input = test_istream_create("string"); + test_istream_set_size(test_input, 3); + test_istream_set_allow_eof(test_input, FALSE); + + input = i_stream_create_chain(&chain, IO_BLOCK_SIZE); + i_stream_chain_append(chain, test_input); + test_assert(i_stream_read(input) == 3); + test_istream_set_size(test_input, 5); + test_assert(i_stream_read(input) == 2); + /* with current implementation we could skip less than 5 and have + v_offset<5, but I don't think that can work in all situations. + the normal case is anyway that we'll read everything up until some + point and skip over all the data up to there. */ + i_stream_skip(input, 5); + i_stream_unref(&input); + + test_assert(test_input->v_offset == 5); + i_stream_unref(&test_input); + test_end(); +} + +static void test_istream_chain_accumulate(void) +{ + struct istream *input, *tmp_istream; + struct istream *test_istreams[5]; + struct istream_chain *chain; + const unsigned char *data; + size_t size; + + test_begin("istream chain accumulate"); + + test_istreams[0] = test_istream_create("abcdefghijklmnopqrst"); + test_istreams[1] = test_istream_create("ABCDEFGHIJKLMNOPQRSTUVWXY"); + test_istreams[2] = test_istream_create("!\"#$%&'()*+,-./01234567890:;<="); + test_istreams[3] = test_istream_create("z1y2x3w4v5u6t7s8r9q0p.o,n"); + test_istreams[4] = test_istream_create("aAbBcCdDeEfFgGhHiIjJ"); + + input = i_stream_create_chain(&chain, IO_BLOCK_SIZE); + /* no input */ + test_assert(i_stream_read(input) == 0); + + /* first stream */ + i_stream_chain_append(chain, test_istreams[0]); + tmp_istream = test_istreams[0]; i_stream_unref(&tmp_istream); + test_assert(i_stream_read_data(input, &data, &size, 0) == 1); + test_assert(size == 20); + test_assert(memcmp(data, "abcdefghijklmnopqrst", 20) == 0); + + /* partially skip */ + i_stream_skip(input, 12); + + /* second stream */ + i_stream_chain_append(chain, test_istreams[1]); + tmp_istream = test_istreams[1]; i_stream_unref(&tmp_istream); + test_istream_set_size(test_istreams[1], 0); + test_assert(i_stream_read_data(input, &data, &size, 10) == 0); + test_assert(size == 8); + test_istream_set_size(test_istreams[1], 10); + test_assert(i_stream_read_data(input, &data, &size, 10) == 1); + test_assert(size == 18); + test_istream_set_allow_eof(test_istreams[1], FALSE); + test_assert(i_stream_read(input) == 0); + test_istream_set_size(test_istreams[1], 25); + test_istream_set_allow_eof(test_istreams[1], TRUE); + test_assert(i_stream_read_data(input, &data, &size, 30) == 1); + test_assert(size == 33); + test_assert(memcmp(data, "mnopqrst" + "ABCDEFGHIJKLMNOPQRSTUVWXY", 33) == 0); + + /* partially skip */ + i_stream_skip(input, 12); + + /* third stream */ + i_stream_chain_append(chain, test_istreams[2]); + tmp_istream = test_istreams[2]; i_stream_unref(&tmp_istream); + test_istream_set_size(test_istreams[2], 0); + test_assert(i_stream_read(input) == 0); + test_istream_set_size(test_istreams[2], 30); + test_assert(i_stream_read_data(input, &data, &size, 25) == 1); + test_assert(size == 51); + test_assert(memcmp(data, "EFGHIJKLMNOPQRSTUVWXY" + "!\"#$%&'()*+,-./01234567890:;<=", 51) == 0); + test_assert(i_stream_read(input) == 0); + + /* partially skip */ + i_stream_skip(input, 12); + + /* forth stream */ + i_stream_chain_append(chain, test_istreams[3]); + tmp_istream = test_istreams[3]; i_stream_unref(&tmp_istream); + test_assert(i_stream_read_data(input, &data, &size, 40) == 1); + test_assert(size == 64); + test_assert(memcmp(data, "QRSTUVWXY" + "!\"#$%&'()*+,-./01234567890:;<=" + "z1y2x3w4v5u6t7s8r9q0p.o,n", 64) == 0); + + /* partially skip */ + i_stream_skip(input, 6); + + /* fifth stream */ + i_stream_chain_append(chain, test_istreams[4]); + tmp_istream = test_istreams[4]; i_stream_unref(&tmp_istream); + test_assert(i_stream_read_data(input, &data, &size, 60) == 1); + test_assert(size == 78); + test_assert(memcmp(data, "WXY" + "!\"#$%&'()*+,-./01234567890:;<=" + "z1y2x3w4v5u6t7s8r9q0p.o,n" + "aAbBcCdDeEfFgGhHiIjJ", 78) == 0); + + /* EOF */ + i_stream_chain_append_eof(chain); + test_assert(i_stream_read(input) == -1); + test_assert(input->eof && input->stream_errno == 0); + test_assert(i_stream_read_data(input, &data, &size, 78) == -1); + test_assert(size == 78); + test_assert(memcmp(data, "WXY" + "!\"#$%&'()*+,-./01234567890:;<=" + "z1y2x3w4v5u6t7s8r9q0p.o,n" + "aAbBcCdDeEfFgGhHiIjJ", 78) == 0); + + /* skip rest */ + i_stream_skip(input, 78); + + test_assert(i_stream_read(input) == -1); + test_assert(input->eof && input->stream_errno == 0); + data = i_stream_get_data(input, &size); + test_assert(size == 0); + + i_stream_unref(&input); + test_end(); +} + +void test_istream_chain(void) +{ + test_istream_chain_basic(); + test_istream_chain_early_end(); + test_istream_chain_accumulate(); +} diff --git a/src/lib/test-istream-concat.c b/src/lib/test-istream-concat.c new file mode 100644 index 0000000..5e80cfa --- /dev/null +++ b/src/lib/test-istream-concat.c @@ -0,0 +1,254 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "istream-private.h" +#include "istream-concat.h" + +#include <fcntl.h> +#include <unistd.h> + +#define TEST_MAX_ISTREAM_COUNT 10 +#define TEST_MAX_ISTREAM_SIZE 1024 +#define TEST_MAX_BUFFER_SIZE 128 + +static void test_istream_concat_one(unsigned int buffer_size) +{ + static const char *input_string = "xyz"; +#define STREAM_COUNT 5 +#define STREAM_BYTES 3 + struct istream *streams[STREAM_COUNT+1]; + struct istream *input; + const unsigned char *data; + size_t size; + unsigned int i, j; + + for (i = 0; i < STREAM_COUNT; i++) { + streams[i] = test_istream_create(input_string); + test_istream_set_allow_eof(streams[i], TRUE); + test_istream_set_size(streams[i], 0); + } + streams[i] = NULL; + + input = i_stream_create_concat(streams); + for (i = 0; i/STREAM_BYTES < STREAM_COUNT; i++) { + test_istream_set_size(streams[i/STREAM_BYTES], (i%STREAM_BYTES) + 1); + test_assert(i_stream_read(input) == 1); + if (i < buffer_size) { + data = i_stream_get_data(input, &size); + test_assert(size == i+1); + } else { + i_stream_skip(input, 1); + data = i_stream_get_data(input, &size); + test_assert(size == buffer_size); + } + for (j = 0; j < size; j++) { + test_assert((char)data[j] == input_string[(input->v_offset + j) % STREAM_BYTES]); + } + test_assert(i_stream_read(input) <= 0); + } + test_assert(i_stream_read(input) == -1); + i_stream_skip(input, i_stream_get_data_size(input)); + i_stream_unref(&input); + + for (i = 0; i < STREAM_COUNT; i++) { + test_assert(streams[i]->eof && streams[i]->stream_errno == 0); + i_stream_unref(&streams[i]); + } +} + +static bool test_istream_concat_random(void) +{ + struct istream **streams, *concat, **limits = NULL; + const unsigned char *data; + unsigned char *w_data; + size_t size = 0; + unsigned int i, j, offset, stream_count, data_len, simult; + + stream_count = i_rand_minmax(2, TEST_MAX_ISTREAM_COUNT + 2 - 1); + streams = t_new(struct istream *, stream_count + 1); + for (i = 0, offset = 0; i < stream_count; i++) { + data_len = i_rand_minmax(1, TEST_MAX_ISTREAM_SIZE); + w_data = t_malloc_no0(data_len); + for (j = 0; j < data_len; j++) + w_data[j] = (offset++) & 0xff; + streams[i] = test_istream_create_data(w_data, data_len); + test_istream_set_allow_eof(streams[i], TRUE); + } + streams[i] = NULL; + i_assert(offset > 0); + + concat = i_stream_create_concat(streams); + i_stream_set_max_buffer_size(concat, TEST_MAX_BUFFER_SIZE); + + simult = i_rand_limit(TEST_MAX_ISTREAM_COUNT); + if (simult > 0) { + limits = t_new(struct istream *, simult); + for (i = 0; i < simult; i++) + limits[i] = i_stream_create_limit(concat, UOFF_T_MAX); + } + + for (i = 0; i < 1000; i++) { + struct istream *input = (simult == 0) ? concat : limits[i_rand_limit(simult)]; + if (i_rand_limit(3) == 0) { + i_stream_seek(input, i_rand_limit(offset)); + } else { + ssize_t ret = i_stream_read(input); + size = i_stream_get_data_size(input); + if (ret == -2) { + test_assert(size >= TEST_MAX_BUFFER_SIZE); + } else if (input->v_offset + size != offset) { + test_assert(ret > 0); + test_assert(input->v_offset + ret <= offset); + i_stream_skip(input, i_rand_limit(ret)); + + data = i_stream_get_data(input, &size); + for (j = 0; j < size; j++) { + test_assert(data[j] == (input->v_offset + j) % 256); + } + } + } + if (test_has_failed()) + break; + } + for (i = 0; i < stream_count; i++) + i_stream_unref(&streams[i]); + for (i = 0; i < simult; i++) + i_stream_unref(&limits[i]); + i_stream_unref(&concat); + return !test_has_failed(); +} + +static void test_istream_concat_seek_end(void) +{ + test_begin("istream concat seek end"); + + struct istream *streams[] = { + test_istream_create("s1"), + test_istream_create("s2"), + NULL + }; + struct istream *input = i_stream_create_concat(streams); + i_stream_unref(&streams[0]); + i_stream_unref(&streams[1]); + + i_stream_seek(input, 4); + test_assert(i_stream_read(input) == -1); + i_stream_unref(&input); + + test_end(); +} + +static void test_istream_concat_early_end(void) +{ + struct istream *input, *streams[2]; + + test_begin("istream concat early end"); + + streams[0] = test_istream_create("stream"); + test_istream_set_size(streams[0], 3); + test_istream_set_allow_eof(streams[0], FALSE); + streams[1] = NULL; + + input = i_stream_create_concat(streams); + test_assert(i_stream_read(input) == 3); + test_istream_set_size(streams[0], 5); + test_assert(i_stream_read(input) == 2); + i_stream_skip(input, 5); + i_stream_unref(&input); + + test_assert(streams[0]->v_offset == 5); + i_stream_unref(&streams[0]); + + test_end(); +} + +static void test_istream_concat_snapshot(void) +{ + struct istream *input; + const unsigned char *data; + size_t size; + + test_begin("istream concat snapshot"); + + struct istream *test_istreams[] = { + test_istream_create("abcdefghijklmnopqrst"), + test_istream_create("ABCDEFGHIJKLMNOPQRSTUVWXY"), + test_istream_create("!\"#$%&'()*+,-./01234567890:;<="), + NULL + }; + + input = i_stream_create_concat(test_istreams); + for (unsigned int i = 0; test_istreams[i] != NULL; i++) { + struct istream *tmp_istream = test_istreams[i]; + i_stream_unref(&tmp_istream); + } + + test_istream_set_size(test_istreams[0], 20); + test_istream_set_size(test_istreams[1], 0); + test_istream_set_size(test_istreams[2], 0); + + /* first stream */ + test_istream_set_allow_eof(test_istreams[0], FALSE); + test_assert(i_stream_read_data(input, &data, &size, 0) == 1); + test_assert(size == 20); + test_assert(memcmp(data, "abcdefghijklmnopqrst", 20) == 0); + + /* partially skip */ + i_stream_skip(input, 12); + + /* second stream */ + test_assert(i_stream_read_data(input, &data, &size, 10) == 0); + test_assert(size == 8); + test_istream_set_allow_eof(test_istreams[0], TRUE); + test_istream_set_size(test_istreams[0], 0); + test_assert(i_stream_read_data(input, &data, &size, 10) == 0); + test_assert(size == 8); + test_istream_set_size(test_istreams[1], 10); + test_assert(i_stream_read_data(input, &data, &size, 10) == 1); + test_assert(size == 18); + test_istream_set_allow_eof(test_istreams[1], FALSE); + test_assert(i_stream_read(input) == 0); + test_istream_set_size(test_istreams[1], 25); + test_istream_set_allow_eof(test_istreams[1], TRUE); + test_assert(i_stream_read_data(input, &data, &size, 30) == 1); + test_assert(size == 33); + test_assert(memcmp(data, "mnopqrst" + "ABCDEFGHIJKLMNOPQRSTUVWXY", 33) == 0); + + /* partially skip */ + i_stream_skip(input, 12); + + /* third stream */ + test_istream_set_size(test_istreams[2], 0); + test_assert(i_stream_read(input) == 0); + test_istream_set_size(test_istreams[2], 30); + test_assert(i_stream_read_data(input, &data, &size, 25) == 1); + test_assert(size == 51); + test_assert(memcmp(data, "EFGHIJKLMNOPQRSTUVWXY" + "!\"#$%&'()*+,-./01234567890:;<=", 51) == 0); + + i_stream_unref(&input); + test_end(); +} + +void test_istream_concat(void) +{ + unsigned int i; + + test_begin("istream concat"); + for (i = 1; i < STREAM_BYTES*STREAM_COUNT; i++) { + test_istream_concat_one(i); + } + test_end(); + + test_begin("istream concat random"); + for (i = 0; i < 100; i++) T_BEGIN { + if(!test_istream_concat_random()) + i = 101; /* don't break a T_BEGIN */ + } T_END; + test_end(); + + test_istream_concat_seek_end(); + test_istream_concat_early_end(); + test_istream_concat_snapshot(); +} diff --git a/src/lib/test-istream-crlf.c b/src/lib/test-istream-crlf.c new file mode 100644 index 0000000..5239e36 --- /dev/null +++ b/src/lib/test-istream-crlf.c @@ -0,0 +1,115 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "istream-private.h" +#include "istream-crlf.h" + +static void test_istream_crlf_input(const char *input) +{ + string_t *output; + const unsigned char *data; + size_t size = 0; + ssize_t ret1, ret2; + unsigned int i, j, pos, input_len = strlen(input); + struct istream *istream, *crlf_istream; + + output = t_str_new(256); + + for (j = 0; j < 4; j++) { + istream = i_stream_create_from_data(input, input_len); + str_truncate(output, 0); + if (j%2 == 0) { + /* drop CRs */ + crlf_istream = i_stream_create_lf(istream); + for (i = 0; i < input_len; i++) { + if (input[i] == '\r' && + (i == input_len-1 || input[i+1] == '\n')) + ; + else + str_append_c(output, input[i]); + } + } else { + /* add missing CRs */ + crlf_istream = i_stream_create_crlf(istream); + for (i = 0; i < input_len; i++) { + if (input[i] == '\n' && + (i == 0 || input[i-1] != '\r')) + str_append_c(output, '\r'); + str_append_c(output, input[i]); + } + } + + pos = 0; + for (i = 1; i <= input_len; i++) { + if (j >= 2) { + i_stream_unref(&istream); + i_stream_unref(&crlf_istream); + istream = i_stream_create_from_data(input, + input_len); + crlf_istream = j%2 == 0 ? + i_stream_create_lf(istream) : + i_stream_create_crlf(istream); + pos = 0; + } + istream->real_stream->pos = i; + ret1 = i_stream_read(crlf_istream); + if (crlf_istream->real_stream->buffer_size != 0) { + /* this is pretty evil */ + crlf_istream->real_stream->buffer_size = + I_MAX(crlf_istream->real_stream->pos, i); + } + ret2 = i_stream_read(crlf_istream); + data = i_stream_get_data(crlf_istream, &size); + if (ret1 > 0 || ret2 > 0) { + ret1 = I_MAX(ret1, 0) + I_MAX(ret2, 0); + test_assert(pos + (unsigned int)ret1 == size); + pos += ret1; + } + if (size > 0) + test_assert_idx(memcmp(data, str_data(output), + size) == 0, j*10000+i); + } + test_assert_idx(size == str_len(output), j*10000+i); + i_stream_unref(&crlf_istream); + i_stream_unref(&istream); + } +} + +void test_istream_crlf(void) +{ + const char *input[] = { + "\rfoo", + "foo\nbar\r\nbaz\r\r\n", + "\r\nfoo", + "\r\r\n", + "\nfoo" + }; + unsigned int i; + + test_begin("istream crlf"); + for (i = 0; i < N_ELEMENTS(input); i++) + test_istream_crlf_input(input[i]); + test_end(); + +#define ISTREAM_CRLF_TEST_REPS 1000 + test_begin("istream crlf(random)"); + for (i = 0; i < ISTREAM_CRLF_TEST_REPS; i++) T_BEGIN { + char buf[100]; + size_t len = 0; + while (len < sizeof(buf) - 1) { + switch(i_rand_limit(16)) { + case 0: goto outahere; + case 1: buf[len] = '\r'; break; + case 2: buf[len] = '\n'; break; + default: buf[len]= '.'; break; + } + len++; + } + outahere: + buf[len] = '\0'; + if (len > 0) + test_istream_crlf_input(buf); + } T_END; + test_end(); +} diff --git a/src/lib/test-istream-failure-at.c b/src/lib/test-istream-failure-at.c new file mode 100644 index 0000000..d0313ba --- /dev/null +++ b/src/lib/test-istream-failure-at.c @@ -0,0 +1,49 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "istream.h" +#include "istream-failure-at.h" + +#define TEST_DATA_LENGTH 128 +#define TEST_ERRMSG "test-istream-failure-at error triggered" + +void test_istream_failure_at(void) +{ + struct istream *input, *data_input; + unsigned char test_data[TEST_DATA_LENGTH]; + unsigned int i; + ssize_t ret; + + test_begin("istream failure at"); + for (i = 0; i < sizeof(test_data); i++) + test_data[i] = i; + data_input = i_stream_create_from_data(test_data, sizeof(test_data)); + for (i = 0; i < TEST_DATA_LENGTH; i++) { + i_stream_seek(data_input, 0); + input = i_stream_create_failure_at(data_input, i, EIO, TEST_ERRMSG); + while ((ret = i_stream_read(input)) > 0) + i_stream_skip(input, ret); + test_assert_idx(ret == -1 && input->v_offset == i && + input->stream_errno == EIO && + strcmp(i_stream_get_error(input), TEST_ERRMSG) == 0, i); + i_stream_destroy(&input); + } + /* shouldn't fail */ + i_stream_seek(data_input, 0); + input = i_stream_create_failure_at(data_input, TEST_DATA_LENGTH, EIO, TEST_ERRMSG); + while ((ret = i_stream_read(input)) > 0) + i_stream_skip(input, ret); + test_assert(ret == -1 && input->stream_errno == 0); + i_stream_destroy(&input); + /* fail at EOF */ + i_stream_seek(data_input, 0); + input = i_stream_create_failure_at_eof(data_input, EIO, TEST_ERRMSG); + while ((ret = i_stream_read(input)) > 0) + i_stream_skip(input, ret); + test_assert_idx(ret == -1 && input->v_offset == TEST_DATA_LENGTH && + input->stream_errno == EIO && + strcmp(i_stream_get_error(input), TEST_ERRMSG) == 0, i); + i_stream_destroy(&input); + i_stream_destroy(&data_input); + test_end(); +} diff --git a/src/lib/test-istream-jsonstr.c b/src/lib/test-istream-jsonstr.c new file mode 100644 index 0000000..626994d --- /dev/null +++ b/src/lib/test-istream-jsonstr.c @@ -0,0 +1,139 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "istream-private.h" +#include "istream-jsonstr.h" + +static const struct { + const char *input; + const char *output; + int stream_errno; +} tests[] = { + { "foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\\uffff\"", + "foo\\\"\b\f\n\r\t\001\xEF\xBF\xBF", 0 }, + { "\\ud801\\udc37\"", "\xf0\x90\x90\xb7", 0 }, /* valid codepoint */ + { "\"", "", 0 }, + { "foo\\?\"", "foo", EINVAL }, + { "foo\\?\"", "foo", EINVAL }, + { "", "", EPIPE }, + { "\\\"", "\"", EPIPE }, + { "foo", "foo", EPIPE }, + { "\\ud801", "", EPIPE }, /* high surrogate alone */ + { "\\udced\\udc37\"", "", EINVAL }, /* low surrogate before high */ + { "\\ud8011\\udc37\"", "", EINVAL }, /* has extra 1 in middle */ + { "hello \\udc37\"", "hello ", EINVAL }, /* low surrogate before high with valid prefix*/ + { "hello \\ud801", "hello ", EPIPE }, /* high surrogate alone with valid prefix */ + { "\\uabcg", "", EINVAL }, /* invalid hex value */ +}; + +static void +run_test_buffer(const char *json_input, const char *output, int stream_errno, + unsigned int skip_count) +{ + size_t json_input_len = strlen(json_input); + struct istream *input_data, *input; + const unsigned char *data; + size_t i, size; + ssize_t ret = 0; + + input_data = test_istream_create_data(json_input, json_input_len); + test_istream_set_allow_eof(input_data, FALSE); + input = i_stream_create_jsonstr(input_data); + + for (i = 1; i < json_input_len;) { + test_istream_set_size(input_data, i); + while ((ret = i_stream_read(input)) > 0) ; + if (ret == -1 && stream_errno != 0) + break; + test_assert_idx(ret == 0, i); + if (i + skip_count < json_input_len) + i += skip_count; + else + i++; + } + test_istream_set_allow_eof(input_data, TRUE); + test_istream_set_size(input_data, json_input_len); + ret = i_stream_read(input); + while (ret > 0 && stream_errno != 0) + ret = i_stream_read(input); + test_assert(ret == -1); + test_assert(input->stream_errno == stream_errno); + + if (stream_errno == 0) { + data = i_stream_get_data(input, &size); + test_assert(size == strlen(output)); + if (size > 0) + test_assert(memcmp(data, output, size) == 0); + } + i_stream_unref(&input); + i_stream_unref(&input_data); +} + +static void +run_test(const char *json_input, const char *output, int stream_errno) +{ + for (unsigned int i = 1; i <= 5; i++) + run_test_buffer(json_input, output, stream_errno, i); +} + +static void test_istream_jsonstr_autoretry(void) +{ + const char *json_input = "\\u0001\""; + const size_t json_input_len = strlen(json_input); + struct istream *input_data, *input; + + test_begin("istream-jsonstr autoretry"); + input_data = test_istream_create_data(json_input, json_input_len); + input = i_stream_create_jsonstr(input_data); + + test_istream_set_size(input_data, 2); + test_assert(i_stream_read(input_data) == 2); + test_istream_set_size(input_data, json_input_len); + test_assert(i_stream_read(input) == 1); + test_assert(i_stream_read(input) == -1); + + i_stream_unref(&input); + i_stream_unref(&input_data); + test_end(); +} + +static void test_istream_jsonstr_partial(void) +{ + size_t len = 0; + const char *json_input = "hello\\u0060x\""; + const char *output = "hello`x"; + const size_t json_input_len = strlen(json_input); + struct istream *input_data, *input; + + test_begin("istream-jsonstr partial"); + + input_data = test_istream_create_data(json_input, json_input_len); + input = i_stream_create_jsonstr(input_data); + test_istream_set_size(input_data, 9); + test_assert(i_stream_read(input) == 5); + test_istream_set_size(input_data, json_input_len); + test_assert(i_stream_read(input) == 2); + test_assert(i_stream_read(input) == -1); + + test_assert(memcmp(i_stream_get_data(input, &len), output, I_MIN(len, strlen(output))) == 0 && + len == strlen(output)); + + i_stream_unref(&input); + i_stream_unref(&input_data); + + test_end(); +} + +void test_istream_jsonstr(void) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(tests); i++) { + test_begin(t_strdup_printf("istream-jsonstr %u", i+1)); + run_test(tests[i].input, tests[i].output, tests[i].stream_errno); + test_end(); + } + test_istream_jsonstr_autoretry(); + test_istream_jsonstr_partial(); +} diff --git a/src/lib/test-istream-multiplex.c b/src/lib/test-istream-multiplex.c new file mode 100644 index 0000000..185c271 --- /dev/null +++ b/src/lib/test-istream-multiplex.c @@ -0,0 +1,372 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "ioloop.h" +#include "str.h" +#include "crc32.h" +#include "randgen.h" +#include "istream-private.h" +#include "istream-multiplex.h" +#include "ostream.h" +#include <unistd.h> + +static void test_istream_multiplex_simple(void) +{ + test_begin("istream multiplex (simple)"); + + static const char data[] = "\x00\x00\x00\x00\x06Hello\x00" + "\x01\x00\x00\x00\x03Wor" + "\x00\x00\x00\x00\x00" + "\x01\x00\x00\x00\x03ld\x00"; + static const size_t data_len = sizeof(data)-1; + struct istream *input = test_istream_create_data(data, data_len); + size_t siz; + + struct istream *chan0 = i_stream_create_multiplex(input, SIZE_MAX); + struct istream *chan1 = i_stream_multiplex_add_channel(chan0, 1); + + /* nothing to read until the first byte */ + for (size_t i = 0; i <= 1+4; i++) { + test_istream_set_size(input, i); + test_assert(i_stream_read(chan0) == 0); + test_assert(i_stream_read(chan1) == 0); + } + + /* partial read of the first packet */ + size_t input_max = 1+4+3; + test_istream_set_size(input, input_max); + test_assert(i_stream_read(chan0) == 3); + test_assert(memcmp(i_stream_get_data(chan0, &siz), "Hel", 3) == 0 && + siz == 3); + test_assert(i_stream_read(chan1) == 0); + + /* read the rest of the first packet and the second packet. + read chan1 before chan0 to see that it works. */ + input_max += 3 + 1+4+3; + test_istream_set_size(input, input_max); + test_assert(i_stream_read(chan1) == 3); + test_assert(i_stream_read(chan0) == 3); + test_assert(memcmp(i_stream_get_data(chan0, &siz), "Hello\0", 6) == 0 && + siz == 6); + test_assert(memcmp(i_stream_get_data(chan1, &siz), "Wor", 3) == 0 && + siz == 3); + + /* 0-sized packet is ignored */ + input_max += 1+4; + test_istream_set_size(input, input_max); + test_assert(i_stream_read(chan0) == 0); + test_assert(i_stream_read(chan1) == 0); + + /* read the final packet */ + input_max += 1+4+3; + i_assert(input_max == data_len); + test_istream_set_size(input, input_max); + test_assert(i_stream_read(chan0) == 0); + test_assert(i_stream_read(chan1) == 3); + + /* we should have the final data in all channels now */ + test_assert(memcmp(i_stream_get_data(chan0, &siz), "Hello\0", 6) == 0 && + siz == 6); + test_assert(memcmp(i_stream_get_data(chan1, &siz), "World\0", 6) == 0 && + siz == 6); + + /* all channels should return EOF */ + test_assert(i_stream_read(chan0) == -1 && chan0->stream_errno == 0); + i_stream_unref(&chan0); + + test_assert(i_stream_read(chan1) == -1 && chan1->stream_errno == 0); + i_stream_unref(&chan1); + + i_stream_unref(&input); + + test_end(); +} + +static void test_istream_multiplex_maxbuf(void) +{ + test_begin("istream multiplex (maxbuf)"); + + static const char data[] = "\x00\x00\x00\x00\x06Hello\x00" + "\x01\x00\x00\x00\x06World\x00"; + static const size_t data_len = sizeof(data)-1; + struct istream *input = test_istream_create_data(data, data_len); + size_t siz; + + struct istream *chan0 = i_stream_create_multiplex(input, 5); + struct istream *chan1 = i_stream_multiplex_add_channel(chan0, 1); + + /* we get data for channel 0 and congest */ + test_assert(i_stream_read(chan1) == 0); + /* we read data for channel 0 */ + test_assert(i_stream_read(chan0) == 5); + /* and now it's congested */ + test_assert(i_stream_read(chan0) == -2); + test_assert(memcmp(i_stream_get_data(chan0, &siz), "Hello", 5) == 0 && + siz == 5); + /* consume data */ + i_stream_skip(chan0, 5); + /* we read data for channel 1 */ + test_assert(i_stream_read(chan1) == 5); + test_assert(memcmp(i_stream_get_data(chan1, &siz), "World", 5) == 0 && + siz == 5); + /* consume data */ + i_stream_skip(chan1, 5); + /* read last byte */ + test_assert(i_stream_read(chan0) == 1); + /* now we get byte for channel 1 */ + test_assert(i_stream_read(chan0) == 0); + /* now we read byte for channel 1 */ + test_assert(i_stream_read(chan1) == 1); + /* and everything should return EOF now */ + test_assert(i_stream_read(chan1) == -1); + test_assert(i_stream_read(chan0) == -1); + + i_stream_unref(&chan0); + i_stream_unref(&chan1); + + i_stream_unref(&input); + + test_end(); +} + +static void test_istream_multiplex_random(void) +{ + const unsigned int max_channel = 6; + const unsigned int packets_count = 30; + + test_begin("istream multiplex (random)"); + + unsigned int i; + uoff_t bytes_written = 0, bytes_read = 0; + buffer_t *buf = buffer_create_dynamic(default_pool, 10240); + uint32_t input_crc[max_channel]; + uint32_t output_crc[max_channel]; + memset(input_crc, 0, sizeof(input_crc)); + memset(output_crc, 0, sizeof(output_crc)); + + for (i = 0; i < packets_count; i++) { + unsigned int len = i_rand_limit(1024+1); + unsigned char packet_data[len]; + uint32_t len_be = cpu32_to_be(len); + unsigned int channel = i_rand_limit(max_channel); + + random_fill(packet_data, len); + input_crc[channel] = + crc32_data_more(input_crc[channel], packet_data, len); + + buffer_append_c(buf, channel); + buffer_append(buf, &len_be, sizeof(len_be)); + buffer_append(buf, packet_data, len); + bytes_written += len; + } + + struct istream *input = test_istream_create_data(buf->data, buf->used); + struct istream *chan[max_channel]; + chan[0] = i_stream_create_multiplex(input, 1024/4); + for (i = 1; i < max_channel; i++) + chan[i] = i_stream_multiplex_add_channel(chan[0], i); + + test_istream_set_size(input, 0); + + /* read from each stream, 1 byte at a time */ + size_t input_size = 0; + int max_ret = -3; + unsigned int read_max_channel = max_channel/2; + bool something_read = FALSE; + for (i = 0;;) { + ssize_t ret = i_stream_read(chan[i]); + if (max_ret < ret) + max_ret = ret; + if (ret > 0) { + size_t size; + const unsigned char *data = + i_stream_get_data(chan[i], &size); + + output_crc[i] = crc32_data_more(output_crc[i], data, size); + bytes_read += size; + + test_assert((size_t)ret == size); + i_stream_skip(chan[i], size); + something_read = TRUE; + } + if (++i < read_max_channel) + ; + else if (max_ret == 0 && !something_read && + read_max_channel < max_channel) { + read_max_channel++; + } else { + if (max_ret <= -1) { + test_assert(max_ret == -1); + break; + } + if (max_ret == 0) + test_istream_set_size(input, ++input_size); + i = 0; + max_ret = -3; + something_read = FALSE; + read_max_channel = max_channel/2; + } + } + test_assert(bytes_read == bytes_written); + for (i = 0; i < max_channel; i++) { + test_assert_idx(input_crc[i] == output_crc[i], i); + test_assert_idx(i_stream_read(chan[i]) == -1 && + chan[i]->stream_errno == 0, i); + i_stream_unref(&chan[i]); + } + i_stream_unref(&input); + buffer_free(&buf); + test_end(); +} + +static unsigned int channel_counter[2] = {0, 0}; + +static const char *msgs[] = { + "", + "a", + "bb", + "ccc", + "dddd", + "eeeee", + "ffffff" +}; + +static void test_istream_multiplex_stream_read(struct istream *channel) +{ + uint8_t cid = i_stream_multiplex_get_channel_id(channel); + const char *line; + size_t siz; + + if (i_stream_read(channel) < 0) + return; + + while((line = i_stream_next_line(channel)) != NULL) { + siz = strlen(line); + test_assert_idx(siz > 0 && siz < N_ELEMENTS(msgs), + channel_counter[cid]); + if (siz > 0 && siz < N_ELEMENTS(msgs)) { + test_assert_idx(strcmp(line, msgs[siz]) == 0, + channel_counter[cid]); + } + channel_counter[cid]++; + } + + if (channel_counter[0] > 100 && channel_counter[1] > 100) + io_loop_stop(current_ioloop); +} + +static void test_send_msg(struct ostream *os, uint8_t cid, const char *msg) +{ + uint32_t len = cpu32_to_be(strlen(msg) + 1); + const struct const_iovec iov[] = { + { &cid, sizeof(cid) }, + { &len, sizeof(len) }, + { msg, strlen(msg) }, + { "\n", 1 } /* newline added for i_stream_next_line */ + }; + o_stream_nsendv(os, iov, N_ELEMENTS(iov)); +} + +static void test_istream_multiplex_stream_write(struct ostream *channel) +{ + size_t rounds = i_rand_limit(10); + for(size_t i = 0; i < rounds; i++) { + uint8_t cid = i_rand_limit(2); + test_send_msg(channel, cid, + msgs[1 + i_rand_limit(N_ELEMENTS(msgs) - 1)]); + } +} + +static void test_istream_multiplex_stream(void) +{ + test_begin("istream multiplex (stream)"); + struct ioloop *ioloop = io_loop_create(); + io_loop_set_current(ioloop); + + int fds[2]; + test_assert(pipe(fds) == 0); + fd_set_nonblock(fds[0], TRUE); + fd_set_nonblock(fds[1], TRUE); + struct ostream *os = o_stream_create_fd(fds[1], SIZE_MAX); + struct istream *is = i_stream_create_fd(fds[0], 10 + i_rand_limit(10)); + + struct istream *chan0 = i_stream_create_multiplex(is, SIZE_MAX); + struct istream *chan1 = i_stream_multiplex_add_channel(chan0, 1); + + struct io *io0 = + io_add_istream(chan0, test_istream_multiplex_stream_read, chan0); + struct io *io1 = + io_add_istream(chan1, test_istream_multiplex_stream_read, chan1); + struct io *io2 = + io_add(fds[1], IO_WRITE, test_istream_multiplex_stream_write, os); + + io_loop_run(current_ioloop); + + io_remove(&io0); + io_remove(&io1); + io_remove(&io2); + + i_stream_unref(&chan1); + i_stream_unref(&chan0); + i_stream_unref(&is); + + test_assert(o_stream_finish(os) > 0); + o_stream_unref(&os); + + io_loop_destroy(&ioloop); + + i_close_fd(&fds[0]); + i_close_fd(&fds[1]); + + test_end(); +} + +static void test_istream_multiplex_close_channel(void) +{ + test_begin("istream multiplex (close channel)"); + static const char *data = "\x00\x00\x00\x00\x06Hello\x00" + "\x01\x00\x00\x00\x06World\x00"; + static const size_t data_len = 22; + struct istream *input = test_istream_create_data(data, data_len); + size_t siz; + + struct istream *chan0 = i_stream_create_multiplex(input, SIZE_MAX); + struct istream *chan1 = i_stream_multiplex_add_channel(chan0, 1); + + i_stream_unref(&chan1); + + test_assert(i_stream_read(chan0) == 6); + + test_assert(memcmp(i_stream_get_data(chan0, &siz), "Hello\0", 6) == 0 && + siz == 6); + + i_stream_unref(&chan0); + i_stream_unref(&input); + + input = test_istream_create_data(data, data_len); + chan0 = i_stream_create_multiplex(input, SIZE_MAX); + chan1 = i_stream_multiplex_add_channel(chan0, 1); + + /* this is needed to populate chan1 data */ + (void)i_stream_read(chan0); + i_stream_unref(&chan0); + + test_assert(i_stream_read(chan1) == 6); + + test_assert(memcmp(i_stream_get_data(chan1, &siz), "World\0", 6) == 0 && + siz == 6); + + i_stream_unref(&chan1); + i_stream_unref(&input); + + test_end(); +} + +void test_istream_multiplex(void) +{ + test_istream_multiplex_simple(); + test_istream_multiplex_maxbuf(); + test_istream_multiplex_random(); + test_istream_multiplex_stream(); + test_istream_multiplex_close_channel(); +} diff --git a/src/lib/test-istream-seekable.c b/src/lib/test-istream-seekable.c new file mode 100644 index 0000000..3373f52 --- /dev/null +++ b/src/lib/test-istream-seekable.c @@ -0,0 +1,290 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "sha2.h" +#include "istream-private.h" +#include "istream-sized.h" +#include "istream-hash.h" +#include "istream-seekable.h" + +#include <fcntl.h> +#include <unistd.h> + +static int fd_callback_fd = -1; + +static int fd_callback(const char **path_r, void *context ATTR_UNUSED) +{ + int fd; + + *path_r = "test-lib.tmp"; + fd = open(*path_r, O_RDWR | O_CREAT | O_TRUNC, 0600); + if (fd == -1) + i_error("creat(%s) failed: %m", *path_r); + else + i_unlink(*path_r); + fd_callback_fd = fd; + return fd; +} + +static void test_istream_seekable_one(unsigned int buffer_size) +{ + static const char *input_string = "xyz"; +#define STREAM_COUNT 5 +#define STREAM_BYTES 3 + struct istream *streams[STREAM_COUNT+1]; + struct istream *input; + const unsigned char *data; + size_t size; + unsigned int i, j; + + for (i = 0; i < STREAM_COUNT; i++) { + streams[i] = test_istream_create(input_string); + streams[i]->seekable = FALSE; + test_istream_set_allow_eof(streams[i], TRUE); + test_istream_set_size(streams[i], 0); + } + streams[i] = NULL; + + input = i_stream_create_seekable(streams, buffer_size, fd_callback, NULL); + test_assert(!input->blocking); + for (i = 0; i/STREAM_BYTES < STREAM_COUNT; i++) { + test_istream_set_size(streams[i/STREAM_BYTES], (i%STREAM_BYTES) + 1); + if (i < buffer_size) { + test_assert(i_stream_read(input) == 1); + data = i_stream_get_data(input, &size); + test_assert(size == i+1); + } else { + test_assert(i_stream_read(input) == -2); + i_stream_skip(input, 1); + test_assert(i_stream_read(input) == 1); + data = i_stream_get_data(input, &size); + test_assert(size == buffer_size); + } + for (j = 0; j < size; j++) { + test_assert((char)data[j] == input_string[(input->v_offset + j) % STREAM_BYTES]); + } + } + test_assert(!input->blocking); + test_assert(i_stream_read(input) == -1); + test_assert(input->blocking); + for (i = 0; i < STREAM_COUNT; i++) { + test_assert(streams[i]->eof && streams[i]->stream_errno == 0); + i_stream_unref(&streams[i]); + } + i_stream_unref(&input); +} + +static void test_istream_seekable_random(void) +{ + struct istream **streams, *input; + const unsigned char *data; + unsigned char *w_data; + size_t size; + unsigned int i, j, offset, stream_count, data_len, buffer_size; + + stream_count = i_rand_minmax(2, 10 + 2 - 1); + streams = t_new(struct istream *, stream_count + 1); + for (i = 0, offset = 0; i < stream_count; i++) { + data_len = i_rand_minmax(1, 100); + w_data = t_malloc_no0(data_len); + for (j = 0; j < data_len; j++) + w_data[j] = (offset++) & 0xff; + streams[i] = test_istream_create_data(w_data, data_len); + streams[i]->seekable = FALSE; + test_istream_set_allow_eof(streams[i], TRUE); + } + streams[i] = NULL; + i_assert(offset > 0); + + buffer_size = i_rand_minmax(1, 100); size = 0; + input = i_stream_create_seekable(streams, buffer_size, fd_callback, NULL); + test_assert(!input->blocking); + + /* first read it through */ + while (i_stream_read(input) > 0) { + size = i_stream_get_data_size(input); + i_stream_skip(input, size); + } + test_assert(input->blocking); + + i_stream_seek(input, 0); + for (i = 0; i < 100; i++) { + if (i_rand_limit(3) == 0) { + i_stream_seek(input, i_rand_limit(offset)); + } else { + ssize_t ret = i_stream_read(input); + if (input->v_offset + size == offset) + test_assert(ret < 0); + else if (ret == -2) { + test_assert(size == buffer_size); + } else { + test_assert(ret > 0); + test_assert(input->v_offset + ret <= offset); + i_stream_skip(input, i_rand_limit(ret + 1)); + + data = i_stream_get_data(input, &size); + for (j = 0; j < size; j++) { + test_assert(data[j] == (input->v_offset + j) % 256); + } + } + } + size = i_stream_get_data_size(input); + } + for (i = 0; i < stream_count; i++) { + test_assert(streams[i]->eof && streams[i]->stream_errno == 0); + i_stream_unref(&streams[i]); + } + i_stream_unref(&input); +} + +static void test_istream_seekable_eof(void) +{ + static const char *in_str = "foo"; + unsigned int in_str_len = strlen(in_str); + struct istream *streams[2], *input; + const unsigned char *data; + size_t size; + + test_begin("istream seekable eof"); + + streams[0] = i_stream_create_from_data(in_str, in_str_len); + streams[0]->seekable = FALSE; + streams[1] = NULL; + + input = i_stream_create_seekable(streams, in_str_len, fd_callback, NULL); + + test_assert(i_stream_read(input) == (ssize_t)in_str_len); + data = i_stream_get_data(input, &size); + test_assert(size == in_str_len); + test_assert(memcmp(data, in_str, in_str_len) == 0); + + test_assert(i_stream_read(input) == -1); + data = i_stream_get_data(input, &size); + test_assert(size == in_str_len); + test_assert(memcmp(data, in_str, in_str_len) == 0); + i_stream_seek(input, size); + + i_stream_unref(&input); + + test_assert(streams[0]->v_offset == in_str_len); + test_assert(streams[0]->eof); + i_stream_unref(&streams[0]); + test_end(); +} + +static void test_istream_seekable_early_end(void) +{ + struct istream *input, *streams[2]; + + test_begin("istream seekable early end"); + + streams[0] = test_istream_create("stream"); + test_istream_set_size(streams[0], 3); + test_istream_set_allow_eof(streams[0], FALSE); + streams[0]->seekable = FALSE; + streams[1] = NULL; + + input = i_stream_create_seekable(streams, 1000, fd_callback, NULL); + test_assert(i_stream_read(input) == 3); + test_istream_set_size(streams[0], 5); + test_assert(i_stream_read(input) == 2); + i_stream_skip(input, 5); + i_stream_unref(&input); + + test_assert(streams[0]->v_offset == 5); + i_stream_unref(&streams[0]); + + test_end(); +} + +static void test_istream_seekable_invalid_read(void) +{ + test_begin("istream seekable + other streams causing invalid read"); + struct sha256_ctx hash_ctx; + sha256_init(&hash_ctx); + struct istream *str_input = test_istream_create("123456"); + str_input->seekable = FALSE; + struct istream *seek_inputs[] = { str_input, NULL }; + struct istream *seek_input = i_stream_create_seekable(seek_inputs, 3, fd_callback, NULL); + struct istream *sized_input = i_stream_create_sized(seek_input, 3); + struct istream *input = i_stream_create_hash(sized_input, &hash_method_sha256, &hash_ctx); + test_assert(i_stream_read(input) == 3); + test_assert(i_stream_read(input) == -2); + i_stream_skip(input, 3); + test_assert(i_stream_read(input) == -1); + i_stream_unref(&input); + i_stream_unref(&sized_input); + i_stream_unref(&seek_input); + i_stream_unref(&str_input); + test_end(); +} + +static void test_istream_seekable_get_size(void) +{ + test_begin("istream seekable get size"); + struct istream *str_input = test_istream_create("123456"); + str_input->seekable = FALSE; + struct istream *seek_inputs[] = { str_input, NULL }; + struct istream *input = i_stream_create_seekable(seek_inputs, 32, fd_callback, NULL); + uoff_t size; + test_assert(i_stream_read(input) == 6); + test_assert(i_stream_read(input) == -1); + test_assert(i_stream_get_size(input, TRUE, &size) == 1 && + size == 6); + i_stream_unref(&input); + i_stream_unref(&str_input); + test_end(); +} + +static void test_istream_seekable_failed_writes(void) +{ + struct istream *input, *streams[2]; + + test_begin("istream seekable failed write"); + streams[0] = test_istream_create("stream"); + test_istream_set_size(streams[0], 3); + test_istream_set_allow_eof(streams[0], FALSE); + streams[0]->seekable = FALSE; + streams[1] = NULL; + + input = i_stream_create_seekable(streams, 2, fd_callback, NULL); + i_stream_set_name(input, "test seekable"); + test_assert(i_stream_read(input) == 2); + i_stream_skip(input, 2); + test_assert(i_stream_read(input) == 1); + i_close_fd(&fd_callback_fd); + test_istream_set_size(streams[0], 5); + + test_expect_error_string("istream-seekable: write_full(test-lib.tmp) failed: Bad file descriptor"); + test_assert(i_stream_read(input) == -1); + test_expect_no_more_errors(); + + test_expect_error_string("file_istream.close((seekable temp-istream for: test seekable)) failed: Bad file descriptor"); + i_stream_unref(&input); + test_expect_no_more_errors(); + + i_stream_unref(&streams[0]); + test_end(); +} + +void test_istream_seekable(void) +{ + unsigned int i; + + test_begin("istream seekable"); + for (i = 1; i <= STREAM_BYTES*STREAM_COUNT; i++) + test_istream_seekable_one(i); + test_end(); + + test_begin("istream seekable random"); + for (i = 0; i < 100; i++) T_BEGIN { + test_istream_seekable_random(); + } T_END; + test_end(); + + test_istream_seekable_eof(); + test_istream_seekable_early_end(); + test_istream_seekable_invalid_read(); + test_istream_seekable_get_size(); + test_istream_seekable_failed_writes(); +} diff --git a/src/lib/test-istream-sized.c b/src/lib/test-istream-sized.c new file mode 100644 index 0000000..82fa663 --- /dev/null +++ b/src/lib/test-istream-sized.c @@ -0,0 +1,111 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "istream.h" +#include "istream-sized.h" + +static const struct { + const char *input; + uoff_t size; + int stream_errno; +} tests[] = { + { "", 0, 0 }, + { "", 1, EPIPE }, + { "a", 1, 0 }, + { "ab", 1, EINVAL }, + { "ab", 0, EINVAL }, + { "ab", UOFF_T_MAX, EPIPE }, +}; + +static void +run_test(const char *sized_input, uoff_t sized_size, int stream_errno) +{ + unsigned int sized_input_len = strlen(sized_input); + struct istream *input_data, *input; + const unsigned char *data; + size_t i, size; + int ret = 0; + + input_data = test_istream_create_data(sized_input, sized_input_len); + test_istream_set_allow_eof(input_data, FALSE); + input = i_stream_create_sized(input_data, sized_size); + + for (i = 1; i < sized_input_len; i++) { + test_istream_set_size(input_data, i); + while ((ret = i_stream_read(input)) > 0) ; + if (ret == -1 && stream_errno != 0) + break; + test_assert(ret == 0); + } + if (ret == 0) { + test_istream_set_allow_eof(input_data, TRUE); + test_istream_set_size(input_data, i); + while ((ret = i_stream_read(input)) > 0) ; + } + test_assert(ret == -1); + test_assert(input->stream_errno == stream_errno); + + data = i_stream_get_data(input, &size); + test_assert(size == I_MIN(sized_input_len, sized_size)); + if (size > 0) + test_assert(memcmp(data, sized_input, size) == 0); + i_stream_unref(&input); + i_stream_unref(&input_data); +} + +static void test_istream_sized_full(bool exact) +{ + const unsigned char test_data[10] = "1234567890"; + struct istream *test_input, *input; + unsigned int i, j; + int expected_errno; + + for (i = 1; i < sizeof(test_data)*2; i++) { + test_input = test_istream_create_data(test_data, sizeof(test_data)); + test_istream_set_allow_eof(test_input, FALSE); + test_istream_set_size(test_input, 0); + + if (exact) + input = i_stream_create_sized(test_input, i); + else + input = i_stream_create_min_sized(test_input, i); + for (j = 1; j <= I_MIN(i, sizeof(test_data)); j++) { + test_assert_idx(i_stream_read(input) == 0, j); + test_istream_set_size(test_input, j); + test_assert_idx(i_stream_read(input) == 1, j); + } + test_assert_idx(i_stream_read(input) == 0, i); + if (j <= sizeof(test_data)) + test_istream_set_size(test_input, j); + else + test_istream_set_allow_eof(test_input, TRUE); + test_assert_idx(i_stream_read(input) == -1 && input->eof, i); + if (i > sizeof(test_data)) + expected_errno = EPIPE; + else if (i < sizeof(test_data) && exact) + expected_errno = EINVAL; + else + expected_errno = 0; + test_assert_idx(input->stream_errno == expected_errno, i); + i_stream_unref(&input); + i_stream_unref(&test_input); + } +} + +void test_istream_sized(void) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(tests); i++) { + test_begin(t_strdup_printf("istream sized %u", i+1)); + run_test(tests[i].input, tests[i].size, tests[i].stream_errno); + test_end(); + } + test_begin("istream sized"); + test_istream_sized_full(TRUE); + test_end(); + + test_begin("istream sized min"); + test_istream_sized_full(FALSE); + test_end(); +} diff --git a/src/lib/test-istream-tee.c b/src/lib/test-istream-tee.c new file mode 100644 index 0000000..53e9a0c --- /dev/null +++ b/src/lib/test-istream-tee.c @@ -0,0 +1,139 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "istream-private.h" +#include "istream-tee.h" + + +#define TEST_BUF_SIZE I_STREAM_MIN_SIZE +#define TEST_STR_LEN (TEST_BUF_SIZE*3) +#define CHILD_COUNT 5 + +static void test_istream_tee_tailing(const char *str) +{ + struct istream *test_input, *child_input[CHILD_COUNT]; + struct tee_istream *tee; + unsigned int i, len, delta; + + test_input = test_istream_create(str); + test_istream_set_max_buffer_size(test_input, TEST_BUF_SIZE); + + test_begin("istream tee tailing"); + tee = tee_i_stream_create(test_input); + for (i = 0; i < CHILD_COUNT; i++) + child_input[i] = tee_i_stream_create_child(tee); + + test_istream_set_allow_eof(test_input, FALSE); + delta = 1; + for (len = 1; len < TEST_BUF_SIZE; len += delta) { + test_istream_set_size(test_input, len); + for (i = 0; i < CHILD_COUNT; i++) { + test_assert_idx(i_stream_read(child_input[i]) == (int)delta, len); + test_assert_idx(!tee_i_stream_child_is_waiting(child_input[i]), len); + test_assert_idx(i_stream_read(child_input[i]) == 0, len); + test_assert_idx(!tee_i_stream_child_is_waiting(child_input[i]), len); + } + delta = i_rand_limit(32); /* may stand still */ + if(delta > TEST_BUF_SIZE - len) + delta = 1; + } + + test_istream_set_size(test_input, len); + for (i = 0; i < CHILD_COUNT; i++) { + test_assert(i_stream_read(child_input[i]) == (int)delta); + test_assert(i_stream_read(child_input[i]) == -2); + test_assert(!tee_i_stream_child_is_waiting(child_input[i])); + } + + delta = 1; + while ((len += delta) <= TEST_STR_LEN) { + unsigned int lagger = i_rand_limit(CHILD_COUNT); + test_istream_set_size(test_input, len); + for (i = 0; i < CHILD_COUNT; i++) { + test_assert(i_stream_read(child_input[i]) == -2); + test_assert(!tee_i_stream_child_is_waiting(child_input[i])); + } + for (i = 0; i < CHILD_COUNT; i++) { + if (i == lagger) + continue; + i_stream_skip(child_input[i], delta); + test_assert(i_stream_read(child_input[i]) == 0); + test_assert(tee_i_stream_child_is_waiting(child_input[i])); + } + i_stream_skip(child_input[lagger], delta); + for (i = 0; i < CHILD_COUNT; i++) { + test_assert(i_stream_read(child_input[i]) == (int)delta); + test_assert(i_stream_read(child_input[i]) == -2); + test_assert(!tee_i_stream_child_is_waiting(child_input[i])); + } + delta = i_rand_minmax(1, 31); /* mustn't stand still */ + if(delta > TEST_STR_LEN - len) + delta = 1; + } + + for (i = 0; i < CHILD_COUNT-1; i++) { + i_stream_skip(child_input[i], 1); + test_assert(i_stream_read(child_input[i]) == 0); + test_assert(tee_i_stream_child_is_waiting(child_input[i])); + } + i_stream_skip(child_input[i], 1); + test_assert(i_stream_read(child_input[i]) == 0); + test_assert(!tee_i_stream_child_is_waiting(child_input[i])); + + test_istream_set_allow_eof(test_input, TRUE); + for (i = 0; i < CHILD_COUNT; i++) { + test_assert(i_stream_read(child_input[i]) == -1); + i_stream_unref(&child_input[i]); + } + i_stream_unref(&test_input); + + test_end(); +} + +static void test_istream_tee_blocks(const char *str) +{ + struct istream *test_input, *child_input[CHILD_COUNT]; + struct tee_istream *tee; + unsigned int i, j; + + test_input = test_istream_create(str); + test_istream_set_max_buffer_size(test_input, TEST_BUF_SIZE); + + test_begin("istream tee blocks"); + tee = tee_i_stream_create(test_input); + for (i = 0; i < CHILD_COUNT; i++) + child_input[i] = tee_i_stream_create_child(tee); + + test_istream_set_allow_eof(test_input, FALSE); + for (j = 1; j <= 3; j++) { + test_istream_set_size(test_input, TEST_BUF_SIZE*j); + for (i = 0; i < CHILD_COUNT; i++) { + test_assert(i_stream_read(child_input[i]) == TEST_BUF_SIZE); + i_stream_skip(child_input[i], TEST_BUF_SIZE); + } + } + test_istream_set_allow_eof(test_input, TRUE); + for (i = 0; i < CHILD_COUNT; i++) { + test_assert(i_stream_read(child_input[i]) == -1); + i_stream_unref(&child_input[i]); + } + i_stream_unref(&test_input); + + test_end(); +} + +void test_istream_tee(void) +{ + string_t *str; + unsigned int i; + + str = str_new(default_pool, TEST_STR_LEN); + for (i = 0; i < TEST_STR_LEN; i++) + str_append_c(str, 'a' + i%26); + + test_istream_tee_tailing(str_c(str)); + test_istream_tee_blocks(str_c(str)); + + str_free(&str); +} diff --git a/src/lib/test-istream-try.c b/src/lib/test-istream-try.c new file mode 100644 index 0000000..888e00f --- /dev/null +++ b/src/lib/test-istream-try.c @@ -0,0 +1,195 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "istream-private.h" +#include "istream-base64.h" +#include "istream-try.h" + +#define MIN_FULL_SIZE 1 + +static void test_istream_try_normal(void) +{ + bool finished = FALSE; + + test_begin("istream try"); + for (unsigned int test = 0; test <= 10; test++) { + struct istream *test_inputs[3], *try_input; + + test_inputs[0] = test_istream_create("1"); + test_inputs[1] = test_istream_create("2"); + test_inputs[2] = NULL; + test_istream_set_size(test_inputs[0], 0); + test_istream_set_size(test_inputs[1], 0); + try_input = istream_try_create(test_inputs, MIN_FULL_SIZE); + + /* nonblocking read */ + test_assert_idx(i_stream_read(try_input) == 0, test); + + switch (test) { + case 0: + /* stream 0 is available */ + test_istream_set_size(test_inputs[0], 1); + test_assert_idx(i_stream_read(try_input) == 1, test); + test_assert_idx(i_stream_get_data_size(test_inputs[0]) == 1, test); + test_assert_idx(i_stream_get_data_size(test_inputs[1]) == 0, test); + break; + case 1: + /* stream 1 is available, but not used before 0 */ + test_istream_set_size(test_inputs[1], 1); + test_assert_idx(i_stream_read(try_input) == 0, test); + test_assert_idx(i_stream_get_data_size(test_inputs[0]) == 0, test); + test_assert_idx(i_stream_get_data_size(test_inputs[1]) == 0, test); + /* continue failing stream 0 -> 1 is available */ + test_inputs[0]->stream_errno = EINVAL; + test_assert_idx(i_stream_read(try_input) == 1, test); + test_assert_idx(i_stream_get_data_size(test_inputs[0]) == 0, test); + test_assert_idx(i_stream_get_data_size(test_inputs[1]) == 1, test); + break; + case 2: + /* both streams are available - stream 0 is read */ + test_istream_set_size(test_inputs[0], 1); + test_istream_set_size(test_inputs[1], 1); + test_assert_idx(i_stream_read(try_input) == 1, test); + test_assert_idx(i_stream_get_data_size(test_inputs[0]) == 1, test); + test_assert_idx(i_stream_get_data_size(test_inputs[1]) == 0, test); + break; + case 3: + /* stream 0 fails */ + test_inputs[0]->stream_errno = EINVAL; + test_assert_idx(i_stream_read(try_input) == 0, test); + test_assert_idx(i_stream_get_data_size(test_inputs[0]) == 0, test); + test_assert_idx(i_stream_get_data_size(test_inputs[1]) == 0, test); + /* continue making stream 1 available */ + test_istream_set_size(test_inputs[1], 1); + test_assert_idx(i_stream_read(try_input) == 1, test); + test_assert_idx(i_stream_get_data_size(test_inputs[0]) == 0, test); + test_assert_idx(i_stream_get_data_size(test_inputs[1]) == 1, test); + break; + case 4: + /* stream 1 fails */ + test_inputs[1]->stream_errno = EINVAL; + test_assert_idx(i_stream_read(try_input) == 0, test); + test_assert_idx(i_stream_get_data_size(test_inputs[0]) == 0, test); + test_assert_idx(i_stream_get_data_size(test_inputs[1]) == 0, test); + break; + case 5: + /* stream 0 fails, stream 1 is available */ + test_inputs[0]->stream_errno = EINVAL; + test_istream_set_size(test_inputs[1], 1); + test_assert_idx(i_stream_read(try_input) == 1, test); + test_assert_idx(i_stream_get_data_size(test_inputs[0]) == 0, test); + test_assert_idx(i_stream_get_data_size(test_inputs[1]) == 1, test); + break; + case 6: + /* stream 0 is available, stream 1 fails */ + test_inputs[1]->stream_errno = EINVAL; + test_istream_set_size(test_inputs[0], 1); + test_assert_idx(i_stream_read(try_input) == 1, test); + test_assert_idx(i_stream_get_data_size(test_inputs[0]) == 1, test); + test_assert_idx(i_stream_get_data_size(test_inputs[1]) == 0, test); + break; + case 7: + /* both streams fail */ + test_inputs[0]->stream_errno = EINVAL; + test_inputs[1]->stream_errno = EINVAL; + test_assert_idx(i_stream_read(try_input) == -1, test); + test_assert_idx(try_input->stream_errno == EINVAL, test); + break; + case 8: + /* stream 0 fails with EINVAL, stream 1 with EIO */ + test_inputs[0]->stream_errno = EINVAL; + test_inputs[1]->stream_errno = EIO; + test_assert_idx(i_stream_read(try_input) == -1, test); + test_assert_idx(try_input->stream_errno == EIO, test); + break; + case 9: + /* stream 0 fails with EIO, stream 1 with EINVAL */ + test_inputs[0]->stream_errno = EIO; + test_inputs[1]->stream_errno = EINVAL; + test_assert_idx(i_stream_read(try_input) == -1, test); + test_assert_idx(try_input->stream_errno == EIO, test); + break; + case 10: + /* stream 0 fails with EIO, stream 1 would work.. */ + test_inputs[0]->stream_errno = EIO; + test_istream_set_size(test_inputs[1], 1); + test_assert_idx(i_stream_read(try_input) == -1, test); + test_assert_idx(try_input->stream_errno == EIO, test); + test_assert_idx(i_stream_get_data_size(test_inputs[1]) == 0, test); + + finished = TRUE; + break; + } + + test_assert_idx(test_inputs[0]->v_offset == 0, test); + test_assert_idx(test_inputs[1]->v_offset == 0, test); + + i_stream_unref(&test_inputs[0]); + i_stream_unref(&test_inputs[1]); + i_stream_unref(&try_input); + } + i_assert(finished); + test_end(); +} + +static void test_istream_try_empty(void) +{ + test_begin("istream try empty stream"); + struct istream *test_inputs[] = { + test_istream_create(""), + test_istream_create(""), + NULL + }; + struct istream *try_input = + istream_try_create(test_inputs, MIN_FULL_SIZE); + test_assert(i_stream_read(try_input) == -1); + test_assert(try_input->eof); + test_assert(try_input->stream_errno == 0); + i_stream_unref(&test_inputs[0]); + i_stream_unref(&test_inputs[1]); + i_stream_unref(&try_input); + test_end(); +} + +static void test_istream_try_buffer_full(void) +{ + const char *test_strings[] = { "Zm9v", "YmFy" }; + struct istream *test_inputs[3], *try_input, *input, *input2; + + test_begin("istream try buffer full"); + + for (unsigned int i = 0; i < 2; i++) { + input = test_istream_create(test_strings[i]); + test_istream_set_size(input, 1); + test_istream_set_max_buffer_size(input, 1); + input2 = i_stream_create_base64_decoder(input); + i_stream_unref(&input); + test_inputs[i] = input2; + }; + test_inputs[2] = NULL; + + try_input = istream_try_create(test_inputs, MIN_FULL_SIZE); + + test_assert(i_stream_read(try_input) == 0); + test_assert(try_input->real_stream->parent != NULL); + test_assert(i_stream_get_data_size(test_inputs[0]) == 0); + test_assert(i_stream_get_data_size(test_inputs[1]) == 0); + + test_istream_set_size(test_inputs[0], 2); + test_assert(i_stream_read(try_input) == 1); + test_assert(i_stream_get_data_size(test_inputs[0]) == 1); + test_assert(i_stream_get_data_size(test_inputs[1]) == 0); + + i_stream_unref(&test_inputs[0]); + i_stream_unref(&test_inputs[1]); + i_stream_unref(&try_input); + + test_end(); +} + +void test_istream_try(void) +{ + test_istream_try_normal(); + test_istream_try_empty(); + test_istream_try_buffer_full(); +} diff --git a/src/lib/test-istream-unix.c b/src/lib/test-istream-unix.c new file mode 100644 index 0000000..e1fb649 --- /dev/null +++ b/src/lib/test-istream-unix.c @@ -0,0 +1,187 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "net.h" +#include "fdpass.h" +#include "istream.h" +#include "istream-unix.h" + +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> + +static int send_fd, send_fd2; + +static void write_one(int fd) +{ + if (write(fd, "1", 1) < 0) + i_fatal("write() failed: %m"); +} + +static void read_one(int fd) +{ + char buf; + + if (read(fd, &buf, 1) < 0) + i_fatal("read() failed: m"); +} + +static void +test_server_read_nofd(struct istream *input, unsigned int idx) +{ + const unsigned char *data; + size_t size; + + test_assert_idx(i_stream_read_more(input, &data, &size) == 1, idx); + i_stream_skip(input, 1); + test_assert_idx(i_stream_unix_get_read_fd(input) == -1, idx); +} + +static void +test_server_read_fd(struct istream *input, int wanted_fd, unsigned int idx) +{ + struct stat st1, st2; + const unsigned char *data; + size_t size; + int recv_fd; + + test_assert_idx(i_stream_read_more(input, &data, &size) == 1, idx); + i_stream_skip(input, 1); + test_assert_idx((recv_fd = i_stream_unix_get_read_fd(input)) != -1, idx); + if (recv_fd != -1) { + if (fstat(recv_fd, &st1) < 0 || fstat(wanted_fd, &st2) < 0) + i_fatal("fstat() failed: %m"); + test_assert_idx(st1.st_ino == st2.st_ino, idx); + i_close_fd(&recv_fd); + } +} + +static void test_istream_unix_server(int fd) +{ + struct istream *input; + const unsigned char *data; + size_t size; + + input = i_stream_create_unix(fd, 1024); + /* 1) simple read */ + test_server_read_nofd(input, 1); + write_one(fd); + + /* 2) fd was sent but we won't get it */ + test_server_read_nofd(input, 2); + /* we still shouldn't have the fd */ + i_stream_set_blocking(input, FALSE); + i_stream_unix_set_read_fd(input); + test_assert(i_stream_read_more(input, &data, &size) == 0); + test_assert(i_stream_unix_get_read_fd(input) == -1); + i_stream_set_blocking(input, TRUE); + write_one(fd); + + /* 3) the previous fd should be lost now */ + test_server_read_nofd(input, 3); + write_one(fd); + + /* 4) we should get the fd now */ + test_server_read_fd(input, send_fd2, 4); + write_one(fd); + + /* 5) the previous fd shouldn't be returned anymore */ + i_stream_unix_set_read_fd(input); + test_server_read_nofd(input, 5); + write_one(fd); + + /* 6) with i_stream_unix_unset_read_fd() we shouldn't get fd anymore */ + i_stream_unix_unset_read_fd(input); + test_server_read_nofd(input, 6); + write_one(fd); + + /* 7-8) two fds were sent, but we'll get only the first one */ + i_stream_unix_set_read_fd(input); + test_server_read_fd(input, send_fd, 7); + test_server_read_nofd(input, 8); + write_one(fd); + + /* 9-10) two fds were sent, and we'll get them both */ + i_stream_unix_set_read_fd(input); + test_server_read_fd(input, send_fd, 9); + i_stream_unix_set_read_fd(input); + test_server_read_fd(input, send_fd2, 10); + write_one(fd); + + i_stream_destroy(&input); + i_close_fd(&fd); +} + +static void test_istream_unix_client(int fd) +{ + /* 1) */ + write_one(fd); + read_one(fd); + + /* 2) */ + if (fd_send(fd, send_fd, "1", 1) < 0) + i_fatal("fd_send() failed: %m"); + read_one(fd); + + /* 3) */ + write_one(fd); + read_one(fd); + + /* 4) */ + if (fd_send(fd, send_fd2, "1", 1) < 0) + i_fatal("fd_send() failed: %m"); + read_one(fd); + + /* 5) */ + write_one(fd); + read_one(fd); + + /* 6) */ + if (fd_send(fd, send_fd, "1", 1) < 0) + i_fatal("fd_send() failed: %m"); + read_one(fd); + + /* 7-8) */ + if (fd_send(fd, send_fd, "1", 1) < 0) + i_fatal("fd_send() failed: %m"); + if (fd_send(fd, send_fd2, "1", 1) < 0) + i_fatal("fd_send() failed: %m"); + read_one(fd); + + /* 9-10) */ + if (fd_send(fd, send_fd, "1", 1) < 0) + i_fatal("fd_send() failed: %m"); + if (fd_send(fd, send_fd2, "1", 1) < 0) + i_fatal("fd_send() failed: %m"); + read_one(fd); + + i_close_fd(&fd); +} + +void test_istream_unix(void) +{ + int fd[2]; + + test_begin("istream unix"); + if ((send_fd = open("/dev/null", O_RDONLY)) == -1) + i_fatal("open(/dev/null) failed: %m"); + if ((send_fd2 = open("/dev/zero", O_RDONLY)) == -1) + i_fatal("open(/dev/zero) failed: %m"); + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) + i_fatal("socketpair() failed: %m"); + switch (fork()) { + case -1: + i_fatal("fork() failed: %m"); + case 0: + i_close_fd(&fd[0]); + test_istream_unix_client(fd[1]); + test_exit(0); + default: + i_close_fd(&fd[1]); + test_istream_unix_server(fd[0]); + break; + } + i_close_fd(&send_fd); + i_close_fd(&send_fd2); + test_end(); +} diff --git a/src/lib/test-istream.c b/src/lib/test-istream.c new file mode 100644 index 0000000..cedc650 --- /dev/null +++ b/src/lib/test-istream.c @@ -0,0 +1,381 @@ +/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "istream.h" +#include "istream-crlf.h" + +static void test_istream_children(void) +{ + struct istream *parent, *child1, *child2; + const unsigned char *data; + size_t size; + + test_begin("istream children"); + + parent = test_istream_create_data("123456789", 9); + test_istream_set_max_buffer_size(parent, 3); + + child1 = i_stream_create_limit(parent, UOFF_T_MAX); + child2 = i_stream_create_limit(parent, UOFF_T_MAX); + + /* child1 read beginning */ + test_assert(i_stream_read(child1) == 3); + data = i_stream_get_data(child1, &size); + test_assert(size == 3 && memcmp(data, "123", 3) == 0); + i_stream_skip(child1, 3); + /* child1 read middle.. */ + test_assert(i_stream_read(child1) == 3); + data = i_stream_get_data(child1, &size); + test_assert(size == 3 && memcmp(data, "456", 3) == 0); + /* child2 read beginning.. */ + test_assert(i_stream_read(child2) == 3); + data = i_stream_get_data(child2, &size); + test_assert(size == 3 && memcmp(data, "123", 3) == 0); + /* child1 check middle again.. the parent has been modified, + so it can't return the original data (without some code changes). */ + test_assert(i_stream_get_data_size(child1) == 0); + i_stream_skip(child1, 3); + /* child1 read end */ + test_assert(i_stream_read(child1) == 3); + data = i_stream_get_data(child1, &size); + test_assert(size == 3 && memcmp(data, "789", 3) == 0); + i_stream_skip(child1, 3); + test_assert(i_stream_read(child1) == -1); + /* child2 check beginning again.. */ + test_assert(i_stream_get_data_size(child1) == 0); + i_stream_skip(child2, 3); + /* child2 read middle */ + test_assert(i_stream_read(child2) == 3); + data = i_stream_get_data(child2, &size); + test_assert(size == 3 && memcmp(data, "456", 3) == 0); + i_stream_skip(child2, 3); + + i_stream_destroy(&child1); + i_stream_destroy(&child2); + i_stream_destroy(&parent); + + test_end(); +} + +static void test_istream_next_line_expect(struct istream *is, const char *expect, + unsigned int i) +{ + const char *line = i_stream_next_line(is); + test_assert_strcmp_idx(line, expect, i); +} + +static void test_istream_next_line(void) +{ + /* single line cases */ +#define TEST_CASE(a, s, b) { \ + .input = (const unsigned char*)((a)), .input_len = sizeof((a)), \ + .skip = s, \ + .output = b } + const struct test_case_sl { + const unsigned char *input; + size_t input_len; + size_t skip; + const char *output; + } test_cases_sl[] = { + TEST_CASE("", 0, NULL), + TEST_CASE("a\n", 1, ""), + TEST_CASE("a\r\n", 0, "a"), + TEST_CASE("a\r\n", 1, ""), + TEST_CASE("a\r\n", 2, ""), + TEST_CASE("hello\nworld\n", 6, "world"), + TEST_CASE("hello\nworld", 6, NULL), + TEST_CASE("hello\n\n\n\n", 6, ""), + TEST_CASE("wrong\n\r\n\n", 0, "wrong"), + TEST_CASE("wrong\n\r\r\n", 6, "\r"), + TEST_CASE("wrong\n\r\r\n", 7, ""), + }; + + test_begin("i_stream_next_line"); + + for(unsigned int i = 0; i < N_ELEMENTS(test_cases_sl); i++) { + const struct test_case_sl *test_case = &test_cases_sl[i]; + struct istream *input = + i_stream_create_copy_from_data(test_case->input, test_case->input_len); + test_assert_idx(i_stream_read(input) >= 0 || + (input->stream_errno == 0 && input->eof), i); + i_stream_skip(input, test_case->skip); + test_assert_strcmp_idx(i_stream_next_line(input), test_case->output, i); + test_assert_idx(input->stream_errno == 0, i); + i_stream_unref(&input); + + input = test_istream_create_data(test_case->input, test_case->input_len); + test_assert_idx(i_stream_read(input) >= 0 || + (input->stream_errno == 0 && input->eof), i); + i_stream_skip(input, test_case->skip); + test_assert_strcmp_idx(i_stream_next_line(input), test_case->output, i); + test_assert_idx(input->stream_errno == 0, i); + i_stream_unref(&input); + } + +#undef TEST_CASE +#define TEST_CASE(a) test_istream_create_data((a), sizeof(a)) + /* multiline tests */ + struct istream *is = TEST_CASE("\n\n\n\n\n\n"); + size_t i; + test_assert(i_stream_read(is) >= 0 || (is->stream_errno == 0 && is->eof)); + for(i = 0; i < 6; i++) + test_istream_next_line_expect(is, "", i); + test_assert(is->stream_errno == 0); + i_stream_unref(&is); + + is = TEST_CASE( + "simple\r\n" + "multiline\n" + "test with\0" + "some exciting\n" + "things\r\n\0"); + test_assert(i_stream_read(is) >= 0 || (is->stream_errno == 0 && is->eof)); + test_istream_next_line_expect(is, "simple", 0); + test_istream_next_line_expect(is, "multiline", 1); + test_istream_next_line_expect(is, "test with", 2); + test_istream_next_line_expect(is, "things", 3); + test_istream_next_line_expect(is, NULL, 4); + test_assert(is->stream_errno == 0); + i_stream_unref(&is); + + is = TEST_CASE( + "NUL\0" + "test\n"); + test_assert(i_stream_read(is) >= 0 || (is->stream_errno == 0 && is->eof)); + test_istream_next_line_expect(is, "NUL", 0); + test_istream_next_line_expect(is, NULL, 1); + test_assert(is->stream_errno == 0); + i_stream_unref(&is); + + const char test_data_1[] = + "this is some data\n" + "written like this\n" + "to attempt and induce\n" + "errors or flaws\n"; + + is = TEST_CASE(test_data_1); + size_t n = 0; + const char *const *lines = t_strsplit(test_data_1, "\n"); + for(i = 0; i < sizeof(test_data_1); i++) { + test_istream_set_size(is, i); + test_assert(i_stream_read(is) >= 0); + const char *line = i_stream_next_line(is); + if (line != NULL) { + test_assert_strcmp_idx(lines[n], line, n); + n++; + } + } + test_assert(n == 4); + test_assert(is->stream_errno == 0); + i_stream_unref(&is); + + const char test_data_2[] = + "this is some data\n" + "written like this\n" + "to attempt and induce\n" + "errors or flaws"; + + is = TEST_CASE(test_data_2); + lines = t_strsplit(test_data_2, "\n"); + i_stream_set_return_partial_line(is, TRUE); + n = 0; + + /* requires one extra read to get the last line */ + for(i = 0; i < sizeof(test_data_1) + 1; i++) { + test_istream_set_size(is, I_MIN(sizeof(test_data_1), i)); + (void)i_stream_read(is); + const char *line = i_stream_next_line(is); + if (line != NULL) { + test_assert_strcmp_idx(lines[n], line, n); + n++; + } + (void)i_stream_read(is); + } + test_assert(n == 4); + test_assert(is->stream_errno == 0); + i_stream_unref(&is); + + const char test_data_3[] = + "this is some data\r\n" + "written like this\r\n" + "to attempt and induce\r\n" + "errors or flaws\r\n"; + + struct istream *is_1 = TEST_CASE(test_data_3); + is = i_stream_create_crlf(is_1); + i_stream_unref(&is_1); + + lines = t_strsplit_spaces(test_data_3, "\r\n"); + n = 0; + + for(i = 0; i < sizeof(test_data_3); i++) { + test_istream_set_size(is, i); + test_assert(i_stream_read(is) >= 0); + const char *line = i_stream_next_line(is); + if (line != NULL) { + test_assert_strcmp_idx(lines[n], line, n); + n++; + } + } + test_assert(n == 4); + test_assert(is->stream_errno == 0); + i_stream_unref(&is); + + test_end(); +} + +static void test_istream_read_next_line(void) +{ + /* single line cases */ +#undef TEST_CASE +#define TEST_CASE(a, s, b) { \ + .input = (const unsigned char*)((a)), .input_len = sizeof((a)), \ + .skip = s, \ + .output = b } + const struct test_case_sl { + const unsigned char *input; + size_t input_len; + size_t skip; + const char *output; + } test_cases_sl[] = { + TEST_CASE("", 0, NULL), + TEST_CASE("a\n", 1, ""), + TEST_CASE("a\r\n", 0, "a"), + TEST_CASE("a\r\n", 1, ""), + TEST_CASE("a\r\n", 2, ""), + TEST_CASE("hello\nworld\n", 6, "world"), + TEST_CASE("hello\nworld", 6, NULL), + TEST_CASE("hello\n\n\n\n", 6, ""), + TEST_CASE("wrong\n\r\n\n", 0, "wrong"), + TEST_CASE("wrong\n\r\r\n", 6, "\r"), + TEST_CASE("wrong\n\r\r\n", 7, ""), + }; + + test_begin("i_stream_read_next_line"); + for(unsigned int i = 0; i < N_ELEMENTS(test_cases_sl); i++) { + const struct test_case_sl *test_case = &test_cases_sl[i]; + struct istream *input = + i_stream_create_copy_from_data(test_case->input, test_case->input_len); + i_stream_skip(input, test_case->skip); + test_assert_strcmp_idx(i_stream_read_next_line(input), test_case->output, i); + test_assert_idx(input->stream_errno == 0, i); + i_stream_unref(&input); + + input = test_istream_create_data(test_case->input, test_case->input_len); + i_stream_skip(input, test_case->skip); + test_assert_strcmp_idx(i_stream_read_next_line(input), test_case->output, i); + test_assert_idx(input->stream_errno == 0, i); + i_stream_unref(&input); + } + + const char test_data_1[] = + "this is some data\n" + "written like this\n" + "to attempt and induce\n" + "errors or flaws\n"; + +#undef TEST_CASE +#define TEST_CASE(a) test_istream_create_data((a), sizeof(a)) + /* multiline tests */ + struct istream *is = TEST_CASE("\n\n\n\n\n\n"); + size_t i; + for(i = 0; i < 6; i++) + test_assert_strcmp_idx(i_stream_read_next_line(is), "", i); + test_assert(is->stream_errno == 0); + i_stream_unref(&is); + + is = TEST_CASE( + "simple\r\n" + "multiline\n" + "test with\0" + "some exciting\n" + "things\r\n\0"); + test_assert_strcmp_idx(i_stream_read_next_line(is), "simple", 0); + test_assert_strcmp_idx(i_stream_read_next_line(is), "multiline", 1); + test_assert_strcmp_idx(i_stream_read_next_line(is), "test with", 2); + test_assert_strcmp_idx(i_stream_read_next_line(is), "things", 3); + test_assert_strcmp_idx(i_stream_read_next_line(is), NULL, 4); + test_assert(is->stream_errno == 0); + i_stream_unref(&is); + + is = TEST_CASE( + "NUL\0" + "test\n"); + test_assert_strcmp_idx(i_stream_read_next_line(is), "NUL", 0); + test_assert_strcmp_idx(i_stream_read_next_line(is), NULL, 1); + test_assert(is->stream_errno == 0); + i_stream_unref(&is); + + is = TEST_CASE(test_data_1); + size_t n = 0; + const char *const *lines = t_strsplit(test_data_1, "\n"); + for(i = 0; i < sizeof(test_data_1); i++) { + test_istream_set_size(is, i); + const char *line = i_stream_read_next_line(is); + if (line != NULL) { + test_assert_strcmp_idx(lines[n], line, n); + n++; + } + } + test_assert(n == 4); + test_assert(is->stream_errno == 0); + i_stream_unref(&is); + + const char test_data_2[] = + "this is some data\n" + "written like this\n" + "to attempt and induce\n" + "errors or flaws"; + + is = TEST_CASE(test_data_2); + lines = t_strsplit(test_data_2, "\n"); + i_stream_set_return_partial_line(is, TRUE); + n = 0; + + for(i = 0; i < sizeof(test_data_1); i++) { + test_istream_set_size(is, i); + const char *line = i_stream_read_next_line(is); + if (line != NULL) { + test_assert_strcmp_idx(lines[n], line, n); + n++; + } + } + test_assert(n == 4); + test_assert(is->stream_errno == 0); + i_stream_unref(&is); + + const char test_data_3[] = + "this is some data\r\n" + "written like this\r\n" + "to attempt and induce\r\n" + "errors or flaws\r\n"; + + + struct istream *is_1 = TEST_CASE(test_data_3); + is = i_stream_create_crlf(is_1); + i_stream_unref(&is_1); + + lines = t_strsplit_spaces(test_data_3, "\r\n"); + n = 0; + + for(i = 0; i < sizeof(test_data_3); i++) { + test_istream_set_size(is, i); + const char *line = i_stream_read_next_line(is); + if (line != NULL) { + test_assert_strcmp_idx(lines[n], line, n); + n++; + } + } + test_assert(n == 4); + test_assert(is->stream_errno == 0); + i_stream_unref(&is); + + test_end(); +} + +void test_istream(void) +{ + test_istream_children(); + test_istream_next_line(); + test_istream_read_next_line(); +} diff --git a/src/lib/test-json-parser.c b/src/lib/test-json-parser.c new file mode 100644 index 0000000..30ac5da --- /dev/null +++ b/src/lib/test-json-parser.c @@ -0,0 +1,436 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "istream-private.h" +#include "json-parser.h" + +#define TYPE_SKIP 100 +#define TYPE_STREAM 101 + +static const char json_input[] = + "{\n" + "\t\"key\"\t:\t\t\"string\"," + " \"key2\" : 1234, \n" + "\"key3\":true," + "\"key4\":false," + "\"skip1\": \"jsifjaisfjiasji\"," + "\"skip2\": { \"x\":{ \"y\":123}, \"z\":[5,[6],{\"k\":0},3]}," + "\"key5\":null," + "\"key6\": {}," + "\"key7\": {" + " \"sub1\":\"value\"" + "}," + "\"key8\": {" + " \"sub2\":-12.456,\n" + " \"sub3\":12.456e9,\n" + " \"sub4\":0.456e-789" + "}," + "\"key9\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\\u10ff\"," + "\"key10\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\\u10ff\"," + "\"key11\": []," + "\"key12\": [ \"foo\" , 5.24,[true],{\"aobj\":[]}]," + "\"key13\": \"\\ud801\\udc37\"," + "\"key14\": \"\xd8\xb3\xd9\x84\xd8\xa7\xd9\x85\"," + "\"key15\": \"\\u10000\"" + "}\n"; + +static const struct { + enum json_type type; + const char *value; +} json_output[] = { + { JSON_TYPE_OBJECT_KEY, "key" }, + { JSON_TYPE_STRING, "string" }, + { JSON_TYPE_OBJECT_KEY, "key2" }, + { JSON_TYPE_NUMBER, "1234" }, + { JSON_TYPE_OBJECT_KEY, "key3" }, + { JSON_TYPE_TRUE, "true" }, + { JSON_TYPE_OBJECT_KEY, "key4" }, + { JSON_TYPE_FALSE, "false" }, + { JSON_TYPE_OBJECT_KEY, "skip1" }, + { TYPE_SKIP, NULL }, + { JSON_TYPE_OBJECT_KEY, "skip2" }, + { TYPE_SKIP, NULL }, + { JSON_TYPE_OBJECT_KEY, "key5" }, + { JSON_TYPE_NULL, NULL }, + { JSON_TYPE_OBJECT_KEY, "key6" }, + { JSON_TYPE_OBJECT, NULL }, + { JSON_TYPE_OBJECT_END, NULL }, + { JSON_TYPE_OBJECT_KEY, "key7" }, + { JSON_TYPE_OBJECT, NULL }, + { JSON_TYPE_OBJECT_KEY, "sub1" }, + { JSON_TYPE_STRING, "value" }, + { JSON_TYPE_OBJECT_END, NULL }, + { JSON_TYPE_OBJECT_KEY, "key8" }, + { JSON_TYPE_OBJECT, NULL }, + { JSON_TYPE_OBJECT_KEY, "sub2" }, + { JSON_TYPE_NUMBER, "-12.456" }, + { JSON_TYPE_OBJECT_KEY, "sub3" }, + { JSON_TYPE_NUMBER, "12.456e9" }, + { JSON_TYPE_OBJECT_KEY, "sub4" }, + { JSON_TYPE_NUMBER, "0.456e-789" }, + { JSON_TYPE_OBJECT_END, NULL }, + { JSON_TYPE_OBJECT_KEY, "key9" }, + { JSON_TYPE_STRING, "foo\\\"\b\f\n\r\t\001\xe1\x83\xbf" }, + { JSON_TYPE_OBJECT_KEY, "key10" }, + { TYPE_STREAM, "foo\\\"\b\f\n\r\t\001\xe1\x83\xbf" }, + { JSON_TYPE_OBJECT_KEY, "key11" }, + { JSON_TYPE_ARRAY, NULL }, + { JSON_TYPE_ARRAY_END, NULL }, + { JSON_TYPE_OBJECT_KEY, "key12" }, + { JSON_TYPE_ARRAY, NULL }, + { JSON_TYPE_STRING, "foo" }, + { JSON_TYPE_NUMBER, "5.24" }, + { JSON_TYPE_ARRAY, NULL }, + { JSON_TYPE_TRUE, "true" }, + { JSON_TYPE_ARRAY_END, NULL }, + { JSON_TYPE_OBJECT, NULL }, + { JSON_TYPE_OBJECT_KEY, "aobj" }, + { JSON_TYPE_ARRAY, NULL }, + { JSON_TYPE_ARRAY_END, NULL }, + { JSON_TYPE_OBJECT_END, NULL }, + { JSON_TYPE_ARRAY_END, NULL }, + { JSON_TYPE_OBJECT_KEY, "key13" }, + { JSON_TYPE_STRING, "\xf0\x90\x90\xb7" }, + { JSON_TYPE_OBJECT_KEY, "key14" }, + { JSON_TYPE_STRING, "\xd8\xb3\xd9\x84\xd8\xa7\xd9\x85" }, + { JSON_TYPE_OBJECT_KEY, "key15" }, + { JSON_TYPE_STRING, "\xe1\x80\x80""0" }, +}; + +static int +stream_read_value(struct istream **input, const char **value_r) +{ + const unsigned char *data; + size_t size; + ssize_t ret; + + while ((ret = i_stream_read(*input)) > 0) ; + if (ret == 0) + return 0; + i_assert(ret == -1); + if ((*input)->stream_errno != 0) + return -1; + + data = i_stream_get_data(*input, &size); + *value_r = t_strndup(data, size); + i_stream_unref(input); + return 1; +} + +static void test_json_parser_success(bool full_size) +{ + struct json_parser *parser; + struct istream *input, *jsoninput = NULL; + enum json_type type; + const char *value, *error; + unsigned int i, pos, json_input_len = strlen(json_input); + int ret = 0; + + test_begin(full_size ? "json parser" : "json parser (nonblocking)"); + input = test_istream_create_data(json_input, json_input_len); + test_istream_set_allow_eof(input, FALSE); + parser = json_parser_init(input); + + i = full_size ? json_input_len : 0; + for (pos = 0; i <= json_input_len; i++) { + test_istream_set_size(input, i); + + for (;;) { + value = NULL; + if (pos < N_ELEMENTS(json_output) && + json_output[pos].type == (enum json_type)TYPE_SKIP) { + json_parse_skip_next(parser); + pos++; + continue; + } else if (pos == N_ELEMENTS(json_output) || + json_output[pos].type != (enum json_type)TYPE_STREAM) { + ret = json_parse_next(parser, &type, &value); + } else { + ret = jsoninput != NULL ? 1 : + json_parse_next_stream(parser, &jsoninput); + if (ret > 0 && jsoninput != NULL) + ret = stream_read_value(&jsoninput, &value); + type = TYPE_STREAM; + } + if (ret <= 0) + break; + + i_assert(pos < N_ELEMENTS(json_output)); + test_assert_idx(json_output[pos].type == type, pos); + test_assert_idx(null_strcmp(json_output[pos].value, value) == 0, pos); + + pos++; + } + test_assert_idx(ret == 0, pos); + } + test_assert(pos == N_ELEMENTS(json_output)); + test_istream_set_allow_eof(input, TRUE); + test_assert(json_parse_next(parser, &type, &value) == -1); + + i_stream_unref(&input); + test_assert(json_parser_deinit(&parser, &error) == 0); + test_end(); +} + +static void test_json_parser_skip_array(void) +{ + static const char *test_input = + "[ 1, {\"foo\": 1 }, 2, \"bar\", 3, 1.234, 4, [], 5, [[]], 6, true ]"; + struct json_parser *parser; + struct istream *input; + enum json_type type; + const char *value, *error; + int i; + + test_begin("json parser skip array"); + + input = test_istream_create_data(test_input, strlen(test_input)); + parser = json_parser_init_flags(input, JSON_PARSER_NO_ROOT_OBJECT); + test_assert(json_parse_next(parser, &type, &value) > 0 && + type == JSON_TYPE_ARRAY); + for (i = 1; i <= 6; i++) { + test_assert(json_parse_next(parser, &type, &value) > 0 && + type == JSON_TYPE_NUMBER && atoi(value) == i); + json_parse_skip_next(parser); + } + test_assert(json_parse_next(parser, &type, &value) > 0 && + type == JSON_TYPE_ARRAY_END); + test_assert(json_parser_deinit(&parser, &error) == 0); + i_stream_unref(&input); + test_end(); +} + +static void test_json_parser_skip_object_fields(void) +{ + static const char *test_input = + "{\"access_token\":\"9a2dea3c-f8be-4271-b9c8-5b37da4f2f7e\"," + "\"grant_type\":\"authorization_code\"," + "\"openid\":\"\"," + "\"scope\":[\"openid\",\"profile\",\"email\"]," + "\"profile\":\"\"," + "\"realm\":\"/employees\"," + "\"token_type\":\"Bearer\"," + "\"expires_in\":2377," + "\"client_i\\u0064\":\"mosaic\\u0064\"," + "\"email\":\"\"," + "\"extensions\":" + "{\"algorithm\":\"cuttlefish\"," + "\"tentacles\":8" + "}" + "}"; + static const char *const keys[] = { + "access_token", "grant_type", "openid", "scope", "profile", + "realm", "token_type", "expires_in", "client_id", "email", + "extensions" + }; + static const unsigned int keys_count = N_ELEMENTS(keys); + struct json_parser *parser; + struct istream *input; + enum json_type type; + const char *value, *error; + unsigned int i; + size_t pos; + int ret; + + test_begin("json parser skip object fields (by key)"); + input = test_istream_create_data(test_input, strlen(test_input)); + parser = json_parser_init(input); + for (i = 0; i < keys_count; i++) { + ret = json_parse_next(parser, &type, &value); + if (ret < 0) + break; + test_assert(ret > 0 && type == JSON_TYPE_OBJECT_KEY); + test_assert(strcmp(value, keys[i]) == 0); + json_parse_skip_next(parser); + } + test_assert(i == keys_count); + test_assert(json_parser_deinit(&parser, &error) == 0); + i_stream_unref(&input); + + i = 0; + input = test_istream_create_data(test_input, strlen(test_input)); + parser = json_parser_init(input); + for (pos = 0; pos <= strlen(test_input)*2; pos++) { + test_istream_set_size(input, pos/2); + ret = json_parse_next(parser, &type, &value); + if (ret == 0) + continue; + if (ret < 0) + break; + i_assert(i < keys_count); + test_assert(ret > 0 && type == JSON_TYPE_OBJECT_KEY); + test_assert(strcmp(value, keys[i]) == 0); + json_parse_skip_next(parser); + i++; + } + test_assert(i == keys_count); + test_assert(json_parser_deinit(&parser, &error) == 0); + i_stream_unref(&input); + test_end(); + + test_begin("json parser skip object fields (by value type)"); + input = test_istream_create_data(test_input, strlen(test_input)); + parser = json_parser_init(input); + for (i = 0; i < keys_count; i++) { + ret = json_parse_next(parser, &type, &value); + if (ret < 0) + break; + test_assert(ret > 0 && type == JSON_TYPE_OBJECT_KEY); + test_assert(strcmp(value, keys[i]) == 0); + ret = json_parse_next(parser, &type, &value); + test_assert(ret > 0 && type != JSON_TYPE_OBJECT_KEY); + json_parse_skip(parser); + } + test_assert(i == keys_count); + test_assert(json_parser_deinit(&parser, &error) == 0); + i_stream_unref(&input); + + i = 0; + input = test_istream_create_data(test_input, strlen(test_input)); + parser = json_parser_init(input); + for (pos = 0; pos <= strlen(test_input)*2; pos++) { + test_istream_set_size(input, pos/2); + ret = json_parse_next(parser, &type, &value); + if (ret < 0) + break; + if (ret == 0) + continue; + test_assert(ret > 0); + if (type == JSON_TYPE_OBJECT_KEY) { + i_assert(i < keys_count); + test_assert(strcmp(value, keys[i]) == 0); + i++; + } else { + json_parse_skip(parser); + } + } + test_assert(i == keys_count); + test_assert(json_parser_deinit(&parser, &error) == 0); + i_stream_unref(&input); + + test_end(); +} + +static int +test_json_parse_input(const void *test_input, size_t test_input_size, + enum json_parser_flags flags) +{ + struct json_parser *parser; + struct istream *input; + enum json_type type; + const char *value, *error; + int ret = 0; + + input = test_istream_create_data(test_input, test_input_size); + parser = json_parser_init_flags(input, flags); + while (json_parse_next(parser, &type, &value) > 0) + ret++; + if (json_parser_deinit(&parser, &error) < 0) + ret = -1; + i_stream_unref(&input); + return ret; +} + +static void test_json_parser_primitive_values(void) +{ + static const struct { + const char *str; + int ret; + } test_inputs[] = { + { "\"hello\"", 1 }, + { "null", 1 }, + { "1234", 1 }, + { "1234.1234", 1 }, + { "{}", 2 }, + { "[]", 2 }, + { "true", 1 }, + { "false", 1 } + }; + unsigned int i; + + test_begin("json_parser (primitives)"); + for (i = 0; i < N_ELEMENTS(test_inputs); i++) + test_assert_idx(test_json_parse_input(test_inputs[i].str, + strlen(test_inputs[i].str), + JSON_PARSER_NO_ROOT_OBJECT) == test_inputs[i].ret, i); + test_end(); +} + +static void test_json_parser_errors(void) +{ + static const char *test_inputs[] = { + "{", + "{:}", + "{\"foo\":}", + "{\"foo\" []}", + "{\"foo\": [1}", + "{\"foo\": [1,]}", + "{\"foo\": [1,]}", + "{\"foo\": 1,}", + "{\"foo\": 1.}}", + "{\"foo\": 1},{}", + "{\"foo\": \"\\ud808\"}", + "{\"foo\": \"\\udfff\"}", + "{\"foo\": \"\\uyyyy\"}", + }; + unsigned int i; + + test_begin("json parser error handling"); + for (i = 0; i < N_ELEMENTS(test_inputs); i++) + test_assert_idx(test_json_parse_input(test_inputs[i], + strlen(test_inputs[i]), + 0) < 0, i); + test_end(); +} + +static void test_json_parser_nuls_in_string(void) +{ + static const unsigned char test_input[] = + { '{', '"', 'k', '"', ':', '"', '\0', '"', '}' }; + static const unsigned char test_input2[] = + { '{', '"', 'k', '"', ':', '"', '\\', '\0', '"', '}' }; + static const unsigned char test_input3[] = + { '{', '"', 'k', '"', ':', '"', '\\', 'u', '0', '0', '0', '0', '"', '}' }; + + test_begin("json parser nuls in string"); + test_assert(test_json_parse_input(test_input, sizeof(test_input), 0) < 0); + test_assert(test_json_parse_input(test_input2, sizeof(test_input2), 0) < 0); + test_assert(test_json_parse_input(test_input3, sizeof(test_input3), 0) < 0); + test_end(); +} + +static void test_json_append_escaped(void) +{ + string_t *str = t_str_new(32); + + test_begin("json_append_escaped()"); + json_append_escaped(str, "\b\f\r\n\t\"\\\001\002-\xC3\xA4\xf0\x90\x90\xb7\xe2\x80\xa8\xe2\x80\xa9\xff"); + test_assert(strcmp(str_c(str), "\\b\\f\\r\\n\\t\\\"\\\\\\u0001\\u0002-\xC3\xA4\xf0\x90\x90\xb7\\u2028\\u2029" UNICODE_REPLACEMENT_CHAR_UTF8) == 0); + test_end(); +} + +static void test_json_append_escaped_data(void) +{ + static const unsigned char test_input[] = + "\b\f\r\n\t\"\\\000\001\002-\xC3\xA4\xf0\x90\x90\xb7\xe2\x80\xa8\xe2\x80\xa9\xff"; + string_t *str = t_str_new(32); + + test_begin("json_append_escaped_data()"); + json_append_escaped_data(str, test_input, sizeof(test_input)-1); + test_assert(strcmp(str_c(str), "\\b\\f\\r\\n\\t\\\"\\\\\\u0000\\u0001\\u0002-\xC3\xA4\xf0\x90\x90\xb7\\u2028\\u2029" UNICODE_REPLACEMENT_CHAR_UTF8) == 0); + test_end(); +} + +void test_json_parser(void) +{ + test_json_parser_success(TRUE); + test_json_parser_success(FALSE); + test_json_parser_skip_array(); + test_json_parser_skip_object_fields(); + test_json_parser_primitive_values(); + test_json_parser_errors(); + test_json_parser_nuls_in_string(); + test_json_append_escaped(); + test_json_append_escaped_data(); +} diff --git a/src/lib/test-json-tree.c b/src/lib/test-json-tree.c new file mode 100644 index 0000000..40eff8c --- /dev/null +++ b/src/lib/test-json-tree.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "json-tree.h" + +struct { + enum json_type type; + const char *value; +} test_input[] = { + { JSON_TYPE_OBJECT_KEY, "key-str" }, + { JSON_TYPE_STRING, "string" }, + { JSON_TYPE_OBJECT_KEY, "key-num" }, + { JSON_TYPE_NUMBER, "1234" }, + { JSON_TYPE_OBJECT_KEY, "key-true" }, + { JSON_TYPE_TRUE, "true" }, + { JSON_TYPE_OBJECT_KEY, "key-false" }, + { JSON_TYPE_FALSE, "false" }, + { JSON_TYPE_OBJECT_KEY, "key-null" }, + { JSON_TYPE_NULL, NULL }, + + { JSON_TYPE_OBJECT_KEY, "key-obj-empty" }, + { JSON_TYPE_OBJECT, NULL }, + { JSON_TYPE_OBJECT_END, NULL }, + + { JSON_TYPE_OBJECT_KEY, "key-obj" }, + { JSON_TYPE_OBJECT, NULL }, + { JSON_TYPE_OBJECT_KEY, "sub" }, + { JSON_TYPE_STRING, "value" }, + { JSON_TYPE_OBJECT_END, NULL }, + + { JSON_TYPE_OBJECT_KEY, "key-arr-empty" }, + { JSON_TYPE_ARRAY, NULL }, + { JSON_TYPE_ARRAY_END, NULL }, + + { JSON_TYPE_OBJECT_KEY, "key-arr" }, + { JSON_TYPE_ARRAY, NULL }, + { JSON_TYPE_STRING, "foo" }, + { JSON_TYPE_ARRAY, NULL }, + { JSON_TYPE_TRUE, "true" }, + { JSON_TYPE_ARRAY_END, NULL }, + { JSON_TYPE_OBJECT, NULL }, + { JSON_TYPE_OBJECT_KEY, "aobj" }, + { JSON_TYPE_ARRAY, NULL }, + { JSON_TYPE_ARRAY_END, NULL }, + { JSON_TYPE_OBJECT_END, NULL }, + { JSON_TYPE_OBJECT, NULL }, + { JSON_TYPE_OBJECT_KEY, "aobj-key" }, + { JSON_TYPE_STRING, "value1" }, + { JSON_TYPE_OBJECT_END, NULL }, + { JSON_TYPE_OBJECT, NULL }, + { JSON_TYPE_OBJECT_KEY, "aobj-key" }, + { JSON_TYPE_STRING, "value2" }, + { JSON_TYPE_OBJECT_END, NULL }, + { JSON_TYPE_ARRAY_END, NULL } +}; + +void test_json_tree(void) +{ + struct json_tree *tree; + const struct json_tree_node *root, *node, *node1, *node2; + unsigned int i; + + test_begin("json tree"); + tree = json_tree_init(); + for (i = 0; i < N_ELEMENTS(test_input); i++) { + test_assert_idx(json_tree_append(tree, test_input[i].type, + test_input[i].value) == 0, i); + } + + root = json_tree_root(tree); + i_assert(root != NULL); + test_assert(root->value_type == JSON_TYPE_OBJECT); + i_assert(root != NULL); + + for (i = 0; i < 10+2; i += 2) { + node = json_tree_find_key(root, test_input[i].value); + test_assert(node != NULL && + node->value_type == test_input[i+1].type && + null_strcmp(json_tree_get_value_str(node), test_input[i+1].value) == 0); + } + + node = json_tree_find_key(root, "key-obj"); + test_assert(node != NULL); + + node = json_tree_find_key(root, "key-arr-empty"); + test_assert(node != NULL && node->value_type == JSON_TYPE_ARRAY && + json_tree_get_child(node) == NULL); + + node = json_tree_find_key(root, "key-arr"); + test_assert(node != NULL && node->value_type == JSON_TYPE_ARRAY); + node = json_tree_get_child(node); + test_assert(node != NULL && node->value_type == JSON_TYPE_STRING && + strcmp(json_tree_get_value_str(node), "foo") == 0); + node = node->next; + test_assert(node != NULL && node->value_type == JSON_TYPE_ARRAY && + json_tree_get_child(node) != NULL && + json_tree_get_child(node)->next == NULL && + json_tree_get_child(node)->value_type == JSON_TYPE_TRUE); + node = node->next; + test_assert(node != NULL && node->value_type == JSON_TYPE_OBJECT && + json_tree_get_child(node) != NULL && + json_tree_get_child(node)->next == NULL && + json_tree_get_child(node)->value_type == JSON_TYPE_ARRAY && + json_tree_get_child(json_tree_get_child(node)) == NULL); + + node1 = json_tree_find_child_with(node->parent, "aobj-key", "value1"); + node2 = json_tree_find_child_with(node->parent, "aobj-key", "value2"); + test_assert(node1 != NULL && node2 != NULL && node1 != node2); + test_assert(json_tree_find_child_with(node->parent, "aobj-key", "value3") == NULL); + + json_tree_deinit(&tree); + test_end(); +} diff --git a/src/lib/test-lib-event.c b/src/lib/test-lib-event.c new file mode 100644 index 0000000..cfc4106 --- /dev/null +++ b/src/lib/test-lib-event.c @@ -0,0 +1,96 @@ +/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" + +static void test_event_strlist(void) +{ + test_begin("event strlist"); + struct event *e1 = event_create(NULL); + event_strlist_append(e1, "key", "s1"); + event_strlist_append(e1, "key", "s2"); + struct event *e2 = event_create(e1); + event_strlist_append(e2, "key", "s3"); + event_strlist_append(e2, "key", "s2"); + + test_assert_strcmp(event_find_field_recursive_str(e1, "key"), "s1,s2"); + test_assert_strcmp(event_find_field_recursive_str(e2, "key"), "s3,s2,s1"); + + const char *new_strlist[] = { "new1", "new2", "new2", "s2" }; + event_strlist_replace(e2, "key", new_strlist, N_ELEMENTS(new_strlist)); + test_assert_strcmp(event_find_field_recursive_str(e2, "key"), "new1,new2,s2,s1"); + + struct event *e3 = event_create(NULL); + event_strlist_copy_recursive(e3, e2, "key"); + test_assert_strcmp(event_find_field_recursive_str(e3, "key"), "new1,new2,s2,s1"); + event_unref(&e3); + + event_unref(&e1); + event_unref(&e2); + test_end(); +} + +static void test_lib_event_reason_code(void) +{ + test_begin("event reason codes"); + test_assert_strcmp(event_reason_code("foo", "bar"), "foo:bar"); + test_assert_strcmp(event_reason_code("foo", "B A-r"), "foo:b_a_r"); + test_assert_strcmp(event_reason_code_prefix("foo", "x", "bar"), "foo:xbar"); + test_assert_strcmp(event_reason_code_prefix("foo", "", "bar"), "foo:bar"); + test_end(); +} + +void test_lib_event(void) +{ + test_event_strlist(); + test_lib_event_reason_code(); +} + +enum fatal_test_state fatal_lib_event(unsigned int stage) +{ + switch (stage) { + case 0: + test_begin("event reason codes - asserts"); + /* module: uppercase */ + test_expect_fatal_string("Invalid module"); + (void)event_reason_code("FOO", "bar"); + return FATAL_TEST_FAILURE; + case 1: + /* module: space */ + test_expect_fatal_string("Invalid module"); + (void)event_reason_code("f oo", "bar"); + return FATAL_TEST_FAILURE; + case 2: + /* module: - */ + test_expect_fatal_string("Invalid module"); + (void)event_reason_code("f-oo", "bar"); + return FATAL_TEST_FAILURE; + case 3: + /* module: empty */ + test_expect_fatal_string("module[0] != '\\0'"); + (void)event_reason_code("", "bar"); + return FATAL_TEST_FAILURE; + case 4: + /* name_prefix: uppercase */ + test_expect_fatal_string("Invalid name_prefix"); + (void)event_reason_code_prefix("module", "FOO", "bar"); + return FATAL_TEST_FAILURE; + case 5: + /* name_prefix: space */ + test_expect_fatal_string("Invalid name_prefix"); + (void)event_reason_code_prefix("module", "f oo", "bar"); + return FATAL_TEST_FAILURE; + case 6: + /* name_prefix: - */ + test_expect_fatal_string("Invalid name_prefix"); + (void)event_reason_code_prefix("module", "f-oo", "bar"); + return FATAL_TEST_FAILURE; + case 7: + /* name: empty */ + test_expect_fatal_string("(name[0] != '\\0')"); + (void)event_reason_code("foo:", ""); + return FATAL_TEST_FAILURE; + default: + test_end(); + return FATAL_TEST_FINISHED; + } +} diff --git a/src/lib/test-lib-signals.c b/src/lib/test-lib-signals.c new file mode 100644 index 0000000..75d324a --- /dev/null +++ b/src/lib/test-lib-signals.c @@ -0,0 +1,245 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "time-util.h" +#include "ioloop.h" +#include "lib-signals.h" + +#include <unistd.h> +#include <sys/types.h> + +struct test_context_delayed { + bool timed_out:1; + bool signal_handled:1; +}; + +static void +kill_timeout(struct test_context_delayed *tctx ATTR_UNUSED) +{ + if (kill(getpid(), SIGALRM) < 0) + i_fatal("Failed to send signal: %m"); +} + +static void +test_timeout(struct test_context_delayed *tctx) +{ + tctx->timed_out = TRUE; + io_loop_stop(current_ioloop); +} + +static void +signal_handler_delayed(const siginfo_t *si ATTR_UNUSED, + void *context ATTR_UNUSED) +{ + struct test_context_delayed *tctx = + (struct test_context_delayed *)context; + tctx->signal_handled = TRUE; + io_loop_stop(current_ioloop); +} + +static void +test_lib_signals_delayed(void) +{ + struct test_context_delayed tctx; + struct timeout *to_kill, *to_test; + struct ioloop *ioloop; + + test_begin("lib-signals delayed - init lib-signals first"); + + i_zero(&tctx); + + lib_signals_init(); + lib_signals_set_handler(SIGALRM, + LIBSIG_FLAGS_SAFE | LIBSIG_FLAG_IOLOOP_AUTOMOVE, + signal_handler_delayed, &tctx); + + ioloop = io_loop_create(); + to_kill = timeout_add_short(200, kill_timeout, &tctx); + to_test = timeout_add_short(400, test_timeout, &tctx); + io_loop_run(ioloop); + + timeout_remove(&to_kill); + timeout_remove(&to_test); + io_loop_destroy(&ioloop); + + lib_signals_deinit(); + + test_assert(!tctx.timed_out); + test_assert(tctx.signal_handled); + + test_end(); + + test_begin("lib-signals delayed - init ioloop first"); + + i_zero(&tctx); + + ioloop = io_loop_create(); + + lib_signals_init(); + lib_signals_set_handler(SIGALRM, + LIBSIG_FLAGS_SAFE | LIBSIG_FLAG_IOLOOP_AUTOMOVE, + signal_handler_delayed, &tctx); + + to_kill = timeout_add_short(200, kill_timeout, &tctx); + to_test = timeout_add_short(400, test_timeout, &tctx); + io_loop_run(ioloop); + + timeout_remove(&to_kill); + timeout_remove(&to_test); + + lib_signals_deinit(); + + io_loop_destroy(&ioloop); + + test_assert(!tctx.timed_out); + test_assert(tctx.signal_handled); + + test_end(); + +} + +static void +test_lib_signals_delayed_nested_ioloop(void) +{ + struct test_context_delayed tctx; + struct timeout *to_kill, *to_test; + struct ioloop *ioloop1, *ioloop2; + + test_begin("lib-signals delayed in nested ioloop"); + + i_zero(&tctx); + + lib_signals_init(); + lib_signals_set_handler(SIGALRM, + LIBSIG_FLAGS_SAFE | LIBSIG_FLAG_IOLOOP_AUTOMOVE, + signal_handler_delayed, &tctx); + + /* briefly run outer ioloop */ + ioloop1 = io_loop_create(); + to_test = timeout_add_short(100, test_timeout, &tctx); + io_loop_run(ioloop1); + timeout_remove(&to_test); + test_assert(tctx.timed_out); + test_assert(!tctx.signal_handled); + tctx.timed_out = FALSE; + + /* run inner ioloop, which triggers the signal */ + ioloop2 = io_loop_create(); + to_kill = timeout_add_short(200, kill_timeout, &tctx); + to_test = timeout_add_short(400, test_timeout, &tctx); + io_loop_run(ioloop2); + + timeout_remove(&to_kill); + timeout_remove(&to_test); + io_loop_destroy(&ioloop2); + + io_loop_destroy(&ioloop1); + + lib_signals_deinit(); + + test_assert(!tctx.timed_out); + test_assert(tctx.signal_handled); + + test_end(); +} + +static void +test_lib_signals_delayed_no_ioloop_automove(void) +{ + struct test_context_delayed tctx; + struct timeout *to_kill, *to_test; + struct ioloop *ioloop1, *ioloop2; + + test_begin("lib-signals delayed with NO_IOLOOP_AUTOMOVE - unmoved"); + + i_zero(&tctx); + + ioloop1 = io_loop_create(); + + lib_signals_init(); + lib_signals_set_handler(SIGALRM, LIBSIG_FLAGS_SAFE, + signal_handler_delayed, &tctx); + + /* briefly run outer ioloop */ + to_test = timeout_add_short(100, test_timeout, &tctx); + io_loop_run(ioloop1); + timeout_remove(&to_test); + test_assert(tctx.timed_out); + test_assert(!tctx.signal_handled); + tctx.timed_out = FALSE; + + /* run inner ioloop, which triggers the signal but musn't handle it */ + ioloop2 = io_loop_create(); + to_kill = timeout_add_short(200, kill_timeout, &tctx); + to_test = timeout_add_short(400, test_timeout, &tctx); + io_loop_run(ioloop2); + + test_assert(tctx.timed_out); + test_assert(!tctx.signal_handled); + tctx.timed_out = FALSE; + + timeout_remove(&to_kill); + timeout_remove(&to_test); + io_loop_destroy(&ioloop2); + + /* run outer ioloop once more */ + to_test = timeout_add_short(100, test_timeout, &tctx); + io_loop_run(ioloop1); + timeout_remove(&to_test); + + lib_signals_deinit(); + + io_loop_destroy(&ioloop1); + + test_assert(!tctx.timed_out); + test_assert(tctx.signal_handled); + + test_end(); + + test_begin("lib-signals delayed with NO_IOLOOP_AUTOMOVE - moved"); + + i_zero(&tctx); + + ioloop1 = io_loop_create(); + + lib_signals_init(); + lib_signals_set_handler(SIGALRM, LIBSIG_FLAGS_SAFE, + signal_handler_delayed, &tctx); + + /* briefly run outer ioloop */ + to_test = timeout_add_short(100, test_timeout, &tctx); + io_loop_run(ioloop1); + timeout_remove(&to_test); + test_assert(tctx.timed_out); + test_assert(!tctx.signal_handled); + tctx.timed_out = FALSE; + + /* run inner ioloop, which triggers the signal */ + ioloop2 = io_loop_create(); + lib_signals_switch_ioloop(SIGALRM, + signal_handler_delayed, &tctx); + + to_kill = timeout_add_short(200, kill_timeout, &tctx); + to_test = timeout_add_short(400, test_timeout, &tctx); + io_loop_run(ioloop2); + + test_assert(!tctx.timed_out); + test_assert(tctx.signal_handled); + + timeout_remove(&to_kill); + timeout_remove(&to_test); + io_loop_destroy(&ioloop2); + + lib_signals_deinit(); + io_loop_destroy(&ioloop1); + + test_end(); +} + + +void test_lib_signals(void) +{ + test_lib_signals_delayed(); + test_lib_signals_delayed_nested_ioloop(); + test_lib_signals_delayed_no_ioloop_automove(); +} diff --git a/src/lib/test-lib.c b/src/lib/test-lib.c new file mode 100644 index 0000000..62e6b0d --- /dev/null +++ b/src/lib/test-lib.c @@ -0,0 +1,28 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" + +int main(int argc, char **argv) +{ + const char *match = ""; + if (argc > 2 && strcmp(argv[1], "--match") == 0) + match = argv[2]; + + static const struct named_test test_functions[] = { +#define TEST(x) TEST_NAMED(x) +#define FATAL(x) +#include "test-lib.inc" +#undef TEST +#undef FATAL + { NULL, NULL } + }; + static const struct named_fatal fatal_functions[] = { +#define TEST(x) +#define FATAL(x) FATAL_NAMED(x) +#include "test-lib.inc" +#undef TEST +#undef FATAL + { NULL, NULL } + }; + return test_run_named_with_fatals(match, test_functions, fatal_functions); +} diff --git a/src/lib/test-lib.h b/src/lib/test-lib.h new file mode 100644 index 0000000..7fdd105 --- /dev/null +++ b/src/lib/test-lib.h @@ -0,0 +1,13 @@ +#ifndef TEST_LIB +#define TEST_LIB + +#include "lib.h" +#include "test-common.h" + +#define TEST(x) TEST_DECL(x) +#define FATAL(x) FATAL_DECL(x) +#include "test-lib.inc" +#undef TEST +#undef FATAL + +#endif diff --git a/src/lib/test-lib.inc b/src/lib/test-lib.inc new file mode 100644 index 0000000..e698708 --- /dev/null +++ b/src/lib/test-lib.inc @@ -0,0 +1,112 @@ +/* This file may be multiply-included, with different definitions of + 'TEST()' macro. This is sometimes called "the X trick" (as the + macro is often imaginatively called X(). */ + +TEST(test_aqueue) +TEST(test_array) +FATAL(fatal_array) +TEST(test_backtrace) +TEST(test_base32) +TEST(test_base64) +TEST(test_bits) +TEST(test_bsearch_insert_pos) +TEST(test_buffer) +TEST(test_buffer_append_full) +FATAL(fatal_buffer) +TEST(test_byteorder) +TEST(test_connection) +TEST(test_crc32) +TEST(test_cpu_limit) +TEST(test_data_stack) +FATAL(fatal_data_stack) +TEST(test_env_util) +FATAL(fatal_env_util) +TEST(test_event_category_register) +FATAL(fatal_event_category_register) +TEST(test_lib_event) +FATAL(fatal_lib_event) +TEST(test_event_filter) +TEST(test_event_filter_expr) +TEST(test_event_filter_merge) +TEST(test_event_filter_parser) +TEST(test_event_flatten) +TEST(test_event_log) +TEST(test_failures) +TEST(test_file_cache) +TEST(test_file_create_locked) +TEST(test_guid) +TEST(test_hash) +TEST(test_hash_format) +TEST(test_hash_method) +TEST(test_hmac) +TEST(test_hex_binary) +FATAL(fatal_i_close) +TEST(test_imem) +TEST(test_ioloop) +TEST(test_iso8601_date) +TEST(test_iostream_pump) +TEST(test_iostream_proxy) +TEST(test_iostream_temp) +TEST(test_istream) +TEST(test_istream_base64_decoder) +TEST(test_istream_base64_encoder) +TEST(test_istream_chain) +TEST(test_istream_concat) +TEST(test_istream_crlf) +TEST(test_istream_failure_at) +TEST(test_istream_jsonstr) +TEST(test_istream_multiplex) +TEST(test_istream_seekable) +TEST(test_istream_sized) +TEST(test_istream_tee) +TEST(test_istream_try) +TEST(test_istream_unix) +TEST(test_json_parser) +TEST(test_json_tree) +TEST(test_lib_event) +TEST(test_lib_signals) +TEST(test_llist) +TEST(test_log_throttle) +TEST(test_macros) +TEST(test_malloc_overflow) +FATAL(fatal_malloc_overflow) +TEST(test_memarea) +TEST(test_mempool) +FATAL(fatal_mempool) +TEST(test_mempool_alloconly) +FATAL(fatal_mempool_alloconly) +TEST(test_mempool_allocfree) +FATAL(fatal_mempool_allocfree) +TEST(test_net) +TEST(test_numpack) +TEST(test_ostream_buffer) +TEST(test_ostream_failure_at) +TEST(test_ostream_file) +TEST(test_ostream_multiplex) +TEST(test_multiplex) +TEST(test_path_util) +TEST(test_pkcs5_pbkdf2) +TEST(test_primes) +TEST(test_printf_format_fix) +FATAL(fatal_printf_format_fix) +TEST(test_priorityq) +TEST(test_random) +FATAL(fatal_random) +TEST(test_seq_range_array) +FATAL(fatal_seq_range_array) +TEST(test_seq_set_builder) +TEST(test_stats_dist) +TEST(test_str) +TEST(test_strescape) +TEST(test_strfuncs) +FATAL(fatal_strfuncs) +TEST(test_strnum) +TEST(test_str_find) +TEST(test_str_sanitize) +TEST(test_str_table) +TEST(test_time_util) +TEST(test_unichar) +TEST(test_uri) +TEST(test_utc_mktime) +TEST(test_var_expand) +TEST(test_wildcard_match) diff --git a/src/lib/test-llist.c b/src/lib/test-llist.c new file mode 100644 index 0000000..d57006c --- /dev/null +++ b/src/lib/test-llist.c @@ -0,0 +1,138 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "llist.h" + + +struct dllist { + struct dllist *prev, *next; +}; + +static void test_dllist(void) +{ + struct dllist *head = NULL, *l4, *l3, *l2, *l1; + struct dllist empty = { NULL, NULL }; + + l4 = t_new(struct dllist, 1); + l3 = t_new(struct dllist, 1); + l2 = t_new(struct dllist, 1); + l1 = t_new(struct dllist, 1); + + test_begin("dllist"); + DLLIST_PREPEND(&head, l4); + test_assert(head == l4); + test_assert(l4->prev == NULL && l4->next == NULL); + DLLIST_PREPEND(&head, l3); + test_assert(head == l3); + test_assert(l3->prev == NULL && l3->next == l4); + test_assert(l4->prev == l3 && l4->next == NULL); + DLLIST_PREPEND(&head, l2); + DLLIST_PREPEND(&head, l1); + /* remove from middle */ + DLLIST_REMOVE(&head, l2); + test_assert(l2->prev == NULL && l2->next == NULL); + test_assert(head == l1); + test_assert(l1->prev == NULL && l1->next == l3); + test_assert(l3->prev == l1 && l3->next == l4); + test_assert(l4->prev == l3 && l4->next == NULL); + /* remove from head */ + DLLIST_REMOVE(&head, l1); + test_assert(l1->prev == NULL && l1->next == NULL); + test_assert(head == l3); + test_assert(l3->prev == NULL && l3->next == l4); + test_assert(l4->prev == l3 && l4->next == NULL); + /* remove from tail */ + DLLIST_PREPEND(&head, l1); + DLLIST_REMOVE(&head, l4); + test_assert(l4->prev == NULL && l4->next == NULL); + test_assert(head == l1); + test_assert(l1->prev == NULL && l1->next == l3); + test_assert(l3->prev == l1 && l3->next == NULL); + /* removal of an entry not in the list shouldn't cause the list to break */ + DLLIST_REMOVE(&head, &empty); + test_assert(head == l1); + test_assert(l1->prev == NULL && l1->next == l3); + test_assert(l3->prev == l1 && l3->next == NULL); + /* remove last two */ + DLLIST_REMOVE(&head, l1); + DLLIST_REMOVE(&head, l3); + test_assert(l3->prev == NULL && l3->next == NULL); + test_assert(head == NULL); + test_end(); +} + +static void test_dllist2(void) +{ + struct dllist *head = NULL, *tail = NULL, *l4, *l3, *l2, *l1; + struct dllist empty = { NULL, NULL }; + + l4 = t_new(struct dllist, 1); + l3 = t_new(struct dllist, 1); + l2 = t_new(struct dllist, 1); + l1 = t_new(struct dllist, 1); + + test_begin("dllist"); + /* prepend to empty */ + DLLIST2_PREPEND(&head, &tail, l3); + test_assert(head == l3 && tail == l3); + test_assert(l3->next == NULL && l3->prev == NULL); + /* remove last */ + DLLIST2_REMOVE(&head, &tail, l3); + test_assert(head == NULL && tail == NULL); + test_assert(l3->next == NULL && l3->prev == NULL); + /* append to empty */ + DLLIST2_APPEND(&head, &tail, l3); + test_assert(head == l3 && tail == l3); + test_assert(l3->next == NULL && l3->prev == NULL); + /* prepend */ + DLLIST2_PREPEND(&head, &tail, l2); + test_assert(head == l2 && tail == l3); + test_assert(l2->prev == NULL && l2->next == l3); + test_assert(l3->prev == l2 && l3->next == NULL); + /* append */ + DLLIST2_APPEND(&head, &tail, l4); + test_assert(head == l2 && tail == l4); + test_assert(l2->prev == NULL && l2->next == l3); + test_assert(l3->prev == l2 && l3->next == l4); + test_assert(l4->prev == l3 && l4->next == NULL); + DLLIST2_PREPEND(&head, &tail, l1); + + /* remove from middle */ + DLLIST2_REMOVE(&head, &tail, l2); + test_assert(l2->prev == NULL && l2->next == NULL); + test_assert(head == l1 && tail == l4); + test_assert(l1->prev == NULL && l1->next == l3); + test_assert(l3->prev == l1 && l3->next == l4); + test_assert(l4->prev == l3 && l4->next == NULL); + /* remove from head */ + DLLIST2_REMOVE(&head, &tail, l1); + test_assert(l1->prev == NULL && l1->next == NULL); + test_assert(head == l3 && tail == l4); + test_assert(l3->prev == NULL && l3->next == l4); + test_assert(l4->prev == l3 && l4->next == NULL); + /* remove from tail */ + DLLIST2_PREPEND(&head, &tail, l1); + DLLIST2_REMOVE(&head, &tail, l4); + test_assert(l4->prev == NULL && l4->next == NULL); + test_assert(head == l1 && tail == l3); + test_assert(l1->prev == NULL && l1->next == l3); + test_assert(l3->prev == l1 && l3->next == NULL); + /* removal of an entry not in the list shouldn't cause the list to break */ + DLLIST2_REMOVE(&head, &tail, &empty); + test_assert(head == l1); + test_assert(head == l1 && tail == l3); + test_assert(l1->prev == NULL && l1->next == l3); + test_assert(l3->prev == l1 && l3->next == NULL); + /* remove last two */ + DLLIST2_REMOVE(&head, &tail, l1); + DLLIST2_REMOVE(&head, &tail, l3); + test_assert(l3->prev == NULL && l3->next == NULL); + test_assert(head == NULL && tail == NULL); + test_end(); +} + +void test_llist(void) +{ + test_dllist(); + test_dllist2(); +} diff --git a/src/lib/test-log-throttle.c b/src/lib/test-log-throttle.c new file mode 100644 index 0000000..430aba0 --- /dev/null +++ b/src/lib/test-log-throttle.c @@ -0,0 +1,57 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "ioloop.h" +#include "log-throttle.h" + +static unsigned int test_log_throttle_new_events_count; + +static void test_log_throttle_callback(unsigned int new_events_count, + struct ioloop *ioloop) +{ + test_log_throttle_new_events_count = new_events_count; + io_loop_stop(ioloop); +} + +void test_log_throttle(void) +{ + const struct log_throttle_settings set = { + .throttle_at_max_per_interval = 10, + .unthrottle_at_max_per_interval = 5, + .interval_msecs = 10, + }; + struct log_throttle *throttle; + struct ioloop *ioloop; + unsigned int i; + + test_begin("log throttle"); + + ioloop = io_loop_create(); + throttle = log_throttle_init(&set, test_log_throttle_callback, ioloop); + + /* throttle once and drop out just below */ + for (i = 0; i < 10; i++) + test_assert_idx(log_throttle_accept(throttle), i); + for (i = 0; i < 4; i++) + test_assert_idx(!log_throttle_accept(throttle), i); + + io_loop_run(ioloop); + test_assert(test_log_throttle_new_events_count == 4); + + /* throttle and continue just above */ + for (i = 0; i < 10; i++) + test_assert_idx(log_throttle_accept(throttle), i); + for (i = 0; i < 5; i++) + test_assert_idx(!log_throttle_accept(throttle), i); + + io_loop_run(ioloop); + test_assert(test_log_throttle_new_events_count == 5); + + /* we should be still throttled */ + test_assert(!log_throttle_accept(throttle)); + + log_throttle_deinit(&throttle); + io_loop_destroy(&ioloop); + + test_end(); +} diff --git a/src/lib/test-macros.c b/src/lib/test-macros.c new file mode 100644 index 0000000..4b9e1b1 --- /dev/null +++ b/src/lib/test-macros.c @@ -0,0 +1,63 @@ +/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" + +struct parent { + unsigned int a; +}; +struct child { + unsigned int b; + struct parent p; +}; + +static void test_container_of(void) +{ + struct child child; + struct parent *parent = &child.p; + + test_begin("container_of()"); + struct child *ptr_child = container_of(parent, struct child, p); + test_assert(ptr_child == &child); + test_end(); +} + +static void test_pointer_cast(void) +{ +#define TEST_POINTER_CAST(type, prefix, value) \ + type prefix ## _num = value; \ + void *prefix ## _ptr = POINTER_CAST(prefix ## _num); \ + test_assert(POINTER_CAST_TO(prefix ## _ptr, type) == prefix ## _num); + test_begin("POINTER_CAST"); + + TEST_POINTER_CAST(unsigned int, uint, 0x87654321); + TEST_POINTER_CAST(uint32_t, uint32, 0xf00dabcd); + TEST_POINTER_CAST(uint16_t, uint16, 0x9876); + TEST_POINTER_CAST(uint8_t, uint8, 0xf8); +#if SIZEOF_VOID_P == 8 + TEST_POINTER_CAST(unsigned long, ulong, 0xfedcba9876543210); + TEST_POINTER_CAST(size_t, size, 0xfedcba9876543210); +#else + TEST_POINTER_CAST(unsigned long, ulong, 0xfedcba98); + TEST_POINTER_CAST(size_t, size, 0xfedcba98); +#endif + + test_end(); +} + +static void test_ptr_offset(void) +{ + uint32_t foo[] = { 1, 2, 3 }; + const uint32_t foo2[] = { 1, 2, 3 }; + + test_begin("PTR_OFFSET"); + test_assert(PTR_OFFSET(foo, 4) == &foo[1]); + test_assert(CONST_PTR_OFFSET(foo2, 8) == &foo2[2]); + test_end(); +} + +void test_macros(void) +{ + test_container_of(); + test_pointer_cast(); + test_ptr_offset(); +} diff --git a/src/lib/test-malloc-overflow.c b/src/lib/test-malloc-overflow.c new file mode 100644 index 0000000..ceb48bb --- /dev/null +++ b/src/lib/test-malloc-overflow.c @@ -0,0 +1,132 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" + +static void test_malloc_overflow_multiply(void) +{ + static const struct { + size_t a, b; + } tests[] = { + { 0, SIZE_MAX }, + { 1, SIZE_MAX }, + { SIZE_MAX/2, 2 }, + }; + test_begin("MALLOC_MULTIPLY()"); + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { + test_assert_idx(MALLOC_MULTIPLY(tests[i].a, tests[i].b) == tests[i].a * tests[i].b, i); + test_assert_idx(MALLOC_MULTIPLY(tests[i].b, tests[i].a) == tests[i].b * tests[i].a, i); + } + test_end(); +} + +static void test_malloc_overflow_add(void) +{ + static const struct { + size_t a, b; + } tests[] = { + { 0, SIZE_MAX }, + { 1, SIZE_MAX-1 }, + { SIZE_MAX/2+1, SIZE_MAX/2 }, + }; + unsigned short n = 2; + + test_begin("MALLOC_ADD()"); + /* check that no compiler warning is given */ + test_assert(MALLOC_ADD(2, n) == 2U+n); + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { + test_assert_idx(MALLOC_ADD(tests[i].a, tests[i].b) == tests[i].a + tests[i].b, i); + test_assert_idx(MALLOC_ADD(tests[i].b, tests[i].a) == tests[i].b + tests[i].a, i); + } + test_end(); +} + +void test_malloc_overflow(void) +{ + test_malloc_overflow_multiply(); + test_malloc_overflow_add(); +} + +static enum fatal_test_state fatal_malloc_overflow_multiply(unsigned int *stage) +{ + const struct { + size_t a, b; + } mul_tests[] = { + { SIZE_MAX/2+1, 2 }, + }; + unsigned int i; + + test_expect_fatal_string("memory allocation overflow"); + switch (*stage) { + case 0: + test_begin("MALLOC_MULTIPLY() overflows"); + i_error("%zu", MALLOC_MULTIPLY((size_t)SIZE_MAX/2, (uint8_t)3)); + break; + case 1: + i_error("%zu", MALLOC_MULTIPLY((uint8_t)3, (size_t)SIZE_MAX/2)); + break; + } + *stage -= 2; + + if (*stage >= N_ELEMENTS(mul_tests)*2) { + *stage -= N_ELEMENTS(mul_tests)*2; + if (*stage == 0) + test_end(); + test_expect_fatal_string(NULL); + return FATAL_TEST_FINISHED; + } + i = *stage / 2; + + if (*stage % 2 == 0) + i_error("%zu", MALLOC_MULTIPLY(mul_tests[i].a, mul_tests[i].b)); + else + i_error("%zu", MALLOC_MULTIPLY(mul_tests[i].b, mul_tests[i].a)); + return FATAL_TEST_FAILURE; +} + +static enum fatal_test_state fatal_malloc_overflow_add(unsigned int *stage) +{ + const struct { + size_t a, b; + } add_tests[] = { + { SIZE_MAX, 1 }, + { SIZE_MAX/2+1, SIZE_MAX/2+1 }, + }; + unsigned int i; + + test_expect_fatal_string("memory allocation overflow"); + switch (*stage) { + case 0: + test_begin("MALLOC_ADD() overflows"); + i_error("%zu", MALLOC_ADD((size_t)SIZE_MAX, (uint8_t)1)); + break; + case 1: + i_error("%zu", MALLOC_ADD((uint8_t)1, (size_t)SIZE_MAX)); + break; + } + *stage -= 2; + + if (*stage >= N_ELEMENTS(add_tests)*2) { + *stage -= N_ELEMENTS(add_tests)*2; + if (*stage == 0) + test_end(); + test_expect_fatal_string(NULL); + return FATAL_TEST_FINISHED; + } + i = *stage / 2; + + if (*stage % 2 == 0) + i_error("%zu", MALLOC_ADD(add_tests[i].a, add_tests[i].b)); + else + i_error("%zu", MALLOC_ADD(add_tests[i].b, add_tests[i].a)); + return FATAL_TEST_FAILURE; +} + +enum fatal_test_state fatal_malloc_overflow(unsigned int stage) +{ + enum fatal_test_state state; + + state = fatal_malloc_overflow_multiply(&stage); + if (state != FATAL_TEST_FINISHED) + return state; + return fatal_malloc_overflow_add(&stage); +} diff --git a/src/lib/test-memarea.c b/src/lib/test-memarea.c new file mode 100644 index 0000000..9a851dc --- /dev/null +++ b/src/lib/test-memarea.c @@ -0,0 +1,42 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "buffer.h" +#include "memarea.h" + +static bool test_callback_called = FALSE; + +static void test_callback(buffer_t *buf) +{ + test_assert(!test_callback_called); + test_callback_called = TRUE; + buffer_free(&buf); +} + +void test_memarea(void) +{ + struct memarea *area, *area2; + buffer_t *buf; + size_t size; + + test_begin("memarea"); + buf = buffer_create_dynamic(default_pool, 128); + buffer_append(buf, "123", 3); + + area = memarea_init(buf->data, buf->used, test_callback, buf); + test_assert(memarea_get_refcount(area) == 1); + test_assert(memarea_get(area, &size) == buf->data && size == buf->used); + + area2 = area; + memarea_ref(area2); + test_assert(memarea_get_refcount(area2) == 2); + test_assert(memarea_get(area2, &size) == buf->data && size == buf->used); + memarea_unref(&area2); + test_assert(area2 == NULL); + test_assert(!test_callback_called); + + memarea_unref(&area); + test_assert(test_callback_called); + + test_end(); +} diff --git a/src/lib/test-mempool-allocfree.c b/src/lib/test-mempool-allocfree.c new file mode 100644 index 0000000..0eb330b --- /dev/null +++ b/src/lib/test-mempool-allocfree.c @@ -0,0 +1,128 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" + +#define SENSE 0xAB /* produces 10101011 */ + +static bool mem_has_bytes(const void *mem, size_t size, uint8_t b) +{ + const uint8_t *bytes = mem; + unsigned int i; + + for (i = 0; i < size; i++) { + if (bytes[i] != b) { + i_debug("bytes[%u] != %u", i, b); + return FALSE; + } + } + return TRUE; +} + +void test_mempool_allocfree(void) +{ + pool_t pool; + unsigned int i; + size_t last_alloc = 0; + size_t used = 0; + void *mem = NULL; + + test_begin("mempool_allocfree"); + pool = pool_allocfree_create("test"); + + for(i = 0; i <= 1000; i++) { + /* release previous allocation */ + if ((i % 3) == 0) { + if (mem != NULL) { + test_assert_idx(mem_has_bytes(mem, last_alloc, SENSE), i); + used -= last_alloc; + } + last_alloc = 0; + p_free(pool, mem); + /* grow previous allocation */ + } else if ((i % 5) == 0) { + if (mem != NULL) + used -= last_alloc; + mem = p_realloc(pool, mem, last_alloc, i*2); + if (last_alloc > 0) + test_assert_idx(mem_has_bytes(mem, last_alloc, SENSE), i); + memset(mem, SENSE, i*2); + last_alloc = i*2; + used += i*2; + /* shrink previous allocation */ + } else if ((i % 7) == 0) { + if (mem != NULL) + used -= last_alloc; + mem = p_realloc(pool, mem, last_alloc, i-2); + if (last_alloc > 0) + test_assert_idx(mem_has_bytes(mem, i-2, SENSE), i); + memset(mem, SENSE, i-2); + last_alloc = i-2; + used += i-2; + /* allocate some memory */ + } else { + mem = p_malloc(pool, i); + /* fill it with sense marker */ + memset(mem, SENSE, i); + used += i; + last_alloc = i; + } + } + + test_assert(pool_allocfree_get_total_used_size(pool) == used); + + pool_unref(&pool); + + /* make sure realloc works correctly */ + pool = pool_allocfree_create("test"); + mem = NULL; + + for(i = 1; i < 1000; i++) { + mem = p_realloc(pool, mem, i-1, i); + test_assert_idx(mem_has_bytes(mem, i-1, 0xde), i); + memset(mem, 0xde, i); + } + + pool_unref(&pool); + + test_end(); +} + +enum fatal_test_state fatal_mempool_allocfree(unsigned int stage) +{ + static pool_t pool; + + if (pool == NULL && stage != 0) + return FATAL_TEST_FAILURE; + + switch(stage) { + case 0: /* forbidden size */ + test_begin("fatal_mempool_allocfree"); + pool = pool_allocfree_create("fatal"); + test_expect_fatal_string("Trying to allocate 0 bytes"); + (void)p_malloc(pool, 0); + return FATAL_TEST_FAILURE; + + case 1: /* logically impossible size */ + test_expect_fatal_string("Trying to allocate"); + (void)p_malloc(pool, POOL_MAX_ALLOC_SIZE + 1ULL); + return FATAL_TEST_FAILURE; + +#ifdef _LP64 /* malloc(POOL_MAX_ALLOC_SIZE) may succeed with 32bit */ + case 2: /* physically impossible size */ + test_expect_fatal_string("Out of memory"); + (void)p_malloc(pool, POOL_MAX_ALLOC_SIZE); + return FATAL_TEST_FAILURE; +#endif + + /* Continue with other tests as follows: + case 3: + something_fatal(); + return FATAL_TEST_FAILURE; + */ + } + + /* Either our tests have finished, or the test suite has got confused. */ + pool_unref(&pool); + test_end(); + return FATAL_TEST_FINISHED; +} diff --git a/src/lib/test-mempool-alloconly.c b/src/lib/test-mempool-alloconly.c new file mode 100644 index 0000000..c3b0001 --- /dev/null +++ b/src/lib/test-mempool-alloconly.c @@ -0,0 +1,94 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" + +static bool mem_has_bytes(const void *mem, size_t size, uint8_t b) +{ + const uint8_t *bytes = mem; + unsigned int i; + + for (i = 0; i < size; i++) { + if (bytes[i] != b) + return FALSE; + } + return TRUE; +} + +void test_mempool_alloconly(void) +{ +#define SENTRY_SIZE 32 +#define SENTRY_CHAR 0xDE +#define PMALLOC_MAX_COUNT 128 + pool_t pool; + unsigned int i, j, k; + void *mem[PMALLOC_MAX_COUNT + 1]; + char *sentry; + + test_begin("mempool_alloconly"); + for (i = 0; i < 64; i++) { + for (j = 1; j <= 128; j++) { + pool = pool_alloconly_create(MEMPOOL_GROWING"test", i); + /* make sure p_malloc() doesn't overwrite unallocated + data in data stack. parts of the code relies on + this. */ + sentry = t_buffer_get(SENTRY_SIZE); + memset(sentry, SENTRY_CHAR, SENTRY_SIZE); + + mem[0] = p_malloc(pool, j); + memset(mem[0], j, j); + + for (k = 1; k <= PMALLOC_MAX_COUNT; k++) { + mem[k] = p_malloc(pool, k); + memset(mem[k], k, k); + } + test_assert(mem_has_bytes(sentry, SENTRY_SIZE, SENTRY_CHAR)); + test_assert(t_buffer_get(SENTRY_SIZE) == sentry); + + test_assert(mem_has_bytes(mem[0], j, j)); + for (k = 1; k <= PMALLOC_MAX_COUNT; k++) + test_assert(mem_has_bytes(mem[k], k, k)); + pool_unref(&pool); + } + } + test_end(); +} + +enum fatal_test_state fatal_mempool_alloconly(unsigned int stage) +{ + static pool_t pool; + + if (pool == NULL && stage != 0) + return FATAL_TEST_FAILURE; + + switch(stage) { + case 0: /* forbidden size */ + test_begin("fatal_mempool_alloconly"); + pool = pool_alloconly_create(MEMPOOL_GROWING"fatal", 1); + test_expect_fatal_string("Trying to allocate 0 bytes"); + (void)p_malloc(pool, 0); + return FATAL_TEST_FAILURE; + + case 1: /* logically impossible size */ + test_expect_fatal_string("Trying to allocate"); + (void)p_malloc(pool, POOL_MAX_ALLOC_SIZE + 1ULL); + return FATAL_TEST_FAILURE; + +#ifdef _LP64 /* malloc(POOL_MAX_ALLOC_SIZE) may succeed with 32bit */ + case 2: /* physically impossible size */ + test_expect_fatal_string("Out of memory"); + (void)p_malloc(pool, POOL_MAX_ALLOC_SIZE); + return FATAL_TEST_FAILURE; +#endif + + /* Continue with other tests as follows: + case 3: + something_fatal(); + return FATAL_TEST_FAILURE; + */ + } + + /* Either our tests have finished, or the test suite has got confused. */ + pool_unref(&pool); + test_end(); + return FATAL_TEST_FINISHED; +} diff --git a/src/lib/test-mempool.c b/src/lib/test-mempool.c new file mode 100644 index 0000000..7c707dd --- /dev/null +++ b/src/lib/test-mempool.c @@ -0,0 +1,117 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" + +#if SIZEOF_VOID_P == 8 +typedef char uint32max_array_t[4294967295]; +#else +typedef char uint32max_array_t[65535]; +#endif + +#define BIG_MAX POOL_MAX_ALLOC_SIZE + +#if defined(_LP64) +#define LITTLE_MAX ((unsigned long long) INT32_MAX) +#else +#define LITTLE_MAX ((unsigned long long) INT16_MAX) +#endif + +extern struct pool test_pool; + +/* Checks allocations & reallocations for a given type. */ +#define CHECK_OVERFLOW(type, nelem, _maxsize) \ + do { \ + const size_t maxsize = (_maxsize); \ + test_begin("mempool overflow - " #type); \ + type *ptr = p_new(&test_pool, type, (nelem)); \ + test_assert(ptr == POINTER_CAST(maxsize)); \ + /* grow: */ \ + test_assert(p_realloc_type(&test_pool, ptr, type, (nelem) - 1, (nelem)) == POINTER_CAST(maxsize)); \ + /* shrink: */ \ + test_assert(p_realloc_type(&test_pool, ptr, type, (nelem), (nelem) - 1) == POINTER_CAST(maxsize - sizeof(type))); \ + test_end(); \ + } while (0) + +static void test_mempool_overflow(void) +{ + CHECK_OVERFLOW(uint32max_array_t, LITTLE_MAX, sizeof(uint32max_array_t) * LITTLE_MAX); + CHECK_OVERFLOW(char, BIG_MAX, BIG_MAX); + CHECK_OVERFLOW(uint32_t, BIG_MAX / sizeof(uint32_t), BIG_MAX - 3); +} + +enum fatal_test_state fatal_mempool(unsigned int stage) +{ + static uint32max_array_t *m1; + static uint32_t *m2; + + switch(stage) { + case 0: + test_expect_fatal_string("Trying to allocate"); + test_begin("fatal mempool overflow"); + m1 = p_new(&test_pool, uint32max_array_t, LITTLE_MAX + 3); + return FATAL_TEST_FAILURE; + case 1: + test_expect_fatal_string("Trying to allocate"); + m2 = p_new(&test_pool, uint32_t, BIG_MAX / sizeof(uint32_t) + 1); + return FATAL_TEST_FAILURE; + case 2: /* grow */ + test_expect_fatal_string("Trying to reallocate"); + m1 = p_realloc_type(&test_pool, m1, uint32max_array_t, + LITTLE_MAX + 2, LITTLE_MAX + 3); + return FATAL_TEST_FAILURE; + case 3: + test_expect_fatal_string("Trying to reallocate"); + m2 = p_realloc_type(&test_pool, m2, uint32_t, + BIG_MAX / sizeof(uint32_t), + BIG_MAX / sizeof(uint32_t) + 1); + return FATAL_TEST_FAILURE; + case 4: /* shrink */ + test_expect_fatal_string("Trying to reallocate"); + m1 = p_realloc_type(&test_pool, m1, uint32max_array_t, + LITTLE_MAX + 3, LITTLE_MAX + 2); + return FATAL_TEST_FAILURE; + case 5: + test_expect_fatal_string("Trying to reallocate"); + m2 = p_realloc_type(&test_pool, m2, uint32_t, + BIG_MAX / sizeof(uint32_t) + 2, + BIG_MAX / sizeof(uint32_t) + 1); + return FATAL_TEST_FAILURE; + } + test_expect_fatal_string(NULL); + test_end(); + return FATAL_TEST_FINISHED; +} + +static const char *pool_test_get_name(pool_t pool ATTR_UNUSED) { return "test"; } +static void pool_test_ref(pool_t pool ATTR_UNUSED) { } +static void pool_test_unref(pool_t *pool) { *pool = NULL; } +static void *pool_test_malloc(pool_t pool ATTR_UNUSED, size_t size) { return POINTER_CAST(size); } +static void pool_test_free(pool_t pool ATTR_UNUSED, void *mem ATTR_UNUSED) { } +static void *pool_test_realloc(pool_t pool ATTR_UNUSED, void *mem ATTR_UNUSED, + size_t old_size ATTR_UNUSED, size_t new_size) { + return POINTER_CAST(new_size); +} +static void pool_test_clear(pool_t pool ATTR_UNUSED) { } +static size_t pool_test_get_max_easy_alloc_size(pool_t pool ATTR_UNUSED) { return 12345; } +static const struct pool_vfuncs test_pool_vfuncs = { + pool_test_get_name, + pool_test_ref, + pool_test_unref, + pool_test_malloc, + pool_test_free, + pool_test_realloc, + pool_test_clear, + pool_test_get_max_easy_alloc_size +}; + +struct pool test_pool = { + .v = &test_pool_vfuncs, + + .alloconly_pool = TRUE, + .datastack_pool = FALSE, +}; + +void test_mempool(void) +{ + test_mempool_overflow(); +} diff --git a/src/lib/test-multiplex.c b/src/lib/test-multiplex.c new file mode 100644 index 0000000..7f36094 --- /dev/null +++ b/src/lib/test-multiplex.c @@ -0,0 +1,167 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "ioloop.h" +#include "str.h" +#include "istream.h" +#include "istream-multiplex.h" +#include "ostream.h" +#include "ostream-multiplex.h" +#include "ostream.h" +#include "randgen.h" + +#include <unistd.h> + +struct test_channel { + int fds[2]; + unsigned int cid; + + struct istream *in; + struct ostream *out; + struct io *io; + + struct istream *in_alt; + struct ostream *out_alt; + struct io *io_alt; + + buffer_t *received; + buffer_t *received_alt; + + unsigned int counter; +}; + +static struct test_channel test_channel[2]; + +static void test_multiplex_channel_write(struct test_channel *channel) +{ + unsigned char buf[128]; + size_t len = i_rand_limit(sizeof(buf)); + random_fill(buf, len); + o_stream_nsend(channel->out, buf, len); + o_stream_nsend(channel->out_alt, buf, len); +} + +static void test_multiplex_stream_write(struct ostream *channel ATTR_UNUSED) +{ + if (test_channel[0].received->used > 1000 && + test_channel[1].received->used > 1000) + io_loop_stop(current_ioloop); + else + test_multiplex_channel_write(&test_channel[i_rand_limit(2)]); +} + +static void test_istream_multiplex_stream_read(struct test_channel *channel) +{ + const unsigned char *data = NULL; + size_t siz = 0; + + if (i_stream_read(channel->in) > 0) { + data = i_stream_get_data(channel->in, &siz); + buffer_append(channel->received, data, siz); + i_stream_skip(channel->in, siz); + } +} + +static void test_istream_read_alt(struct test_channel *channel) +{ + const unsigned char *data = NULL; + size_t siz = 0; + + if (i_stream_read(channel->in_alt) > 0) { + data = i_stream_get_data(channel->in_alt, &siz); + buffer_append(channel->received_alt, data, siz); + i_stream_skip(channel->in_alt, siz); + } +} + +static void setup_channel(struct test_channel *channel, + struct istream *is, struct ostream *os) +{ + /* setup first channel */ + channel->in = is; + channel->out = os; + channel->io = io_add_istream(is, test_istream_multiplex_stream_read, + channel); + test_assert(pipe(channel->fds) == 0); + fd_set_nonblock(channel->fds[0], TRUE); + fd_set_nonblock(channel->fds[1], TRUE); + channel->in_alt = i_stream_create_fd(channel->fds[0], SIZE_MAX); + channel->out_alt = o_stream_create_fd(channel->fds[1], IO_BLOCK_SIZE); + channel->io_alt = io_add_istream(channel->in_alt, test_istream_read_alt, + channel); + channel->received = buffer_create_dynamic(default_pool, 32768); + channel->received_alt = buffer_create_dynamic(default_pool, 32768); +} + +static void teardown_channel(struct test_channel *channel) +{ + test_istream_read_alt(channel); + test_assert(memcmp(channel->received->data, + channel->received_alt->data, + channel->received->used) == 0); + test_assert(channel->received->used == channel->received_alt->used); + + buffer_free(&channel->received); + buffer_free(&channel->received_alt); + + io_remove(&channel->io); + io_remove(&channel->io_alt); + i_stream_unref(&channel->in); + test_assert(o_stream_finish(channel->out) > 0); + o_stream_unref(&channel->out); + i_stream_unref(&channel->in_alt); + test_assert(o_stream_finish(channel->out_alt) > 0); + o_stream_unref(&channel->out_alt); + i_close_fd(&channel->fds[0]); + i_close_fd(&channel->fds[1]); +} + +static void test_multiplex_stream(void) { + test_begin("test multiplex (stream)"); + + struct ioloop *ioloop = io_loop_create(); + io_loop_set_current(ioloop); + + int fds[2]; + test_assert(pipe(fds) == 0); + fd_set_nonblock(fds[0], TRUE); + fd_set_nonblock(fds[1], TRUE); + struct ostream *os = o_stream_create_fd(fds[1], SIZE_MAX); + struct istream *is = i_stream_create_fd(fds[0], SIZE_MAX); + + struct istream *ichan0 = i_stream_create_multiplex(is, SIZE_MAX); + struct istream *ichan1 = i_stream_multiplex_add_channel(ichan0, 1); + i_stream_unref(&is); + + struct ostream *ochan0 = o_stream_create_multiplex(os, 1024); + struct ostream *ochan1 = o_stream_multiplex_add_channel(ochan0, 1); + o_stream_unref(&os); + + struct io *io = io_add(fds[1], IO_WRITE, test_multiplex_stream_write, os); + + setup_channel(&test_channel[0], ichan0, ochan0); + setup_channel(&test_channel[1], ichan1, ochan1); + + test_channel[0].cid = 0; + test_channel[1].cid = 1; + + io_loop_run(current_ioloop); + + io_remove(&io); + + teardown_channel(&test_channel[0]); + teardown_channel(&test_channel[1]); + + io_loop_destroy(&ioloop); + + i_close_fd(&fds[0]); + i_close_fd(&fds[1]); + + test_end(); +} + +void test_multiplex(void) { + random_init(); + test_multiplex_stream(); + random_deinit(); +} diff --git a/src/lib/test-net.c b/src/lib/test-net.c new file mode 100644 index 0000000..fb19d5b --- /dev/null +++ b/src/lib/test-net.c @@ -0,0 +1,167 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "net.h" + +struct test_net_is_in_network_input { + const char *ip; + const char *net; + unsigned int bits; + bool ret; +}; + +static void test_net_is_in_network(void) +{ + static const struct test_net_is_in_network_input input[] = { + { "1.2.3.4", "1.2.3.4", 32, TRUE }, + { "1.2.3.4", "1.2.3.3", 32, FALSE }, + { "1.2.3.4", "1.2.3.5", 32, FALSE }, + { "1.2.3.4", "1.2.2.4", 32, FALSE }, + { "1.2.3.4", "1.1.3.4", 32, FALSE }, + { "1.2.3.4", "0.2.3.4", 32, FALSE }, + { "1.2.3.253", "1.2.3.254", 31, FALSE }, + { "1.2.3.254", "1.2.3.254", 31, TRUE }, + { "1.2.3.255", "1.2.3.254", 31, TRUE }, + { "1.2.3.255", "1.2.3.0", 24, TRUE }, + { "1.2.255.255", "1.2.254.0", 23, TRUE }, + { "255.255.255.255", "128.0.0.0", 1, TRUE }, + { "255.255.255.255", "127.0.0.0", 1, FALSE }, + { "1234:5678::abcf", "1234:5678::abce", 127, TRUE }, + { "1234:5678::abcd", "1234:5678::abce", 127, FALSE }, + { "123e::ffff", "123e::0", 15, TRUE }, + { "::ffff:1.2.3.4", "1.2.3.4", 32, TRUE }, + { "::ffff:1.2.3.4", "1.2.3.3", 32, FALSE }, + { "::ffff:1.2.3.4", "::ffff:1.2.3.4", 0, FALSE } + }; + struct ip_addr ip, net_ip; + unsigned int i; + + test_begin("net_is_in_network()"); + for (i = 0; i < N_ELEMENTS(input); i++) { + test_assert(net_addr2ip(input[i].ip, &ip) == 0); + test_assert(net_addr2ip(input[i].net, &net_ip) == 0); + test_assert_idx(net_is_in_network(&ip, &net_ip, input[i].bits) == + input[i].ret, i); + } + /* make sure non-IPv4 and non-IPv6 ip_addrs fail */ + test_assert(net_addr2ip("127.0.0.1", &ip) == 0); + net_ip = ip; + net_ip.family = 0; + test_assert(!net_is_in_network(&ip, &net_ip, 0)); + test_assert(!net_is_in_network(&net_ip, &ip, 0)); + test_assert(net_addr2ip("::1", &ip) == 0); + net_ip = ip; + net_ip.family = 0; + test_assert(!net_is_in_network(&ip, &net_ip, 0)); + test_assert(!net_is_in_network(&net_ip, &ip, 0)); + test_end(); +} + +static void test_net_ip2addr(void) +{ + struct ip_addr ip; + + test_begin("net_ip2addr()"); + test_assert(net_addr2ip("127.0.0.1", &ip) == 0 && + ip.family == AF_INET && + ntohl(ip.u.ip4.s_addr) == (0x7f000001)); + test_assert(net_addr2ip("2130706433", &ip) == 0 && + ip.family == AF_INET && + ntohl(ip.u.ip4.s_addr) == (0x7f000001)); + test_assert(strcmp(net_ip2addr(&ip), "127.0.0.1") == 0); + test_assert(net_addr2ip("255.254.253.252", &ip) == 0 && + ip.family == AF_INET && + ntohl(ip.u.ip4.s_addr) == (0xfffefdfc)); + test_assert(strcmp(net_ip2addr(&ip), "255.254.253.252") == 0); + test_assert(net_addr2ip("::5", &ip) == 0 && + ip.family == AF_INET6 && + ip.u.ip6.s6_addr[15] == 5); + test_assert(strcmp(net_ip2addr(&ip), "::5") == 0); + test_assert(net_addr2ip("[::5]", &ip) == 0 && + ip.family == AF_INET6 && + ip.u.ip6.s6_addr[15] == 5); + test_assert(strcmp(net_ip2addr(&ip), "::5") == 0); + ip.family = 123; + test_assert(net_addr2ip("abc", &ip) < 0 && + ip.family == 123); + test_end(); +} + +static void test_net_str2hostport(void) +{ + const char *host; + in_port_t port; + + test_begin("net_str2hostport()"); + /* [IPv6] */ + test_assert(net_str2hostport("[1::4]", 0, &host, &port) == 0 && + strcmp(host, "1::4") == 0 && port == 0); + test_assert(net_str2hostport("[1::4]", 1234, &host, &port) == 0 && + strcmp(host, "1::4") == 0 && port == 1234); + test_assert(net_str2hostport("[1::4]:78", 1234, &host, &port) == 0 && + strcmp(host, "1::4") == 0 && port == 78); + host = NULL; + test_assert(net_str2hostport("[1::4]:", 1234, &host, &port) < 0 && host == NULL); + test_assert(net_str2hostport("[1::4]:0", 1234, &host, &port) < 0 && host == NULL); + test_assert(net_str2hostport("[1::4]:x", 1234, &host, &port) < 0 && host == NULL); + /* IPv6 */ + test_assert(net_str2hostport("1::4", 0, &host, &port) == 0 && + strcmp(host, "1::4") == 0 && port == 0); + test_assert(net_str2hostport("1::4", 1234, &host, &port) == 0 && + strcmp(host, "1::4") == 0 && port == 1234); + /* host */ + test_assert(net_str2hostport("foo", 0, &host, &port) == 0 && + strcmp(host, "foo") == 0 && port == 0); + test_assert(net_str2hostport("foo", 1234, &host, &port) == 0 && + strcmp(host, "foo") == 0 && port == 1234); + test_assert(net_str2hostport("foo:78", 1234, &host, &port) == 0 && + strcmp(host, "foo") == 0 && port == 78); + host = NULL; + test_assert(net_str2hostport("foo:", 1234, &host, &port) < 0 && host == NULL); + test_assert(net_str2hostport("foo:0", 1234, &host, &port) < 0 && host == NULL); + test_assert(net_str2hostport("foo:x", 1234, &host, &port) < 0 && host == NULL); + /* edge cases with multiple ':' - currently these don't return errors, + but perhaps they should. */ + test_assert(net_str2hostport("foo::78", 1234, &host, &port) == 0 && + strcmp(host, "foo::78") == 0 && port == 1234); + test_assert(net_str2hostport("::foo:78", 1234, &host, &port) == 0 && + strcmp(host, "::foo:78") == 0 && port == 1234); + test_assert(net_str2hostport("[::foo]:78", 1234, &host, &port) == 0 && + strcmp(host, "::foo") == 0 && port == 78); + test_assert(net_str2hostport("[::::]", 1234, &host, &port) == 0 && + strcmp(host, "::::") == 0 && port == 1234); + test_assert(net_str2hostport("[::::]:78", 1234, &host, &port) == 0 && + strcmp(host, "::::") == 0 && port == 78); + test_end(); +} + +static void test_net_unix_long_paths(void) +{ +#ifdef ENAMETOOLONG + int long_errno = ENAMETOOLONG; +#else + int long_errno = EOVERFLOW; +#endif + + test_begin("net_*_unix() - long paths"); + + char path[PATH_MAX]; + memset(path, 'x', sizeof(path)-1); + path[sizeof(path)-1] = '\0'; + + test_assert(net_listen_unix(path, 1) == -1); + test_assert(errno == long_errno); + + test_assert(net_connect_unix(path) == -1); + test_assert(errno == long_errno); + + test_end(); +} + +void test_net(void) +{ + test_net_is_in_network(); + test_net_ip2addr(); + test_net_str2hostport(); + test_net_unix_long_paths(); +} diff --git a/src/lib/test-numpack.c b/src/lib/test-numpack.c new file mode 100644 index 0000000..9da8bb2 --- /dev/null +++ b/src/lib/test-numpack.c @@ -0,0 +1,64 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "buffer.h" +#include "numpack.h" + + +static const struct test { + uint64_t input; + uint8_t output[10]; + unsigned int output_size; +} enc_tests[] = { + { 0xffffffff, { 0xff, 0xff, 0xff, 0xff, 0xf }, 5 }, + { 0, { 0 }, 1 }, + { 0x7f, { 0x7f }, 1 }, + { 0x80, { 0x80, 1 }, 2 }, + { 0x81, { 0x81, 1 }, 2 }, + { 0xdeadbeefcafe, { 0xfe, 0x95, 0xbf, 0xf7, 0xdb, 0xd5, 0x37 }, 7 }, + { 0xffffffffffffffff, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1 }, 10 }, + { 0xfffffffe, { 0xfe, 0xff, 0xff, 0xff, 0xf }, 5 }, +}; +static const struct fail { + uint8_t input[11]; + unsigned int input_size; +} dec_fails[] = { + { { 0 }, 0 }, /* has no termination byte */ + { { 0x80 }, 1 }, /* ditto */ + { { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, 10 }, /* ditto*/ + { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2 }, 10 }, /* overflow */ + { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f }, 11 }, /* ditto */ +}; + +void test_numpack(void) +{ + buffer_t *buf = t_buffer_create(32); + unsigned int i; + const uint8_t *p, *end; + uint64_t num; + uint64_t magic=0x9669699669969669; + + test_begin("numpack (good)"); + for (i = 0; i < N_ELEMENTS(enc_tests); i++) { + buffer_set_used_size(buf, 0); + numpack_encode(buf, enc_tests[i].input); + test_assert_idx(buf->used == enc_tests[i].output_size, i); + test_assert_idx(memcmp(buf->data, enc_tests[i].output, + enc_tests[i].output_size) == 0, + i); + + p = buf->data; end = p + buf->used; + test_assert_idx(numpack_decode(&p, end, &num) == 0, i); + test_assert_idx(num == enc_tests[i].input, i); + } + test_end(); + + test_begin("numpack (bad)"); + for (i = 0; i < N_ELEMENTS(dec_fails); i++) { + p = dec_fails[i].input; end = p + dec_fails[i].input_size; + num = magic; + test_assert_idx(numpack_decode(&p, end, &num) == -1, i); + test_assert_idx(p == dec_fails[i].input && num == magic, i); + } + test_end(); +} diff --git a/src/lib/test-ostream-buffer.c b/src/lib/test-ostream-buffer.c new file mode 100644 index 0000000..4a449f6 --- /dev/null +++ b/src/lib/test-ostream-buffer.c @@ -0,0 +1,108 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "buffer.h" +#include "str.h" +#include "randgen.h" +#include "istream.h" +#include "ostream.h" + +#define MAX_BUFSIZE 256 + +static void test_ostream_buffer_random_once(void) +{ + buffer_t *buffer; + struct ostream *output; + char buf[MAX_BUFSIZE*4], randbuf[MAX_BUFSIZE]; + unsigned int i, offset, size; + + buffer = buffer_create_dynamic(default_pool, 8); + + memset(buf, 0, sizeof(buf)); + + output = o_stream_create_buffer(buffer); + o_stream_cork(output); + + size = i_rand_minmax(1, MAX_BUFSIZE); + random_fill(randbuf, size); + memcpy(buf, randbuf, size); + test_assert(o_stream_send(output, buf, size) > 0); + + for (i = 0; i < 10; i++) { + offset = i_rand_limit(MAX_BUFSIZE * 3); + size = i_rand_minmax(1, MAX_BUFSIZE); + random_fill(randbuf, size); + memcpy(buf + offset, randbuf, size); + test_assert(o_stream_pwrite(output, randbuf, size, offset) == 0); + if (i_rand_limit(10) == 0) + test_assert(o_stream_flush(output) > 0); + } + + o_stream_uncork(output); + test_assert(o_stream_finish(output) > 0); + + i_assert(buffer->used <= MAX_BUFSIZE*4); + test_assert(memcmp(buf, buffer->data, buffer->used) == 0); + + o_stream_unref(&output); + buffer_free(&buffer); +} + +static void test_ostream_buffer_random(void) +{ + unsigned int i; + + test_begin("ostream buffer pwrite random"); + for (i = 0; i < 100; i++) T_BEGIN { + test_ostream_buffer_random_once(); + } T_END; + test_end(); +} + +static void test_ostream_buffer_size(void) +{ + struct ostream *output; + string_t *str = t_str_new(64); + + test_begin("ostream buffer size/available"); + output = o_stream_create_buffer(str); + test_assert(o_stream_get_buffer_used_size(output) == 0); + test_assert(o_stream_get_buffer_avail_size(output) == SIZE_MAX); + + /* test shrinking sink's max buffer size */ + o_stream_set_max_buffer_size(output, 10); + test_assert(o_stream_get_buffer_used_size(output) == 0); + test_assert(o_stream_get_buffer_avail_size(output) == 10); + + /* partial send */ + const char *partial_input = "01234567890123456789"; + ssize_t ret = o_stream_send_str(output, partial_input); + test_assert(ret == 10); + test_assert(o_stream_get_buffer_used_size(output) == 10); + test_assert(o_stream_get_buffer_avail_size(output) == 0); + + /* increase max buffer size so that it can hold the whole message */ + o_stream_set_max_buffer_size(output, 100); + test_assert(o_stream_get_buffer_used_size(output) == 10); + test_assert(o_stream_get_buffer_avail_size(output) == 90); + + /* send the rest */ + ret += o_stream_send_str(output, partial_input + ret); + test_assert(ret == (ssize_t)strlen(partial_input)); + test_assert(output->offset == str_len(str)); + test_assert(o_stream_get_buffer_used_size(output) == 20); + test_assert(o_stream_get_buffer_avail_size(output) == 80); + + /* check buffered data */ + test_assert(strcmp(str_c(str), partial_input) == 0); + + o_stream_unref(&output); + + test_end(); +} + +void test_ostream_buffer(void) +{ + test_ostream_buffer_random(); + test_ostream_buffer_size(); +} diff --git a/src/lib/test-ostream-failure-at.c b/src/lib/test-ostream-failure-at.c new file mode 100644 index 0000000..ba17966 --- /dev/null +++ b/src/lib/test-ostream-failure-at.c @@ -0,0 +1,52 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "buffer.h" +#include "ostream.h" +#include "ostream-failure-at.h" + +#define TEST_DATA_LENGTH 128 +#define TEST_ERRMSG "test-ostream-failure-at error triggered" + +void test_ostream_failure_at(void) +{ + unsigned char test_data[TEST_DATA_LENGTH]; + struct ostream *output, *buf_output; + buffer_t *buf = t_buffer_create(256); + unsigned int i; + + test_begin("ostream failure at"); + for (i = 0; i < sizeof(test_data); i++) + test_data[i] = i; + for (i = 0; i < TEST_DATA_LENGTH; i++) { + buf_output = o_stream_create_buffer(buf); + output = o_stream_create_failure_at(buf_output, i, TEST_ERRMSG); + if (i > 0) + test_assert(o_stream_send(output, test_data, sizeof(test_data)) == (int)i); + test_assert_idx(o_stream_send(output, test_data, sizeof(test_data)) == -1 && + output->offset == i && + output->stream_errno == EIO && + strcmp(o_stream_get_error(output), TEST_ERRMSG) == 0, i); + o_stream_destroy(&output); + o_stream_destroy(&buf_output); + } + /* shouldn't fail */ + buf_output = o_stream_create_buffer(buf); + output = o_stream_create_failure_at(buf_output, TEST_DATA_LENGTH, TEST_ERRMSG); + test_assert(o_stream_send(output, test_data, sizeof(test_data)) == TEST_DATA_LENGTH); + test_assert(o_stream_flush(output) > 0 && + output->offset == TEST_DATA_LENGTH && + output->stream_errno == 0); + o_stream_destroy(&output); + o_stream_destroy(&buf_output); + + /* fail at flush */ + buf_output = o_stream_create_buffer(buf); + output = o_stream_create_failure_at_flush(buf_output, TEST_ERRMSG); + test_assert(o_stream_send(output, test_data, sizeof(test_data)) == TEST_DATA_LENGTH); + test_assert(o_stream_flush(output) < 0 && output->stream_errno == EIO && + strcmp(o_stream_get_error(output), TEST_ERRMSG) == 0); + o_stream_destroy(&output); + o_stream_destroy(&buf_output); + test_end(); +} diff --git a/src/lib/test-ostream-file.c b/src/lib/test-ostream-file.c new file mode 100644 index 0000000..d4a9eff --- /dev/null +++ b/src/lib/test-ostream-file.c @@ -0,0 +1,189 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "net.h" +#include "str.h" +#include "safe-mkstemp.h" +#include "randgen.h" +#include "istream.h" +#include "ostream.h" + +#include <fcntl.h> +#include <unistd.h> + +#define MAX_BUFSIZE 256 + +static void test_ostream_file_random_once(void) +{ + struct ostream *output; + string_t *path = t_str_new(128); + char buf[MAX_BUFSIZE*4], buf2[MAX_BUFSIZE*4], randbuf[MAX_BUFSIZE]; + unsigned int i, offset, size; + ssize_t ret; + int fd; + + memset(buf, 0, sizeof(buf)); + fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); + if (fd == -1) + i_fatal("safe_mkstemp(%s) failed: %m", str_c(path)); + i_unlink(str_c(path)); + output = o_stream_create_fd(fd, MAX_BUFSIZE); + o_stream_cork(output); + + size = i_rand_minmax(1, MAX_BUFSIZE); + random_fill(randbuf, size); + memcpy(buf, randbuf, size); + test_assert(o_stream_send(output, buf, size) > 0); + + for (i = 0; i < 10; i++) { + offset = i_rand_limit(MAX_BUFSIZE * 3); + size = i_rand_minmax(1, MAX_BUFSIZE); + random_fill(randbuf, size); + memcpy(buf + offset, randbuf, size); + test_assert(o_stream_pwrite(output, randbuf, size, offset) == 0); + if (i_rand_limit(10) == 0) + test_assert(o_stream_flush(output) > 0); + } + + o_stream_uncork(output); + test_assert(o_stream_finish(output) > 0); + ret = pread(fd, buf2, sizeof(buf2), 0); + if (ret < 0) + i_fatal("pread() failed: %m"); + else { + i_assert(ret > 0); + test_assert(memcmp(buf, buf2, ret) == 0); + } + o_stream_unref(&output); + i_close_fd(&fd); +} + +static void test_ostream_file_random(void) +{ + unsigned int i; + + test_begin("ostream pwrite random"); + for (i = 0; i < 100; i++) T_BEGIN { + test_ostream_file_random_once(); + } T_END; + test_end(); +} + +static void test_ostream_file_send_istream_file(void) +{ + struct istream *input, *input2; + struct ostream *output; + char buf[10]; + int fd; + + test_begin("ostream file send istream file"); + + /* temp file istream */ + fd = open(".temp.istream", O_RDWR | O_CREAT | O_TRUNC, 0600); + if (fd == -1) + i_fatal("creat(.temp.istream) failed: %m"); + test_assert(write(fd, "1234567890", 10) == 10); + test_assert(lseek(fd, 0, SEEK_SET) == 0); + input = i_stream_create_fd_autoclose(&fd, 1024); + + /* temp file ostream */ + fd = open(".temp.ostream", O_RDWR | O_CREAT | O_TRUNC, 0600); + if (fd == -1) + i_fatal("creat(.temp.ostream) failed: %m"); + output = o_stream_create_fd(fd, 0); + + /* test that writing works between two files */ + i_stream_seek(input, 3); + input2 = i_stream_create_limit(input, 4); + test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); + test_assert(output->offset == 4); + test_assert(pread(fd, buf, sizeof(buf), 0) == 4 && + memcmp(buf, "4567", 4) == 0); + i_stream_unref(&input2); + + /* test that writing works within the same file */ + i_stream_destroy(&input); + + input = i_stream_create_fd(fd, 1024); + /* forwards: 4567 -> 4677 */ + o_stream_seek(output, 1); + i_stream_seek(input, 2); + input2 = i_stream_create_limit(input, 2); + test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); + test_assert(output->offset == 3); + test_assert(pread(fd, buf, sizeof(buf), 0) == 4 && + memcmp(buf, "4677", 4) == 0); + i_stream_destroy(&input2); + i_stream_destroy(&input); + + /* backwards: 1234 -> 11234 */ + memcpy(buf, "1234", 4); + test_assert(pwrite(fd, buf, 4, 0) == 4); + input = i_stream_create_fd(fd, 1024); + o_stream_seek(output, 1); + test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); + test_assert(output->offset == 5); + test_assert(pread(fd, buf, sizeof(buf), 0) == 5 && + memcmp(buf, "11234", 5) == 0); + i_stream_destroy(&input); + + o_stream_destroy(&output); + i_close_fd(&fd); + + i_unlink(".temp.istream"); + i_unlink(".temp.ostream"); + test_end(); +} + +static void test_ostream_file_send_istream_sendfile(void) +{ + struct istream *input, *input2; + struct ostream *output; + char buf[10]; + int fd, sock_fd[2]; + + test_begin("ostream file send istream sendfile()"); + + /* temp file istream */ + fd = open(".temp.istream", O_RDWR | O_CREAT | O_TRUNC, 0600); + if (fd == -1) + i_fatal("creat(.temp.istream) failed: %m"); + test_assert(write(fd, "abcdefghij", 10) == 10); + test_assert(lseek(fd, 0, SEEK_SET) == 0); + input = i_stream_create_fd_autoclose(&fd, 1024); + + /* temp socket ostream */ + i_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fd) == 0); + output = o_stream_create_fd_autoclose(sock_fd, 0); + + /* test that sendfile() works */ + i_stream_seek(input, 3); + input2 = i_stream_create_limit(input, 4); + test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); + test_assert(output->offset == 4); + test_assert(read(sock_fd[1], buf, sizeof(buf)) == 4 && + memcmp(buf, "defg", 4) == 0); + i_stream_unref(&input2); + + /* test reading past EOF */ + i_stream_seek(input, 0); + input2 = i_stream_create_limit(input, 20); + test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); + test_assert(input2->v_offset == 10); + test_assert(output->offset == 14); + i_stream_unref(&input2); + + i_stream_unref(&input); + o_stream_destroy(&output); + i_close_fd(&sock_fd[1]); + + i_unlink(".temp.istream"); + test_end(); +} + +void test_ostream_file(void) +{ + test_ostream_file_random(); + test_ostream_file_send_istream_file(); + test_ostream_file_send_istream_sendfile(); +} diff --git a/src/lib/test-ostream-multiplex.c b/src/lib/test-ostream-multiplex.c new file mode 100644 index 0000000..d5b7ac1 --- /dev/null +++ b/src/lib/test-ostream-multiplex.c @@ -0,0 +1,405 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "ioloop.h" +#include "str.h" +#include "istream.h" +#include "ostream-private.h" +#include "istream-multiplex.h" +#include "ostream-multiplex.h" +#include "ostream.h" +#include <unistd.h> + +#include "hex-binary.h" + +static void test_ostream_multiplex_simple(void) +{ + test_begin("ostream multiplex (simple)"); + + const unsigned char expected[] = { + '\x00','\x00','\x00','\x00','\x05','\x68','\x65', + '\x6c','\x6c','\x6f','\x01','\x00','\x00','\x00', + '\x05','\x77','\x6f','\x72','\x6c','\x64' + }; + + buffer_t *result = t_str_new(64); + struct ostream *os = test_ostream_create(result); + struct ostream *os2 = o_stream_create_multiplex(os, SIZE_MAX); + struct ostream *os3 = o_stream_multiplex_add_channel(os2, 1); + + test_assert(o_stream_send_str(os2, "hello") == 5); + test_assert(o_stream_send_str(os3, "world") == 5); + + o_stream_unref(&os3); + o_stream_unref(&os2); + + test_assert(o_stream_finish(os) == 1); + o_stream_unref(&os); + + test_assert(sizeof(expected) == result->used); + test_assert(memcmp(result->data, expected, I_MIN(sizeof(expected), + result->used)) == 0); + + test_end(); +} + +static unsigned int channel_counter[2] = {0, 0}; +static struct ostream *chan0, *chan1; + +static const char *msgs[] = { + "", + "a", + "bb", + "ccc", + "dddd", + "eeeee", + "ffffff" +}; + +static void test_ostream_multiplex_stream_read(struct istream *is) +{ + uint8_t cid; + const unsigned char *data; + size_t siz,dlen=0,pos=0; + + if (i_stream_read_more(is, &data, &siz)>0) { + /* parse stream */ + for(;pos<siz;) { + if (dlen > 0) { + if (dlen < N_ELEMENTS(msgs)) { + test_assert_idx(memcmp(&data[pos], + msgs[dlen], dlen)==0, + channel_counter[data[0] % 2]); + } + channel_counter[data[0] % 2]++; + pos += dlen; + dlen = 0; + } else if (dlen == 0) { + cid = data[pos] % 2; + test_assert_idx(data[pos] < 2, channel_counter[cid]); + pos++; + dlen = be32_to_cpu_unaligned(&data[pos]); + pos += 4; + test_assert(dlen > 0 && dlen < N_ELEMENTS(msgs)); + } + } + i_stream_skip(is, siz); + } + + if (channel_counter[0] > 100 && channel_counter[1] > 100) + io_loop_stop(current_ioloop); +} + +static void test_ostream_multiplex_stream_write(struct ostream *channel ATTR_UNUSED) +{ + size_t rounds = 1 + i_rand_limit(10); + for(size_t i = 0; i < rounds; i++) { + if ((i_rand_limit(2)) != 0) { + o_stream_cork(chan1); + /* send one byte at a time */ + for(const char *p = msgs[i_rand_limit(N_ELEMENTS(msgs))]; + *p != '\0'; p++) { + o_stream_nsend(chan1, p, 1); + } + o_stream_uncork(chan1); + } else { + o_stream_nsend_str(chan0, + msgs[i_rand_limit(N_ELEMENTS(msgs))]); + } + } +} + +static void test_ostream_multiplex_stream(void) +{ + test_begin("ostream multiplex (stream)"); + + struct ioloop *ioloop = io_loop_create(); + io_loop_set_current(ioloop); + + int fds[2]; + test_assert(pipe(fds) == 0); + fd_set_nonblock(fds[0], TRUE); + fd_set_nonblock(fds[1], TRUE); + struct ostream *os = o_stream_create_fd(fds[1], SIZE_MAX); + struct istream *is = i_stream_create_fd(fds[0], SIZE_MAX); + + chan0 = o_stream_create_multiplex(os, SIZE_MAX); + chan1 = o_stream_multiplex_add_channel(chan0, 1); + + struct io *io0 = + io_add_istream(is, test_ostream_multiplex_stream_read, is); + struct io *io1 = + io_add(fds[1], IO_WRITE, test_ostream_multiplex_stream_write, os); + + io_loop_run(current_ioloop); + + io_remove(&io0); + io_remove(&io1); + + test_assert(o_stream_finish(chan1) > 0); + o_stream_unref(&chan1); + test_assert(o_stream_finish(chan0) > 0); + o_stream_unref(&chan0); + + i_stream_unref(&is); + o_stream_unref(&os); + + io_loop_destroy(&ioloop); + + i_close_fd(&fds[0]); + i_close_fd(&fds[1]); + + test_end(); +} + +static void test_ostream_multiplex_cork(void) +{ + test_begin("ostream multiplex (corking)"); + buffer_t *output = t_buffer_create(128); + struct ostream *os = test_ostream_create(output); + struct ostream *chan0 = o_stream_create_multiplex(os, SIZE_MAX); + + const struct const_iovec iov[] = { + { "hello", 5 }, + { " ", 1 }, + { "world", 5 }, + { "!", 1 } + }; + + /* send data in parts, expect to see single blob */ + o_stream_cork(chan0); + o_stream_nsendv(chan0, iov, N_ELEMENTS(iov)); + o_stream_uncork(chan0); + test_assert(o_stream_flush(os) == 1); + + /* check output */ + test_assert(memcmp(output->data, "\0\0\0\0\f", 5) == 0); + test_assert(strcmp(str_c(output)+5, "hello world!") == 0); + + test_assert(o_stream_finish(chan0) > 0); + o_stream_unref(&chan0); + o_stream_unref(&os); + + test_end(); +} + +struct test_hang_context { + struct istream *input1, *input2; + size_t sent_bytes, sent2_bytes; + size_t read_bytes, read2_bytes; +}; + +static void test_hang_input(struct test_hang_context *ctx) +{ + ssize_t ret, ret2; + + do { + ret = i_stream_read(ctx->input1); + if (ret > 0) { + i_stream_skip(ctx->input1, ret); + ctx->read_bytes += ret; + } + ret2 = i_stream_read(ctx->input2); + if (ret2 > 0) { + i_stream_skip(ctx->input2, ret2); + ctx->read2_bytes += ret2; + } + } while (ret > 0 || ret2 > 0); + + test_assert(ret == 0 && ret2 == 0); + if (ctx->read_bytes == ctx->sent_bytes && + ctx->read2_bytes == ctx->sent2_bytes) + io_loop_stop(current_ioloop); +} + +static void test_ostream_multiplex_hang(void) +{ + int fd[2]; + + test_begin("ostream multiplex hang"); + if (pipe(fd) < 0) + i_fatal("pipe() failed: %m"); + fd_set_nonblock(fd[0], TRUE); + fd_set_nonblock(fd[1], TRUE); + + struct ioloop *ioloop = io_loop_create(); + struct ostream *file_output = o_stream_create_fd(fd[1], 1024); + o_stream_set_no_error_handling(file_output, TRUE); + struct ostream *channel = o_stream_create_multiplex(file_output, 4096); + struct ostream *channel2 = o_stream_multiplex_add_channel(channel, 1); + char buf[256]; + + /* send multiplex output until the buffer is full */ + ssize_t ret, ret2; + size_t sent_bytes = 0, sent2_bytes = 0; + i_zero(&buf); + o_stream_cork(channel); + o_stream_cork(channel2); + while ((ret = o_stream_send(channel, buf, sizeof(buf))) > 0) { + sent_bytes += ret; + ret2 = o_stream_send(channel2, buf, sizeof(buf)); + if (ret2 <= 0) + break; + sent2_bytes += ret2; + } + test_assert(o_stream_finish(channel) == 0); + test_assert(o_stream_finish(channel2) == 0); + o_stream_uncork(channel); + o_stream_uncork(channel2); + /* We expect the first channel to have data buffered */ + test_assert(o_stream_get_buffer_used_size(channel) >= + o_stream_get_buffer_used_size(file_output)); + test_assert(o_stream_get_buffer_used_size(channel) - + o_stream_get_buffer_used_size(file_output) > 0); + + /* read everything that was already sent */ + struct istream *file_input = i_stream_create_fd(fd[0], 1024); + struct istream *input = i_stream_create_multiplex(file_input, 4096); + struct istream *input2 = i_stream_multiplex_add_channel(input, 1); + + struct test_hang_context ctx = { + .input1 = input, + .input2 = input2, + .sent_bytes = sent_bytes, + .sent2_bytes = sent2_bytes, + }; + + struct timeout *to = timeout_add(5000, io_loop_stop, current_ioloop); + struct io *io = io_add_istream(file_input, test_hang_input, &ctx); + io_loop_run(ioloop); + io_remove(&io); + timeout_remove(&to); + + /* everything that was sent should have been received now. + ostream-multiplex's internal buffer is also supposed to have + been sent. */ + test_assert(input->v_offset == sent_bytes); + test_assert(input2->v_offset == sent2_bytes); + test_assert(o_stream_get_buffer_used_size(channel) == 0); + test_assert(o_stream_get_buffer_used_size(channel2) == 0); + + i_stream_unref(&file_input); + i_stream_unref(&input); + i_stream_unref(&input2); + o_stream_unref(&channel); + o_stream_unref(&channel2); + o_stream_unref(&file_output); + io_loop_destroy(&ioloop); + test_end(); +} + +#define FLUSH_CALLBACK_TOTAL_BYTES 10240 + +struct test_flush_context { + struct ostream *output1, *output2; + struct istream *input1, *input2; +}; + +static int flush_callback1(struct test_flush_context *ctx) +{ + char buf[32]; + + i_assert(ctx->output1->offset <= FLUSH_CALLBACK_TOTAL_BYTES); + size_t bytes_left = FLUSH_CALLBACK_TOTAL_BYTES - ctx->output1->offset; + + memset(buf, '1', sizeof(buf)); + if (o_stream_send(ctx->output1, buf, I_MIN(sizeof(buf), bytes_left)) < 0) + return -1; + return ctx->output1->offset < FLUSH_CALLBACK_TOTAL_BYTES ? 0 : 1; +} + +static int flush_callback2(struct test_flush_context *ctx) +{ + char buf[64]; + + i_assert(ctx->output2->offset <= FLUSH_CALLBACK_TOTAL_BYTES); + size_t bytes_left = FLUSH_CALLBACK_TOTAL_BYTES - ctx->output2->offset; + + memset(buf, '2', sizeof(buf)); + if (o_stream_send(ctx->output2, buf, I_MIN(sizeof(buf), bytes_left)) < 0) + return -1; + return ctx->output2->offset < FLUSH_CALLBACK_TOTAL_BYTES ? 0 : 1; +} + +static void test_flush_input(struct test_flush_context *ctx) +{ + ssize_t ret, ret2; + + do { + ret = i_stream_read(ctx->input1); + if (ret > 0) + i_stream_skip(ctx->input1, ret); + ret2 = i_stream_read(ctx->input2); + if (ret2 > 0) + i_stream_skip(ctx->input2, ret2); + } while (ret > 0 || ret2 > 0); + + test_assert(ret == 0 && ret2 == 0); + if (ctx->input1->v_offset == FLUSH_CALLBACK_TOTAL_BYTES && + ctx->input2->v_offset == FLUSH_CALLBACK_TOTAL_BYTES) + io_loop_stop(current_ioloop); +} + +static void test_ostream_multiplex_flush_callback(void) +{ + int fd[2]; + + test_begin("ostream multiplex flush callback"); + if (pipe(fd) < 0) + i_fatal("pipe() failed: %m"); + fd_set_nonblock(fd[0], TRUE); + fd_set_nonblock(fd[1], TRUE); + + struct ioloop *ioloop = io_loop_create(); + struct ostream *file_output = o_stream_create_fd(fd[1], 1024); + o_stream_set_no_error_handling(file_output, TRUE); + struct ostream *channel = o_stream_create_multiplex(file_output, 4096); + struct ostream *channel2 = o_stream_multiplex_add_channel(channel, 1); + + struct istream *file_input = i_stream_create_fd(fd[0], 1024); + struct istream *input = i_stream_create_multiplex(file_input, 4096); + struct istream *input2 = i_stream_multiplex_add_channel(input, 1); + + struct test_flush_context ctx = { + .output1 = channel, + .output2 = channel2, + .input1 = input, + .input2 = input2, + }; + o_stream_set_flush_callback(channel, flush_callback1, &ctx); + o_stream_set_flush_callback(channel2, flush_callback2, &ctx); + o_stream_set_flush_pending(channel, TRUE); + o_stream_set_flush_pending(channel2, TRUE); + + struct timeout *to = timeout_add(5000, io_loop_stop, current_ioloop); + struct io *io = io_add_istream(file_input, test_flush_input, &ctx); + io_loop_run(ioloop); + io_remove(&io); + timeout_remove(&to); + + test_assert(channel->offset == FLUSH_CALLBACK_TOTAL_BYTES); + test_assert(channel2->offset == FLUSH_CALLBACK_TOTAL_BYTES); + test_assert(input->v_offset == FLUSH_CALLBACK_TOTAL_BYTES); + test_assert(input2->v_offset == FLUSH_CALLBACK_TOTAL_BYTES); + + test_assert(o_stream_finish(channel) == 1); + test_assert(o_stream_finish(channel2) == 1); + + i_stream_unref(&file_input); + i_stream_unref(&input); + i_stream_unref(&input2); + o_stream_unref(&channel); + o_stream_unref(&channel2); + o_stream_unref(&file_output); + io_loop_destroy(&ioloop); + test_end(); +} + +void test_ostream_multiplex(void) +{ + test_ostream_multiplex_simple(); + test_ostream_multiplex_stream(); + test_ostream_multiplex_cork(); + test_ostream_multiplex_hang(); + test_ostream_multiplex_flush_callback(); +} diff --git a/src/lib/test-path-util.c b/src/lib/test-path-util.c new file mode 100644 index 0000000..c5d4122 --- /dev/null +++ b/src/lib/test-path-util.c @@ -0,0 +1,278 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "path-util.h" +#include "unlink-directory.h" +#include "str.h" + +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <sys/stat.h> + +#define TEMP_DIRNAME ".test-path-util" + +static const char *tmpdir; +static const char *cwd; +static const char *link1; +static const char *link2; +static const char *link3; +static const char *link4; + +static void test_local_path(void) +{ + const char *expected = t_strconcat(cwd, "/README.md", NULL); + const char *npath = NULL, *error = NULL; + test_assert(t_normpath_to("README.md", cwd, &npath, &error) == 0); + test_assert_strcmp(npath, expected); +} + +static void test_absolute_path_no_change(void) +{ + const char *npath = NULL, *error = NULL; + test_assert(t_normpath_to("/", "/", &npath, &error) == 0); + test_assert_strcmp(npath, "/"); + + test_assert(t_normpath_to(cwd, cwd, &npath, &error) == 0); + test_assert_strcmp(npath, cwd); +} + +static int path_height(const char *p) +{ + int n; + for (n = 0; *p != '\0'; ++p) + n += *p == '/'; + return n; +} + +static void test_travel_to_root(void) +{ + int l = path_height(cwd); + const char *npath = cwd; + for (npath = cwd; l != 0; l--) { + const char *error; + test_assert_idx(t_normpath_to("../", npath, &npath, &error) == 0, l); + } + test_assert_strcmp(npath, "/"); +} + +static void test_extra_slashes(void) +{ + const char *npath = NULL, *error = NULL; + test_assert(t_normpath_to(".", cwd, &npath, &error) == 0); + test_assert_strcmp(npath, cwd); + + test_assert(t_normpath_to("./", cwd, &npath, &error) == 0); + test_assert_strcmp(npath, cwd); + + test_assert(t_normpath_to(".////", cwd, &npath, &error) == 0); + test_assert_strcmp(npath, cwd); +} + +static void test_nonexistent_path(void) +{ + const char *npath = NULL, *error = NULL; + const char *expected = t_strconcat(cwd, "/nonexistent", NULL); + test_assert(t_normpath_to("nonexistent", cwd, &npath, &error) == 0); + test_assert_strcmp(npath, expected); + test_assert(t_realpath_to("nonexistent", cwd, &npath, &error) == -1); + test_assert(error != NULL); +} + +static void test_relative_dotdot(void) +{ + const char *rel_path = "../"TEMP_DIRNAME; + const char *npath = NULL, *error = NULL; + test_assert(t_normpath_to(rel_path, tmpdir, &npath, &error) == 0); + test_assert_strcmp(npath, tmpdir); + + test_assert(t_normpath_to("..", tmpdir, &npath, &error) == 0); + test_assert_strcmp(npath, cwd); + + test_assert(t_normpath_to("../", tmpdir, &npath, &error) == 0); + test_assert_strcmp(npath, cwd); + + test_assert(t_normpath_to("../.", tmpdir, &npath, &error) == 0); + test_assert_strcmp(npath, cwd); +} + +static void test_link1(void) +{ + const char *old_dir, *npath = NULL, *error = NULL; + test_assert(t_realpath_to(link1, "/", &npath, &error) == 0); + test_assert_strcmp(npath, tmpdir); + + /* .../link1/link1/child */ + test_assert(t_realpath_to(t_strconcat(link1, "/link1/child", NULL), + "/", &npath, &error) == 0); + test_assert_strcmp(npath, t_strconcat(tmpdir, "/child", NULL)); + + /* relative link1/link1/child */ + if (t_get_working_dir(&old_dir, &error) < 0) + i_fatal("t_get_working_dir() failed: %s", error); + if (chdir(tmpdir) < 0) + i_fatal("chdir(%s) failed: %m", tmpdir); + test_assert(t_realpath(t_strconcat("link1", "/link1/child", NULL), + &npath, &error) == 0); + if (chdir(old_dir) < 0) + i_fatal("chdir(%s) failed: %m", old_dir); +} + +static void test_link4(void) +{ + const char *npath = NULL, *error = NULL; + + test_assert(t_realpath_to(t_strconcat(link1, "/link4/child", NULL), + "/", &npath, &error) == 0); + test_assert_strcmp(npath, t_strconcat(tmpdir, "/child", NULL)); +} + +static void test_link_loop(void) +{ + const char *npath = NULL, *error = NULL; + errno = 0; + test_assert(t_realpath_to(link2, "/", &npath, &error) == -1); + test_assert(errno == ELOOP); + test_assert(error != NULL); +} + +static void test_abspath_vs_normpath(void) +{ + const char *abs = t_abspath_to("../../bin", "/usr/lib/"); + test_assert_strcmp(abs, "/usr/lib//../../bin"); + + const char *norm = NULL, *error = NULL; + test_assert(t_normpath_to("../../bin", "/usr///lib/", &norm, &error) == 0); + test_assert_strcmp(norm, "/bin"); +} + +static void create_links(const char *tmpdir) +{ + link1 = t_strconcat(tmpdir, "/link1", NULL); + if (symlink(tmpdir, link1) < 0) + i_fatal("symlink(%s, %s) failed: %m", tmpdir, link1); + + const char *link1_child = t_strconcat(link1, "/child", NULL); + int fd = creat(link1_child, 0600); + if (fd == -1) + i_fatal("creat(%s) failed: %m", link1_child); + i_close_fd(&fd); + + /* link2 and link3 point to each other to create a loop */ + link2 = t_strconcat(tmpdir, "/link2", NULL); + link3 = t_strconcat(tmpdir, "/link3", NULL); + if (symlink(link3, link2) < 0) + i_fatal("symlink(%s, %s) failed: %m", link3, link2); + if (symlink(link2, link3) < 0) + i_fatal("symlink(%s, %s) failed: %m", link2, link3); + + /* link4 points to link1 */ + link4 = t_strconcat(tmpdir, "/link4", NULL); + if (symlink("link1", link4) < 0) + i_fatal("symlink(link1, %s) failed: %m", link4); +} + +static void test_link_alloc(void) +{ +#define COMPONENT_COMPONENT "/component-component" + const char *o_tmpdir; + + /* idea here is to make sure component-component + would optimally hit to the nearest_power value. + + it has to be big enough to cause requirement for + allocation in t_realpath. */ + string_t *basedir = t_str_new(256); + str_append(basedir, cwd); + str_append(basedir, "/"TEMP_DIRNAME); + size_t len = nearest_power(I_MAX(127, str_len(basedir) + strlen(COMPONENT_COMPONENT) + 1)) - + strlen(COMPONENT_COMPONENT); + + while(str_len(basedir) < len) { + str_append(basedir, COMPONENT_COMPONENT); + (void)mkdir(str_c(basedir), 0700); + } + o_tmpdir = tmpdir; + tmpdir = str_c(basedir); + + create_links(tmpdir); + + test_link1(); + test_link_loop(); + + tmpdir = o_tmpdir; +} + +static void test_link_alloc2(void) +{ + const char *o_tmpdir; + + /* try enough different sized base directory lengths so the code + hits the different reallocations and tests for off-by-one errors */ + string_t *basedir = t_str_new(256); + str_append(basedir, cwd); + str_append(basedir, "/"TEMP_DIRNAME); + str_append_c(basedir, '/'); + size_t base_len = str_len(basedir); + + o_tmpdir = tmpdir; + /* path_normalize() initially allocates 128 bytes, so we'll test paths + up to that length+1. */ + unsigned char buf[128+1]; + memset(buf, 'x', sizeof(buf)); + for (size_t i = 1; i <= sizeof(buf); i++) { + str_truncate(basedir, base_len); + str_append_data(basedir, buf, i); + tmpdir = str_c(basedir); + (void)mkdir(str_c(basedir), 0700); + + create_links(tmpdir); + test_link1(); + test_link_loop(); + } + tmpdir = o_tmpdir; +} + +static void test_cleanup(void) +{ + const char *error; + + if (unlink_directory(tmpdir, UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0) + i_error("unlink_directory() failed: %s", error); +} + +static void test_init(void) +{ + const char *error; + test_assert(t_get_working_dir(&cwd, &error) == 0); + tmpdir = t_strconcat(cwd, "/"TEMP_DIRNAME, NULL); + + test_cleanup(); + if (mkdir(tmpdir, 0700) < 0) { + i_fatal("mkdir: %m"); + } + + create_links(tmpdir); +} + +void test_path_util(void) +{ + test_begin("test_path_util"); + alarm(20); + test_init(); + test_local_path(); + test_absolute_path_no_change(); + test_travel_to_root(); + test_extra_slashes(); + test_nonexistent_path(); + test_relative_dotdot(); + test_link1(); + test_link4(); + test_link_loop(); + test_abspath_vs_normpath(); + test_link_alloc(); + test_link_alloc2(); + test_cleanup(); + alarm(0); + test_end(); +} diff --git a/src/lib/test-pkcs5.c b/src/lib/test-pkcs5.c new file mode 100644 index 0000000..c826eea --- /dev/null +++ b/src/lib/test-pkcs5.c @@ -0,0 +1,49 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "buffer.h" +#include "hash-method.h" +#include "pkcs5.h" + +struct test_vector { + const char *prf; + unsigned char *p; + size_t pLen; + unsigned char *s; + size_t sLen; + unsigned int i; + unsigned char *dk; + size_t dkLen; +}; + +#define TEST_BUF(x) (unsigned char*)x, sizeof(x)-1 + +/* RFC 6070 test vectors */ +static const struct test_vector test_vectors_v2[] = { + { "sha1", TEST_BUF("password"), TEST_BUF("salt"), 1, TEST_BUF("\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6") }, + { "sha1", TEST_BUF("password"), TEST_BUF("salt"), 2, TEST_BUF("\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57") }, + { "sha1", TEST_BUF("password"), TEST_BUF("salt"), 4096, TEST_BUF("\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1") }, +/* enable the next test only when you need it, it takes quite long time */ +/* { "sha1", TEST_BUF("password"), TEST_BUF("salt"), 16777216, TEST_BUF("\xee\xfe\x3d\x61\xcd\x4d\xa4\xe4\xe9\x94\x5b\x3d\x6b\xa2\x15\x8c\x26\x34\xe9\x84") }, */ + { "sha1", TEST_BUF("passwordPASSWORDpassword"), TEST_BUF("saltSALTsaltSALTsaltSALTsaltSALTsalt"), 4096, TEST_BUF("\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96\x4c\xf2\xf0\x70\x38") }, + { "sha1", TEST_BUF("pass\0word"), TEST_BUF("sa\0lt"), 4096, TEST_BUF("\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37\xd7\xf0\x34\x25\xe0\xc3") } +}; + +void test_pkcs5_pbkdf2(void) +{ + buffer_t *res = buffer_create_dynamic(default_pool, 25); + + test_begin("pkcs5_pbkdf2"); + + for(size_t i = 0; i < N_ELEMENTS(test_vectors_v2); i++) { + buffer_set_used_size(res, 0); + const struct test_vector *vec = &(test_vectors_v2[i]); + pkcs5_pbkdf(PKCS5_PBKDF2, hash_method_lookup(vec->prf), vec->p, vec->pLen, vec->s, vec->sLen, vec->i, vec->dkLen, res); + test_assert_idx(memcmp(res->data, vec->dk, vec->dkLen) == 0, i); + } + + buffer_free(&res); + + test_end(); +} diff --git a/src/lib/test-primes.c b/src/lib/test-primes.c new file mode 100644 index 0000000..fc792b4 --- /dev/null +++ b/src/lib/test-primes.c @@ -0,0 +1,24 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "primes.h" + +void test_primes(void) +{ + unsigned int i, j, num; + bool success; + + success = primes_closest(0) > 0; + for (num = 1; num < 1024; num++) { + if (primes_closest(num) < num) + success = FALSE; + } + for (i = 10; i < 32; i++) { + num = (1U << i) - 100; + for (j = 0; j < 200; j++, num++) { + if (primes_closest(num) < num) + success = FALSE; + } + } + test_out("primes_closest()", success); +} diff --git a/src/lib/test-printf-format-fix.c b/src/lib/test-printf-format-fix.c new file mode 100644 index 0000000..c9d6203 --- /dev/null +++ b/src/lib/test-printf-format-fix.c @@ -0,0 +1,142 @@ +/* Copyright (c) 2001-2018 Dovecot authors, see the included COPYING file */ + +/* Unit tests for printf-format-fix helper */ + +#include "test-lib.h" +#include "printf-format-fix.h" + +#include <string.h> + +struct format_fix_rewrites { + const char *input; + const char *output; + size_t length; +}; + +static void test_unchanged() +{ + static const char *tests[] = { + "Hello world", + "Embedded %%, %u, %f, %s, etc. are OK", + "Allow %#0- +s flags", + "duplicate flags in different args %0-123s %0-123s", + "Minimum length %9999s", + "Minimum length parameter %*s", + "Precision %.9999s", + "Precision %1.9999s", + "Precision parameter %1.*s %.*s", + "Floating precisions such as %.0f %0.4f %-4.0f", + "Length modifiers %hd %hhd %ld %lld %Lg %jd %zd %td", + "Specifiers %s %u %d %c %i %x %X %p %o %e %E %f %F %g %G %a %A", + "%%doesn't cause confusion in %%m and %%n", + }; + unsigned int i; + + test_begin("printf_format_fix(safe)"); + for (i = 0; i < N_ELEMENTS(tests); ++i) { + size_t len; + T_BEGIN { + test_assert_idx(printf_format_fix(tests[i]) + == tests[i], i); + test_assert_idx(printf_format_fix_get_len(tests[i], &len) + == tests[i], i); + test_assert_idx(len == strlen(tests[i]), i); + } T_END; + } + test_end(); +} + +static void test_ok_changes() +{ + static const char *tests[] = { + "OK to have a trailing %m", + "%m can appear at the start too", + "Even %m in the middle with a confusing %%m elsewhere is OK", + }; + unsigned int i; + const char *needle; + unsigned int needlen; + int old_errno = errno; + + test_begin("printf_format_fix(rewrites)"); + + errno = EINVAL; + needle = strerror(errno); + i_assert(needle != NULL); + needlen = strlen(needle); + + for (i = 0; i < N_ELEMENTS(tests); ++i) { + size_t len; + char const *chgd; + char const *insert; + unsigned int offs; + + T_BEGIN { + chgd = printf_format_fix(tests[i]); + test_assert_idx(chgd != tests[i], i); + insert = strstr(chgd, needle); + test_assert_idx(insert != NULL, i); + offs = insert - chgd; + test_assert_idx(memcmp(chgd, tests[i], offs) == 0, i); + test_assert_idx(memcmp(chgd+offs, needle, needlen) == 0, i); + test_assert_idx(strcmp(chgd+offs+needlen, tests[i]+offs+2) == 0, i); + + chgd = printf_format_fix_get_len(tests[i], &len); + test_assert_idx(chgd != tests[i], i); + test_assert_idx(len == strlen(chgd), i); + insert = strstr(chgd, needle); + test_assert_idx(insert != NULL, i); + offs = insert - chgd; + test_assert_idx(memcmp(chgd, tests[i], offs) == 0, i); + test_assert_idx(memcmp(chgd+offs, needle, needlen) == 0, i); + test_assert_idx(memcmp(chgd+offs+needlen, tests[i]+offs+2, len-needlen-offs) == 0, i); + } T_END; + } + + errno = old_errno; + + test_end(); +} + +void test_printf_format_fix() +{ + test_unchanged(); + test_ok_changes(); +} + +/* Want to test the panics too? go for it! */ +enum fatal_test_state fatal_printf_format_fix(unsigned int stage) +{ + static const struct { + const char *format; + const char *expected_fatal; + } fatals[] = { + { "no no no %n's", "%n modifier used" }, + { "no no no %-1234567890123n's with extra stuff", "Too large minimum field width" }, + { "%m allowed once, but not twice: %m", "%m used twice" }, + { "%m must not obscure a later %n", "%n modifier used" }, + { "definitely can't have a tailing %", "Missing % specifier" }, + { "Evil %**%n", "Unsupported 0x2a specifier" }, + { "Evil %*#%99999$s", "Unsupported 0x23 specifier" }, + { "No weird %% with %0%", "Unsupported 0x25 specifier" }, + { "No duplicate modifiers %00s", "Duplicate % flag '0'" }, + { "Minimum length can't be too long %10000s", "Too large minimum field width" }, + { "Minimum length doesn't support %*1$s", "Unsupported 0x31 specifier" }, + { "Precision can't be too long %.10000s", "Too large precision" }, + { "Precision can't be too long %1.10000s", "Too large precision" }, + { "Precision doesn't support %1.-1s", "Unsupported 0x2d specifier" }, + }; + + if(stage >= N_ELEMENTS(fatals)) { + test_end(); + return FATAL_TEST_FINISHED; + } + + if(stage == 0) + test_begin("fatal_printf_format_fix"); + + /* let's crash! */ + test_expect_fatal_string(fatals[stage].expected_fatal); + (void)printf_format_fix(fatals[stage].format); + return FATAL_TEST_FAILURE; +} diff --git a/src/lib/test-priorityq.c b/src/lib/test-priorityq.c new file mode 100644 index 0000000..a4eb90f --- /dev/null +++ b/src/lib/test-priorityq.c @@ -0,0 +1,106 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "priorityq.h" + + +struct pq_test_item { + struct priorityq_item item; + int num; +}; + +static int cmp_int(const void *p1, const void *p2) +{ + const struct pq_test_item *i1 = p1, *i2 = p2; + + return i1->num - i2->num; +} + +void test_priorityq(void) +{ +#define PQ_MAX_ITEMS 100 + static const int input[] = { + 1, 2, 3, 4, 5, 6, 7, 8, -1, + 8, 7, 6, 5, 4, 3, 2, 1, -1, + 8, 7, 5, 6, 1, 3, 4, 2, -1, + -1 + }; + static const int output[] = { + 1, 2, 3, 4, 5, 6, 7, 8 + }; + struct pq_test_item *item, items[PQ_MAX_ITEMS]; + struct priorityq_item *const *all_items; + unsigned int i, j; + struct priorityq *pq; + pool_t pool; + int prev; + + pool = pool_alloconly_create("priorityq items", 1024); + + /* simple tests with popping only */ + test_begin("priorityq"); + for (i = 0; input[i] != -1; i++) { + p_clear(pool); + pq = priorityq_init(cmp_int, 1); + for (j = 0; input[i] != -1; i++, j++) { + test_assert(priorityq_count(pq) == j); + item = p_new(pool, struct pq_test_item, 1); + item->num = input[i]; + priorityq_add(pq, &item->item); + } + all_items = priorityq_items(pq); + test_assert(priorityq_count(pq) == N_ELEMENTS(output)); + item = (struct pq_test_item *)all_items[0]; + test_assert(item->num == output[0]); + for (j = 1; j < N_ELEMENTS(output); j++) { + item = (struct pq_test_item *)all_items[j]; + test_assert(item->num > output[0]); + test_assert(item->num <= output[N_ELEMENTS(output)-1]); + } + for (j = 0; j < N_ELEMENTS(output); j++) { + test_assert(priorityq_count(pq) == N_ELEMENTS(output) - j); + + item = (struct pq_test_item *)priorityq_peek(pq); + i_assert(item != NULL); + test_assert(output[j] == item->num); + item = (struct pq_test_item *)priorityq_pop(pq); + i_assert(item != NULL); + test_assert(output[j] == item->num); + } + test_assert(priorityq_count(pq) == 0); + test_assert(priorityq_peek(pq) == NULL); + test_assert(priorityq_pop(pq) == NULL); + priorityq_deinit(&pq); + } + test_end(); + + /* randomized tests, remove elements */ + test_begin("priorityq randomized"); + for (i = 0; i < 100; i++) { + pq = priorityq_init(cmp_int, 1); + for (j = 0; j < PQ_MAX_ITEMS; j++) { + items[j].num = i_rand_limit(INT_MAX); + priorityq_add(pq, &items[j].item); + } + for (j = 0; j < PQ_MAX_ITEMS; j++) { + if (i_rand_limit(3) == 0) { + priorityq_remove(pq, &items[j].item); + items[j].num = -1; + } + } + prev = 0; + while (priorityq_count(pq) > 0) { + item = (struct pq_test_item *)priorityq_pop(pq); + i_assert(item != NULL); + test_assert(item->num >= 0 && prev <= item->num); + prev = item->num; + item->num = -1; + } + for (j = 0; j < PQ_MAX_ITEMS; j++) { + test_assert(items[j].num == -1); + } + priorityq_deinit(&pq); + } + test_end(); + pool_unref(&pool); +} diff --git a/src/lib/test-random.c b/src/lib/test-random.c new file mode 100644 index 0000000..6c83ff6 --- /dev/null +++ b/src/lib/test-random.c @@ -0,0 +1,67 @@ +/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "stats-dist.h" +#include "randgen.h" +#include <math.h> + +#define TEST_RAND_SIZE_MEDIAN 100000.0 + +static void test_random_median(void) +{ + uint64_t tmp; + double median, average; + + struct stats_dist *s = stats_dist_init_with_size(TEST_RAND_SIZE_MEDIAN); + test_begin("test_random (median & average)"); + for(unsigned int i = 0; i < TEST_RAND_SIZE_MEDIAN; i++) { + uint64_t value; + value = i_rand_limit(TEST_RAND_SIZE_MEDIAN); + stats_dist_add(s, value); + } + tmp = stats_dist_get_median(s); + + /* median should be 0.5 +-2% */ + median = (double)tmp / TEST_RAND_SIZE_MEDIAN; + test_assert(fabs(median - 0.5) < 0.01); + + /* average should be 0.5 +- %2 */ + average = stats_dist_get_avg(s) / TEST_RAND_SIZE_MEDIAN; + + test_assert(fabs(average - 0.5) < 0.01); + + stats_dist_deinit(&s); + test_end(); +} + +static void test_random_limits(void) +{ + test_begin("random limits"); + test_assert(i_rand_limit(1) == 0); + test_assert(i_rand_minmax(0, 0) == 0); + test_assert(i_rand_minmax(UINT32_MAX, UINT32_MAX) == UINT32_MAX); + test_end(); +} + +void test_random(void) +{ + test_random_median(); + test_random_limits(); +} + +enum fatal_test_state fatal_random(unsigned int stage) +{ + switch (stage) { + case 0: + test_begin("random fatals"); + test_expect_fatal_string("min_val <= max_val"); + (void)i_rand_minmax(1, 0); + return FATAL_TEST_FAILURE; + case 1: + test_expect_fatal_string("upper_bound > 0"); + (void)i_rand_limit(0); + return FATAL_TEST_FAILURE; + } + test_end(); + return FATAL_TEST_FINISHED; +} diff --git a/src/lib/test-seq-range-array.c b/src/lib/test-seq-range-array.c new file mode 100644 index 0000000..ca0178a --- /dev/null +++ b/src/lib/test-seq-range-array.c @@ -0,0 +1,419 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "array.h" +#include "seq-range-array.h" + + +static void +boundaries_permute(uint32_t *input, unsigned int i, unsigned int count) +{ + ARRAY_TYPE(seq_range) range; + const struct seq_range *seqs; + unsigned int seqs_count; + uint32_t tmp; + unsigned int j; + + if (i+1 < count) { + for (j = i; j < count; j++) { + tmp = input[i]; input[i] = input[j]; input[j] = tmp; + boundaries_permute(input, i+1, count); + tmp = input[i]; input[i] = input[j]; input[j] = tmp; + } + return; + } + t_array_init(&range, 4); + for (i = 0; i < count; i++) + seq_range_array_add(&range, input[i]); + seqs = array_get(&range, &seqs_count); + test_assert(seqs_count == 2); + test_assert(seqs[0].seq1 == 0); + test_assert(seqs[0].seq2 == 1); + test_assert(seqs[1].seq1 == (uint32_t)-2); + test_assert(seqs[1].seq2 == (uint32_t)-1); +} + +static void test_seq_range_array_add_boundaries(void) +{ + static uint32_t input[] = { 0, 1, (uint32_t)-2, (uint32_t)-1 }; + + boundaries_permute(input, 0, N_ELEMENTS(input)); +} + +static void test_seq_range_array_add_merge(void) +{ + ARRAY_TYPE(seq_range) range; + + test_begin("seq_range_array_add() merging"); + t_array_init(&range, 8); + seq_range_array_add(&range, 4); + seq_range_array_add(&range, 1); + seq_range_array_add(&range, 2); + test_assert(array_count(&range) == 2); + + seq_range_array_add_range(&range, 1, (uint32_t)-1); + test_assert(array_count(&range) == 1); + seq_range_array_add_range(&range, 1, (uint32_t)-1); + test_assert(array_count(&range) == 1); + test_end(); +} + +static void test_seq_range_array_merge_n(void) +{ + ARRAY_TYPE(seq_range) src, dest, dest2; + struct seq_range_iter iter; + const uint32_t seqs[] = { 4, 5, 7, 8, 9, 11 }; + uint32_t seq; + + test_begin("seq_range_array_merge_n()"); + t_array_init(&src, 16); + t_array_init(&dest, 16); + t_array_init(&dest2, 16); + for (unsigned int i = 0; i < N_ELEMENTS(seqs); i++) + seq_range_array_add(&src, seqs[i]); + + for (unsigned int i = 0; i <= N_ELEMENTS(seqs); i++) { + array_clear(&dest); + array_clear(&dest2); + seq_range_array_merge_n(&dest, &src, i); + test_assert_idx(seq_range_count(&dest) == I_MIN(i, N_ELEMENTS(seqs)), i); + + seq_range_array_iter_init(&iter, &src); + for (unsigned int j = 0; j < i; j++) { + test_assert_idx(seq_range_array_iter_nth(&iter, j, &seq), i); + seq_range_array_add(&dest2, seq); + } + seq_range_array_invert(&dest2, 1, UINT32_MAX); + seq_range_array_intersect(&dest2, &dest); + test_assert_idx(array_count(&dest2) == 0, i); + } + test_end(); +} + +static void test_seq_range_array_remove_nth(void) +{ + ARRAY_TYPE(seq_range) range; + const struct seq_range *r; + + test_begin("seq_range_array_remove_nth()"); + t_array_init(&range, 8); + seq_range_array_add_range(&range, 1, 5); + seq_range_array_add(&range, 7); + seq_range_array_add_range(&range, 10,20); + test_assert(array_count(&range) == 3); + + seq_range_array_remove_nth(&range, 0, 2); + r = array_front(&range); test_assert(r->seq1 == 3 && r->seq2 == 5); + + seq_range_array_remove_nth(&range, 1, 4); + r = array_front(&range); test_assert(r->seq1 == 3 && r->seq2 == 3); + r = array_idx(&range, 1); test_assert(r->seq1 == 11 && r->seq2 == 20); + + seq_range_array_remove_nth(&range, 5, (uint32_t)-1); + r = array_idx(&range, 1); test_assert(r->seq1 == 11 && r->seq2 == 14); + + test_assert(array_count(&range) == 2); + test_end(); +} + +static void test_seq_range_array_remove_range(void) +{ + ARRAY_TYPE(seq_range) range; + const struct seq_range *r; + + test_begin("seq_range_array_remove_range()"); + t_array_init(&range, 8); + + seq_range_array_add_range(&range, 0, (uint32_t)-2); + test_assert(seq_range_array_remove_range(&range, 0, 2) == 3); + r = array_front(&range); test_assert(r->seq1 == 3 && r->seq2 == (uint32_t)-2); + + seq_range_array_add_range(&range, 0, (uint32_t)-2); + test_assert(array_count(&range) == 1); + test_assert(seq_range_array_remove_range(&range, 0, (uint32_t)-2) == UINT_MAX); + test_assert(array_count(&range) == 0); + + seq_range_array_add_range(&range, (uint32_t)-1, (uint32_t)-1); + test_assert(seq_range_array_remove_range(&range, (uint32_t)-1, (uint32_t)-1) == 1); + test_assert(array_count(&range) == 0); + + seq_range_array_add_range(&range, (uint32_t)-1, (uint32_t)-1); + test_assert(seq_range_array_remove_range(&range, 1, (uint32_t)-1) == 1); + test_assert(array_count(&range) == 0); + + seq_range_array_add_range(&range, 1, 10); + test_assert(seq_range_array_remove_range(&range, 5, 6) == 2); + test_assert(seq_range_array_remove_range(&range, 4, 7) == 2); + test_assert(seq_range_array_remove_range(&range, 1, 4) == 3); + test_assert(seq_range_array_remove_range(&range, 8, 10) == 3); + test_assert(array_count(&range) == 0); + + test_end(); +} + +static void test_seq_range_array_random(void) +{ +#define SEQ_RANGE_TEST_BUFSIZE 100 +#define SEQ_RANGE_TEST_COUNT 20000 + unsigned char shadowbuf[SEQ_RANGE_TEST_BUFSIZE]; + ARRAY_TYPE(seq_range) range; + const struct seq_range *seqs; + uint32_t seq1, seq2; + unsigned int i, j, ret, ret2, count; + int test = -1; + + ret = ret2 = 0; + i_array_init(&range, 1); + memset(shadowbuf, 0, sizeof(shadowbuf)); + for (i = 0; i < SEQ_RANGE_TEST_COUNT; i++) { + seq1 = i_rand_limit(SEQ_RANGE_TEST_BUFSIZE); + seq2 = seq1 + i_rand_limit(SEQ_RANGE_TEST_BUFSIZE - seq1); + test = i_rand_limit(4); + switch (test) { + case 0: + ret = seq_range_array_add(&range, seq1) ? 0 : 1; /* FALSE == added */ + ret2 = shadowbuf[seq1] == 0 ? 1 : 0; + shadowbuf[seq1] = 1; + break; + case 1: + ret = seq_range_array_add_range_count(&range, seq1, seq2); + for (ret2 = 0; seq1 <= seq2; seq1++) { + if (shadowbuf[seq1] == 0) { + ret2++; + shadowbuf[seq1] = 1; + } + } + break; + case 2: + ret = seq_range_array_remove(&range, seq1) ? 1 : 0; + ret2 = shadowbuf[seq1] != 0 ? 1 : 0; + shadowbuf[seq1] = 0; + break; + case 3: + ret = seq_range_array_remove_range(&range, seq1, seq2); + for (ret2 = 0; seq1 <= seq2; seq1++) { + if (shadowbuf[seq1] != 0) { + ret2++; + shadowbuf[seq1] = 0; + } + } + break; + } + if (ret != ret2) + break; + + seqs = array_get(&range, &count); + for (j = 0, seq1 = 0; j < count; j++) { + if (j > 0 && seqs[j-1].seq2+1 >= seqs[j].seq1) + goto fail; + for (; seq1 < seqs[j].seq1; seq1++) { + if (shadowbuf[seq1] != 0) + goto fail; + } + for (; seq1 <= seqs[j].seq2; seq1++) { + if (shadowbuf[seq1] == 0) + goto fail; + } + } + i_assert(seq1 <= SEQ_RANGE_TEST_BUFSIZE); + for (; seq1 < SEQ_RANGE_TEST_BUFSIZE; seq1++) { + if (shadowbuf[seq1] != 0) + goto fail; + } + } +fail: + if (i == SEQ_RANGE_TEST_COUNT) + test_out("seq_range_array random", TRUE); + else { + test_out_reason("seq_range_array random", FALSE, + t_strdup_printf("round %u test %d failed", i, test)); + } + array_free(&range); +} + +static void test_seq_range_array_invert_minmax(uint32_t min, uint32_t max) +{ + ARRAY_TYPE(seq_range) range = ARRAY_INIT; + struct seq_range_iter iter; + unsigned int n, inverse_mask, mask_inside, mask_size = max-min+1; + uint32_t seq; + + i_assert(mask_size <= sizeof(unsigned int)*8); + t_array_init(&range, 16); + for (unsigned int mask = 0; mask < mask_size; mask++) { + array_clear(&range); + for (unsigned int i = 0; i < mask_size; i++) { + if ((mask & (1 << i)) != 0) + seq_range_array_add(&range, min+i); + } + seq_range_array_invert(&range, min, max); + + inverse_mask = 0; + seq_range_array_iter_init(&iter, &range); n = 0; + while (seq_range_array_iter_nth(&iter, n++, &seq)) { + test_assert(seq >= min && seq <= max); + inverse_mask |= 1 << (seq-min); + } + mask_inside = ((1 << mask_size)-1); + test_assert_idx((inverse_mask & ~mask_inside) == 0, mask); + test_assert_idx(inverse_mask == (mask ^ mask_inside), mask); + } +} + +static void test_seq_range_array_invert(void) +{ + test_begin("seq_range_array_invert()"); + /* first numbers */ + for (unsigned int min = 0; min <= 7; min++) { + for (unsigned int max = min; max <= 7; max++) T_BEGIN { + test_seq_range_array_invert_minmax(min, max); + } T_END; + } + /* last numbers */ + for (uint64_t min = 0xffffffff-7; min <= 0xffffffff; min++) { + for (uint64_t max = min; max <= 0xffffffff; max++) T_BEGIN { + test_seq_range_array_invert_minmax(min, max); + } T_END; + } + test_end(); +} + +static void test_seq_range_array_invert_edges(void) +{ + static const struct { + int64_t a_seq1, a_seq2, b_seq1, b_seq2; + int64_t resa_seq1, resa_seq2, resb_seq1, resb_seq2; + } tests[] = { + { -1, -1, -1, -1, + 0, 0xffffffff, -1, -1 }, + /*{ 0, 0xffffffff, -1, -1, too large, will assert-crash + -1, -1, -1, -1 }, */ + { 0, 0xfffffffe, -1, -1, + 0xffffffff, 0xffffffff, -1, -1 }, + { 1, 0xfffffffe, -1, -1, + 0, 0, 0xffffffff, 0xffffffff }, + { 1, 0xffffffff, -1, -1, + 0, 0, -1, -1 }, + { 0, 0, 0xffffffff, 0xffffffff, + 1, 0xfffffffe, -1, -1 }, + { 0xffffffff, 0xffffffff, -1, -1, + 0, 0xfffffffe, -1, -1 }, + }; + ARRAY_TYPE(seq_range) range = ARRAY_INIT; + const struct seq_range *result; + unsigned int count; + + test_begin("seq_range_array_invert() edges"); + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) T_BEGIN { + t_array_init(&range, 10); + if (tests[i].a_seq1 != -1) + seq_range_array_add_range(&range, tests[i].a_seq1, tests[i].a_seq2); + if (tests[i].b_seq1 != -1) + seq_range_array_add_range(&range, tests[i].b_seq1, tests[i].b_seq2); + seq_range_array_invert(&range, 0, 0xffffffff); + + result = array_get(&range, &count); + if (tests[i].resa_seq1 == -1) + test_assert_idx(count == 0, i); + else { + test_assert(result[0].seq1 == tests[i].resa_seq1); + test_assert(result[0].seq2 == tests[i].resa_seq2); + if (tests[i].resb_seq1 == -1) + test_assert_idx(count == 1, i); + else { + test_assert(result[1].seq1 == tests[i].resb_seq1); + test_assert(result[1].seq2 == tests[i].resb_seq2); + } + } + } T_END; + test_end(); +} + +static void test_seq_range_create(ARRAY_TYPE(seq_range) *array, uint8_t byte) +{ + unsigned int i; + + array_clear(array); + for (i = 0; i < 8; i++) { + if ((byte & (1 << i)) != 0) + seq_range_array_add(array, i + 1); + } +} + +static void test_seq_range_array_have_common(void) +{ + ARRAY_TYPE(seq_range) arr1, arr2; + unsigned int i, j; + bool ret1, ret2, success = TRUE; + + t_array_init(&arr1, 8); + t_array_init(&arr2, 8); + for (i = 0; i < 256; i++) { + test_seq_range_create(&arr1, i); + for (j = 0; j < 256; j++) { + test_seq_range_create(&arr2, j); + ret1 = seq_range_array_have_common(&arr1, &arr2); + ret2 = (i & j) != 0; + if (ret1 != ret2) + success = FALSE; + } + } + test_out("seq_range_array_have_common()", success); +} + +void test_seq_range_array(void) +{ + test_seq_range_array_add_boundaries(); + test_seq_range_array_add_merge(); + test_seq_range_array_merge_n(); + test_seq_range_array_remove_nth(); + test_seq_range_array_remove_range(); + test_seq_range_array_invert(); + test_seq_range_array_invert_edges(); + test_seq_range_array_have_common(); + test_seq_range_array_random(); +} + +enum fatal_test_state fatal_seq_range_array(unsigned int stage) +{ + ARRAY_TYPE(seq_range) arr; + struct seq_range *range; + + t_array_init(&arr, 2); + switch (stage) { + case 0: + test_begin("seq_range_array fatals"); + test_expect_fatal_string("!seq_range_is_overflowed(array)"); + seq_range_array_add_range(&arr, 0, (uint32_t)-1); + return FATAL_TEST_FAILURE; + case 1: + seq_range_array_add_range(&arr, 1, (uint32_t)-1); + test_expect_fatal_string("!seq_range_is_overflowed(array)"); + seq_range_array_add(&arr, 0); + return FATAL_TEST_FAILURE; + case 2: + seq_range_array_add_range(&arr, 0, (uint32_t)-2); + test_expect_fatal_string("!seq_range_is_overflowed(array)"); + seq_range_array_add(&arr, (uint32_t)-1); + return FATAL_TEST_FAILURE; + case 3: + range = array_append_space(&arr); + range->seq2 = (uint32_t)-1; + test_expect_fatal_string("range->seq1 > 0 || range->seq2 < (uint32_t)-1"); + i_error("This shouldn't return: %u", seq_range_count(&arr)); + return FATAL_TEST_FAILURE; + case 4: + range = array_append_space(&arr); + range->seq2 = (uint32_t)-2; + test_assert(seq_range_count(&arr) == (uint32_t)-1); + + range = array_append_space(&arr); + range->seq1 = (uint32_t)-2; + range->seq2 = (uint32_t)-1; + test_expect_fatal_string("UINT_MAX - seq_count >= seq_range_length(range)"); + i_error("This shouldn't return: %u", seq_range_count(&arr)); + return FATAL_TEST_FAILURE; + } + test_end(); + return FATAL_TEST_FINISHED; +} diff --git a/src/lib/test-seq-set-builder.c b/src/lib/test-seq-set-builder.c new file mode 100644 index 0000000..c449caf --- /dev/null +++ b/src/lib/test-seq-set-builder.c @@ -0,0 +1,132 @@ +/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "seq-set-builder.h" + +static void test_seq_set_builder_add(void) +{ + struct seqset_builder *seq_set_builder; + + test_begin("seq set builder add"); + string_t *test_str = t_str_new(128); + str_append(test_str, "UID COPY "); + seq_set_builder = seqset_builder_init(test_str); + seqset_builder_add(seq_set_builder, 1); + seqset_builder_add(seq_set_builder, 3); + seqset_builder_add(seq_set_builder, 6); + seqset_builder_add(seq_set_builder, 7); + seqset_builder_add(seq_set_builder, 8); + seqset_builder_add(seq_set_builder, 9); + seqset_builder_add(seq_set_builder, 10); + seqset_builder_add(seq_set_builder, 12); + seqset_builder_deinit(&seq_set_builder); + test_assert_strcmp(str_c(test_str), "UID COPY 1,3,6:10,12"); + + str_truncate(test_str, 0); + seq_set_builder = seqset_builder_init(test_str); + seqset_builder_add(seq_set_builder, 99999); + seqset_builder_add(seq_set_builder, 100000); + seqset_builder_add(seq_set_builder, 5); + seqset_builder_add(seq_set_builder, 7); + seqset_builder_add(seq_set_builder, 9); + seqset_builder_add(seq_set_builder, 10); + seqset_builder_add(seq_set_builder, 120); + seqset_builder_add(seq_set_builder, 121); + seqset_builder_add(seq_set_builder, 122); + seqset_builder_add(seq_set_builder, 125); + seqset_builder_deinit(&seq_set_builder); + test_assert_strcmp(str_c(test_str), "99999:100000,5,7,9:10,120:122,125"); + + str_truncate(test_str, 0); + str_append(test_str, "UID COPY "); + seq_set_builder = seqset_builder_init(test_str); + seqset_builder_add(seq_set_builder, 287409); + seqset_builder_add(seq_set_builder, 287410); + seqset_builder_deinit(&seq_set_builder); + test_assert_strcmp(str_c(test_str), "UID COPY 287409:287410"); + + str_truncate(test_str, 0); + str_append(test_str, "UID COPY 287409,"); + seq_set_builder = seqset_builder_init(test_str); + seqset_builder_add(seq_set_builder, 287410); + seqset_builder_add(seq_set_builder, 287411); + test_assert_strcmp(str_c(test_str), "UID COPY 287409,287410:287411,"); + seqset_builder_deinit(&seq_set_builder); + test_assert_strcmp(str_c(test_str), "UID COPY 287409,287410:287411"); + + str_truncate(test_str, 0); + seq_set_builder = seqset_builder_init(test_str); + seqset_builder_add(seq_set_builder, 4294967289); + seqset_builder_add(seq_set_builder, 4294967291); + seqset_builder_add(seq_set_builder, 4294967293); + seqset_builder_add(seq_set_builder, 4294967294); + seqset_builder_add(seq_set_builder, 4294967295); + test_assert_strcmp(str_c(test_str), "4294967289,4294967291,4294967293:4294967295,"); + seqset_builder_deinit(&seq_set_builder); + test_assert_strcmp(str_c(test_str), "4294967289,4294967291,4294967293:4294967295"); + + str_truncate(test_str, 0); + str_append(test_str, ";j;,"); + seq_set_builder = seqset_builder_init(test_str); + test_assert_strcmp(str_c(test_str), ";j;,"); + seqset_builder_deinit(&seq_set_builder); + test_assert_strcmp(str_c(test_str), ";j;,"); + + test_end(); +} + +static void test_seq_set_builder_try_add(void) +{ + struct seqset_builder *seq_set_builder; + + test_begin("seq set builder try add"); + + string_t *test_str = t_str_new(128); + str_append(test_str, "UID MOVE "); + + seq_set_builder = seqset_builder_init(test_str); + test_assert(seqset_builder_try_add(seq_set_builder, 20, 1)); + test_assert(seqset_builder_try_add(seq_set_builder, 20, 3)); + test_assert(seqset_builder_try_add(seq_set_builder, 20, 5)); + test_assert(seqset_builder_try_add(seq_set_builder, 20, 7)); + test_assert(seqset_builder_try_add(seq_set_builder, 20, 9)); + test_assert(19 == str_len(test_str)); + + test_assert_strcmp(str_c(test_str), "UID MOVE 1,3,5,7,9,"); + + test_assert(!seqset_builder_try_add(seq_set_builder, 20, 11)); + test_assert(str_len(test_str) <= 20); + test_assert_strcmp(str_c(test_str), "UID MOVE 1,3,5,7,9,"); + + test_assert(seqset_builder_try_add(seq_set_builder, 21, 2)); + test_assert(str_len(test_str) <= 21); + test_assert_strcmp(str_c(test_str), "UID MOVE 1,3,5,7,9,2,"); + + test_assert(!seqset_builder_try_add(seq_set_builder, 20, 15)); + test_assert(seqset_builder_try_add(seq_set_builder, 24, 13)); + test_assert(!seqset_builder_try_add(seq_set_builder, 24, 17)); + test_assert(str_len(test_str) <= 24); + test_assert_strcmp(str_c(test_str), "UID MOVE 1,3,5,7,9,2,13,"); + seqset_builder_deinit(&seq_set_builder); + + str_truncate(test_str, 0); + seq_set_builder = seqset_builder_init(test_str); + test_assert(seqset_builder_try_add(seq_set_builder, 32, 4294967289)); + test_assert(seqset_builder_try_add(seq_set_builder, 32, 4294967291)); + test_assert(seqset_builder_try_add(seq_set_builder, 32, 4294967292)); + test_assert(!seqset_builder_try_add(seq_set_builder, 32, 4294967293)); + test_assert(seqset_builder_try_add(seq_set_builder, 50, 4294967293)); + test_assert(seqset_builder_try_add(seq_set_builder, 50, 4294967295)); + test_assert_strcmp(str_c(test_str), "4294967289,4294967291:4294967293,4294967295,"); + seqset_builder_deinit(&seq_set_builder); + test_assert_strcmp(str_c(test_str), "4294967289,4294967291:4294967293,4294967295"); + + test_end(); +} + +void test_seq_set_builder(void) +{ + test_seq_set_builder_add(); + test_seq_set_builder_try_add(); +} diff --git a/src/lib/test-stats-dist.c b/src/lib/test-stats-dist.c new file mode 100644 index 0000000..795c22f --- /dev/null +++ b/src/lib/test-stats-dist.c @@ -0,0 +1,132 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "stats-dist.h" +#include "sort.h" +#include "math.h" + +#define DBL_EQ(a, b) (fabs((a)-(b)) < 0.001) + +static void +test_stats_dist_verify(struct stats_dist *t, const int64_t *input, + unsigned int input_size) +{ + uint64_t min = INT_MAX, max = 0, sum = 0; + uint64_t *copy; + unsigned int i; + + i_assert(input_size > 0); + + copy = i_new(uint64_t, input_size); + for (i = 0; i < input_size; i++) { + uint64_t value = input[i]; + + if (min > value) + min = value; + if (max < value) + max = value; + sum += value; + copy[i] = value; + } + i_qsort(copy, input_size, sizeof(*copy), uint64_cmp); + + test_assert_idx(stats_dist_get_count(t) == input_size, input_size); + test_assert_idx(stats_dist_get_sum(t) == sum, input_size); + test_assert_idx(stats_dist_get_min(t) == min, input_size); + test_assert_idx(stats_dist_get_max(t) == max, input_size); + test_assert_idx(DBL_EQ(stats_dist_get_avg(t), (double)sum/input_size), + input_size); + + /* these aren't always fully accurate: */ + test_assert_idx(stats_dist_get_median(t) >= copy[(input_size-1)/2] && + stats_dist_get_median(t) <= copy[input_size/2], + input_size); + /* when we have 20 elements, [19] is the max, not the 95th %ile, so subtract 1 */ + test_assert_idx(stats_dist_get_95th(t) == copy[input_size*95/100 - ((input_size%20) == 0 ? 1 : 0)], + input_size); + + i_free(copy); +} + +static void test_stats_dist_get_variance(void) +{ + static const struct { + int64_t in[10]; + double out; + } tests[] = { + { .in = { 2, 2, 2, -1 }, .out = 0.0 }, + { .in = { -1 }, .out = 0.0 }, + { .in = { 1, 2, 3, 4, 5, 6, 7, 8, -1 }, .out = 5.25 }, + }; + + struct stats_dist *t; + unsigned int i, j; + + test_begin("stats_dists_get_variance"); + + for (i = 0; i < N_ELEMENTS(tests); i++) { + t = stats_dist_init(); + for (j = 0; tests[i].in[j] >= 0; j++) { + stats_dist_add(t, tests[i].in[j]); + test_stats_dist_verify(t, tests[i].in, j+1); + } + test_assert_idx(DBL_EQ(stats_dist_get_variance(t), + tests[i].out), i); + + stats_dist_deinit(&t); + } + + test_end(); +} + +void test_stats_dist(void) +{ + static int64_t test_input1[] = { + 20, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, -1 + }; + static int64_t test_input2[] = { + 20, 21, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, -1 + }; + static int64_t test_input3[] = { + 20, 21, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 22, -1 + }; + static int64_t *test_inputs[] = { + test_input1, test_input2, test_input3 + }; + struct stats_dist *t; + unsigned int i, j; + + for (i = 0; i < N_ELEMENTS(test_inputs); i++) { + test_begin(t_strdup_printf("stats_dists %u", i)); + t = stats_dist_init(); + for (j = 0; test_inputs[i][j] >= 0; j++) { + stats_dist_add(t, test_inputs[i][j]); + test_stats_dist_verify(t, test_inputs[i], j+1); + } + stats_dist_reset(t); + test_assert(stats_dist_get_count(t) == 0); + test_assert(stats_dist_get_max(t) == 0); + stats_dist_deinit(&t); + test_end(); + } + + test_begin("stats_dists large"); + t = stats_dist_init(); + for (i = 0; i < 10000; i++) + stats_dist_add(t, i); + test_assert(stats_dist_get_count(t) == i); + test_assert(stats_dist_get_sum(t) == (i-1)*i/2); + test_assert(stats_dist_get_min(t) == 0); + test_assert(stats_dist_get_max(t) == i-1); + test_assert(DBL_EQ(stats_dist_get_avg(t), 4999.500000)); + /* just test that these work: */ + test_assert(stats_dist_get_median(t) > 0 && stats_dist_get_median(t) < i-1); + test_assert(stats_dist_get_95th(t) > 0 && stats_dist_get_95th(t) < i-1); + stats_dist_deinit(&t); + test_end(); + + test_stats_dist_get_variance(); +} diff --git a/src/lib/test-str-find.c b/src/lib/test-str-find.c new file mode 100644 index 0000000..a6a6340 --- /dev/null +++ b/src/lib/test-str-find.c @@ -0,0 +1,86 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str-find.h" + +static const char *str_find_text = "xababcd"; + +static bool test_str_find_substring(const char *key, int expected_pos) +{ + const unsigned char *text = (const unsigned char *)str_find_text; + const unsigned int text_len = strlen(str_find_text); + struct str_find_context *ctx; + unsigned int i, j, pos, max, offset; + bool ret; + + ctx = str_find_init(pool_datastack_create(), key); + /* divide text into every possible block combination and test that + it matches */ + i_assert(text_len > 0); + max = 1U << (text_len-1); + for (i = 0; i < max; i++) { + str_find_reset(ctx); + pos = 0; offset = 0; ret = FALSE; + for (j = 0; j < text_len; j++) { + if ((i & (1 << j)) != 0) { + if (str_find_more(ctx, text+pos, j-pos+1)) { + ret = TRUE; + break; + } + offset += j-pos + 1; + pos = j + 1; + } + } + if (pos != text_len && !ret) { + if (str_find_more(ctx, text+pos, j-pos)) + ret = TRUE; + } + if (expected_pos < 0) { + if (ret) + return FALSE; + } else { + if (!ret) + return FALSE; + + pos = str_find_get_match_end_pos(ctx) + + offset - strlen(key); + if ((int)pos != expected_pos) + return FALSE; + } + } + return TRUE; +} + +struct str_find_input { + const char *str; + int pos; +}; + +void test_str_find(void) +{ + static const char *fail_input[] = { + "xabc", + "xabd", + "abd" + }; + unsigned int idx, len; + const char *key, *p; + unsigned int i; + bool success = TRUE; + + for (idx = 0; idx < strlen(str_find_text); idx++) { + for (len = strlen(str_find_text)-idx; len > 0; len--) { + /* we'll get a search key for all substrings of text */ + T_BEGIN { + key = t_strndup(str_find_text + idx, len); + p = strstr(str_find_text, key); + success = test_str_find_substring(key, p - str_find_text); + } T_END; + if (!success) + break; + } + } + for (i = 0; i < N_ELEMENTS(fail_input) && success; i++) + success = test_str_find_substring(fail_input[i], -1); + test_out("str_find()", success); +} diff --git a/src/lib/test-str-sanitize.c b/src/lib/test-str-sanitize.c new file mode 100644 index 0000000..d3c9716 --- /dev/null +++ b/src/lib/test-str-sanitize.c @@ -0,0 +1,127 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "str-sanitize.h" + +struct str_sanitize_test { + const char *str; + unsigned int max_len; + const char *sanitized; /* NULL for no change */ +}; + +static void test_str_sanitize_max_bytes(void) +{ + static const struct str_sanitize_test tests[] = { + { NULL, 2, NULL }, + { "", 2, NULL }, + { "a", 2, NULL }, + { "ab", 2, NULL }, + { "abc", 2, "..." }, + { "abcd", 3, "..." }, + { "abcde", 4, "a..." }, + { "\xD1\x81", 1, "..." }, + { "\xD1\x81", 2, "\xD1\x81" }, + { "\xD1\x81", 3, NULL }, + { "\xC3\xA4\xC3\xA4zyxa", 1, "..." }, + { "\xC3\xA4\xC3\xA4zyxa", 2, "..." }, + { "\xC3\xA4\xC3\xA4zyxa", 3, "..." }, + { "\xC3\xA4\xC3\xA4zyxa", 4, "..." }, + { "\xC3\xA4\xC3\xA4zyxa", 5, "\xC3\xA4..." }, + { "\xC3\xA4\xC3\xA4zyxa", 6, "\xC3\xA4..." }, + { "\xC3\xA4\xC3\xA4zyxa", 7, "\xC3\xA4\xC3\xA4..." }, + { "\xC3\xA4\xC3\xA4zyxa", 8, "\xC3\xA4\xC3\xA4zyxa" }, + { "\001x\x1fy\x81", 10, "?x?y?" } + }; + const char *str; + string_t *str2; + unsigned int i; + + test_begin("str_sanitize"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + str = str_sanitize(tests[i].str, tests[i].max_len); + if (tests[i].sanitized != NULL) + test_assert_idx(null_strcmp(str, tests[i].sanitized) == 0, i); + else + test_assert_idx(str == tests[i].str, i); + } + test_end(); + + test_begin("str_sanitize_append"); + str2 = t_str_new(128); + for (i = 0; i < N_ELEMENTS(tests); i++) { + if (tests[i].str == NULL) + continue; + str_truncate(str2, 0); + str_append(str2, "1234567890"); + str_sanitize_append(str2, tests[i].str, tests[i].max_len); + + test_assert_idx(str_begins(str_c(str2), "1234567890"), i); + if (tests[i].sanitized != NULL) + test_assert_idx(strcmp(str_c(str2)+10, tests[i].sanitized) == 0, i); + else + test_assert_idx(strcmp(str_c(str2)+10, tests[i].str) == 0, i); + } + test_end(); +} + +static void test_str_sanitize_max_codepoints(void) +{ + static const struct str_sanitize_test tests[] = { + { NULL, 2, NULL }, + { "", 2, NULL }, + { "a", 2, NULL }, + { "ab", 2, NULL }, + { "abc", 2, "a\xE2\x80\xA6" }, + { "abcd", 3, "ab\xE2\x80\xA6" }, + { "abcde", 4, "abc\xE2\x80\xA6" }, + { "\xD1\x81", 1, "\xD1\x81" }, + { "\xD1\x81", 2, "\xD1\x81" }, + { "\xD1\x81", 3, NULL }, + { "\xC3\xA4\xC3\xA4zyxa", 1, "\xE2\x80\xA6" }, + { "\xC3\xA4\xC3\xA4zyxa", 2, "\xC3\xA4\xE2\x80\xA6" }, + { "\xC3\xA4\xC3\xA4zyxa", 3, "\xC3\xA4\xC3\xA4\xE2\x80\xA6" }, + { "\xC3\xA4\xC3\xA4zyxa", 4, "\xC3\xA4\xC3\xA4z\xE2\x80\xA6" }, + { "\xC3\xA4\xC3\xA4zyxa", 5, "\xC3\xA4\xC3\xA4zy\xE2\x80\xA6" }, + { "\xC3\xA4\xC3\xA4zyxa", 6, "\xC3\xA4\xC3\xA4zyxa" }, + { "\xC3\xA4\xC3\xA4zyxa", 7, "\xC3\xA4\xC3\xA4zyxa" }, + { "\xC3\xA4\xC3\xA4zyxa", 8, "\xC3\xA4\xC3\xA4zyxa" }, + { "\001x\x1fy\x81", 10, "\xEF\xBF\xBDx\xEF\xBF\xBDy\xEF\xBF\xBD" } + }; + const char *str; + string_t *str2; + unsigned int i; + + test_begin("str_sanitize_utf8"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + str = str_sanitize_utf8(tests[i].str, tests[i].max_len); + if (tests[i].sanitized != NULL) + test_assert_idx(null_strcmp(str, tests[i].sanitized) == 0, i); + else + test_assert_idx(str == tests[i].str, i); + } + test_end(); + + test_begin("str_sanitize_append_utf8"); + str2 = t_str_new(128); + for (i = 0; i < N_ELEMENTS(tests); i++) { + if (tests[i].str == NULL) + continue; + str_truncate(str2, 0); + str_append(str2, "1234567890"); + str_sanitize_append_utf8(str2, tests[i].str, tests[i].max_len); + + test_assert_idx(strncmp(str_c(str2), "1234567890", 10) == 0, i); + if (tests[i].sanitized != NULL) + test_assert_idx(strcmp(str_c(str2)+10, tests[i].sanitized) == 0, i); + else + test_assert_idx(strcmp(str_c(str2)+10, tests[i].str) == 0, i); + } + test_end(); +} + +void test_str_sanitize(void) +{ + test_str_sanitize_max_bytes(); + test_str_sanitize_max_codepoints(); +} diff --git a/src/lib/test-str-table.c b/src/lib/test-str-table.c new file mode 100644 index 0000000..ea97d14 --- /dev/null +++ b/src/lib/test-str-table.c @@ -0,0 +1,33 @@ +/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str-table.h" + +void test_str_table(void) +{ + struct str_table *table; + const char *key1, *key2, *key1_copy, *key2_copy; + + test_begin("str_table"); + table = str_table_init(); + + key1 = str_table_ref(table, "str1"); + key2 = str_table_ref(table, "str2"); + test_assert(key1 != key2); + key1_copy = str_table_ref(table, "str1"); + test_assert(key1_copy == key1); + key2_copy = str_table_ref(table, "str2"); + test_assert(key2_copy == key2); + + str_table_unref(table, &key1); + test_assert(key1 == NULL); + str_table_unref(table, &key1_copy); + + str_table_unref(table, &key2); + str_table_unref(table, &key2_copy); + test_assert(str_table_is_empty(table)); + + str_table_deinit(&table); + test_assert(table == NULL); + test_end(); +} diff --git a/src/lib/test-str.c b/src/lib/test-str.c new file mode 100644 index 0000000..e20e7f9 --- /dev/null +++ b/src/lib/test-str.c @@ -0,0 +1,182 @@ +/* Copyright (c) 2012-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "unichar.h" +#include "str.h" + +static void test_str_append(void) +{ + string_t *str = t_str_new(32); + string_t *str2 = t_str_new(32); + + test_begin("str_append_*()"); + str_append(str, "foo"); + str_append_c(str, '|'); + str_append_c(str, '\0'); + test_assert(str->used == 5 && memcmp(str_data(str), "foo|\0", 5) == 0); + + str_append(str2, "sec"); + str_append_c(str2, '\0'); + str_append(str2, "ond"); + str_append_str(str, str2); + test_assert(str->used == 5+7 && memcmp(str_data(str), "foo|\0sec\0ond", 5+7) == 0); + + test_end(); +} + +static void test_str_c(void) +{ + string_t *str; + unsigned int i, j; + + test_begin("str_c()"); + str = t_str_new(0); + T_BEGIN { + (void)str_c(str); + } T_END; + + for (i = 0; i < 32; i++) T_BEGIN { + str = t_str_new(15); + for (j = 0; j < i; j++) + str_append_c(str, 'x'); + T_BEGIN { + (void)str_c(str); + } T_END; + } T_END; + test_end(); +} + +static void test_str_insert(void) +{ + string_t *str = t_str_new(32); + + test_begin("str_insert()"); + str_insert(str, 0, "foo"); + str_insert(str, 3, ">"); + str_insert(str, 3, "bar"); + str_insert(str, 0, "<"); + test_assert(str->used == 8 && memcmp(str_data(str), "<foobar>", 8) == 0); + + str_insert(str, 10, "!"); + test_assert(str->used == 11 && memcmp(str_data(str), "<foobar>\0\0!", 11) == 0); + + test_end(); +} + +static void test_str_delete(void) +{ + string_t *str = t_str_new(32); + + test_begin("str_delete()"); + str_delete(str, 0, 100); + str_append(str, "123456"); + str_delete(str, 0, 1); + str_delete(str, 4, 1); + str_delete(str, 1, 1); + test_assert(str->used == 3 && memcmp(str_data(str), "245", 3) == 0); + + str_delete(str, 1, 2); + test_assert(str->used == 1 && memcmp(str_data(str), "2", 1) == 0); + + str_append(str, "bar"); + str_delete(str, 1, 100); + test_assert(str->used == 1 && memcmp(str_data(str), "2", 1) == 0); + + test_end(); +} + +static void test_str_append_max(void) +{ + string_t *str = t_str_new(32); + + test_begin("str_append_max()"); + str_append_max(str, "foo", 0); + test_assert(str->used == 0); + + str_append_max(str, "\0foo", 4); + test_assert(str->used == 0); + + str_append_max(str, "foo", 3); + test_assert(str->used == 3 && memcmp(str_data(str), "foo", 3) == 0); + str_truncate(str, 0); + + str_append_max(str, "foo", 2); + test_assert(str->used == 2 && memcmp(str_data(str), "fo", 2) == 0); + str_truncate(str, 0); + + str_append_max(str, "foo\0bar", 7); + test_assert(str->used == 3 && memcmp(str_data(str), "foo", 3) == 0); + str_truncate(str, 0); + test_end(); +} + +static void test_str_truncate(void) +{ + string_t *str = t_str_new(8); + int i; + + test_begin("str_truncate()"); + str_append(str, "123456"); + for (i = 100; i >= 6; i--) { + str_truncate(str, i); + test_assert_idx(str_len(str) == 6, i); + } + for (; i >= 0; i--) { + str_truncate(str, i); + test_assert_idx(str_len(str) == (unsigned int)i, i); + } + test_end(); +} + +static void test_str_truncate_utf8(void) +{ + string_t *str = t_str_new(8); + int i; + + test_begin("str_truncate_utf8()"); + str_append(str, "123456"); + for (i = 100; i >= 6; i--) { + str_truncate_utf8(str, i); + test_assert_idx(str_len(str) == 6, i); + } + for (; i >= 0; i--) { + str_truncate_utf8(str, i); + test_assert_idx(str_len(str) == (unsigned int)i, i); + } + + str_append(str, "\xE4\xB8\x80\xE4\xBa\x8C\xE4\xB8\x89" + "\xE5\x9b\x9b\xE4\xBa\x94\xE5\x85\xAD"); + for (i = 100; i >= 18; i--) { + str_truncate_utf8(str, i); + test_assert_idx(str_len(str) == 18, i); + } + for (; i >= 0; i--) { + str_truncate_utf8(str, i); + test_assert_idx(str_len(str) % 3 == 0, i); + test_assert_idx((str_len(str) / 3) == ((unsigned int)i / 3), i); + } + + str_append(str, "\xE4\xB8\x80""1""\xE4\xBa\x8C""2""\xE4\xB8\x89""3" + "\xE5\x9b\x9b""4""\xE4\xBa\x94""5""\xE5\x85\xAD""6"); + for (i = 100; i >= 24; i--) { + str_truncate_utf8(str, i); + test_assert_idx(str_len(str) == 24, i); + } + for (; i >= 0; i--) { + str_truncate_utf8(str, i); + test_assert_idx(uni_utf8_data_is_valid(str_data(str), + str_len(str)), i); + } + test_end(); +} + +void test_str(void) +{ + test_str_append(); + test_str_c(); + test_str_insert(); + test_str_delete(); + test_str_append_max(); + test_str_truncate(); + test_str_truncate_utf8(); +} diff --git a/src/lib/test-strescape.c b/src/lib/test-strescape.c new file mode 100644 index 0000000..5b13232 --- /dev/null +++ b/src/lib/test-strescape.c @@ -0,0 +1,197 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "strescape.h" + +struct strinput { + const char *input; + const char *output; +}; + +static const char tabescaped_input[] = "\0011\001t\001r\001nplip\001n"; +static const char tabescaped_input_with_nul[] = "\0011\001t\001r\001nplip\001n\0010"; +static const char tabunescaped_input[] = "\001\t\r\nplip\n"; + +static const char *wrong_tabescaped_input = "a\001\001b\001\nc\0011\001t\001r\001nplip\001n"; +static const char *wrong_tabescaped_output = "a\001b\nc\001\t\r\nplip\n"; + +static struct { + const char *input; + const char *const *output; +} strsplit_tests[] = { + { /*tabescaped_input3*/NULL, (const char *const []) { + tabunescaped_input, + tabunescaped_input, + tabunescaped_input, + "", + NULL + } }, + { "", (const char *const []) { NULL } }, + { "\t", (const char *const []) { "", "", NULL } }, + { tabescaped_input, (const char *const []) { + tabunescaped_input, + NULL + } }, +}; + +static void test_str_escape(void) +{ + static const struct strinput unesc[] = { + { "foo", "foo" }, + { "\\\\\\\\\\\"\\\"\\\'\\\'", "\\\\\"\"\'\'" }, + { "\\a\\n\\r\\", "anr" } + }; + static const struct strinput tabesc[] = { + { "foo", "foo" }, + { "\001", "\0011" }, + { "\t", "\001t" }, + { "\r", "\001r" }, + { "\n", "\001n" }, + { "\001\001\t\t\r\r\n\n", "\0011\0011\001t\001t\001r\001r\001n\001n" } + }; + unsigned char buf[1 << CHAR_BIT]; + const char *escaped, *tabstr, *unesc_str; + string_t *str; + unsigned int i; + + test_begin("str_escape"); + for (i = 1; i < sizeof(buf); i++) + buf[i-1] = i; + buf[i-1] = '\0'; + + escaped = str_escape((char *)buf); + test_assert(strlen(escaped) == (1 << CHAR_BIT) - 1 + 3); + test_assert(escaped['\"'-1] == '\\'); /* 34 */ + test_assert(escaped['\"'] == '\"'); + test_assert(escaped['\''+1-1] == '\\'); /* 39 */ + test_assert(escaped['\''+1] == '\''); + test_assert(escaped['\\'+2-1] == '\\'); /* 92 */ + test_assert(escaped['\\'+2] == '\\'); + test_assert(strcmp(str_escape("\\\\\"\"\'\'"), + "\\\\\\\\\\\"\\\"\\\'\\\'") == 0); + test_end(); + + test_begin("str_nescape"); + + escaped = str_nescape("\"escape only first but not 'this'", 10); + test_assert(strcmp(escaped, "\\\"escape on") == 0); + + escaped = str_nescape("\"hello\"\0\"world\"", 15); + test_assert(memcmp(escaped, "\\\"hello\\\"\0\\\"world\\\"", 19) == 0); + + test_end(); + + str = t_str_new(256); + test_begin("str_unescape"); + for (i = 0; i < N_ELEMENTS(unesc); i++) { + test_assert(strcmp(str_unescape(t_strdup_noconst(unesc[i].input)), + unesc[i].output) == 0); + str_truncate(str, 0); + str_append_unescaped(str, unesc[i].input, strlen(unesc[i].input)); + test_assert(strcmp(str_c(str), unesc[i].output) == 0); + } + test_end(); + + test_begin("str_unescape_next"); + escaped = "foo\"bar\\\"b\\\\az\"plop"; + test_assert(str_unescape_next(&escaped, &unesc_str) == 0); + test_assert(strcmp(unesc_str, "foo") == 0); + test_assert(str_unescape_next(&escaped, &unesc_str) == 0); + test_assert(strcmp(unesc_str, "bar\"b\\az") == 0); + test_assert(str_unescape_next(&escaped, &unesc_str) == -1); + escaped = "foo\\"; + test_assert(str_unescape_next(&escaped, &unesc_str) == -1); + test_end(); + + test_begin("str_tabescape"); + for (i = 0; i < N_ELEMENTS(tabesc); i++) { + test_assert(strcmp(t_str_tabunescape(tabesc[i].output), + tabesc[i].input) == 0); + test_assert(strcmp(str_tabunescape(t_strdup_noconst(tabesc[i].output)), + tabesc[i].input) == 0); + test_assert(strcmp(str_tabescape(tabesc[i].input), + tabesc[i].output) == 0); + str_truncate(str, 0); + str_append_tabunescaped(str, tabesc[i].output, strlen(tabesc[i].output)); + test_assert(strcmp(str_c(str), tabesc[i].input) == 0); + } + str_truncate(str, 0); + tabstr = "\0012\001l\001"; + str_append_tabunescaped(str, tabstr, strlen(tabstr)); + test_assert(strcmp(str_c(str), "2l") == 0); + test_assert(strcmp(str_c(str), str_tabunescape(t_strdup_noconst(tabstr))) == 0); + test_end(); +} + +static void test_tabescape(void) +{ + string_t *str = t_str_new(128); + + test_begin("string tabescaping"); + test_assert(strcmp(str_tabescape(tabunescaped_input), tabescaped_input) == 0); + + str_append_tabescaped(str, tabunescaped_input); + test_assert(strcmp(str_c(str), tabescaped_input) == 0); + + /* test escaping the trailing NUL as well */ + str_truncate(str, 0); + str_append_tabescaped_n(str, (const unsigned char *)tabunescaped_input, + strlen(tabunescaped_input)+1); + test_assert_strcmp(str_c(str), tabescaped_input_with_nul); + + /* unescaping */ + str_truncate(str, 0); + str_append_tabunescaped(str, tabescaped_input, strlen(tabescaped_input)); + test_assert(strcmp(str_c(str), tabunescaped_input) == 0); + + test_assert(strcmp(str_tabunescape(t_strdup_noconst(tabescaped_input)), tabunescaped_input) == 0); + test_assert(strcmp(t_str_tabunescape(tabescaped_input), tabunescaped_input) == 0); + + /* unescaping with wrongly written tabescape-input */ + str_truncate(str, 0); + str_append_tabunescaped(str, wrong_tabescaped_input, strlen(wrong_tabescaped_input)); + test_assert(strcmp(str_c(str), wrong_tabescaped_output) == 0); + + test_assert(strcmp(str_tabunescape(t_strdup_noconst(wrong_tabescaped_input)), wrong_tabescaped_output) == 0); + test_assert(strcmp(t_str_tabunescape(wrong_tabescaped_input), wrong_tabescaped_output) == 0); + + test_end(); +} + +static void test_strsplit_tabescaped(void) +{ + const char *const *args; + + test_begin("*_strsplit_tabescaped()"); + for (unsigned int i = 0; i < N_ELEMENTS(strsplit_tests); i++) { + args = t_strsplit_tabescaped(strsplit_tests[i].input); + for (unsigned int j = 0; strsplit_tests[i].output[j] != NULL; j++) + test_assert_idx(null_strcmp(strsplit_tests[i].output[j], args[j]) == 0, i); + } + test_end(); +} + +static void test_strsplit_tabescaped_inplace(void) +{ + const char *const *args; + + test_begin("*_strsplit_tabescaped_inplace()"); + for (unsigned int i = 0; i < N_ELEMENTS(strsplit_tests); i++) { + char *input = t_strdup_noconst(strsplit_tests[i].input); + args = t_strsplit_tabescaped_inplace(input); + for (unsigned int j = 0; strsplit_tests[i].output[j] != NULL; j++) + test_assert_idx(null_strcmp(strsplit_tests[i].output[j], args[j]) == 0, i); + } + test_end(); +} + +void test_strescape(void) +{ + strsplit_tests[0].input = t_strdup_printf("%s\t%s\t%s\t", + tabescaped_input, tabescaped_input, tabescaped_input); + test_str_escape(); + test_tabescape(); + test_strsplit_tabescaped(); + test_strsplit_tabescaped_inplace(); +} diff --git a/src/lib/test-strfuncs.c b/src/lib/test-strfuncs.c new file mode 100644 index 0000000..b546384 --- /dev/null +++ b/src/lib/test-strfuncs.c @@ -0,0 +1,640 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "array.h" + +static void test_p_strdup(void) +{ + test_begin("p_strdup()"); + test_assert(p_strdup(default_pool, NULL) == NULL); + + const char *src = "foo"; + char *str = p_strdup(default_pool, src); + test_assert(str != src && str != NULL && strcmp(src, str) == 0); + p_free(default_pool, str); + + test_end(); +} + +static void test_p_strndup(void) +{ + struct { + const char *input; + const char *output; + size_t len; + } tests[] = { + { "foo", "fo", 2 }, + { "foo", "foo", 3 }, + { "foo", "foo", 4 }, + { "foo\0more", "foo", 8 }, + }; + test_begin("p_strndup()"); + + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { + char *str = p_strndup(default_pool, tests[i].input, + tests[i].len); + test_assert_strcmp_idx(str, tests[i].output, i); + p_free(default_pool, str); + } + test_end(); +} + +static void test_p_strdup_empty(void) +{ + test_begin("p_strdup_empty()"); + test_assert(p_strdup_empty(default_pool, NULL) == NULL); + test_assert(p_strdup_empty(default_pool, "") == NULL); + + const char *src = "foo"; + char *str = p_strdup_empty(default_pool, src); + test_assert(str != src && str != NULL && strcmp(src, str) == 0); + p_free(default_pool, str); + + test_end(); +} + +static void test_p_strdup_until(void) +{ + const char src[] = "foo\0bar"; + char *str; + + test_begin("p_strdup_until()"); + str = p_strdup_until(default_pool, src, src+2); + test_assert(strcmp(str, "fo") == 0); + p_free(default_pool, str); + + str = p_strdup_until(default_pool, src, src+3); + test_assert(strcmp(str, "foo") == 0); + p_free(default_pool, str); + + /* \0 is ignored */ + str = p_strdup_until(default_pool, src, src+7); + test_assert(memcmp(str, src, sizeof(src)) == 0); + p_free(default_pool, str); + + str = p_strdup_until(default_pool, src, src+8); + test_assert(memcmp(str, src, sizeof(src)) == 0); + p_free(default_pool, str); + + test_end(); +} + +static void test_p_strarray_dup(void) +{ + const char *input[][3] = { + { NULL }, + { "a", NULL }, + { "foobar", NULL }, + { "a", "foo", NULL } + }; + const char **ret; + unsigned int i, j; + + test_begin("p_strarray_dup"); + + for (i = 0; i < N_ELEMENTS(input); i++) { + ret = p_strarray_dup(default_pool, input[i]); + for (j = 0; input[i][j] != NULL; j++) { + test_assert(strcmp(input[i][j], ret[j]) == 0); + test_assert(input[i][j] != ret[j]); + } + test_assert(ret[j] == NULL); + i_free(ret); + } + test_end(); +} + +static void test_t_strsplit(void) +{ + struct { + const char *input; + const char *const *output; + } tests[] = { + /* empty string -> empty array. was this perhaps a mistake for + the API to do this originally?.. can't really change now + anyway. */ + { "", (const char *const []) { NULL } }, + { "\n", (const char *const []) { "", "", NULL } }, + { "\n\n", (const char *const []) { "", "", "", NULL } }, + { "foo", (const char *const []) { "foo", NULL } }, + { "foo\n", (const char *const []) { "foo", "", NULL } }, + { "foo\nbar", (const char *const []) { "foo", "bar", NULL } }, + { "foo\nbar\n", (const char *const []) { "foo", "bar", "", NULL } }, + { "\nfoo\n\nbar\n\n", (const char *const []) { "", "foo", "", "bar", "", "", NULL } }, + }; + const char *const *args, *const *args2, *const *args3; + + test_begin("t_strsplit"); + + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { + /* split_str_fast() with single separator */ + args = t_strsplit(tests[i].input, "\n"); + /* split_str_slow() with a secondary separator */ + args2 = t_strsplit(tests[i].input, "\r\n"); + /* also as suffix */ + args3 = t_strsplit(tests[i].input, "\n\r"); + for (unsigned int j = 0; tests[i].output[j] != NULL; j++) { + test_assert_idx(null_strcmp(tests[i].output[j], args[j]) == 0, i); + test_assert_idx(null_strcmp(args[j], args2[j]) == 0, i); + test_assert_idx(null_strcmp(args[j], args3[j]) == 0, i); + } + } + test_end(); +} + +static void test_t_strsplit_spaces(void) +{ + struct { + const char *input; + const char *const *output; + } tests[] = { + /* empty strings */ + { "", (const char *const []) { NULL } }, + { "\n", (const char *const []) { NULL } }, + { "\n\n", (const char *const []) { NULL } }, + /* normal */ + { "foo", (const char *const []) { "foo", NULL } }, + { "foo\n", (const char *const []) { "foo", NULL } }, + { "foo\nbar", (const char *const []) { "foo", "bar", NULL } }, + { "foo\nbar\n", (const char *const []) { "foo", "bar", NULL } }, + { "\nfoo\n\nbar\n\n", (const char *const []) { "foo", "bar", NULL } }, + }; + const char *const *args, *const *args2, *const *args3; + + test_begin("t_strsplit_spaces"); + + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { + args = t_strsplit_spaces(tests[i].input, "\n"); + /* test also with a secondary nonexistent separator */ + args2 = t_strsplit_spaces(tests[i].input, "\r\n"); + /* also as suffix */ + args3 = t_strsplit_spaces(tests[i].input, "\n\r"); + for (unsigned int j = 0; tests[i].output[j] != NULL; j++) { + test_assert_idx(null_strcmp(tests[i].output[j], args[j]) == 0, i); + test_assert_idx(null_strcmp(args[j], args2[j]) == 0, i); + test_assert_idx(null_strcmp(args[j], args3[j]) == 0, i); + } + } + + /* multiple separators */ + args = t_strsplit_spaces(" , , ,str1 , ,,, , str2 , ", " ,"); + test_assert(strcmp(args[0], "str1") == 0); + test_assert(strcmp(args[1], "str2") == 0); + test_assert(args[2] == NULL); + test_end(); +} + +static void test_t_str_replace(void) +{ + test_begin("t_str_replace"); + test_assert(strcmp(t_str_replace("foo", 'a', 'b'), "foo") == 0); + test_assert(strcmp(t_str_replace("fooa", 'a', 'b'), "foob") == 0); + test_assert(strcmp(t_str_replace("afooa", 'a', 'b'), "bfoob") == 0); + test_assert(strcmp(t_str_replace("", 'a', 'b'), "") == 0); + test_assert(strcmp(t_str_replace("a", 'a', 'b'), "b") == 0); + test_assert(strcmp(t_str_replace("aaa", 'a', 'b'), "bbb") == 0); + test_assert(strcmp(t_str_replace("bbb", 'a', 'b'), "bbb") == 0); + test_assert(strcmp(t_str_replace("aba", 'a', 'b'), "bbb") == 0); + test_end(); +} + +static void test_t_str_oneline(void) +{ + test_begin("t_str_oneline"); + test_assert(strcmp(t_str_oneline("\n"), "") == 0); + test_assert(strcmp(t_str_oneline("\r"), "") == 0); + test_assert(strcmp(t_str_oneline("\n\n"), "") == 0); + test_assert(strcmp(t_str_oneline("\r\r"), "") == 0); + test_assert(strcmp(t_str_oneline("\r\n"), "") == 0); + test_assert(strcmp(t_str_oneline("\r\n\r\n"), "") == 0); + test_assert(strcmp(t_str_oneline("\n\r"), "") == 0); + test_assert(strcmp(t_str_oneline("\n\r\n\r"), "") == 0); + test_assert(strcmp(t_str_oneline("foo"), "foo") == 0); + test_assert(strcmp(t_str_oneline("\nfoo"), "foo") == 0); + test_assert(strcmp(t_str_oneline("foo\n"), "foo") == 0); + test_assert(strcmp(t_str_oneline("\nfoo\n"), "foo") == 0); + test_assert(strcmp(t_str_oneline("foo\nbar"), "foo bar") == 0); + test_assert(strcmp(t_str_oneline("foo\n\nbar"), "foo bar") == 0); + test_assert(strcmp(t_str_oneline("\nfoo\nbar"), "foo bar") == 0); + test_assert(strcmp(t_str_oneline("foo\nbar\n"), "foo bar") == 0); + test_assert(strcmp(t_str_oneline("foo\nbar\nbaz"), "foo bar baz") == 0); + test_assert(strcmp(t_str_oneline("\rfoo"), "foo") == 0); + test_assert(strcmp(t_str_oneline("foo\r"), "foo") == 0); + test_assert(strcmp(t_str_oneline("\rfoo\r"), "foo") == 0); + test_assert(strcmp(t_str_oneline("foo\rbar"), "foobar") == 0); + test_assert(strcmp(t_str_oneline("foo\r\rbar"), "foobar") == 0); + test_assert(strcmp(t_str_oneline("\rfoo\rbar"), "foobar") == 0); + test_assert(strcmp(t_str_oneline("foo\rbar\r"), "foobar") == 0); + test_assert(strcmp(t_str_oneline("foo\rbar\rbaz"), "foobarbaz") == 0); + test_assert(strcmp(t_str_oneline("\r\nfoo\r\n"), "foo") == 0); + test_assert(strcmp(t_str_oneline("foo\r\n"), "foo") == 0); + test_assert(strcmp(t_str_oneline("\r\nfoo"), "foo") == 0); + test_assert(strcmp(t_str_oneline("foo\r\nbar"), "foo bar") == 0); + test_assert(strcmp(t_str_oneline("foo\r\n\r\nbar"), "foo bar") == 0); + test_assert(strcmp(t_str_oneline("\r\nfoo\r\nbar"), "foo bar") == 0); + test_assert(strcmp(t_str_oneline("foo\r\nbar\r\n"), "foo bar") == 0); + test_assert(strcmp(t_str_oneline("foo\r\nbar\r\nbaz"), "foo bar baz") == 0); + test_end(); +} + +static void test_t_str_trim(void) +{ + test_begin("t_str_trim"); + test_assert(strcmp(t_str_trim("", " "), "") == 0); + test_assert(strcmp(t_str_trim(" ", " "), "") == 0); + test_assert(strcmp(t_str_trim(" \t ", "\t "), "") == 0); + test_assert(strcmp(t_str_trim("f \t ", "\t "), "f") == 0); + test_assert(strcmp(t_str_trim("foo", ""), "foo") == 0); + test_assert(strcmp(t_str_trim("foo", " "), "foo") == 0); + test_assert(strcmp(t_str_trim("foo ", " "), "foo") == 0); + test_assert(strcmp(t_str_trim(" foo", " "), "foo") == 0); + test_assert(strcmp(t_str_trim(" foo ", " "), "foo") == 0); + test_assert(strcmp(t_str_trim("\tfoo ", "\t "), "foo") == 0); + test_assert(strcmp(t_str_trim(" \tfoo\t ", "\t "), "foo") == 0); + test_assert(strcmp(t_str_trim("\r \tfoo\t \r", "\t \r"), "foo") == 0); + test_assert(strcmp(t_str_trim("\r \tfoo foo\t \r", "\t \r"), "foo foo") == 0); + test_assert(strcmp(t_str_trim("\tfoo\tfoo\t", "\t \r"), "foo\tfoo") == 0); + test_end(); +} + +static void test_t_str_ltrim(void) +{ + test_begin("t_str_ltrim"); + test_assert(strcmp(t_str_ltrim("", " "), "") == 0); + test_assert(strcmp(t_str_ltrim(" ", " "), "") == 0); + test_assert(strcmp(t_str_ltrim(" \t ", "\t "), "") == 0); + test_assert(strcmp(t_str_ltrim(" \t f", "\t "), "f") == 0); + test_assert(strcmp(t_str_ltrim("foo", ""), "foo") == 0); + test_assert(strcmp(t_str_ltrim("foo", " "), "foo") == 0); + test_assert(strcmp(t_str_ltrim("foo ", " "), "foo ") == 0); + test_assert(strcmp(t_str_ltrim(" foo", " "), "foo") == 0); + test_assert(strcmp(t_str_ltrim(" foo ", " "), "foo ") == 0); + test_assert(strcmp(t_str_ltrim("\tfoo ", "\t "), "foo ") == 0); + test_assert(strcmp(t_str_ltrim(" \tfoo\t ", "\t "), "foo\t ") == 0); + test_assert(strcmp(t_str_ltrim("\r \tfoo\t \r", "\t \r"), "foo\t \r") == 0); + test_assert(strcmp(t_str_ltrim("\r \tfoo foo\t \r", "\t \r"), "foo foo\t \r") == 0); + test_assert(strcmp(t_str_ltrim("\tfoo\tfoo\t", "\t \r"), "foo\tfoo\t") == 0); + test_end(); +} + +static void test_t_str_rtrim(void) +{ + test_begin("t_str_rtrim"); + test_assert(strcmp(t_str_rtrim("", " "), "") == 0); + test_assert(strcmp(t_str_rtrim(" ", " "), "") == 0); + test_assert(strcmp(t_str_rtrim(" \t ", "\t "), "") == 0); + test_assert(strcmp(t_str_rtrim("f \t ", "\t "), "f") == 0); + test_assert(strcmp(t_str_rtrim("foo", ""), "foo") == 0); + test_assert(strcmp(t_str_rtrim("foo", " "), "foo") == 0); + test_assert(strcmp(t_str_rtrim("foo ", " "), "foo") == 0); + test_assert(strcmp(t_str_rtrim(" foo", " "), " foo") == 0); + test_assert(strcmp(t_str_rtrim(" foo ", " "), " foo") == 0); + test_assert(strcmp(t_str_rtrim("\tfoo ", "\t "), "\tfoo") == 0); + test_assert(strcmp(t_str_rtrim(" \tfoo\t ", "\t "), " \tfoo") == 0); + test_assert(strcmp(t_str_rtrim("\r \tfoo\t \r", "\t \r"), "\r \tfoo") == 0); + test_assert(strcmp(t_str_rtrim("\r \tfoo foo\t \r", "\t \r"), "\r \tfoo foo") == 0); + test_assert(strcmp(t_str_rtrim("\tfoo\tfoo\t", "\t \r"), "\tfoo\tfoo") == 0); + test_end(); +} + +static const char *const test_strarray_input[] = { + "", "hello", "world", "", "yay", "", NULL +}; +static const struct { + const char *separator; + const char *output; +} test_strarray_outputs[] = { + { "", "helloworldyay" }, + { " ", " hello world yay " }, + { "!-?", "!-?hello!-?world!-?!-?yay!-?" } +}; + +static const char *const test_strarray_input2[] = { + "", "", "hello", "world", "", "yay", "", NULL +}; +static struct { + const char *separator; + const char *output; +} test_strarray_outputs2[] = { + { "", "helloworldyay" }, + { " ", " hello world yay " }, + { "!-?", "!-?!-?hello!-?world!-?!-?yay!-?" } +}; + +static const char *const test_strarray_input3[] = { + "hello", "", "", "yay", NULL +}; +static struct { + const char *separator; + const char *output; +} test_strarray_outputs3[] = { + { "", "helloyay" }, + { " ", "hello yay" }, + { "!-?", "hello!-?!-?!-?yay" } +}; + +static void test_t_strarray_join(void) +{ + const char *null = NULL; + unsigned int i; + + test_begin("t_strarray_join()"); + + /* empty array -> empty string */ + test_assert(strcmp(t_strarray_join(&null, " "), "") == 0); + + for (i = 0; i < N_ELEMENTS(test_strarray_outputs); i++) { + test_assert_idx(strcmp(t_strarray_join(test_strarray_input, + test_strarray_outputs[i].separator), + test_strarray_outputs[i].output) == 0, i); + } + for (i = 0; i < N_ELEMENTS(test_strarray_outputs2); i++) { + test_assert_idx(strcmp(t_strarray_join(test_strarray_input2, + test_strarray_outputs2[i].separator), + test_strarray_outputs2[i].output) == 0, i); + } + for (i = 0; i < N_ELEMENTS(test_strarray_outputs3); i++) { + test_assert_idx(strcmp(t_strarray_join(test_strarray_input3, + test_strarray_outputs3[i].separator), + test_strarray_outputs3[i].output) == 0, i); + } + test_end(); +} + +static void test_p_array_const_string_join(void) +{ + ARRAY_TYPE(const_string) arr; + unsigned int i; + char *res; + + test_begin("p_array_const_string_join()"); + + i_array_init(&arr, 2); + /* empty array -> empty string */ + test_assert(strcmp(t_array_const_string_join(&arr, " "), "") == 0); + + array_append(&arr, test_strarray_input, + str_array_length(test_strarray_input)); + for (i = 0; i < N_ELEMENTS(test_strarray_outputs); i++) { + res = p_array_const_string_join(default_pool, &arr, + test_strarray_outputs[i].separator); + test_assert_idx(strcmp(res, test_strarray_outputs[i].output) == 0, i); + i_free(res); + } + + array_free(&arr); + test_end(); +} + +static void test_mem_equals_timing_safe(void) +{ + const struct { + const char *a, *b; + } tests[] = { + { "", "" }, + { "a", "a" }, + { "b", "a" }, + { "ab", "ab" }, + { "ab", "ba" }, + { "ab", "bc" }, + }; + test_begin("mem_equals_timing_safe()"); + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { + size_t len = strlen(tests[i].a); + i_assert(len == strlen(tests[i].b)); + test_assert((memcmp(tests[i].a, tests[i].b, len) == 0) == + mem_equals_timing_safe(tests[i].a, tests[i].b, len)); + test_assert((memcmp(tests[i].a, tests[i].b, len) == 0) == + mem_equals_timing_safe(tests[i].b, tests[i].a, len)); + } + test_end(); +} + +static void test_str_equals_timing_almost_safe(void) +{ + const struct { + const char *a, *b; + } tests[] = { + { "", "" }, + { "a", "a" }, + { "b", "a" }, + { "ab", "ab" }, + { "ab", "ba" }, + { "ab", "bc" }, + { "a", "" }, + { "a", "ab" }, + { "a", "abc" }, + { "ab", "abc" }, + }; + test_begin("str_equals_timing_almost_safe()"); + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { + test_assert((strcmp(tests[i].a, tests[i].b) == 0) == + str_equals_timing_almost_safe(tests[i].a, tests[i].b)); + test_assert((strcmp(tests[i].a, tests[i].b) == 0) == + str_equals_timing_almost_safe(tests[i].b, tests[i].a)); + } + test_end(); +} + +static void test_dec2str_buf(void) +{ + const uintmax_t test_input[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 99, 999, 9999, 65535, 65536, 99999, 999999, 9999999, + 99999999, 999999999, 4294967295, 4294967296ULL, + 9999999999999999999ULL, + 18446744073709551615ULL + }; + char buf[MAX_INT_STRLEN], buf2[MAX_INT_STRLEN]; + + test_begin("dec2str_buf()"); + for (unsigned int i = 0; i < N_ELEMENTS(test_input); i++) { + i_snprintf(buf2, sizeof(buf2), "%ju", test_input[i]); + test_assert_idx(strcmp(dec2str_buf(buf, test_input[i]), + buf2) == 0, i); + } + test_end(); +} + +static void +test_str_match(void) +{ + static const struct { + const char*s1, *s2; size_t match; + } tests[] = { +#define MATCH_TEST(common, left, right) { common left, common right, sizeof(common)-1 } + MATCH_TEST("", "", ""), + MATCH_TEST("", "x", ""), + MATCH_TEST("", "", "x"), + MATCH_TEST("", "foo", "bar"), + MATCH_TEST("x", "", ""), + MATCH_TEST("x", "y", "z"), + MATCH_TEST("blahblahblah", "", ""), + MATCH_TEST("blahblahblah", "", "bar"), + MATCH_TEST("blahblahblah", "foo", ""), + MATCH_TEST("blahblahblah", "foo", "bar"), +#undef MATCH_TEST + }; + + unsigned int i; + + test_begin("str_match"); + for (i = 0; i < N_ELEMENTS(tests); i++) + test_assert_idx(str_match(tests[i].s1, tests[i].s2) == tests[i].match, i); + test_end(); + + test_begin("str_begins"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + /* This is just 2 ways of wording the same test, but that also + sanity tests the match values above. */ + test_assert_idx(str_begins(tests[i].s1, tests[i].s2) == + (str_begins(tests[i].s1, tests[i].s2)), i); + test_assert_idx(str_begins(tests[i].s1, tests[i].s2) == + (strlen(tests[i].s2) == tests[i].match), i); + } + test_end(); +} + +static void test_memspn(void) +{ +#undef TEST_CASE +/* we substract 1 to ensure we don't include the final \0 byte */ +#define TEST_CASE(a, b, r) { \ + .input = (const unsigned char*)((a)), .input_len = sizeof((a))-1, \ + .accept = (const unsigned char*)((b)), .accept_len = sizeof((b))-1, \ + .result = r, \ +} + + static struct { + const unsigned char *input; + size_t input_len; + const unsigned char *accept; + size_t accept_len; + size_t result; + } tests[] = { + TEST_CASE("", "", 0), + TEST_CASE("", "123456789", 0), + TEST_CASE("123456789", "", 0), + TEST_CASE("hello, world", "helo", 5), + TEST_CASE("hello, uuuuu", "helo", 5), + TEST_CASE("\0\0\0\0\0hello", "\0", 5), + TEST_CASE("\r\r\r\r", "\r", 4), + TEST_CASE("aaa", "a", 3), + TEST_CASE("bbb", "a", 0), + /* null safety test */ + { + .input = NULL, .accept = NULL, + .input_len = 0, .accept_len = 0, + .result = 0, + } + }; + + test_begin("i_memspn"); + + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { + size_t a = i_memspn(tests[i].input, tests[i].input_len, + tests[i].accept, tests[i].accept_len); + test_assert_ucmp_idx(a, ==, tests[i].result, i); + if (tests[i].input == NULL) + continue; + a = i_memspn(tests[i].input, strlen((const char*)tests[i].input), + tests[i].accept, strlen((const char*)tests[i].accept)); + size_t b = strspn((const char*)tests[i].input, + (const char*)tests[i].accept); + test_assert_ucmp_idx(a, ==, b, i); + } + + test_end(); +} + +static void test_memcspn(void) +{ +#undef TEST_CASE +/* we substract 1 to ensure we don't include the final \0 byte */ +#define TEST_CASE(a, b, r) { \ + .input = (const unsigned char*)((a)), .input_len = sizeof((a))-1, \ + .reject = (const unsigned char*)((b)), .reject_len = sizeof((b))-1, \ + .result = r, \ +} + + static struct { + const unsigned char *input; + size_t input_len; + const unsigned char *reject; + size_t reject_len; + size_t result; + } tests[] = { + TEST_CASE("", "", 0), + TEST_CASE("hello", "", 5), + TEST_CASE("uuuuu, hello", "helo", 7), + TEST_CASE("\0\0\0\0\0\0hello", "u", 11), + TEST_CASE("this\0is\0test", "\0", 4), + TEST_CASE("hello, world\r", "\r", 12), + TEST_CASE("aaa", "a", 0), + TEST_CASE("bbb", "a", 3), + /* null safety test */ + { + .input = NULL, .reject = NULL, + .input_len = 0, .reject_len = 0, + .result = 0, + } + }; + + test_begin("i_memcspn"); + + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { + size_t a = i_memcspn(tests[i].input, tests[i].input_len, + tests[i].reject, tests[i].reject_len); + test_assert_ucmp_idx(a, ==, tests[i].result, i); + if (tests[i].input == NULL) + continue; + a = i_memcspn(tests[i].input, strlen((const char*)tests[i].input), + tests[i].reject, strlen((const char*)tests[i].reject)); + size_t b = strcspn((const char*)tests[i].input, + (const char*)tests[i].reject); + test_assert_ucmp_idx(a, ==, b, i); + } + + test_end(); +} + +void test_strfuncs(void) +{ + test_p_strdup(); + test_p_strndup(); + test_p_strdup_empty(); + test_p_strdup_until(); + test_p_strarray_dup(); + test_t_strsplit(); + test_t_strsplit_spaces(); + test_t_str_replace(); + test_t_str_oneline(); + test_t_str_trim(); + test_t_str_ltrim(); + test_t_str_rtrim(); + test_t_strarray_join(); + test_p_array_const_string_join(); + test_mem_equals_timing_safe(); + test_str_equals_timing_almost_safe(); + test_dec2str_buf(); + test_str_match(); + test_memspn(); + test_memcspn(); +} + +enum fatal_test_state fatal_strfuncs(unsigned int stage) +{ + switch (stage) { + case 0: + test_begin("fatal p_strndup()"); + test_expect_fatal_string("(str != NULL)"); + (void)p_strndup(default_pool, NULL, 100); + return FATAL_TEST_FAILURE; + case 1: + test_expect_fatal_string("(max_chars != SIZE_MAX)"); + (void)p_strndup(default_pool, "foo", SIZE_MAX); + return FATAL_TEST_FAILURE; + } + test_end(); + return FATAL_TEST_FINISHED; +} diff --git a/src/lib/test-strnum.c b/src/lib/test-strnum.c new file mode 100644 index 0000000..892c579 --- /dev/null +++ b/src/lib/test-strnum.c @@ -0,0 +1,398 @@ +/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" + + +#define INVALID(n) { #n, -1, 0 } +#define VALID(n) { #n, 0, n } + +/* always pads with leading zeros to a size of 9 digits */ +static int crappy_uintmax_to_str(char *into, uintmax_t val) +{ +#define BIGBASE 1000000000ull +#define STRINGIFY(s) #s +#define STRINGIFY2(s) STRINGIFY(s) + int len = 0; + if(val >= BIGBASE) { + len = crappy_uintmax_to_str(into, val/BIGBASE); + } + i_snprintf(into + len, 10, "%09llu", + (unsigned long long)(val % BIGBASE)); + return len + strlen(STRINGIFY2(BIGBASE))-4; +#undef STRINGIFY2 +#undef STRINGIFY +#undef BIGBASE +} +static void test_str_to_uintmax(void) +{ + unsigned int i=0; + int randrange = i_rand_minmax(1, 15); /* when 1, will max out on 1s */ + uintmax_t value = 0, valbase = i_rand() * 1000ull; + int len, ret; + char buff[50]; /* totally assumes < 159 bits */ + + test_begin("str_to_uintmax in range"); + while (i < sizeof(uintmax_t)*CHAR_BIT) { + uintmax_t value_back; + const char *endp; + + value = (value << 1) + 1; + if (value >= 64) + value -= i_rand_limit(randrange); /* don't always test the same numbers */ + len = crappy_uintmax_to_str(buff, value); + ret = str_to_uintmax(buff, &value_back); + test_assert_idx(ret == 0, i); + test_assert_idx(value == value_back, i); + + /* test with trailing noise */ + buff[len] = 'x'; /* don't even null-terminate, let's be evil */ + value_back = 0x1234567890123456; + ret = str_to_uintmax(buff, &value_back); + test_assert_idx(ret < 0, i); + test_assert_idx(value_back == 0x1234567890123456, i); + ret = str_parse_uintmax(buff, &value_back, &endp); + test_assert_idx(ret == 0, i); + test_assert_idx(value_back == value, i); + test_assert_idx(endp == &buff[len], i); + i++; + } + test_end(); + + /* not knowing exactly how large a uintmax_t is, we have to construct + the troublesome near-10/9*MAX strings manually by appending digits + to a MAX/9 string which we can easily create. Do a wider range + of 30 rather than the obvious 10, just in case - all are too large.*/ + test_begin("str_to_uintmax overflow corner case"); + value = UINTMAX_MAX/9-1; + len = crappy_uintmax_to_str(buff, value); + buff[len] = '0'; + buff[len+1] = '\0'; + for(i = 0; i <= 30; ++i) { + int j = len + 1; + while (buff[--j] == '9') + buff[j] = '0'; + buff[j]++; + value = valbase + i; + ret = str_to_uintmax(buff, &value); + test_assert_idx(ret < 0 && value == valbase + i, i); + } + test_end(); +} + +/* always pads with leading zeros to a size of 9 digits */ +static int crappy_uintmax_to_str_hex(char *into, uintmax_t val) +{ +#define BIGBASE 0x1000000000ull +#define STRINGIFY(s) #s +#define STRINGIFY2(s) STRINGIFY(s) + int len = 0; + if(val >= BIGBASE) { + len = crappy_uintmax_to_str_hex(into, val/BIGBASE); + } + i_snprintf(into + len, 10, "%09llx", + (unsigned long long)(val % BIGBASE)); + return len + strlen(STRINGIFY2(BIGBASE))-6; +#undef STRINGIFY2 +#undef STRINGIFY +#undef BIGBASE +} +static void test_str_to_uintmax_hex(void) +{ + unsigned int i=0; + int randrange = i_rand_minmax(1, 15); /* when 1, will max out on 1s */ + uintmax_t value = 0, valbase = i_rand() * 1000ull; + int len, ret; + char buff[52]; /* totally assumes < 200 bits */ + + test_begin("str_to_uintmax_hex in range"); + while (i < sizeof(uintmax_t)*CHAR_BIT) { + uintmax_t value_back; + const char *endp; + + value = (value << 1) + 1; + if (value >= 64) + value -= i_rand_limit(randrange); /* don't always test the same numbers */ + len = crappy_uintmax_to_str_hex(buff, value); + ret = str_to_uintmax_hex(buff, &value_back); + test_assert_idx(ret == 0, i); + test_assert_idx(value == value_back, i); + + /* test with trailing noise */ + buff[len] = 'x'; /* don't even null-terminate, let's be evil */ + value_back = 0x1234567890123456; + ret = str_to_uintmax_hex(buff, &value_back); + test_assert_idx(ret < 0, i); + test_assert_idx(value_back == 0x1234567890123456, i); + ret = str_parse_uintmax_hex(buff, &value_back, &endp); + test_assert_idx(ret == 0, i); + test_assert_idx(value_back == value, i); + test_assert_idx(endp == &buff[len], i); + i++; + } + test_end(); + + /* not knowing exactly how large a uintmax_t is, we have to construct + the troublesome near-0x10/0x0F*MAX strings manually by appending digits + to a MAX/0x0f string which we can easily create. Do a wider range + of 0x30 rather than the obvious 0x10, just in case - all are too large.*/ + test_begin("str_to_uintmax_hex overflow corner case"); + value = (UINTMAX_MAX/0x0f)-1; + len = crappy_uintmax_to_str_hex(buff, value); + buff[len] = '0'; + buff[len+1] = '\0'; + for(i = 0; i <= 0x30; ++i) { + int j = len + 1; + while (buff[--j] == 'f') + buff[j] = '0'; + if (buff[j] == '9') + buff[j] = 'a'; + else + buff[j]++; + value = valbase + i; + ret = str_to_uintmax_hex(buff, &value); + test_assert_idx(ret < 0 && value == valbase + i, i); + } + test_end(); +} + +/* always pads with leading zeros to a size of 9 digits */ +static int crappy_uintmax_to_str_oct(char *into, uintmax_t val) +{ +#define BIGBASE 01000000000ull +#define STRINGIFY(s) #s +#define STRINGIFY2(s) STRINGIFY(s) + int len = 0; + if(val >= BIGBASE) { + len = crappy_uintmax_to_str_oct(into, val/BIGBASE); + } + i_snprintf(into + len, 10, "%09llo", + (unsigned long long)(val % BIGBASE)); + return len + strlen(STRINGIFY2(BIGBASE))-5; +#undef STRINGIFY2 +#undef STRINGIFY +#undef BIGBASE +} +static void test_str_to_uintmax_oct(void) +{ + unsigned int i=0; + int randrange = i_rand_minmax(1, 15); /* when 1, will max out on 1s */ + uintmax_t value = 0, valbase = i_rand() * 1000ull; + int len, ret; + char buff[69]; /* totally assumes < 200 bits */ + + test_begin("str_to_uintmax_oct in range"); + while (i < sizeof(uintmax_t)*CHAR_BIT) { + uintmax_t value_back; + const char *endp = NULL; + + value = (value << 1) + 1; + if (value >= 64) + value -= i_rand_limit(randrange); /* don't always test the same numbers */ + len = crappy_uintmax_to_str_oct(buff, value); + ret = str_to_uintmax_oct(buff, &value_back); + test_assert_idx(ret == 0, i); + test_assert_idx(value == value_back, i); + + /* test with trailing noise */ + buff[len] = 'x'; /* don't even null-terminate, let's be evil */ + value_back = 0x1234567890123456; + ret = str_to_uintmax_oct(buff, &value_back); + test_assert_idx(ret < 0, i); + test_assert_idx(value_back == 0x1234567890123456, i); + ret = str_parse_uintmax_oct(buff, &value_back, &endp); + test_assert_idx(ret == 0, i); + test_assert_idx(value_back == value, i); + test_assert_idx(endp == &buff[len], i); + i++; + } + test_end(); + + /* not knowing exactly how large a uintmax_t is, we have to construct + the troublesome near-010/007*MAX strings manually by appending digits + to a MAX/007 string which we can easily create. Do a wider range + of 030 rather than the obvious 010, just in case - all are too large.*/ + test_begin("str_to_uintmax_oct overflow corner case"); + value = (UINTMAX_MAX/007)-1; + len = crappy_uintmax_to_str_oct(buff, value); + buff[len] = '0'; + buff[len+1] = '\0'; + for(i = 0; i <= 030; ++i) { + int j = len + 1; + while (buff[--j] == '7') + buff[j] = '0'; + buff[j]++; + value = valbase + i; + ret = str_to_uintmax_oct(buff, &value); + test_assert_idx(ret < 0 && value == valbase + i, i); + } + test_end(); +} + +static void test_str_to_u64(void) +{ + unsigned int i; + const struct { + const char *input; + int ret; + uint64_t val; + } u64tests[] = { + INVALID(-1), + INVALID(foo), + VALID(0), + VALID(000000000000000000000000000000000000000000000000000000000000000), + { "000000000000000000000000000000000000000000000000000001000000001", 0, 1000000001 }, + { "18446744073709551615", 0, 18446744073709551615ULL }, + INVALID(18446744073709551616), + INVALID(20496382304121724010), /* 2^64*10/9 doesn't wrap */ + INVALID(20496382304121724017), /* 2^64*10/9 wraps only after addition */ + INVALID(20496382304121724020), /* 2^64*10/9 wraps on multiply*/ + }; + test_begin("str_to_uint64"); + for (i = 0; i < N_ELEMENTS(u64tests); ++i) { + uint64_t val = 0xBADBEEF15BADF00D; + int ret = str_to_uint64(u64tests[i].input, &val); + test_assert_idx(ret == u64tests[i].ret, i); + if (ret == 0) + test_assert_idx(val == u64tests[i].val, i); + else + test_assert_idx(val == 0xBADBEEF15BADF00D, i); + + if (ret == 0) + T_BEGIN { + const char *longer = t_strconcat(u64tests[i].input, "x", NULL); + ret = str_to_uint64(longer, &val); + test_assert_idx(ret < 0, i); + } T_END; + } + test_end(); +} + +static void test_str_to_u32(void) +{ + unsigned int i; + const struct { + const char *input; + int ret; + uint32_t val; + } u32tests[] = { + VALID(0), + INVALID(-0), + VALID(4294967295), + INVALID(4294967296), + INVALID(4772185880), + INVALID(4772185884), + INVALID(4772185890), + }; + test_begin("str_to_uint32"); + for (i = 0; i < N_ELEMENTS(u32tests); ++i) { + uint32_t val = 0xDEADF00D; + int ret = str_to_uint32(u32tests[i].input, &val); + test_assert_idx(ret == u32tests[i].ret, i); + if (ret == 0) + test_assert_idx(val == u32tests[i].val, i); + else + test_assert_idx(val == 0xDEADF00D, i); + } + test_end(); +} + +/* Assumes long long is 64 bit, 2's complement */ +static void test_str_to_llong(void) +{ + unsigned int i; + const struct { + const char *input; + int ret; + long long val; + } i64tests[] = { + VALID(0), + VALID(-0), + INVALID(--0), + VALID(2147483648), + VALID(-2147483649), + VALID(9223372036854775807), + { "-9223372036854775808", 0, -9223372036854775807-1 }, + INVALID(9223372036854775808), + INVALID(-9223372036854775809), + }; + test_begin("str_to_llong"); + for (i = 0; i < N_ELEMENTS(i64tests); ++i) { + long long val = 123456789; + int ret = str_to_llong(i64tests[i].input, &val); + test_assert_idx(ret == i64tests[i].ret, i); + if (ret == 0) + test_assert_idx(val == i64tests[i].val, i); + else + test_assert_idx(val == 123456789, i); + } + test_end(); +} + +/* Assumes int is 32 bit, 2's complement */ +static void test_str_to_i32(void) +{ + unsigned int i; + const struct { + const char *input; + int ret; + int val; + } i32tests[] = { + VALID(0), + VALID(-0), + INVALID(--0), + VALID(2147483647), + VALID(-2147483648), + INVALID(2147483648), + INVALID(-2147483649), + }; + test_begin("str_to_int"); + for (i = 0; i < N_ELEMENTS(i32tests); ++i) { + int val = 123456789; + int ret = str_to_int(i32tests[i].input, &val); + test_assert_idx(ret == i32tests[i].ret, i); + if (ret == 0) + test_assert_idx(val == i32tests[i].val, i); + else + test_assert_idx(val == 123456789, i); + } + test_end(); +} + +static void test_str_is_float(void) +{ + test_begin("str_is_float accepts integer"); + /* accepts integer */ + test_assert(str_is_float("0",'\0')); + test_assert(str_is_float("1234",'\0')); + test_end(); + test_begin("str_is_float accepts float"); + test_assert(str_is_float("0.0",'\0')); + test_assert(str_is_float("1234.0",'\0')); + test_assert(str_is_float("0.1234",'\0')); + test_assert(str_is_float("1234.1234",'\0')); + test_assert(str_is_float("0.1234 ",' ')); + test_assert(str_is_float("1234.1234",'.')); + test_end(); + test_begin("str_is_float refuses invalid values"); + test_assert(!str_is_float(".",'\0')); + test_assert(!str_is_float(".1234",'\0')); + test_assert(!str_is_float("1234.",'\0')); + test_assert(!str_is_float("i am not a float at all",'\0')); + test_assert(!str_is_float("0x1234.0x1234",'\0')); + test_assert(!str_is_float(".0",'\0')); + test_assert(!str_is_float("0.",'\0')); + test_end(); +} + +void test_strnum(void) +{ + /* If the above isn't true, then we do expect some failures possibly */ + test_str_to_uintmax(); + test_str_to_uintmax_hex(); + test_str_to_uintmax_oct(); + test_str_to_u64(); + test_str_to_u32(); + test_str_to_llong(); + test_str_to_i32(); + test_str_is_float(); +} diff --git a/src/lib/test-time-util.c b/src/lib/test-time-util.c new file mode 100644 index 0000000..675a5db --- /dev/null +++ b/src/lib/test-time-util.c @@ -0,0 +1,406 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "time-util.h" + +#include <time.h> + +static void test_timeval_cmp(void) +{ + static const struct { + struct timeval tv1, tv2; + int output; + } tests[] = { + { + .tv1 = { 0, 0 }, + .tv2 = { 0, 0 }, + .output = 0, + }, { + .tv1 = { INT_MAX, 999999 }, + .tv2 = { INT_MAX, 999999 }, + .output = 0, + }, { + .tv1 = { 0, 0 }, + .tv2 = { 0, 1 }, + .output = -1, + }, { + .tv1 = { 0, 0 }, + .tv2 = { 1, 0 }, + .output = -1, + }, { + .tv1 = { 0, 999999 }, + .tv2 = { 1, 0 }, + .output = -1, + }, { + .tv1 = { 1, 0 }, + .tv2 = { 1, 1 }, + .output = -1, + }, { + .tv1 = { -INT_MAX, 0 }, + .tv2 = { INT_MAX, 0 }, + .output = -1, + } + }; + unsigned int i; + + test_begin("timeval_cmp()"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + const struct timeval *tv1 = &tests[i].tv1, *tv2 = &tests[i].tv2; + int output = tests[i].output; + + test_assert(timeval_cmp(tv1, tv2) == output); + test_assert(timeval_cmp(tv2, tv1) == -output); + } + test_end(); +} + +static void test_timeval_cmp_margin(void) +{ + static const struct { + struct timeval tv1, tv2; + unsigned int margin; + int output; + } tests[] = { + { + .tv1 = { 0, 0 }, + .tv2 = { 0, 0 }, + .output = 0, + },{ + .tv1 = { INT_MAX, 999999 }, + .tv2 = { INT_MAX, 999999 }, + .output = 0, + + },{ + .tv1 = { 0, 0 }, + .tv2 = { 0, 1 }, + .output = -1, + },{ + .tv1 = { 0, 0 }, + .tv2 = { 1, 0 }, + .output = -1, + },{ + .tv1 = { 0, 999999 }, + .tv2 = { 1, 0 }, + .output = -1, + },{ + .tv1 = { 1, 0 }, + .tv2 = { 1, 1 }, + .output = -1, + },{ + .tv1 = { -INT_MAX, 0 }, + .tv2 = { INT_MAX, 0 }, + .output = -1, + },{ + .tv1 = { 0, 999999 }, + .tv2 = { 1, 0 }, + .margin = 1, + .output = 0, + },{ + .tv1 = { 1, 0 }, + .tv2 = { 1, 1 }, + .margin = 1, + .output = 0, + },{ + .tv1 = { 0, 999998 }, + .tv2 = { 1, 0 }, + .margin = 1, + .output = -1, + },{ + .tv1 = { 1, 0 }, + .tv2 = { 1, 2 }, + .margin = 1, + .output = -1, + },{ + .tv1 = { 0, 998000 }, + .tv2 = { 1, 0 }, + .margin = 2000, + .output = 0, + },{ + .tv1 = { 1, 0 }, + .tv2 = { 1, 2000 }, + .margin = 2000, + .output = 0, + },{ + .tv1 = { 0, 997999 }, + .tv2 = { 1, 0 }, + .margin = 2000, + .output = -1, + },{ + .tv1 = { 1, 0 }, + .tv2 = { 1, 2001 }, + .margin = 2000, + .output = -1, + },{ + .tv1 = { 0, 1 }, + .tv2 = { 1, 0 }, + .margin = 999999, + .output = 0, + },{ + .tv1 = { 1, 0 }, + .tv2 = { 1, 999999 }, + .margin = 999999, + .output = 0, + },{ + .tv1 = { 0, 0 }, + .tv2 = { 1, 0 }, + .margin = 999999, + .output = -1, + },{ + .tv1 = { 1, 0 }, + .tv2 = { 2, 0 }, + .margin = 999999, + .output = -1, + },{ + .tv1 = { 10, 0 }, + .tv2 = { 11, 500000 }, + .margin = 1500000, + .output = 0, + },{ + .tv1 = { 8, 500000 }, + .tv2 = { 10, 0 }, + .margin = 1500000, + .output = 0, + },{ + .tv1 = { 10, 0 }, + .tv2 = { 11, 500001 }, + .margin = 1500000, + .output = -1, + },{ + .tv1 = { 8, 499999 }, + .tv2 = { 10, 0 }, + .margin = 1500000, + .output = -1, + },{ + .tv1 = { 1517925358, 999989 }, + .tv2 = { 1517925359, 753 }, + .margin = 2000, + .output = 0, + } + }; + unsigned int i; + + test_begin("timeval_cmp_margin()"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + const struct timeval *tv1 = &tests[i].tv1, *tv2 = &tests[i].tv2; + unsigned int margin = tests[i].margin; + int output = tests[i].output; + + test_assert(timeval_cmp_margin(tv1, tv2, margin) == output); + test_assert(timeval_cmp_margin(tv2, tv1, margin) == -output); + } + test_end(); +} + +static void test_timeval_diff(void) +{ + static const struct timeval input[] = { + { 1, 0 }, { 0, 999999 }, + { 1, 0 }, { 0, 999001 }, + { 1, 1 }, { 0, 999001 }, + { 2, 1 }, { 1, 0 }, + { INT_MAX, 0 }, { INT_MAX-1, 1 } + }; + static int output[] = { + 1, + 999, + 1000, + 1000001, + 999999 + }; + unsigned int i; + long long udiff; + int mdiff; + + test_begin("timeval_diff_*()"); + for (i = 0; i < N_ELEMENTS(input); i += 2) { + udiff = timeval_diff_usecs(&input[i], &input[i+1]); + mdiff = timeval_diff_msecs(&input[i], &input[i+1]); + test_assert(udiff == output[i/2]); + test_assert(mdiff == udiff/1000); + + udiff = timeval_diff_usecs(&input[i+1], &input[i]); + mdiff = timeval_diff_msecs(&input[i+1], &input[i]); + test_assert(udiff == -output[i/2]); + test_assert(mdiff == udiff/1000); + } + test_end(); +} + +static void test_time_to_local_day_start(void) +{ + /* Try this around days when DST changes in some of the more popular + timezones. If that works, everything else probably works too. */ + const struct tm tests[] = { + /* Europe winter -> summer */ + { .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 26 }, + { .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 26, + .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, + /* Europe summer -> winter */ + { .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 29 }, + { .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 29, + .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, + /* USA winter -> summer */ + { .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 12 }, + { .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 12, + .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, + /* USA summer -> winter */ + { .tm_year = 2017-1900, .tm_mon = 10, .tm_mday = 5 }, + { .tm_year = 2017-1900, .tm_mon = 10, .tm_mday = 5, + .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, + + /* (some of) Australia summer -> winter */ + { .tm_year = 2017-1900, .tm_mon = 3, .tm_mday = 2 }, + { .tm_year = 2017-1900, .tm_mon = 3, .tm_mday = 2, + .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, + /* (some of) Australia winter -> summer */ + { .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 1 }, + { .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 1, + .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, + }; + const struct tm *tm; + struct tm tm_copy; + time_t t; + + test_begin("time_to_local_day_start()"); + for (unsigned i = 0; i < N_ELEMENTS(tests); i++) { + tm_copy = tests[i]; + tm_copy.tm_isdst = -1; + t = mktime(&tm_copy); + test_assert_idx(t != (time_t)-1, i); + + t = time_to_local_day_start(t); + tm = localtime(&t); + test_assert_idx(tm->tm_year == tests[i].tm_year && + tm->tm_mon == tests[i].tm_mon && + tm->tm_mday == tests[i].tm_mday, i); + test_assert_idx(tm->tm_hour == 0 && tm->tm_min == 0 && + tm->tm_sec == 0, i); + } + test_end(); +} + +static void test_timestamp(const char *ts, int idx) +{ + /* %G:%H:%M:%S */ + const char **t = t_strsplit(ts, ":"); + unsigned len = str_array_length(t); + test_assert_idx(len == 4, idx); + + /* %G - ISO 8601 year */ + test_assert_idx(strlen(t[0]) == 4, idx); + unsigned v = 0; + test_assert_idx(str_to_uint(t[0], &v) == 0, idx); + test_assert_idx(1000 <= v, idx); + test_assert_idx(v <= 3000, idx); + + /* %H - hour from 00 to 23 */ + test_assert_idx(strlen(t[1]) == 2, idx); + test_assert_idx(str_to_uint(t[1], &v) == 0, idx); + test_assert_idx(v <= 23, idx); + + /* %M - minute from 00 to 59 */ + test_assert_idx(strlen(t[2]) == 2, idx); + test_assert_idx(str_to_uint(t[2], &v) == 0, idx); + test_assert_idx(v <= 59, idx); + + /* %S - second from 00 to 60 */ + test_assert_idx(strlen(t[3]) == 2, idx); + test_assert_idx(str_to_uint(t[3], &v) == 0, idx); + test_assert_idx(v <= 60, idx); +} + +#define TS_FMT "%G:%H:%M:%S" +static void test_strftime_now(void) +{ + test_begin("t_strftime and variants now"); + + time_t now = time(NULL); + test_timestamp(t_strftime(TS_FMT, gmtime(&now)), 0); + test_timestamp(t_strfgmtime(TS_FMT, now), 1); + test_timestamp(t_strflocaltime(TS_FMT, now), 2); + + test_end(); +} + +#define RFC2822_FMT "%a, %d %b %Y %T" +static void test_strftime_fixed(void) +{ + test_begin("t_strftime and variants fixed timestamp"); + + time_t ts = 1481222536; + const char *exp = "Thu, 08 Dec 2016 18:42:16"; + test_assert(strcmp(t_strftime(RFC2822_FMT, gmtime(&ts)), exp) == 0); + test_assert(strcmp(t_strfgmtime(RFC2822_FMT, ts), exp) == 0); + + test_end(); +} + +static void test_micro_nanoseconds(void) +{ + uint64_t secs, usecs, nsecs; + + test_begin("i_microseconds() and i_nanoseconds()"); + + secs = time(NULL); + usecs = i_microseconds(); + nsecs = i_nanoseconds(); + + /* Assume max 1 seconds time difference between the calls. That should + be more than enough, while still not failing if there are temporary + hangs when running in heavily loaded systems. */ + test_assert(usecs/1000000 - secs <= 1); + test_assert(nsecs/1000 - usecs <= 1000000); + + test_end(); +} + +static void test_str_to_timeval(void) +{ + struct { + const char *str; + time_t tv_sec; + suseconds_t tv_usec; + } tests[] = { + { "0", 0, 0 }, + { "0.0", 0, 0 }, + { "0.000000", 0, 0 }, + { "0.1", 0, 100000 }, + { "0.100000", 0, 100000 }, + { "0.000001", 0, 1 }, + { "0.100000", 0, 100000 }, + { "2147483647", 2147483647, 0 }, + { "2147483647.999999", 2147483647, 999999 }, + }; + const char *test_failures[] = { + "", + "0.", + "0.0000000", + "1234.-1", + "1234.x", + "x", + "x.100", + }; + struct timeval tv; + + test_begin("str_to_timeval"); + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { + test_assert_idx(str_to_timeval(tests[i].str, &tv) == 0, i); + test_assert_idx(tv.tv_sec == tests[i].tv_sec, i); + test_assert_idx(tv.tv_usec == tests[i].tv_usec, i); + } + for (unsigned int i = 0; i < N_ELEMENTS(test_failures); i++) + test_assert_idx(str_to_timeval(test_failures[i], &tv) == -1, i); + test_end(); +} + +void test_time_util(void) +{ + test_timeval_cmp(); + test_timeval_cmp_margin(); + test_timeval_diff(); + test_time_to_local_day_start(); + test_strftime_now(); + test_strftime_fixed(); + test_micro_nanoseconds(); + test_str_to_timeval(); +} diff --git a/src/lib/test-unichar.c b/src/lib/test-unichar.c new file mode 100644 index 0000000..b5d3d06 --- /dev/null +++ b/src/lib/test-unichar.c @@ -0,0 +1,185 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "buffer.h" +#include "unichar.h" + +static void test_unichar_uni_utf8_strlen(void) +{ + static const char input[] = "\xC3\xA4\xC3\xA4\0a"; + + test_begin("uni_utf8_strlen()"); + test_assert(uni_utf8_strlen(input) == 2); + test_end(); + + test_begin("uni_utf8_strlen_n()"); + test_assert(uni_utf8_strlen_n(input, 1) == 0); + test_assert(uni_utf8_strlen_n(input, 2) == 1); + test_assert(uni_utf8_strlen_n(input, 3) == 1); + test_assert(uni_utf8_strlen_n(input, 4) == 2); + test_end(); +} + +static void test_unichar_uni_utf8_partial_strlen_n(void) +{ + static const char input[] = "\xC3\xA4\xC3\xA4\0a"; + size_t pos; + + test_begin("uni_utf8_partial_strlen_n()"); + test_assert(uni_utf8_partial_strlen_n(input, 1, &pos) == 0 && pos == 0); + test_assert(uni_utf8_partial_strlen_n(input, 2, &pos) == 1 && pos == 2); + test_assert(uni_utf8_partial_strlen_n(input, 3, &pos) == 1 && pos == 2); + test_assert(uni_utf8_partial_strlen_n(input, 4, &pos) == 2 && pos == 4); + test_assert(uni_utf8_partial_strlen_n(input, 5, &pos) == 3 && pos == 5); + test_assert(uni_utf8_partial_strlen_n(input, 6, &pos) == 4 && pos == 6); + test_end(); +} + +static void test_unichar_valid_unicode(void) +{ + struct { + const char *input; + bool valid; + unichar_t expected; + } test_cases[] = { + { "a", TRUE, 'a' }, + { "\xc3\xb1", TRUE, 0x00F1 }, /* U+00F1 */ + { "\xc3\x28", FALSE, 0x0 }, /* has invalid 2nd octet */ + { "\xa0\xa1", FALSE, 0x0 }, /* invalid sequence identifier */ + { "\xed\xb2\x80", FALSE, 0x0 }, /* UTF-8B */ + { "\xed\xa0\x80", FALSE, 0x0 }, /* surrogate halves, U+D800 .. */ + { "\xed\xa0\x80", FALSE, 0x0 }, + { "\xed\xa1\x80", FALSE, 0x0 }, + { "\xed\xa2\x80", FALSE, 0x0 }, + { "\xed\xa3\x80", FALSE, 0x0 }, + { "\xed\xa4\x80", FALSE, 0x0 }, + { "\xed\xa5\x80", FALSE, 0x0 }, + { "\xed\xa6\x80", FALSE, 0x0 }, + { "\xed\xa7\x80", FALSE, 0x0 }, + { "\xed\xa8\x80", FALSE, 0x0 }, + { "\xed\xa9\x80", FALSE, 0x0 }, + { "\xed\xaa\x80", FALSE, 0x0 }, + { "\xed\xab\x80", FALSE, 0x0 }, + { "\xed\xac\x80", FALSE, 0x0 }, + { "\xed\xad\x80", FALSE, 0x0 }, + { "\xed\xaf\x80", FALSE, 0x0 }, + { "\xed\xb0\x80", FALSE, 0x0 }, + { "\xed\xb1\x80", FALSE, 0x0 }, + { "\xed\xb2\x80", FALSE, 0x0 }, + { "\xed\xb3\x80", FALSE, 0x0 }, + { "\xed\xb4\x80", FALSE, 0x0 }, + { "\xed\xb5\x80", FALSE, 0x0 }, + { "\xed\xb6\x80", FALSE, 0x0 }, + { "\xed\xb7\x80", FALSE, 0x0 }, + { "\xed\xb8\x80", FALSE, 0x0 }, + { "\xed\xb9\x80", FALSE, 0x0 }, + { "\xed\xba\x80", FALSE, 0x0 }, + { "\xed\xbb\x80", FALSE, 0x0 }, + { "\xed\xbc\x80", FALSE, 0x0 }, + { "\xed\xbd\x80", FALSE, 0x0 }, + { "\xed\xbf\x80", FALSE, 0x0 }, /* .. U+DFFF */ + { "\xe2\x82\xa1", TRUE, 0x20A1 }, /* U+20A1 */ + { "\xe2\x28\xa1", FALSE, 0x0 }, /* invalid 2nd octet */ + { "\xe2\x82\x28", FALSE, 0x0 }, /* invalid 3rd octet */ + { "\xf0\x90\x8c\xbc", TRUE, 0x1033C }, /* U+1033C */ + { "\xf0\x28\x8c\xbc", FALSE, 0x0 }, /*invalid 2nd octet*/ + { "\xf0\x90\x28\xbc", FALSE, 0x0 }, /* invalid 3rd octet */ + { "\xf0\x28\x8c\x28", FALSE, 0x0 }, /* invalid 4th octet */ + { "\xf4\x80\x80\x80", TRUE, 0x100000 }, /* U+100000, supplementary plane start */ + { "\xf4\x8f\xbf\xbf", TRUE, 0x10FFFF }, /* U+10FFFF, maximum value */ + { "\xf8\xa1\xa1\xa1\xa1", FALSE, 0x0 }, /* invalid unicode */ + { "\xfc\xa1\xa1\xa1\xa1\xa1", FALSE, 0x0 }, /* invalid unicode */ + }; + + test_begin("unichar valid unicode"); + + for(size_t i = 0; i < N_ELEMENTS(test_cases); i++) { + unichar_t chr; + if (test_cases[i].valid) { + test_assert_idx(uni_utf8_get_char(test_cases[i].input, &chr) > 0, i); + test_assert_idx(test_cases[i].expected == chr, i); + } else { + test_assert_idx(uni_utf8_get_char(test_cases[i].input, &chr) < 1, i); + } + } + + test_end(); +} + +static void test_unichar_surrogates(void) +{ + unichar_t orig, high, low; + test_begin("unichar surrogates"); + + orig = 0x10437; + uni_split_surrogate(orig, &high, &low); + test_assert(high == 0xD801); + test_assert(low == 0xDC37); + test_assert(uni_join_surrogate(high, low) == orig); + + test_end(); +} + +void test_unichar(void) +{ + static const char overlong_utf8[] = "\xf8\x80\x95\x81\xa1"; + static const char collate_in[] = "\xc3\xbc \xc2\xb3"; + static const char collate_exp[] = "U\xcc\x88 3"; + buffer_t *collate_out; + unichar_t chr, chr2; + string_t *str = t_str_new(16); + + test_begin("unichars encode/decode"); + for (chr = 0; chr <= 0x10ffff; chr++) { + /* skip surrogates */ + if ((chr & 0xfff800) == 0xd800) + continue; + /* The bottom 6 bits should be irrelevant to code coverage, + only test 000000, 111111, and something in between. */ + if ((chr & 63) == 1) + chr += i_rand_limit(62); /* After 0, somewhere between 1 and 62 */ + else if ((chr & 63) > 0 && (chr & 63) < 63) + chr |= 63; /* After random, straight to 63 */ + + str_truncate(str, 0); + uni_ucs4_to_utf8_c(chr, str); + test_assert(uni_utf8_str_is_valid(str_c(str))); + test_assert(uni_utf8_get_char(str_c(str), &chr2) == (int)uni_utf8_char_bytes(*str_data(str))); + test_assert(chr2 == chr); + + if ((chr & 0x63) == 0) { + unsigned int utf8len = uni_utf8_char_bytes((unsigned char)*str_c(str)); + + /* virtually truncate the byte string */ + while (--utf8len > 0) + test_assert(uni_utf8_get_char_n(str_c(str), utf8len, &chr2) == 0); + + utf8len = uni_utf8_char_bytes((unsigned char)*str_c(str)); + + /* actually truncate the byte stream */ + while (--utf8len > 0) { + str_truncate(str, utf8len); + test_assert(!uni_utf8_str_is_valid(str_c(str))); + test_assert(uni_utf8_get_char(str_c(str), &chr2) == 0); + } + } + } + test_end(); + + test_begin("unichar collation"); + collate_out = buffer_create_dynamic(default_pool, 32); + uni_utf8_to_decomposed_titlecase(collate_in, sizeof(collate_in), + collate_out); + test_assert(strcmp(collate_out->data, collate_exp) == 0); + buffer_free(&collate_out); + + test_assert(!uni_utf8_str_is_valid(overlong_utf8)); + test_assert(uni_utf8_get_char(overlong_utf8, &chr2) < 0); + test_end(); + + test_unichar_uni_utf8_strlen(); + test_unichar_uni_utf8_partial_strlen_n(); + test_unichar_valid_unicode(); + test_unichar_surrogates(); +} diff --git a/src/lib/test-uri.c b/src/lib/test-uri.c new file mode 100644 index 0000000..299214d --- /dev/null +++ b/src/lib/test-uri.c @@ -0,0 +1,807 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "test-common.h" +#include "str.h" +#include "str-sanitize.h" +#include "uri-util.h" + +/* Valid uri tests */ +const char *valid_uri_tests[] = { + "http://www.dovecot.org", + "http://127.0.0.1", + "http://www.dovecot.org/frop", + "http://www.dovecot.org/frop%20frop", + "http://www.dovecot.org/frop/frop", + "http://www.dovecot.org/frop/frop?query", + "http://www.dovecot.org?query", + "http://www.dovecot.org?query&query", + "mailto:frop@example.com", +}; + +unsigned int valid_uri_test_count = N_ELEMENTS(valid_uri_tests); + +static void test_uri_valid(void) +{ + unsigned int i; + + test_begin("uri valid"); + for (i = 0; i < valid_uri_test_count; i++) T_BEGIN { + const char *uri_in, *error = NULL; + int ret; + + uri_in = valid_uri_tests[i]; + + ret = uri_check(uri_in, 0, &error); + test_out_quiet( + t_strdup_printf("parse [%u] <%s>", i, str_sanitize(uri_in, 64)), + ret >= 0); + } T_END; + test_end(); +} + +/* Invalid uri tests */ +const char *invalid_uri_tests[] = { + "http", + "http$44", + "/index.html", + "imap:[", + "imap://[", + "frop://friep\"", + "http://example.com/settings/%00/", + "http://[]/index.html", + "http://example.com:65536/index.html" +}; + +unsigned int invalid_uri_test_count = N_ELEMENTS(invalid_uri_tests); + +static void test_uri_invalid(void) +{ + unsigned int i; + + test_begin("uri invalid"); + for (i = 0; i < invalid_uri_test_count; i++) T_BEGIN { + const char *uri_in, *error = NULL; + int ret; + + uri_in = invalid_uri_tests[i]; + + ret = uri_check(uri_in, 0, &error); + test_out_quiet( + t_strdup_printf("parse [%u] <%s>", i, str_sanitize(uri_in, 64)), + ret < 0); + } T_END; + test_end(); +} + +/* RFC uri tests */ +const char *rfc_uri_tests[] = { + /* from RFC 1738 */ + "http://www.acl.lanl.gov/URI/archive/uri-archive.index.html", + "file://vms.host.edu/disk$user/my/notes/note12345.txt", + "ftp://@host.com/", + "ftp://host.com/", + "ftp://foo:@host.com/", + "ftp://myname@host.dom/%2Fetc/motd", + "ftp://myname@host.dom/etc/motd", + "ftp://myname@host.dom//etc/motd", + "ftp://info.cern.ch/pub/www/doc;type=d", + "http://ds.internic.net/instructions/overview.html#WARNING", + /* from RFC 2056 */ + "z39.50s://melvyl.ucop.edu/cat", + "z39.50r://melvyl.ucop.edu/mags?elecworld.v30.n19", + "z39.50r://cnidr.org:2100/tmf?bkirch_rules__a1;esn=f;rs=marc", + /* from RFC 2122 */ + "vemmi://zeus.mctel.fr/demo", + "vemmi://zeus.mctel.fr", + "vemmi://zeus.mctel.fr", + "vemmi://mctel.fr/demo;$USERDATA=smith;account=1234", + "vemmi://ares.mctel.fr/TEST", + /* from RFC 2141 */ + "URN:foo:a123,456", + "urn:foo:a123,456", + "urn:FOO:a123,456", + "urn:foo:A123,456", + "urn:foo:a123%2C456", + "URN:FOO:a123%2c456", + /* from RFC 2224 */ + "nfs://server/d/e/f", + "nfs://server//a/b/c/d/e/f", + "nfs://server/a/b", + /* from RFC 2229 */ + "dict://dict.org/d:shortcake:", + "dict://dict.org/d:shortcake:*", + "dict://dict.org/d:shortcake:wordnet:", + "dict://dict.org/d:shortcake:wordnet:1", + "dict://dict.org/d:abcdefgh", + "dict://dict.org/d:sun", + "dict://dict.org/d:sun::1", + "dict://dict.org/m:sun", + "dict://dict.org/m:sun::soundex", + "dict://dict.org/m:sun:wordnet::1", + "dict://dict.org/m:sun::soundex:1", + "dict://dict.org/m:sun:::", + /* from RFC 2326 */ + "rtsp://media.example.com:554/twister/audiotrack", + "rtsp://media.example.com:554/twister", + "rtsp://server.example.com/fizzle/foo", + "rtsp://example.com/foo/bar/baz.rm", + "rtsp://audio.example.com/audio", + "rtsp://audio.example.com/twister.en", + "rtsp://audio.example.com/meeting.en", + "rtsp://example.com/fizzle/foo", + "rtsp://bigserver.com:8001", + "rtsp://example.com/meeting/audio.en", + "rtsp://foo.com/bar.file", + "rtsp://foo.com/bar.avi/streamid=0;seq=45102", + "rtsp://foo.com/bar.avi/streamid=1;seq=30211", + "rtsp://audio.example.com/twister/audio.en", + "rtsp://video.example.com/twister/video", + "rtsp://video.example.com/twister/video;seq=12312232;rtptime=78712811", + "rtsp://audio.example.com/twister/audio.en;seq=876655;rtptime=1032181", + "rtsp://foo/twister/video;seq=9810092;rtptime=3450012", + "rtsp://foo.com/test.wav/streamid=0;seq=981888;rtptime=3781123", + "rtsp://server.example.com/demo/548/sound", + "rtsp://server.example.com/demo/548/sound", + "rtsp://server.example.com/meeting", + "rtsp://server.example.com/meeting/audiotrack", + "rtsp://server.example.com/meeting/videotrack", + "rtsp://server.example.com/meeting", + "rtsp://example.com/movie/trackID=1", + "rtsp://media.example.com:554/twister", + /* from RFC 2371 */ + "tip://123.123.123.123/?urn:xopen:xid", + "tip://123.123.123.123/?transid1", + /* from RFC 2384 */ + "pop://rg@mailsrv.qualcomm.com", + "pop://rg;AUTH=+APOP@mail.eudora.com:8110", + "pop://baz;AUTH=SCRAM-MD5@foo.bar", + /* from RFC 2392 */ + "mid:960830.1639@XIson.com/partA.960830.1639@XIson.com", + "cid:foo4%25foo1@bar.net", + "cid:foo4*foo1@bar.net", + /* from RFC 2397 */ + "data:,A%20brief%20note", + "data:image/gif;base64,R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAw" + "AAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapyuvUUlvONmOZtfzgFz" + "ByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSp" + "a/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJl" + "ZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uis" + "F81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PH" + "hhx4dbgYKAAA7", +#if 0 // this one doesn't comply with RFC 3986 + "data:text/plain;charset=iso-8859-7,%be%fg%be", +#endif + "data:application/vnd-xxx-query,select_vcount,fcol_from_fieldtable/local", + /* from RFC 2838 */ + "tv:wqed.org", + "tv:nbc.com", + "tv:", + "tv:abc.com", + "tv:abc.co.au", + "tv:east.hbo.com", + "tv:west.hbo.com", + /* from RFC 3261 */ +#if 0 // these don't comply with RFC 3986 + "sip:+1-212-555-1212:1234@gateway.com;user=phone", + "sip:+12125551212@server.phone2net.com", + "sip:+12125551212@server.phone2net.com;tag=887s", + "sip:+358-555-1234567@foo.com;postd=pp22;user=phone", + "sip:+358-555-1234567;isub=1411;postd=pp22@foo.com;user=phone", + "sip:+358-555-1234567;phone-context=5;tsp=a.b@foo.com;user=phone", + "sip:+358-555-1234567;postd=pp22@foo.com;user=phone", + "sip:+358-555-1234567;POSTD=PP22@foo.com;user=phone", + "sip:+358-555-1234567;postd=pp22;isub=1411@foo.com;user=phone", + "sip:%61lice@atlanta.com;transport=TCPv", + "sip:agb@bell-telephone.com", + "sip:alice@192.0.2.4v", + "sip:alice@atlanta.covm", + "sip:alice@atlanta.com;maddr=239.255.255.1;ttl=15", + "sip:alice@atlanta.com?priority=urgent&subject=project%20x", + "sip:alice@atlanta.com?subject=project%20x&priority=urgent", + "sip:alice@AtLanTa.CoM;Transport=tcp", + "sip:alice@AtLanTa.CoM;Transport=UDP", + "SIP:ALICE@AtLanTa.CoM;Transport=udp", + "sip:alice;day=tuesday@atlanta.com", + "sip:alice@pc33.atlanta.com", + "sip:alice:secretword@atlanta.com;transport=tcp", + "sip:anonymous@anonymizer.invalid", + "sip:atlanta.com;method=REGISTER?to=alice%40atlanta.com", + "sip:bigbox3.site3.atlanta.com;lr", + "sip:biloxi.com;method=REGISTER;transport=tcp?to=sip:bob%40biloxi.com", + "sip:biloxi.com;transport=tcp;method=REGISTER?to=sip:bob%40biloxi.com", + "sip:bob@192.0.2.4", + "sip:bob@biloxi.com", + "sip:bob@biloxi.com:5060", + "sip:bob@biloxi.com:6000;transport=tcp", + "sip:bob@biloxi.com;transport=udp", + "sip:bob@engineering.biloxi.com", + "sip:bob@phone21.boxesbybob.com", + "sip:c8oqz84zk7z@privacy.org>;tag=hyh8", + "sip:callee@domain.com", + "sip:callee@gateway.leftprivatespace.com", + "sip:callee@u2.domain.com", + "sip:callee@u2.rightprivatespace.com", + "sip:caller@u1.example.com", + "sip:carol@chicago.com", + "sip:carol@chicago.com;security=off", + "sip:carol@chicago.com;security=on", + "sip:carol@chicago.com;newparam=5", + "sip:carol@chicago.com;security=off", + "sip:carol@chicago.com;security=on", + "sip:carol@chicago.com?Subject=next%20meeting", + "sip:carol@cube2214a.chicago.com", + "sip:chicago.com", + "sip:not-in-service-recording@atlanta.com", + "sip:operator@cs.columbia.edu", + "sip:p1.domain.com;lr", + "sip:p1.example.com;lr", + "sip:p2.domain.com;lr", + "sips:1212@gateway.com", + "sips:+358-555-1234567@foo.com;postd=pp22;user=phone", + "sips:+358-555-1234567;postd=pp22@foo.com;user=phone", + "sips:alice@atlanta.com?subject=project%20x&priority=urgent", + "sip:server10.biloxi.com;lr", + "sip:ss1.carrier.com", + "sip:user@host?Subject=foo&Call-Info=<http://www.foo.com>", + "sip:watson@bell-telephone.com", + "sip:watson@worcester.bell-telephone.com", +#endif + /* from RFC 3368 */ + "go:Mercedes%20Benz", + "go://?Mercedes%20Benz", + "go://cnrp.foo.com?Mercedes%20Benz;geography=US-ga", + "go://cnrp.foo.org?Martin%20J.%20D%C3%BCrst", + "go://cnrp.foo.com?id=5432345", + /* from RFC 3507 */ + "icap://icap.example.net:2000/services/icap-service-1", + "icap://icap.net/service?mode=translate&lang=french", + "icap://icap.example.net/translate?mode=french", + "icap://icap-server.net/server?arg=87", + "icap://icap.example.org/satisf", + "icap://icap.server.net/sample-service", + /* from RFC 3510 */ + "ipp://example.com", + "ipp://example.com/printer", + "ipp://example.com/printer/tiger", + "ipp://example.com/printer/fox", + "ipp://example.com/printer/tiger/bob", + "ipp://example.com/printer/tiger/ira", + "ipp://example.com", + "ipp://example.com/~smith/printer", + "ipp://example.com:631/~smith/printer", + "ipp://example.com/printer/123", + "ipp://example.com/printer/tiger/job123", + /* from RFC 3529 */ + "xmlrpc.beep://stateserver.example.com/NumberToName", + "xmlrpc.beep://stateserver.example.com:1026", + "xmlrpc.beep://stateserver.example.com", + "xmlrpc.beep://10.0.0.2:1026", + "xmlrpc.beeps://stateserver.example.com/NumberToName", + /* from RFC 3617 */ + "tftp://example.com/myconfigurationfile;mode=netascii", + "tftp://example.com/mystartupfile", + /* from RFC 3859 */ + "pres:fred@example.com", + /* from RFC 3860 */ + "im:fred@example.com", + "im:pepp=example.com/fred@relay-domain", + /* from RFC 3966 */ + "tel:+1-201-555-0123", + "tel:7042;phone-context=example.com", + "tel:863-1234;phone-context=+1-914-555", + /* from RFC 3981 */ + "iris:dreg1//example.com/local/myhosts", + "iris:dreg1//com", + "iris:dreg1//com/iris/id", + "iris:dreg1//example.com/domain/example.com", + "iris:dreg1//example.com", + "iris:dreg1//com/domain/example.com", + "iris:dreg1//192.0.2.1:44/domain/example.com", + "iris.lwz:dreg1//192.0.2.1:44/domain/example.com", + "iris.beep:dreg1//com/domain/example.com", + "iris:dreg1/bottom/example.com/domain/example.com", + "iris.beep:dreg1/bottom/example.com/domain/example.com", + /* from RFC 3986 */ + "ftp://ftp.is.co.za/rfc/rfc1808.txt", + "http://www.ietf.org/rfc/rfc2396.txt", + "ldap://[2001:db8::7]/c=GB?objectClass?one", + "mailto:John.Doe@example.com", + "news:comp.infosystems.www.servers.unix", + "tel:+1-816-555-1212", + "telnet://192.0.2.16:80/", + "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", + /* from RFC 4078 */ + "crid://example.com/foobar", + "crid://example.co.jp/%E3%82%A8%E3%82%A4%E3%82%AC", + /* from RFC 4088 */ + "snmp://example.com", + "snmp://tester5@example.com:8161", + "snmp://example.com/bridge1", + "snmp://example.com/bridge1;800002b804616263", + "snmp://example.com//1.3.6.1.2.1.1.3.0", + "snmp://example.com//1.3.6.1.2.1.1.3+", + "snmp://example.com//1.3.6.1.2.1.1.3.*", + "snmp://example.com/bridge1/1.3.6.1.2.1.2.2.1.8.*", + "snmp://example.com//(1.3.6.1.2.1.2.2.1.7,1.3.6.1.2.1.2.2.1.8).*", + /* from RFC 4151 */ + "tag:timothy@hpl.hp.com,2001:web/externalHome", + "tag:sandro@w3.org,2004-05:Sandro", + "tag:my-ids.com,2001-09-15:TimKindberg:presentations:UBath2004-05-19", + "tag:blogger.com,1999:blog-555", + "tag:yaml.org,2002:int", + /* from RFC 4227 */ + "soap.beep://stockquoteserver.example.com/StockQuote", + "soap.beep://stockquoteserver.example.com:1026", + "soap.beep://stockquoteserver.example.com", + "soap.beep://192.0.2.0:1026", + /* from RFC 4324 */ + "cap://cal.example.com", + "cap://cal.example.com/Company/Holidays", + "cap://cal.example.com/abcd1234Usr", + "cap://cal.example.com/abcd1234USR", + "cap://host.com/joe", + "cap:example.com/Doug", + "cap://cal.example.com/sdfifgty4321", + "cap://calendar.example.com", + "cap://mycal.example.com", + /* from RFC 4452 */ + "info:ddc/22/eng//004.678", + "info:lccn/2002022641", + "info:sici/0363-0277(19950315)120:5%3C%3E1.0.TX;2-V", + "info:bibcode/2003Icar..163..263Z", + "info:pmid/12376099", + /* from RFC 4501 */ + "dns:www.example.org.?clAsS=IN;tYpE=A", + "dns:www.example.org", + "dns:simon.example.org?type=CERT", + "dns://192.168.1.1/ftp.example.org?type=A", + "dns:world%20wide%20web.example%5c.domain.org?TYPE=TXT", +#if 0 // contains %00 encoding, which is currently always rejected + "dns://fw.example.org/*.%20%00.example?type=TXT", +#endif + /* from RFC 4516 */ + "ldap:///o=University%20of%20Michigan,c=US", + "ldap://ldap1.example.net/o=University%20of%20Michigan,c=US", + "ldap://ldap1.example.net/o=University%20of%20Michigan," + "c=US?postalAddress", + "ldap://ldap1.example.net:6666/o=University%20of%20Michigan," + "c=US?\?sub?(cn=Babs%20Jensen)", + "LDAP://ldap1.example.com/c=GB?objectClass?ONE", + "ldap://ldap2.example.com/o=Question%3f,c=US?mail", + "ldap://ldap3.example.com/o=Babsco,c=US" + "??\?(four-octet=%5c00%5c00%5c00%5c04)", + "ldap://ldap.example.com/o=An%20Example%5C2C%20Inc.,c=US", + "ldap://ldap.example.net", + "ldap://ldap.example.net/", + "ldap://ldap.example.net/?", + "ldap:///?\?sub?\?e-bindname=cn=Manager%2cdc=example%2cdc=com", + "ldap:///?\?sub?\?!e-bindname=cn=Manager%2cdc=example%2cdc=com" + /* from RFC 4975 */ + "msrp://atlanta.example.com:7654/jshA7weztas;tcp", + "msrp://biloxi.example.com:12763/kjhd37s2s20w2a;tcp", + "msrp://host.example.com:8493/asfd34;tcp", + "msrp://alice.example.com:7394/2s93i9ek2a;tcp", + "msrp://bob.example.com:8493/si438dsaodes;tcp", + "msrp://alicepc.example.com:7777/iau39soe2843z;tcp", + "msrp://bob.example.com:8888/9di4eae923wzd;tcp", + "msrp://alice.example.com:7777/iau39soe2843z;tcp", + "msrp://bobpc.example.com:8888/9di4eae923wzd;tcp", + "msrp://alicepc.example.com:7654/iau39soe2843z;tcp", + "msrp://alicepc.example.com:8888/9di4eae923wzd;tcp", + "msrp://example.com:7777/iau39soe2843z;tcp", + "msrp://bob.example.com:8888/9di4eae923wzd;tcp", + /* from RFC 5092 */ + "imap://michael@example.org/INBOX", + "imap://bester@example.org/INBOX", + "imap://joe@example.com/INBOX/;uid=20/;section=1.2;urlauth=" + "submit+fred:internal:91354a473744909de610943775f92038", + "imap://minbari.example.org/gray-council;UIDVALIDITY=385759045/;" + "UID=20/;PARTIAL=0.1024", + "imap://psicorp.example.org/~peter/%E6%97%A5%E6%9C%AC%E8%AA%9E/" + "%E5%8F%B0%E5%8C%97", + "imap://;AUTH=GSSAPI@minbari.example.org/gray-council/;uid=20/" + ";section=1.2", + "imap://;AUTH=*@minbari.example.org/gray%20council?" + "SUBJECT%20shadows", + "imap://john;AUTH=*@minbari.example.org/babylon5/personel?" + "charset%20UTF-8%20SUBJECT%20%7B14+%7D%0D%0A%D0%98%D0%B2%" + "D0%B0%D0%BD%D0%BE%D0%B2%D0%B0", + /* from RFC 5122 */ + "xmpp:node@example.com", + "xmpp://guest@example.com", + "xmpp:guest@example.com", + "xmpp://guest@example.com/support@example.com?message", + "xmpp:support@example.com?message", + "xmpp:example-node@example.com", + "xmpp:example-node@example.com/some-resource", + "xmpp:example.com", + "xmpp:example-node@example.com?message", + "xmpp:example-node@example.com?message;subject=Hello%20World", + "xmpp:example-node@example.com", + "xmpp:example-node@example.com?query", + "xmpp:nasty!%23$%25()*+,-.;=%3F%5B%5C%5D%5E_%60%7B%7C%7D~node@example.com", + "xmpp:node@example.com/repulsive%20!%23%22$%25&'()*+,-.%2F:;%3C=" + "%3E%3F%40%5B%5C%5D%5E_%60%7B%7C%7D~resource", + "xmpp:ji%C5%99i@%C4%8Dechy.example/v%20Praze", + /* from RFC 5456 */ +#if 0 // these don't comply with RFC 3986 + "iax:example.com/alice", + "iax:example.com:4569/alice", + "iax:example.com:4570/alice?friends", + "iax:192.0.2.4:4569/alice?friends", + "iax:[2001:db8::1]:4569/alice?friends", + "iax:example.com/12022561414", + "iax:johnQ@example.com/12022561414", + "iax:atlanta.com/alice", + "iax:AtLaNtA.com/ALicE", + "iax:atlanta.com:4569/alice", + "iax:alice@atlanta.com/alice", + "iax:alice@AtLaNtA.com:4569/ALicE", + "iax:ALICE@atlanta.com/alice", + "iax:alice@atlanta.com/alice", +#endif + /* from RFC 5724 */ + "sms:+15105550101", + "sms:+15105550101,+15105550102", + "sms:+15105550101?body=hello%20there", + /* from RFC 5804 */ + "sieve://example.com//script", + "sieve://example.com/script", + /* from RFC 5538 */ + "news://news.server.example/example.group.this", + "news://news.server.example/*", + "news://news.server.example/", + "news://wild.server.example/example.group.th%3Fse", + "news:example.group.*", + "news:example.group.this", + "news://news.gmane.org/gmane.ietf.tools", + "news://news.gmane.org/p0624081dc30b8699bf9b@%5B10.20.30.108%5D", + "nntp://wild.server.example/example.group.n%2Fa/12345", + "nntp://news.server.example/example.group.this", + "nntp://news.gmane.org/gmane.ietf.tools/742", + "nntp://news.server.example/example.group.this/12345", + /* from RFC 5870 */ + "geo:13.4125,103.8667", + "geo:48.2010,16.3695,183", + "geo:48.198634,16.371648;crs=wgs84;u=40", + "geo:90,-22.43;crs=WGS84", + "geo:90,46", + "geo:22.300;-118.44", + "geo:22.3;-118.4400", + "geo:66,30;u=6.500;FOo=this%2dthat", + "geo:66.0,30;u=6.5;foo=this-that", + "geo:70,20;foo=1.00;bar=white", + "geo:70,20;foo=1;bar=white", + "geo:47,11;foo=blue;bar=white", + "geo:47,11;bar=white;foo=blue", + "geo:22,0;bar=Blue", + "geo:22,0;BAR=blue", + /* from RFC 6068 */ + "mailto:addr1@an.example,addr2@an.example", + "mailto:?to=addr1@an.example,addr2@an.example", + "mailto:addr1@an.example?to=addr2@an.example", + "mailto:chris@example.com", + "mailto:infobot@example.com?subject=current-issue", + "mailto:infobot@example.com?body=send%20current-issue", + "mailto:infobot@example.com?body=send%20current-issue%0D%0Asend%20index", + "mailto:list@example.org?In-Reply-To=%3C3469A91.D10AF4C@example.com%3E", + "mailto:majordomo@example.com?body=subscribe%20bamboo-l", + "mailto:joe@example.com?cc=bob@example.com&body=hello", + "mailto:gorby%25kremvax@example.com", + "mailto:unlikely%3Faddress@example.com?blat=foop", + "mailto:joe@an.example?cc=bob@an.example&body=hello", + "mailto:Mike%26family@example.org", + "mailto:%22not%40me%22@example.org", + "mailto:%22oh%5C%5Cno%22@example.org", + "mailto:%22%5C%5C%5C%22it's%5C%20ugly%5C%5C%5C%22%22@example.org", + "mailto:user@example.org?subject=caf%C3%A9", + "mailto:user@example.org?subject=%3D%3Futf-8%3FQ%3Fcaf%3DC3%3DA9%3F%3D", + "mailto:user@example.org?subject=%3D%3Fiso-8859-1%3FQ%3Fcaf%3DE9%3F%3D", + "mailto:user@example.org?subject=caf%C3%A9&body=caf%C3%A9", + "mailto:user@%E7%B4%8D%E8%B1%86.example.org?subject=Test&body=NATTO", + /* from RFC 6455 */ + "ws://example.com/chat", + /* from RFC 6694 */ + "about:blank", + /* from RFC 6733 */ +#if 0 // these don't comply with RFC 3986 + "aaa://host.example.com;transport=tcp", + "aaa://host.example.com:6666;transport=tcp", + "aaa://host.example.com;protocol=diameter", + "aaa://host.example.com:6666;protocol=diameter", + "aaa://host.example.com:6666;transport=tcp;protocol=diameter", + "aaa://host.example.com:1813;transport=udp;protocol=radius", +#endif + /* from RFC 6787 */ + "session:request1@form-level.store", + "session:help@root-level.store", + "session:menu1@menu-level.store", + "session:request1@form-level.store", + "session:request2@field-level.store", + "session:helpgramar@root-level.store", + "session:request1@form-level.store", + "session:field3@form-level.store", + /* from RFC 6920 */ + "ni:///sha-256;UyaQV-Ev4rdLoHyJJWCi11OHfrYv9E1aGQAlMO2X_-Q", + "ni:///sha-256-32;f4OxZQ?ct=text/plain", + "ni:///sha-256;f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk", + "ni://example.com/sha-256;f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk", + "nih:sha-256-120;5326-9057-e12f-e2b7-4ba0-7c89-2560-a2;f", + "nih:sha-256-32;53269057;b", + "nih:3;532690-57e12f-e2b74b-a07c89-2560a2;f", + /* from RFC 7064 */ + "stun:example.org", + "stuns:example.org", + "stun:example.org:8000", + /* from RFC 7065 */ + "turn:example.org", + "turns:example.org", + "turn:example.org:8000", + "turn:example.org?transport=udp", + "turn:example.org?transport=tcp", + "turns:example.org?transport=tcp", + /* from RFC 7230 */ + "http://www.example.com/hello.txt", + "http://example.com:80/~smith/home.html", + "http://EXAMPLE.com/%7Esmith/home.html", + "http://EXAMPLE.com:/%7esmith/home.html", + "http://www.example.org/where?q=now", + "http://www.example.org/pub/WWW/TheProject.html", + "http://www.example.org:8001", + "http://www.example.org:8080/pub/WWW/TheProject.html", + /* from RFC 7252 */ + "coap://example.com:5683/~sensors/temp.xml", + "coap://EXAMPLE.com/%7Esensors/temp.xml", + "coap://EXAMPLE.com:/%7esensors/temp.xml", + "coap://server/temperature", + "coap://[2001:db8::2:1]/", + "coap://example.net/", + "coap://example.net/.well-known/core", + "coap://xn--18j4d.example/%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF", + "coap://198.51.100.1:61616//%2F//?%2F%2F&?%26" + /* from draft-ietf-appsawg-acct-uri-06 */ + "acct:foobar@status.example.net", + "acct:user@example.com", + "acct:bob@example.com", + /* from draft-mcdonald-ipps-uri-scheme-18 */ + "ipps://example.com/", + "ipps://example.com/ipp", + "ipps://example.com/ipp/faxout", + "ipps://example.com/ipp/print", + "ipps://example.com/ipp/scan", + "ipps://example.com/ipp/print/bob", + "ipps://example.com/ipp/print/ira", + "ipps://example.com/", + "ipps://example.com/ipp/print", + "ipps://example.com:631/ipp/print", + /* from draft-pechanec-pkcs11uri-21 */ + "pkcs11:", + "pkcs11:object=my-pubkey;type=public", + "pkcs11:object=my-key;type=private?pin-source=file:/etc/token", + "pkcs11:token=The%20Software%20PKCS%2311%20Softtoken;" + "manufacturer=Snake%20Oil,%20Inc.;model=1.0;" + "object=my-certificate;type=cert;" + "id=%69%95%3E%5C%F4%BD%EC%91;serial=" + "?pin-source=file:/etc/token_pin", + "pkcs11:object=my-sign-key;type=private?module-name=mypkcs11", + "pkcs11:object=my-sign-key;type=private" + "?module-path=/mnt/libmypkcs11.so.1", + "pkcs11:token=Software%20PKCS%2311%20softtoken;" + "manufacturer=Snake%20Oil,%20Inc.?pin-value=the-pin", + "pkcs11:slot-description=Sun%20Metaslot", + "pkcs11:library-manufacturer=Snake%20Oil,%20Inc.;" + "library-description=Soft%20Token%20Library;" + "library-version=1.23", + "pkcs11:token=My%20token%25%20created%20by%20Joe;" + "library-version=3;id=%01%02%03%Ba%dd%Ca%fe%04%05%06", + "pkcs11:token=A%20name%20with%20a%20substring%20%25%3B;" + "object=my-certificate;type=cert", + "pkcs11:token=Name%20with%20a%20small%20A%20with%20acute:%20%C3%A1;" + "object=my-certificate;type=cert", + "pkcs11:token=my-token;object=my-certificate;" + "type=cert;vendor-aaa=value-a" + "?pin-source=file:/etc/token_pin&vendor-bbb=value-b" +}; + +unsigned int rfc_uri_test_count = N_ELEMENTS(rfc_uri_tests); + +static void test_uri_rfc(void) +{ + unsigned int i; + + test_begin("uri from rfcs"); + for (i = 0; i < rfc_uri_test_count; i++) T_BEGIN { + const char *uri_in, *error = NULL; + int ret; + + uri_in = rfc_uri_tests[i]; + + ret = uri_check(uri_in, URI_PARSE_ALLOW_FRAGMENT_PART, &error); + test_out_quiet( + t_strdup_printf("parse [%d] <%s>", i, str_sanitize(uri_in, 64)), + ret >= 0); + } T_END; + test_end(); +} + +static void test_uri_escape(void) +{ + string_t *str = t_str_new(256); + + test_begin("uri escape - userinfo"); + uri_append_user_data(str, NULL, "abcdefghijklmnopqrstuvwxyz"); + test_assert(strcmp(str_c(str), "abcdefghijklmnopqrstuvwxyz") == 0); + str_truncate(str, 0); + uri_append_user_data(str, NULL, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + test_assert(strcmp(str_c(str), "ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 0); + str_truncate(str, 0); + uri_append_user_data(str, NULL, "0123456789"); + test_assert(strcmp(str_c(str), "0123456789") == 0); + str_truncate(str, 0); + uri_append_user_data(str, NULL, "-._~!$&'()*+,;="); + test_assert(strcmp(str_c(str), "-._~!$&'()*+,;=") == 0); + str_truncate(str, 0); + uri_append_user_data(str, NULL, "a@b/c/d:e"); + test_assert(strcmp(str_c(str), "a%40b%2fc%2fd:e") == 0); + str_truncate(str, 0); + uri_append_user_data(str, NULL, "[yes]what?oh#13"); + test_assert(strcmp(str_c(str), "%5byes%5dwhat%3foh%2313") == 0); + str_truncate(str, 0); + uri_append_user_data(str, ":", "a@b/c/d:e"); + test_assert(strcmp(str_c(str), "a%40b%2fc%2fd%3ae") == 0); + str_truncate(str, 0); + test_end(); + + test_begin("uri escape - path segment"); + uri_append_path_segment_data(str, NULL, "abcdefghijklmnopqrstuvwxyz"); + test_assert(strcmp(str_c(str), "abcdefghijklmnopqrstuvwxyz") == 0); + str_truncate(str, 0); + uri_append_path_segment_data(str, NULL, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + test_assert(strcmp(str_c(str), "ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 0); + str_truncate(str, 0); + uri_append_path_segment_data(str, NULL, "0123456789"); + test_assert(strcmp(str_c(str), "0123456789") == 0); + str_truncate(str, 0); + uri_append_path_segment_data(str, NULL, "-._~!$&'()*+,;="); + test_assert(strcmp(str_c(str), "-._~!$&'()*+,;=") == 0); + str_truncate(str, 0); + uri_append_path_segment_data(str, NULL, "a@b/c/d:e"); + test_assert(strcmp(str_c(str), "a@b%2fc%2fd:e") == 0); + str_truncate(str, 0); + uri_append_path_segment_data(str, NULL, "[yes]what?oh#13"); + test_assert(strcmp(str_c(str), "%5byes%5dwhat%3foh%2313") == 0); + str_truncate(str, 0); + uri_append_path_segment_data(str, "@", "a@b/c/d:e"); + test_assert(strcmp(str_c(str), "a%40b%2fc%2fd:e") == 0); + str_truncate(str, 0); + test_end(); + + test_begin("uri escape - path"); + uri_append_path_data(str, NULL, "abcdefghijklmnopqrstuvwxyz"); + test_assert(strcmp(str_c(str), "abcdefghijklmnopqrstuvwxyz") == 0); + str_truncate(str, 0); + uri_append_path_data(str, NULL, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + test_assert(strcmp(str_c(str), "ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 0); + str_truncate(str, 0); + uri_append_path_data(str, NULL, "0123456789"); + test_assert(strcmp(str_c(str), "0123456789") == 0); + str_truncate(str, 0); + uri_append_path_data(str, NULL, "-._~!$&'()*+,;="); + test_assert(strcmp(str_c(str), "-._~!$&'()*+,;=") == 0); + str_truncate(str, 0); + uri_append_path_data(str, NULL, "a@b/c/d:e"); + test_assert(strcmp(str_c(str), "a@b/c/d:e") == 0); + str_truncate(str, 0); + uri_append_path_data(str, NULL, "[yes]what?oh#13"); + test_assert(strcmp(str_c(str), "%5byes%5dwhat%3foh%2313") == 0); + str_truncate(str, 0); + uri_append_path_data(str, "@", "a@b/c/d:e"); + test_assert(strcmp(str_c(str), "a%40b/c/d:e") == 0); + str_truncate(str, 0); + test_end(); + + test_begin("uri escape - query"); + uri_append_query_data(str, NULL, "abcdefghijklmnopqrstuvwxyz"); + test_assert(strcmp(str_c(str), "abcdefghijklmnopqrstuvwxyz") == 0); + str_truncate(str, 0); + uri_append_query_data(str, NULL, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + test_assert(strcmp(str_c(str), "ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 0); + str_truncate(str, 0); + uri_append_query_data(str, NULL, "0123456789"); + test_assert(strcmp(str_c(str), "0123456789") == 0); + str_truncate(str, 0); + uri_append_query_data(str, NULL, "-._~!$&'()*+,;="); + test_assert(strcmp(str_c(str), "-._~!$&'()*+,;=") == 0); + str_truncate(str, 0); + uri_append_query_data(str, NULL, "a@b/c/d:e"); + test_assert(strcmp(str_c(str), "a@b/c/d:e") == 0); + str_truncate(str, 0); + uri_append_query_data(str, NULL, "[yes]what?oh#13"); + test_assert(strcmp(str_c(str), "%5byes%5dwhat?oh%2313") == 0); + str_truncate(str, 0); + uri_append_query_data(str, "@", "a@b/c/d:e"); + test_assert(strcmp(str_c(str), "a%40b/c/d:e") == 0); + str_truncate(str, 0); + test_end(); + + test_begin("uri escape - fragment"); + uri_append_fragment_data(str, NULL, "abcdefghijklmnopqrstuvwxyz"); + test_assert(strcmp(str_c(str), "abcdefghijklmnopqrstuvwxyz") == 0); + str_truncate(str, 0); + uri_append_fragment_data(str, NULL, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + test_assert(strcmp(str_c(str), "ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 0); + str_truncate(str, 0); + uri_append_fragment_data(str, NULL, "0123456789"); + test_assert(strcmp(str_c(str), "0123456789") == 0); + str_truncate(str, 0); + uri_append_fragment_data(str, NULL, "-._~!$&'()*+,;="); + test_assert(strcmp(str_c(str), "-._~!$&'()*+,;=") == 0); + str_truncate(str, 0); + uri_append_fragment_data(str, NULL, "a@b/c/d:e"); + test_assert(strcmp(str_c(str), "a@b/c/d:e") == 0); + str_truncate(str, 0); + uri_append_fragment_data(str, NULL, "[yes]what?oh#13"); + test_assert(strcmp(str_c(str), "%5byes%5dwhat?oh%2313") == 0); + str_truncate(str, 0); + uri_append_fragment_data(str, "@", "a@b/c/d:e"); + test_assert(strcmp(str_c(str), "a%40b/c/d:e") == 0); + str_truncate(str, 0); + test_end(); + + test_begin("uri escape - unreserved"); + uri_append_unreserved(str, "abcdefghijklmnopqrstuvwxyz"); + test_assert(strcmp(str_c(str), "abcdefghijklmnopqrstuvwxyz") == 0); + str_truncate(str, 0); + uri_append_unreserved(str, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + test_assert(strcmp(str_c(str), "ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 0); + str_truncate(str, 0); + uri_append_unreserved(str, "0123456789"); + test_assert(strcmp(str_c(str), "0123456789") == 0); + str_truncate(str, 0); + uri_append_unreserved(str, "-._~"); + test_assert(strcmp(str_c(str), "-._~") == 0); + str_truncate(str, 0); + uri_append_unreserved(str, "!$&'()*+,;="); + test_assert(strcmp(str_c(str), "%21%24%26%27%28%29%2a%2b%2c%3b%3d") == 0); + str_truncate(str, 0); + uri_append_unreserved(str, "a@b/c/d:e"); + test_assert(strcmp(str_c(str), "a%40b%2fc%2fd%3ae") == 0); + str_truncate(str, 0); + uri_append_unreserved(str, "[yes]what?oh#13"); + test_assert(strcmp(str_c(str), "%5byes%5dwhat%3foh%2313") == 0); + str_truncate(str, 0); + test_end(); + + test_begin("uri escape - unreserved"); + uri_append_unreserved_path(str, "abcdefghijklmnopqrstuvwxyz"); + test_assert(strcmp(str_c(str), "abcdefghijklmnopqrstuvwxyz") == 0); + str_truncate(str, 0); + uri_append_unreserved_path(str, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + test_assert(strcmp(str_c(str), "ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 0); + str_truncate(str, 0); + uri_append_unreserved_path(str, "0123456789"); + test_assert(strcmp(str_c(str), "0123456789") == 0); + str_truncate(str, 0); + uri_append_unreserved_path(str, "-._~"); + test_assert(strcmp(str_c(str), "-._~") == 0); + str_truncate(str, 0); + uri_append_unreserved_path(str, "!$&'()*+,;="); + test_assert(strcmp(str_c(str), "%21%24%26%27%28%29%2a%2b%2c%3b%3d") == 0); + str_truncate(str, 0); + uri_append_unreserved_path(str, "a@b/c/d:e"); + test_assert(strcmp(str_c(str), "a%40b/c/d%3ae") == 0); + str_truncate(str, 0); + uri_append_unreserved_path(str, "[yes]what?oh#13"); + test_assert(strcmp(str_c(str), "%5byes%5dwhat%3foh%2313") == 0); + str_truncate(str, 0); + test_end(); +} + +void test_uri(void) +{ + test_uri_valid(); + test_uri_invalid(); + test_uri_rfc(); + test_uri_escape(); +} diff --git a/src/lib/test-utc-mktime.c b/src/lib/test-utc-mktime.c new file mode 100644 index 0000000..157179b --- /dev/null +++ b/src/lib/test-utc-mktime.c @@ -0,0 +1,61 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "utc-mktime.h" + +struct test_utc_mktime { + int year, month, day, hour, min, sec; + time_t out; +}; + +void test_utc_mktime(void) +{ + static const struct test_utc_mktime tests[] = { +#ifdef TIME_T_SIGNED + { 1969, 12, 31, 23, 59, 59, -1 }, + { 1901, 12, 13, 20, 45, 53, -2147483647 }, +#endif +#if (TIME_T_MAX_BITS > 32 || !defined(TIME_T_SIGNED)) + { 2106, 2, 7, 6, 28, 15, 4294967295 }, + { 2038, 1, 19, 3, 14, 8, 2147483648 }, +#endif + { 2007, 11, 7, 1, 7, 20, 1194397640 }, + { 1970, 1, 1, 0, 0, 0, 0 }, + { 2038, 1, 19, 3, 14, 7, 2147483647 }, + { INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, -1 }, +#if TIME_T_MAX_BITS > 32 + { 2106, 2, 7, 6, 28, 16, 4294967296 }, +#endif + /* June leap second */ + { 2015, 6, 30, 23, 59, 59, 1435708799 }, + { 2015, 6, 30, 23, 59, 60, 1435708799 }, + { 2015, 7, 1, 0, 0, 0, 1435708800 }, + /* Invalid leap second */ + { 2017, 1, 24, 16, 40, 60, 1485276059 }, + /* Dec leap second */ + { 2016, 12, 31, 23, 59, 59, 1483228799 }, + { 2016, 12, 31, 23, 59, 60, 1483228799 }, + { 2017, 1, 1, 0, 0, 0, 1483228800 }, + }; + struct tm tm; + unsigned int i; + time_t t; + bool success; + + for (i = 0; i < N_ELEMENTS(tests); i++) { + const struct test_utc_mktime *test = &tests[i]; + i_zero(&tm); + tm.tm_year = test->year - 1900; + tm.tm_mon = test->month - 1; + tm.tm_mday = test->day; + tm.tm_hour = test->hour; + tm.tm_min = test->min; + tm.tm_sec = test->sec; + + t = utc_mktime(&tm); + success = t == test->out; + test_out_reason(t_strdup_printf("utc_mktime(%d)", i), success, + success ? NULL : t_strdup_printf("%ld != %ld", + (long)t, (long)test->out)); + } +} diff --git a/src/lib/test-var-expand.c b/src/lib/test-var-expand.c new file mode 100644 index 0000000..558ef76 --- /dev/null +++ b/src/lib/test-var-expand.c @@ -0,0 +1,474 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "env-util.h" +#include "hostpid.h" +#include "var-expand.h" +#include "var-expand-private.h" + +struct var_expand_test { + const char *in; + const char *out; + int ret; +}; + +struct var_get_key_range_test { + const char *in; + unsigned int idx, size; +}; + +static void test_var_expand_ranges(void) +{ + static const struct var_expand_test tests[] = { + { "%v", "value1234", 1 }, + { "%3v", "val", 1 }, + { "%3.2v", "ue", 1 }, + { "%3.-2v", "ue12", 1 }, + { "%-3.2v", "23", 1 }, + { "%0.-1v", "value123", 1 }, + { "%-4.-1v", "123", 1 } + }; + static const struct var_expand_table table[] = { + { 'v', "value1234", NULL }, + { '\0', NULL, NULL } + }; + string_t *str = t_str_new(128); + const char *error; + unsigned int i; + + test_begin("var_expand - ranges"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + str_truncate(str, 0); + test_assert(var_expand(str, tests[i].in, table, &error) == tests[i].ret); + test_assert(strcmp(tests[i].out, str_c(str)) == 0); + } + test_end(); +} + +static void test_var_expand_builtin(void) +{ + static struct var_expand_test tests[] = { + { "%{hostname}", NULL, 1 }, + { "%{pid}", NULL, 1 }, + { "a%{env:FOO}b", "abaRb", 1 }, + { "%50Hv", "1f", 1 }, + { "%50Hw", "2e", 1 }, + { "%50Nv", "25", 1 }, + { "%50Nw", "e", 1 }, + + { "%{nonexistent}", "UNSUPPORTED_VARIABLE_nonexistent", 0 }, + { "%1.2M{nonexistent:default}", "UNSUPPORTED_VARIABLE_nonexistent", 0 }, + { "%x", "UNSUPPORTED_VARIABLE_x", 0 }, + { "%5Mm", "UNSUPPORTED_VARIABLE_m", 0 }, + }; + static const struct var_expand_table table[] = { + { 'v', "value", NULL }, + { 'w', "value2", NULL }, + { '\0', NULL, NULL } + }; + string_t *str = t_str_new(128); + const char *error; + unsigned int i; + + tests[0].out = my_hostname; + tests[1].out = my_pid; + env_put("FOO", "baR"); + + test_begin("var_expand - builtin"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + str_truncate(str, 0); + test_assert_idx(var_expand(str, tests[i].in, table, &error) == tests[i].ret, i); + test_assert_idx(strcmp(tests[i].out, str_c(str)) == 0, i); + } + test_end(); +} + +static void test_var_get_key_range(void) +{ + static const struct var_get_key_range_test tests[] = { + { "", 0, 0 }, + { "{", 1, 0 }, + { "k", 0, 1 }, + { "{key}", 1, 3 }, + { "5.5Rk", 4, 1 }, + { "5.5R{key}", 5, 3 }, + { "{key", 1, 3 }, + { "{if;%{if;%{value};eq;value;t;f};eq;t;t;f}", 1, 39 }, + }; + unsigned int i, idx, size; + + test_begin("var_get_key_range"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + var_get_key_range(tests[i].in, &idx, &size); + test_assert_idx(tests[i].idx == idx, i); + test_assert_idx(tests[i].size == size, i); + + if (tests[i].size == 1) + test_assert_idx(tests[i].in[idx] == var_get_key(tests[i].in), i); + } + test_end(); +} + +static int test_var_expand_func1(const char *data, void *context, + const char **value_r, + const char **error_r ATTR_UNUSED) +{ + test_assert(*(int *)context == 0xabcdef); + *value_r = t_strdup_printf("<%s>", data); + return 1; +} + +static int test_var_expand_func2(const char *data ATTR_UNUSED, + void *context ATTR_UNUSED, + const char **value_r, + const char **error_r ATTR_UNUSED) +{ + *value_r = ""; + return 1; +} + +static int test_var_expand_func3(const char *data ATTR_UNUSED, + void *context ATTR_UNUSED, + const char **value_r, + const char **error_r ATTR_UNUSED) +{ + *value_r = NULL; + return 1; +} + +static int test_var_expand_func4(const char *data, + void *context ATTR_UNUSED, + const char **value_r ATTR_UNUSED, + const char **error_r) +{ + *error_r = t_strdup_printf("Unknown data %s", data == NULL ? "" : data); + return 0; +} + +static int test_var_expand_func5(const char *data ATTR_UNUSED, + void *context ATTR_UNUSED, + const char **value_r ATTR_UNUSED, + const char **error_r) +{ + *error_r = "Internal error"; + return -1; +} + +static void test_var_expand_with_funcs(void) +{ + static const struct var_expand_test tests[] = { + { "%{func1}", "<>", 1 }, + { "%{func1:foo}", "<foo>", 1 }, + { "%{func2}", "", 1 }, + { "%{func3}", "", 1 }, + { "%{func4}", "", 0 }, + { "%{func5}", "", -1 }, + { "%{func4}%{func5}", "", -1 }, + { "%{func5}%{func4}%{func3}", "", -1 }, + }; + static const struct var_expand_table table[] = { + { '\0', NULL, NULL } + }; + static const struct var_expand_func_table func_table[] = { + { "func1", test_var_expand_func1 }, + { "func2", test_var_expand_func2 }, + { "func3", test_var_expand_func3 }, + { "func4", test_var_expand_func4 }, + { "func5", test_var_expand_func5 }, + { NULL, NULL } + }; + string_t *str = t_str_new(128); + const char *error; + unsigned int i; + int ctx = 0xabcdef; + + test_begin("var_expand_with_funcs"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + str_truncate(str, 0); + test_assert_idx(var_expand_with_funcs(str, tests[i].in, table, func_table, &ctx, &error) == tests[i].ret, i); + test_assert_idx(strcmp(tests[i].out, str_c(str)) == 0, i); + } + test_end(); +} + +static void test_var_get_key(void) +{ + static const struct { + const char *str; + char key; + } tests[] = { + { "x", 'x' }, + { "2.5Mx", 'x' }, + { "200MDx", 'x' }, + { "200MD{foo}", '{' }, + { "{foo}", '{' }, + { "", '\0' }, + }; + + test_begin("var_get_key"); + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) + test_assert_idx(var_get_key(tests[i].str) == tests[i].key, i); + test_end(); +} + +static void test_var_has_key(void) +{ + static const struct { + const char *str; + char key; + const char *long_key; + bool result; + } tests[] = { + { "%x%y", 'x', NULL, TRUE }, + { "%x%y", 'y', NULL, TRUE }, + { "%x%y", 'z', NULL, FALSE }, + { "%{foo}", 'f', NULL, FALSE }, + { "%{foo}", 'o', NULL, FALSE }, + { "%{foo}", '\0', "foo", TRUE }, + { "%{foo}", 'o', "foo", TRUE }, + { "%2.5Mx%y", 'x', NULL, TRUE }, + { "%2.5M{foo}", '\0', "foo", TRUE }, + }; + + test_begin("var_has_key"); + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) + test_assert_idx(var_has_key(tests[i].str, tests[i].key, tests[i].long_key) == tests[i].result, i); + test_end(); +} + +static int test_var_expand_hashing_func1(const char *data, + void *context ATTR_UNUSED, + const char **value_r, + const char **error_r ATTR_UNUSED) +{ + *value_r = data; + return 1; +} + +static int test_var_expand_bad_func(struct var_expand_context *ctx ATTR_UNUSED, + const char *key, + const char *field ATTR_UNUSED, + const char **result_r ATTR_UNUSED, + const char **error_r) +{ + if (strcmp(key, "notfound") == 0) return 0; + *error_r = "Bad parameters"; + return -1; +} + +static const struct var_expand_extension_func_table test_extension_funcs[] = { + { "notfound", test_var_expand_bad_func }, + { "badparam", test_var_expand_bad_func }, + { NULL, NULL } +}; + +static void test_var_expand_extensions(void) +{ + const char *error; + test_begin("var_expand_extensions"); + + var_expand_register_func_array(test_extension_funcs); + + static const struct var_expand_table table[] = { + {'\0', "example", "value" }, + {'\0', "other-example", "other-value" }, + {'\0', NULL, NULL} + }; + + static const struct { + const char *in; + const char *out; + } tests[] = { + { "md5: %M{value} %{md5:value}", "md5: 1a79a4d60de6718e8e5b326e338ae533 1a79a4d60de6718e8e5b326e338ae533" }, + { "sha1: %{sha1:value}", "sha1: c3499c2729730a7f807efb8676a92dcb6f8a3f8f" }, + { "sha1: %{sha1:func1:example}", "sha1: c3499c2729730a7f807efb8676a92dcb6f8a3f8f" }, + { "truncate: %{sha1;truncate=12:value}", "truncate: 0c34" }, + { "truncate: %{sha1;truncate=16:value}", "truncate: c349" }, + { "rounds,salt: %{sha1;rounds=1000,salt=seawater:value}", "rounds,salt: b515c85884f6b82dc7588279f3643a73e55d2289" }, + { "rounds,salt,expand: %{sha1;rounds=1000,salt=%{other-value}:value} %{other-value}", "rounds,salt,expand: 49a598ee110af615e175f2e4511cc5d7ccff96ab other-example" }, + { "format: %4.8{sha1:value}", "format: 9c272973" }, + { "base64: %{sha1;format=base64:value}", "base64: w0mcJylzCn+AfvuGdqkty2+KP48=" }, + }; + + static const struct var_expand_func_table func_table[] = { + { "func1", test_var_expand_hashing_func1 }, + { NULL, NULL } + }; + + string_t *str = t_str_new(128); + + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { + str_truncate(str, 0); + error = NULL; + test_assert(var_expand_with_funcs(str, tests[i].in, table, + func_table, NULL, &error) == 1); + test_assert_idx(strcmp(str_c(str), tests[i].out) == 0, i); + if (error != NULL) { + i_debug("Error: %s", error); + } + } + + test_assert(var_expand_with_funcs(str, "notfound: %{notfound:field}", + table, func_table, NULL, &error) == 0); + error = NULL; + test_assert(var_expand_with_funcs(str, "notfound: %{badparam:field}", + table, func_table, NULL, &error) == -1); + test_assert(error != NULL); + + var_expand_unregister_func_array(test_extension_funcs); + + test_end(); +} + +static void test_var_expand_if(void) +{ + static const struct var_expand_table table[] = { + { 'a', "alpha", "alpha" }, + { 'b', "beta", "beta" }, + { 'o', "1", "one" }, + { 't', "2", "two" }, + { '\0', ";:", "evil1" }, + { '\0', ";test;", "evil2" }, + { '\0', NULL, NULL } + }; + const char *error; + string_t *dest = t_str_new(64); + test_begin("var_expand_if"); + + static const struct var_expand_test tests[] = { + /* basic numeric operand test */ + { "%{if;1;==;1;yes;no}", "yes", 1 }, + { "%{if;1;==;2;yes;no}", "no", 1 }, + { "%{if;1;<;1;yes;no}", "no", 1 }, + { "%{if;1;<;2;yes;no}", "yes", 1 }, + { "%{if;1;<=;1;yes;no}", "yes", 1 }, + { "%{if;1;<=;2;yes;no}", "yes", 1 }, + { "%{if;1;>;1;yes;no}", "no", 1 }, + { "%{if;1;>;2;yes;no}", "no", 1 }, + { "%{if;1;>=;1;yes;no}", "yes", 1 }, + { "%{if;1;>=;2;yes;no}", "no", 1 }, + { "%{if;1;!=;1;yes;no}", "no", 1 }, + { "%{if;1;!=;2;yes;no}", "yes", 1 }, + /* basic string operand test */ + { "%{if;a;eq;a;yes;no}", "yes", 1 }, + { "%{if;a;eq;b;yes;no}", "no", 1 }, + { "%{if;a;lt;a;yes;no}", "no", 1 }, + { "%{if;a;lt;b;yes;no}", "yes", 1 }, + { "%{if;a;le;a;yes;no}", "yes", 1 }, + { "%{if;a;le;b;yes;no}", "yes", 1 }, + { "%{if;a;gt;a;yes;no}", "no", 1 }, + { "%{if;a;gt;b;yes;no}", "no", 1 }, + { "%{if;a;ge;a;yes;no}", "yes", 1 }, + { "%{if;a;ge;b;yes;no}", "no", 1 }, + { "%{if;a;ne;a;yes;no}", "no", 1 }, + { "%{if;a;ne;b;yes;no}", "yes", 1 }, + { "%{if;a;*;a;yes;no}", "yes", 1 }, + { "%{if;a;*;b;yes;no}", "no", 1 }, + { "%{if;a;*;*a*;yes;no}", "yes", 1 }, + { "%{if;a;*;*b*;yes;no}", "no", 1 }, + { "%{if;a;*;*;yes;no}", "yes", 1 }, + { "%{if;a;!*;a;yes;no}", "no", 1 }, + { "%{if;a;!*;b;yes;no}", "yes", 1 }, + { "%{if;a;!*;*a*;yes;no}", "no", 1 }, + { "%{if;a;!*;*b*;yes;no}", "yes", 1 }, + { "%{if;a;!*;*;yes;no}", "no", 1 }, + { "%{if;a;~;a;yes;no}", "yes", 1 }, + { "%{if;a;~;b;yes;no}", "no", 1 }, + { "%{if;a;~;.*a.*;yes;no}", "yes", 1 }, + { "%{if;a;~;.*b.*;yes;no}", "no", 1 }, + { "%{if;a;~;.*;yes;no}", "yes", 1 }, + { "%{if;a;!~;a;yes;no}", "no", 1 }, + { "%{if;a;!~;b;yes;no}", "yes", 1 }, + { "%{if;a;!~;.*a.*;yes;no}", "no", 1 }, + { "%{if;a;!~;.*b.*;yes;no}", "yes", 1 }, + { "%{if;a;!~;.*;yes;no}", "no", 1 }, + { "%{if;this is test;~;^test;yes;no}", "no", 1 }, + { "%{if;this is test;~;.*test;yes;no}", "yes", 1 }, + /* variable expansion */ + { "%{if;%a;eq;%a;yes;no}", "yes", 1 }, + { "%{if;%a;eq;%b;yes;no}", "no", 1 }, + { "%{if;%{alpha};eq;%{alpha};yes;no}", "yes", 1 }, + { "%{if;%{alpha};eq;%{beta};yes;no}", "no", 1 }, + { "%{if;%o;eq;%o;yes;no}", "yes", 1 }, + { "%{if;%o;eq;%t;yes;no}", "no", 1 }, + { "%{if;%{one};eq;%{one};yes;no}", "yes", 1 }, + { "%{if;%{one};eq;%{two};yes;no}", "no", 1 }, + { "%{if;%{one};eq;%{one};%{one};%{two}}", "1", 1 }, + { "%{if;%{one};gt;%{two};%{one};%{two}}", "2", 1 }, + { "%{if;%{evil1};eq;\\;\\:;%{evil2};no}", ";test;", 1 }, + /* inner if */ + { "%{if;%{if;%{one};eq;1;1;0};eq;%{if;%{two};eq;2;2;3};yes;no}", "no", 1 }, + /* no false */ + { "%{if;1;==;1;yes}", "yes", 1 }, + { "%{if;1;==;2;yes}", "", 1 }, + /* invalid input */ + { "%{if;}", "", -1 }, + { "%{if;1;}", "", -1 }, + { "%{if;1;==;}", "", -1 }, + { "%{if;1;==;2;}", "", -1 }, + { "%{if;1;fu;2;yes;no}", "", -1 }, + /* missing variables */ + { "%{if;%{missing1};==;%{missing2};yes;no}", "", 0 }, + }; + + for(size_t i = 0; i < N_ELEMENTS(tests); i++) { + int ret; + error = NULL; + str_truncate(dest, 0); + ret = var_expand(dest, tests[i].in, table, &error); + test_assert_idx(tests[i].ret == ret, i); + test_assert_idx(strcmp(tests[i].out, str_c(dest)) == 0, i); + } + + test_end(); +} + +static void test_var_expand_merge_tables(void) +{ + const struct var_expand_table one[] = { + { 'a', "1", "alpha" }, + { '\0', "2", "beta" }, + { '\0', NULL, NULL } + }, + two[] = { + { 't', "3", "theta" }, + { '\0', "4", "phi" }, + { '\0', NULL, NULL } + }, + *merged = NULL; + + + test_begin("var_expand_merge_tables"); + + merged = t_var_expand_merge_tables(one, two); + + test_assert(var_expand_table_size(merged) == 4); + for(unsigned int i = 0; i < var_expand_table_size(merged); i++) { + if (i < 2) { + test_assert_idx(merged[i].key == one[i].key, i); + test_assert_idx(merged[i].value == one[i].value || strcmp(merged[i].value, one[i].value) == 0, i); + test_assert_idx(merged[i].long_key == one[i].long_key || strcmp(merged[i].long_key, one[i].long_key) == 0, i); + } else if (i < 4) { + test_assert_idx(merged[i].key == two[i-2].key, i); + test_assert_idx(merged[i].value == two[i-2].value || strcmp(merged[i].value, two[i-2].value) == 0, i); + test_assert_idx(merged[i].long_key == two[i-2].long_key || strcmp(merged[i].long_key, two[i-2].long_key) == 0, i); + } else { + break; + } + } + test_end(); +} + +void test_var_expand(void) +{ + test_var_expand_ranges(); + test_var_expand_builtin(); + test_var_get_key_range(); + test_var_expand_with_funcs(); + test_var_get_key(); + test_var_has_key(); + test_var_expand_extensions(); + test_var_expand_if(); + test_var_expand_merge_tables(); +} diff --git a/src/lib/test-wildcard-match.c b/src/lib/test-wildcard-match.c new file mode 100644 index 0000000..7a459da --- /dev/null +++ b/src/lib/test-wildcard-match.c @@ -0,0 +1,48 @@ +/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "wildcard-match.h" + +static const struct { + const char *data; + const char *mask; + bool result; +} tests[] = { + { "foo", "*", TRUE }, + { "foo", "*foo*", TRUE }, + { "foo", "foo", TRUE }, + { "foo", "f*o*o", TRUE }, + { "foo", "f??", TRUE }, + { "foo", "f?o", TRUE }, + { "foo", "*??", TRUE }, + { "foo", "???", TRUE }, + { "foo", "f??*", TRUE }, + { "foo", "???*", TRUE }, + + { "foo", "", FALSE }, + { "foo", "f", FALSE }, + { "foo", "fo", FALSE }, + { "foo", "fooo", FALSE }, + { "foo", "????", FALSE }, + { "foo", "f*o*o*o", FALSE }, + { "foo", "f???*", FALSE }, + + { "*foo", "foo", FALSE }, + { "foo*", "foo", FALSE }, + { "*foo*", "foo", FALSE }, + + { "", "*", TRUE }, + { "", "", TRUE }, + { "", "?", FALSE } +}; + +void test_wildcard_match(void) +{ + unsigned int i; + + test_begin("wildcard_match()"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + test_assert_idx(wildcard_match(tests[i].data, tests[i].mask) == tests[i].result, i); + } + test_end(); +} diff --git a/src/lib/time-util.c b/src/lib/time-util.c new file mode 100644 index 0000000..3f4cd01 --- /dev/null +++ b/src/lib/time-util.c @@ -0,0 +1,169 @@ +/* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "time-util.h" + +#include <time.h> + +#define STRFTIME_MAX_BUFSIZE (1024*64) + +void i_gettimeofday(struct timeval *tv_r) +{ + if (gettimeofday(tv_r, NULL) < 0) + i_fatal("gettimeofday() failed: %m"); +} + +uint64_t i_nanoseconds(void) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_REALTIME, &ts) < 0) + i_fatal("clock_gettime() failed: %m"); + return ts.tv_sec * 1000000000ULL + ts.tv_nsec; +} + +int timeval_cmp(const struct timeval *tv1, const struct timeval *tv2) +{ + if (tv1->tv_sec < tv2->tv_sec) + return -1; + if (tv1->tv_sec > tv2->tv_sec) + return 1; + if (tv1->tv_usec < tv2->tv_usec) + return -1; + if (tv1->tv_usec > tv2->tv_usec) + return 1; + return 0; +} + +int timeval_cmp_margin(const struct timeval *tv1, const struct timeval *tv2, + unsigned int usec_margin) +{ + long long secs_diff, usecs_diff; + int sec_margin, ret; + + if (tv1->tv_sec < tv2->tv_sec) { + sec_margin = ((int)usec_margin / 1000000) + 1; + secs_diff = (long long)tv2->tv_sec - (long long)tv1->tv_sec; + if (secs_diff > sec_margin) + return -1; + usecs_diff = secs_diff * 1000000LL + + (tv2->tv_usec - tv1->tv_usec); + ret = -1; + } else if (tv1->tv_sec > tv2->tv_sec) { + sec_margin = ((int)usec_margin / 1000000) + 1; + secs_diff = (long long)tv1->tv_sec - (long long)tv2->tv_sec; + if (secs_diff > sec_margin) + return 1; + usecs_diff = secs_diff * 1000000LL + + (tv1->tv_usec - tv2->tv_usec); + ret = 1; + } else if (tv1->tv_usec < tv2->tv_usec) { + usecs_diff = tv2->tv_usec - tv1->tv_usec; + ret = -1; + } else { + usecs_diff = tv1->tv_usec - tv2->tv_usec; + ret = 1; + } + i_assert(usecs_diff >= 0); + return (unsigned long long)usecs_diff > usec_margin ? ret : 0; +} + +int timeval_diff_msecs(const struct timeval *tv1, const struct timeval *tv2) +{ + long long diff = timeval_diff_usecs(tv1, tv2) / 1000LL; +#ifdef DEBUG + /* FIXME v2.4: Remove the ifdef */ + i_assert(diff <= INT_MAX); +#endif + return (int)diff; +} + +long long timeval_diff_usecs(const struct timeval *tv1, + const struct timeval *tv2) +{ + time_t secs; + int usecs; + + secs = tv1->tv_sec - tv2->tv_sec; + usecs = tv1->tv_usec - tv2->tv_usec; + if (usecs < 0) { + secs--; + usecs += 1000000; + } + return ((long long)secs * 1000000LL) + usecs; +} + +time_t time_to_local_day_start(time_t t) +{ + const struct tm *day_tm; + struct tm tm; + time_t new_start_time; + + day_tm = localtime(&t); + i_zero(&tm); + tm.tm_year = day_tm->tm_year; + tm.tm_mon = day_tm->tm_mon; + tm.tm_mday = day_tm->tm_mday; + tm.tm_isdst = -1; + new_start_time = mktime(&tm); + i_assert(new_start_time != (time_t)-1); + return new_start_time; +} + +static const char *strftime_real(const char *fmt, const struct tm *tm) +{ + size_t bufsize = strlen(fmt) + 32; + char *buf = t_buffer_get(bufsize); + size_t ret; + + while ((ret = strftime(buf, bufsize, fmt, tm)) == 0) { + bufsize *= 2; + i_assert(bufsize <= STRFTIME_MAX_BUFSIZE); + buf = t_buffer_get(bufsize); + } + t_buffer_alloc(ret + 1); + return buf; +} + +const char *t_strftime(const char *fmt, const struct tm *tm) +{ + return strftime_real(fmt, tm); +} + +const char *t_strflocaltime(const char *fmt, time_t t) +{ + return strftime_real(fmt, localtime(&t)); +} + +const char *t_strfgmtime(const char *fmt, time_t t) +{ + return strftime_real(fmt, gmtime(&t)); +} + +int str_to_timeval(const char *str, struct timeval *tv_r) +{ + tv_r->tv_usec = 0; + + const char *p = strchr(str, '.'); + if (p == NULL) + return str_to_time(str, &tv_r->tv_sec); + + int ret; + T_BEGIN { + ret = str_to_time(t_strdup_until(str, p++), &tv_r->tv_sec); + } T_END; + if (ret < 0 || p[0] == '\0') + return -1; + + unsigned int len = strlen(p); + if (len > 6) + return -1; /* we don't support sub-microseconds */ + char usecs_str[7] = "000000"; + memcpy(usecs_str, p, len); + + unsigned int usec; + if (str_to_uint(usecs_str, &usec) < 0) + return -1; + tv_r->tv_usec = usec; + return 0; +} diff --git a/src/lib/time-util.h b/src/lib/time-util.h new file mode 100644 index 0000000..0cda92c --- /dev/null +++ b/src/lib/time-util.h @@ -0,0 +1,108 @@ +#ifndef TIME_UTIL_H +#define TIME_UTIL_H + +#include <sys/time.h> /* for struct timeval */ + +/* Same as gettimeofday(), but call i_fatal() if the call fails. */ +void i_gettimeofday(struct timeval *tv_r); +/* Return nanoseconds since UNIX epoch (1970-01-01). */ +uint64_t i_nanoseconds(void); +/* Return microseconds since UNIX epoch (1970-01-01). */ +static inline uint64_t i_microseconds(void) { + return i_nanoseconds() / 1000; +} + +/* Returns -1 if tv1<tv2, 1 if tv1>tv2, 0 if they're equal. */ +int timeval_cmp(const struct timeval *tv1, const struct timeval *tv2); +/* Same as timeval_cmp, but tv->usecs must differ by at least usec_margin */ +int timeval_cmp_margin(const struct timeval *tv1, const struct timeval *tv2, + unsigned int usec_margin); +/* Returns tv1-tv2 in milliseconds. */ +int timeval_diff_msecs(const struct timeval *tv1, const struct timeval *tv2); +/* Returns tv1-tv2 in microseconds. */ +long long timeval_diff_usecs(const struct timeval *tv1, + const struct timeval *tv2); + +static inline void +timeval_add_usecs(struct timeval *tv, long long usecs) +{ + i_assert(usecs >= 0); + tv->tv_sec += usecs / 1000000; + tv->tv_usec += (usecs % 1000000); + if (tv->tv_usec >= 1000000) { + tv->tv_sec++; + tv->tv_usec -= 1000000; + } +} + +static inline void +timeval_sub_usecs(struct timeval *tv, long long usecs) +{ + i_assert(usecs >= 0); + tv->tv_sec -= usecs / 1000000; + tv->tv_usec -= (usecs % 1000000); + if (tv->tv_usec < 0) { + tv->tv_sec--; + tv->tv_usec += 1000000; + } +} + +static inline void +timeval_add_msecs(struct timeval *tv, unsigned int msecs) +{ + tv->tv_sec += msecs / 1000; + tv->tv_usec += (msecs % 1000) * 1000; + if (tv->tv_usec >= 1000000) { + tv->tv_sec++; + tv->tv_usec -= 1000000; + } +} + +static inline void +timeval_sub_msecs(struct timeval *tv, unsigned int msecs) +{ + tv->tv_sec -= msecs / 1000; + tv->tv_usec -= (msecs % 1000) * 1000; + if (tv->tv_usec < 0) { + tv->tv_sec--; + tv->tv_usec += 1000000; + } +} + +static inline unsigned long long timeval_to_usecs(const struct timeval *tv) +{ + return (tv->tv_sec * 1000000ULL + tv->tv_usec); +} + +static inline void timeval_add(struct timeval *tv, const struct timeval *val) +{ + i_assert(val->tv_usec < 1000000); + tv->tv_sec += val->tv_sec; + tv->tv_usec += val->tv_usec; + if (tv->tv_usec >= 1000000) { + tv->tv_sec++; + tv->tv_usec -= 1000000; + } +} + +static inline time_t timeval_round(struct timeval *tv) +{ + return (tv->tv_usec < 500000 ? tv->tv_sec : tv->tv_sec + 1); +} + +/* Convert t to local time and return timestamp on that day at 00:00:00. */ +time_t time_to_local_day_start(time_t t); + +/* Wrappers to strftime() */ +const char *t_strftime(const char *fmt, const struct tm *tm) ATTR_STRFTIME(1); +const char *t_strflocaltime(const char *fmt, time_t t) ATTR_STRFTIME(1); +const char *t_strfgmtime(const char *fmt, time_t t) ATTR_STRFTIME(1); + +/* Parse string as <unix timestamp>[.<usecs>] into timeval. <usecs> must not + have higher precision time, i.e. a maximum of 6 digits is allowed. Note that + ".1" is handled as ".1000000" so the string should have been written using + "%06u" printf format. */ +int str_to_timeval(const char *str, struct timeval *tv_r) + ATTR_WARN_UNUSED_RESULT; + +#endif diff --git a/src/lib/unichar.c b/src/lib/unichar.c new file mode 100644 index 0000000..7036e73 --- /dev/null +++ b/src/lib/unichar.c @@ -0,0 +1,447 @@ +/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "bsearch-insert-pos.h" +#include "unichar.h" + +#include "unicodemap.c" + +#define HANGUL_FIRST 0xac00 +#define HANGUL_LAST 0xd7a3 + +const unsigned char utf8_replacement_char[UTF8_REPLACEMENT_CHAR_LEN] = + { 0xef, 0xbf, 0xbd }; /* 0xfffd */ + +static const uint8_t utf8_non1_bytes[256 - 192 - 2] = { + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; + +const uint8_t *const uni_utf8_non1_bytes = utf8_non1_bytes; + +unsigned int uni_strlen(const unichar_t *str) +{ + unsigned int len = 0; + + for (len = 0; str[len] != 0; len++) ; + + return len; +} + +int uni_utf8_get_char(const char *input, unichar_t *chr_r) +{ + return uni_utf8_get_char_n((const unsigned char *)input, SIZE_MAX, + chr_r); +} + +int uni_utf8_get_char_n(const void *_input, size_t max_len, unichar_t *chr_r) +{ + static unichar_t lowest_valid_chr_table[] = + { 0, 0, 0x80, 0x800, 0x10000, 0x200000, 0x4000000 }; + const unsigned char *input = _input; + unichar_t chr, lowest_valid_chr; + unsigned int i, len; + int ret; + + i_assert(max_len > 0); + + if (*input < 0x80) { + *chr_r = *input; + return 1; + } + + /* first byte has len highest bits set, followed by zero bit. + the rest of the bits are used as the highest bits of the value. */ + chr = *input; + len = uni_utf8_char_bytes(*input); + switch (len) { + case 2: + chr &= 0x1f; + break; + case 3: + chr &= 0x0f; + break; + case 4: + chr &= 0x07; + break; + case 5: + chr &= 0x03; + break; + case 6: + chr &= 0x01; + break; + default: + /* only 7bit chars should have len==1 */ + i_assert(len == 1); + return -1; + } + + if (len <= max_len) { + lowest_valid_chr = lowest_valid_chr_table[len]; + ret = len; + } else { + /* check first if the input is invalid before returning 0 */ + lowest_valid_chr = 0; + ret = 0; + len = max_len; + } + + /* the following bytes must all be 10xxxxxx */ + for (i = 1; i < len; i++) { + if ((input[i] & 0xc0) != 0x80) + return input[i] == '\0' ? 0 : -1; + + chr <<= 6; + chr |= input[i] & 0x3f; + } + /* these are specified as invalid encodings by standards + see RFC3629 */ + if (!uni_is_valid_ucs4(chr)) + return -1; + if (chr < lowest_valid_chr) { + /* overlong encoding */ + return -1; + } + + *chr_r = chr; + return ret; +} + +int uni_utf8_to_ucs4(const char *input, ARRAY_TYPE(unichars) *output) +{ + unichar_t chr; + + while (*input != '\0') { + int len = uni_utf8_get_char(input, &chr); + if (len <= 0) { + /* invalid input */ + return -1; + } + input += len; + + array_push_back(output, &chr); + } + return 0; +} + +int uni_utf8_to_ucs4_n(const unsigned char *input, size_t size, + ARRAY_TYPE(unichars) *output) +{ + unichar_t chr; + + while (size > 0) { + int len = uni_utf8_get_char_n(input, size, &chr); + if (len <= 0) + return -1; /* invalid input */ + input += len; size -= len; + + array_push_back(output, &chr); + } + return 0; +} + +void uni_ucs4_to_utf8(const unichar_t *input, size_t len, buffer_t *output) +{ + for (; len > 0 && *input != '\0'; input++, len--) + uni_ucs4_to_utf8_c(*input, output); +} + +void uni_ucs4_to_utf8_c(unichar_t chr, buffer_t *output) +{ + unsigned char first; + int bitpos; + + if (chr < 0x80) { + buffer_append_c(output, chr); + return; + } + + i_assert(uni_is_valid_ucs4(chr)); + + if (chr < (1 << (6 + 5))) { + /* 110xxxxx */ + bitpos = 6; + first = 0x80 | 0x40; + } else if (chr < (1 << ((2*6) + 4))) { + /* 1110xxxx */ + bitpos = 2*6; + first = 0x80 | 0x40 | 0x20; + } else if (chr < (1 << ((3*6) + 3))) { + /* 11110xxx */ + bitpos = 3*6; + first = 0x80 | 0x40 | 0x20 | 0x10; + } else if (chr < (1 << ((4*6) + 2))) { + /* 111110xx */ + bitpos = 4*6; + first = 0x80 | 0x40 | 0x20 | 0x10 | 0x08; + } else { + /* 1111110x */ + bitpos = 5*6; + first = 0x80 | 0x40 | 0x20 | 0x10 | 0x08 | 0x04; + } + buffer_append_c(output, first | (chr >> bitpos)); + + do { + bitpos -= 6; + buffer_append_c(output, 0x80 | ((chr >> bitpos) & 0x3f)); + } while (bitpos > 0); +} + +unsigned int uni_utf8_strlen(const char *input) +{ + return uni_utf8_strlen_n(input, strlen(input)); +} + +unsigned int uni_utf8_strlen_n(const void *input, size_t size) +{ + size_t partial_pos; + + return uni_utf8_partial_strlen_n(input, size, &partial_pos); +} + +unsigned int uni_utf8_partial_strlen_n(const void *_input, size_t size, + size_t *partial_pos_r) +{ + const unsigned char *input = _input; + unsigned int count, len = 0; + size_t i; + + for (i = 0; i < size; ) { + count = uni_utf8_char_bytes(input[i]); + if (i + count > size) + break; + i += count; + len++; + } + *partial_pos_r = i; + return len; +} + +static bool uint16_find(const uint16_t *data, unsigned int count, + uint16_t value, unsigned int *idx_r) +{ + BINARY_NUMBER_SEARCH(data, count, value, idx_r); +} + +static bool uint32_find(const uint32_t *data, unsigned int count, + uint32_t value, unsigned int *idx_r) +{ + BINARY_NUMBER_SEARCH(data, count, value, idx_r); +} + +unichar_t uni_ucs4_to_titlecase(unichar_t chr) +{ + unsigned int idx; + + if (chr <= 0xff) + return titlecase8_map[chr]; + else if (chr <= 0xffff) { + if (!uint16_find(titlecase16_keys, N_ELEMENTS(titlecase16_keys), + chr, &idx)) + return chr; + else + return titlecase16_values[idx]; + } else { + if (!uint32_find(titlecase32_keys, N_ELEMENTS(titlecase32_keys), + chr, &idx)) + return chr; + else + return titlecase32_values[idx]; + } +} + +static bool uni_ucs4_decompose_uni(unichar_t *chr) +{ + unsigned int idx; + + if (*chr <= 0xff) { + if (uni8_decomp_map[*chr] == *chr) + return FALSE; + *chr = uni8_decomp_map[*chr]; + } else if (*chr <= 0xffff) { + if (*chr < uni16_decomp_keys[0]) + return FALSE; + + if (!uint16_find(uni16_decomp_keys, + N_ELEMENTS(uni16_decomp_keys), *chr, &idx)) + return FALSE; + *chr = uni16_decomp_values[idx]; + } else { + if (!uint32_find(uni32_decomp_keys, + N_ELEMENTS(uni32_decomp_keys), *chr, &idx)) + return FALSE; + *chr = uni32_decomp_values[idx]; + } + return TRUE; +} + +static void uni_ucs4_decompose_hangul_utf8(unichar_t chr, buffer_t *output) +{ +#define SBase HANGUL_FIRST +#define LBase 0x1100 +#define VBase 0x1161 +#define TBase 0x11A7 +#define LCount 19 +#define VCount 21 +#define TCount 28 +#define NCount (VCount * TCount) + unsigned int SIndex = chr - SBase; + unichar_t L = LBase + SIndex / NCount; + unichar_t V = VBase + (SIndex % NCount) / TCount; + unichar_t T = TBase + SIndex % TCount; + + uni_ucs4_to_utf8_c(L, output); + uni_ucs4_to_utf8_c(V, output); + if (T != TBase) uni_ucs4_to_utf8_c(T, output); +} + +static bool uni_ucs4_decompose_multi_utf8(unichar_t chr, buffer_t *output) +{ + const uint32_t *value; + unsigned int idx; + + if (chr < multidecomp_keys[0] || chr > 0xffff) + return FALSE; + + if (!uint32_find(multidecomp_keys, N_ELEMENTS(multidecomp_keys), + chr, &idx)) + return FALSE; + + value = &multidecomp_values[multidecomp_offsets[idx]]; + for (; *value != 0; value++) + uni_ucs4_to_utf8_c(*value, output); + return TRUE; +} + +static void output_add_replacement_char(buffer_t *output) +{ + if (output->used >= UTF8_REPLACEMENT_CHAR_LEN && + memcmp(CONST_PTR_OFFSET(output->data, + output->used - UTF8_REPLACEMENT_CHAR_LEN), + utf8_replacement_char, UTF8_REPLACEMENT_CHAR_LEN) == 0) { + /* don't add the replacement char multiple times */ + return; + } + buffer_append(output, utf8_replacement_char, UTF8_REPLACEMENT_CHAR_LEN); +} + +int uni_utf8_to_decomposed_titlecase(const void *_input, size_t size, + buffer_t *output) +{ + const unsigned char *input = _input; + unichar_t chr; + int ret = 0; + + while (size > 0) { + int bytes = uni_utf8_get_char_n(input, size, &chr); + if (bytes <= 0) { + /* invalid input. try the next byte. */ + ret = -1; + input++; size--; + output_add_replacement_char(output); + continue; + } + input += bytes; + size -= bytes; + + chr = uni_ucs4_to_titlecase(chr); + if (chr >= HANGUL_FIRST && chr <= HANGUL_LAST) + uni_ucs4_decompose_hangul_utf8(chr, output); + else if (uni_ucs4_decompose_uni(&chr) || + !uni_ucs4_decompose_multi_utf8(chr, output)) + uni_ucs4_to_utf8_c(chr, output); + } + return ret; +} + +static inline unsigned int +is_valid_utf8_seq(const unsigned char *input, unsigned int size) +{ + unichar_t chr; + int len = uni_utf8_get_char_n(input, size, &chr); + return len <= 0 ? 0 : len; +} + +static int uni_utf8_find_invalid_pos(const unsigned char *input, size_t size, + size_t *pos_r) +{ + size_t i, len; + + /* find the first invalid utf8 sequence */ + for (i = 0; i < size;) { + if (input[i] < 0x80) + i++; + else { + len = is_valid_utf8_seq(input + i, size-i); + if (unlikely(len == 0)) { + *pos_r = i; + return -1; + } + i += len; + } + } + return 0; +} + +bool uni_utf8_get_valid_data(const unsigned char *input, size_t size, + buffer_t *buf) +{ + size_t i, len; + + if (uni_utf8_find_invalid_pos(input, size, &i) == 0) + return TRUE; + + /* broken utf-8 input - skip the broken characters */ + buffer_append(buf, input, i++); + + output_add_replacement_char(buf); + while (i < size) { + if (input[i] < 0x80) { + buffer_append_c(buf, input[i++]); + continue; + } + + len = is_valid_utf8_seq(input + i, size-i); + if (len == 0) { + i++; + output_add_replacement_char(buf); + continue; + } + buffer_append(buf, input + i, len); + i += len; + } + return FALSE; +} + +bool uni_utf8_str_is_valid(const char *str) +{ + size_t i; + + return uni_utf8_find_invalid_pos((const unsigned char *)str, + strlen(str), &i) == 0; +} + +bool uni_utf8_data_is_valid(const unsigned char *data, size_t size) +{ + size_t i; + + return uni_utf8_find_invalid_pos(data, size, &i) == 0; +} + +size_t uni_utf8_data_truncate(const unsigned char *data, size_t old_size, + size_t max_new_size) +{ + if (max_new_size >= old_size) + return old_size; + if (max_new_size == 0) + return 0; + + if ((data[max_new_size] & 0x80) == 0) + return max_new_size; + while (max_new_size > 0 && (data[max_new_size-1] & 0xc0) == 0x80) + max_new_size--; + if (max_new_size > 0 && (data[max_new_size-1] & 0xc0) == 0xc0) + max_new_size--; + return max_new_size; +} diff --git a/src/lib/unichar.h b/src/lib/unichar.h new file mode 100644 index 0000000..88defcd --- /dev/null +++ b/src/lib/unichar.h @@ -0,0 +1,145 @@ +#ifndef UNICHAR_H +#define UNICHAR_H + +/* Character used to replace invalid input. */ +#define UNICODE_REPLACEMENT_CHAR 0xfffd +#define UNICODE_REPLACEMENT_CHAR_UTF8 "\xEF\xBF\xBD" +#define UNICODE_REPLACEMENT_CHAR_UTF8_LEN \ + (sizeof(UNICODE_REPLACEMENT_CHAR_UTF8) - 1); +/* Horizontal ellipsis character ('...') */ +#define UNICODE_HORIZONTAL_ELLIPSIS_CHAR 0x2026 +#define UNICODE_HORIZONTAL_ELLIPSIS_CHAR_UTF8 "\xE2\x80\xA6" +#define UNICODE_HORIZONTAL_ELLIPSIS_CHAR_UTF8_LEN \ + (sizeof(UNICODE_HORIZONTAL_ELLIPSIS_CHAR_UTF8) - 1); + +/* Characters >= base require surrogates */ +#define UTF16_SURROGATE_BASE 0x10000 + +#define UTF16_SURROGATE_SHIFT 10 +#define UTF16_SURROGATE_MASK 0x03ff +#define UTF16_SURROGATE_HIGH_FIRST 0xd800 +#define UTF16_SURROGATE_HIGH_LAST 0xdbff +#define UTF16_SURROGATE_HIGH_MAX 0xdfff +#define UTF16_SURROGATE_LOW_FIRST 0xdc00 +#define UTF16_SURROGATE_LOW_LAST 0xdfff + +#define UTF16_SURROGATE_HIGH(chr) \ + (UTF16_SURROGATE_HIGH_FIRST + \ + (((chr) - UTF16_SURROGATE_BASE) >> UTF16_SURROGATE_SHIFT)) +#define UTF16_SURROGATE_LOW(chr) \ + (UTF16_SURROGATE_LOW_FIRST + \ + (((chr) - UTF16_SURROGATE_BASE) & UTF16_SURROGATE_MASK)) + +/* Returns TRUE if given byte is ASCII character or the beginning of a + multibyte UTF-8 sequence */ +#define UTF8_IS_START_SEQ(b) \ + (((b) & 0x80) == 0 || ((b) & 0xC0) == 0xC0) + +#define UTF8_REPLACEMENT_CHAR_LEN 3 + +#define UNICHAR_T_MAX 0x10ffff + +#define UTF16_VALID_HIGH_SURROGATE(chr) (((chr) & 0xfffc00) == UTF16_SURROGATE_HIGH_FIRST) +#define UTF16_VALID_LOW_SURROGATE(chr) (((chr) & 0xfffc00) == UTF16_SURROGATE_LOW_FIRST) + +typedef uint32_t unichar_t; +ARRAY_DEFINE_TYPE(unichars, unichar_t); + +/* Normalize UTF8 input and append it to output buffer. + Returns 0 if ok, -1 if input was invalid. Even if input was invalid, + as much as possible should be added to output. */ +typedef int normalizer_func_t(const void *input, size_t size, + buffer_t *output); + +extern const unsigned char utf8_replacement_char[UTF8_REPLACEMENT_CHAR_LEN]; +extern const uint8_t *const uni_utf8_non1_bytes; + +static inline bool ATTR_PURE uni_is_valid_ucs4(unichar_t chr) +{ + return (!UTF16_VALID_HIGH_SURROGATE(chr) && + !UTF16_VALID_LOW_SURROGATE(chr) && + chr <= UNICHAR_T_MAX); +}; + +/* Returns number of characters in a NUL-terminated unicode string */ +unsigned int uni_strlen(const unichar_t *str) ATTR_PURE; +/* Translates UTF-8 input to UCS-4 output. Returns 0 if ok, -1 if input was + invalid */ +int uni_utf8_to_ucs4(const char *input, ARRAY_TYPE(unichars) *output); +int uni_utf8_to_ucs4_n(const unsigned char *input, size_t size, + ARRAY_TYPE(unichars) *output); +/* Translates UCS-4 input to UTF-8 output. */ +void uni_ucs4_to_utf8(const unichar_t *input, size_t len, buffer_t *output); +void uni_ucs4_to_utf8_c(unichar_t chr, buffer_t *output); + +/* Returns char_bytes (>0) if *chr_r is set, 0 for incomplete trailing character, + -1 for invalid input. */ +int uni_utf8_get_char(const char *input, unichar_t *chr_r); +int uni_utf8_get_char_n(const void *input, size_t max_len, unichar_t *chr_r); +/* Returns number of characters in UTF-8 string. */ +unsigned int uni_utf8_strlen(const char *input) ATTR_PURE; +/* Returns number of characters in UTF-8 input of specified size. */ +unsigned int uni_utf8_strlen_n(const void *input, size_t size) ATTR_PURE; +/* Same as uni_utf8_strlen_n(), but if input ends with a partial UTF-8 + character, don't include it in the return value and set partial_pos_r to + where the character begins. Otherwise partial_pos_r is set to the end + of the input. */ +unsigned int uni_utf8_partial_strlen_n(const void *input, size_t size, + size_t *partial_pos_r); + +/* Returns the number of bytes belonging to this UTF-8 character. The given + parameter is the first byte of the UTF-8 sequence. Invalid input is + returned with length 1. */ +static inline unsigned int ATTR_CONST +uni_utf8_char_bytes(unsigned char chr) +{ + /* 0x00 .. 0x7f are ASCII. 0x80 .. 0xC1 are invalid. */ + if (chr < (192 + 2)) + return 1; + return uni_utf8_non1_bytes[chr - (192 + 2)]; +} + +/* Return given character in titlecase. */ +unichar_t uni_ucs4_to_titlecase(unichar_t chr) ATTR_CONST; + +/* Convert UTF-8 input to titlecase and decompose the titlecase characters to + output buffer. Returns 0 if ok, -1 if input was invalid. This generates + output that's compatible with i;unicode-casemap comparator. Invalid input + is replaced with unicode replacement character (0xfffd). */ +int uni_utf8_to_decomposed_titlecase(const void *input, size_t size, + buffer_t *output); + +/* If input contains only valid UTF-8 characters, return TRUE without updating + buf. If input contains invalid UTF-8 characters, replace them with unicode + replacement character (0xfffd), write the output to buf and return FALSE. */ +bool uni_utf8_get_valid_data(const unsigned char *input, size_t size, + buffer_t *buf) ATTR_WARN_UNUSED_RESULT; +/* Returns TRUE if string is valid UTF-8 input. */ +bool uni_utf8_str_is_valid(const char *str); +/* Returns TRUE if data contains only valid UTF-8 input. */ +bool uni_utf8_data_is_valid(const unsigned char *data, size_t size); +/* Returns the size of the data when truncated to be less than or equal to + max_new_size, making sure UTF-8 character boundaries are respected. This only + looks at the last character at the new boundary. */ +size_t uni_utf8_data_truncate(const unsigned char *data, size_t old_size, + size_t max_new_size); + +/* surrogate handling */ +static inline unichar_t uni_join_surrogate(unichar_t high, unichar_t low) +{ + i_assert(UTF16_VALID_HIGH_SURROGATE(high) && + UTF16_VALID_LOW_SURROGATE(low)); + + return ((high - UTF16_SURROGATE_HIGH_FIRST)<<10) + + (low - UTF16_SURROGATE_LOW_FIRST) + + UTF16_SURROGATE_BASE; +} + +static inline void uni_split_surrogate(unichar_t chr, unichar_t *high_r, unichar_t *low_r) +{ + i_assert(chr >= UTF16_SURROGATE_BASE && chr <= UNICHAR_T_MAX); + i_assert(high_r != NULL && low_r != NULL); + *high_r = UTF16_SURROGATE_HIGH(chr); + *low_r = UTF16_SURROGATE_LOW(chr); +} +#endif diff --git a/src/lib/unicodemap.c b/src/lib/unicodemap.c new file mode 100644 index 0000000..ba39cae --- /dev/null +++ b/src/lib/unicodemap.c @@ -0,0 +1,2474 @@ +/* This file is automatically generated by unicodemap.pl from UnicodeData.txt + + NOTE: decompositions for characters having titlecase characters + are not included, because we first translate everything to titlecase */ +static const uint16_t titlecase8_map[256] = { + 0x00000, 0x00001, 0x00002, 0x00003, 0x00004, 0x00005, 0x00006, 0x00007, + 0x00008, 0x00009, 0x0000a, 0x0000b, 0x0000c, 0x0000d, 0x0000e, 0x0000f, + 0x00010, 0x00011, 0x00012, 0x00013, 0x00014, 0x00015, 0x00016, 0x00017, + 0x00018, 0x00019, 0x0001a, 0x0001b, 0x0001c, 0x0001d, 0x0001e, 0x0001f, + 0x00020, 0x00021, 0x00022, 0x00023, 0x00024, 0x00025, 0x00026, 0x00027, + 0x00028, 0x00029, 0x0002a, 0x0002b, 0x0002c, 0x0002d, 0x0002e, 0x0002f, + 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, + 0x00038, 0x00039, 0x0003a, 0x0003b, 0x0003c, 0x0003d, 0x0003e, 0x0003f, + 0x00040, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, + 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, + 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, + 0x00058, 0x00059, 0x0005a, 0x0005b, 0x0005c, 0x0005d, 0x0005e, 0x0005f, + 0x00060, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, + 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, + 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, + 0x00058, 0x00059, 0x0005a, 0x0007b, 0x0007c, 0x0007d, 0x0007e, 0x0007f, + 0x00080, 0x00081, 0x00082, 0x00083, 0x00084, 0x00085, 0x00086, 0x00087, + 0x00088, 0x00089, 0x0008a, 0x0008b, 0x0008c, 0x0008d, 0x0008e, 0x0008f, + 0x00090, 0x00091, 0x00092, 0x00093, 0x00094, 0x00095, 0x00096, 0x00097, + 0x00098, 0x00099, 0x0009a, 0x0009b, 0x0009c, 0x0009d, 0x0009e, 0x0009f, + 0x000a0, 0x000a1, 0x000a2, 0x000a3, 0x000a4, 0x000a5, 0x000a6, 0x000a7, + 0x000a8, 0x000a9, 0x000aa, 0x000ab, 0x000ac, 0x000ad, 0x000ae, 0x000af, + 0x000b0, 0x000b1, 0x000b2, 0x000b3, 0x000b4, 0x0039c, 0x000b6, 0x000b7, + 0x000b8, 0x000b9, 0x000ba, 0x000bb, 0x000bc, 0x000bd, 0x000be, 0x000bf, + 0x000c0, 0x000c1, 0x000c2, 0x000c3, 0x000c4, 0x000c5, 0x000c6, 0x000c7, + 0x000c8, 0x000c9, 0x000ca, 0x000cb, 0x000cc, 0x000cd, 0x000ce, 0x000cf, + 0x000d0, 0x000d1, 0x000d2, 0x000d3, 0x000d4, 0x000d5, 0x000d6, 0x000d7, + 0x000d8, 0x000d9, 0x000da, 0x000db, 0x000dc, 0x000dd, 0x000de, 0x000df, + 0x000c0, 0x000c1, 0x000c2, 0x000c3, 0x000c4, 0x000c5, 0x000c6, 0x000c7, + 0x000c8, 0x000c9, 0x000ca, 0x000cb, 0x000cc, 0x000cd, 0x000ce, 0x000cf, + 0x000d0, 0x000d1, 0x000d2, 0x000d3, 0x000d4, 0x000d5, 0x000d6, 0x000f7, + 0x000d8, 0x000d9, 0x000da, 0x000db, 0x000dc, 0x000dd, 0x000de, 0x00178 +}; +static const uint16_t titlecase16_keys[] = { + 0x00101, 0x00103, 0x00105, 0x00107, 0x00109, 0x0010b, 0x0010d, 0x0010f, + 0x00111, 0x00113, 0x00115, 0x00117, 0x00119, 0x0011b, 0x0011d, 0x0011f, + 0x00121, 0x00123, 0x00125, 0x00127, 0x00129, 0x0012b, 0x0012d, 0x0012f, + 0x00131, 0x00133, 0x00135, 0x00137, 0x0013a, 0x0013c, 0x0013e, 0x00140, + 0x00142, 0x00144, 0x00146, 0x00148, 0x0014b, 0x0014d, 0x0014f, 0x00151, + 0x00153, 0x00155, 0x00157, 0x00159, 0x0015b, 0x0015d, 0x0015f, 0x00161, + 0x00163, 0x00165, 0x00167, 0x00169, 0x0016b, 0x0016d, 0x0016f, 0x00171, + 0x00173, 0x00175, 0x00177, 0x0017a, 0x0017c, 0x0017e, 0x0017f, 0x00180, + 0x00183, 0x00185, 0x00188, 0x0018c, 0x00192, 0x00195, 0x00199, 0x0019a, + 0x0019e, 0x001a1, 0x001a3, 0x001a5, 0x001a8, 0x001ad, 0x001b0, 0x001b4, + 0x001b6, 0x001b9, 0x001bd, 0x001bf, 0x001c4, 0x001c6, 0x001c7, 0x001c9, + 0x001ca, 0x001cc, 0x001ce, 0x001d0, 0x001d2, 0x001d4, 0x001d6, 0x001d8, + 0x001da, 0x001dc, 0x001dd, 0x001df, 0x001e1, 0x001e3, 0x001e5, 0x001e7, + 0x001e9, 0x001eb, 0x001ed, 0x001ef, 0x001f1, 0x001f3, 0x001f5, 0x001f9, + 0x001fb, 0x001fd, 0x001ff, 0x00201, 0x00203, 0x00205, 0x00207, 0x00209, + 0x0020b, 0x0020d, 0x0020f, 0x00211, 0x00213, 0x00215, 0x00217, 0x00219, + 0x0021b, 0x0021d, 0x0021f, 0x00223, 0x00225, 0x00227, 0x00229, 0x0022b, + 0x0022d, 0x0022f, 0x00231, 0x00233, 0x0023c, 0x0023f, 0x00240, 0x00242, + 0x00247, 0x00249, 0x0024b, 0x0024d, 0x0024f, 0x00250, 0x00251, 0x00252, + 0x00253, 0x00254, 0x00256, 0x00257, 0x00259, 0x0025b, 0x0025c, 0x00260, + 0x00261, 0x00263, 0x00265, 0x00266, 0x00268, 0x00269, 0x0026a, 0x0026b, + 0x0026c, 0x0026f, 0x00271, 0x00272, 0x00275, 0x0027d, 0x00280, 0x00283, + 0x00287, 0x00288, 0x00289, 0x0028a, 0x0028b, 0x0028c, 0x00292, 0x0029d, + 0x0029e, 0x00345, 0x00371, 0x00373, 0x00377, 0x0037b, 0x0037c, 0x0037d, + 0x003ac, 0x003ad, 0x003ae, 0x003af, 0x003b1, 0x003b2, 0x003b3, 0x003b4, + 0x003b5, 0x003b6, 0x003b7, 0x003b8, 0x003b9, 0x003ba, 0x003bb, 0x003bc, + 0x003bd, 0x003be, 0x003bf, 0x003c0, 0x003c1, 0x003c2, 0x003c3, 0x003c4, + 0x003c5, 0x003c6, 0x003c7, 0x003c8, 0x003c9, 0x003ca, 0x003cb, 0x003cc, + 0x003cd, 0x003ce, 0x003d0, 0x003d1, 0x003d5, 0x003d6, 0x003d7, 0x003d9, + 0x003db, 0x003dd, 0x003df, 0x003e1, 0x003e3, 0x003e5, 0x003e7, 0x003e9, + 0x003eb, 0x003ed, 0x003ef, 0x003f0, 0x003f1, 0x003f2, 0x003f3, 0x003f5, + 0x003f8, 0x003fb, 0x00430, 0x00431, 0x00432, 0x00433, 0x00434, 0x00435, + 0x00436, 0x00437, 0x00438, 0x00439, 0x0043a, 0x0043b, 0x0043c, 0x0043d, + 0x0043e, 0x0043f, 0x00440, 0x00441, 0x00442, 0x00443, 0x00444, 0x00445, + 0x00446, 0x00447, 0x00448, 0x00449, 0x0044a, 0x0044b, 0x0044c, 0x0044d, + 0x0044e, 0x0044f, 0x00450, 0x00451, 0x00452, 0x00453, 0x00454, 0x00455, + 0x00456, 0x00457, 0x00458, 0x00459, 0x0045a, 0x0045b, 0x0045c, 0x0045d, + 0x0045e, 0x0045f, 0x00461, 0x00463, 0x00465, 0x00467, 0x00469, 0x0046b, + 0x0046d, 0x0046f, 0x00471, 0x00473, 0x00475, 0x00477, 0x00479, 0x0047b, + 0x0047d, 0x0047f, 0x00481, 0x0048b, 0x0048d, 0x0048f, 0x00491, 0x00493, + 0x00495, 0x00497, 0x00499, 0x0049b, 0x0049d, 0x0049f, 0x004a1, 0x004a3, + 0x004a5, 0x004a7, 0x004a9, 0x004ab, 0x004ad, 0x004af, 0x004b1, 0x004b3, + 0x004b5, 0x004b7, 0x004b9, 0x004bb, 0x004bd, 0x004bf, 0x004c2, 0x004c4, + 0x004c6, 0x004c8, 0x004ca, 0x004cc, 0x004ce, 0x004cf, 0x004d1, 0x004d3, + 0x004d5, 0x004d7, 0x004d9, 0x004db, 0x004dd, 0x004df, 0x004e1, 0x004e3, + 0x004e5, 0x004e7, 0x004e9, 0x004eb, 0x004ed, 0x004ef, 0x004f1, 0x004f3, + 0x004f5, 0x004f7, 0x004f9, 0x004fb, 0x004fd, 0x004ff, 0x00501, 0x00503, + 0x00505, 0x00507, 0x00509, 0x0050b, 0x0050d, 0x0050f, 0x00511, 0x00513, + 0x00515, 0x00517, 0x00519, 0x0051b, 0x0051d, 0x0051f, 0x00521, 0x00523, + 0x00525, 0x00527, 0x00529, 0x0052b, 0x0052d, 0x0052f, 0x00561, 0x00562, + 0x00563, 0x00564, 0x00565, 0x00566, 0x00567, 0x00568, 0x00569, 0x0056a, + 0x0056b, 0x0056c, 0x0056d, 0x0056e, 0x0056f, 0x00570, 0x00571, 0x00572, + 0x00573, 0x00574, 0x00575, 0x00576, 0x00577, 0x00578, 0x00579, 0x0057a, + 0x0057b, 0x0057c, 0x0057d, 0x0057e, 0x0057f, 0x00580, 0x00581, 0x00582, + 0x00583, 0x00584, 0x00585, 0x00586, 0x013f8, 0x013f9, 0x013fa, 0x013fb, + 0x013fc, 0x013fd, 0x01c80, 0x01c81, 0x01c82, 0x01c83, 0x01c84, 0x01c85, + 0x01c86, 0x01c87, 0x01c88, 0x01d79, 0x01d7d, 0x01e01, 0x01e03, 0x01e05, + 0x01e07, 0x01e09, 0x01e0b, 0x01e0d, 0x01e0f, 0x01e11, 0x01e13, 0x01e15, + 0x01e17, 0x01e19, 0x01e1b, 0x01e1d, 0x01e1f, 0x01e21, 0x01e23, 0x01e25, + 0x01e27, 0x01e29, 0x01e2b, 0x01e2d, 0x01e2f, 0x01e31, 0x01e33, 0x01e35, + 0x01e37, 0x01e39, 0x01e3b, 0x01e3d, 0x01e3f, 0x01e41, 0x01e43, 0x01e45, + 0x01e47, 0x01e49, 0x01e4b, 0x01e4d, 0x01e4f, 0x01e51, 0x01e53, 0x01e55, + 0x01e57, 0x01e59, 0x01e5b, 0x01e5d, 0x01e5f, 0x01e61, 0x01e63, 0x01e65, + 0x01e67, 0x01e69, 0x01e6b, 0x01e6d, 0x01e6f, 0x01e71, 0x01e73, 0x01e75, + 0x01e77, 0x01e79, 0x01e7b, 0x01e7d, 0x01e7f, 0x01e81, 0x01e83, 0x01e85, + 0x01e87, 0x01e89, 0x01e8b, 0x01e8d, 0x01e8f, 0x01e91, 0x01e93, 0x01e95, + 0x01e9b, 0x01ea1, 0x01ea3, 0x01ea5, 0x01ea7, 0x01ea9, 0x01eab, 0x01ead, + 0x01eaf, 0x01eb1, 0x01eb3, 0x01eb5, 0x01eb7, 0x01eb9, 0x01ebb, 0x01ebd, + 0x01ebf, 0x01ec1, 0x01ec3, 0x01ec5, 0x01ec7, 0x01ec9, 0x01ecb, 0x01ecd, + 0x01ecf, 0x01ed1, 0x01ed3, 0x01ed5, 0x01ed7, 0x01ed9, 0x01edb, 0x01edd, + 0x01edf, 0x01ee1, 0x01ee3, 0x01ee5, 0x01ee7, 0x01ee9, 0x01eeb, 0x01eed, + 0x01eef, 0x01ef1, 0x01ef3, 0x01ef5, 0x01ef7, 0x01ef9, 0x01efb, 0x01efd, + 0x01eff, 0x01f00, 0x01f01, 0x01f02, 0x01f03, 0x01f04, 0x01f05, 0x01f06, + 0x01f07, 0x01f10, 0x01f11, 0x01f12, 0x01f13, 0x01f14, 0x01f15, 0x01f20, + 0x01f21, 0x01f22, 0x01f23, 0x01f24, 0x01f25, 0x01f26, 0x01f27, 0x01f30, + 0x01f31, 0x01f32, 0x01f33, 0x01f34, 0x01f35, 0x01f36, 0x01f37, 0x01f40, + 0x01f41, 0x01f42, 0x01f43, 0x01f44, 0x01f45, 0x01f51, 0x01f53, 0x01f55, + 0x01f57, 0x01f60, 0x01f61, 0x01f62, 0x01f63, 0x01f64, 0x01f65, 0x01f66, + 0x01f67, 0x01f70, 0x01f71, 0x01f72, 0x01f73, 0x01f74, 0x01f75, 0x01f76, + 0x01f77, 0x01f78, 0x01f79, 0x01f7a, 0x01f7b, 0x01f7c, 0x01f7d, 0x01f80, + 0x01f81, 0x01f82, 0x01f83, 0x01f84, 0x01f85, 0x01f86, 0x01f87, 0x01f90, + 0x01f91, 0x01f92, 0x01f93, 0x01f94, 0x01f95, 0x01f96, 0x01f97, 0x01fa0, + 0x01fa1, 0x01fa2, 0x01fa3, 0x01fa4, 0x01fa5, 0x01fa6, 0x01fa7, 0x01fb0, + 0x01fb1, 0x01fb3, 0x01fbe, 0x01fc3, 0x01fd0, 0x01fd1, 0x01fe0, 0x01fe1, + 0x01fe5, 0x01ff3, 0x0214e, 0x02170, 0x02171, 0x02172, 0x02173, 0x02174, + 0x02175, 0x02176, 0x02177, 0x02178, 0x02179, 0x0217a, 0x0217b, 0x0217c, + 0x0217d, 0x0217e, 0x0217f, 0x02184, 0x024d0, 0x024d1, 0x024d2, 0x024d3, + 0x024d4, 0x024d5, 0x024d6, 0x024d7, 0x024d8, 0x024d9, 0x024da, 0x024db, + 0x024dc, 0x024dd, 0x024de, 0x024df, 0x024e0, 0x024e1, 0x024e2, 0x024e3, + 0x024e4, 0x024e5, 0x024e6, 0x024e7, 0x024e8, 0x024e9, 0x02c30, 0x02c31, + 0x02c32, 0x02c33, 0x02c34, 0x02c35, 0x02c36, 0x02c37, 0x02c38, 0x02c39, + 0x02c3a, 0x02c3b, 0x02c3c, 0x02c3d, 0x02c3e, 0x02c3f, 0x02c40, 0x02c41, + 0x02c42, 0x02c43, 0x02c44, 0x02c45, 0x02c46, 0x02c47, 0x02c48, 0x02c49, + 0x02c4a, 0x02c4b, 0x02c4c, 0x02c4d, 0x02c4e, 0x02c4f, 0x02c50, 0x02c51, + 0x02c52, 0x02c53, 0x02c54, 0x02c55, 0x02c56, 0x02c57, 0x02c58, 0x02c59, + 0x02c5a, 0x02c5b, 0x02c5c, 0x02c5d, 0x02c5e, 0x02c61, 0x02c65, 0x02c66, + 0x02c68, 0x02c6a, 0x02c6c, 0x02c73, 0x02c76, 0x02c81, 0x02c83, 0x02c85, + 0x02c87, 0x02c89, 0x02c8b, 0x02c8d, 0x02c8f, 0x02c91, 0x02c93, 0x02c95, + 0x02c97, 0x02c99, 0x02c9b, 0x02c9d, 0x02c9f, 0x02ca1, 0x02ca3, 0x02ca5, + 0x02ca7, 0x02ca9, 0x02cab, 0x02cad, 0x02caf, 0x02cb1, 0x02cb3, 0x02cb5, + 0x02cb7, 0x02cb9, 0x02cbb, 0x02cbd, 0x02cbf, 0x02cc1, 0x02cc3, 0x02cc5, + 0x02cc7, 0x02cc9, 0x02ccb, 0x02ccd, 0x02ccf, 0x02cd1, 0x02cd3, 0x02cd5, + 0x02cd7, 0x02cd9, 0x02cdb, 0x02cdd, 0x02cdf, 0x02ce1, 0x02ce3, 0x02cec, + 0x02cee, 0x02cf3, 0x02d00, 0x02d01, 0x02d02, 0x02d03, 0x02d04, 0x02d05, + 0x02d06, 0x02d07, 0x02d08, 0x02d09, 0x02d0a, 0x02d0b, 0x02d0c, 0x02d0d, + 0x02d0e, 0x02d0f, 0x02d10, 0x02d11, 0x02d12, 0x02d13, 0x02d14, 0x02d15, + 0x02d16, 0x02d17, 0x02d18, 0x02d19, 0x02d1a, 0x02d1b, 0x02d1c, 0x02d1d, + 0x02d1e, 0x02d1f, 0x02d20, 0x02d21, 0x02d22, 0x02d23, 0x02d24, 0x02d25, + 0x02d27, 0x02d2d, 0x0a641, 0x0a643, 0x0a645, 0x0a647, 0x0a649, 0x0a64b, + 0x0a64d, 0x0a64f, 0x0a651, 0x0a653, 0x0a655, 0x0a657, 0x0a659, 0x0a65b, + 0x0a65d, 0x0a65f, 0x0a661, 0x0a663, 0x0a665, 0x0a667, 0x0a669, 0x0a66b, + 0x0a66d, 0x0a681, 0x0a683, 0x0a685, 0x0a687, 0x0a689, 0x0a68b, 0x0a68d, + 0x0a68f, 0x0a691, 0x0a693, 0x0a695, 0x0a697, 0x0a699, 0x0a69b, 0x0a723, + 0x0a725, 0x0a727, 0x0a729, 0x0a72b, 0x0a72d, 0x0a72f, 0x0a733, 0x0a735, + 0x0a737, 0x0a739, 0x0a73b, 0x0a73d, 0x0a73f, 0x0a741, 0x0a743, 0x0a745, + 0x0a747, 0x0a749, 0x0a74b, 0x0a74d, 0x0a74f, 0x0a751, 0x0a753, 0x0a755, + 0x0a757, 0x0a759, 0x0a75b, 0x0a75d, 0x0a75f, 0x0a761, 0x0a763, 0x0a765, + 0x0a767, 0x0a769, 0x0a76b, 0x0a76d, 0x0a76f, 0x0a77a, 0x0a77c, 0x0a77f, + 0x0a781, 0x0a783, 0x0a785, 0x0a787, 0x0a78c, 0x0a791, 0x0a793, 0x0a797, + 0x0a799, 0x0a79b, 0x0a79d, 0x0a79f, 0x0a7a1, 0x0a7a3, 0x0a7a5, 0x0a7a7, + 0x0a7a9, 0x0a7b5, 0x0a7b7, 0x0ab53, 0x0ab70, 0x0ab71, 0x0ab72, 0x0ab73, + 0x0ab74, 0x0ab75, 0x0ab76, 0x0ab77, 0x0ab78, 0x0ab79, 0x0ab7a, 0x0ab7b, + 0x0ab7c, 0x0ab7d, 0x0ab7e, 0x0ab7f, 0x0ab80, 0x0ab81, 0x0ab82, 0x0ab83, + 0x0ab84, 0x0ab85, 0x0ab86, 0x0ab87, 0x0ab88, 0x0ab89, 0x0ab8a, 0x0ab8b, + 0x0ab8c, 0x0ab8d, 0x0ab8e, 0x0ab8f, 0x0ab90, 0x0ab91, 0x0ab92, 0x0ab93, + 0x0ab94, 0x0ab95, 0x0ab96, 0x0ab97, 0x0ab98, 0x0ab99, 0x0ab9a, 0x0ab9b, + 0x0ab9c, 0x0ab9d, 0x0ab9e, 0x0ab9f, 0x0aba0, 0x0aba1, 0x0aba2, 0x0aba3, + 0x0aba4, 0x0aba5, 0x0aba6, 0x0aba7, 0x0aba8, 0x0aba9, 0x0abaa, 0x0abab, + 0x0abac, 0x0abad, 0x0abae, 0x0abaf, 0x0abb0, 0x0abb1, 0x0abb2, 0x0abb3, + 0x0abb4, 0x0abb5, 0x0abb6, 0x0abb7, 0x0abb8, 0x0abb9, 0x0abba, 0x0abbb, + 0x0abbc, 0x0abbd, 0x0abbe, 0x0abbf, 0x0ff41, 0x0ff42, 0x0ff43, 0x0ff44, + 0x0ff45, 0x0ff46, 0x0ff47, 0x0ff48, 0x0ff49, 0x0ff4a, 0x0ff4b, 0x0ff4c, + 0x0ff4d, 0x0ff4e, 0x0ff4f, 0x0ff50, 0x0ff51, 0x0ff52, 0x0ff53, 0x0ff54, + 0x0ff55, 0x0ff56, 0x0ff57, 0x0ff58, 0x0ff59, 0x0ff5a +}; +static const uint16_t titlecase16_values[] = { + 0x00100, 0x00102, 0x00104, 0x00106, 0x00108, 0x0010a, 0x0010c, 0x0010e, + 0x00110, 0x00112, 0x00114, 0x00116, 0x00118, 0x0011a, 0x0011c, 0x0011e, + 0x00120, 0x00122, 0x00124, 0x00126, 0x00128, 0x0012a, 0x0012c, 0x0012e, + 0x00049, 0x00132, 0x00134, 0x00136, 0x00139, 0x0013b, 0x0013d, 0x0013f, + 0x00141, 0x00143, 0x00145, 0x00147, 0x0014a, 0x0014c, 0x0014e, 0x00150, + 0x00152, 0x00154, 0x00156, 0x00158, 0x0015a, 0x0015c, 0x0015e, 0x00160, + 0x00162, 0x00164, 0x00166, 0x00168, 0x0016a, 0x0016c, 0x0016e, 0x00170, + 0x00172, 0x00174, 0x00176, 0x00179, 0x0017b, 0x0017d, 0x00053, 0x00243, + 0x00182, 0x00184, 0x00187, 0x0018b, 0x00191, 0x001f6, 0x00198, 0x0023d, + 0x00220, 0x001a0, 0x001a2, 0x001a4, 0x001a7, 0x001ac, 0x001af, 0x001b3, + 0x001b5, 0x001b8, 0x001bc, 0x001f7, 0x001c5, 0x001c5, 0x001c8, 0x001c8, + 0x001cb, 0x001cb, 0x001cd, 0x001cf, 0x001d1, 0x001d3, 0x001d5, 0x001d7, + 0x001d9, 0x001db, 0x0018e, 0x001de, 0x001e0, 0x001e2, 0x001e4, 0x001e6, + 0x001e8, 0x001ea, 0x001ec, 0x001ee, 0x001f2, 0x001f2, 0x001f4, 0x001f8, + 0x001fa, 0x001fc, 0x001fe, 0x00200, 0x00202, 0x00204, 0x00206, 0x00208, + 0x0020a, 0x0020c, 0x0020e, 0x00210, 0x00212, 0x00214, 0x00216, 0x00218, + 0x0021a, 0x0021c, 0x0021e, 0x00222, 0x00224, 0x00226, 0x00228, 0x0022a, + 0x0022c, 0x0022e, 0x00230, 0x00232, 0x0023b, 0x02c7e, 0x02c7f, 0x00241, + 0x00246, 0x00248, 0x0024a, 0x0024c, 0x0024e, 0x02c6f, 0x02c6d, 0x02c70, + 0x00181, 0x00186, 0x00189, 0x0018a, 0x0018f, 0x00190, 0x0a7ab, 0x00193, + 0x0a7ac, 0x00194, 0x0a78d, 0x0a7aa, 0x00197, 0x00196, 0x0a7ae, 0x02c62, + 0x0a7ad, 0x0019c, 0x02c6e, 0x0019d, 0x0019f, 0x02c64, 0x001a6, 0x001a9, + 0x0a7b1, 0x001ae, 0x00244, 0x001b1, 0x001b2, 0x00245, 0x001b7, 0x0a7b2, + 0x0a7b0, 0x00399, 0x00370, 0x00372, 0x00376, 0x003fd, 0x003fe, 0x003ff, + 0x00386, 0x00388, 0x00389, 0x0038a, 0x00391, 0x00392, 0x00393, 0x00394, + 0x00395, 0x00396, 0x00397, 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, + 0x0039d, 0x0039e, 0x0039f, 0x003a0, 0x003a1, 0x003a3, 0x003a3, 0x003a4, + 0x003a5, 0x003a6, 0x003a7, 0x003a8, 0x003a9, 0x003aa, 0x003ab, 0x0038c, + 0x0038e, 0x0038f, 0x00392, 0x00398, 0x003a6, 0x003a0, 0x003cf, 0x003d8, + 0x003da, 0x003dc, 0x003de, 0x003e0, 0x003e2, 0x003e4, 0x003e6, 0x003e8, + 0x003ea, 0x003ec, 0x003ee, 0x0039a, 0x003a1, 0x003f9, 0x0037f, 0x00395, + 0x003f7, 0x003fa, 0x00410, 0x00411, 0x00412, 0x00413, 0x00414, 0x00415, + 0x00416, 0x00417, 0x00418, 0x00419, 0x0041a, 0x0041b, 0x0041c, 0x0041d, + 0x0041e, 0x0041f, 0x00420, 0x00421, 0x00422, 0x00423, 0x00424, 0x00425, + 0x00426, 0x00427, 0x00428, 0x00429, 0x0042a, 0x0042b, 0x0042c, 0x0042d, + 0x0042e, 0x0042f, 0x00400, 0x00401, 0x00402, 0x00403, 0x00404, 0x00405, + 0x00406, 0x00407, 0x00408, 0x00409, 0x0040a, 0x0040b, 0x0040c, 0x0040d, + 0x0040e, 0x0040f, 0x00460, 0x00462, 0x00464, 0x00466, 0x00468, 0x0046a, + 0x0046c, 0x0046e, 0x00470, 0x00472, 0x00474, 0x00476, 0x00478, 0x0047a, + 0x0047c, 0x0047e, 0x00480, 0x0048a, 0x0048c, 0x0048e, 0x00490, 0x00492, + 0x00494, 0x00496, 0x00498, 0x0049a, 0x0049c, 0x0049e, 0x004a0, 0x004a2, + 0x004a4, 0x004a6, 0x004a8, 0x004aa, 0x004ac, 0x004ae, 0x004b0, 0x004b2, + 0x004b4, 0x004b6, 0x004b8, 0x004ba, 0x004bc, 0x004be, 0x004c1, 0x004c3, + 0x004c5, 0x004c7, 0x004c9, 0x004cb, 0x004cd, 0x004c0, 0x004d0, 0x004d2, + 0x004d4, 0x004d6, 0x004d8, 0x004da, 0x004dc, 0x004de, 0x004e0, 0x004e2, + 0x004e4, 0x004e6, 0x004e8, 0x004ea, 0x004ec, 0x004ee, 0x004f0, 0x004f2, + 0x004f4, 0x004f6, 0x004f8, 0x004fa, 0x004fc, 0x004fe, 0x00500, 0x00502, + 0x00504, 0x00506, 0x00508, 0x0050a, 0x0050c, 0x0050e, 0x00510, 0x00512, + 0x00514, 0x00516, 0x00518, 0x0051a, 0x0051c, 0x0051e, 0x00520, 0x00522, + 0x00524, 0x00526, 0x00528, 0x0052a, 0x0052c, 0x0052e, 0x00531, 0x00532, + 0x00533, 0x00534, 0x00535, 0x00536, 0x00537, 0x00538, 0x00539, 0x0053a, + 0x0053b, 0x0053c, 0x0053d, 0x0053e, 0x0053f, 0x00540, 0x00541, 0x00542, + 0x00543, 0x00544, 0x00545, 0x00546, 0x00547, 0x00548, 0x00549, 0x0054a, + 0x0054b, 0x0054c, 0x0054d, 0x0054e, 0x0054f, 0x00550, 0x00551, 0x00552, + 0x00553, 0x00554, 0x00555, 0x00556, 0x013f0, 0x013f1, 0x013f2, 0x013f3, + 0x013f4, 0x013f5, 0x00412, 0x00414, 0x0041e, 0x00421, 0x00422, 0x00422, + 0x0042a, 0x00462, 0x0a64a, 0x0a77d, 0x02c63, 0x01e00, 0x01e02, 0x01e04, + 0x01e06, 0x01e08, 0x01e0a, 0x01e0c, 0x01e0e, 0x01e10, 0x01e12, 0x01e14, + 0x01e16, 0x01e18, 0x01e1a, 0x01e1c, 0x01e1e, 0x01e20, 0x01e22, 0x01e24, + 0x01e26, 0x01e28, 0x01e2a, 0x01e2c, 0x01e2e, 0x01e30, 0x01e32, 0x01e34, + 0x01e36, 0x01e38, 0x01e3a, 0x01e3c, 0x01e3e, 0x01e40, 0x01e42, 0x01e44, + 0x01e46, 0x01e48, 0x01e4a, 0x01e4c, 0x01e4e, 0x01e50, 0x01e52, 0x01e54, + 0x01e56, 0x01e58, 0x01e5a, 0x01e5c, 0x01e5e, 0x01e60, 0x01e62, 0x01e64, + 0x01e66, 0x01e68, 0x01e6a, 0x01e6c, 0x01e6e, 0x01e70, 0x01e72, 0x01e74, + 0x01e76, 0x01e78, 0x01e7a, 0x01e7c, 0x01e7e, 0x01e80, 0x01e82, 0x01e84, + 0x01e86, 0x01e88, 0x01e8a, 0x01e8c, 0x01e8e, 0x01e90, 0x01e92, 0x01e94, + 0x01e60, 0x01ea0, 0x01ea2, 0x01ea4, 0x01ea6, 0x01ea8, 0x01eaa, 0x01eac, + 0x01eae, 0x01eb0, 0x01eb2, 0x01eb4, 0x01eb6, 0x01eb8, 0x01eba, 0x01ebc, + 0x01ebe, 0x01ec0, 0x01ec2, 0x01ec4, 0x01ec6, 0x01ec8, 0x01eca, 0x01ecc, + 0x01ece, 0x01ed0, 0x01ed2, 0x01ed4, 0x01ed6, 0x01ed8, 0x01eda, 0x01edc, + 0x01ede, 0x01ee0, 0x01ee2, 0x01ee4, 0x01ee6, 0x01ee8, 0x01eea, 0x01eec, + 0x01eee, 0x01ef0, 0x01ef2, 0x01ef4, 0x01ef6, 0x01ef8, 0x01efa, 0x01efc, + 0x01efe, 0x01f08, 0x01f09, 0x01f0a, 0x01f0b, 0x01f0c, 0x01f0d, 0x01f0e, + 0x01f0f, 0x01f18, 0x01f19, 0x01f1a, 0x01f1b, 0x01f1c, 0x01f1d, 0x01f28, + 0x01f29, 0x01f2a, 0x01f2b, 0x01f2c, 0x01f2d, 0x01f2e, 0x01f2f, 0x01f38, + 0x01f39, 0x01f3a, 0x01f3b, 0x01f3c, 0x01f3d, 0x01f3e, 0x01f3f, 0x01f48, + 0x01f49, 0x01f4a, 0x01f4b, 0x01f4c, 0x01f4d, 0x01f59, 0x01f5b, 0x01f5d, + 0x01f5f, 0x01f68, 0x01f69, 0x01f6a, 0x01f6b, 0x01f6c, 0x01f6d, 0x01f6e, + 0x01f6f, 0x01fba, 0x01fbb, 0x01fc8, 0x01fc9, 0x01fca, 0x01fcb, 0x01fda, + 0x01fdb, 0x01ff8, 0x01ff9, 0x01fea, 0x01feb, 0x01ffa, 0x01ffb, 0x01f88, + 0x01f89, 0x01f8a, 0x01f8b, 0x01f8c, 0x01f8d, 0x01f8e, 0x01f8f, 0x01f98, + 0x01f99, 0x01f9a, 0x01f9b, 0x01f9c, 0x01f9d, 0x01f9e, 0x01f9f, 0x01fa8, + 0x01fa9, 0x01faa, 0x01fab, 0x01fac, 0x01fad, 0x01fae, 0x01faf, 0x01fb8, + 0x01fb9, 0x01fbc, 0x00399, 0x01fcc, 0x01fd8, 0x01fd9, 0x01fe8, 0x01fe9, + 0x01fec, 0x01ffc, 0x02132, 0x02160, 0x02161, 0x02162, 0x02163, 0x02164, + 0x02165, 0x02166, 0x02167, 0x02168, 0x02169, 0x0216a, 0x0216b, 0x0216c, + 0x0216d, 0x0216e, 0x0216f, 0x02183, 0x024b6, 0x024b7, 0x024b8, 0x024b9, + 0x024ba, 0x024bb, 0x024bc, 0x024bd, 0x024be, 0x024bf, 0x024c0, 0x024c1, + 0x024c2, 0x024c3, 0x024c4, 0x024c5, 0x024c6, 0x024c7, 0x024c8, 0x024c9, + 0x024ca, 0x024cb, 0x024cc, 0x024cd, 0x024ce, 0x024cf, 0x02c00, 0x02c01, + 0x02c02, 0x02c03, 0x02c04, 0x02c05, 0x02c06, 0x02c07, 0x02c08, 0x02c09, + 0x02c0a, 0x02c0b, 0x02c0c, 0x02c0d, 0x02c0e, 0x02c0f, 0x02c10, 0x02c11, + 0x02c12, 0x02c13, 0x02c14, 0x02c15, 0x02c16, 0x02c17, 0x02c18, 0x02c19, + 0x02c1a, 0x02c1b, 0x02c1c, 0x02c1d, 0x02c1e, 0x02c1f, 0x02c20, 0x02c21, + 0x02c22, 0x02c23, 0x02c24, 0x02c25, 0x02c26, 0x02c27, 0x02c28, 0x02c29, + 0x02c2a, 0x02c2b, 0x02c2c, 0x02c2d, 0x02c2e, 0x02c60, 0x0023a, 0x0023e, + 0x02c67, 0x02c69, 0x02c6b, 0x02c72, 0x02c75, 0x02c80, 0x02c82, 0x02c84, + 0x02c86, 0x02c88, 0x02c8a, 0x02c8c, 0x02c8e, 0x02c90, 0x02c92, 0x02c94, + 0x02c96, 0x02c98, 0x02c9a, 0x02c9c, 0x02c9e, 0x02ca0, 0x02ca2, 0x02ca4, + 0x02ca6, 0x02ca8, 0x02caa, 0x02cac, 0x02cae, 0x02cb0, 0x02cb2, 0x02cb4, + 0x02cb6, 0x02cb8, 0x02cba, 0x02cbc, 0x02cbe, 0x02cc0, 0x02cc2, 0x02cc4, + 0x02cc6, 0x02cc8, 0x02cca, 0x02ccc, 0x02cce, 0x02cd0, 0x02cd2, 0x02cd4, + 0x02cd6, 0x02cd8, 0x02cda, 0x02cdc, 0x02cde, 0x02ce0, 0x02ce2, 0x02ceb, + 0x02ced, 0x02cf2, 0x010a0, 0x010a1, 0x010a2, 0x010a3, 0x010a4, 0x010a5, + 0x010a6, 0x010a7, 0x010a8, 0x010a9, 0x010aa, 0x010ab, 0x010ac, 0x010ad, + 0x010ae, 0x010af, 0x010b0, 0x010b1, 0x010b2, 0x010b3, 0x010b4, 0x010b5, + 0x010b6, 0x010b7, 0x010b8, 0x010b9, 0x010ba, 0x010bb, 0x010bc, 0x010bd, + 0x010be, 0x010bf, 0x010c0, 0x010c1, 0x010c2, 0x010c3, 0x010c4, 0x010c5, + 0x010c7, 0x010cd, 0x0a640, 0x0a642, 0x0a644, 0x0a646, 0x0a648, 0x0a64a, + 0x0a64c, 0x0a64e, 0x0a650, 0x0a652, 0x0a654, 0x0a656, 0x0a658, 0x0a65a, + 0x0a65c, 0x0a65e, 0x0a660, 0x0a662, 0x0a664, 0x0a666, 0x0a668, 0x0a66a, + 0x0a66c, 0x0a680, 0x0a682, 0x0a684, 0x0a686, 0x0a688, 0x0a68a, 0x0a68c, + 0x0a68e, 0x0a690, 0x0a692, 0x0a694, 0x0a696, 0x0a698, 0x0a69a, 0x0a722, + 0x0a724, 0x0a726, 0x0a728, 0x0a72a, 0x0a72c, 0x0a72e, 0x0a732, 0x0a734, + 0x0a736, 0x0a738, 0x0a73a, 0x0a73c, 0x0a73e, 0x0a740, 0x0a742, 0x0a744, + 0x0a746, 0x0a748, 0x0a74a, 0x0a74c, 0x0a74e, 0x0a750, 0x0a752, 0x0a754, + 0x0a756, 0x0a758, 0x0a75a, 0x0a75c, 0x0a75e, 0x0a760, 0x0a762, 0x0a764, + 0x0a766, 0x0a768, 0x0a76a, 0x0a76c, 0x0a76e, 0x0a779, 0x0a77b, 0x0a77e, + 0x0a780, 0x0a782, 0x0a784, 0x0a786, 0x0a78b, 0x0a790, 0x0a792, 0x0a796, + 0x0a798, 0x0a79a, 0x0a79c, 0x0a79e, 0x0a7a0, 0x0a7a2, 0x0a7a4, 0x0a7a6, + 0x0a7a8, 0x0a7b4, 0x0a7b6, 0x0a7b3, 0x013a0, 0x013a1, 0x013a2, 0x013a3, + 0x013a4, 0x013a5, 0x013a6, 0x013a7, 0x013a8, 0x013a9, 0x013aa, 0x013ab, + 0x013ac, 0x013ad, 0x013ae, 0x013af, 0x013b0, 0x013b1, 0x013b2, 0x013b3, + 0x013b4, 0x013b5, 0x013b6, 0x013b7, 0x013b8, 0x013b9, 0x013ba, 0x013bb, + 0x013bc, 0x013bd, 0x013be, 0x013bf, 0x013c0, 0x013c1, 0x013c2, 0x013c3, + 0x013c4, 0x013c5, 0x013c6, 0x013c7, 0x013c8, 0x013c9, 0x013ca, 0x013cb, + 0x013cc, 0x013cd, 0x013ce, 0x013cf, 0x013d0, 0x013d1, 0x013d2, 0x013d3, + 0x013d4, 0x013d5, 0x013d6, 0x013d7, 0x013d8, 0x013d9, 0x013da, 0x013db, + 0x013dc, 0x013dd, 0x013de, 0x013df, 0x013e0, 0x013e1, 0x013e2, 0x013e3, + 0x013e4, 0x013e5, 0x013e6, 0x013e7, 0x013e8, 0x013e9, 0x013ea, 0x013eb, + 0x013ec, 0x013ed, 0x013ee, 0x013ef, 0x0ff21, 0x0ff22, 0x0ff23, 0x0ff24, + 0x0ff25, 0x0ff26, 0x0ff27, 0x0ff28, 0x0ff29, 0x0ff2a, 0x0ff2b, 0x0ff2c, + 0x0ff2d, 0x0ff2e, 0x0ff2f, 0x0ff30, 0x0ff31, 0x0ff32, 0x0ff33, 0x0ff34, + 0x0ff35, 0x0ff36, 0x0ff37, 0x0ff38, 0x0ff39, 0x0ff3a +}; +static const uint32_t titlecase32_keys[] = { + 0x10428, 0x10429, 0x1042a, 0x1042b, 0x1042c, 0x1042d, 0x1042e, 0x1042f, + 0x10430, 0x10431, 0x10432, 0x10433, 0x10434, 0x10435, 0x10436, 0x10437, + 0x10438, 0x10439, 0x1043a, 0x1043b, 0x1043c, 0x1043d, 0x1043e, 0x1043f, + 0x10440, 0x10441, 0x10442, 0x10443, 0x10444, 0x10445, 0x10446, 0x10447, + 0x10448, 0x10449, 0x1044a, 0x1044b, 0x1044c, 0x1044d, 0x1044e, 0x1044f, + 0x104d8, 0x104d9, 0x104da, 0x104db, 0x104dc, 0x104dd, 0x104de, 0x104df, + 0x104e0, 0x104e1, 0x104e2, 0x104e3, 0x104e4, 0x104e5, 0x104e6, 0x104e7, + 0x104e8, 0x104e9, 0x104ea, 0x104eb, 0x104ec, 0x104ed, 0x104ee, 0x104ef, + 0x104f0, 0x104f1, 0x104f2, 0x104f3, 0x104f4, 0x104f5, 0x104f6, 0x104f7, + 0x104f8, 0x104f9, 0x104fa, 0x104fb, 0x10cc0, 0x10cc1, 0x10cc2, 0x10cc3, + 0x10cc4, 0x10cc5, 0x10cc6, 0x10cc7, 0x10cc8, 0x10cc9, 0x10cca, 0x10ccb, + 0x10ccc, 0x10ccd, 0x10cce, 0x10ccf, 0x10cd0, 0x10cd1, 0x10cd2, 0x10cd3, + 0x10cd4, 0x10cd5, 0x10cd6, 0x10cd7, 0x10cd8, 0x10cd9, 0x10cda, 0x10cdb, + 0x10cdc, 0x10cdd, 0x10cde, 0x10cdf, 0x10ce0, 0x10ce1, 0x10ce2, 0x10ce3, + 0x10ce4, 0x10ce5, 0x10ce6, 0x10ce7, 0x10ce8, 0x10ce9, 0x10cea, 0x10ceb, + 0x10cec, 0x10ced, 0x10cee, 0x10cef, 0x10cf0, 0x10cf1, 0x10cf2, 0x118c0, + 0x118c1, 0x118c2, 0x118c3, 0x118c4, 0x118c5, 0x118c6, 0x118c7, 0x118c8, + 0x118c9, 0x118ca, 0x118cb, 0x118cc, 0x118cd, 0x118ce, 0x118cf, 0x118d0, + 0x118d1, 0x118d2, 0x118d3, 0x118d4, 0x118d5, 0x118d6, 0x118d7, 0x118d8, + 0x118d9, 0x118da, 0x118db, 0x118dc, 0x118dd, 0x118de, 0x118df, 0x1e922, + 0x1e923, 0x1e924, 0x1e925, 0x1e926, 0x1e927, 0x1e928, 0x1e929, 0x1e92a, + 0x1e92b, 0x1e92c, 0x1e92d, 0x1e92e, 0x1e92f, 0x1e930, 0x1e931, 0x1e932, + 0x1e933, 0x1e934, 0x1e935, 0x1e936, 0x1e937, 0x1e938, 0x1e939, 0x1e93a, + 0x1e93b, 0x1e93c, 0x1e93d, 0x1e93e, 0x1e93f, 0x1e940, 0x1e941, 0x1e942, + 0x1e943 +}; +static const uint32_t titlecase32_values[] = { + 0x10400, 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, + 0x10408, 0x10409, 0x1040a, 0x1040b, 0x1040c, 0x1040d, 0x1040e, 0x1040f, + 0x10410, 0x10411, 0x10412, 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, + 0x10418, 0x10419, 0x1041a, 0x1041b, 0x1041c, 0x1041d, 0x1041e, 0x1041f, + 0x10420, 0x10421, 0x10422, 0x10423, 0x10424, 0x10425, 0x10426, 0x10427, + 0x104b0, 0x104b1, 0x104b2, 0x104b3, 0x104b4, 0x104b5, 0x104b6, 0x104b7, + 0x104b8, 0x104b9, 0x104ba, 0x104bb, 0x104bc, 0x104bd, 0x104be, 0x104bf, + 0x104c0, 0x104c1, 0x104c2, 0x104c3, 0x104c4, 0x104c5, 0x104c6, 0x104c7, + 0x104c8, 0x104c9, 0x104ca, 0x104cb, 0x104cc, 0x104cd, 0x104ce, 0x104cf, + 0x104d0, 0x104d1, 0x104d2, 0x104d3, 0x10c80, 0x10c81, 0x10c82, 0x10c83, + 0x10c84, 0x10c85, 0x10c86, 0x10c87, 0x10c88, 0x10c89, 0x10c8a, 0x10c8b, + 0x10c8c, 0x10c8d, 0x10c8e, 0x10c8f, 0x10c90, 0x10c91, 0x10c92, 0x10c93, + 0x10c94, 0x10c95, 0x10c96, 0x10c97, 0x10c98, 0x10c99, 0x10c9a, 0x10c9b, + 0x10c9c, 0x10c9d, 0x10c9e, 0x10c9f, 0x10ca0, 0x10ca1, 0x10ca2, 0x10ca3, + 0x10ca4, 0x10ca5, 0x10ca6, 0x10ca7, 0x10ca8, 0x10ca9, 0x10caa, 0x10cab, + 0x10cac, 0x10cad, 0x10cae, 0x10caf, 0x10cb0, 0x10cb1, 0x10cb2, 0x118a0, + 0x118a1, 0x118a2, 0x118a3, 0x118a4, 0x118a5, 0x118a6, 0x118a7, 0x118a8, + 0x118a9, 0x118aa, 0x118ab, 0x118ac, 0x118ad, 0x118ae, 0x118af, 0x118b0, + 0x118b1, 0x118b2, 0x118b3, 0x118b4, 0x118b5, 0x118b6, 0x118b7, 0x118b8, + 0x118b9, 0x118ba, 0x118bb, 0x118bc, 0x118bd, 0x118be, 0x118bf, 0x1e900, + 0x1e901, 0x1e902, 0x1e903, 0x1e904, 0x1e905, 0x1e906, 0x1e907, 0x1e908, + 0x1e909, 0x1e90a, 0x1e90b, 0x1e90c, 0x1e90d, 0x1e90e, 0x1e90f, 0x1e910, + 0x1e911, 0x1e912, 0x1e913, 0x1e914, 0x1e915, 0x1e916, 0x1e917, 0x1e918, + 0x1e919, 0x1e91a, 0x1e91b, 0x1e91c, 0x1e91d, 0x1e91e, 0x1e91f, 0x1e920, + 0x1e921 +}; +static const uint16_t uni8_decomp_map[256] = { + 0x00000, 0x00001, 0x00002, 0x00003, 0x00004, 0x00005, 0x00006, 0x00007, + 0x00008, 0x00009, 0x0000a, 0x0000b, 0x0000c, 0x0000d, 0x0000e, 0x0000f, + 0x00010, 0x00011, 0x00012, 0x00013, 0x00014, 0x00015, 0x00016, 0x00017, + 0x00018, 0x00019, 0x0001a, 0x0001b, 0x0001c, 0x0001d, 0x0001e, 0x0001f, + 0x00020, 0x00021, 0x00022, 0x00023, 0x00024, 0x00025, 0x00026, 0x00027, + 0x00028, 0x00029, 0x0002a, 0x0002b, 0x0002c, 0x0002d, 0x0002e, 0x0002f, + 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, + 0x00038, 0x00039, 0x0003a, 0x0003b, 0x0003c, 0x0003d, 0x0003e, 0x0003f, + 0x00040, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, + 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, + 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, + 0x00058, 0x00059, 0x0005a, 0x0005b, 0x0005c, 0x0005d, 0x0005e, 0x0005f, + 0x00060, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, + 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, + 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, + 0x00078, 0x00079, 0x0007a, 0x0007b, 0x0007c, 0x0007d, 0x0007e, 0x0007f, + 0x00080, 0x00081, 0x00082, 0x00083, 0x00084, 0x00085, 0x00086, 0x00087, + 0x00088, 0x00089, 0x0008a, 0x0008b, 0x0008c, 0x0008d, 0x0008e, 0x0008f, + 0x00090, 0x00091, 0x00092, 0x00093, 0x00094, 0x00095, 0x00096, 0x00097, + 0x00098, 0x00099, 0x0009a, 0x0009b, 0x0009c, 0x0009d, 0x0009e, 0x0009f, + 0x00020, 0x000a1, 0x000a2, 0x000a3, 0x000a4, 0x000a5, 0x000a6, 0x000a7, + 0x000a8, 0x000a9, 0x00061, 0x000ab, 0x000ac, 0x000ad, 0x000ae, 0x000af, + 0x000b0, 0x000b1, 0x00032, 0x00033, 0x000b4, 0x000b5, 0x000b6, 0x000b7, + 0x000b8, 0x00031, 0x0006f, 0x000bb, 0x000bc, 0x000bd, 0x000be, 0x000bf, + 0x000c0, 0x000c1, 0x000c2, 0x000c3, 0x000c4, 0x000c5, 0x000c6, 0x000c7, + 0x000c8, 0x000c9, 0x000ca, 0x000cb, 0x000cc, 0x000cd, 0x000ce, 0x000cf, + 0x000d0, 0x000d1, 0x000d2, 0x000d3, 0x000d4, 0x000d5, 0x000d6, 0x000d7, + 0x000d8, 0x000d9, 0x000da, 0x000db, 0x000dc, 0x000dd, 0x000de, 0x000df, + 0x000e0, 0x000e1, 0x000e2, 0x000e3, 0x000e4, 0x000e5, 0x000e6, 0x000e7, + 0x000e8, 0x000e9, 0x000ea, 0x000eb, 0x000ec, 0x000ed, 0x000ee, 0x000ef, + 0x000f0, 0x000f1, 0x000f2, 0x000f3, 0x000f4, 0x000f5, 0x000f6, 0x000f7, + 0x000f8, 0x000f9, 0x000fa, 0x000fb, 0x000fc, 0x000fd, 0x000fe, 0x000ff +}; +static const uint16_t uni16_decomp_keys[] = { + 0x002b0, 0x002b1, 0x002b2, 0x002b3, 0x002b4, 0x002b5, 0x002b6, 0x002b7, + 0x002b8, 0x002e0, 0x002e1, 0x002e2, 0x002e3, 0x002e4, 0x00340, 0x00341, + 0x00343, 0x00374, 0x0037e, 0x00387, 0x003d2, 0x003f4, 0x003f9, 0x00f0c, + 0x010fc, 0x01d2c, 0x01d2d, 0x01d2e, 0x01d30, 0x01d31, 0x01d32, 0x01d33, + 0x01d34, 0x01d35, 0x01d36, 0x01d37, 0x01d38, 0x01d39, 0x01d3a, 0x01d3c, + 0x01d3d, 0x01d3e, 0x01d3f, 0x01d40, 0x01d41, 0x01d42, 0x01d43, 0x01d44, + 0x01d45, 0x01d46, 0x01d47, 0x01d48, 0x01d49, 0x01d4a, 0x01d4b, 0x01d4c, + 0x01d4d, 0x01d4f, 0x01d50, 0x01d51, 0x01d52, 0x01d53, 0x01d54, 0x01d55, + 0x01d56, 0x01d57, 0x01d58, 0x01d59, 0x01d5a, 0x01d5b, 0x01d5c, 0x01d5d, + 0x01d5e, 0x01d5f, 0x01d60, 0x01d61, 0x01d62, 0x01d63, 0x01d64, 0x01d65, + 0x01d66, 0x01d67, 0x01d68, 0x01d69, 0x01d6a, 0x01d78, 0x01d9b, 0x01d9c, + 0x01d9d, 0x01d9e, 0x01d9f, 0x01da0, 0x01da1, 0x01da2, 0x01da3, 0x01da4, + 0x01da5, 0x01da6, 0x01da7, 0x01da8, 0x01da9, 0x01daa, 0x01dab, 0x01dac, + 0x01dad, 0x01dae, 0x01daf, 0x01db0, 0x01db1, 0x01db2, 0x01db3, 0x01db4, + 0x01db5, 0x01db6, 0x01db7, 0x01db8, 0x01db9, 0x01dba, 0x01dbb, 0x01dbc, + 0x01dbd, 0x01dbe, 0x01dbf, 0x01fbb, 0x01fc9, 0x01fcb, 0x01fd3, 0x01fdb, + 0x01fe3, 0x01feb, 0x01fee, 0x01fef, 0x01ff9, 0x01ffb, 0x01ffd, 0x02000, + 0x02001, 0x02002, 0x02003, 0x02004, 0x02005, 0x02006, 0x02007, 0x02008, + 0x02009, 0x0200a, 0x02011, 0x02024, 0x0202f, 0x0205f, 0x02070, 0x02071, + 0x02074, 0x02075, 0x02076, 0x02077, 0x02078, 0x02079, 0x0207a, 0x0207b, + 0x0207c, 0x0207d, 0x0207e, 0x0207f, 0x02080, 0x02081, 0x02082, 0x02083, + 0x02084, 0x02085, 0x02086, 0x02087, 0x02088, 0x02089, 0x0208a, 0x0208b, + 0x0208c, 0x0208d, 0x0208e, 0x02090, 0x02091, 0x02092, 0x02093, 0x02094, + 0x02095, 0x02096, 0x02097, 0x02098, 0x02099, 0x0209a, 0x0209b, 0x0209c, + 0x02102, 0x02107, 0x0210a, 0x0210b, 0x0210c, 0x0210d, 0x0210e, 0x0210f, + 0x02110, 0x02111, 0x02112, 0x02113, 0x02115, 0x02119, 0x0211a, 0x0211b, + 0x0211c, 0x0211d, 0x02124, 0x02126, 0x02128, 0x0212a, 0x0212b, 0x0212c, + 0x0212d, 0x0212f, 0x02130, 0x02131, 0x02133, 0x02134, 0x02135, 0x02136, + 0x02137, 0x02138, 0x02139, 0x0213c, 0x0213d, 0x0213e, 0x0213f, 0x02140, + 0x02145, 0x02146, 0x02147, 0x02148, 0x02149, 0x02160, 0x02164, 0x02169, + 0x0216c, 0x0216d, 0x0216e, 0x0216f, 0x02329, 0x0232a, 0x02460, 0x02461, + 0x02462, 0x02463, 0x02464, 0x02465, 0x02466, 0x02467, 0x02468, 0x024b6, + 0x024b7, 0x024b8, 0x024b9, 0x024ba, 0x024bb, 0x024bc, 0x024bd, 0x024be, + 0x024bf, 0x024c0, 0x024c1, 0x024c2, 0x024c3, 0x024c4, 0x024c5, 0x024c6, + 0x024c7, 0x024c8, 0x024c9, 0x024ca, 0x024cb, 0x024cc, 0x024cd, 0x024ce, + 0x024cf, 0x024ea, 0x02c7c, 0x02c7d, 0x02d6f, 0x02e9f, 0x02ef3, 0x02f00, + 0x02f01, 0x02f02, 0x02f03, 0x02f04, 0x02f05, 0x02f06, 0x02f07, 0x02f08, + 0x02f09, 0x02f0a, 0x02f0b, 0x02f0c, 0x02f0d, 0x02f0e, 0x02f0f, 0x02f10, + 0x02f11, 0x02f12, 0x02f13, 0x02f14, 0x02f15, 0x02f16, 0x02f17, 0x02f18, + 0x02f19, 0x02f1a, 0x02f1b, 0x02f1c, 0x02f1d, 0x02f1e, 0x02f1f, 0x02f20, + 0x02f21, 0x02f22, 0x02f23, 0x02f24, 0x02f25, 0x02f26, 0x02f27, 0x02f28, + 0x02f29, 0x02f2a, 0x02f2b, 0x02f2c, 0x02f2d, 0x02f2e, 0x02f2f, 0x02f30, + 0x02f31, 0x02f32, 0x02f33, 0x02f34, 0x02f35, 0x02f36, 0x02f37, 0x02f38, + 0x02f39, 0x02f3a, 0x02f3b, 0x02f3c, 0x02f3d, 0x02f3e, 0x02f3f, 0x02f40, + 0x02f41, 0x02f42, 0x02f43, 0x02f44, 0x02f45, 0x02f46, 0x02f47, 0x02f48, + 0x02f49, 0x02f4a, 0x02f4b, 0x02f4c, 0x02f4d, 0x02f4e, 0x02f4f, 0x02f50, + 0x02f51, 0x02f52, 0x02f53, 0x02f54, 0x02f55, 0x02f56, 0x02f57, 0x02f58, + 0x02f59, 0x02f5a, 0x02f5b, 0x02f5c, 0x02f5d, 0x02f5e, 0x02f5f, 0x02f60, + 0x02f61, 0x02f62, 0x02f63, 0x02f64, 0x02f65, 0x02f66, 0x02f67, 0x02f68, + 0x02f69, 0x02f6a, 0x02f6b, 0x02f6c, 0x02f6d, 0x02f6e, 0x02f6f, 0x02f70, + 0x02f71, 0x02f72, 0x02f73, 0x02f74, 0x02f75, 0x02f76, 0x02f77, 0x02f78, + 0x02f79, 0x02f7a, 0x02f7b, 0x02f7c, 0x02f7d, 0x02f7e, 0x02f7f, 0x02f80, + 0x02f81, 0x02f82, 0x02f83, 0x02f84, 0x02f85, 0x02f86, 0x02f87, 0x02f88, + 0x02f89, 0x02f8a, 0x02f8b, 0x02f8c, 0x02f8d, 0x02f8e, 0x02f8f, 0x02f90, + 0x02f91, 0x02f92, 0x02f93, 0x02f94, 0x02f95, 0x02f96, 0x02f97, 0x02f98, + 0x02f99, 0x02f9a, 0x02f9b, 0x02f9c, 0x02f9d, 0x02f9e, 0x02f9f, 0x02fa0, + 0x02fa1, 0x02fa2, 0x02fa3, 0x02fa4, 0x02fa5, 0x02fa6, 0x02fa7, 0x02fa8, + 0x02fa9, 0x02faa, 0x02fab, 0x02fac, 0x02fad, 0x02fae, 0x02faf, 0x02fb0, + 0x02fb1, 0x02fb2, 0x02fb3, 0x02fb4, 0x02fb5, 0x02fb6, 0x02fb7, 0x02fb8, + 0x02fb9, 0x02fba, 0x02fbb, 0x02fbc, 0x02fbd, 0x02fbe, 0x02fbf, 0x02fc0, + 0x02fc1, 0x02fc2, 0x02fc3, 0x02fc4, 0x02fc5, 0x02fc6, 0x02fc7, 0x02fc8, + 0x02fc9, 0x02fca, 0x02fcb, 0x02fcc, 0x02fcd, 0x02fce, 0x02fcf, 0x02fd0, + 0x02fd1, 0x02fd2, 0x02fd3, 0x02fd4, 0x02fd5, 0x03000, 0x03036, 0x03038, + 0x03039, 0x0303a, 0x03131, 0x03132, 0x03133, 0x03134, 0x03135, 0x03136, + 0x03137, 0x03138, 0x03139, 0x0313a, 0x0313b, 0x0313c, 0x0313d, 0x0313e, + 0x0313f, 0x03140, 0x03141, 0x03142, 0x03143, 0x03144, 0x03145, 0x03146, + 0x03147, 0x03148, 0x03149, 0x0314a, 0x0314b, 0x0314c, 0x0314d, 0x0314e, + 0x0314f, 0x03150, 0x03151, 0x03152, 0x03153, 0x03154, 0x03155, 0x03156, + 0x03157, 0x03158, 0x03159, 0x0315a, 0x0315b, 0x0315c, 0x0315d, 0x0315e, + 0x0315f, 0x03160, 0x03161, 0x03162, 0x03163, 0x03164, 0x03165, 0x03166, + 0x03167, 0x03168, 0x03169, 0x0316a, 0x0316b, 0x0316c, 0x0316d, 0x0316e, + 0x0316f, 0x03170, 0x03171, 0x03172, 0x03173, 0x03174, 0x03175, 0x03176, + 0x03177, 0x03178, 0x03179, 0x0317a, 0x0317b, 0x0317c, 0x0317d, 0x0317e, + 0x0317f, 0x03180, 0x03181, 0x03182, 0x03183, 0x03184, 0x03185, 0x03186, + 0x03187, 0x03188, 0x03189, 0x0318a, 0x0318b, 0x0318c, 0x0318d, 0x0318e, + 0x03192, 0x03193, 0x03194, 0x03195, 0x03196, 0x03197, 0x03198, 0x03199, + 0x0319a, 0x0319b, 0x0319c, 0x0319d, 0x0319e, 0x0319f, 0x03244, 0x03245, + 0x03246, 0x03247, 0x03260, 0x03261, 0x03262, 0x03263, 0x03264, 0x03265, + 0x03266, 0x03267, 0x03268, 0x03269, 0x0326a, 0x0326b, 0x0326c, 0x0326d, + 0x03280, 0x03281, 0x03282, 0x03283, 0x03284, 0x03285, 0x03286, 0x03287, + 0x03288, 0x03289, 0x0328a, 0x0328b, 0x0328c, 0x0328d, 0x0328e, 0x0328f, + 0x03290, 0x03291, 0x03292, 0x03293, 0x03294, 0x03295, 0x03296, 0x03297, + 0x03298, 0x03299, 0x0329a, 0x0329b, 0x0329c, 0x0329d, 0x0329e, 0x0329f, + 0x032a0, 0x032a1, 0x032a2, 0x032a3, 0x032a4, 0x032a5, 0x032a6, 0x032a7, + 0x032a8, 0x032a9, 0x032aa, 0x032ab, 0x032ac, 0x032ad, 0x032ae, 0x032af, + 0x032b0, 0x032d0, 0x032d1, 0x032d2, 0x032d3, 0x032d4, 0x032d5, 0x032d6, + 0x032d7, 0x032d8, 0x032d9, 0x032da, 0x032db, 0x032dc, 0x032dd, 0x032de, + 0x032df, 0x032e0, 0x032e1, 0x032e2, 0x032e3, 0x032e4, 0x032e5, 0x032e6, + 0x032e7, 0x032e8, 0x032e9, 0x032ea, 0x032eb, 0x032ec, 0x032ed, 0x032ee, + 0x032ef, 0x032f0, 0x032f1, 0x032f2, 0x032f3, 0x032f4, 0x032f5, 0x032f6, + 0x032f7, 0x032f8, 0x032f9, 0x032fa, 0x032fb, 0x032fc, 0x032fd, 0x032fe, + 0x0a69c, 0x0a69d, 0x0a770, 0x0a7f8, 0x0a7f9, 0x0ab5c, 0x0ab5d, 0x0ab5e, + 0x0ab5f, 0x0f900, 0x0f901, 0x0f902, 0x0f903, 0x0f904, 0x0f905, 0x0f906, + 0x0f907, 0x0f908, 0x0f909, 0x0f90a, 0x0f90b, 0x0f90c, 0x0f90d, 0x0f90e, + 0x0f90f, 0x0f910, 0x0f911, 0x0f912, 0x0f913, 0x0f914, 0x0f915, 0x0f916, + 0x0f917, 0x0f918, 0x0f919, 0x0f91a, 0x0f91b, 0x0f91c, 0x0f91d, 0x0f91e, + 0x0f91f, 0x0f920, 0x0f921, 0x0f922, 0x0f923, 0x0f924, 0x0f925, 0x0f926, + 0x0f927, 0x0f928, 0x0f929, 0x0f92a, 0x0f92b, 0x0f92c, 0x0f92d, 0x0f92e, + 0x0f92f, 0x0f930, 0x0f931, 0x0f932, 0x0f933, 0x0f934, 0x0f935, 0x0f936, + 0x0f937, 0x0f938, 0x0f939, 0x0f93a, 0x0f93b, 0x0f93c, 0x0f93d, 0x0f93e, + 0x0f93f, 0x0f940, 0x0f941, 0x0f942, 0x0f943, 0x0f944, 0x0f945, 0x0f946, + 0x0f947, 0x0f948, 0x0f949, 0x0f94a, 0x0f94b, 0x0f94c, 0x0f94d, 0x0f94e, + 0x0f94f, 0x0f950, 0x0f951, 0x0f952, 0x0f953, 0x0f954, 0x0f955, 0x0f956, + 0x0f957, 0x0f958, 0x0f959, 0x0f95a, 0x0f95b, 0x0f95c, 0x0f95d, 0x0f95e, + 0x0f95f, 0x0f960, 0x0f961, 0x0f962, 0x0f963, 0x0f964, 0x0f965, 0x0f966, + 0x0f967, 0x0f968, 0x0f969, 0x0f96a, 0x0f96b, 0x0f96c, 0x0f96d, 0x0f96e, + 0x0f96f, 0x0f970, 0x0f971, 0x0f972, 0x0f973, 0x0f974, 0x0f975, 0x0f976, + 0x0f977, 0x0f978, 0x0f979, 0x0f97a, 0x0f97b, 0x0f97c, 0x0f97d, 0x0f97e, + 0x0f97f, 0x0f980, 0x0f981, 0x0f982, 0x0f983, 0x0f984, 0x0f985, 0x0f986, + 0x0f987, 0x0f988, 0x0f989, 0x0f98a, 0x0f98b, 0x0f98c, 0x0f98d, 0x0f98e, + 0x0f98f, 0x0f990, 0x0f991, 0x0f992, 0x0f993, 0x0f994, 0x0f995, 0x0f996, + 0x0f997, 0x0f998, 0x0f999, 0x0f99a, 0x0f99b, 0x0f99c, 0x0f99d, 0x0f99e, + 0x0f99f, 0x0f9a0, 0x0f9a1, 0x0f9a2, 0x0f9a3, 0x0f9a4, 0x0f9a5, 0x0f9a6, + 0x0f9a7, 0x0f9a8, 0x0f9a9, 0x0f9aa, 0x0f9ab, 0x0f9ac, 0x0f9ad, 0x0f9ae, + 0x0f9af, 0x0f9b0, 0x0f9b1, 0x0f9b2, 0x0f9b3, 0x0f9b4, 0x0f9b5, 0x0f9b6, + 0x0f9b7, 0x0f9b8, 0x0f9b9, 0x0f9ba, 0x0f9bb, 0x0f9bc, 0x0f9bd, 0x0f9be, + 0x0f9bf, 0x0f9c0, 0x0f9c1, 0x0f9c2, 0x0f9c3, 0x0f9c4, 0x0f9c5, 0x0f9c6, + 0x0f9c7, 0x0f9c8, 0x0f9c9, 0x0f9ca, 0x0f9cb, 0x0f9cc, 0x0f9cd, 0x0f9ce, + 0x0f9cf, 0x0f9d0, 0x0f9d1, 0x0f9d2, 0x0f9d3, 0x0f9d4, 0x0f9d5, 0x0f9d6, + 0x0f9d7, 0x0f9d8, 0x0f9d9, 0x0f9da, 0x0f9db, 0x0f9dc, 0x0f9dd, 0x0f9de, + 0x0f9df, 0x0f9e0, 0x0f9e1, 0x0f9e2, 0x0f9e3, 0x0f9e4, 0x0f9e5, 0x0f9e6, + 0x0f9e7, 0x0f9e8, 0x0f9e9, 0x0f9ea, 0x0f9eb, 0x0f9ec, 0x0f9ed, 0x0f9ee, + 0x0f9ef, 0x0f9f0, 0x0f9f1, 0x0f9f2, 0x0f9f3, 0x0f9f4, 0x0f9f5, 0x0f9f6, + 0x0f9f7, 0x0f9f8, 0x0f9f9, 0x0f9fa, 0x0f9fb, 0x0f9fc, 0x0f9fd, 0x0f9fe, + 0x0f9ff, 0x0fa00, 0x0fa01, 0x0fa02, 0x0fa03, 0x0fa04, 0x0fa05, 0x0fa06, + 0x0fa07, 0x0fa08, 0x0fa09, 0x0fa0a, 0x0fa0b, 0x0fa0c, 0x0fa0d, 0x0fa10, + 0x0fa12, 0x0fa15, 0x0fa16, 0x0fa17, 0x0fa18, 0x0fa19, 0x0fa1a, 0x0fa1b, + 0x0fa1c, 0x0fa1d, 0x0fa1e, 0x0fa20, 0x0fa22, 0x0fa25, 0x0fa26, 0x0fa2a, + 0x0fa2b, 0x0fa2c, 0x0fa2d, 0x0fa2e, 0x0fa2f, 0x0fa30, 0x0fa31, 0x0fa32, + 0x0fa33, 0x0fa34, 0x0fa35, 0x0fa36, 0x0fa37, 0x0fa38, 0x0fa39, 0x0fa3a, + 0x0fa3b, 0x0fa3c, 0x0fa3d, 0x0fa3e, 0x0fa3f, 0x0fa40, 0x0fa41, 0x0fa42, + 0x0fa43, 0x0fa44, 0x0fa45, 0x0fa46, 0x0fa47, 0x0fa48, 0x0fa49, 0x0fa4a, + 0x0fa4b, 0x0fa4c, 0x0fa4d, 0x0fa4e, 0x0fa4f, 0x0fa50, 0x0fa51, 0x0fa52, + 0x0fa53, 0x0fa54, 0x0fa55, 0x0fa56, 0x0fa57, 0x0fa58, 0x0fa59, 0x0fa5a, + 0x0fa5b, 0x0fa5c, 0x0fa5d, 0x0fa5e, 0x0fa5f, 0x0fa60, 0x0fa61, 0x0fa62, + 0x0fa63, 0x0fa64, 0x0fa65, 0x0fa66, 0x0fa67, 0x0fa68, 0x0fa69, 0x0fa6a, + 0x0fa6b, 0x0fa6c, 0x0fa6d, 0x0fa70, 0x0fa71, 0x0fa72, 0x0fa73, 0x0fa74, + 0x0fa75, 0x0fa76, 0x0fa77, 0x0fa78, 0x0fa79, 0x0fa7a, 0x0fa7b, 0x0fa7c, + 0x0fa7d, 0x0fa7e, 0x0fa7f, 0x0fa80, 0x0fa81, 0x0fa82, 0x0fa83, 0x0fa84, + 0x0fa85, 0x0fa86, 0x0fa87, 0x0fa88, 0x0fa89, 0x0fa8a, 0x0fa8b, 0x0fa8c, + 0x0fa8d, 0x0fa8e, 0x0fa8f, 0x0fa90, 0x0fa91, 0x0fa92, 0x0fa93, 0x0fa94, + 0x0fa95, 0x0fa96, 0x0fa97, 0x0fa98, 0x0fa99, 0x0fa9a, 0x0fa9b, 0x0fa9c, + 0x0fa9d, 0x0fa9e, 0x0fa9f, 0x0faa0, 0x0faa1, 0x0faa2, 0x0faa3, 0x0faa4, + 0x0faa5, 0x0faa6, 0x0faa7, 0x0faa8, 0x0faa9, 0x0faaa, 0x0faab, 0x0faac, + 0x0faad, 0x0faae, 0x0faaf, 0x0fab0, 0x0fab1, 0x0fab2, 0x0fab3, 0x0fab4, + 0x0fab5, 0x0fab6, 0x0fab7, 0x0fab8, 0x0fab9, 0x0faba, 0x0fabb, 0x0fabc, + 0x0fabd, 0x0fabe, 0x0fabf, 0x0fac0, 0x0fac1, 0x0fac2, 0x0fac3, 0x0fac4, + 0x0fac5, 0x0fac6, 0x0fac7, 0x0fac8, 0x0fac9, 0x0faca, 0x0facb, 0x0facc, + 0x0facd, 0x0face, 0x0facf, 0x0fad0, 0x0fad1, 0x0fad2, 0x0fad3, 0x0fad4, + 0x0fad5, 0x0fad6, 0x0fad7, 0x0fad8, 0x0fad9, 0x0fb20, 0x0fb21, 0x0fb22, + 0x0fb23, 0x0fb24, 0x0fb25, 0x0fb26, 0x0fb27, 0x0fb28, 0x0fb29, 0x0fb50, + 0x0fb51, 0x0fb52, 0x0fb53, 0x0fb54, 0x0fb55, 0x0fb56, 0x0fb57, 0x0fb58, + 0x0fb59, 0x0fb5a, 0x0fb5b, 0x0fb5c, 0x0fb5d, 0x0fb5e, 0x0fb5f, 0x0fb60, + 0x0fb61, 0x0fb62, 0x0fb63, 0x0fb64, 0x0fb65, 0x0fb66, 0x0fb67, 0x0fb68, + 0x0fb69, 0x0fb6a, 0x0fb6b, 0x0fb6c, 0x0fb6d, 0x0fb6e, 0x0fb6f, 0x0fb70, + 0x0fb71, 0x0fb72, 0x0fb73, 0x0fb74, 0x0fb75, 0x0fb76, 0x0fb77, 0x0fb78, + 0x0fb79, 0x0fb7a, 0x0fb7b, 0x0fb7c, 0x0fb7d, 0x0fb7e, 0x0fb7f, 0x0fb80, + 0x0fb81, 0x0fb82, 0x0fb83, 0x0fb84, 0x0fb85, 0x0fb86, 0x0fb87, 0x0fb88, + 0x0fb89, 0x0fb8a, 0x0fb8b, 0x0fb8c, 0x0fb8d, 0x0fb8e, 0x0fb8f, 0x0fb90, + 0x0fb91, 0x0fb92, 0x0fb93, 0x0fb94, 0x0fb95, 0x0fb96, 0x0fb97, 0x0fb98, + 0x0fb99, 0x0fb9a, 0x0fb9b, 0x0fb9c, 0x0fb9d, 0x0fb9e, 0x0fb9f, 0x0fba0, + 0x0fba1, 0x0fba2, 0x0fba3, 0x0fba4, 0x0fba5, 0x0fba6, 0x0fba7, 0x0fba8, + 0x0fba9, 0x0fbaa, 0x0fbab, 0x0fbac, 0x0fbad, 0x0fbae, 0x0fbaf, 0x0fbb0, + 0x0fbb1, 0x0fbd3, 0x0fbd4, 0x0fbd5, 0x0fbd6, 0x0fbd7, 0x0fbd8, 0x0fbd9, + 0x0fbda, 0x0fbdb, 0x0fbdc, 0x0fbdd, 0x0fbde, 0x0fbdf, 0x0fbe0, 0x0fbe1, + 0x0fbe2, 0x0fbe3, 0x0fbe4, 0x0fbe5, 0x0fbe6, 0x0fbe7, 0x0fbe8, 0x0fbe9, + 0x0fbfc, 0x0fbfd, 0x0fbfe, 0x0fbff, 0x0fe10, 0x0fe11, 0x0fe12, 0x0fe13, + 0x0fe14, 0x0fe15, 0x0fe16, 0x0fe17, 0x0fe18, 0x0fe19, 0x0fe30, 0x0fe31, + 0x0fe32, 0x0fe33, 0x0fe34, 0x0fe35, 0x0fe36, 0x0fe37, 0x0fe38, 0x0fe39, + 0x0fe3a, 0x0fe3b, 0x0fe3c, 0x0fe3d, 0x0fe3e, 0x0fe3f, 0x0fe40, 0x0fe41, + 0x0fe42, 0x0fe43, 0x0fe44, 0x0fe47, 0x0fe48, 0x0fe49, 0x0fe4a, 0x0fe4b, + 0x0fe4c, 0x0fe4d, 0x0fe4e, 0x0fe4f, 0x0fe50, 0x0fe51, 0x0fe52, 0x0fe54, + 0x0fe55, 0x0fe56, 0x0fe57, 0x0fe58, 0x0fe59, 0x0fe5a, 0x0fe5b, 0x0fe5c, + 0x0fe5d, 0x0fe5e, 0x0fe5f, 0x0fe60, 0x0fe61, 0x0fe62, 0x0fe63, 0x0fe64, + 0x0fe65, 0x0fe66, 0x0fe68, 0x0fe69, 0x0fe6a, 0x0fe6b, 0x0fe80, 0x0fe81, + 0x0fe82, 0x0fe83, 0x0fe84, 0x0fe85, 0x0fe86, 0x0fe87, 0x0fe88, 0x0fe89, + 0x0fe8a, 0x0fe8b, 0x0fe8c, 0x0fe8d, 0x0fe8e, 0x0fe8f, 0x0fe90, 0x0fe91, + 0x0fe92, 0x0fe93, 0x0fe94, 0x0fe95, 0x0fe96, 0x0fe97, 0x0fe98, 0x0fe99, + 0x0fe9a, 0x0fe9b, 0x0fe9c, 0x0fe9d, 0x0fe9e, 0x0fe9f, 0x0fea0, 0x0fea1, + 0x0fea2, 0x0fea3, 0x0fea4, 0x0fea5, 0x0fea6, 0x0fea7, 0x0fea8, 0x0fea9, + 0x0feaa, 0x0feab, 0x0feac, 0x0fead, 0x0feae, 0x0feaf, 0x0feb0, 0x0feb1, + 0x0feb2, 0x0feb3, 0x0feb4, 0x0feb5, 0x0feb6, 0x0feb7, 0x0feb8, 0x0feb9, + 0x0feba, 0x0febb, 0x0febc, 0x0febd, 0x0febe, 0x0febf, 0x0fec0, 0x0fec1, + 0x0fec2, 0x0fec3, 0x0fec4, 0x0fec5, 0x0fec6, 0x0fec7, 0x0fec8, 0x0fec9, + 0x0feca, 0x0fecb, 0x0fecc, 0x0fecd, 0x0fece, 0x0fecf, 0x0fed0, 0x0fed1, + 0x0fed2, 0x0fed3, 0x0fed4, 0x0fed5, 0x0fed6, 0x0fed7, 0x0fed8, 0x0fed9, + 0x0feda, 0x0fedb, 0x0fedc, 0x0fedd, 0x0fede, 0x0fedf, 0x0fee0, 0x0fee1, + 0x0fee2, 0x0fee3, 0x0fee4, 0x0fee5, 0x0fee6, 0x0fee7, 0x0fee8, 0x0fee9, + 0x0feea, 0x0feeb, 0x0feec, 0x0feed, 0x0feee, 0x0feef, 0x0fef0, 0x0fef1, + 0x0fef2, 0x0fef3, 0x0fef4, 0x0ff01, 0x0ff02, 0x0ff03, 0x0ff04, 0x0ff05, + 0x0ff06, 0x0ff07, 0x0ff08, 0x0ff09, 0x0ff0a, 0x0ff0b, 0x0ff0c, 0x0ff0d, + 0x0ff0e, 0x0ff0f, 0x0ff10, 0x0ff11, 0x0ff12, 0x0ff13, 0x0ff14, 0x0ff15, + 0x0ff16, 0x0ff17, 0x0ff18, 0x0ff19, 0x0ff1a, 0x0ff1b, 0x0ff1c, 0x0ff1d, + 0x0ff1e, 0x0ff1f, 0x0ff20, 0x0ff21, 0x0ff22, 0x0ff23, 0x0ff24, 0x0ff25, + 0x0ff26, 0x0ff27, 0x0ff28, 0x0ff29, 0x0ff2a, 0x0ff2b, 0x0ff2c, 0x0ff2d, + 0x0ff2e, 0x0ff2f, 0x0ff30, 0x0ff31, 0x0ff32, 0x0ff33, 0x0ff34, 0x0ff35, + 0x0ff36, 0x0ff37, 0x0ff38, 0x0ff39, 0x0ff3a, 0x0ff3b, 0x0ff3c, 0x0ff3d, + 0x0ff3e, 0x0ff3f, 0x0ff40, 0x0ff5b, 0x0ff5c, 0x0ff5d, 0x0ff5e, 0x0ff5f, + 0x0ff60, 0x0ff61, 0x0ff62, 0x0ff63, 0x0ff64, 0x0ff65, 0x0ff66, 0x0ff67, + 0x0ff68, 0x0ff69, 0x0ff6a, 0x0ff6b, 0x0ff6c, 0x0ff6d, 0x0ff6e, 0x0ff6f, + 0x0ff70, 0x0ff71, 0x0ff72, 0x0ff73, 0x0ff74, 0x0ff75, 0x0ff76, 0x0ff77, + 0x0ff78, 0x0ff79, 0x0ff7a, 0x0ff7b, 0x0ff7c, 0x0ff7d, 0x0ff7e, 0x0ff7f, + 0x0ff80, 0x0ff81, 0x0ff82, 0x0ff83, 0x0ff84, 0x0ff85, 0x0ff86, 0x0ff87, + 0x0ff88, 0x0ff89, 0x0ff8a, 0x0ff8b, 0x0ff8c, 0x0ff8d, 0x0ff8e, 0x0ff8f, + 0x0ff90, 0x0ff91, 0x0ff92, 0x0ff93, 0x0ff94, 0x0ff95, 0x0ff96, 0x0ff97, + 0x0ff98, 0x0ff99, 0x0ff9a, 0x0ff9b, 0x0ff9c, 0x0ff9d, 0x0ff9e, 0x0ff9f, + 0x0ffa0, 0x0ffa1, 0x0ffa2, 0x0ffa3, 0x0ffa4, 0x0ffa5, 0x0ffa6, 0x0ffa7, + 0x0ffa8, 0x0ffa9, 0x0ffaa, 0x0ffab, 0x0ffac, 0x0ffad, 0x0ffae, 0x0ffaf, + 0x0ffb0, 0x0ffb1, 0x0ffb2, 0x0ffb3, 0x0ffb4, 0x0ffb5, 0x0ffb6, 0x0ffb7, + 0x0ffb8, 0x0ffb9, 0x0ffba, 0x0ffbb, 0x0ffbc, 0x0ffbd, 0x0ffbe, 0x0ffc2, + 0x0ffc3, 0x0ffc4, 0x0ffc5, 0x0ffc6, 0x0ffc7, 0x0ffca, 0x0ffcb, 0x0ffcc, + 0x0ffcd, 0x0ffce, 0x0ffcf, 0x0ffd2, 0x0ffd3, 0x0ffd4, 0x0ffd5, 0x0ffd6, + 0x0ffd7, 0x0ffda, 0x0ffdb, 0x0ffdc, 0x0ffe0, 0x0ffe1, 0x0ffe2, 0x0ffe3, + 0x0ffe4, 0x0ffe5, 0x0ffe6, 0x0ffe8, 0x0ffe9, 0x0ffea, 0x0ffeb, 0x0ffec, + 0x0ffed, 0x0ffee +}; +static const uint32_t uni16_decomp_values[] = { + 0x00068, 0x00266, 0x0006a, 0x00072, 0x00279, 0x0027b, 0x00281, 0x00077, + 0x00079, 0x00263, 0x0006c, 0x00073, 0x00078, 0x00295, 0x00300, 0x00301, + 0x00313, 0x002b9, 0x0003b, 0x000b7, 0x003a5, 0x00398, 0x003a3, 0x00f0b, + 0x010dc, 0x00041, 0x000c6, 0x00042, 0x00044, 0x00045, 0x0018e, 0x00047, + 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, + 0x00222, 0x00050, 0x00052, 0x00054, 0x00055, 0x00057, 0x00061, 0x00250, + 0x00251, 0x01d02, 0x00062, 0x00064, 0x00065, 0x00259, 0x0025b, 0x0025c, + 0x00067, 0x0006b, 0x0006d, 0x0014b, 0x0006f, 0x00254, 0x01d16, 0x01d17, + 0x00070, 0x00074, 0x00075, 0x01d1d, 0x0026f, 0x00076, 0x01d25, 0x003b2, + 0x003b3, 0x003b4, 0x003c6, 0x003c7, 0x00069, 0x00072, 0x00075, 0x00076, + 0x003b2, 0x003b3, 0x003c1, 0x003c6, 0x003c7, 0x0043d, 0x00252, 0x00063, + 0x00255, 0x000f0, 0x0025c, 0x00066, 0x0025f, 0x00261, 0x00265, 0x00268, + 0x00269, 0x0026a, 0x01d7b, 0x0029d, 0x0026d, 0x01d85, 0x0029f, 0x00271, + 0x00270, 0x00272, 0x00273, 0x00274, 0x00275, 0x00278, 0x00282, 0x00283, + 0x001ab, 0x00289, 0x0028a, 0x01d1c, 0x0028b, 0x0028c, 0x0007a, 0x00290, + 0x00291, 0x00292, 0x003b8, 0x00386, 0x00388, 0x00389, 0x00390, 0x0038a, + 0x003b0, 0x0038e, 0x00385, 0x00060, 0x0038c, 0x0038f, 0x000b4, 0x02002, + 0x02003, 0x00020, 0x00020, 0x00020, 0x00020, 0x00020, 0x00020, 0x00020, + 0x00020, 0x00020, 0x02010, 0x0002e, 0x00020, 0x00020, 0x00030, 0x00069, + 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x0002b, 0x02212, + 0x0003d, 0x00028, 0x00029, 0x0006e, 0x00030, 0x00031, 0x00032, 0x00033, + 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x0002b, 0x02212, + 0x0003d, 0x00028, 0x00029, 0x00061, 0x00065, 0x0006f, 0x00078, 0x00259, + 0x00068, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x00070, 0x00073, 0x00074, + 0x00043, 0x00190, 0x00067, 0x00048, 0x00048, 0x00048, 0x00068, 0x00127, + 0x00049, 0x00049, 0x0004c, 0x0006c, 0x0004e, 0x00050, 0x00051, 0x00052, + 0x00052, 0x00052, 0x0005a, 0x003a9, 0x0005a, 0x0004b, 0x000c5, 0x00042, + 0x00043, 0x00065, 0x00045, 0x00046, 0x0004d, 0x0006f, 0x005d0, 0x005d1, + 0x005d2, 0x005d3, 0x00069, 0x003c0, 0x003b3, 0x00393, 0x003a0, 0x02211, + 0x00044, 0x00064, 0x00065, 0x00069, 0x0006a, 0x00049, 0x00056, 0x00058, + 0x0004c, 0x00043, 0x00044, 0x0004d, 0x03008, 0x03009, 0x00031, 0x00032, + 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x00041, + 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, + 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, + 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, + 0x0005a, 0x00030, 0x0006a, 0x00056, 0x02d61, 0x06bcd, 0x09f9f, 0x04e00, + 0x04e28, 0x04e36, 0x04e3f, 0x04e59, 0x04e85, 0x04e8c, 0x04ea0, 0x04eba, + 0x0513f, 0x05165, 0x0516b, 0x05182, 0x05196, 0x051ab, 0x051e0, 0x051f5, + 0x05200, 0x0529b, 0x052f9, 0x05315, 0x0531a, 0x05338, 0x05341, 0x0535c, + 0x05369, 0x05382, 0x053b6, 0x053c8, 0x053e3, 0x056d7, 0x0571f, 0x058eb, + 0x05902, 0x0590a, 0x05915, 0x05927, 0x05973, 0x05b50, 0x05b80, 0x05bf8, + 0x05c0f, 0x05c22, 0x05c38, 0x05c6e, 0x05c71, 0x05ddb, 0x05de5, 0x05df1, + 0x05dfe, 0x05e72, 0x05e7a, 0x05e7f, 0x05ef4, 0x05efe, 0x05f0b, 0x05f13, + 0x05f50, 0x05f61, 0x05f73, 0x05fc3, 0x06208, 0x06236, 0x0624b, 0x0652f, + 0x06534, 0x06587, 0x06597, 0x065a4, 0x065b9, 0x065e0, 0x065e5, 0x066f0, + 0x06708, 0x06728, 0x06b20, 0x06b62, 0x06b79, 0x06bb3, 0x06bcb, 0x06bd4, + 0x06bdb, 0x06c0f, 0x06c14, 0x06c34, 0x0706b, 0x0722a, 0x07236, 0x0723b, + 0x0723f, 0x07247, 0x07259, 0x0725b, 0x072ac, 0x07384, 0x07389, 0x074dc, + 0x074e6, 0x07518, 0x0751f, 0x07528, 0x07530, 0x0758b, 0x07592, 0x07676, + 0x0767d, 0x076ae, 0x076bf, 0x076ee, 0x077db, 0x077e2, 0x077f3, 0x0793a, + 0x079b8, 0x079be, 0x07a74, 0x07acb, 0x07af9, 0x07c73, 0x07cf8, 0x07f36, + 0x07f51, 0x07f8a, 0x07fbd, 0x08001, 0x0800c, 0x08012, 0x08033, 0x0807f, + 0x08089, 0x081e3, 0x081ea, 0x081f3, 0x081fc, 0x0820c, 0x0821b, 0x0821f, + 0x0826e, 0x08272, 0x08278, 0x0864d, 0x0866b, 0x08840, 0x0884c, 0x08863, + 0x0897e, 0x0898b, 0x089d2, 0x08a00, 0x08c37, 0x08c46, 0x08c55, 0x08c78, + 0x08c9d, 0x08d64, 0x08d70, 0x08db3, 0x08eab, 0x08eca, 0x08f9b, 0x08fb0, + 0x08fb5, 0x09091, 0x09149, 0x091c6, 0x091cc, 0x091d1, 0x09577, 0x09580, + 0x0961c, 0x096b6, 0x096b9, 0x096e8, 0x09751, 0x0975e, 0x09762, 0x09769, + 0x097cb, 0x097ed, 0x097f3, 0x09801, 0x098a8, 0x098db, 0x098df, 0x09996, + 0x09999, 0x099ac, 0x09aa8, 0x09ad8, 0x09adf, 0x09b25, 0x09b2f, 0x09b32, + 0x09b3c, 0x09b5a, 0x09ce5, 0x09e75, 0x09e7f, 0x09ea5, 0x09ebb, 0x09ec3, + 0x09ecd, 0x09ed1, 0x09ef9, 0x09efd, 0x09f0e, 0x09f13, 0x09f20, 0x09f3b, + 0x09f4a, 0x09f52, 0x09f8d, 0x09f9c, 0x09fa0, 0x00020, 0x03012, 0x05341, + 0x05344, 0x05345, 0x01100, 0x01101, 0x011aa, 0x01102, 0x011ac, 0x011ad, + 0x01103, 0x01104, 0x01105, 0x011b0, 0x011b1, 0x011b2, 0x011b3, 0x011b4, + 0x011b5, 0x0111a, 0x01106, 0x01107, 0x01108, 0x01121, 0x01109, 0x0110a, + 0x0110b, 0x0110c, 0x0110d, 0x0110e, 0x0110f, 0x01110, 0x01111, 0x01112, + 0x01161, 0x01162, 0x01163, 0x01164, 0x01165, 0x01166, 0x01167, 0x01168, + 0x01169, 0x0116a, 0x0116b, 0x0116c, 0x0116d, 0x0116e, 0x0116f, 0x01170, + 0x01171, 0x01172, 0x01173, 0x01174, 0x01175, 0x01160, 0x01114, 0x01115, + 0x011c7, 0x011c8, 0x011cc, 0x011ce, 0x011d3, 0x011d7, 0x011d9, 0x0111c, + 0x011dd, 0x011df, 0x0111d, 0x0111e, 0x01120, 0x01122, 0x01123, 0x01127, + 0x01129, 0x0112b, 0x0112c, 0x0112d, 0x0112e, 0x0112f, 0x01132, 0x01136, + 0x01140, 0x01147, 0x0114c, 0x011f1, 0x011f2, 0x01157, 0x01158, 0x01159, + 0x01184, 0x01185, 0x01188, 0x01191, 0x01192, 0x01194, 0x0119e, 0x011a1, + 0x04e00, 0x04e8c, 0x04e09, 0x056db, 0x04e0a, 0x04e2d, 0x04e0b, 0x07532, + 0x04e59, 0x04e19, 0x04e01, 0x05929, 0x05730, 0x04eba, 0x0554f, 0x05e7c, + 0x06587, 0x07b8f, 0x01100, 0x01102, 0x01103, 0x01105, 0x01106, 0x01107, + 0x01109, 0x0110b, 0x0110c, 0x0110e, 0x0110f, 0x01110, 0x01111, 0x01112, + 0x04e00, 0x04e8c, 0x04e09, 0x056db, 0x04e94, 0x0516d, 0x04e03, 0x0516b, + 0x04e5d, 0x05341, 0x06708, 0x0706b, 0x06c34, 0x06728, 0x091d1, 0x0571f, + 0x065e5, 0x0682a, 0x06709, 0x0793e, 0x0540d, 0x07279, 0x08ca1, 0x0795d, + 0x052b4, 0x079d8, 0x07537, 0x05973, 0x09069, 0x0512a, 0x05370, 0x06ce8, + 0x09805, 0x04f11, 0x05199, 0x06b63, 0x04e0a, 0x04e2d, 0x04e0b, 0x05de6, + 0x053f3, 0x0533b, 0x05b97, 0x05b66, 0x076e3, 0x04f01, 0x08cc7, 0x05354, + 0x0591c, 0x030a2, 0x030a4, 0x030a6, 0x030a8, 0x030aa, 0x030ab, 0x030ad, + 0x030af, 0x030b1, 0x030b3, 0x030b5, 0x030b7, 0x030b9, 0x030bb, 0x030bd, + 0x030bf, 0x030c1, 0x030c4, 0x030c6, 0x030c8, 0x030ca, 0x030cb, 0x030cc, + 0x030cd, 0x030ce, 0x030cf, 0x030d2, 0x030d5, 0x030d8, 0x030db, 0x030de, + 0x030df, 0x030e0, 0x030e1, 0x030e2, 0x030e4, 0x030e6, 0x030e8, 0x030e9, + 0x030ea, 0x030eb, 0x030ec, 0x030ed, 0x030ef, 0x030f0, 0x030f1, 0x030f2, + 0x0044a, 0x0044c, 0x0a76f, 0x00126, 0x00153, 0x0a727, 0x0ab37, 0x0026b, + 0x0ab52, 0x08c48, 0x066f4, 0x08eca, 0x08cc8, 0x06ed1, 0x04e32, 0x053e5, + 0x09f9c, 0x09f9c, 0x05951, 0x091d1, 0x05587, 0x05948, 0x061f6, 0x07669, + 0x07f85, 0x0863f, 0x087ba, 0x088f8, 0x0908f, 0x06a02, 0x06d1b, 0x070d9, + 0x073de, 0x0843d, 0x0916a, 0x099f1, 0x04e82, 0x05375, 0x06b04, 0x0721b, + 0x0862d, 0x09e1e, 0x05d50, 0x06feb, 0x085cd, 0x08964, 0x062c9, 0x081d8, + 0x0881f, 0x05eca, 0x06717, 0x06d6a, 0x072fc, 0x090ce, 0x04f86, 0x051b7, + 0x052de, 0x064c4, 0x06ad3, 0x07210, 0x076e7, 0x08001, 0x08606, 0x0865c, + 0x08def, 0x09732, 0x09b6f, 0x09dfa, 0x0788c, 0x0797f, 0x07da0, 0x083c9, + 0x09304, 0x09e7f, 0x08ad6, 0x058df, 0x05f04, 0x07c60, 0x0807e, 0x07262, + 0x078ca, 0x08cc2, 0x096f7, 0x058d8, 0x05c62, 0x06a13, 0x06dda, 0x06f0f, + 0x07d2f, 0x07e37, 0x0964b, 0x052d2, 0x0808b, 0x051dc, 0x051cc, 0x07a1c, + 0x07dbe, 0x083f1, 0x09675, 0x08b80, 0x062cf, 0x06a02, 0x08afe, 0x04e39, + 0x05be7, 0x06012, 0x07387, 0x07570, 0x05317, 0x078fb, 0x04fbf, 0x05fa9, + 0x04e0d, 0x06ccc, 0x06578, 0x07d22, 0x053c3, 0x0585e, 0x07701, 0x08449, + 0x08aaa, 0x06bba, 0x08fb0, 0x06c88, 0x062fe, 0x082e5, 0x063a0, 0x07565, + 0x04eae, 0x05169, 0x051c9, 0x06881, 0x07ce7, 0x0826f, 0x08ad2, 0x091cf, + 0x052f5, 0x05442, 0x05973, 0x05eec, 0x065c5, 0x06ffe, 0x0792a, 0x095ad, + 0x09a6a, 0x09e97, 0x09ece, 0x0529b, 0x066c6, 0x06b77, 0x08f62, 0x05e74, + 0x06190, 0x06200, 0x0649a, 0x06f23, 0x07149, 0x07489, 0x079ca, 0x07df4, + 0x0806f, 0x08f26, 0x084ee, 0x09023, 0x0934a, 0x05217, 0x052a3, 0x054bd, + 0x070c8, 0x088c2, 0x08aaa, 0x05ec9, 0x05ff5, 0x0637b, 0x06bae, 0x07c3e, + 0x07375, 0x04ee4, 0x056f9, 0x05be7, 0x05dba, 0x0601c, 0x073b2, 0x07469, + 0x07f9a, 0x08046, 0x09234, 0x096f6, 0x09748, 0x09818, 0x04f8b, 0x079ae, + 0x091b4, 0x096b8, 0x060e1, 0x04e86, 0x050da, 0x05bee, 0x05c3f, 0x06599, + 0x06a02, 0x071ce, 0x07642, 0x084fc, 0x0907c, 0x09f8d, 0x06688, 0x0962e, + 0x05289, 0x0677b, 0x067f3, 0x06d41, 0x06e9c, 0x07409, 0x07559, 0x0786b, + 0x07d10, 0x0985e, 0x0516d, 0x0622e, 0x09678, 0x0502b, 0x05d19, 0x06dea, + 0x08f2a, 0x05f8b, 0x06144, 0x06817, 0x07387, 0x09686, 0x05229, 0x0540f, + 0x05c65, 0x06613, 0x0674e, 0x068a8, 0x06ce5, 0x07406, 0x075e2, 0x07f79, + 0x088cf, 0x088e1, 0x091cc, 0x096e2, 0x0533f, 0x06eba, 0x0541d, 0x071d0, + 0x07498, 0x085fa, 0x096a3, 0x09c57, 0x09e9f, 0x06797, 0x06dcb, 0x081e8, + 0x07acb, 0x07b20, 0x07c92, 0x072c0, 0x07099, 0x08b58, 0x04ec0, 0x08336, + 0x0523a, 0x05207, 0x05ea6, 0x062d3, 0x07cd6, 0x05b85, 0x06d1e, 0x066b4, + 0x08f3b, 0x0884c, 0x0964d, 0x0898b, 0x05ed3, 0x05140, 0x055c0, 0x0585a, + 0x06674, 0x051de, 0x0732a, 0x076ca, 0x0793c, 0x0795e, 0x07965, 0x0798f, + 0x09756, 0x07cbe, 0x07fbd, 0x08612, 0x08af8, 0x09038, 0x090fd, 0x098ef, + 0x098fc, 0x09928, 0x09db4, 0x090de, 0x096b7, 0x04fae, 0x050e7, 0x0514d, + 0x052c9, 0x052e4, 0x05351, 0x0559d, 0x05606, 0x05668, 0x05840, 0x058a8, + 0x05c64, 0x05c6e, 0x06094, 0x06168, 0x0618e, 0x061f2, 0x0654f, 0x065e2, + 0x06691, 0x06885, 0x06d77, 0x06e1a, 0x06f22, 0x0716e, 0x0722b, 0x07422, + 0x07891, 0x0793e, 0x07949, 0x07948, 0x07950, 0x07956, 0x0795d, 0x0798d, + 0x0798e, 0x07a40, 0x07a81, 0x07bc0, 0x07df4, 0x07e09, 0x07e41, 0x07f72, + 0x08005, 0x081ed, 0x08279, 0x08279, 0x08457, 0x08910, 0x08996, 0x08b01, + 0x08b39, 0x08cd3, 0x08d08, 0x08fb6, 0x09038, 0x096e3, 0x097ff, 0x0983b, + 0x06075, 0x242ee, 0x08218, 0x04e26, 0x051b5, 0x05168, 0x04f80, 0x05145, + 0x05180, 0x052c7, 0x052fa, 0x0559d, 0x05555, 0x05599, 0x055e2, 0x0585a, + 0x058b3, 0x05944, 0x05954, 0x05a62, 0x05b28, 0x05ed2, 0x05ed9, 0x05f69, + 0x05fad, 0x060d8, 0x0614e, 0x06108, 0x0618e, 0x06160, 0x061f2, 0x06234, + 0x063c4, 0x0641c, 0x06452, 0x06556, 0x06674, 0x06717, 0x0671b, 0x06756, + 0x06b79, 0x06bba, 0x06d41, 0x06edb, 0x06ecb, 0x06f22, 0x0701e, 0x0716e, + 0x077a7, 0x07235, 0x072af, 0x0732a, 0x07471, 0x07506, 0x0753b, 0x0761d, + 0x0761f, 0x076ca, 0x076db, 0x076f4, 0x0774a, 0x07740, 0x078cc, 0x07ab1, + 0x07bc0, 0x07c7b, 0x07d5b, 0x07df4, 0x07f3e, 0x08005, 0x08352, 0x083ef, + 0x08779, 0x08941, 0x08986, 0x08996, 0x08abf, 0x08af8, 0x08acb, 0x08b01, + 0x08afe, 0x08aed, 0x08b39, 0x08b8a, 0x08d08, 0x08f38, 0x09072, 0x09199, + 0x09276, 0x0967c, 0x096e3, 0x09756, 0x097db, 0x097ff, 0x0980b, 0x0983b, + 0x09b12, 0x09f9c, 0x2284a, 0x22844, 0x233d5, 0x03b9d, 0x04018, 0x04039, + 0x25249, 0x25cd0, 0x27ed3, 0x09f43, 0x09f8e, 0x005e2, 0x005d0, 0x005d3, + 0x005d4, 0x005db, 0x005dc, 0x005dd, 0x005e8, 0x005ea, 0x0002b, 0x00671, + 0x00671, 0x0067b, 0x0067b, 0x0067b, 0x0067b, 0x0067e, 0x0067e, 0x0067e, + 0x0067e, 0x00680, 0x00680, 0x00680, 0x00680, 0x0067a, 0x0067a, 0x0067a, + 0x0067a, 0x0067f, 0x0067f, 0x0067f, 0x0067f, 0x00679, 0x00679, 0x00679, + 0x00679, 0x006a4, 0x006a4, 0x006a4, 0x006a4, 0x006a6, 0x006a6, 0x006a6, + 0x006a6, 0x00684, 0x00684, 0x00684, 0x00684, 0x00683, 0x00683, 0x00683, + 0x00683, 0x00686, 0x00686, 0x00686, 0x00686, 0x00687, 0x00687, 0x00687, + 0x00687, 0x0068d, 0x0068d, 0x0068c, 0x0068c, 0x0068e, 0x0068e, 0x00688, + 0x00688, 0x00698, 0x00698, 0x00691, 0x00691, 0x006a9, 0x006a9, 0x006a9, + 0x006a9, 0x006af, 0x006af, 0x006af, 0x006af, 0x006b3, 0x006b3, 0x006b3, + 0x006b3, 0x006b1, 0x006b1, 0x006b1, 0x006b1, 0x006ba, 0x006ba, 0x006bb, + 0x006bb, 0x006bb, 0x006bb, 0x006c0, 0x006c0, 0x006c1, 0x006c1, 0x006c1, + 0x006c1, 0x006be, 0x006be, 0x006be, 0x006be, 0x006d2, 0x006d2, 0x006d3, + 0x006d3, 0x006ad, 0x006ad, 0x006ad, 0x006ad, 0x006c7, 0x006c7, 0x006c6, + 0x006c6, 0x006c8, 0x006c8, 0x00677, 0x006cb, 0x006cb, 0x006c5, 0x006c5, + 0x006c9, 0x006c9, 0x006d0, 0x006d0, 0x006d0, 0x006d0, 0x00649, 0x00649, + 0x006cc, 0x006cc, 0x006cc, 0x006cc, 0x0002c, 0x03001, 0x03002, 0x0003a, + 0x0003b, 0x00021, 0x0003f, 0x03016, 0x03017, 0x02026, 0x02025, 0x02014, + 0x02013, 0x0005f, 0x0005f, 0x00028, 0x00029, 0x0007b, 0x0007d, 0x03014, + 0x03015, 0x03010, 0x03011, 0x0300a, 0x0300b, 0x03008, 0x03009, 0x0300c, + 0x0300d, 0x0300e, 0x0300f, 0x0005b, 0x0005d, 0x0203e, 0x0203e, 0x0203e, + 0x0203e, 0x0005f, 0x0005f, 0x0005f, 0x0002c, 0x03001, 0x0002e, 0x0003b, + 0x0003a, 0x0003f, 0x00021, 0x02014, 0x00028, 0x00029, 0x0007b, 0x0007d, + 0x03014, 0x03015, 0x00023, 0x00026, 0x0002a, 0x0002b, 0x0002d, 0x0003c, + 0x0003e, 0x0003d, 0x0005c, 0x00024, 0x00025, 0x00040, 0x00621, 0x00622, + 0x00622, 0x00623, 0x00623, 0x00624, 0x00624, 0x00625, 0x00625, 0x00626, + 0x00626, 0x00626, 0x00626, 0x00627, 0x00627, 0x00628, 0x00628, 0x00628, + 0x00628, 0x00629, 0x00629, 0x0062a, 0x0062a, 0x0062a, 0x0062a, 0x0062b, + 0x0062b, 0x0062b, 0x0062b, 0x0062c, 0x0062c, 0x0062c, 0x0062c, 0x0062d, + 0x0062d, 0x0062d, 0x0062d, 0x0062e, 0x0062e, 0x0062e, 0x0062e, 0x0062f, + 0x0062f, 0x00630, 0x00630, 0x00631, 0x00631, 0x00632, 0x00632, 0x00633, + 0x00633, 0x00633, 0x00633, 0x00634, 0x00634, 0x00634, 0x00634, 0x00635, + 0x00635, 0x00635, 0x00635, 0x00636, 0x00636, 0x00636, 0x00636, 0x00637, + 0x00637, 0x00637, 0x00637, 0x00638, 0x00638, 0x00638, 0x00638, 0x00639, + 0x00639, 0x00639, 0x00639, 0x0063a, 0x0063a, 0x0063a, 0x0063a, 0x00641, + 0x00641, 0x00641, 0x00641, 0x00642, 0x00642, 0x00642, 0x00642, 0x00643, + 0x00643, 0x00643, 0x00643, 0x00644, 0x00644, 0x00644, 0x00644, 0x00645, + 0x00645, 0x00645, 0x00645, 0x00646, 0x00646, 0x00646, 0x00646, 0x00647, + 0x00647, 0x00647, 0x00647, 0x00648, 0x00648, 0x00649, 0x00649, 0x0064a, + 0x0064a, 0x0064a, 0x0064a, 0x00021, 0x00022, 0x00023, 0x00024, 0x00025, + 0x00026, 0x00027, 0x00028, 0x00029, 0x0002a, 0x0002b, 0x0002c, 0x0002d, + 0x0002e, 0x0002f, 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, + 0x00036, 0x00037, 0x00038, 0x00039, 0x0003a, 0x0003b, 0x0003c, 0x0003d, + 0x0003e, 0x0003f, 0x00040, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, + 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, + 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, + 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x0005b, 0x0005c, 0x0005d, + 0x0005e, 0x0005f, 0x00060, 0x0007b, 0x0007c, 0x0007d, 0x0007e, 0x02985, + 0x02986, 0x03002, 0x0300c, 0x0300d, 0x03001, 0x030fb, 0x030f2, 0x030a1, + 0x030a3, 0x030a5, 0x030a7, 0x030a9, 0x030e3, 0x030e5, 0x030e7, 0x030c3, + 0x030fc, 0x030a2, 0x030a4, 0x030a6, 0x030a8, 0x030aa, 0x030ab, 0x030ad, + 0x030af, 0x030b1, 0x030b3, 0x030b5, 0x030b7, 0x030b9, 0x030bb, 0x030bd, + 0x030bf, 0x030c1, 0x030c4, 0x030c6, 0x030c8, 0x030ca, 0x030cb, 0x030cc, + 0x030cd, 0x030ce, 0x030cf, 0x030d2, 0x030d5, 0x030d8, 0x030db, 0x030de, + 0x030df, 0x030e0, 0x030e1, 0x030e2, 0x030e4, 0x030e6, 0x030e8, 0x030e9, + 0x030ea, 0x030eb, 0x030ec, 0x030ed, 0x030ef, 0x030f3, 0x03099, 0x0309a, + 0x03164, 0x03131, 0x03132, 0x03133, 0x03134, 0x03135, 0x03136, 0x03137, + 0x03138, 0x03139, 0x0313a, 0x0313b, 0x0313c, 0x0313d, 0x0313e, 0x0313f, + 0x03140, 0x03141, 0x03142, 0x03143, 0x03144, 0x03145, 0x03146, 0x03147, + 0x03148, 0x03149, 0x0314a, 0x0314b, 0x0314c, 0x0314d, 0x0314e, 0x0314f, + 0x03150, 0x03151, 0x03152, 0x03153, 0x03154, 0x03155, 0x03156, 0x03157, + 0x03158, 0x03159, 0x0315a, 0x0315b, 0x0315c, 0x0315d, 0x0315e, 0x0315f, + 0x03160, 0x03161, 0x03162, 0x03163, 0x000a2, 0x000a3, 0x000ac, 0x000af, + 0x000a6, 0x000a5, 0x020a9, 0x02502, 0x02190, 0x02191, 0x02192, 0x02193, + 0x025a0, 0x025cb +}; +static const uint32_t uni32_decomp_keys[] = { + 0x1d400, 0x1d401, 0x1d402, 0x1d403, 0x1d404, 0x1d405, 0x1d406, 0x1d407, + 0x1d408, 0x1d409, 0x1d40a, 0x1d40b, 0x1d40c, 0x1d40d, 0x1d40e, 0x1d40f, + 0x1d410, 0x1d411, 0x1d412, 0x1d413, 0x1d414, 0x1d415, 0x1d416, 0x1d417, + 0x1d418, 0x1d419, 0x1d41a, 0x1d41b, 0x1d41c, 0x1d41d, 0x1d41e, 0x1d41f, + 0x1d420, 0x1d421, 0x1d422, 0x1d423, 0x1d424, 0x1d425, 0x1d426, 0x1d427, + 0x1d428, 0x1d429, 0x1d42a, 0x1d42b, 0x1d42c, 0x1d42d, 0x1d42e, 0x1d42f, + 0x1d430, 0x1d431, 0x1d432, 0x1d433, 0x1d434, 0x1d435, 0x1d436, 0x1d437, + 0x1d438, 0x1d439, 0x1d43a, 0x1d43b, 0x1d43c, 0x1d43d, 0x1d43e, 0x1d43f, + 0x1d440, 0x1d441, 0x1d442, 0x1d443, 0x1d444, 0x1d445, 0x1d446, 0x1d447, + 0x1d448, 0x1d449, 0x1d44a, 0x1d44b, 0x1d44c, 0x1d44d, 0x1d44e, 0x1d44f, + 0x1d450, 0x1d451, 0x1d452, 0x1d453, 0x1d454, 0x1d456, 0x1d457, 0x1d458, + 0x1d459, 0x1d45a, 0x1d45b, 0x1d45c, 0x1d45d, 0x1d45e, 0x1d45f, 0x1d460, + 0x1d461, 0x1d462, 0x1d463, 0x1d464, 0x1d465, 0x1d466, 0x1d467, 0x1d468, + 0x1d469, 0x1d46a, 0x1d46b, 0x1d46c, 0x1d46d, 0x1d46e, 0x1d46f, 0x1d470, + 0x1d471, 0x1d472, 0x1d473, 0x1d474, 0x1d475, 0x1d476, 0x1d477, 0x1d478, + 0x1d479, 0x1d47a, 0x1d47b, 0x1d47c, 0x1d47d, 0x1d47e, 0x1d47f, 0x1d480, + 0x1d481, 0x1d482, 0x1d483, 0x1d484, 0x1d485, 0x1d486, 0x1d487, 0x1d488, + 0x1d489, 0x1d48a, 0x1d48b, 0x1d48c, 0x1d48d, 0x1d48e, 0x1d48f, 0x1d490, + 0x1d491, 0x1d492, 0x1d493, 0x1d494, 0x1d495, 0x1d496, 0x1d497, 0x1d498, + 0x1d499, 0x1d49a, 0x1d49b, 0x1d49c, 0x1d49e, 0x1d49f, 0x1d4a2, 0x1d4a5, + 0x1d4a6, 0x1d4a9, 0x1d4aa, 0x1d4ab, 0x1d4ac, 0x1d4ae, 0x1d4af, 0x1d4b0, + 0x1d4b1, 0x1d4b2, 0x1d4b3, 0x1d4b4, 0x1d4b5, 0x1d4b6, 0x1d4b7, 0x1d4b8, + 0x1d4b9, 0x1d4bb, 0x1d4bd, 0x1d4be, 0x1d4bf, 0x1d4c0, 0x1d4c1, 0x1d4c2, + 0x1d4c3, 0x1d4c5, 0x1d4c6, 0x1d4c7, 0x1d4c8, 0x1d4c9, 0x1d4ca, 0x1d4cb, + 0x1d4cc, 0x1d4cd, 0x1d4ce, 0x1d4cf, 0x1d4d0, 0x1d4d1, 0x1d4d2, 0x1d4d3, + 0x1d4d4, 0x1d4d5, 0x1d4d6, 0x1d4d7, 0x1d4d8, 0x1d4d9, 0x1d4da, 0x1d4db, + 0x1d4dc, 0x1d4dd, 0x1d4de, 0x1d4df, 0x1d4e0, 0x1d4e1, 0x1d4e2, 0x1d4e3, + 0x1d4e4, 0x1d4e5, 0x1d4e6, 0x1d4e7, 0x1d4e8, 0x1d4e9, 0x1d4ea, 0x1d4eb, + 0x1d4ec, 0x1d4ed, 0x1d4ee, 0x1d4ef, 0x1d4f0, 0x1d4f1, 0x1d4f2, 0x1d4f3, + 0x1d4f4, 0x1d4f5, 0x1d4f6, 0x1d4f7, 0x1d4f8, 0x1d4f9, 0x1d4fa, 0x1d4fb, + 0x1d4fc, 0x1d4fd, 0x1d4fe, 0x1d4ff, 0x1d500, 0x1d501, 0x1d502, 0x1d503, + 0x1d504, 0x1d505, 0x1d507, 0x1d508, 0x1d509, 0x1d50a, 0x1d50d, 0x1d50e, + 0x1d50f, 0x1d510, 0x1d511, 0x1d512, 0x1d513, 0x1d514, 0x1d516, 0x1d517, + 0x1d518, 0x1d519, 0x1d51a, 0x1d51b, 0x1d51c, 0x1d51e, 0x1d51f, 0x1d520, + 0x1d521, 0x1d522, 0x1d523, 0x1d524, 0x1d525, 0x1d526, 0x1d527, 0x1d528, + 0x1d529, 0x1d52a, 0x1d52b, 0x1d52c, 0x1d52d, 0x1d52e, 0x1d52f, 0x1d530, + 0x1d531, 0x1d532, 0x1d533, 0x1d534, 0x1d535, 0x1d536, 0x1d537, 0x1d538, + 0x1d539, 0x1d53b, 0x1d53c, 0x1d53d, 0x1d53e, 0x1d540, 0x1d541, 0x1d542, + 0x1d543, 0x1d544, 0x1d546, 0x1d54a, 0x1d54b, 0x1d54c, 0x1d54d, 0x1d54e, + 0x1d54f, 0x1d550, 0x1d552, 0x1d553, 0x1d554, 0x1d555, 0x1d556, 0x1d557, + 0x1d558, 0x1d559, 0x1d55a, 0x1d55b, 0x1d55c, 0x1d55d, 0x1d55e, 0x1d55f, + 0x1d560, 0x1d561, 0x1d562, 0x1d563, 0x1d564, 0x1d565, 0x1d566, 0x1d567, + 0x1d568, 0x1d569, 0x1d56a, 0x1d56b, 0x1d56c, 0x1d56d, 0x1d56e, 0x1d56f, + 0x1d570, 0x1d571, 0x1d572, 0x1d573, 0x1d574, 0x1d575, 0x1d576, 0x1d577, + 0x1d578, 0x1d579, 0x1d57a, 0x1d57b, 0x1d57c, 0x1d57d, 0x1d57e, 0x1d57f, + 0x1d580, 0x1d581, 0x1d582, 0x1d583, 0x1d584, 0x1d585, 0x1d586, 0x1d587, + 0x1d588, 0x1d589, 0x1d58a, 0x1d58b, 0x1d58c, 0x1d58d, 0x1d58e, 0x1d58f, + 0x1d590, 0x1d591, 0x1d592, 0x1d593, 0x1d594, 0x1d595, 0x1d596, 0x1d597, + 0x1d598, 0x1d599, 0x1d59a, 0x1d59b, 0x1d59c, 0x1d59d, 0x1d59e, 0x1d59f, + 0x1d5a0, 0x1d5a1, 0x1d5a2, 0x1d5a3, 0x1d5a4, 0x1d5a5, 0x1d5a6, 0x1d5a7, + 0x1d5a8, 0x1d5a9, 0x1d5aa, 0x1d5ab, 0x1d5ac, 0x1d5ad, 0x1d5ae, 0x1d5af, + 0x1d5b0, 0x1d5b1, 0x1d5b2, 0x1d5b3, 0x1d5b4, 0x1d5b5, 0x1d5b6, 0x1d5b7, + 0x1d5b8, 0x1d5b9, 0x1d5ba, 0x1d5bb, 0x1d5bc, 0x1d5bd, 0x1d5be, 0x1d5bf, + 0x1d5c0, 0x1d5c1, 0x1d5c2, 0x1d5c3, 0x1d5c4, 0x1d5c5, 0x1d5c6, 0x1d5c7, + 0x1d5c8, 0x1d5c9, 0x1d5ca, 0x1d5cb, 0x1d5cc, 0x1d5cd, 0x1d5ce, 0x1d5cf, + 0x1d5d0, 0x1d5d1, 0x1d5d2, 0x1d5d3, 0x1d5d4, 0x1d5d5, 0x1d5d6, 0x1d5d7, + 0x1d5d8, 0x1d5d9, 0x1d5da, 0x1d5db, 0x1d5dc, 0x1d5dd, 0x1d5de, 0x1d5df, + 0x1d5e0, 0x1d5e1, 0x1d5e2, 0x1d5e3, 0x1d5e4, 0x1d5e5, 0x1d5e6, 0x1d5e7, + 0x1d5e8, 0x1d5e9, 0x1d5ea, 0x1d5eb, 0x1d5ec, 0x1d5ed, 0x1d5ee, 0x1d5ef, + 0x1d5f0, 0x1d5f1, 0x1d5f2, 0x1d5f3, 0x1d5f4, 0x1d5f5, 0x1d5f6, 0x1d5f7, + 0x1d5f8, 0x1d5f9, 0x1d5fa, 0x1d5fb, 0x1d5fc, 0x1d5fd, 0x1d5fe, 0x1d5ff, + 0x1d600, 0x1d601, 0x1d602, 0x1d603, 0x1d604, 0x1d605, 0x1d606, 0x1d607, + 0x1d608, 0x1d609, 0x1d60a, 0x1d60b, 0x1d60c, 0x1d60d, 0x1d60e, 0x1d60f, + 0x1d610, 0x1d611, 0x1d612, 0x1d613, 0x1d614, 0x1d615, 0x1d616, 0x1d617, + 0x1d618, 0x1d619, 0x1d61a, 0x1d61b, 0x1d61c, 0x1d61d, 0x1d61e, 0x1d61f, + 0x1d620, 0x1d621, 0x1d622, 0x1d623, 0x1d624, 0x1d625, 0x1d626, 0x1d627, + 0x1d628, 0x1d629, 0x1d62a, 0x1d62b, 0x1d62c, 0x1d62d, 0x1d62e, 0x1d62f, + 0x1d630, 0x1d631, 0x1d632, 0x1d633, 0x1d634, 0x1d635, 0x1d636, 0x1d637, + 0x1d638, 0x1d639, 0x1d63a, 0x1d63b, 0x1d63c, 0x1d63d, 0x1d63e, 0x1d63f, + 0x1d640, 0x1d641, 0x1d642, 0x1d643, 0x1d644, 0x1d645, 0x1d646, 0x1d647, + 0x1d648, 0x1d649, 0x1d64a, 0x1d64b, 0x1d64c, 0x1d64d, 0x1d64e, 0x1d64f, + 0x1d650, 0x1d651, 0x1d652, 0x1d653, 0x1d654, 0x1d655, 0x1d656, 0x1d657, + 0x1d658, 0x1d659, 0x1d65a, 0x1d65b, 0x1d65c, 0x1d65d, 0x1d65e, 0x1d65f, + 0x1d660, 0x1d661, 0x1d662, 0x1d663, 0x1d664, 0x1d665, 0x1d666, 0x1d667, + 0x1d668, 0x1d669, 0x1d66a, 0x1d66b, 0x1d66c, 0x1d66d, 0x1d66e, 0x1d66f, + 0x1d670, 0x1d671, 0x1d672, 0x1d673, 0x1d674, 0x1d675, 0x1d676, 0x1d677, + 0x1d678, 0x1d679, 0x1d67a, 0x1d67b, 0x1d67c, 0x1d67d, 0x1d67e, 0x1d67f, + 0x1d680, 0x1d681, 0x1d682, 0x1d683, 0x1d684, 0x1d685, 0x1d686, 0x1d687, + 0x1d688, 0x1d689, 0x1d68a, 0x1d68b, 0x1d68c, 0x1d68d, 0x1d68e, 0x1d68f, + 0x1d690, 0x1d691, 0x1d692, 0x1d693, 0x1d694, 0x1d695, 0x1d696, 0x1d697, + 0x1d698, 0x1d699, 0x1d69a, 0x1d69b, 0x1d69c, 0x1d69d, 0x1d69e, 0x1d69f, + 0x1d6a0, 0x1d6a1, 0x1d6a2, 0x1d6a3, 0x1d6a4, 0x1d6a5, 0x1d6a8, 0x1d6a9, + 0x1d6aa, 0x1d6ab, 0x1d6ac, 0x1d6ad, 0x1d6ae, 0x1d6af, 0x1d6b0, 0x1d6b1, + 0x1d6b2, 0x1d6b3, 0x1d6b4, 0x1d6b5, 0x1d6b6, 0x1d6b7, 0x1d6b8, 0x1d6b9, + 0x1d6ba, 0x1d6bb, 0x1d6bc, 0x1d6bd, 0x1d6be, 0x1d6bf, 0x1d6c0, 0x1d6c1, + 0x1d6c2, 0x1d6c3, 0x1d6c4, 0x1d6c5, 0x1d6c6, 0x1d6c7, 0x1d6c8, 0x1d6c9, + 0x1d6ca, 0x1d6cb, 0x1d6cc, 0x1d6cd, 0x1d6ce, 0x1d6cf, 0x1d6d0, 0x1d6d1, + 0x1d6d2, 0x1d6d3, 0x1d6d4, 0x1d6d5, 0x1d6d6, 0x1d6d7, 0x1d6d8, 0x1d6d9, + 0x1d6da, 0x1d6db, 0x1d6dc, 0x1d6dd, 0x1d6de, 0x1d6df, 0x1d6e0, 0x1d6e1, + 0x1d6e2, 0x1d6e3, 0x1d6e4, 0x1d6e5, 0x1d6e6, 0x1d6e7, 0x1d6e8, 0x1d6e9, + 0x1d6ea, 0x1d6eb, 0x1d6ec, 0x1d6ed, 0x1d6ee, 0x1d6ef, 0x1d6f0, 0x1d6f1, + 0x1d6f2, 0x1d6f3, 0x1d6f4, 0x1d6f5, 0x1d6f6, 0x1d6f7, 0x1d6f8, 0x1d6f9, + 0x1d6fa, 0x1d6fb, 0x1d6fc, 0x1d6fd, 0x1d6fe, 0x1d6ff, 0x1d700, 0x1d701, + 0x1d702, 0x1d703, 0x1d704, 0x1d705, 0x1d706, 0x1d707, 0x1d708, 0x1d709, + 0x1d70a, 0x1d70b, 0x1d70c, 0x1d70d, 0x1d70e, 0x1d70f, 0x1d710, 0x1d711, + 0x1d712, 0x1d713, 0x1d714, 0x1d715, 0x1d716, 0x1d717, 0x1d718, 0x1d719, + 0x1d71a, 0x1d71b, 0x1d71c, 0x1d71d, 0x1d71e, 0x1d71f, 0x1d720, 0x1d721, + 0x1d722, 0x1d723, 0x1d724, 0x1d725, 0x1d726, 0x1d727, 0x1d728, 0x1d729, + 0x1d72a, 0x1d72b, 0x1d72c, 0x1d72d, 0x1d72e, 0x1d72f, 0x1d730, 0x1d731, + 0x1d732, 0x1d733, 0x1d734, 0x1d735, 0x1d736, 0x1d737, 0x1d738, 0x1d739, + 0x1d73a, 0x1d73b, 0x1d73c, 0x1d73d, 0x1d73e, 0x1d73f, 0x1d740, 0x1d741, + 0x1d742, 0x1d743, 0x1d744, 0x1d745, 0x1d746, 0x1d747, 0x1d748, 0x1d749, + 0x1d74a, 0x1d74b, 0x1d74c, 0x1d74d, 0x1d74e, 0x1d74f, 0x1d750, 0x1d751, + 0x1d752, 0x1d753, 0x1d754, 0x1d755, 0x1d756, 0x1d757, 0x1d758, 0x1d759, + 0x1d75a, 0x1d75b, 0x1d75c, 0x1d75d, 0x1d75e, 0x1d75f, 0x1d760, 0x1d761, + 0x1d762, 0x1d763, 0x1d764, 0x1d765, 0x1d766, 0x1d767, 0x1d768, 0x1d769, + 0x1d76a, 0x1d76b, 0x1d76c, 0x1d76d, 0x1d76e, 0x1d76f, 0x1d770, 0x1d771, + 0x1d772, 0x1d773, 0x1d774, 0x1d775, 0x1d776, 0x1d777, 0x1d778, 0x1d779, + 0x1d77a, 0x1d77b, 0x1d77c, 0x1d77d, 0x1d77e, 0x1d77f, 0x1d780, 0x1d781, + 0x1d782, 0x1d783, 0x1d784, 0x1d785, 0x1d786, 0x1d787, 0x1d788, 0x1d789, + 0x1d78a, 0x1d78b, 0x1d78c, 0x1d78d, 0x1d78e, 0x1d78f, 0x1d790, 0x1d791, + 0x1d792, 0x1d793, 0x1d794, 0x1d795, 0x1d796, 0x1d797, 0x1d798, 0x1d799, + 0x1d79a, 0x1d79b, 0x1d79c, 0x1d79d, 0x1d79e, 0x1d79f, 0x1d7a0, 0x1d7a1, + 0x1d7a2, 0x1d7a3, 0x1d7a4, 0x1d7a5, 0x1d7a6, 0x1d7a7, 0x1d7a8, 0x1d7a9, + 0x1d7aa, 0x1d7ab, 0x1d7ac, 0x1d7ad, 0x1d7ae, 0x1d7af, 0x1d7b0, 0x1d7b1, + 0x1d7b2, 0x1d7b3, 0x1d7b4, 0x1d7b5, 0x1d7b6, 0x1d7b7, 0x1d7b8, 0x1d7b9, + 0x1d7ba, 0x1d7bb, 0x1d7bc, 0x1d7bd, 0x1d7be, 0x1d7bf, 0x1d7c0, 0x1d7c1, + 0x1d7c2, 0x1d7c3, 0x1d7c4, 0x1d7c5, 0x1d7c6, 0x1d7c7, 0x1d7c8, 0x1d7c9, + 0x1d7ca, 0x1d7cb, 0x1d7ce, 0x1d7cf, 0x1d7d0, 0x1d7d1, 0x1d7d2, 0x1d7d3, + 0x1d7d4, 0x1d7d5, 0x1d7d6, 0x1d7d7, 0x1d7d8, 0x1d7d9, 0x1d7da, 0x1d7db, + 0x1d7dc, 0x1d7dd, 0x1d7de, 0x1d7df, 0x1d7e0, 0x1d7e1, 0x1d7e2, 0x1d7e3, + 0x1d7e4, 0x1d7e5, 0x1d7e6, 0x1d7e7, 0x1d7e8, 0x1d7e9, 0x1d7ea, 0x1d7eb, + 0x1d7ec, 0x1d7ed, 0x1d7ee, 0x1d7ef, 0x1d7f0, 0x1d7f1, 0x1d7f2, 0x1d7f3, + 0x1d7f4, 0x1d7f5, 0x1d7f6, 0x1d7f7, 0x1d7f8, 0x1d7f9, 0x1d7fa, 0x1d7fb, + 0x1d7fc, 0x1d7fd, 0x1d7fe, 0x1d7ff, 0x1ee00, 0x1ee01, 0x1ee02, 0x1ee03, + 0x1ee05, 0x1ee06, 0x1ee07, 0x1ee08, 0x1ee09, 0x1ee0a, 0x1ee0b, 0x1ee0c, + 0x1ee0d, 0x1ee0e, 0x1ee0f, 0x1ee10, 0x1ee11, 0x1ee12, 0x1ee13, 0x1ee14, + 0x1ee15, 0x1ee16, 0x1ee17, 0x1ee18, 0x1ee19, 0x1ee1a, 0x1ee1b, 0x1ee1c, + 0x1ee1d, 0x1ee1e, 0x1ee1f, 0x1ee21, 0x1ee22, 0x1ee24, 0x1ee27, 0x1ee29, + 0x1ee2a, 0x1ee2b, 0x1ee2c, 0x1ee2d, 0x1ee2e, 0x1ee2f, 0x1ee30, 0x1ee31, + 0x1ee32, 0x1ee34, 0x1ee35, 0x1ee36, 0x1ee37, 0x1ee39, 0x1ee3b, 0x1ee42, + 0x1ee47, 0x1ee49, 0x1ee4b, 0x1ee4d, 0x1ee4e, 0x1ee4f, 0x1ee51, 0x1ee52, + 0x1ee54, 0x1ee57, 0x1ee59, 0x1ee5b, 0x1ee5d, 0x1ee5f, 0x1ee61, 0x1ee62, + 0x1ee64, 0x1ee67, 0x1ee68, 0x1ee69, 0x1ee6a, 0x1ee6c, 0x1ee6d, 0x1ee6e, + 0x1ee6f, 0x1ee70, 0x1ee71, 0x1ee72, 0x1ee74, 0x1ee75, 0x1ee76, 0x1ee77, + 0x1ee79, 0x1ee7a, 0x1ee7b, 0x1ee7c, 0x1ee7e, 0x1ee80, 0x1ee81, 0x1ee82, + 0x1ee83, 0x1ee84, 0x1ee85, 0x1ee86, 0x1ee87, 0x1ee88, 0x1ee89, 0x1ee8b, + 0x1ee8c, 0x1ee8d, 0x1ee8e, 0x1ee8f, 0x1ee90, 0x1ee91, 0x1ee92, 0x1ee93, + 0x1ee94, 0x1ee95, 0x1ee96, 0x1ee97, 0x1ee98, 0x1ee99, 0x1ee9a, 0x1ee9b, + 0x1eea1, 0x1eea2, 0x1eea3, 0x1eea5, 0x1eea6, 0x1eea7, 0x1eea8, 0x1eea9, + 0x1eeab, 0x1eeac, 0x1eead, 0x1eeae, 0x1eeaf, 0x1eeb0, 0x1eeb1, 0x1eeb2, + 0x1eeb3, 0x1eeb4, 0x1eeb5, 0x1eeb6, 0x1eeb7, 0x1eeb8, 0x1eeb9, 0x1eeba, + 0x1eebb, 0x1f12b, 0x1f12c, 0x1f130, 0x1f131, 0x1f132, 0x1f133, 0x1f134, + 0x1f135, 0x1f136, 0x1f137, 0x1f138, 0x1f139, 0x1f13a, 0x1f13b, 0x1f13c, + 0x1f13d, 0x1f13e, 0x1f13f, 0x1f140, 0x1f141, 0x1f142, 0x1f143, 0x1f144, + 0x1f145, 0x1f146, 0x1f147, 0x1f148, 0x1f149, 0x1f202, 0x1f210, 0x1f211, + 0x1f212, 0x1f213, 0x1f214, 0x1f215, 0x1f216, 0x1f217, 0x1f218, 0x1f219, + 0x1f21a, 0x1f21b, 0x1f21c, 0x1f21d, 0x1f21e, 0x1f21f, 0x1f220, 0x1f221, + 0x1f222, 0x1f223, 0x1f224, 0x1f225, 0x1f226, 0x1f227, 0x1f228, 0x1f229, + 0x1f22a, 0x1f22b, 0x1f22c, 0x1f22d, 0x1f22e, 0x1f22f, 0x1f230, 0x1f231, + 0x1f232, 0x1f233, 0x1f234, 0x1f235, 0x1f236, 0x1f237, 0x1f238, 0x1f239, + 0x1f23a, 0x1f23b, 0x1f250, 0x1f251, 0x2f800, 0x2f801, 0x2f802, 0x2f803, + 0x2f804, 0x2f805, 0x2f806, 0x2f807, 0x2f808, 0x2f809, 0x2f80a, 0x2f80b, + 0x2f80c, 0x2f80d, 0x2f80e, 0x2f80f, 0x2f810, 0x2f811, 0x2f812, 0x2f813, + 0x2f814, 0x2f815, 0x2f816, 0x2f817, 0x2f818, 0x2f819, 0x2f81a, 0x2f81b, + 0x2f81c, 0x2f81d, 0x2f81e, 0x2f81f, 0x2f820, 0x2f821, 0x2f822, 0x2f823, + 0x2f824, 0x2f825, 0x2f826, 0x2f827, 0x2f828, 0x2f829, 0x2f82a, 0x2f82b, + 0x2f82c, 0x2f82d, 0x2f82e, 0x2f82f, 0x2f830, 0x2f831, 0x2f832, 0x2f833, + 0x2f834, 0x2f835, 0x2f836, 0x2f837, 0x2f838, 0x2f839, 0x2f83a, 0x2f83b, + 0x2f83c, 0x2f83d, 0x2f83e, 0x2f83f, 0x2f840, 0x2f841, 0x2f842, 0x2f843, + 0x2f844, 0x2f845, 0x2f846, 0x2f847, 0x2f848, 0x2f849, 0x2f84a, 0x2f84b, + 0x2f84c, 0x2f84d, 0x2f84e, 0x2f84f, 0x2f850, 0x2f851, 0x2f852, 0x2f853, + 0x2f854, 0x2f855, 0x2f856, 0x2f857, 0x2f858, 0x2f859, 0x2f85a, 0x2f85b, + 0x2f85c, 0x2f85d, 0x2f85e, 0x2f85f, 0x2f860, 0x2f861, 0x2f862, 0x2f863, + 0x2f864, 0x2f865, 0x2f866, 0x2f867, 0x2f868, 0x2f869, 0x2f86a, 0x2f86b, + 0x2f86c, 0x2f86d, 0x2f86e, 0x2f86f, 0x2f870, 0x2f871, 0x2f872, 0x2f873, + 0x2f874, 0x2f875, 0x2f876, 0x2f877, 0x2f878, 0x2f879, 0x2f87a, 0x2f87b, + 0x2f87c, 0x2f87d, 0x2f87e, 0x2f87f, 0x2f880, 0x2f881, 0x2f882, 0x2f883, + 0x2f884, 0x2f885, 0x2f886, 0x2f887, 0x2f888, 0x2f889, 0x2f88a, 0x2f88b, + 0x2f88c, 0x2f88d, 0x2f88e, 0x2f88f, 0x2f890, 0x2f891, 0x2f892, 0x2f893, + 0x2f894, 0x2f895, 0x2f896, 0x2f897, 0x2f898, 0x2f899, 0x2f89a, 0x2f89b, + 0x2f89c, 0x2f89d, 0x2f89e, 0x2f89f, 0x2f8a0, 0x2f8a1, 0x2f8a2, 0x2f8a3, + 0x2f8a4, 0x2f8a5, 0x2f8a6, 0x2f8a7, 0x2f8a8, 0x2f8a9, 0x2f8aa, 0x2f8ab, + 0x2f8ac, 0x2f8ad, 0x2f8ae, 0x2f8af, 0x2f8b0, 0x2f8b1, 0x2f8b2, 0x2f8b3, + 0x2f8b4, 0x2f8b5, 0x2f8b6, 0x2f8b7, 0x2f8b8, 0x2f8b9, 0x2f8ba, 0x2f8bb, + 0x2f8bc, 0x2f8bd, 0x2f8be, 0x2f8bf, 0x2f8c0, 0x2f8c1, 0x2f8c2, 0x2f8c3, + 0x2f8c4, 0x2f8c5, 0x2f8c6, 0x2f8c7, 0x2f8c8, 0x2f8c9, 0x2f8ca, 0x2f8cb, + 0x2f8cc, 0x2f8cd, 0x2f8ce, 0x2f8cf, 0x2f8d0, 0x2f8d1, 0x2f8d2, 0x2f8d3, + 0x2f8d4, 0x2f8d5, 0x2f8d6, 0x2f8d7, 0x2f8d8, 0x2f8d9, 0x2f8da, 0x2f8db, + 0x2f8dc, 0x2f8dd, 0x2f8de, 0x2f8df, 0x2f8e0, 0x2f8e1, 0x2f8e2, 0x2f8e3, + 0x2f8e4, 0x2f8e5, 0x2f8e6, 0x2f8e7, 0x2f8e8, 0x2f8e9, 0x2f8ea, 0x2f8eb, + 0x2f8ec, 0x2f8ed, 0x2f8ee, 0x2f8ef, 0x2f8f0, 0x2f8f1, 0x2f8f2, 0x2f8f3, + 0x2f8f4, 0x2f8f5, 0x2f8f6, 0x2f8f7, 0x2f8f8, 0x2f8f9, 0x2f8fa, 0x2f8fb, + 0x2f8fc, 0x2f8fd, 0x2f8fe, 0x2f8ff, 0x2f900, 0x2f901, 0x2f902, 0x2f903, + 0x2f904, 0x2f905, 0x2f906, 0x2f907, 0x2f908, 0x2f909, 0x2f90a, 0x2f90b, + 0x2f90c, 0x2f90d, 0x2f90e, 0x2f90f, 0x2f910, 0x2f911, 0x2f912, 0x2f913, + 0x2f914, 0x2f915, 0x2f916, 0x2f917, 0x2f918, 0x2f919, 0x2f91a, 0x2f91b, + 0x2f91c, 0x2f91d, 0x2f91e, 0x2f91f, 0x2f920, 0x2f921, 0x2f922, 0x2f923, + 0x2f924, 0x2f925, 0x2f926, 0x2f927, 0x2f928, 0x2f929, 0x2f92a, 0x2f92b, + 0x2f92c, 0x2f92d, 0x2f92e, 0x2f92f, 0x2f930, 0x2f931, 0x2f932, 0x2f933, + 0x2f934, 0x2f935, 0x2f936, 0x2f937, 0x2f938, 0x2f939, 0x2f93a, 0x2f93b, + 0x2f93c, 0x2f93d, 0x2f93e, 0x2f93f, 0x2f940, 0x2f941, 0x2f942, 0x2f943, + 0x2f944, 0x2f945, 0x2f946, 0x2f947, 0x2f948, 0x2f949, 0x2f94a, 0x2f94b, + 0x2f94c, 0x2f94d, 0x2f94e, 0x2f94f, 0x2f950, 0x2f951, 0x2f952, 0x2f953, + 0x2f954, 0x2f955, 0x2f956, 0x2f957, 0x2f958, 0x2f959, 0x2f95a, 0x2f95b, + 0x2f95c, 0x2f95d, 0x2f95e, 0x2f95f, 0x2f960, 0x2f961, 0x2f962, 0x2f963, + 0x2f964, 0x2f965, 0x2f966, 0x2f967, 0x2f968, 0x2f969, 0x2f96a, 0x2f96b, + 0x2f96c, 0x2f96d, 0x2f96e, 0x2f96f, 0x2f970, 0x2f971, 0x2f972, 0x2f973, + 0x2f974, 0x2f975, 0x2f976, 0x2f977, 0x2f978, 0x2f979, 0x2f97a, 0x2f97b, + 0x2f97c, 0x2f97d, 0x2f97e, 0x2f97f, 0x2f980, 0x2f981, 0x2f982, 0x2f983, + 0x2f984, 0x2f985, 0x2f986, 0x2f987, 0x2f988, 0x2f989, 0x2f98a, 0x2f98b, + 0x2f98c, 0x2f98d, 0x2f98e, 0x2f98f, 0x2f990, 0x2f991, 0x2f992, 0x2f993, + 0x2f994, 0x2f995, 0x2f996, 0x2f997, 0x2f998, 0x2f999, 0x2f99a, 0x2f99b, + 0x2f99c, 0x2f99d, 0x2f99e, 0x2f99f, 0x2f9a0, 0x2f9a1, 0x2f9a2, 0x2f9a3, + 0x2f9a4, 0x2f9a5, 0x2f9a6, 0x2f9a7, 0x2f9a8, 0x2f9a9, 0x2f9aa, 0x2f9ab, + 0x2f9ac, 0x2f9ad, 0x2f9ae, 0x2f9af, 0x2f9b0, 0x2f9b1, 0x2f9b2, 0x2f9b3, + 0x2f9b4, 0x2f9b5, 0x2f9b6, 0x2f9b7, 0x2f9b8, 0x2f9b9, 0x2f9ba, 0x2f9bb, + 0x2f9bc, 0x2f9bd, 0x2f9be, 0x2f9bf, 0x2f9c0, 0x2f9c1, 0x2f9c2, 0x2f9c3, + 0x2f9c4, 0x2f9c5, 0x2f9c6, 0x2f9c7, 0x2f9c8, 0x2f9c9, 0x2f9ca, 0x2f9cb, + 0x2f9cc, 0x2f9cd, 0x2f9ce, 0x2f9cf, 0x2f9d0, 0x2f9d1, 0x2f9d2, 0x2f9d3, + 0x2f9d4, 0x2f9d5, 0x2f9d6, 0x2f9d7, 0x2f9d8, 0x2f9d9, 0x2f9da, 0x2f9db, + 0x2f9dc, 0x2f9dd, 0x2f9de, 0x2f9df, 0x2f9e0, 0x2f9e1, 0x2f9e2, 0x2f9e3, + 0x2f9e4, 0x2f9e5, 0x2f9e6, 0x2f9e7, 0x2f9e8, 0x2f9e9, 0x2f9ea, 0x2f9eb, + 0x2f9ec, 0x2f9ed, 0x2f9ee, 0x2f9ef, 0x2f9f0, 0x2f9f1, 0x2f9f2, 0x2f9f3, + 0x2f9f4, 0x2f9f5, 0x2f9f6, 0x2f9f7, 0x2f9f8, 0x2f9f9, 0x2f9fa, 0x2f9fb, + 0x2f9fc, 0x2f9fd, 0x2f9fe, 0x2f9ff, 0x2fa00, 0x2fa01, 0x2fa02, 0x2fa03, + 0x2fa04, 0x2fa05, 0x2fa06, 0x2fa07, 0x2fa08, 0x2fa09, 0x2fa0a, 0x2fa0b, + 0x2fa0c, 0x2fa0d, 0x2fa0e, 0x2fa0f, 0x2fa10, 0x2fa11, 0x2fa12, 0x2fa13, + 0x2fa14, 0x2fa15, 0x2fa16, 0x2fa17, 0x2fa18, 0x2fa19, 0x2fa1a, 0x2fa1b, + 0x2fa1c, 0x2fa1d +}; +static const uint32_t uni32_decomp_values[] = { + 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, + 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, + 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, + 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, + 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, + 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, + 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00043, 0x00044, + 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, + 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, + 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, + 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00069, 0x0006a, 0x0006b, + 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, + 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, + 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, + 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, + 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, + 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, + 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, + 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, + 0x00078, 0x00079, 0x0007a, 0x00041, 0x00043, 0x00044, 0x00047, 0x0004a, + 0x0004b, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00053, 0x00054, 0x00055, + 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, + 0x00064, 0x00066, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, + 0x0006e, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, + 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00043, 0x00044, + 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, + 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, + 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, + 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, + 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, + 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, + 0x00041, 0x00042, 0x00044, 0x00045, 0x00046, 0x00047, 0x0004a, 0x0004b, + 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00053, 0x00054, + 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x00061, 0x00062, 0x00063, + 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, + 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, + 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, + 0x00042, 0x00044, 0x00045, 0x00046, 0x00047, 0x00049, 0x0004a, 0x0004b, + 0x0004c, 0x0004d, 0x0004f, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, + 0x00058, 0x00059, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, + 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, + 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, + 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00043, 0x00044, + 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, + 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, + 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, + 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, + 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, + 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, + 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, + 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, + 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, + 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, + 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, + 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, + 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00043, 0x00044, + 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, + 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, + 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, + 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, + 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, + 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, + 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, + 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, + 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, + 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, + 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, + 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, + 0x00077, 0x00078, 0x00079, 0x0007a, 0x00041, 0x00042, 0x00043, 0x00044, + 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, + 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, + 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x00061, 0x00062, + 0x00063, 0x00064, 0x00065, 0x00066, 0x00067, 0x00068, 0x00069, 0x0006a, + 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f, 0x00070, 0x00071, 0x00072, + 0x00073, 0x00074, 0x00075, 0x00076, 0x00077, 0x00078, 0x00079, 0x0007a, + 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, + 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, + 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, + 0x00059, 0x0005a, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, + 0x00067, 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, + 0x0006f, 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, + 0x00077, 0x00078, 0x00079, 0x0007a, 0x00131, 0x00237, 0x00391, 0x00392, + 0x00393, 0x00394, 0x00395, 0x00396, 0x00397, 0x00398, 0x00399, 0x0039a, + 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f, 0x003a0, 0x003a1, 0x003f4, + 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7, 0x003a8, 0x003a9, 0x02207, + 0x003b1, 0x003b2, 0x003b3, 0x003b4, 0x003b5, 0x003b6, 0x003b7, 0x003b8, + 0x003b9, 0x003ba, 0x003bb, 0x003bc, 0x003bd, 0x003be, 0x003bf, 0x003c0, + 0x003c1, 0x003c2, 0x003c3, 0x003c4, 0x003c5, 0x003c6, 0x003c7, 0x003c8, + 0x003c9, 0x02202, 0x003f5, 0x003d1, 0x003f0, 0x003d5, 0x003f1, 0x003d6, + 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397, 0x00398, + 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f, 0x003a0, + 0x003a1, 0x003f4, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7, 0x003a8, + 0x003a9, 0x02207, 0x003b1, 0x003b2, 0x003b3, 0x003b4, 0x003b5, 0x003b6, + 0x003b7, 0x003b8, 0x003b9, 0x003ba, 0x003bb, 0x003bc, 0x003bd, 0x003be, + 0x003bf, 0x003c0, 0x003c1, 0x003c2, 0x003c3, 0x003c4, 0x003c5, 0x003c6, + 0x003c7, 0x003c8, 0x003c9, 0x02202, 0x003f5, 0x003d1, 0x003f0, 0x003d5, + 0x003f1, 0x003d6, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, + 0x00397, 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, + 0x0039f, 0x003a0, 0x003a1, 0x003f4, 0x003a3, 0x003a4, 0x003a5, 0x003a6, + 0x003a7, 0x003a8, 0x003a9, 0x02207, 0x003b1, 0x003b2, 0x003b3, 0x003b4, + 0x003b5, 0x003b6, 0x003b7, 0x003b8, 0x003b9, 0x003ba, 0x003bb, 0x003bc, + 0x003bd, 0x003be, 0x003bf, 0x003c0, 0x003c1, 0x003c2, 0x003c3, 0x003c4, + 0x003c5, 0x003c6, 0x003c7, 0x003c8, 0x003c9, 0x02202, 0x003f5, 0x003d1, + 0x003f0, 0x003d5, 0x003f1, 0x003d6, 0x00391, 0x00392, 0x00393, 0x00394, + 0x00395, 0x00396, 0x00397, 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, + 0x0039d, 0x0039e, 0x0039f, 0x003a0, 0x003a1, 0x003f4, 0x003a3, 0x003a4, + 0x003a5, 0x003a6, 0x003a7, 0x003a8, 0x003a9, 0x02207, 0x003b1, 0x003b2, + 0x003b3, 0x003b4, 0x003b5, 0x003b6, 0x003b7, 0x003b8, 0x003b9, 0x003ba, + 0x003bb, 0x003bc, 0x003bd, 0x003be, 0x003bf, 0x003c0, 0x003c1, 0x003c2, + 0x003c3, 0x003c4, 0x003c5, 0x003c6, 0x003c7, 0x003c8, 0x003c9, 0x02202, + 0x003f5, 0x003d1, 0x003f0, 0x003d5, 0x003f1, 0x003d6, 0x00391, 0x00392, + 0x00393, 0x00394, 0x00395, 0x00396, 0x00397, 0x00398, 0x00399, 0x0039a, + 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f, 0x003a0, 0x003a1, 0x003f4, + 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7, 0x003a8, 0x003a9, 0x02207, + 0x003b1, 0x003b2, 0x003b3, 0x003b4, 0x003b5, 0x003b6, 0x003b7, 0x003b8, + 0x003b9, 0x003ba, 0x003bb, 0x003bc, 0x003bd, 0x003be, 0x003bf, 0x003c0, + 0x003c1, 0x003c2, 0x003c3, 0x003c4, 0x003c5, 0x003c6, 0x003c7, 0x003c8, + 0x003c9, 0x02202, 0x003f5, 0x003d1, 0x003f0, 0x003d5, 0x003f1, 0x003d6, + 0x003dc, 0x003dd, 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, + 0x00036, 0x00037, 0x00038, 0x00039, 0x00030, 0x00031, 0x00032, 0x00033, + 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x00030, 0x00031, + 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, + 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, + 0x00038, 0x00039, 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, + 0x00036, 0x00037, 0x00038, 0x00039, 0x00627, 0x00628, 0x0062c, 0x0062f, + 0x00648, 0x00632, 0x0062d, 0x00637, 0x0064a, 0x00643, 0x00644, 0x00645, + 0x00646, 0x00633, 0x00639, 0x00641, 0x00635, 0x00642, 0x00631, 0x00634, + 0x0062a, 0x0062b, 0x0062e, 0x00630, 0x00636, 0x00638, 0x0063a, 0x0066e, + 0x006ba, 0x006a1, 0x0066f, 0x00628, 0x0062c, 0x00647, 0x0062d, 0x0064a, + 0x00643, 0x00644, 0x00645, 0x00646, 0x00633, 0x00639, 0x00641, 0x00635, + 0x00642, 0x00634, 0x0062a, 0x0062b, 0x0062e, 0x00636, 0x0063a, 0x0062c, + 0x0062d, 0x0064a, 0x00644, 0x00646, 0x00633, 0x00639, 0x00635, 0x00642, + 0x00634, 0x0062e, 0x00636, 0x0063a, 0x006ba, 0x0066f, 0x00628, 0x0062c, + 0x00647, 0x0062d, 0x00637, 0x0064a, 0x00643, 0x00645, 0x00646, 0x00633, + 0x00639, 0x00641, 0x00635, 0x00642, 0x00634, 0x0062a, 0x0062b, 0x0062e, + 0x00636, 0x00638, 0x0063a, 0x0066e, 0x006a1, 0x00627, 0x00628, 0x0062c, + 0x0062f, 0x00647, 0x00648, 0x00632, 0x0062d, 0x00637, 0x0064a, 0x00644, + 0x00645, 0x00646, 0x00633, 0x00639, 0x00641, 0x00635, 0x00642, 0x00631, + 0x00634, 0x0062a, 0x0062b, 0x0062e, 0x00630, 0x00636, 0x00638, 0x0063a, + 0x00628, 0x0062c, 0x0062f, 0x00648, 0x00632, 0x0062d, 0x00637, 0x0064a, + 0x00644, 0x00645, 0x00646, 0x00633, 0x00639, 0x00641, 0x00635, 0x00642, + 0x00631, 0x00634, 0x0062a, 0x0062b, 0x0062e, 0x00630, 0x00636, 0x00638, + 0x0063a, 0x00043, 0x00052, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, + 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, + 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, + 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x030b5, 0x0624b, 0x05b57, + 0x053cc, 0x030c7, 0x04e8c, 0x0591a, 0x089e3, 0x05929, 0x04ea4, 0x06620, + 0x07121, 0x06599, 0x0524d, 0x05f8c, 0x0518d, 0x065b0, 0x0521d, 0x07d42, + 0x0751f, 0x08ca9, 0x058f0, 0x05439, 0x06f14, 0x06295, 0x06355, 0x04e00, + 0x04e09, 0x0904a, 0x05de6, 0x04e2d, 0x053f3, 0x06307, 0x08d70, 0x06253, + 0x07981, 0x07a7a, 0x05408, 0x06e80, 0x06709, 0x06708, 0x07533, 0x05272, + 0x055b6, 0x0914d, 0x05f97, 0x053ef, 0x04e3d, 0x04e38, 0x04e41, 0x20122, + 0x04f60, 0x04fae, 0x04fbb, 0x05002, 0x0507a, 0x05099, 0x050e7, 0x050cf, + 0x0349e, 0x2063a, 0x0514d, 0x05154, 0x05164, 0x05177, 0x2051c, 0x034b9, + 0x05167, 0x0518d, 0x2054b, 0x05197, 0x051a4, 0x04ecc, 0x051ac, 0x051b5, + 0x291df, 0x051f5, 0x05203, 0x034df, 0x0523b, 0x05246, 0x05272, 0x05277, + 0x03515, 0x052c7, 0x052c9, 0x052e4, 0x052fa, 0x05305, 0x05306, 0x05317, + 0x05349, 0x05351, 0x0535a, 0x05373, 0x0537d, 0x0537f, 0x0537f, 0x0537f, + 0x20a2c, 0x07070, 0x053ca, 0x053df, 0x20b63, 0x053eb, 0x053f1, 0x05406, + 0x0549e, 0x05438, 0x05448, 0x05468, 0x054a2, 0x054f6, 0x05510, 0x05553, + 0x05563, 0x05584, 0x05584, 0x05599, 0x055ab, 0x055b3, 0x055c2, 0x05716, + 0x05606, 0x05717, 0x05651, 0x05674, 0x05207, 0x058ee, 0x057ce, 0x057f4, + 0x0580d, 0x0578b, 0x05832, 0x05831, 0x058ac, 0x214e4, 0x058f2, 0x058f7, + 0x05906, 0x0591a, 0x05922, 0x05962, 0x216a8, 0x216ea, 0x059ec, 0x05a1b, + 0x05a27, 0x059d8, 0x05a66, 0x036ee, 0x036fc, 0x05b08, 0x05b3e, 0x05b3e, + 0x219c8, 0x05bc3, 0x05bd8, 0x05be7, 0x05bf3, 0x21b18, 0x05bff, 0x05c06, + 0x05f53, 0x05c22, 0x03781, 0x05c60, 0x05c6e, 0x05cc0, 0x05c8d, 0x21de4, + 0x05d43, 0x21de6, 0x05d6e, 0x05d6b, 0x05d7c, 0x05de1, 0x05de2, 0x0382f, + 0x05dfd, 0x05e28, 0x05e3d, 0x05e69, 0x03862, 0x22183, 0x0387c, 0x05eb0, + 0x05eb3, 0x05eb6, 0x05eca, 0x2a392, 0x05efe, 0x22331, 0x22331, 0x08201, + 0x05f22, 0x05f22, 0x038c7, 0x232b8, 0x261da, 0x05f62, 0x05f6b, 0x038e3, + 0x05f9a, 0x05fcd, 0x05fd7, 0x05ff9, 0x06081, 0x0393a, 0x0391c, 0x06094, + 0x226d4, 0x060c7, 0x06148, 0x0614c, 0x0614e, 0x0614c, 0x0617a, 0x0618e, + 0x061b2, 0x061a4, 0x061af, 0x061de, 0x061f2, 0x061f6, 0x06210, 0x0621b, + 0x0625d, 0x062b1, 0x062d4, 0x06350, 0x22b0c, 0x0633d, 0x062fc, 0x06368, + 0x06383, 0x063e4, 0x22bf1, 0x06422, 0x063c5, 0x063a9, 0x03a2e, 0x06469, + 0x0647e, 0x0649d, 0x06477, 0x03a6c, 0x0654f, 0x0656c, 0x2300a, 0x065e3, + 0x066f8, 0x06649, 0x03b19, 0x06691, 0x03b08, 0x03ae4, 0x05192, 0x05195, + 0x06700, 0x0669c, 0x080ad, 0x043d9, 0x06717, 0x0671b, 0x06721, 0x0675e, + 0x06753, 0x233c3, 0x03b49, 0x067fa, 0x06785, 0x06852, 0x06885, 0x2346d, + 0x0688e, 0x0681f, 0x06914, 0x03b9d, 0x06942, 0x069a3, 0x069ea, 0x06aa8, + 0x236a3, 0x06adb, 0x03c18, 0x06b21, 0x238a7, 0x06b54, 0x03c4e, 0x06b72, + 0x06b9f, 0x06bba, 0x06bbb, 0x23a8d, 0x21d0b, 0x23afa, 0x06c4e, 0x23cbc, + 0x06cbf, 0x06ccd, 0x06c67, 0x06d16, 0x06d3e, 0x06d77, 0x06d41, 0x06d69, + 0x06d78, 0x06d85, 0x23d1e, 0x06d34, 0x06e2f, 0x06e6e, 0x03d33, 0x06ecb, + 0x06ec7, 0x23ed1, 0x06df9, 0x06f6e, 0x23f5e, 0x23f8e, 0x06fc6, 0x07039, + 0x0701e, 0x0701b, 0x03d96, 0x0704a, 0x0707d, 0x07077, 0x070ad, 0x20525, + 0x07145, 0x24263, 0x0719c, 0x243ab, 0x07228, 0x07235, 0x07250, 0x24608, + 0x07280, 0x07295, 0x24735, 0x24814, 0x0737a, 0x0738b, 0x03eac, 0x073a5, + 0x03eb8, 0x03eb8, 0x07447, 0x0745c, 0x07471, 0x07485, 0x074ca, 0x03f1b, + 0x07524, 0x24c36, 0x0753e, 0x24c92, 0x07570, 0x2219f, 0x07610, 0x24fa1, + 0x24fb8, 0x25044, 0x03ffc, 0x04008, 0x076f4, 0x250f3, 0x250f2, 0x25119, + 0x25133, 0x0771e, 0x0771f, 0x0771f, 0x0774a, 0x04039, 0x0778b, 0x04046, + 0x04096, 0x2541d, 0x0784e, 0x0788c, 0x078cc, 0x040e3, 0x25626, 0x07956, + 0x2569a, 0x256c5, 0x0798f, 0x079eb, 0x0412f, 0x07a40, 0x07a4a, 0x07a4f, + 0x2597c, 0x25aa7, 0x25aa7, 0x07aee, 0x04202, 0x25bab, 0x07bc6, 0x07bc9, + 0x04227, 0x25c80, 0x07cd2, 0x042a0, 0x07ce8, 0x07ce3, 0x07d00, 0x25f86, + 0x07d63, 0x04301, 0x07dc7, 0x07e02, 0x07e45, 0x04334, 0x26228, 0x26247, + 0x04359, 0x262d9, 0x07f7a, 0x2633e, 0x07f95, 0x07ffa, 0x08005, 0x264da, + 0x26523, 0x08060, 0x265a8, 0x08070, 0x2335f, 0x043d5, 0x080b2, 0x08103, + 0x0440b, 0x0813e, 0x05ab5, 0x267a7, 0x267b5, 0x23393, 0x2339c, 0x08201, + 0x08204, 0x08f9e, 0x0446b, 0x08291, 0x0828b, 0x0829d, 0x052b3, 0x082b1, + 0x082b3, 0x082bd, 0x082e6, 0x26b3c, 0x082e5, 0x0831d, 0x08363, 0x083ad, + 0x08323, 0x083bd, 0x083e7, 0x08457, 0x08353, 0x083ca, 0x083cc, 0x083dc, + 0x26c36, 0x26d6b, 0x26cd5, 0x0452b, 0x084f1, 0x084f3, 0x08516, 0x273ca, + 0x08564, 0x26f2c, 0x0455d, 0x04561, 0x26fb1, 0x270d2, 0x0456b, 0x08650, + 0x0865c, 0x08667, 0x08669, 0x086a9, 0x08688, 0x0870e, 0x086e2, 0x08779, + 0x08728, 0x0876b, 0x08786, 0x045d7, 0x087e1, 0x08801, 0x045f9, 0x08860, + 0x08863, 0x27667, 0x088d7, 0x088de, 0x04635, 0x088fa, 0x034bb, 0x278ae, + 0x27966, 0x046be, 0x046c7, 0x08aa0, 0x08aed, 0x08b8a, 0x08c55, 0x27ca8, + 0x08cab, 0x08cc1, 0x08d1b, 0x08d77, 0x27f2f, 0x20804, 0x08dcb, 0x08dbc, + 0x08df0, 0x208de, 0x08ed4, 0x08f38, 0x285d2, 0x285ed, 0x09094, 0x090f1, + 0x09111, 0x2872e, 0x0911b, 0x09238, 0x092d7, 0x092d8, 0x0927c, 0x093f9, + 0x09415, 0x28bfa, 0x0958b, 0x04995, 0x095b7, 0x28d77, 0x049e6, 0x096c3, + 0x05db2, 0x09723, 0x29145, 0x2921a, 0x04a6e, 0x04a76, 0x097e0, 0x2940a, + 0x04ab2, 0x29496, 0x0980b, 0x0980b, 0x09829, 0x295b6, 0x098e2, 0x04b33, + 0x09929, 0x099a7, 0x099c2, 0x099fe, 0x04bce, 0x29b30, 0x09b12, 0x09c40, + 0x09cfd, 0x04cce, 0x04ced, 0x09d67, 0x2a0ce, 0x04cf8, 0x2a105, 0x2a20e, + 0x2a291, 0x09ebb, 0x04d56, 0x09ef9, 0x09efe, 0x09f05, 0x09f0f, 0x09f16, + 0x09f3b, 0x2a600 +}; +static const uint32_t multidecomp_keys[] = { + 0x000a8, 0x000af, 0x000b4, 0x000b8, 0x000bc, 0x000bd, 0x000be, 0x000c0, + 0x000c1, 0x000c2, 0x000c3, 0x000c4, 0x000c5, 0x000c7, 0x000c8, 0x000c9, + 0x000ca, 0x000cb, 0x000cc, 0x000cd, 0x000ce, 0x000cf, 0x000d1, 0x000d2, + 0x000d3, 0x000d4, 0x000d5, 0x000d6, 0x000d9, 0x000da, 0x000db, 0x000dc, + 0x000dd, 0x00100, 0x00102, 0x00104, 0x00106, 0x00108, 0x0010a, 0x0010c, + 0x0010e, 0x00112, 0x00114, 0x00116, 0x00118, 0x0011a, 0x0011c, 0x0011e, + 0x00120, 0x00122, 0x00124, 0x00128, 0x0012a, 0x0012c, 0x0012e, 0x00130, + 0x00132, 0x00134, 0x00136, 0x00139, 0x0013b, 0x0013d, 0x0013f, 0x00143, + 0x00145, 0x00147, 0x00149, 0x0014c, 0x0014e, 0x00150, 0x00154, 0x00156, + 0x00158, 0x0015a, 0x0015c, 0x0015e, 0x00160, 0x00162, 0x00164, 0x00168, + 0x0016a, 0x0016c, 0x0016e, 0x00170, 0x00172, 0x00174, 0x00176, 0x00178, + 0x00179, 0x0017b, 0x0017d, 0x001a0, 0x001af, 0x001cd, 0x001cf, 0x001d1, + 0x001d3, 0x001d5, 0x001d7, 0x001d9, 0x001db, 0x001de, 0x001e0, 0x001e2, + 0x001e6, 0x001e8, 0x001ea, 0x001ec, 0x001ee, 0x001f0, 0x001f4, 0x001f8, + 0x001fa, 0x001fc, 0x001fe, 0x00200, 0x00202, 0x00204, 0x00206, 0x00208, + 0x0020a, 0x0020c, 0x0020e, 0x00210, 0x00212, 0x00214, 0x00216, 0x00218, + 0x0021a, 0x0021e, 0x00226, 0x00228, 0x0022a, 0x0022c, 0x0022e, 0x00230, + 0x00232, 0x002d8, 0x002d9, 0x002da, 0x002db, 0x002dc, 0x002dd, 0x00344, + 0x0037a, 0x00384, 0x00385, 0x00386, 0x00388, 0x00389, 0x0038a, 0x0038c, + 0x0038e, 0x0038f, 0x00390, 0x003aa, 0x003ab, 0x003b0, 0x003d3, 0x003d4, + 0x00400, 0x00401, 0x00403, 0x00407, 0x0040c, 0x0040d, 0x0040e, 0x00419, + 0x00476, 0x004c1, 0x004d0, 0x004d2, 0x004d6, 0x004da, 0x004dc, 0x004de, + 0x004e2, 0x004e4, 0x004e6, 0x004ea, 0x004ec, 0x004ee, 0x004f0, 0x004f2, + 0x004f4, 0x004f8, 0x00587, 0x00622, 0x00623, 0x00624, 0x00625, 0x00626, + 0x00675, 0x00676, 0x00677, 0x00678, 0x006c0, 0x006c2, 0x006d3, 0x00929, + 0x00931, 0x00934, 0x00958, 0x00959, 0x0095a, 0x0095b, 0x0095c, 0x0095d, + 0x0095e, 0x0095f, 0x009cb, 0x009cc, 0x009dc, 0x009dd, 0x009df, 0x00a33, + 0x00a36, 0x00a59, 0x00a5a, 0x00a5b, 0x00a5e, 0x00b48, 0x00b4b, 0x00b4c, + 0x00b5c, 0x00b5d, 0x00b94, 0x00bca, 0x00bcb, 0x00bcc, 0x00c48, 0x00cc0, + 0x00cc7, 0x00cc8, 0x00cca, 0x00ccb, 0x00d4a, 0x00d4b, 0x00d4c, 0x00dda, + 0x00ddc, 0x00ddd, 0x00dde, 0x00e33, 0x00eb3, 0x00edc, 0x00edd, 0x00f43, + 0x00f4d, 0x00f52, 0x00f57, 0x00f5c, 0x00f69, 0x00f73, 0x00f75, 0x00f76, + 0x00f77, 0x00f78, 0x00f79, 0x00f81, 0x00f93, 0x00f9d, 0x00fa2, 0x00fa7, + 0x00fac, 0x00fb9, 0x01026, 0x01b06, 0x01b08, 0x01b0a, 0x01b0c, 0x01b0e, + 0x01b12, 0x01b3b, 0x01b3d, 0x01b40, 0x01b41, 0x01b43, 0x01e00, 0x01e02, + 0x01e04, 0x01e06, 0x01e08, 0x01e0a, 0x01e0c, 0x01e0e, 0x01e10, 0x01e12, + 0x01e14, 0x01e16, 0x01e18, 0x01e1a, 0x01e1c, 0x01e1e, 0x01e20, 0x01e22, + 0x01e24, 0x01e26, 0x01e28, 0x01e2a, 0x01e2c, 0x01e2e, 0x01e30, 0x01e32, + 0x01e34, 0x01e36, 0x01e38, 0x01e3a, 0x01e3c, 0x01e3e, 0x01e40, 0x01e42, + 0x01e44, 0x01e46, 0x01e48, 0x01e4a, 0x01e4c, 0x01e4e, 0x01e50, 0x01e52, + 0x01e54, 0x01e56, 0x01e58, 0x01e5a, 0x01e5c, 0x01e5e, 0x01e60, 0x01e62, + 0x01e64, 0x01e66, 0x01e68, 0x01e6a, 0x01e6c, 0x01e6e, 0x01e70, 0x01e72, + 0x01e74, 0x01e76, 0x01e78, 0x01e7a, 0x01e7c, 0x01e7e, 0x01e80, 0x01e82, + 0x01e84, 0x01e86, 0x01e88, 0x01e8a, 0x01e8c, 0x01e8e, 0x01e90, 0x01e92, + 0x01e94, 0x01e96, 0x01e97, 0x01e98, 0x01e99, 0x01e9a, 0x01ea0, 0x01ea2, + 0x01ea4, 0x01ea6, 0x01ea8, 0x01eaa, 0x01eac, 0x01eae, 0x01eb0, 0x01eb2, + 0x01eb4, 0x01eb6, 0x01eb8, 0x01eba, 0x01ebc, 0x01ebe, 0x01ec0, 0x01ec2, + 0x01ec4, 0x01ec6, 0x01ec8, 0x01eca, 0x01ecc, 0x01ece, 0x01ed0, 0x01ed2, + 0x01ed4, 0x01ed6, 0x01ed8, 0x01eda, 0x01edc, 0x01ede, 0x01ee0, 0x01ee2, + 0x01ee4, 0x01ee6, 0x01ee8, 0x01eea, 0x01eec, 0x01eee, 0x01ef0, 0x01ef2, + 0x01ef4, 0x01ef6, 0x01ef8, 0x01f08, 0x01f09, 0x01f0a, 0x01f0b, 0x01f0c, + 0x01f0d, 0x01f0e, 0x01f0f, 0x01f18, 0x01f19, 0x01f1a, 0x01f1b, 0x01f1c, + 0x01f1d, 0x01f28, 0x01f29, 0x01f2a, 0x01f2b, 0x01f2c, 0x01f2d, 0x01f2e, + 0x01f2f, 0x01f38, 0x01f39, 0x01f3a, 0x01f3b, 0x01f3c, 0x01f3d, 0x01f3e, + 0x01f3f, 0x01f48, 0x01f49, 0x01f4a, 0x01f4b, 0x01f4c, 0x01f4d, 0x01f50, + 0x01f52, 0x01f54, 0x01f56, 0x01f59, 0x01f5b, 0x01f5d, 0x01f5f, 0x01f68, + 0x01f69, 0x01f6a, 0x01f6b, 0x01f6c, 0x01f6d, 0x01f6e, 0x01f6f, 0x01f88, + 0x01f89, 0x01f8a, 0x01f8b, 0x01f8c, 0x01f8d, 0x01f8e, 0x01f8f, 0x01f98, + 0x01f99, 0x01f9a, 0x01f9b, 0x01f9c, 0x01f9d, 0x01f9e, 0x01f9f, 0x01fa8, + 0x01fa9, 0x01faa, 0x01fab, 0x01fac, 0x01fad, 0x01fae, 0x01faf, 0x01fb2, + 0x01fb4, 0x01fb6, 0x01fb7, 0x01fb8, 0x01fb9, 0x01fba, 0x01fbc, 0x01fbd, + 0x01fbf, 0x01fc0, 0x01fc1, 0x01fc2, 0x01fc4, 0x01fc6, 0x01fc7, 0x01fc8, + 0x01fca, 0x01fcc, 0x01fcd, 0x01fce, 0x01fcf, 0x01fd2, 0x01fd6, 0x01fd7, + 0x01fd8, 0x01fd9, 0x01fda, 0x01fdd, 0x01fde, 0x01fdf, 0x01fe2, 0x01fe4, + 0x01fe6, 0x01fe7, 0x01fe8, 0x01fe9, 0x01fea, 0x01fec, 0x01fed, 0x01ff2, + 0x01ff4, 0x01ff6, 0x01ff7, 0x01ff8, 0x01ffa, 0x01ffc, 0x01ffe, 0x02017, + 0x02025, 0x02026, 0x02033, 0x02034, 0x02036, 0x02037, 0x0203c, 0x0203e, + 0x02047, 0x02048, 0x02049, 0x02057, 0x020a8, 0x02100, 0x02101, 0x02103, + 0x02105, 0x02106, 0x02109, 0x02116, 0x02120, 0x02121, 0x02122, 0x0213b, + 0x02150, 0x02151, 0x02152, 0x02153, 0x02154, 0x02155, 0x02156, 0x02157, + 0x02158, 0x02159, 0x0215a, 0x0215b, 0x0215c, 0x0215d, 0x0215e, 0x0215f, + 0x02161, 0x02162, 0x02163, 0x02165, 0x02166, 0x02167, 0x02168, 0x0216a, + 0x0216b, 0x02189, 0x0219a, 0x0219b, 0x021ae, 0x021cd, 0x021ce, 0x021cf, + 0x02204, 0x02209, 0x0220c, 0x02224, 0x02226, 0x0222c, 0x0222d, 0x0222f, + 0x02230, 0x02241, 0x02244, 0x02247, 0x02249, 0x02260, 0x02262, 0x0226d, + 0x0226e, 0x0226f, 0x02270, 0x02271, 0x02274, 0x02275, 0x02278, 0x02279, + 0x02280, 0x02281, 0x02284, 0x02285, 0x02288, 0x02289, 0x022ac, 0x022ad, + 0x022ae, 0x022af, 0x022e0, 0x022e1, 0x022e2, 0x022e3, 0x022ea, 0x022eb, + 0x022ec, 0x022ed, 0x02469, 0x0246a, 0x0246b, 0x0246c, 0x0246d, 0x0246e, + 0x0246f, 0x02470, 0x02471, 0x02472, 0x02473, 0x02474, 0x02475, 0x02476, + 0x02477, 0x02478, 0x02479, 0x0247a, 0x0247b, 0x0247c, 0x0247d, 0x0247e, + 0x0247f, 0x02480, 0x02481, 0x02482, 0x02483, 0x02484, 0x02485, 0x02486, + 0x02487, 0x02488, 0x02489, 0x0248a, 0x0248b, 0x0248c, 0x0248d, 0x0248e, + 0x0248f, 0x02490, 0x02491, 0x02492, 0x02493, 0x02494, 0x02495, 0x02496, + 0x02497, 0x02498, 0x02499, 0x0249a, 0x0249b, 0x0249c, 0x0249d, 0x0249e, + 0x0249f, 0x024a0, 0x024a1, 0x024a2, 0x024a3, 0x024a4, 0x024a5, 0x024a6, + 0x024a7, 0x024a8, 0x024a9, 0x024aa, 0x024ab, 0x024ac, 0x024ad, 0x024ae, + 0x024af, 0x024b0, 0x024b1, 0x024b2, 0x024b3, 0x024b4, 0x024b5, 0x02a0c, + 0x02a74, 0x02a75, 0x02a76, 0x02adc, 0x0304c, 0x0304e, 0x03050, 0x03052, + 0x03054, 0x03056, 0x03058, 0x0305a, 0x0305c, 0x0305e, 0x03060, 0x03062, + 0x03065, 0x03067, 0x03069, 0x03070, 0x03071, 0x03073, 0x03074, 0x03076, + 0x03077, 0x03079, 0x0307a, 0x0307c, 0x0307d, 0x03094, 0x0309b, 0x0309c, + 0x0309e, 0x0309f, 0x030ac, 0x030ae, 0x030b0, 0x030b2, 0x030b4, 0x030b6, + 0x030b8, 0x030ba, 0x030bc, 0x030be, 0x030c0, 0x030c2, 0x030c5, 0x030c7, + 0x030c9, 0x030d0, 0x030d1, 0x030d3, 0x030d4, 0x030d6, 0x030d7, 0x030d9, + 0x030da, 0x030dc, 0x030dd, 0x030f4, 0x030f7, 0x030f8, 0x030f9, 0x030fa, + 0x030fe, 0x030ff, 0x03200, 0x03201, 0x03202, 0x03203, 0x03204, 0x03205, + 0x03206, 0x03207, 0x03208, 0x03209, 0x0320a, 0x0320b, 0x0320c, 0x0320d, + 0x0320e, 0x0320f, 0x03210, 0x03211, 0x03212, 0x03213, 0x03214, 0x03215, + 0x03216, 0x03217, 0x03218, 0x03219, 0x0321a, 0x0321b, 0x0321c, 0x0321d, + 0x0321e, 0x03220, 0x03221, 0x03222, 0x03223, 0x03224, 0x03225, 0x03226, + 0x03227, 0x03228, 0x03229, 0x0322a, 0x0322b, 0x0322c, 0x0322d, 0x0322e, + 0x0322f, 0x03230, 0x03231, 0x03232, 0x03233, 0x03234, 0x03235, 0x03236, + 0x03237, 0x03238, 0x03239, 0x0323a, 0x0323b, 0x0323c, 0x0323d, 0x0323e, + 0x0323f, 0x03240, 0x03241, 0x03242, 0x03243, 0x03250, 0x03251, 0x03252, + 0x03253, 0x03254, 0x03255, 0x03256, 0x03257, 0x03258, 0x03259, 0x0325a, + 0x0325b, 0x0325c, 0x0325d, 0x0325e, 0x0325f, 0x0326e, 0x0326f, 0x03270, + 0x03271, 0x03272, 0x03273, 0x03274, 0x03275, 0x03276, 0x03277, 0x03278, + 0x03279, 0x0327a, 0x0327b, 0x0327c, 0x0327d, 0x0327e, 0x032b1, 0x032b2, + 0x032b3, 0x032b4, 0x032b5, 0x032b6, 0x032b7, 0x032b8, 0x032b9, 0x032ba, + 0x032bb, 0x032bc, 0x032bd, 0x032be, 0x032bf, 0x032c0, 0x032c1, 0x032c2, + 0x032c3, 0x032c4, 0x032c5, 0x032c6, 0x032c7, 0x032c8, 0x032c9, 0x032ca, + 0x032cb, 0x032cc, 0x032cd, 0x032ce, 0x032cf, 0x03300, 0x03301, 0x03302, + 0x03303, 0x03304, 0x03305, 0x03306, 0x03307, 0x03308, 0x03309, 0x0330a, + 0x0330b, 0x0330c, 0x0330d, 0x0330e, 0x0330f, 0x03310, 0x03311, 0x03312, + 0x03313, 0x03314, 0x03315, 0x03316, 0x03317, 0x03318, 0x03319, 0x0331a, + 0x0331b, 0x0331c, 0x0331d, 0x0331e, 0x0331f, 0x03320, 0x03321, 0x03322, + 0x03323, 0x03324, 0x03325, 0x03326, 0x03327, 0x03328, 0x03329, 0x0332a, + 0x0332b, 0x0332c, 0x0332d, 0x0332e, 0x0332f, 0x03330, 0x03331, 0x03332, + 0x03333, 0x03334, 0x03335, 0x03336, 0x03337, 0x03338, 0x03339, 0x0333a, + 0x0333b, 0x0333c, 0x0333d, 0x0333e, 0x0333f, 0x03340, 0x03341, 0x03342, + 0x03343, 0x03344, 0x03345, 0x03346, 0x03347, 0x03348, 0x03349, 0x0334a, + 0x0334b, 0x0334c, 0x0334d, 0x0334e, 0x0334f, 0x03350, 0x03351, 0x03352, + 0x03353, 0x03354, 0x03355, 0x03356, 0x03357, 0x03358, 0x03359, 0x0335a, + 0x0335b, 0x0335c, 0x0335d, 0x0335e, 0x0335f, 0x03360, 0x03361, 0x03362, + 0x03363, 0x03364, 0x03365, 0x03366, 0x03367, 0x03368, 0x03369, 0x0336a, + 0x0336b, 0x0336c, 0x0336d, 0x0336e, 0x0336f, 0x03370, 0x03371, 0x03372, + 0x03373, 0x03374, 0x03375, 0x03376, 0x03377, 0x03378, 0x03379, 0x0337a, + 0x0337b, 0x0337c, 0x0337d, 0x0337e, 0x0337f, 0x03380, 0x03381, 0x03382, + 0x03383, 0x03384, 0x03385, 0x03386, 0x03387, 0x03388, 0x03389, 0x0338a, + 0x0338b, 0x0338c, 0x0338d, 0x0338e, 0x0338f, 0x03390, 0x03391, 0x03392, + 0x03393, 0x03394, 0x03395, 0x03396, 0x03397, 0x03398, 0x03399, 0x0339a, + 0x0339b, 0x0339c, 0x0339d, 0x0339e, 0x0339f, 0x033a0, 0x033a1, 0x033a2, + 0x033a3, 0x033a4, 0x033a5, 0x033a6, 0x033a7, 0x033a8, 0x033a9, 0x033aa, + 0x033ab, 0x033ac, 0x033ad, 0x033ae, 0x033af, 0x033b0, 0x033b1, 0x033b2, + 0x033b3, 0x033b4, 0x033b5, 0x033b6, 0x033b7, 0x033b8, 0x033b9, 0x033ba, + 0x033bb, 0x033bc, 0x033bd, 0x033be, 0x033bf, 0x033c0, 0x033c1, 0x033c2, + 0x033c3, 0x033c4, 0x033c5, 0x033c6, 0x033c7, 0x033c8, 0x033c9, 0x033ca, + 0x033cb, 0x033cc, 0x033cd, 0x033ce, 0x033cf, 0x033d0, 0x033d1, 0x033d2, + 0x033d3, 0x033d4, 0x033d5, 0x033d6, 0x033d7, 0x033d8, 0x033d9, 0x033da, + 0x033db, 0x033dc, 0x033dd, 0x033de, 0x033df, 0x033e0, 0x033e1, 0x033e2, + 0x033e3, 0x033e4, 0x033e5, 0x033e6, 0x033e7, 0x033e8, 0x033e9, 0x033ea, + 0x033eb, 0x033ec, 0x033ed, 0x033ee, 0x033ef, 0x033f0, 0x033f1, 0x033f2, + 0x033f3, 0x033f4, 0x033f5, 0x033f6, 0x033f7, 0x033f8, 0x033f9, 0x033fa, + 0x033fb, 0x033fc, 0x033fd, 0x033fe, 0x033ff, 0x0fb00, 0x0fb01, 0x0fb02, + 0x0fb03, 0x0fb04, 0x0fb05, 0x0fb06, 0x0fb13, 0x0fb14, 0x0fb15, 0x0fb16, + 0x0fb17, 0x0fb1d, 0x0fb1f, 0x0fb2a, 0x0fb2b, 0x0fb2c, 0x0fb2d, 0x0fb2e, + 0x0fb2f, 0x0fb30, 0x0fb31, 0x0fb32, 0x0fb33, 0x0fb34, 0x0fb35, 0x0fb36, + 0x0fb38, 0x0fb39, 0x0fb3a, 0x0fb3b, 0x0fb3c, 0x0fb3e, 0x0fb40, 0x0fb41, + 0x0fb43, 0x0fb44, 0x0fb46, 0x0fb47, 0x0fb48, 0x0fb49, 0x0fb4a, 0x0fb4b, + 0x0fb4c, 0x0fb4d, 0x0fb4e, 0x0fb4f, 0x0fbea, 0x0fbeb, 0x0fbec, 0x0fbed, + 0x0fbee, 0x0fbef, 0x0fbf0, 0x0fbf1, 0x0fbf2, 0x0fbf3, 0x0fbf4, 0x0fbf5, + 0x0fbf6, 0x0fbf7, 0x0fbf8, 0x0fbf9, 0x0fbfa, 0x0fbfb, 0x0fc00, 0x0fc01, + 0x0fc02, 0x0fc03, 0x0fc04, 0x0fc05, 0x0fc06, 0x0fc07, 0x0fc08, 0x0fc09, + 0x0fc0a, 0x0fc0b, 0x0fc0c, 0x0fc0d, 0x0fc0e, 0x0fc0f, 0x0fc10, 0x0fc11, + 0x0fc12, 0x0fc13, 0x0fc14, 0x0fc15, 0x0fc16, 0x0fc17, 0x0fc18, 0x0fc19, + 0x0fc1a, 0x0fc1b, 0x0fc1c, 0x0fc1d, 0x0fc1e, 0x0fc1f, 0x0fc20, 0x0fc21, + 0x0fc22, 0x0fc23, 0x0fc24, 0x0fc25, 0x0fc26, 0x0fc27, 0x0fc28, 0x0fc29, + 0x0fc2a, 0x0fc2b, 0x0fc2c, 0x0fc2d, 0x0fc2e, 0x0fc2f, 0x0fc30, 0x0fc31, + 0x0fc32, 0x0fc33, 0x0fc34, 0x0fc35, 0x0fc36, 0x0fc37, 0x0fc38, 0x0fc39, + 0x0fc3a, 0x0fc3b, 0x0fc3c, 0x0fc3d, 0x0fc3e, 0x0fc3f, 0x0fc40, 0x0fc41, + 0x0fc42, 0x0fc43, 0x0fc44, 0x0fc45, 0x0fc46, 0x0fc47, 0x0fc48, 0x0fc49, + 0x0fc4a, 0x0fc4b, 0x0fc4c, 0x0fc4d, 0x0fc4e, 0x0fc4f, 0x0fc50, 0x0fc51, + 0x0fc52, 0x0fc53, 0x0fc54, 0x0fc55, 0x0fc56, 0x0fc57, 0x0fc58, 0x0fc59, + 0x0fc5a, 0x0fc5b, 0x0fc5c, 0x0fc5d, 0x0fc5e, 0x0fc5f, 0x0fc60, 0x0fc61, + 0x0fc62, 0x0fc63, 0x0fc64, 0x0fc65, 0x0fc66, 0x0fc67, 0x0fc68, 0x0fc69, + 0x0fc6a, 0x0fc6b, 0x0fc6c, 0x0fc6d, 0x0fc6e, 0x0fc6f, 0x0fc70, 0x0fc71, + 0x0fc72, 0x0fc73, 0x0fc74, 0x0fc75, 0x0fc76, 0x0fc77, 0x0fc78, 0x0fc79, + 0x0fc7a, 0x0fc7b, 0x0fc7c, 0x0fc7d, 0x0fc7e, 0x0fc7f, 0x0fc80, 0x0fc81, + 0x0fc82, 0x0fc83, 0x0fc84, 0x0fc85, 0x0fc86, 0x0fc87, 0x0fc88, 0x0fc89, + 0x0fc8a, 0x0fc8b, 0x0fc8c, 0x0fc8d, 0x0fc8e, 0x0fc8f, 0x0fc90, 0x0fc91, + 0x0fc92, 0x0fc93, 0x0fc94, 0x0fc95, 0x0fc96, 0x0fc97, 0x0fc98, 0x0fc99, + 0x0fc9a, 0x0fc9b, 0x0fc9c, 0x0fc9d, 0x0fc9e, 0x0fc9f, 0x0fca0, 0x0fca1, + 0x0fca2, 0x0fca3, 0x0fca4, 0x0fca5, 0x0fca6, 0x0fca7, 0x0fca8, 0x0fca9, + 0x0fcaa, 0x0fcab, 0x0fcac, 0x0fcad, 0x0fcae, 0x0fcaf, 0x0fcb0, 0x0fcb1, + 0x0fcb2, 0x0fcb3, 0x0fcb4, 0x0fcb5, 0x0fcb6, 0x0fcb7, 0x0fcb8, 0x0fcb9, + 0x0fcba, 0x0fcbb, 0x0fcbc, 0x0fcbd, 0x0fcbe, 0x0fcbf, 0x0fcc0, 0x0fcc1, + 0x0fcc2, 0x0fcc3, 0x0fcc4, 0x0fcc5, 0x0fcc6, 0x0fcc7, 0x0fcc8, 0x0fcc9, + 0x0fcca, 0x0fccb, 0x0fccc, 0x0fccd, 0x0fcce, 0x0fccf, 0x0fcd0, 0x0fcd1, + 0x0fcd2, 0x0fcd3, 0x0fcd4, 0x0fcd5, 0x0fcd6, 0x0fcd7, 0x0fcd8, 0x0fcd9, + 0x0fcda, 0x0fcdb, 0x0fcdc, 0x0fcdd, 0x0fcde, 0x0fcdf, 0x0fce0, 0x0fce1, + 0x0fce2, 0x0fce3, 0x0fce4, 0x0fce5, 0x0fce6, 0x0fce7, 0x0fce8, 0x0fce9, + 0x0fcea, 0x0fceb, 0x0fcec, 0x0fced, 0x0fcee, 0x0fcef, 0x0fcf0, 0x0fcf1, + 0x0fcf2, 0x0fcf3, 0x0fcf4, 0x0fcf5, 0x0fcf6, 0x0fcf7, 0x0fcf8, 0x0fcf9, + 0x0fcfa, 0x0fcfb, 0x0fcfc, 0x0fcfd, 0x0fcfe, 0x0fcff, 0x0fd00, 0x0fd01, + 0x0fd02, 0x0fd03, 0x0fd04, 0x0fd05, 0x0fd06, 0x0fd07, 0x0fd08, 0x0fd09, + 0x0fd0a, 0x0fd0b, 0x0fd0c, 0x0fd0d, 0x0fd0e, 0x0fd0f, 0x0fd10, 0x0fd11, + 0x0fd12, 0x0fd13, 0x0fd14, 0x0fd15, 0x0fd16, 0x0fd17, 0x0fd18, 0x0fd19, + 0x0fd1a, 0x0fd1b, 0x0fd1c, 0x0fd1d, 0x0fd1e, 0x0fd1f, 0x0fd20, 0x0fd21, + 0x0fd22, 0x0fd23, 0x0fd24, 0x0fd25, 0x0fd26, 0x0fd27, 0x0fd28, 0x0fd29, + 0x0fd2a, 0x0fd2b, 0x0fd2c, 0x0fd2d, 0x0fd2e, 0x0fd2f, 0x0fd30, 0x0fd31, + 0x0fd32, 0x0fd33, 0x0fd34, 0x0fd35, 0x0fd36, 0x0fd37, 0x0fd38, 0x0fd39, + 0x0fd3a, 0x0fd3b, 0x0fd3c, 0x0fd3d, 0x0fd50, 0x0fd51, 0x0fd52, 0x0fd53, + 0x0fd54, 0x0fd55, 0x0fd56, 0x0fd57, 0x0fd58, 0x0fd59, 0x0fd5a, 0x0fd5b, + 0x0fd5c, 0x0fd5d, 0x0fd5e, 0x0fd5f, 0x0fd60, 0x0fd61, 0x0fd62, 0x0fd63, + 0x0fd64, 0x0fd65, 0x0fd66, 0x0fd67, 0x0fd68, 0x0fd69, 0x0fd6a, 0x0fd6b, + 0x0fd6c, 0x0fd6d, 0x0fd6e, 0x0fd6f, 0x0fd70, 0x0fd71, 0x0fd72, 0x0fd73, + 0x0fd74, 0x0fd75, 0x0fd76, 0x0fd77, 0x0fd78, 0x0fd79, 0x0fd7a, 0x0fd7b, + 0x0fd7c, 0x0fd7d, 0x0fd7e, 0x0fd7f, 0x0fd80, 0x0fd81, 0x0fd82, 0x0fd83, + 0x0fd84, 0x0fd85, 0x0fd86, 0x0fd87, 0x0fd88, 0x0fd89, 0x0fd8a, 0x0fd8b, + 0x0fd8c, 0x0fd8d, 0x0fd8e, 0x0fd8f, 0x0fd92, 0x0fd93, 0x0fd94, 0x0fd95, + 0x0fd96, 0x0fd97, 0x0fd98, 0x0fd99, 0x0fd9a, 0x0fd9b, 0x0fd9c, 0x0fd9d, + 0x0fd9e, 0x0fd9f, 0x0fda0, 0x0fda1, 0x0fda2, 0x0fda3, 0x0fda4, 0x0fda5, + 0x0fda6, 0x0fda7, 0x0fda8, 0x0fda9, 0x0fdaa, 0x0fdab, 0x0fdac, 0x0fdad, + 0x0fdae, 0x0fdaf, 0x0fdb0, 0x0fdb1, 0x0fdb2, 0x0fdb3, 0x0fdb4, 0x0fdb5, + 0x0fdb6, 0x0fdb7, 0x0fdb8, 0x0fdb9, 0x0fdba, 0x0fdbb, 0x0fdbc, 0x0fdbd, + 0x0fdbe, 0x0fdbf, 0x0fdc0, 0x0fdc1, 0x0fdc2, 0x0fdc3, 0x0fdc4, 0x0fdc5, + 0x0fdc6, 0x0fdc7, 0x0fdf0, 0x0fdf1, 0x0fdf2, 0x0fdf3, 0x0fdf4, 0x0fdf5, + 0x0fdf6, 0x0fdf7, 0x0fdf8, 0x0fdf9, 0x0fdfa, 0x0fdfb, 0x0fdfc, 0x0fe70, + 0x0fe71, 0x0fe72, 0x0fe74, 0x0fe76, 0x0fe77, 0x0fe78, 0x0fe79, 0x0fe7a, + 0x0fe7b, 0x0fe7c, 0x0fe7d, 0x0fe7e, 0x0fe7f, 0x0fef5, 0x0fef6, 0x0fef7, + 0x0fef8, 0x0fef9, 0x0fefa, 0x0fefb, 0x0fefc, 0x1109a, 0x1109c, 0x110ab, + 0x1112e, 0x1112f, 0x1134b, 0x1134c, 0x114bb, 0x114bc, 0x114be, 0x115ba, + 0x115bb, 0x1d15e, 0x1d15f, 0x1d160, 0x1d161, 0x1d162, 0x1d163, 0x1d164, + 0x1d1bb, 0x1d1bc, 0x1d1bd, 0x1d1be, 0x1d1bf, 0x1d1c0, 0x1f100, 0x1f101, + 0x1f102, 0x1f103, 0x1f104, 0x1f105, 0x1f106, 0x1f107, 0x1f108, 0x1f109, + 0x1f10a, 0x1f110, 0x1f111, 0x1f112, 0x1f113, 0x1f114, 0x1f115, 0x1f116, + 0x1f117, 0x1f118, 0x1f119, 0x1f11a, 0x1f11b, 0x1f11c, 0x1f11d, 0x1f11e, + 0x1f11f, 0x1f120, 0x1f121, 0x1f122, 0x1f123, 0x1f124, 0x1f125, 0x1f126, + 0x1f127, 0x1f128, 0x1f129, 0x1f12a, 0x1f12d, 0x1f12e, 0x1f14a, 0x1f14b, + 0x1f14c, 0x1f14d, 0x1f14e, 0x1f14f, 0x1f16a, 0x1f16b, 0x1f190, 0x1f200, + 0x1f201, 0x1f240, 0x1f241, 0x1f242, 0x1f243, 0x1f244, 0x1f245, 0x1f246, + 0x1f247, 0x1f248 +}; +static const uint16_t multidecomp_offsets[] = { + 0x00000, 0x00003, 0x00006, 0x00009, 0x0000c, 0x00010, 0x00014, 0x00018, + 0x0001b, 0x0001e, 0x00021, 0x00024, 0x00027, 0x0002a, 0x0002d, 0x00030, + 0x00033, 0x00036, 0x00039, 0x0003c, 0x0003f, 0x00042, 0x00045, 0x00048, + 0x0004b, 0x0004e, 0x00051, 0x00054, 0x00057, 0x0005a, 0x0005d, 0x00060, + 0x00063, 0x00066, 0x00069, 0x0006c, 0x0006f, 0x00072, 0x00075, 0x00078, + 0x0007b, 0x0007e, 0x00081, 0x00084, 0x00087, 0x0008a, 0x0008d, 0x00090, + 0x00093, 0x00096, 0x00099, 0x0009c, 0x0009f, 0x000a2, 0x000a5, 0x000a8, + 0x000ab, 0x000ae, 0x000b1, 0x000b4, 0x000b7, 0x000ba, 0x000bd, 0x000c0, + 0x000c3, 0x000c6, 0x000c9, 0x000cc, 0x000cf, 0x000d2, 0x000d5, 0x000d8, + 0x000db, 0x000de, 0x000e1, 0x000e4, 0x000e7, 0x000ea, 0x000ed, 0x000f0, + 0x000f3, 0x000f6, 0x000f9, 0x000fc, 0x000ff, 0x00102, 0x00105, 0x00108, + 0x0010b, 0x0010e, 0x00111, 0x00114, 0x00117, 0x0011a, 0x0011d, 0x00120, + 0x00123, 0x00126, 0x00129, 0x0012c, 0x0012f, 0x00132, 0x00135, 0x00138, + 0x0013b, 0x0013e, 0x00141, 0x00144, 0x00147, 0x0014a, 0x0014d, 0x00150, + 0x00153, 0x00156, 0x00159, 0x0015c, 0x0015f, 0x00162, 0x00165, 0x00168, + 0x0016b, 0x0016e, 0x00171, 0x00174, 0x00177, 0x0017a, 0x0017d, 0x00180, + 0x00183, 0x00186, 0x00189, 0x0018c, 0x0018f, 0x00192, 0x00195, 0x00198, + 0x0019b, 0x0019e, 0x001a1, 0x001a4, 0x001a7, 0x001aa, 0x001ad, 0x001b0, + 0x001b3, 0x001b6, 0x001b9, 0x001bc, 0x001bf, 0x001c2, 0x001c5, 0x001c8, + 0x001cb, 0x001ce, 0x001d1, 0x001d4, 0x001d7, 0x001da, 0x001dd, 0x001e0, + 0x001e3, 0x001e6, 0x001e9, 0x001ec, 0x001ef, 0x001f2, 0x001f5, 0x001f8, + 0x001fb, 0x001fe, 0x00201, 0x00204, 0x00207, 0x0020a, 0x0020d, 0x00210, + 0x00213, 0x00216, 0x00219, 0x0021c, 0x0021f, 0x00222, 0x00225, 0x00228, + 0x0022b, 0x0022e, 0x00231, 0x00234, 0x00237, 0x0023a, 0x0023d, 0x00240, + 0x00243, 0x00246, 0x00249, 0x0024c, 0x0024f, 0x00252, 0x00255, 0x00258, + 0x0025b, 0x0025e, 0x00261, 0x00264, 0x00267, 0x0026a, 0x0026d, 0x00270, + 0x00273, 0x00276, 0x00279, 0x0027c, 0x0027f, 0x00282, 0x00285, 0x00288, + 0x0028b, 0x0028e, 0x00291, 0x00294, 0x00297, 0x0029a, 0x0029d, 0x002a0, + 0x002a3, 0x002a6, 0x002a9, 0x002ac, 0x002af, 0x002b2, 0x002b5, 0x002b8, + 0x002bb, 0x002be, 0x002c1, 0x002c4, 0x002c7, 0x002ca, 0x002cd, 0x002d0, + 0x002d3, 0x002d6, 0x002d9, 0x002dc, 0x002df, 0x002e2, 0x002e5, 0x002e8, + 0x002eb, 0x002ee, 0x002f1, 0x002f4, 0x002f7, 0x002fa, 0x002fd, 0x00300, + 0x00303, 0x00306, 0x00309, 0x0030c, 0x0030f, 0x00312, 0x00315, 0x00318, + 0x0031b, 0x0031e, 0x00321, 0x00324, 0x00327, 0x0032a, 0x0032d, 0x00330, + 0x00333, 0x00336, 0x00339, 0x0033c, 0x0033f, 0x00342, 0x00345, 0x00348, + 0x0034b, 0x0034e, 0x00351, 0x00354, 0x00357, 0x0035a, 0x0035d, 0x00360, + 0x00363, 0x00366, 0x00369, 0x0036c, 0x0036f, 0x00372, 0x00375, 0x00378, + 0x0037b, 0x0037e, 0x00381, 0x00384, 0x00387, 0x0038a, 0x0038d, 0x00390, + 0x00393, 0x00396, 0x00399, 0x0039c, 0x0039f, 0x003a2, 0x003a5, 0x003a8, + 0x003ab, 0x003ae, 0x003b1, 0x003b4, 0x003b7, 0x003ba, 0x003bd, 0x003c0, + 0x003c3, 0x003c6, 0x003c9, 0x003cc, 0x003cf, 0x003d2, 0x003d5, 0x003d8, + 0x003db, 0x003de, 0x003e1, 0x003e4, 0x003e7, 0x003ea, 0x003ed, 0x003f0, + 0x003f3, 0x003f6, 0x003f9, 0x003fc, 0x003ff, 0x00402, 0x00405, 0x00408, + 0x0040b, 0x0040e, 0x00411, 0x00414, 0x00417, 0x0041a, 0x0041d, 0x00420, + 0x00423, 0x00426, 0x00429, 0x0042c, 0x0042f, 0x00432, 0x00435, 0x00438, + 0x0043b, 0x0043e, 0x00441, 0x00444, 0x00447, 0x0044a, 0x0044d, 0x00450, + 0x00453, 0x00456, 0x00459, 0x0045c, 0x0045f, 0x00462, 0x00465, 0x00468, + 0x0046b, 0x0046e, 0x00471, 0x00474, 0x00477, 0x0047a, 0x0047d, 0x00480, + 0x00483, 0x00486, 0x00489, 0x0048c, 0x0048f, 0x00492, 0x00495, 0x00498, + 0x0049b, 0x0049e, 0x004a1, 0x004a4, 0x004a7, 0x004aa, 0x004ad, 0x004b0, + 0x004b3, 0x004b6, 0x004b9, 0x004bc, 0x004bf, 0x004c2, 0x004c5, 0x004c8, + 0x004cb, 0x004ce, 0x004d1, 0x004d4, 0x004d7, 0x004da, 0x004dd, 0x004e0, + 0x004e3, 0x004e6, 0x004e9, 0x004ec, 0x004ef, 0x004f2, 0x004f5, 0x004f8, + 0x004fb, 0x004fe, 0x00501, 0x00504, 0x00507, 0x0050a, 0x0050d, 0x00510, + 0x00513, 0x00516, 0x00519, 0x0051c, 0x0051f, 0x00522, 0x00525, 0x00528, + 0x0052b, 0x0052e, 0x00531, 0x00534, 0x00537, 0x0053a, 0x0053d, 0x00540, + 0x00543, 0x00546, 0x00549, 0x0054c, 0x0054f, 0x00552, 0x00555, 0x00558, + 0x0055b, 0x0055e, 0x00561, 0x00564, 0x00567, 0x0056a, 0x0056d, 0x00570, + 0x00573, 0x00576, 0x00579, 0x0057c, 0x0057f, 0x00582, 0x00585, 0x00588, + 0x0058b, 0x0058e, 0x00591, 0x00594, 0x00597, 0x0059a, 0x0059d, 0x005a0, + 0x005a3, 0x005a6, 0x005a9, 0x005ac, 0x005af, 0x005b2, 0x005b5, 0x005b8, + 0x005bb, 0x005be, 0x005c1, 0x005c4, 0x005c7, 0x005ca, 0x005cd, 0x005d0, + 0x005d3, 0x005d6, 0x005d9, 0x005dc, 0x005df, 0x005e2, 0x005e5, 0x005e8, + 0x005eb, 0x005ee, 0x005f1, 0x005f4, 0x005f7, 0x005fa, 0x005fd, 0x00600, + 0x00603, 0x00606, 0x00609, 0x0060c, 0x0060f, 0x00612, 0x00615, 0x00618, + 0x0061b, 0x0061e, 0x00621, 0x00624, 0x00627, 0x0062a, 0x0062d, 0x00630, + 0x00633, 0x00636, 0x0063a, 0x0063d, 0x00641, 0x00644, 0x00648, 0x0064b, + 0x0064e, 0x00651, 0x00654, 0x00657, 0x0065c, 0x0065f, 0x00663, 0x00667, + 0x0066a, 0x0066e, 0x00672, 0x00675, 0x00678, 0x0067b, 0x0067f, 0x00682, + 0x00686, 0x0068a, 0x0068e, 0x00693, 0x00697, 0x0069b, 0x0069f, 0x006a3, + 0x006a7, 0x006ab, 0x006af, 0x006b3, 0x006b7, 0x006bb, 0x006bf, 0x006c3, + 0x006c6, 0x006c9, 0x006cd, 0x006d0, 0x006d3, 0x006d7, 0x006dc, 0x006df, + 0x006e2, 0x006e6, 0x006ea, 0x006ed, 0x006f0, 0x006f3, 0x006f6, 0x006f9, + 0x006fc, 0x006ff, 0x00702, 0x00705, 0x00708, 0x0070b, 0x0070e, 0x00712, + 0x00715, 0x00719, 0x0071c, 0x0071f, 0x00722, 0x00725, 0x00728, 0x0072b, + 0x0072e, 0x00731, 0x00734, 0x00737, 0x0073a, 0x0073d, 0x00740, 0x00743, + 0x00746, 0x00749, 0x0074c, 0x0074f, 0x00752, 0x00755, 0x00758, 0x0075b, + 0x0075e, 0x00761, 0x00764, 0x00767, 0x0076a, 0x0076d, 0x00770, 0x00773, + 0x00776, 0x00779, 0x0077c, 0x0077f, 0x00782, 0x00785, 0x00788, 0x0078b, + 0x0078e, 0x00791, 0x00794, 0x00797, 0x0079a, 0x0079d, 0x007a1, 0x007a5, + 0x007a9, 0x007ad, 0x007b1, 0x007b5, 0x007b9, 0x007bd, 0x007c1, 0x007c6, + 0x007cb, 0x007d0, 0x007d5, 0x007da, 0x007df, 0x007e4, 0x007e9, 0x007ee, + 0x007f3, 0x007f8, 0x007fb, 0x007fe, 0x00801, 0x00804, 0x00807, 0x0080a, + 0x0080d, 0x00810, 0x00813, 0x00817, 0x0081b, 0x0081f, 0x00823, 0x00827, + 0x0082b, 0x0082f, 0x00833, 0x00837, 0x0083b, 0x0083f, 0x00843, 0x00847, + 0x0084b, 0x0084f, 0x00853, 0x00857, 0x0085b, 0x0085f, 0x00863, 0x00867, + 0x0086b, 0x0086f, 0x00873, 0x00877, 0x0087b, 0x0087f, 0x00883, 0x00887, + 0x0088b, 0x0088f, 0x00893, 0x00897, 0x0089b, 0x0089f, 0x008a3, 0x008a7, + 0x008ac, 0x008b0, 0x008b3, 0x008b7, 0x008ba, 0x008bd, 0x008c0, 0x008c3, + 0x008c6, 0x008c9, 0x008cc, 0x008cf, 0x008d2, 0x008d5, 0x008d8, 0x008db, + 0x008de, 0x008e1, 0x008e4, 0x008e7, 0x008ea, 0x008ed, 0x008f0, 0x008f3, + 0x008f6, 0x008f9, 0x008fc, 0x008ff, 0x00902, 0x00905, 0x00908, 0x0090b, + 0x0090e, 0x00911, 0x00914, 0x00917, 0x0091a, 0x0091d, 0x00920, 0x00923, + 0x00926, 0x00929, 0x0092c, 0x0092f, 0x00932, 0x00935, 0x00938, 0x0093b, + 0x0093e, 0x00941, 0x00944, 0x00947, 0x0094a, 0x0094d, 0x00950, 0x00953, + 0x00956, 0x00959, 0x0095c, 0x0095f, 0x00962, 0x00965, 0x00968, 0x0096b, + 0x0096e, 0x00971, 0x00974, 0x00978, 0x0097c, 0x00980, 0x00984, 0x00988, + 0x0098c, 0x00990, 0x00994, 0x00998, 0x0099c, 0x009a0, 0x009a4, 0x009a8, + 0x009ac, 0x009b1, 0x009b6, 0x009bb, 0x009c0, 0x009c5, 0x009ca, 0x009cf, + 0x009d4, 0x009d9, 0x009de, 0x009e3, 0x009e8, 0x009ed, 0x009f2, 0x009f7, + 0x009ff, 0x00a06, 0x00a0a, 0x00a0e, 0x00a12, 0x00a16, 0x00a1a, 0x00a1e, + 0x00a22, 0x00a26, 0x00a2a, 0x00a2e, 0x00a32, 0x00a36, 0x00a3a, 0x00a3e, + 0x00a42, 0x00a46, 0x00a4a, 0x00a4e, 0x00a52, 0x00a56, 0x00a5a, 0x00a5e, + 0x00a62, 0x00a66, 0x00a6a, 0x00a6e, 0x00a72, 0x00a76, 0x00a7a, 0x00a7e, + 0x00a82, 0x00a86, 0x00a8a, 0x00a8e, 0x00a92, 0x00a96, 0x00a9a, 0x00a9d, + 0x00aa0, 0x00aa3, 0x00aa6, 0x00aa9, 0x00aac, 0x00aaf, 0x00ab2, 0x00ab5, + 0x00ab8, 0x00abb, 0x00abe, 0x00ac1, 0x00ac4, 0x00ac7, 0x00aca, 0x00acd, + 0x00ad0, 0x00ad3, 0x00ad6, 0x00ad9, 0x00adc, 0x00adf, 0x00ae2, 0x00ae5, + 0x00ae8, 0x00aeb, 0x00aee, 0x00af1, 0x00af7, 0x00afc, 0x00aff, 0x00b02, + 0x00b05, 0x00b08, 0x00b0b, 0x00b0e, 0x00b11, 0x00b14, 0x00b17, 0x00b1a, + 0x00b1d, 0x00b20, 0x00b23, 0x00b26, 0x00b29, 0x00b2c, 0x00b2f, 0x00b32, + 0x00b35, 0x00b38, 0x00b3b, 0x00b3e, 0x00b41, 0x00b44, 0x00b47, 0x00b4b, + 0x00b4f, 0x00b53, 0x00b56, 0x00b5a, 0x00b5d, 0x00b61, 0x00b66, 0x00b6b, + 0x00b70, 0x00b74, 0x00b79, 0x00b7d, 0x00b81, 0x00b87, 0x00b8c, 0x00b90, + 0x00b94, 0x00b98, 0x00b9d, 0x00ba2, 0x00ba6, 0x00baa, 0x00bad, 0x00bb1, + 0x00bb6, 0x00bbb, 0x00bbe, 0x00bc4, 0x00bcb, 0x00bd1, 0x00bd5, 0x00bdb, + 0x00be1, 0x00be6, 0x00bea, 0x00bee, 0x00bf2, 0x00bf7, 0x00bfd, 0x00c02, + 0x00c06, 0x00c0a, 0x00c0e, 0x00c11, 0x00c14, 0x00c17, 0x00c1a, 0x00c1e, + 0x00c22, 0x00c28, 0x00c2c, 0x00c31, 0x00c37, 0x00c3b, 0x00c3e, 0x00c41, + 0x00c47, 0x00c4c, 0x00c52, 0x00c56, 0x00c5c, 0x00c5f, 0x00c63, 0x00c67, + 0x00c6b, 0x00c6f, 0x00c73, 0x00c78, 0x00c7c, 0x00c7f, 0x00c83, 0x00c87, + 0x00c8b, 0x00c90, 0x00c94, 0x00c98, 0x00c9c, 0x00ca2, 0x00ca7, 0x00caa, + 0x00cb0, 0x00cb3, 0x00cb8, 0x00cbd, 0x00cc1, 0x00cc5, 0x00cc9, 0x00cce, + 0x00cd1, 0x00cd5, 0x00cda, 0x00cdd, 0x00ce3, 0x00ce7, 0x00cea, 0x00ced, + 0x00cf0, 0x00cf3, 0x00cf6, 0x00cf9, 0x00cfc, 0x00cff, 0x00d02, 0x00d05, + 0x00d09, 0x00d0d, 0x00d11, 0x00d15, 0x00d19, 0x00d1d, 0x00d21, 0x00d25, + 0x00d29, 0x00d2d, 0x00d31, 0x00d35, 0x00d39, 0x00d3d, 0x00d41, 0x00d45, + 0x00d48, 0x00d4b, 0x00d4f, 0x00d52, 0x00d55, 0x00d58, 0x00d5c, 0x00d60, + 0x00d63, 0x00d66, 0x00d69, 0x00d6c, 0x00d6f, 0x00d74, 0x00d77, 0x00d7a, + 0x00d7d, 0x00d80, 0x00d83, 0x00d86, 0x00d89, 0x00d8c, 0x00d90, 0x00d95, + 0x00d98, 0x00d9b, 0x00d9e, 0x00da1, 0x00da4, 0x00da7, 0x00daa, 0x00dae, + 0x00db2, 0x00db6, 0x00dba, 0x00dbd, 0x00dc0, 0x00dc3, 0x00dc6, 0x00dc9, + 0x00dcc, 0x00dcf, 0x00dd2, 0x00dd5, 0x00dd8, 0x00ddc, 0x00de0, 0x00de3, + 0x00de7, 0x00deb, 0x00def, 0x00df2, 0x00df6, 0x00dfa, 0x00dff, 0x00e02, + 0x00e06, 0x00e0a, 0x00e0e, 0x00e12, 0x00e18, 0x00e1f, 0x00e22, 0x00e25, + 0x00e28, 0x00e2b, 0x00e2e, 0x00e31, 0x00e34, 0x00e37, 0x00e3a, 0x00e3d, + 0x00e40, 0x00e43, 0x00e46, 0x00e49, 0x00e4c, 0x00e4f, 0x00e52, 0x00e55, + 0x00e5a, 0x00e5d, 0x00e60, 0x00e63, 0x00e68, 0x00e6c, 0x00e6f, 0x00e72, + 0x00e75, 0x00e78, 0x00e7b, 0x00e7e, 0x00e81, 0x00e84, 0x00e87, 0x00e8a, + 0x00e8e, 0x00e91, 0x00e94, 0x00e98, 0x00e9c, 0x00e9f, 0x00ea4, 0x00ea8, + 0x00eab, 0x00eae, 0x00eb1, 0x00eb4, 0x00eb8, 0x00ebc, 0x00ebf, 0x00ec2, + 0x00ec5, 0x00ec8, 0x00ecb, 0x00ece, 0x00ed1, 0x00ed4, 0x00ed7, 0x00edb, + 0x00edf, 0x00ee3, 0x00ee7, 0x00eeb, 0x00eef, 0x00ef3, 0x00ef7, 0x00efb, + 0x00eff, 0x00f03, 0x00f07, 0x00f0b, 0x00f0f, 0x00f13, 0x00f17, 0x00f1b, + 0x00f1f, 0x00f23, 0x00f27, 0x00f2b, 0x00f2f, 0x00f33, 0x00f36, 0x00f39, + 0x00f3c, 0x00f40, 0x00f44, 0x00f47, 0x00f4a, 0x00f4d, 0x00f50, 0x00f53, + 0x00f56, 0x00f59, 0x00f5c, 0x00f5f, 0x00f62, 0x00f65, 0x00f68, 0x00f6b, + 0x00f6e, 0x00f71, 0x00f74, 0x00f77, 0x00f7a, 0x00f7d, 0x00f80, 0x00f83, + 0x00f86, 0x00f89, 0x00f8c, 0x00f8f, 0x00f92, 0x00f95, 0x00f98, 0x00f9b, + 0x00f9e, 0x00fa1, 0x00fa4, 0x00fa7, 0x00faa, 0x00fad, 0x00fb0, 0x00fb3, + 0x00fb6, 0x00fb9, 0x00fbc, 0x00fbf, 0x00fc2, 0x00fc5, 0x00fc8, 0x00fcb, + 0x00fce, 0x00fd1, 0x00fd4, 0x00fd7, 0x00fda, 0x00fdd, 0x00fe0, 0x00fe3, + 0x00fe6, 0x00fe9, 0x00fec, 0x00fef, 0x00ff2, 0x00ff5, 0x00ff8, 0x00ffb, + 0x00ffe, 0x01001, 0x01004, 0x01007, 0x0100a, 0x0100d, 0x01010, 0x01013, + 0x01016, 0x01019, 0x0101c, 0x0101f, 0x01022, 0x01025, 0x01028, 0x0102b, + 0x0102e, 0x01031, 0x01034, 0x01037, 0x0103a, 0x0103d, 0x01040, 0x01043, + 0x01046, 0x01049, 0x0104c, 0x0104f, 0x01052, 0x01055, 0x01058, 0x0105b, + 0x0105e, 0x01061, 0x01064, 0x01067, 0x0106a, 0x0106d, 0x01070, 0x01073, + 0x01076, 0x01079, 0x0107c, 0x0107f, 0x01082, 0x01085, 0x01088, 0x0108b, + 0x0108e, 0x01091, 0x01094, 0x01097, 0x0109a, 0x0109d, 0x010a0, 0x010a3, + 0x010a6, 0x010a9, 0x010ac, 0x010af, 0x010b2, 0x010b5, 0x010b8, 0x010bb, + 0x010be, 0x010c1, 0x010c4, 0x010c7, 0x010ca, 0x010cd, 0x010d0, 0x010d3, + 0x010d6, 0x010d9, 0x010dc, 0x010df, 0x010e2, 0x010e5, 0x010e8, 0x010eb, + 0x010ee, 0x010f1, 0x010f4, 0x010f7, 0x010fa, 0x010fd, 0x01100, 0x01103, + 0x01106, 0x01109, 0x0110c, 0x0110f, 0x01112, 0x01116, 0x0111a, 0x0111e, + 0x01122, 0x01126, 0x0112a, 0x0112d, 0x01130, 0x01133, 0x01136, 0x01139, + 0x0113c, 0x0113f, 0x01142, 0x01145, 0x01148, 0x0114b, 0x0114e, 0x01151, + 0x01154, 0x01157, 0x0115a, 0x0115d, 0x01160, 0x01163, 0x01166, 0x01169, + 0x0116c, 0x0116f, 0x01172, 0x01175, 0x01178, 0x0117b, 0x0117e, 0x01181, + 0x01184, 0x01187, 0x0118a, 0x0118d, 0x01190, 0x01193, 0x01196, 0x01199, + 0x0119c, 0x0119f, 0x011a2, 0x011a5, 0x011a8, 0x011ab, 0x011ae, 0x011b1, + 0x011b4, 0x011b7, 0x011ba, 0x011bd, 0x011c0, 0x011c3, 0x011c6, 0x011c9, + 0x011cc, 0x011cf, 0x011d2, 0x011d5, 0x011d8, 0x011db, 0x011de, 0x011e1, + 0x011e4, 0x011e7, 0x011ea, 0x011ed, 0x011f0, 0x011f3, 0x011f6, 0x011f9, + 0x011fc, 0x011ff, 0x01202, 0x01205, 0x01208, 0x0120b, 0x0120e, 0x01211, + 0x01214, 0x01217, 0x0121a, 0x0121d, 0x01220, 0x01223, 0x01226, 0x01229, + 0x0122c, 0x0122f, 0x01232, 0x01235, 0x01238, 0x0123b, 0x0123e, 0x01241, + 0x01244, 0x01247, 0x0124a, 0x0124d, 0x01250, 0x01253, 0x01256, 0x01259, + 0x0125c, 0x0125f, 0x01262, 0x01265, 0x01268, 0x0126b, 0x0126e, 0x01271, + 0x01274, 0x01277, 0x0127a, 0x0127d, 0x01280, 0x01283, 0x01286, 0x01289, + 0x0128c, 0x0128f, 0x01292, 0x01295, 0x01298, 0x0129b, 0x0129e, 0x012a1, + 0x012a4, 0x012a7, 0x012aa, 0x012ad, 0x012b0, 0x012b3, 0x012b6, 0x012b9, + 0x012bc, 0x012bf, 0x012c2, 0x012c5, 0x012c8, 0x012cb, 0x012ce, 0x012d1, + 0x012d4, 0x012d8, 0x012dc, 0x012e0, 0x012e3, 0x012e6, 0x012e9, 0x012ec, + 0x012ef, 0x012f2, 0x012f5, 0x012f8, 0x012fb, 0x012fe, 0x01301, 0x01304, + 0x01307, 0x0130a, 0x0130d, 0x01310, 0x01313, 0x01316, 0x01319, 0x0131c, + 0x0131f, 0x01322, 0x01325, 0x01328, 0x0132b, 0x0132e, 0x01331, 0x01334, + 0x01337, 0x0133a, 0x0133d, 0x01340, 0x01343, 0x01346, 0x01349, 0x0134c, + 0x0134f, 0x01352, 0x01355, 0x01358, 0x0135b, 0x0135e, 0x01361, 0x01364, + 0x01367, 0x0136a, 0x0136d, 0x01370, 0x01373, 0x01376, 0x01379, 0x0137c, + 0x0137f, 0x01382, 0x01385, 0x01388, 0x0138b, 0x0138e, 0x01391, 0x01394, + 0x01397, 0x0139a, 0x0139d, 0x013a0, 0x013a3, 0x013a6, 0x013a9, 0x013ac, + 0x013af, 0x013b2, 0x013b5, 0x013b8, 0x013bb, 0x013bf, 0x013c3, 0x013c7, + 0x013cb, 0x013cf, 0x013d3, 0x013d7, 0x013db, 0x013df, 0x013e3, 0x013e7, + 0x013eb, 0x013ef, 0x013f3, 0x013f7, 0x013fb, 0x013ff, 0x01403, 0x01407, + 0x0140b, 0x0140f, 0x01413, 0x01417, 0x0141b, 0x0141f, 0x01423, 0x01427, + 0x0142b, 0x0142f, 0x01433, 0x01437, 0x0143b, 0x0143f, 0x01443, 0x01447, + 0x0144b, 0x0144f, 0x01453, 0x01457, 0x0145b, 0x0145f, 0x01463, 0x01467, + 0x0146b, 0x0146f, 0x01473, 0x01477, 0x0147b, 0x0147f, 0x01483, 0x01487, + 0x0148b, 0x0148f, 0x01493, 0x01497, 0x0149b, 0x0149f, 0x014a3, 0x014a7, + 0x014ab, 0x014af, 0x014b3, 0x014b7, 0x014bb, 0x014bf, 0x014c3, 0x014c7, + 0x014cb, 0x014cf, 0x014d3, 0x014d7, 0x014db, 0x014df, 0x014e3, 0x014e7, + 0x014eb, 0x014ef, 0x014f3, 0x014f7, 0x014fb, 0x014ff, 0x01503, 0x01507, + 0x0150b, 0x0150f, 0x01513, 0x01517, 0x0151b, 0x0151f, 0x01523, 0x01527, + 0x0152b, 0x0152f, 0x01533, 0x01537, 0x0153b, 0x0153f, 0x01543, 0x01547, + 0x0154b, 0x0154f, 0x01553, 0x01557, 0x0155b, 0x0155f, 0x01563, 0x01567, + 0x0156b, 0x0156f, 0x01573, 0x01577, 0x0157b, 0x0157f, 0x01583, 0x01587, + 0x0158b, 0x0158f, 0x01593, 0x01597, 0x0159b, 0x015a0, 0x015a5, 0x015aa, + 0x015af, 0x015b4, 0x015b9, 0x015be, 0x015c2, 0x015d5, 0x015de, 0x015e3, + 0x015e6, 0x015e9, 0x015ec, 0x015ef, 0x015f2, 0x015f5, 0x015f8, 0x015fb, + 0x015fe, 0x01601, 0x01604, 0x01607, 0x0160a, 0x0160d, 0x01610, 0x01613, + 0x01616, 0x01619, 0x0161c, 0x0161f, 0x01622, 0x01625, 0x01628, 0x0162b, + 0x0162e, 0x01631, 0x01634, 0x01637, 0x0163a, 0x0163d, 0x01640, 0x01643, + 0x01646, 0x01649, 0x0164c, 0x0164f, 0x01652, 0x01655, 0x01658, 0x0165b, + 0x0165e, 0x01661, 0x01664, 0x01667, 0x0166a, 0x0166d, 0x01670, 0x01673, + 0x01676, 0x01679, 0x0167c, 0x0167f, 0x01682, 0x01685, 0x01688, 0x0168b, + 0x0168e, 0x01691, 0x01695, 0x01699, 0x0169d, 0x016a1, 0x016a5, 0x016a9, + 0x016ad, 0x016b1, 0x016b5, 0x016b9, 0x016bd, 0x016c1, 0x016c5, 0x016c9, + 0x016cd, 0x016d1, 0x016d5, 0x016d9, 0x016dd, 0x016e1, 0x016e5, 0x016e9, + 0x016ed, 0x016f1, 0x016f5, 0x016f9, 0x016fd, 0x01700, 0x01703, 0x01706, + 0x01709, 0x0170c, 0x0170f, 0x01713, 0x01716, 0x01719, 0x0171c, 0x0171f, + 0x01722, 0x01725, 0x01729, 0x0172d, 0x01731, 0x01735, 0x01739, 0x0173d, + 0x01741, 0x01745 +}; +static const uint32_t multidecomp_values[] = { + 0x00020, 0x00308, 0x00000, 0x00020, 0x00304, 0x00000, 0x00020, 0x00301, + 0x00000, 0x00020, 0x00327, 0x00000, 0x00031, 0x02044, 0x00034, 0x00000, + 0x00031, 0x02044, 0x00032, 0x00000, 0x00033, 0x02044, 0x00034, 0x00000, + 0x00041, 0x00300, 0x00000, 0x00041, 0x00301, 0x00000, 0x00041, 0x00302, + 0x00000, 0x00041, 0x00303, 0x00000, 0x00041, 0x00308, 0x00000, 0x00041, + 0x0030a, 0x00000, 0x00043, 0x00327, 0x00000, 0x00045, 0x00300, 0x00000, + 0x00045, 0x00301, 0x00000, 0x00045, 0x00302, 0x00000, 0x00045, 0x00308, + 0x00000, 0x00049, 0x00300, 0x00000, 0x00049, 0x00301, 0x00000, 0x00049, + 0x00302, 0x00000, 0x00049, 0x00308, 0x00000, 0x0004e, 0x00303, 0x00000, + 0x0004f, 0x00300, 0x00000, 0x0004f, 0x00301, 0x00000, 0x0004f, 0x00302, + 0x00000, 0x0004f, 0x00303, 0x00000, 0x0004f, 0x00308, 0x00000, 0x00055, + 0x00300, 0x00000, 0x00055, 0x00301, 0x00000, 0x00055, 0x00302, 0x00000, + 0x00055, 0x00308, 0x00000, 0x00059, 0x00301, 0x00000, 0x00041, 0x00304, + 0x00000, 0x00041, 0x00306, 0x00000, 0x00041, 0x00328, 0x00000, 0x00043, + 0x00301, 0x00000, 0x00043, 0x00302, 0x00000, 0x00043, 0x00307, 0x00000, + 0x00043, 0x0030c, 0x00000, 0x00044, 0x0030c, 0x00000, 0x00045, 0x00304, + 0x00000, 0x00045, 0x00306, 0x00000, 0x00045, 0x00307, 0x00000, 0x00045, + 0x00328, 0x00000, 0x00045, 0x0030c, 0x00000, 0x00047, 0x00302, 0x00000, + 0x00047, 0x00306, 0x00000, 0x00047, 0x00307, 0x00000, 0x00047, 0x00327, + 0x00000, 0x00048, 0x00302, 0x00000, 0x00049, 0x00303, 0x00000, 0x00049, + 0x00304, 0x00000, 0x00049, 0x00306, 0x00000, 0x00049, 0x00328, 0x00000, + 0x00049, 0x00307, 0x00000, 0x00049, 0x0004a, 0x00000, 0x0004a, 0x00302, + 0x00000, 0x0004b, 0x00327, 0x00000, 0x0004c, 0x00301, 0x00000, 0x0004c, + 0x00327, 0x00000, 0x0004c, 0x0030c, 0x00000, 0x0004c, 0x000b7, 0x00000, + 0x0004e, 0x00301, 0x00000, 0x0004e, 0x00327, 0x00000, 0x0004e, 0x0030c, + 0x00000, 0x002bc, 0x0006e, 0x00000, 0x0004f, 0x00304, 0x00000, 0x0004f, + 0x00306, 0x00000, 0x0004f, 0x0030b, 0x00000, 0x00052, 0x00301, 0x00000, + 0x00052, 0x00327, 0x00000, 0x00052, 0x0030c, 0x00000, 0x00053, 0x00301, + 0x00000, 0x00053, 0x00302, 0x00000, 0x00053, 0x00327, 0x00000, 0x00053, + 0x0030c, 0x00000, 0x00054, 0x00327, 0x00000, 0x00054, 0x0030c, 0x00000, + 0x00055, 0x00303, 0x00000, 0x00055, 0x00304, 0x00000, 0x00055, 0x00306, + 0x00000, 0x00055, 0x0030a, 0x00000, 0x00055, 0x0030b, 0x00000, 0x00055, + 0x00328, 0x00000, 0x00057, 0x00302, 0x00000, 0x00059, 0x00302, 0x00000, + 0x00059, 0x00308, 0x00000, 0x0005a, 0x00301, 0x00000, 0x0005a, 0x00307, + 0x00000, 0x0005a, 0x0030c, 0x00000, 0x0004f, 0x0031b, 0x00000, 0x00055, + 0x0031b, 0x00000, 0x00041, 0x0030c, 0x00000, 0x00049, 0x0030c, 0x00000, + 0x0004f, 0x0030c, 0x00000, 0x00055, 0x0030c, 0x00000, 0x000dc, 0x00304, + 0x00000, 0x000dc, 0x00301, 0x00000, 0x000dc, 0x0030c, 0x00000, 0x000dc, + 0x00300, 0x00000, 0x000c4, 0x00304, 0x00000, 0x00226, 0x00304, 0x00000, + 0x000c6, 0x00304, 0x00000, 0x00047, 0x0030c, 0x00000, 0x0004b, 0x0030c, + 0x00000, 0x0004f, 0x00328, 0x00000, 0x001ea, 0x00304, 0x00000, 0x001b7, + 0x0030c, 0x00000, 0x0006a, 0x0030c, 0x00000, 0x00047, 0x00301, 0x00000, + 0x0004e, 0x00300, 0x00000, 0x000c5, 0x00301, 0x00000, 0x000c6, 0x00301, + 0x00000, 0x000d8, 0x00301, 0x00000, 0x00041, 0x0030f, 0x00000, 0x00041, + 0x00311, 0x00000, 0x00045, 0x0030f, 0x00000, 0x00045, 0x00311, 0x00000, + 0x00049, 0x0030f, 0x00000, 0x00049, 0x00311, 0x00000, 0x0004f, 0x0030f, + 0x00000, 0x0004f, 0x00311, 0x00000, 0x00052, 0x0030f, 0x00000, 0x00052, + 0x00311, 0x00000, 0x00055, 0x0030f, 0x00000, 0x00055, 0x00311, 0x00000, + 0x00053, 0x00326, 0x00000, 0x00054, 0x00326, 0x00000, 0x00048, 0x0030c, + 0x00000, 0x00041, 0x00307, 0x00000, 0x00045, 0x00327, 0x00000, 0x000d6, + 0x00304, 0x00000, 0x000d5, 0x00304, 0x00000, 0x0004f, 0x00307, 0x00000, + 0x0022e, 0x00304, 0x00000, 0x00059, 0x00304, 0x00000, 0x00020, 0x00306, + 0x00000, 0x00020, 0x00307, 0x00000, 0x00020, 0x0030a, 0x00000, 0x00020, + 0x00328, 0x00000, 0x00020, 0x00303, 0x00000, 0x00020, 0x0030b, 0x00000, + 0x00308, 0x00301, 0x00000, 0x00020, 0x00345, 0x00000, 0x00020, 0x00301, + 0x00000, 0x000a8, 0x00301, 0x00000, 0x00391, 0x00301, 0x00000, 0x00395, + 0x00301, 0x00000, 0x00397, 0x00301, 0x00000, 0x00399, 0x00301, 0x00000, + 0x0039f, 0x00301, 0x00000, 0x003a5, 0x00301, 0x00000, 0x003a9, 0x00301, + 0x00000, 0x003ca, 0x00301, 0x00000, 0x00399, 0x00308, 0x00000, 0x003a5, + 0x00308, 0x00000, 0x003cb, 0x00301, 0x00000, 0x003d2, 0x00301, 0x00000, + 0x003d2, 0x00308, 0x00000, 0x00415, 0x00300, 0x00000, 0x00415, 0x00308, + 0x00000, 0x00413, 0x00301, 0x00000, 0x00406, 0x00308, 0x00000, 0x0041a, + 0x00301, 0x00000, 0x00418, 0x00300, 0x00000, 0x00423, 0x00306, 0x00000, + 0x00418, 0x00306, 0x00000, 0x00474, 0x0030f, 0x00000, 0x00416, 0x00306, + 0x00000, 0x00410, 0x00306, 0x00000, 0x00410, 0x00308, 0x00000, 0x00415, + 0x00306, 0x00000, 0x004d8, 0x00308, 0x00000, 0x00416, 0x00308, 0x00000, + 0x00417, 0x00308, 0x00000, 0x00418, 0x00304, 0x00000, 0x00418, 0x00308, + 0x00000, 0x0041e, 0x00308, 0x00000, 0x004e8, 0x00308, 0x00000, 0x0042d, + 0x00308, 0x00000, 0x00423, 0x00304, 0x00000, 0x00423, 0x00308, 0x00000, + 0x00423, 0x0030b, 0x00000, 0x00427, 0x00308, 0x00000, 0x0042b, 0x00308, + 0x00000, 0x00565, 0x00582, 0x00000, 0x00627, 0x00653, 0x00000, 0x00627, + 0x00654, 0x00000, 0x00648, 0x00654, 0x00000, 0x00627, 0x00655, 0x00000, + 0x0064a, 0x00654, 0x00000, 0x00627, 0x00674, 0x00000, 0x00648, 0x00674, + 0x00000, 0x006c7, 0x00674, 0x00000, 0x0064a, 0x00674, 0x00000, 0x006d5, + 0x00654, 0x00000, 0x006c1, 0x00654, 0x00000, 0x006d2, 0x00654, 0x00000, + 0x00928, 0x0093c, 0x00000, 0x00930, 0x0093c, 0x00000, 0x00933, 0x0093c, + 0x00000, 0x00915, 0x0093c, 0x00000, 0x00916, 0x0093c, 0x00000, 0x00917, + 0x0093c, 0x00000, 0x0091c, 0x0093c, 0x00000, 0x00921, 0x0093c, 0x00000, + 0x00922, 0x0093c, 0x00000, 0x0092b, 0x0093c, 0x00000, 0x0092f, 0x0093c, + 0x00000, 0x009c7, 0x009be, 0x00000, 0x009c7, 0x009d7, 0x00000, 0x009a1, + 0x009bc, 0x00000, 0x009a2, 0x009bc, 0x00000, 0x009af, 0x009bc, 0x00000, + 0x00a32, 0x00a3c, 0x00000, 0x00a38, 0x00a3c, 0x00000, 0x00a16, 0x00a3c, + 0x00000, 0x00a17, 0x00a3c, 0x00000, 0x00a1c, 0x00a3c, 0x00000, 0x00a2b, + 0x00a3c, 0x00000, 0x00b47, 0x00b56, 0x00000, 0x00b47, 0x00b3e, 0x00000, + 0x00b47, 0x00b57, 0x00000, 0x00b21, 0x00b3c, 0x00000, 0x00b22, 0x00b3c, + 0x00000, 0x00b92, 0x00bd7, 0x00000, 0x00bc6, 0x00bbe, 0x00000, 0x00bc7, + 0x00bbe, 0x00000, 0x00bc6, 0x00bd7, 0x00000, 0x00c46, 0x00c56, 0x00000, + 0x00cbf, 0x00cd5, 0x00000, 0x00cc6, 0x00cd5, 0x00000, 0x00cc6, 0x00cd6, + 0x00000, 0x00cc6, 0x00cc2, 0x00000, 0x00cca, 0x00cd5, 0x00000, 0x00d46, + 0x00d3e, 0x00000, 0x00d47, 0x00d3e, 0x00000, 0x00d46, 0x00d57, 0x00000, + 0x00dd9, 0x00dca, 0x00000, 0x00dd9, 0x00dcf, 0x00000, 0x00ddc, 0x00dca, + 0x00000, 0x00dd9, 0x00ddf, 0x00000, 0x00e4d, 0x00e32, 0x00000, 0x00ecd, + 0x00eb2, 0x00000, 0x00eab, 0x00e99, 0x00000, 0x00eab, 0x00ea1, 0x00000, + 0x00f42, 0x00fb7, 0x00000, 0x00f4c, 0x00fb7, 0x00000, 0x00f51, 0x00fb7, + 0x00000, 0x00f56, 0x00fb7, 0x00000, 0x00f5b, 0x00fb7, 0x00000, 0x00f40, + 0x00fb5, 0x00000, 0x00f71, 0x00f72, 0x00000, 0x00f71, 0x00f74, 0x00000, + 0x00fb2, 0x00f80, 0x00000, 0x00fb2, 0x00f81, 0x00000, 0x00fb3, 0x00f80, + 0x00000, 0x00fb3, 0x00f81, 0x00000, 0x00f71, 0x00f80, 0x00000, 0x00f92, + 0x00fb7, 0x00000, 0x00f9c, 0x00fb7, 0x00000, 0x00fa1, 0x00fb7, 0x00000, + 0x00fa6, 0x00fb7, 0x00000, 0x00fab, 0x00fb7, 0x00000, 0x00f90, 0x00fb5, + 0x00000, 0x01025, 0x0102e, 0x00000, 0x01b05, 0x01b35, 0x00000, 0x01b07, + 0x01b35, 0x00000, 0x01b09, 0x01b35, 0x00000, 0x01b0b, 0x01b35, 0x00000, + 0x01b0d, 0x01b35, 0x00000, 0x01b11, 0x01b35, 0x00000, 0x01b3a, 0x01b35, + 0x00000, 0x01b3c, 0x01b35, 0x00000, 0x01b3e, 0x01b35, 0x00000, 0x01b3f, + 0x01b35, 0x00000, 0x01b42, 0x01b35, 0x00000, 0x00041, 0x00325, 0x00000, + 0x00042, 0x00307, 0x00000, 0x00042, 0x00323, 0x00000, 0x00042, 0x00331, + 0x00000, 0x000c7, 0x00301, 0x00000, 0x00044, 0x00307, 0x00000, 0x00044, + 0x00323, 0x00000, 0x00044, 0x00331, 0x00000, 0x00044, 0x00327, 0x00000, + 0x00044, 0x0032d, 0x00000, 0x00112, 0x00300, 0x00000, 0x00112, 0x00301, + 0x00000, 0x00045, 0x0032d, 0x00000, 0x00045, 0x00330, 0x00000, 0x00228, + 0x00306, 0x00000, 0x00046, 0x00307, 0x00000, 0x00047, 0x00304, 0x00000, + 0x00048, 0x00307, 0x00000, 0x00048, 0x00323, 0x00000, 0x00048, 0x00308, + 0x00000, 0x00048, 0x00327, 0x00000, 0x00048, 0x0032e, 0x00000, 0x00049, + 0x00330, 0x00000, 0x000cf, 0x00301, 0x00000, 0x0004b, 0x00301, 0x00000, + 0x0004b, 0x00323, 0x00000, 0x0004b, 0x00331, 0x00000, 0x0004c, 0x00323, + 0x00000, 0x01e36, 0x00304, 0x00000, 0x0004c, 0x00331, 0x00000, 0x0004c, + 0x0032d, 0x00000, 0x0004d, 0x00301, 0x00000, 0x0004d, 0x00307, 0x00000, + 0x0004d, 0x00323, 0x00000, 0x0004e, 0x00307, 0x00000, 0x0004e, 0x00323, + 0x00000, 0x0004e, 0x00331, 0x00000, 0x0004e, 0x0032d, 0x00000, 0x000d5, + 0x00301, 0x00000, 0x000d5, 0x00308, 0x00000, 0x0014c, 0x00300, 0x00000, + 0x0014c, 0x00301, 0x00000, 0x00050, 0x00301, 0x00000, 0x00050, 0x00307, + 0x00000, 0x00052, 0x00307, 0x00000, 0x00052, 0x00323, 0x00000, 0x01e5a, + 0x00304, 0x00000, 0x00052, 0x00331, 0x00000, 0x00053, 0x00307, 0x00000, + 0x00053, 0x00323, 0x00000, 0x0015a, 0x00307, 0x00000, 0x00160, 0x00307, + 0x00000, 0x01e62, 0x00307, 0x00000, 0x00054, 0x00307, 0x00000, 0x00054, + 0x00323, 0x00000, 0x00054, 0x00331, 0x00000, 0x00054, 0x0032d, 0x00000, + 0x00055, 0x00324, 0x00000, 0x00055, 0x00330, 0x00000, 0x00055, 0x0032d, + 0x00000, 0x00168, 0x00301, 0x00000, 0x0016a, 0x00308, 0x00000, 0x00056, + 0x00303, 0x00000, 0x00056, 0x00323, 0x00000, 0x00057, 0x00300, 0x00000, + 0x00057, 0x00301, 0x00000, 0x00057, 0x00308, 0x00000, 0x00057, 0x00307, + 0x00000, 0x00057, 0x00323, 0x00000, 0x00058, 0x00307, 0x00000, 0x00058, + 0x00308, 0x00000, 0x00059, 0x00307, 0x00000, 0x0005a, 0x00302, 0x00000, + 0x0005a, 0x00323, 0x00000, 0x0005a, 0x00331, 0x00000, 0x00068, 0x00331, + 0x00000, 0x00074, 0x00308, 0x00000, 0x00077, 0x0030a, 0x00000, 0x00079, + 0x0030a, 0x00000, 0x00061, 0x002be, 0x00000, 0x00041, 0x00323, 0x00000, + 0x00041, 0x00309, 0x00000, 0x000c2, 0x00301, 0x00000, 0x000c2, 0x00300, + 0x00000, 0x000c2, 0x00309, 0x00000, 0x000c2, 0x00303, 0x00000, 0x01ea0, + 0x00302, 0x00000, 0x00102, 0x00301, 0x00000, 0x00102, 0x00300, 0x00000, + 0x00102, 0x00309, 0x00000, 0x00102, 0x00303, 0x00000, 0x01ea0, 0x00306, + 0x00000, 0x00045, 0x00323, 0x00000, 0x00045, 0x00309, 0x00000, 0x00045, + 0x00303, 0x00000, 0x000ca, 0x00301, 0x00000, 0x000ca, 0x00300, 0x00000, + 0x000ca, 0x00309, 0x00000, 0x000ca, 0x00303, 0x00000, 0x01eb8, 0x00302, + 0x00000, 0x00049, 0x00309, 0x00000, 0x00049, 0x00323, 0x00000, 0x0004f, + 0x00323, 0x00000, 0x0004f, 0x00309, 0x00000, 0x000d4, 0x00301, 0x00000, + 0x000d4, 0x00300, 0x00000, 0x000d4, 0x00309, 0x00000, 0x000d4, 0x00303, + 0x00000, 0x01ecc, 0x00302, 0x00000, 0x001a0, 0x00301, 0x00000, 0x001a0, + 0x00300, 0x00000, 0x001a0, 0x00309, 0x00000, 0x001a0, 0x00303, 0x00000, + 0x001a0, 0x00323, 0x00000, 0x00055, 0x00323, 0x00000, 0x00055, 0x00309, + 0x00000, 0x001af, 0x00301, 0x00000, 0x001af, 0x00300, 0x00000, 0x001af, + 0x00309, 0x00000, 0x001af, 0x00303, 0x00000, 0x001af, 0x00323, 0x00000, + 0x00059, 0x00300, 0x00000, 0x00059, 0x00323, 0x00000, 0x00059, 0x00309, + 0x00000, 0x00059, 0x00303, 0x00000, 0x00391, 0x00313, 0x00000, 0x00391, + 0x00314, 0x00000, 0x01f08, 0x00300, 0x00000, 0x01f09, 0x00300, 0x00000, + 0x01f08, 0x00301, 0x00000, 0x01f09, 0x00301, 0x00000, 0x01f08, 0x00342, + 0x00000, 0x01f09, 0x00342, 0x00000, 0x00395, 0x00313, 0x00000, 0x00395, + 0x00314, 0x00000, 0x01f18, 0x00300, 0x00000, 0x01f19, 0x00300, 0x00000, + 0x01f18, 0x00301, 0x00000, 0x01f19, 0x00301, 0x00000, 0x00397, 0x00313, + 0x00000, 0x00397, 0x00314, 0x00000, 0x01f28, 0x00300, 0x00000, 0x01f29, + 0x00300, 0x00000, 0x01f28, 0x00301, 0x00000, 0x01f29, 0x00301, 0x00000, + 0x01f28, 0x00342, 0x00000, 0x01f29, 0x00342, 0x00000, 0x00399, 0x00313, + 0x00000, 0x00399, 0x00314, 0x00000, 0x01f38, 0x00300, 0x00000, 0x01f39, + 0x00300, 0x00000, 0x01f38, 0x00301, 0x00000, 0x01f39, 0x00301, 0x00000, + 0x01f38, 0x00342, 0x00000, 0x01f39, 0x00342, 0x00000, 0x0039f, 0x00313, + 0x00000, 0x0039f, 0x00314, 0x00000, 0x01f48, 0x00300, 0x00000, 0x01f49, + 0x00300, 0x00000, 0x01f48, 0x00301, 0x00000, 0x01f49, 0x00301, 0x00000, + 0x003c5, 0x00313, 0x00000, 0x01f50, 0x00300, 0x00000, 0x01f50, 0x00301, + 0x00000, 0x01f50, 0x00342, 0x00000, 0x003a5, 0x00314, 0x00000, 0x01f59, + 0x00300, 0x00000, 0x01f59, 0x00301, 0x00000, 0x01f59, 0x00342, 0x00000, + 0x003a9, 0x00313, 0x00000, 0x003a9, 0x00314, 0x00000, 0x01f68, 0x00300, + 0x00000, 0x01f69, 0x00300, 0x00000, 0x01f68, 0x00301, 0x00000, 0x01f69, + 0x00301, 0x00000, 0x01f68, 0x00342, 0x00000, 0x01f69, 0x00342, 0x00000, + 0x01f08, 0x00345, 0x00000, 0x01f09, 0x00345, 0x00000, 0x01f0a, 0x00345, + 0x00000, 0x01f0b, 0x00345, 0x00000, 0x01f0c, 0x00345, 0x00000, 0x01f0d, + 0x00345, 0x00000, 0x01f0e, 0x00345, 0x00000, 0x01f0f, 0x00345, 0x00000, + 0x01f28, 0x00345, 0x00000, 0x01f29, 0x00345, 0x00000, 0x01f2a, 0x00345, + 0x00000, 0x01f2b, 0x00345, 0x00000, 0x01f2c, 0x00345, 0x00000, 0x01f2d, + 0x00345, 0x00000, 0x01f2e, 0x00345, 0x00000, 0x01f2f, 0x00345, 0x00000, + 0x01f68, 0x00345, 0x00000, 0x01f69, 0x00345, 0x00000, 0x01f6a, 0x00345, + 0x00000, 0x01f6b, 0x00345, 0x00000, 0x01f6c, 0x00345, 0x00000, 0x01f6d, + 0x00345, 0x00000, 0x01f6e, 0x00345, 0x00000, 0x01f6f, 0x00345, 0x00000, + 0x01f70, 0x00345, 0x00000, 0x003ac, 0x00345, 0x00000, 0x003b1, 0x00342, + 0x00000, 0x01fb6, 0x00345, 0x00000, 0x00391, 0x00306, 0x00000, 0x00391, + 0x00304, 0x00000, 0x00391, 0x00300, 0x00000, 0x00391, 0x00345, 0x00000, + 0x00020, 0x00313, 0x00000, 0x00020, 0x00313, 0x00000, 0x00020, 0x00342, + 0x00000, 0x000a8, 0x00342, 0x00000, 0x01f74, 0x00345, 0x00000, 0x003ae, + 0x00345, 0x00000, 0x003b7, 0x00342, 0x00000, 0x01fc6, 0x00345, 0x00000, + 0x00395, 0x00300, 0x00000, 0x00397, 0x00300, 0x00000, 0x00397, 0x00345, + 0x00000, 0x01fbf, 0x00300, 0x00000, 0x01fbf, 0x00301, 0x00000, 0x01fbf, + 0x00342, 0x00000, 0x003ca, 0x00300, 0x00000, 0x003b9, 0x00342, 0x00000, + 0x003ca, 0x00342, 0x00000, 0x00399, 0x00306, 0x00000, 0x00399, 0x00304, + 0x00000, 0x00399, 0x00300, 0x00000, 0x01ffe, 0x00300, 0x00000, 0x01ffe, + 0x00301, 0x00000, 0x01ffe, 0x00342, 0x00000, 0x003cb, 0x00300, 0x00000, + 0x003c1, 0x00313, 0x00000, 0x003c5, 0x00342, 0x00000, 0x003cb, 0x00342, + 0x00000, 0x003a5, 0x00306, 0x00000, 0x003a5, 0x00304, 0x00000, 0x003a5, + 0x00300, 0x00000, 0x003a1, 0x00314, 0x00000, 0x000a8, 0x00300, 0x00000, + 0x01f7c, 0x00345, 0x00000, 0x003ce, 0x00345, 0x00000, 0x003c9, 0x00342, + 0x00000, 0x01ff6, 0x00345, 0x00000, 0x0039f, 0x00300, 0x00000, 0x003a9, + 0x00300, 0x00000, 0x003a9, 0x00345, 0x00000, 0x00020, 0x00314, 0x00000, + 0x00020, 0x00333, 0x00000, 0x0002e, 0x0002e, 0x00000, 0x0002e, 0x0002e, + 0x0002e, 0x00000, 0x02032, 0x02032, 0x00000, 0x02032, 0x02032, 0x02032, + 0x00000, 0x02035, 0x02035, 0x00000, 0x02035, 0x02035, 0x02035, 0x00000, + 0x00021, 0x00021, 0x00000, 0x00020, 0x00305, 0x00000, 0x0003f, 0x0003f, + 0x00000, 0x0003f, 0x00021, 0x00000, 0x00021, 0x0003f, 0x00000, 0x02032, + 0x02032, 0x02032, 0x02032, 0x00000, 0x00052, 0x00073, 0x00000, 0x00061, + 0x0002f, 0x00063, 0x00000, 0x00061, 0x0002f, 0x00073, 0x00000, 0x000b0, + 0x00043, 0x00000, 0x00063, 0x0002f, 0x0006f, 0x00000, 0x00063, 0x0002f, + 0x00075, 0x00000, 0x000b0, 0x00046, 0x00000, 0x0004e, 0x0006f, 0x00000, + 0x00053, 0x0004d, 0x00000, 0x00054, 0x00045, 0x0004c, 0x00000, 0x00054, + 0x0004d, 0x00000, 0x00046, 0x00041, 0x00058, 0x00000, 0x00031, 0x02044, + 0x00037, 0x00000, 0x00031, 0x02044, 0x00039, 0x00000, 0x00031, 0x02044, + 0x00031, 0x00030, 0x00000, 0x00031, 0x02044, 0x00033, 0x00000, 0x00032, + 0x02044, 0x00033, 0x00000, 0x00031, 0x02044, 0x00035, 0x00000, 0x00032, + 0x02044, 0x00035, 0x00000, 0x00033, 0x02044, 0x00035, 0x00000, 0x00034, + 0x02044, 0x00035, 0x00000, 0x00031, 0x02044, 0x00036, 0x00000, 0x00035, + 0x02044, 0x00036, 0x00000, 0x00031, 0x02044, 0x00038, 0x00000, 0x00033, + 0x02044, 0x00038, 0x00000, 0x00035, 0x02044, 0x00038, 0x00000, 0x00037, + 0x02044, 0x00038, 0x00000, 0x00031, 0x02044, 0x00000, 0x00049, 0x00049, + 0x00000, 0x00049, 0x00049, 0x00049, 0x00000, 0x00049, 0x00056, 0x00000, + 0x00056, 0x00049, 0x00000, 0x00056, 0x00049, 0x00049, 0x00000, 0x00056, + 0x00049, 0x00049, 0x00049, 0x00000, 0x00049, 0x00058, 0x00000, 0x00058, + 0x00049, 0x00000, 0x00058, 0x00049, 0x00049, 0x00000, 0x00030, 0x02044, + 0x00033, 0x00000, 0x02190, 0x00338, 0x00000, 0x02192, 0x00338, 0x00000, + 0x02194, 0x00338, 0x00000, 0x021d0, 0x00338, 0x00000, 0x021d4, 0x00338, + 0x00000, 0x021d2, 0x00338, 0x00000, 0x02203, 0x00338, 0x00000, 0x02208, + 0x00338, 0x00000, 0x0220b, 0x00338, 0x00000, 0x02223, 0x00338, 0x00000, + 0x02225, 0x00338, 0x00000, 0x0222b, 0x0222b, 0x00000, 0x0222b, 0x0222b, + 0x0222b, 0x00000, 0x0222e, 0x0222e, 0x00000, 0x0222e, 0x0222e, 0x0222e, + 0x00000, 0x0223c, 0x00338, 0x00000, 0x02243, 0x00338, 0x00000, 0x02245, + 0x00338, 0x00000, 0x02248, 0x00338, 0x00000, 0x0003d, 0x00338, 0x00000, + 0x02261, 0x00338, 0x00000, 0x0224d, 0x00338, 0x00000, 0x0003c, 0x00338, + 0x00000, 0x0003e, 0x00338, 0x00000, 0x02264, 0x00338, 0x00000, 0x02265, + 0x00338, 0x00000, 0x02272, 0x00338, 0x00000, 0x02273, 0x00338, 0x00000, + 0x02276, 0x00338, 0x00000, 0x02277, 0x00338, 0x00000, 0x0227a, 0x00338, + 0x00000, 0x0227b, 0x00338, 0x00000, 0x02282, 0x00338, 0x00000, 0x02283, + 0x00338, 0x00000, 0x02286, 0x00338, 0x00000, 0x02287, 0x00338, 0x00000, + 0x022a2, 0x00338, 0x00000, 0x022a8, 0x00338, 0x00000, 0x022a9, 0x00338, + 0x00000, 0x022ab, 0x00338, 0x00000, 0x0227c, 0x00338, 0x00000, 0x0227d, + 0x00338, 0x00000, 0x02291, 0x00338, 0x00000, 0x02292, 0x00338, 0x00000, + 0x022b2, 0x00338, 0x00000, 0x022b3, 0x00338, 0x00000, 0x022b4, 0x00338, + 0x00000, 0x022b5, 0x00338, 0x00000, 0x00031, 0x00030, 0x00000, 0x00031, + 0x00031, 0x00000, 0x00031, 0x00032, 0x00000, 0x00031, 0x00033, 0x00000, + 0x00031, 0x00034, 0x00000, 0x00031, 0x00035, 0x00000, 0x00031, 0x00036, + 0x00000, 0x00031, 0x00037, 0x00000, 0x00031, 0x00038, 0x00000, 0x00031, + 0x00039, 0x00000, 0x00032, 0x00030, 0x00000, 0x00028, 0x00031, 0x00029, + 0x00000, 0x00028, 0x00032, 0x00029, 0x00000, 0x00028, 0x00033, 0x00029, + 0x00000, 0x00028, 0x00034, 0x00029, 0x00000, 0x00028, 0x00035, 0x00029, + 0x00000, 0x00028, 0x00036, 0x00029, 0x00000, 0x00028, 0x00037, 0x00029, + 0x00000, 0x00028, 0x00038, 0x00029, 0x00000, 0x00028, 0x00039, 0x00029, + 0x00000, 0x00028, 0x00031, 0x00030, 0x00029, 0x00000, 0x00028, 0x00031, + 0x00031, 0x00029, 0x00000, 0x00028, 0x00031, 0x00032, 0x00029, 0x00000, + 0x00028, 0x00031, 0x00033, 0x00029, 0x00000, 0x00028, 0x00031, 0x00034, + 0x00029, 0x00000, 0x00028, 0x00031, 0x00035, 0x00029, 0x00000, 0x00028, + 0x00031, 0x00036, 0x00029, 0x00000, 0x00028, 0x00031, 0x00037, 0x00029, + 0x00000, 0x00028, 0x00031, 0x00038, 0x00029, 0x00000, 0x00028, 0x00031, + 0x00039, 0x00029, 0x00000, 0x00028, 0x00032, 0x00030, 0x00029, 0x00000, + 0x00031, 0x0002e, 0x00000, 0x00032, 0x0002e, 0x00000, 0x00033, 0x0002e, + 0x00000, 0x00034, 0x0002e, 0x00000, 0x00035, 0x0002e, 0x00000, 0x00036, + 0x0002e, 0x00000, 0x00037, 0x0002e, 0x00000, 0x00038, 0x0002e, 0x00000, + 0x00039, 0x0002e, 0x00000, 0x00031, 0x00030, 0x0002e, 0x00000, 0x00031, + 0x00031, 0x0002e, 0x00000, 0x00031, 0x00032, 0x0002e, 0x00000, 0x00031, + 0x00033, 0x0002e, 0x00000, 0x00031, 0x00034, 0x0002e, 0x00000, 0x00031, + 0x00035, 0x0002e, 0x00000, 0x00031, 0x00036, 0x0002e, 0x00000, 0x00031, + 0x00037, 0x0002e, 0x00000, 0x00031, 0x00038, 0x0002e, 0x00000, 0x00031, + 0x00039, 0x0002e, 0x00000, 0x00032, 0x00030, 0x0002e, 0x00000, 0x00028, + 0x00061, 0x00029, 0x00000, 0x00028, 0x00062, 0x00029, 0x00000, 0x00028, + 0x00063, 0x00029, 0x00000, 0x00028, 0x00064, 0x00029, 0x00000, 0x00028, + 0x00065, 0x00029, 0x00000, 0x00028, 0x00066, 0x00029, 0x00000, 0x00028, + 0x00067, 0x00029, 0x00000, 0x00028, 0x00068, 0x00029, 0x00000, 0x00028, + 0x00069, 0x00029, 0x00000, 0x00028, 0x0006a, 0x00029, 0x00000, 0x00028, + 0x0006b, 0x00029, 0x00000, 0x00028, 0x0006c, 0x00029, 0x00000, 0x00028, + 0x0006d, 0x00029, 0x00000, 0x00028, 0x0006e, 0x00029, 0x00000, 0x00028, + 0x0006f, 0x00029, 0x00000, 0x00028, 0x00070, 0x00029, 0x00000, 0x00028, + 0x00071, 0x00029, 0x00000, 0x00028, 0x00072, 0x00029, 0x00000, 0x00028, + 0x00073, 0x00029, 0x00000, 0x00028, 0x00074, 0x00029, 0x00000, 0x00028, + 0x00075, 0x00029, 0x00000, 0x00028, 0x00076, 0x00029, 0x00000, 0x00028, + 0x00077, 0x00029, 0x00000, 0x00028, 0x00078, 0x00029, 0x00000, 0x00028, + 0x00079, 0x00029, 0x00000, 0x00028, 0x0007a, 0x00029, 0x00000, 0x0222b, + 0x0222b, 0x0222b, 0x0222b, 0x00000, 0x0003a, 0x0003a, 0x0003d, 0x00000, + 0x0003d, 0x0003d, 0x00000, 0x0003d, 0x0003d, 0x0003d, 0x00000, 0x02add, + 0x00338, 0x00000, 0x0304b, 0x03099, 0x00000, 0x0304d, 0x03099, 0x00000, + 0x0304f, 0x03099, 0x00000, 0x03051, 0x03099, 0x00000, 0x03053, 0x03099, + 0x00000, 0x03055, 0x03099, 0x00000, 0x03057, 0x03099, 0x00000, 0x03059, + 0x03099, 0x00000, 0x0305b, 0x03099, 0x00000, 0x0305d, 0x03099, 0x00000, + 0x0305f, 0x03099, 0x00000, 0x03061, 0x03099, 0x00000, 0x03064, 0x03099, + 0x00000, 0x03066, 0x03099, 0x00000, 0x03068, 0x03099, 0x00000, 0x0306f, + 0x03099, 0x00000, 0x0306f, 0x0309a, 0x00000, 0x03072, 0x03099, 0x00000, + 0x03072, 0x0309a, 0x00000, 0x03075, 0x03099, 0x00000, 0x03075, 0x0309a, + 0x00000, 0x03078, 0x03099, 0x00000, 0x03078, 0x0309a, 0x00000, 0x0307b, + 0x03099, 0x00000, 0x0307b, 0x0309a, 0x00000, 0x03046, 0x03099, 0x00000, + 0x00020, 0x03099, 0x00000, 0x00020, 0x0309a, 0x00000, 0x0309d, 0x03099, + 0x00000, 0x03088, 0x0308a, 0x00000, 0x030ab, 0x03099, 0x00000, 0x030ad, + 0x03099, 0x00000, 0x030af, 0x03099, 0x00000, 0x030b1, 0x03099, 0x00000, + 0x030b3, 0x03099, 0x00000, 0x030b5, 0x03099, 0x00000, 0x030b7, 0x03099, + 0x00000, 0x030b9, 0x03099, 0x00000, 0x030bb, 0x03099, 0x00000, 0x030bd, + 0x03099, 0x00000, 0x030bf, 0x03099, 0x00000, 0x030c1, 0x03099, 0x00000, + 0x030c4, 0x03099, 0x00000, 0x030c6, 0x03099, 0x00000, 0x030c8, 0x03099, + 0x00000, 0x030cf, 0x03099, 0x00000, 0x030cf, 0x0309a, 0x00000, 0x030d2, + 0x03099, 0x00000, 0x030d2, 0x0309a, 0x00000, 0x030d5, 0x03099, 0x00000, + 0x030d5, 0x0309a, 0x00000, 0x030d8, 0x03099, 0x00000, 0x030d8, 0x0309a, + 0x00000, 0x030db, 0x03099, 0x00000, 0x030db, 0x0309a, 0x00000, 0x030a6, + 0x03099, 0x00000, 0x030ef, 0x03099, 0x00000, 0x030f0, 0x03099, 0x00000, + 0x030f1, 0x03099, 0x00000, 0x030f2, 0x03099, 0x00000, 0x030fd, 0x03099, + 0x00000, 0x030b3, 0x030c8, 0x00000, 0x00028, 0x01100, 0x00029, 0x00000, + 0x00028, 0x01102, 0x00029, 0x00000, 0x00028, 0x01103, 0x00029, 0x00000, + 0x00028, 0x01105, 0x00029, 0x00000, 0x00028, 0x01106, 0x00029, 0x00000, + 0x00028, 0x01107, 0x00029, 0x00000, 0x00028, 0x01109, 0x00029, 0x00000, + 0x00028, 0x0110b, 0x00029, 0x00000, 0x00028, 0x0110c, 0x00029, 0x00000, + 0x00028, 0x0110e, 0x00029, 0x00000, 0x00028, 0x0110f, 0x00029, 0x00000, + 0x00028, 0x01110, 0x00029, 0x00000, 0x00028, 0x01111, 0x00029, 0x00000, + 0x00028, 0x01112, 0x00029, 0x00000, 0x00028, 0x01100, 0x01161, 0x00029, + 0x00000, 0x00028, 0x01102, 0x01161, 0x00029, 0x00000, 0x00028, 0x01103, + 0x01161, 0x00029, 0x00000, 0x00028, 0x01105, 0x01161, 0x00029, 0x00000, + 0x00028, 0x01106, 0x01161, 0x00029, 0x00000, 0x00028, 0x01107, 0x01161, + 0x00029, 0x00000, 0x00028, 0x01109, 0x01161, 0x00029, 0x00000, 0x00028, + 0x0110b, 0x01161, 0x00029, 0x00000, 0x00028, 0x0110c, 0x01161, 0x00029, + 0x00000, 0x00028, 0x0110e, 0x01161, 0x00029, 0x00000, 0x00028, 0x0110f, + 0x01161, 0x00029, 0x00000, 0x00028, 0x01110, 0x01161, 0x00029, 0x00000, + 0x00028, 0x01111, 0x01161, 0x00029, 0x00000, 0x00028, 0x01112, 0x01161, + 0x00029, 0x00000, 0x00028, 0x0110c, 0x0116e, 0x00029, 0x00000, 0x00028, + 0x0110b, 0x01169, 0x0110c, 0x01165, 0x011ab, 0x00029, 0x00000, 0x00028, + 0x0110b, 0x01169, 0x01112, 0x0116e, 0x00029, 0x00000, 0x00028, 0x04e00, + 0x00029, 0x00000, 0x00028, 0x04e8c, 0x00029, 0x00000, 0x00028, 0x04e09, + 0x00029, 0x00000, 0x00028, 0x056db, 0x00029, 0x00000, 0x00028, 0x04e94, + 0x00029, 0x00000, 0x00028, 0x0516d, 0x00029, 0x00000, 0x00028, 0x04e03, + 0x00029, 0x00000, 0x00028, 0x0516b, 0x00029, 0x00000, 0x00028, 0x04e5d, + 0x00029, 0x00000, 0x00028, 0x05341, 0x00029, 0x00000, 0x00028, 0x06708, + 0x00029, 0x00000, 0x00028, 0x0706b, 0x00029, 0x00000, 0x00028, 0x06c34, + 0x00029, 0x00000, 0x00028, 0x06728, 0x00029, 0x00000, 0x00028, 0x091d1, + 0x00029, 0x00000, 0x00028, 0x0571f, 0x00029, 0x00000, 0x00028, 0x065e5, + 0x00029, 0x00000, 0x00028, 0x0682a, 0x00029, 0x00000, 0x00028, 0x06709, + 0x00029, 0x00000, 0x00028, 0x0793e, 0x00029, 0x00000, 0x00028, 0x0540d, + 0x00029, 0x00000, 0x00028, 0x07279, 0x00029, 0x00000, 0x00028, 0x08ca1, + 0x00029, 0x00000, 0x00028, 0x0795d, 0x00029, 0x00000, 0x00028, 0x052b4, + 0x00029, 0x00000, 0x00028, 0x04ee3, 0x00029, 0x00000, 0x00028, 0x0547c, + 0x00029, 0x00000, 0x00028, 0x05b66, 0x00029, 0x00000, 0x00028, 0x076e3, + 0x00029, 0x00000, 0x00028, 0x04f01, 0x00029, 0x00000, 0x00028, 0x08cc7, + 0x00029, 0x00000, 0x00028, 0x05354, 0x00029, 0x00000, 0x00028, 0x0796d, + 0x00029, 0x00000, 0x00028, 0x04f11, 0x00029, 0x00000, 0x00028, 0x081ea, + 0x00029, 0x00000, 0x00028, 0x081f3, 0x00029, 0x00000, 0x00050, 0x00054, + 0x00045, 0x00000, 0x00032, 0x00031, 0x00000, 0x00032, 0x00032, 0x00000, + 0x00032, 0x00033, 0x00000, 0x00032, 0x00034, 0x00000, 0x00032, 0x00035, + 0x00000, 0x00032, 0x00036, 0x00000, 0x00032, 0x00037, 0x00000, 0x00032, + 0x00038, 0x00000, 0x00032, 0x00039, 0x00000, 0x00033, 0x00030, 0x00000, + 0x00033, 0x00031, 0x00000, 0x00033, 0x00032, 0x00000, 0x00033, 0x00033, + 0x00000, 0x00033, 0x00034, 0x00000, 0x00033, 0x00035, 0x00000, 0x01100, + 0x01161, 0x00000, 0x01102, 0x01161, 0x00000, 0x01103, 0x01161, 0x00000, + 0x01105, 0x01161, 0x00000, 0x01106, 0x01161, 0x00000, 0x01107, 0x01161, + 0x00000, 0x01109, 0x01161, 0x00000, 0x0110b, 0x01161, 0x00000, 0x0110c, + 0x01161, 0x00000, 0x0110e, 0x01161, 0x00000, 0x0110f, 0x01161, 0x00000, + 0x01110, 0x01161, 0x00000, 0x01111, 0x01161, 0x00000, 0x01112, 0x01161, + 0x00000, 0x0110e, 0x01161, 0x011b7, 0x01100, 0x01169, 0x00000, 0x0110c, + 0x0116e, 0x0110b, 0x01174, 0x00000, 0x0110b, 0x0116e, 0x00000, 0x00033, + 0x00036, 0x00000, 0x00033, 0x00037, 0x00000, 0x00033, 0x00038, 0x00000, + 0x00033, 0x00039, 0x00000, 0x00034, 0x00030, 0x00000, 0x00034, 0x00031, + 0x00000, 0x00034, 0x00032, 0x00000, 0x00034, 0x00033, 0x00000, 0x00034, + 0x00034, 0x00000, 0x00034, 0x00035, 0x00000, 0x00034, 0x00036, 0x00000, + 0x00034, 0x00037, 0x00000, 0x00034, 0x00038, 0x00000, 0x00034, 0x00039, + 0x00000, 0x00035, 0x00030, 0x00000, 0x00031, 0x06708, 0x00000, 0x00032, + 0x06708, 0x00000, 0x00033, 0x06708, 0x00000, 0x00034, 0x06708, 0x00000, + 0x00035, 0x06708, 0x00000, 0x00036, 0x06708, 0x00000, 0x00037, 0x06708, + 0x00000, 0x00038, 0x06708, 0x00000, 0x00039, 0x06708, 0x00000, 0x00031, + 0x00030, 0x06708, 0x00000, 0x00031, 0x00031, 0x06708, 0x00000, 0x00031, + 0x00032, 0x06708, 0x00000, 0x00048, 0x00067, 0x00000, 0x00065, 0x00072, + 0x00067, 0x00000, 0x00065, 0x00056, 0x00000, 0x0004c, 0x00054, 0x00044, + 0x00000, 0x030a2, 0x030d1, 0x030fc, 0x030c8, 0x00000, 0x030a2, 0x030eb, + 0x030d5, 0x030a1, 0x00000, 0x030a2, 0x030f3, 0x030da, 0x030a2, 0x00000, + 0x030a2, 0x030fc, 0x030eb, 0x00000, 0x030a4, 0x030cb, 0x030f3, 0x030b0, + 0x00000, 0x030a4, 0x030f3, 0x030c1, 0x00000, 0x030a6, 0x030a9, 0x030f3, + 0x00000, 0x030a8, 0x030b9, 0x030af, 0x030fc, 0x030c9, 0x00000, 0x030a8, + 0x030fc, 0x030ab, 0x030fc, 0x00000, 0x030aa, 0x030f3, 0x030b9, 0x00000, + 0x030aa, 0x030fc, 0x030e0, 0x00000, 0x030ab, 0x030a4, 0x030ea, 0x00000, + 0x030ab, 0x030e9, 0x030c3, 0x030c8, 0x00000, 0x030ab, 0x030ed, 0x030ea, + 0x030fc, 0x00000, 0x030ac, 0x030ed, 0x030f3, 0x00000, 0x030ac, 0x030f3, + 0x030de, 0x00000, 0x030ae, 0x030ac, 0x00000, 0x030ae, 0x030cb, 0x030fc, + 0x00000, 0x030ad, 0x030e5, 0x030ea, 0x030fc, 0x00000, 0x030ae, 0x030eb, + 0x030c0, 0x030fc, 0x00000, 0x030ad, 0x030ed, 0x00000, 0x030ad, 0x030ed, + 0x030b0, 0x030e9, 0x030e0, 0x00000, 0x030ad, 0x030ed, 0x030e1, 0x030fc, + 0x030c8, 0x030eb, 0x00000, 0x030ad, 0x030ed, 0x030ef, 0x030c3, 0x030c8, + 0x00000, 0x030b0, 0x030e9, 0x030e0, 0x00000, 0x030b0, 0x030e9, 0x030e0, + 0x030c8, 0x030f3, 0x00000, 0x030af, 0x030eb, 0x030bc, 0x030a4, 0x030ed, + 0x00000, 0x030af, 0x030ed, 0x030fc, 0x030cd, 0x00000, 0x030b1, 0x030fc, + 0x030b9, 0x00000, 0x030b3, 0x030eb, 0x030ca, 0x00000, 0x030b3, 0x030fc, + 0x030dd, 0x00000, 0x030b5, 0x030a4, 0x030af, 0x030eb, 0x00000, 0x030b5, + 0x030f3, 0x030c1, 0x030fc, 0x030e0, 0x00000, 0x030b7, 0x030ea, 0x030f3, + 0x030b0, 0x00000, 0x030bb, 0x030f3, 0x030c1, 0x00000, 0x030bb, 0x030f3, + 0x030c8, 0x00000, 0x030c0, 0x030fc, 0x030b9, 0x00000, 0x030c7, 0x030b7, + 0x00000, 0x030c9, 0x030eb, 0x00000, 0x030c8, 0x030f3, 0x00000, 0x030ca, + 0x030ce, 0x00000, 0x030ce, 0x030c3, 0x030c8, 0x00000, 0x030cf, 0x030a4, + 0x030c4, 0x00000, 0x030d1, 0x030fc, 0x030bb, 0x030f3, 0x030c8, 0x00000, + 0x030d1, 0x030fc, 0x030c4, 0x00000, 0x030d0, 0x030fc, 0x030ec, 0x030eb, + 0x00000, 0x030d4, 0x030a2, 0x030b9, 0x030c8, 0x030eb, 0x00000, 0x030d4, + 0x030af, 0x030eb, 0x00000, 0x030d4, 0x030b3, 0x00000, 0x030d3, 0x030eb, + 0x00000, 0x030d5, 0x030a1, 0x030e9, 0x030c3, 0x030c9, 0x00000, 0x030d5, + 0x030a3, 0x030fc, 0x030c8, 0x00000, 0x030d6, 0x030c3, 0x030b7, 0x030a7, + 0x030eb, 0x00000, 0x030d5, 0x030e9, 0x030f3, 0x00000, 0x030d8, 0x030af, + 0x030bf, 0x030fc, 0x030eb, 0x00000, 0x030da, 0x030bd, 0x00000, 0x030da, + 0x030cb, 0x030d2, 0x00000, 0x030d8, 0x030eb, 0x030c4, 0x00000, 0x030da, + 0x030f3, 0x030b9, 0x00000, 0x030da, 0x030fc, 0x030b8, 0x00000, 0x030d9, + 0x030fc, 0x030bf, 0x00000, 0x030dd, 0x030a4, 0x030f3, 0x030c8, 0x00000, + 0x030dc, 0x030eb, 0x030c8, 0x00000, 0x030db, 0x030f3, 0x00000, 0x030dd, + 0x030f3, 0x030c9, 0x00000, 0x030db, 0x030fc, 0x030eb, 0x00000, 0x030db, + 0x030fc, 0x030f3, 0x00000, 0x030de, 0x030a4, 0x030af, 0x030ed, 0x00000, + 0x030de, 0x030a4, 0x030eb, 0x00000, 0x030de, 0x030c3, 0x030cf, 0x00000, + 0x030de, 0x030eb, 0x030af, 0x00000, 0x030de, 0x030f3, 0x030b7, 0x030e7, + 0x030f3, 0x00000, 0x030df, 0x030af, 0x030ed, 0x030f3, 0x00000, 0x030df, + 0x030ea, 0x00000, 0x030df, 0x030ea, 0x030d0, 0x030fc, 0x030eb, 0x00000, + 0x030e1, 0x030ac, 0x00000, 0x030e1, 0x030ac, 0x030c8, 0x030f3, 0x00000, + 0x030e1, 0x030fc, 0x030c8, 0x030eb, 0x00000, 0x030e4, 0x030fc, 0x030c9, + 0x00000, 0x030e4, 0x030fc, 0x030eb, 0x00000, 0x030e6, 0x030a2, 0x030f3, + 0x00000, 0x030ea, 0x030c3, 0x030c8, 0x030eb, 0x00000, 0x030ea, 0x030e9, + 0x00000, 0x030eb, 0x030d4, 0x030fc, 0x00000, 0x030eb, 0x030fc, 0x030d6, + 0x030eb, 0x00000, 0x030ec, 0x030e0, 0x00000, 0x030ec, 0x030f3, 0x030c8, + 0x030b2, 0x030f3, 0x00000, 0x030ef, 0x030c3, 0x030c8, 0x00000, 0x00030, + 0x070b9, 0x00000, 0x00031, 0x070b9, 0x00000, 0x00032, 0x070b9, 0x00000, + 0x00033, 0x070b9, 0x00000, 0x00034, 0x070b9, 0x00000, 0x00035, 0x070b9, + 0x00000, 0x00036, 0x070b9, 0x00000, 0x00037, 0x070b9, 0x00000, 0x00038, + 0x070b9, 0x00000, 0x00039, 0x070b9, 0x00000, 0x00031, 0x00030, 0x070b9, + 0x00000, 0x00031, 0x00031, 0x070b9, 0x00000, 0x00031, 0x00032, 0x070b9, + 0x00000, 0x00031, 0x00033, 0x070b9, 0x00000, 0x00031, 0x00034, 0x070b9, + 0x00000, 0x00031, 0x00035, 0x070b9, 0x00000, 0x00031, 0x00036, 0x070b9, + 0x00000, 0x00031, 0x00037, 0x070b9, 0x00000, 0x00031, 0x00038, 0x070b9, + 0x00000, 0x00031, 0x00039, 0x070b9, 0x00000, 0x00032, 0x00030, 0x070b9, + 0x00000, 0x00032, 0x00031, 0x070b9, 0x00000, 0x00032, 0x00032, 0x070b9, + 0x00000, 0x00032, 0x00033, 0x070b9, 0x00000, 0x00032, 0x00034, 0x070b9, + 0x00000, 0x00068, 0x00050, 0x00061, 0x00000, 0x00064, 0x00061, 0x00000, + 0x00041, 0x00055, 0x00000, 0x00062, 0x00061, 0x00072, 0x00000, 0x0006f, + 0x00056, 0x00000, 0x00070, 0x00063, 0x00000, 0x00064, 0x0006d, 0x00000, + 0x00064, 0x0006d, 0x000b2, 0x00000, 0x00064, 0x0006d, 0x000b3, 0x00000, + 0x00049, 0x00055, 0x00000, 0x05e73, 0x06210, 0x00000, 0x0662d, 0x0548c, + 0x00000, 0x05927, 0x06b63, 0x00000, 0x0660e, 0x06cbb, 0x00000, 0x0682a, + 0x05f0f, 0x04f1a, 0x0793e, 0x00000, 0x00070, 0x00041, 0x00000, 0x0006e, + 0x00041, 0x00000, 0x003bc, 0x00041, 0x00000, 0x0006d, 0x00041, 0x00000, + 0x0006b, 0x00041, 0x00000, 0x0004b, 0x00042, 0x00000, 0x0004d, 0x00042, + 0x00000, 0x00047, 0x00042, 0x00000, 0x00063, 0x00061, 0x0006c, 0x00000, + 0x0006b, 0x00063, 0x00061, 0x0006c, 0x00000, 0x00070, 0x00046, 0x00000, + 0x0006e, 0x00046, 0x00000, 0x003bc, 0x00046, 0x00000, 0x003bc, 0x00067, + 0x00000, 0x0006d, 0x00067, 0x00000, 0x0006b, 0x00067, 0x00000, 0x00048, + 0x0007a, 0x00000, 0x0006b, 0x00048, 0x0007a, 0x00000, 0x0004d, 0x00048, + 0x0007a, 0x00000, 0x00047, 0x00048, 0x0007a, 0x00000, 0x00054, 0x00048, + 0x0007a, 0x00000, 0x003bc, 0x02113, 0x00000, 0x0006d, 0x02113, 0x00000, + 0x00064, 0x02113, 0x00000, 0x0006b, 0x02113, 0x00000, 0x00066, 0x0006d, + 0x00000, 0x0006e, 0x0006d, 0x00000, 0x003bc, 0x0006d, 0x00000, 0x0006d, + 0x0006d, 0x00000, 0x00063, 0x0006d, 0x00000, 0x0006b, 0x0006d, 0x00000, + 0x0006d, 0x0006d, 0x000b2, 0x00000, 0x00063, 0x0006d, 0x000b2, 0x00000, + 0x0006d, 0x000b2, 0x00000, 0x0006b, 0x0006d, 0x000b2, 0x00000, 0x0006d, + 0x0006d, 0x000b3, 0x00000, 0x00063, 0x0006d, 0x000b3, 0x00000, 0x0006d, + 0x000b3, 0x00000, 0x0006b, 0x0006d, 0x000b3, 0x00000, 0x0006d, 0x02215, + 0x00073, 0x00000, 0x0006d, 0x02215, 0x00073, 0x000b2, 0x00000, 0x00050, + 0x00061, 0x00000, 0x0006b, 0x00050, 0x00061, 0x00000, 0x0004d, 0x00050, + 0x00061, 0x00000, 0x00047, 0x00050, 0x00061, 0x00000, 0x00072, 0x00061, + 0x00064, 0x00000, 0x00072, 0x00061, 0x00064, 0x02215, 0x00073, 0x00000, + 0x00072, 0x00061, 0x00064, 0x02215, 0x00073, 0x000b2, 0x00000, 0x00070, + 0x00073, 0x00000, 0x0006e, 0x00073, 0x00000, 0x003bc, 0x00073, 0x00000, + 0x0006d, 0x00073, 0x00000, 0x00070, 0x00056, 0x00000, 0x0006e, 0x00056, + 0x00000, 0x003bc, 0x00056, 0x00000, 0x0006d, 0x00056, 0x00000, 0x0006b, + 0x00056, 0x00000, 0x0004d, 0x00056, 0x00000, 0x00070, 0x00057, 0x00000, + 0x0006e, 0x00057, 0x00000, 0x003bc, 0x00057, 0x00000, 0x0006d, 0x00057, + 0x00000, 0x0006b, 0x00057, 0x00000, 0x0004d, 0x00057, 0x00000, 0x0006b, + 0x003a9, 0x00000, 0x0004d, 0x003a9, 0x00000, 0x00061, 0x0002e, 0x0006d, + 0x0002e, 0x00000, 0x00042, 0x00071, 0x00000, 0x00063, 0x00063, 0x00000, + 0x00063, 0x00064, 0x00000, 0x00043, 0x02215, 0x0006b, 0x00067, 0x00000, + 0x00043, 0x0006f, 0x0002e, 0x00000, 0x00064, 0x00042, 0x00000, 0x00047, + 0x00079, 0x00000, 0x00068, 0x00061, 0x00000, 0x00048, 0x00050, 0x00000, + 0x00069, 0x0006e, 0x00000, 0x0004b, 0x0004b, 0x00000, 0x0004b, 0x0004d, + 0x00000, 0x0006b, 0x00074, 0x00000, 0x0006c, 0x0006d, 0x00000, 0x0006c, + 0x0006e, 0x00000, 0x0006c, 0x0006f, 0x00067, 0x00000, 0x0006c, 0x00078, + 0x00000, 0x0006d, 0x00062, 0x00000, 0x0006d, 0x00069, 0x0006c, 0x00000, + 0x0006d, 0x0006f, 0x0006c, 0x00000, 0x00050, 0x00048, 0x00000, 0x00070, + 0x0002e, 0x0006d, 0x0002e, 0x00000, 0x00050, 0x00050, 0x0004d, 0x00000, + 0x00050, 0x00052, 0x00000, 0x00073, 0x00072, 0x00000, 0x00053, 0x00076, + 0x00000, 0x00057, 0x00062, 0x00000, 0x00056, 0x02215, 0x0006d, 0x00000, + 0x00041, 0x02215, 0x0006d, 0x00000, 0x00031, 0x065e5, 0x00000, 0x00032, + 0x065e5, 0x00000, 0x00033, 0x065e5, 0x00000, 0x00034, 0x065e5, 0x00000, + 0x00035, 0x065e5, 0x00000, 0x00036, 0x065e5, 0x00000, 0x00037, 0x065e5, + 0x00000, 0x00038, 0x065e5, 0x00000, 0x00039, 0x065e5, 0x00000, 0x00031, + 0x00030, 0x065e5, 0x00000, 0x00031, 0x00031, 0x065e5, 0x00000, 0x00031, + 0x00032, 0x065e5, 0x00000, 0x00031, 0x00033, 0x065e5, 0x00000, 0x00031, + 0x00034, 0x065e5, 0x00000, 0x00031, 0x00035, 0x065e5, 0x00000, 0x00031, + 0x00036, 0x065e5, 0x00000, 0x00031, 0x00037, 0x065e5, 0x00000, 0x00031, + 0x00038, 0x065e5, 0x00000, 0x00031, 0x00039, 0x065e5, 0x00000, 0x00032, + 0x00030, 0x065e5, 0x00000, 0x00032, 0x00031, 0x065e5, 0x00000, 0x00032, + 0x00032, 0x065e5, 0x00000, 0x00032, 0x00033, 0x065e5, 0x00000, 0x00032, + 0x00034, 0x065e5, 0x00000, 0x00032, 0x00035, 0x065e5, 0x00000, 0x00032, + 0x00036, 0x065e5, 0x00000, 0x00032, 0x00037, 0x065e5, 0x00000, 0x00032, + 0x00038, 0x065e5, 0x00000, 0x00032, 0x00039, 0x065e5, 0x00000, 0x00033, + 0x00030, 0x065e5, 0x00000, 0x00033, 0x00031, 0x065e5, 0x00000, 0x00067, + 0x00061, 0x0006c, 0x00000, 0x00066, 0x00066, 0x00000, 0x00066, 0x00069, + 0x00000, 0x00066, 0x0006c, 0x00000, 0x00066, 0x00066, 0x00069, 0x00000, + 0x00066, 0x00066, 0x0006c, 0x00000, 0x0017f, 0x00074, 0x00000, 0x00073, + 0x00074, 0x00000, 0x00574, 0x00576, 0x00000, 0x00574, 0x00565, 0x00000, + 0x00574, 0x0056b, 0x00000, 0x0057e, 0x00576, 0x00000, 0x00574, 0x0056d, + 0x00000, 0x005d9, 0x005b4, 0x00000, 0x005f2, 0x005b7, 0x00000, 0x005e9, + 0x005c1, 0x00000, 0x005e9, 0x005c2, 0x00000, 0x0fb49, 0x005c1, 0x00000, + 0x0fb49, 0x005c2, 0x00000, 0x005d0, 0x005b7, 0x00000, 0x005d0, 0x005b8, + 0x00000, 0x005d0, 0x005bc, 0x00000, 0x005d1, 0x005bc, 0x00000, 0x005d2, + 0x005bc, 0x00000, 0x005d3, 0x005bc, 0x00000, 0x005d4, 0x005bc, 0x00000, + 0x005d5, 0x005bc, 0x00000, 0x005d6, 0x005bc, 0x00000, 0x005d8, 0x005bc, + 0x00000, 0x005d9, 0x005bc, 0x00000, 0x005da, 0x005bc, 0x00000, 0x005db, + 0x005bc, 0x00000, 0x005dc, 0x005bc, 0x00000, 0x005de, 0x005bc, 0x00000, + 0x005e0, 0x005bc, 0x00000, 0x005e1, 0x005bc, 0x00000, 0x005e3, 0x005bc, + 0x00000, 0x005e4, 0x005bc, 0x00000, 0x005e6, 0x005bc, 0x00000, 0x005e7, + 0x005bc, 0x00000, 0x005e8, 0x005bc, 0x00000, 0x005e9, 0x005bc, 0x00000, + 0x005ea, 0x005bc, 0x00000, 0x005d5, 0x005b9, 0x00000, 0x005d1, 0x005bf, + 0x00000, 0x005db, 0x005bf, 0x00000, 0x005e4, 0x005bf, 0x00000, 0x005d0, + 0x005dc, 0x00000, 0x00626, 0x00627, 0x00000, 0x00626, 0x00627, 0x00000, + 0x00626, 0x006d5, 0x00000, 0x00626, 0x006d5, 0x00000, 0x00626, 0x00648, + 0x00000, 0x00626, 0x00648, 0x00000, 0x00626, 0x006c7, 0x00000, 0x00626, + 0x006c7, 0x00000, 0x00626, 0x006c6, 0x00000, 0x00626, 0x006c6, 0x00000, + 0x00626, 0x006c8, 0x00000, 0x00626, 0x006c8, 0x00000, 0x00626, 0x006d0, + 0x00000, 0x00626, 0x006d0, 0x00000, 0x00626, 0x006d0, 0x00000, 0x00626, + 0x00649, 0x00000, 0x00626, 0x00649, 0x00000, 0x00626, 0x00649, 0x00000, + 0x00626, 0x0062c, 0x00000, 0x00626, 0x0062d, 0x00000, 0x00626, 0x00645, + 0x00000, 0x00626, 0x00649, 0x00000, 0x00626, 0x0064a, 0x00000, 0x00628, + 0x0062c, 0x00000, 0x00628, 0x0062d, 0x00000, 0x00628, 0x0062e, 0x00000, + 0x00628, 0x00645, 0x00000, 0x00628, 0x00649, 0x00000, 0x00628, 0x0064a, + 0x00000, 0x0062a, 0x0062c, 0x00000, 0x0062a, 0x0062d, 0x00000, 0x0062a, + 0x0062e, 0x00000, 0x0062a, 0x00645, 0x00000, 0x0062a, 0x00649, 0x00000, + 0x0062a, 0x0064a, 0x00000, 0x0062b, 0x0062c, 0x00000, 0x0062b, 0x00645, + 0x00000, 0x0062b, 0x00649, 0x00000, 0x0062b, 0x0064a, 0x00000, 0x0062c, + 0x0062d, 0x00000, 0x0062c, 0x00645, 0x00000, 0x0062d, 0x0062c, 0x00000, + 0x0062d, 0x00645, 0x00000, 0x0062e, 0x0062c, 0x00000, 0x0062e, 0x0062d, + 0x00000, 0x0062e, 0x00645, 0x00000, 0x00633, 0x0062c, 0x00000, 0x00633, + 0x0062d, 0x00000, 0x00633, 0x0062e, 0x00000, 0x00633, 0x00645, 0x00000, + 0x00635, 0x0062d, 0x00000, 0x00635, 0x00645, 0x00000, 0x00636, 0x0062c, + 0x00000, 0x00636, 0x0062d, 0x00000, 0x00636, 0x0062e, 0x00000, 0x00636, + 0x00645, 0x00000, 0x00637, 0x0062d, 0x00000, 0x00637, 0x00645, 0x00000, + 0x00638, 0x00645, 0x00000, 0x00639, 0x0062c, 0x00000, 0x00639, 0x00645, + 0x00000, 0x0063a, 0x0062c, 0x00000, 0x0063a, 0x00645, 0x00000, 0x00641, + 0x0062c, 0x00000, 0x00641, 0x0062d, 0x00000, 0x00641, 0x0062e, 0x00000, + 0x00641, 0x00645, 0x00000, 0x00641, 0x00649, 0x00000, 0x00641, 0x0064a, + 0x00000, 0x00642, 0x0062d, 0x00000, 0x00642, 0x00645, 0x00000, 0x00642, + 0x00649, 0x00000, 0x00642, 0x0064a, 0x00000, 0x00643, 0x00627, 0x00000, + 0x00643, 0x0062c, 0x00000, 0x00643, 0x0062d, 0x00000, 0x00643, 0x0062e, + 0x00000, 0x00643, 0x00644, 0x00000, 0x00643, 0x00645, 0x00000, 0x00643, + 0x00649, 0x00000, 0x00643, 0x0064a, 0x00000, 0x00644, 0x0062c, 0x00000, + 0x00644, 0x0062d, 0x00000, 0x00644, 0x0062e, 0x00000, 0x00644, 0x00645, + 0x00000, 0x00644, 0x00649, 0x00000, 0x00644, 0x0064a, 0x00000, 0x00645, + 0x0062c, 0x00000, 0x00645, 0x0062d, 0x00000, 0x00645, 0x0062e, 0x00000, + 0x00645, 0x00645, 0x00000, 0x00645, 0x00649, 0x00000, 0x00645, 0x0064a, + 0x00000, 0x00646, 0x0062c, 0x00000, 0x00646, 0x0062d, 0x00000, 0x00646, + 0x0062e, 0x00000, 0x00646, 0x00645, 0x00000, 0x00646, 0x00649, 0x00000, + 0x00646, 0x0064a, 0x00000, 0x00647, 0x0062c, 0x00000, 0x00647, 0x00645, + 0x00000, 0x00647, 0x00649, 0x00000, 0x00647, 0x0064a, 0x00000, 0x0064a, + 0x0062c, 0x00000, 0x0064a, 0x0062d, 0x00000, 0x0064a, 0x0062e, 0x00000, + 0x0064a, 0x00645, 0x00000, 0x0064a, 0x00649, 0x00000, 0x0064a, 0x0064a, + 0x00000, 0x00630, 0x00670, 0x00000, 0x00631, 0x00670, 0x00000, 0x00649, + 0x00670, 0x00000, 0x00020, 0x0064c, 0x00651, 0x00000, 0x00020, 0x0064d, + 0x00651, 0x00000, 0x00020, 0x0064e, 0x00651, 0x00000, 0x00020, 0x0064f, + 0x00651, 0x00000, 0x00020, 0x00650, 0x00651, 0x00000, 0x00020, 0x00651, + 0x00670, 0x00000, 0x00626, 0x00631, 0x00000, 0x00626, 0x00632, 0x00000, + 0x00626, 0x00645, 0x00000, 0x00626, 0x00646, 0x00000, 0x00626, 0x00649, + 0x00000, 0x00626, 0x0064a, 0x00000, 0x00628, 0x00631, 0x00000, 0x00628, + 0x00632, 0x00000, 0x00628, 0x00645, 0x00000, 0x00628, 0x00646, 0x00000, + 0x00628, 0x00649, 0x00000, 0x00628, 0x0064a, 0x00000, 0x0062a, 0x00631, + 0x00000, 0x0062a, 0x00632, 0x00000, 0x0062a, 0x00645, 0x00000, 0x0062a, + 0x00646, 0x00000, 0x0062a, 0x00649, 0x00000, 0x0062a, 0x0064a, 0x00000, + 0x0062b, 0x00631, 0x00000, 0x0062b, 0x00632, 0x00000, 0x0062b, 0x00645, + 0x00000, 0x0062b, 0x00646, 0x00000, 0x0062b, 0x00649, 0x00000, 0x0062b, + 0x0064a, 0x00000, 0x00641, 0x00649, 0x00000, 0x00641, 0x0064a, 0x00000, + 0x00642, 0x00649, 0x00000, 0x00642, 0x0064a, 0x00000, 0x00643, 0x00627, + 0x00000, 0x00643, 0x00644, 0x00000, 0x00643, 0x00645, 0x00000, 0x00643, + 0x00649, 0x00000, 0x00643, 0x0064a, 0x00000, 0x00644, 0x00645, 0x00000, + 0x00644, 0x00649, 0x00000, 0x00644, 0x0064a, 0x00000, 0x00645, 0x00627, + 0x00000, 0x00645, 0x00645, 0x00000, 0x00646, 0x00631, 0x00000, 0x00646, + 0x00632, 0x00000, 0x00646, 0x00645, 0x00000, 0x00646, 0x00646, 0x00000, + 0x00646, 0x00649, 0x00000, 0x00646, 0x0064a, 0x00000, 0x00649, 0x00670, + 0x00000, 0x0064a, 0x00631, 0x00000, 0x0064a, 0x00632, 0x00000, 0x0064a, + 0x00645, 0x00000, 0x0064a, 0x00646, 0x00000, 0x0064a, 0x00649, 0x00000, + 0x0064a, 0x0064a, 0x00000, 0x00626, 0x0062c, 0x00000, 0x00626, 0x0062d, + 0x00000, 0x00626, 0x0062e, 0x00000, 0x00626, 0x00645, 0x00000, 0x00626, + 0x00647, 0x00000, 0x00628, 0x0062c, 0x00000, 0x00628, 0x0062d, 0x00000, + 0x00628, 0x0062e, 0x00000, 0x00628, 0x00645, 0x00000, 0x00628, 0x00647, + 0x00000, 0x0062a, 0x0062c, 0x00000, 0x0062a, 0x0062d, 0x00000, 0x0062a, + 0x0062e, 0x00000, 0x0062a, 0x00645, 0x00000, 0x0062a, 0x00647, 0x00000, + 0x0062b, 0x00645, 0x00000, 0x0062c, 0x0062d, 0x00000, 0x0062c, 0x00645, + 0x00000, 0x0062d, 0x0062c, 0x00000, 0x0062d, 0x00645, 0x00000, 0x0062e, + 0x0062c, 0x00000, 0x0062e, 0x00645, 0x00000, 0x00633, 0x0062c, 0x00000, + 0x00633, 0x0062d, 0x00000, 0x00633, 0x0062e, 0x00000, 0x00633, 0x00645, + 0x00000, 0x00635, 0x0062d, 0x00000, 0x00635, 0x0062e, 0x00000, 0x00635, + 0x00645, 0x00000, 0x00636, 0x0062c, 0x00000, 0x00636, 0x0062d, 0x00000, + 0x00636, 0x0062e, 0x00000, 0x00636, 0x00645, 0x00000, 0x00637, 0x0062d, + 0x00000, 0x00638, 0x00645, 0x00000, 0x00639, 0x0062c, 0x00000, 0x00639, + 0x00645, 0x00000, 0x0063a, 0x0062c, 0x00000, 0x0063a, 0x00645, 0x00000, + 0x00641, 0x0062c, 0x00000, 0x00641, 0x0062d, 0x00000, 0x00641, 0x0062e, + 0x00000, 0x00641, 0x00645, 0x00000, 0x00642, 0x0062d, 0x00000, 0x00642, + 0x00645, 0x00000, 0x00643, 0x0062c, 0x00000, 0x00643, 0x0062d, 0x00000, + 0x00643, 0x0062e, 0x00000, 0x00643, 0x00644, 0x00000, 0x00643, 0x00645, + 0x00000, 0x00644, 0x0062c, 0x00000, 0x00644, 0x0062d, 0x00000, 0x00644, + 0x0062e, 0x00000, 0x00644, 0x00645, 0x00000, 0x00644, 0x00647, 0x00000, + 0x00645, 0x0062c, 0x00000, 0x00645, 0x0062d, 0x00000, 0x00645, 0x0062e, + 0x00000, 0x00645, 0x00645, 0x00000, 0x00646, 0x0062c, 0x00000, 0x00646, + 0x0062d, 0x00000, 0x00646, 0x0062e, 0x00000, 0x00646, 0x00645, 0x00000, + 0x00646, 0x00647, 0x00000, 0x00647, 0x0062c, 0x00000, 0x00647, 0x00645, + 0x00000, 0x00647, 0x00670, 0x00000, 0x0064a, 0x0062c, 0x00000, 0x0064a, + 0x0062d, 0x00000, 0x0064a, 0x0062e, 0x00000, 0x0064a, 0x00645, 0x00000, + 0x0064a, 0x00647, 0x00000, 0x00626, 0x00645, 0x00000, 0x00626, 0x00647, + 0x00000, 0x00628, 0x00645, 0x00000, 0x00628, 0x00647, 0x00000, 0x0062a, + 0x00645, 0x00000, 0x0062a, 0x00647, 0x00000, 0x0062b, 0x00645, 0x00000, + 0x0062b, 0x00647, 0x00000, 0x00633, 0x00645, 0x00000, 0x00633, 0x00647, + 0x00000, 0x00634, 0x00645, 0x00000, 0x00634, 0x00647, 0x00000, 0x00643, + 0x00644, 0x00000, 0x00643, 0x00645, 0x00000, 0x00644, 0x00645, 0x00000, + 0x00646, 0x00645, 0x00000, 0x00646, 0x00647, 0x00000, 0x0064a, 0x00645, + 0x00000, 0x0064a, 0x00647, 0x00000, 0x00640, 0x0064e, 0x00651, 0x00000, + 0x00640, 0x0064f, 0x00651, 0x00000, 0x00640, 0x00650, 0x00651, 0x00000, + 0x00637, 0x00649, 0x00000, 0x00637, 0x0064a, 0x00000, 0x00639, 0x00649, + 0x00000, 0x00639, 0x0064a, 0x00000, 0x0063a, 0x00649, 0x00000, 0x0063a, + 0x0064a, 0x00000, 0x00633, 0x00649, 0x00000, 0x00633, 0x0064a, 0x00000, + 0x00634, 0x00649, 0x00000, 0x00634, 0x0064a, 0x00000, 0x0062d, 0x00649, + 0x00000, 0x0062d, 0x0064a, 0x00000, 0x0062c, 0x00649, 0x00000, 0x0062c, + 0x0064a, 0x00000, 0x0062e, 0x00649, 0x00000, 0x0062e, 0x0064a, 0x00000, + 0x00635, 0x00649, 0x00000, 0x00635, 0x0064a, 0x00000, 0x00636, 0x00649, + 0x00000, 0x00636, 0x0064a, 0x00000, 0x00634, 0x0062c, 0x00000, 0x00634, + 0x0062d, 0x00000, 0x00634, 0x0062e, 0x00000, 0x00634, 0x00645, 0x00000, + 0x00634, 0x00631, 0x00000, 0x00633, 0x00631, 0x00000, 0x00635, 0x00631, + 0x00000, 0x00636, 0x00631, 0x00000, 0x00637, 0x00649, 0x00000, 0x00637, + 0x0064a, 0x00000, 0x00639, 0x00649, 0x00000, 0x00639, 0x0064a, 0x00000, + 0x0063a, 0x00649, 0x00000, 0x0063a, 0x0064a, 0x00000, 0x00633, 0x00649, + 0x00000, 0x00633, 0x0064a, 0x00000, 0x00634, 0x00649, 0x00000, 0x00634, + 0x0064a, 0x00000, 0x0062d, 0x00649, 0x00000, 0x0062d, 0x0064a, 0x00000, + 0x0062c, 0x00649, 0x00000, 0x0062c, 0x0064a, 0x00000, 0x0062e, 0x00649, + 0x00000, 0x0062e, 0x0064a, 0x00000, 0x00635, 0x00649, 0x00000, 0x00635, + 0x0064a, 0x00000, 0x00636, 0x00649, 0x00000, 0x00636, 0x0064a, 0x00000, + 0x00634, 0x0062c, 0x00000, 0x00634, 0x0062d, 0x00000, 0x00634, 0x0062e, + 0x00000, 0x00634, 0x00645, 0x00000, 0x00634, 0x00631, 0x00000, 0x00633, + 0x00631, 0x00000, 0x00635, 0x00631, 0x00000, 0x00636, 0x00631, 0x00000, + 0x00634, 0x0062c, 0x00000, 0x00634, 0x0062d, 0x00000, 0x00634, 0x0062e, + 0x00000, 0x00634, 0x00645, 0x00000, 0x00633, 0x00647, 0x00000, 0x00634, + 0x00647, 0x00000, 0x00637, 0x00645, 0x00000, 0x00633, 0x0062c, 0x00000, + 0x00633, 0x0062d, 0x00000, 0x00633, 0x0062e, 0x00000, 0x00634, 0x0062c, + 0x00000, 0x00634, 0x0062d, 0x00000, 0x00634, 0x0062e, 0x00000, 0x00637, + 0x00645, 0x00000, 0x00638, 0x00645, 0x00000, 0x00627, 0x0064b, 0x00000, + 0x00627, 0x0064b, 0x00000, 0x0062a, 0x0062c, 0x00645, 0x00000, 0x0062a, + 0x0062d, 0x0062c, 0x00000, 0x0062a, 0x0062d, 0x0062c, 0x00000, 0x0062a, + 0x0062d, 0x00645, 0x00000, 0x0062a, 0x0062e, 0x00645, 0x00000, 0x0062a, + 0x00645, 0x0062c, 0x00000, 0x0062a, 0x00645, 0x0062d, 0x00000, 0x0062a, + 0x00645, 0x0062e, 0x00000, 0x0062c, 0x00645, 0x0062d, 0x00000, 0x0062c, + 0x00645, 0x0062d, 0x00000, 0x0062d, 0x00645, 0x0064a, 0x00000, 0x0062d, + 0x00645, 0x00649, 0x00000, 0x00633, 0x0062d, 0x0062c, 0x00000, 0x00633, + 0x0062c, 0x0062d, 0x00000, 0x00633, 0x0062c, 0x00649, 0x00000, 0x00633, + 0x00645, 0x0062d, 0x00000, 0x00633, 0x00645, 0x0062d, 0x00000, 0x00633, + 0x00645, 0x0062c, 0x00000, 0x00633, 0x00645, 0x00645, 0x00000, 0x00633, + 0x00645, 0x00645, 0x00000, 0x00635, 0x0062d, 0x0062d, 0x00000, 0x00635, + 0x0062d, 0x0062d, 0x00000, 0x00635, 0x00645, 0x00645, 0x00000, 0x00634, + 0x0062d, 0x00645, 0x00000, 0x00634, 0x0062d, 0x00645, 0x00000, 0x00634, + 0x0062c, 0x0064a, 0x00000, 0x00634, 0x00645, 0x0062e, 0x00000, 0x00634, + 0x00645, 0x0062e, 0x00000, 0x00634, 0x00645, 0x00645, 0x00000, 0x00634, + 0x00645, 0x00645, 0x00000, 0x00636, 0x0062d, 0x00649, 0x00000, 0x00636, + 0x0062e, 0x00645, 0x00000, 0x00636, 0x0062e, 0x00645, 0x00000, 0x00637, + 0x00645, 0x0062d, 0x00000, 0x00637, 0x00645, 0x0062d, 0x00000, 0x00637, + 0x00645, 0x00645, 0x00000, 0x00637, 0x00645, 0x0064a, 0x00000, 0x00639, + 0x0062c, 0x00645, 0x00000, 0x00639, 0x00645, 0x00645, 0x00000, 0x00639, + 0x00645, 0x00645, 0x00000, 0x00639, 0x00645, 0x00649, 0x00000, 0x0063a, + 0x00645, 0x00645, 0x00000, 0x0063a, 0x00645, 0x0064a, 0x00000, 0x0063a, + 0x00645, 0x00649, 0x00000, 0x00641, 0x0062e, 0x00645, 0x00000, 0x00641, + 0x0062e, 0x00645, 0x00000, 0x00642, 0x00645, 0x0062d, 0x00000, 0x00642, + 0x00645, 0x00645, 0x00000, 0x00644, 0x0062d, 0x00645, 0x00000, 0x00644, + 0x0062d, 0x0064a, 0x00000, 0x00644, 0x0062d, 0x00649, 0x00000, 0x00644, + 0x0062c, 0x0062c, 0x00000, 0x00644, 0x0062c, 0x0062c, 0x00000, 0x00644, + 0x0062e, 0x00645, 0x00000, 0x00644, 0x0062e, 0x00645, 0x00000, 0x00644, + 0x00645, 0x0062d, 0x00000, 0x00644, 0x00645, 0x0062d, 0x00000, 0x00645, + 0x0062d, 0x0062c, 0x00000, 0x00645, 0x0062d, 0x00645, 0x00000, 0x00645, + 0x0062d, 0x0064a, 0x00000, 0x00645, 0x0062c, 0x0062d, 0x00000, 0x00645, + 0x0062c, 0x00645, 0x00000, 0x00645, 0x0062e, 0x0062c, 0x00000, 0x00645, + 0x0062e, 0x00645, 0x00000, 0x00645, 0x0062c, 0x0062e, 0x00000, 0x00647, + 0x00645, 0x0062c, 0x00000, 0x00647, 0x00645, 0x00645, 0x00000, 0x00646, + 0x0062d, 0x00645, 0x00000, 0x00646, 0x0062d, 0x00649, 0x00000, 0x00646, + 0x0062c, 0x00645, 0x00000, 0x00646, 0x0062c, 0x00645, 0x00000, 0x00646, + 0x0062c, 0x00649, 0x00000, 0x00646, 0x00645, 0x0064a, 0x00000, 0x00646, + 0x00645, 0x00649, 0x00000, 0x0064a, 0x00645, 0x00645, 0x00000, 0x0064a, + 0x00645, 0x00645, 0x00000, 0x00628, 0x0062e, 0x0064a, 0x00000, 0x0062a, + 0x0062c, 0x0064a, 0x00000, 0x0062a, 0x0062c, 0x00649, 0x00000, 0x0062a, + 0x0062e, 0x0064a, 0x00000, 0x0062a, 0x0062e, 0x00649, 0x00000, 0x0062a, + 0x00645, 0x0064a, 0x00000, 0x0062a, 0x00645, 0x00649, 0x00000, 0x0062c, + 0x00645, 0x0064a, 0x00000, 0x0062c, 0x0062d, 0x00649, 0x00000, 0x0062c, + 0x00645, 0x00649, 0x00000, 0x00633, 0x0062e, 0x00649, 0x00000, 0x00635, + 0x0062d, 0x0064a, 0x00000, 0x00634, 0x0062d, 0x0064a, 0x00000, 0x00636, + 0x0062d, 0x0064a, 0x00000, 0x00644, 0x0062c, 0x0064a, 0x00000, 0x00644, + 0x00645, 0x0064a, 0x00000, 0x0064a, 0x0062d, 0x0064a, 0x00000, 0x0064a, + 0x0062c, 0x0064a, 0x00000, 0x0064a, 0x00645, 0x0064a, 0x00000, 0x00645, + 0x00645, 0x0064a, 0x00000, 0x00642, 0x00645, 0x0064a, 0x00000, 0x00646, + 0x0062d, 0x0064a, 0x00000, 0x00642, 0x00645, 0x0062d, 0x00000, 0x00644, + 0x0062d, 0x00645, 0x00000, 0x00639, 0x00645, 0x0064a, 0x00000, 0x00643, + 0x00645, 0x0064a, 0x00000, 0x00646, 0x0062c, 0x0062d, 0x00000, 0x00645, + 0x0062e, 0x0064a, 0x00000, 0x00644, 0x0062c, 0x00645, 0x00000, 0x00643, + 0x00645, 0x00645, 0x00000, 0x00644, 0x0062c, 0x00645, 0x00000, 0x00646, + 0x0062c, 0x0062d, 0x00000, 0x0062c, 0x0062d, 0x0064a, 0x00000, 0x0062d, + 0x0062c, 0x0064a, 0x00000, 0x00645, 0x0062c, 0x0064a, 0x00000, 0x00641, + 0x00645, 0x0064a, 0x00000, 0x00628, 0x0062d, 0x0064a, 0x00000, 0x00643, + 0x00645, 0x00645, 0x00000, 0x00639, 0x0062c, 0x00645, 0x00000, 0x00635, + 0x00645, 0x00645, 0x00000, 0x00633, 0x0062e, 0x0064a, 0x00000, 0x00646, + 0x0062c, 0x0064a, 0x00000, 0x00635, 0x00644, 0x006d2, 0x00000, 0x00642, + 0x00644, 0x006d2, 0x00000, 0x00627, 0x00644, 0x00644, 0x00647, 0x00000, + 0x00627, 0x00643, 0x00628, 0x00631, 0x00000, 0x00645, 0x0062d, 0x00645, + 0x0062f, 0x00000, 0x00635, 0x00644, 0x00639, 0x00645, 0x00000, 0x00631, + 0x00633, 0x00648, 0x00644, 0x00000, 0x00639, 0x00644, 0x0064a, 0x00647, + 0x00000, 0x00648, 0x00633, 0x00644, 0x00645, 0x00000, 0x00635, 0x00644, + 0x00649, 0x00000, 0x00635, 0x00644, 0x00649, 0x00020, 0x00627, 0x00644, + 0x00644, 0x00647, 0x00020, 0x00639, 0x00644, 0x0064a, 0x00647, 0x00020, + 0x00648, 0x00633, 0x00644, 0x00645, 0x00000, 0x0062c, 0x00644, 0x00020, + 0x0062c, 0x00644, 0x00627, 0x00644, 0x00647, 0x00000, 0x00631, 0x006cc, + 0x00627, 0x00644, 0x00000, 0x00020, 0x0064b, 0x00000, 0x00640, 0x0064b, + 0x00000, 0x00020, 0x0064c, 0x00000, 0x00020, 0x0064d, 0x00000, 0x00020, + 0x0064e, 0x00000, 0x00640, 0x0064e, 0x00000, 0x00020, 0x0064f, 0x00000, + 0x00640, 0x0064f, 0x00000, 0x00020, 0x00650, 0x00000, 0x00640, 0x00650, + 0x00000, 0x00020, 0x00651, 0x00000, 0x00640, 0x00651, 0x00000, 0x00020, + 0x00652, 0x00000, 0x00640, 0x00652, 0x00000, 0x00644, 0x00622, 0x00000, + 0x00644, 0x00622, 0x00000, 0x00644, 0x00623, 0x00000, 0x00644, 0x00623, + 0x00000, 0x00644, 0x00625, 0x00000, 0x00644, 0x00625, 0x00000, 0x00644, + 0x00627, 0x00000, 0x00644, 0x00627, 0x00000, 0x11099, 0x110ba, 0x00000, + 0x1109b, 0x110ba, 0x00000, 0x110a5, 0x110ba, 0x00000, 0x11131, 0x11127, + 0x00000, 0x11132, 0x11127, 0x00000, 0x11347, 0x1133e, 0x00000, 0x11347, + 0x11357, 0x00000, 0x114b9, 0x114ba, 0x00000, 0x114b9, 0x114b0, 0x00000, + 0x114b9, 0x114bd, 0x00000, 0x115b8, 0x115af, 0x00000, 0x115b9, 0x115af, + 0x00000, 0x1d157, 0x1d165, 0x00000, 0x1d158, 0x1d165, 0x00000, 0x1d15f, + 0x1d16e, 0x00000, 0x1d15f, 0x1d16f, 0x00000, 0x1d15f, 0x1d170, 0x00000, + 0x1d15f, 0x1d171, 0x00000, 0x1d15f, 0x1d172, 0x00000, 0x1d1b9, 0x1d165, + 0x00000, 0x1d1ba, 0x1d165, 0x00000, 0x1d1bb, 0x1d16e, 0x00000, 0x1d1bc, + 0x1d16e, 0x00000, 0x1d1bb, 0x1d16f, 0x00000, 0x1d1bc, 0x1d16f, 0x00000, + 0x00030, 0x0002e, 0x00000, 0x00030, 0x0002c, 0x00000, 0x00031, 0x0002c, + 0x00000, 0x00032, 0x0002c, 0x00000, 0x00033, 0x0002c, 0x00000, 0x00034, + 0x0002c, 0x00000, 0x00035, 0x0002c, 0x00000, 0x00036, 0x0002c, 0x00000, + 0x00037, 0x0002c, 0x00000, 0x00038, 0x0002c, 0x00000, 0x00039, 0x0002c, + 0x00000, 0x00028, 0x00041, 0x00029, 0x00000, 0x00028, 0x00042, 0x00029, + 0x00000, 0x00028, 0x00043, 0x00029, 0x00000, 0x00028, 0x00044, 0x00029, + 0x00000, 0x00028, 0x00045, 0x00029, 0x00000, 0x00028, 0x00046, 0x00029, + 0x00000, 0x00028, 0x00047, 0x00029, 0x00000, 0x00028, 0x00048, 0x00029, + 0x00000, 0x00028, 0x00049, 0x00029, 0x00000, 0x00028, 0x0004a, 0x00029, + 0x00000, 0x00028, 0x0004b, 0x00029, 0x00000, 0x00028, 0x0004c, 0x00029, + 0x00000, 0x00028, 0x0004d, 0x00029, 0x00000, 0x00028, 0x0004e, 0x00029, + 0x00000, 0x00028, 0x0004f, 0x00029, 0x00000, 0x00028, 0x00050, 0x00029, + 0x00000, 0x00028, 0x00051, 0x00029, 0x00000, 0x00028, 0x00052, 0x00029, + 0x00000, 0x00028, 0x00053, 0x00029, 0x00000, 0x00028, 0x00054, 0x00029, + 0x00000, 0x00028, 0x00055, 0x00029, 0x00000, 0x00028, 0x00056, 0x00029, + 0x00000, 0x00028, 0x00057, 0x00029, 0x00000, 0x00028, 0x00058, 0x00029, + 0x00000, 0x00028, 0x00059, 0x00029, 0x00000, 0x00028, 0x0005a, 0x00029, + 0x00000, 0x03014, 0x00053, 0x03015, 0x00000, 0x00043, 0x00044, 0x00000, + 0x00057, 0x0005a, 0x00000, 0x00048, 0x00056, 0x00000, 0x0004d, 0x00056, + 0x00000, 0x00053, 0x00044, 0x00000, 0x00053, 0x00053, 0x00000, 0x00050, + 0x00050, 0x00056, 0x00000, 0x00057, 0x00043, 0x00000, 0x0004d, 0x00043, + 0x00000, 0x0004d, 0x00044, 0x00000, 0x00044, 0x0004a, 0x00000, 0x0307b, + 0x0304b, 0x00000, 0x030b3, 0x030b3, 0x00000, 0x03014, 0x0672c, 0x03015, + 0x00000, 0x03014, 0x04e09, 0x03015, 0x00000, 0x03014, 0x04e8c, 0x03015, + 0x00000, 0x03014, 0x05b89, 0x03015, 0x00000, 0x03014, 0x070b9, 0x03015, + 0x00000, 0x03014, 0x06253, 0x03015, 0x00000, 0x03014, 0x076d7, 0x03015, + 0x00000, 0x03014, 0x052dd, 0x03015, 0x00000, 0x03014, 0x06557, 0x03015, + 0x00000 +}; diff --git a/src/lib/unicodemap.pl b/src/lib/unicodemap.pl new file mode 100755 index 0000000..2c1bf7a --- /dev/null +++ b/src/lib/unicodemap.pl @@ -0,0 +1,162 @@ +#!/usr/bin/env perl +use strict; + +my (%titlecase8, %uni8_decomp); +my (@titlecase16_keys, @titlecase16_values); +my (@titlecase32_keys, @titlecase32_values); +my (@uni16_decomp_keys, @uni16_decomp_values); +my (@uni32_decomp_keys, @uni32_decomp_values); +my (@multidecomp_keys, @multidecomp_offsets, @multidecomp_values); +while (<>) { + chomp $_; + my @arr = split(";"); + my $code = eval("0x".$arr[0]); + my $decomp = $arr[5]; + my $titlecode = $arr[14]; + + if ($titlecode ne "") { + # titlecase mapping + my $value = eval("0x$titlecode"); + if ($value == $code) { + # the same character, ignore + } elsif ($code <= 0xff) { + die "Error: We've assumed 8bit keys have max. 16bit values" if ($value > 0xffff); + $titlecase8{$code} = $value; + } elsif ($code <= 0xffff) { + die "Error: We've assumed 16bit keys have max. 16bit values" if ($value > 0xffff); + push @titlecase16_keys, $code; + push @titlecase16_values, $value; + } else { + push @titlecase32_keys, $code; + push @titlecase32_values, $value; + } + } elsif ($decomp =~ /(?:\<[^>]*> )?(.+)/) { + # decompositions + my $decomp_codes = $1; + if ($decomp_codes =~ /^([0-9A-Z]*)$/i) { + # unicharacter decomposition. use separate lists for this + my $value = eval("0x$1"); + if ($value > 0xffffffff) { + print STDERR "Error: We've assumed decomposition codes are max. 32bit\n"; + exit 1; + } + if ($code <= 0xff) { + $uni8_decomp{$code} = $value; + } elsif ($code <= 0xffff) { + push @uni16_decomp_keys, $code; + push @uni16_decomp_values, $value; + } else { + push @uni32_decomp_keys, $code; + push @uni32_decomp_values, $value; + } + } else { + # multicharacter decomposition. + if ($code > 0xffffffff) { + print STDERR "Error: We've assumed multi-decomposition key codes are max. 32bit\n"; + exit 1; + } + + push @multidecomp_keys, $code; + push @multidecomp_offsets, scalar(@multidecomp_values); + + foreach my $dcode (split(" ", $decomp_codes)) { + my $value = eval("0x$dcode"); + if ($value > 0xffffffff) { + print STDERR "Error: We've assumed decomposition codes are max. 32bit\n"; + exit 1; + } + push @multidecomp_values, $value; + } + push @multidecomp_values, 0; + } + } +} + +sub print_list { + my @list = @{$_[0]}; + + my $last = $#list; + my $n = 0; + foreach my $key (@list) { + printf("0x%05x", $key); + last if ($n == $last); + print ","; + + $n++; + if (($n % 8) == 0) { + print "\n\t"; + } else { + print " "; + } + } +} + +print "/* This file is automatically generated by unicodemap.pl from UnicodeData.txt + + NOTE: decompositions for characters having titlecase characters + are not included, because we first translate everything to titlecase */\n"; + +sub print_map8 { + my %map = %{$_[0]}; + my @list; + for (my $i = 0; $i <= 0xff; $i++) { + if (defined($map{$i})) { + push @list, $map{$i}; + } else { + push @list, $i; + } + } + print_list(\@list); +} + +print "static const uint16_t titlecase8_map[256] = {\n\t"; +print_map8(\%titlecase8); +print "\n};\n"; + +print "static const uint16_t titlecase16_keys[] = {\n\t"; +print_list(\@titlecase16_keys); +print "\n};\n"; + +print "static const uint16_t titlecase16_values[] = {\n\t"; +print_list(\@titlecase16_values); +print "\n};\n"; + +print "static const uint32_t titlecase32_keys[] = {\n\t"; +print_list(\@titlecase32_keys); +print "\n};\n"; + +print "static const uint32_t titlecase32_values[] = {\n\t"; +print_list(\@titlecase32_values); +print "\n};\n"; + +print "static const uint16_t uni8_decomp_map[256] = {\n\t"; +print_map8(\%uni8_decomp); +print "\n};\n"; + +print "static const uint16_t uni16_decomp_keys[] = {\n\t"; +print_list(\@uni16_decomp_keys); +print "\n};\n"; + +print "static const uint32_t uni16_decomp_values[] = {\n\t"; +print_list(\@uni16_decomp_values); +print "\n};\n"; + +print "static const uint32_t uni32_decomp_keys[] = {\n\t"; +print_list(\@uni32_decomp_keys); +print "\n};\n"; + +print "static const uint32_t uni32_decomp_values[] = {\n\t"; +print_list(\@uni32_decomp_values); +print "\n};\n"; + +print "static const uint32_t multidecomp_keys[] = {\n\t"; +print_list(\@multidecomp_keys); +print "\n};\n"; + +print "static const uint16_t multidecomp_offsets[] = {\n\t"; +print_list(\@multidecomp_offsets); +print "\n};\n"; + +print "static const uint32_t multidecomp_values[] = {\n\t"; +print_list(\@multidecomp_values); +print "\n};\n"; diff --git a/src/lib/unix-socket-create.c b/src/lib/unix-socket-create.c new file mode 100644 index 0000000..3614df1 --- /dev/null +++ b/src/lib/unix-socket-create.c @@ -0,0 +1,36 @@ +/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "net.h" +#include "unix-socket-create.h" + +#include <unistd.h> +#include <sys/stat.h> + +int unix_socket_create(const char *path, int mode, + uid_t uid, gid_t gid, int backlog) +{ + mode_t old_umask; + int fd; + + old_umask = umask(0777 ^ mode); + fd = net_listen_unix_unlink_stale(path, backlog); + umask(old_umask); + + if (fd < 0) { + i_error("net_listen_unix(%s) failed: %m", path); + return -1; + } + + if (uid != (uid_t)-1 || gid != (gid_t)-1) { + /* set correct permissions */ + if (chown(path, uid, gid) < 0) { + i_error("chown(%s, %s, %s) failed: %m", + path, dec2str(uid), dec2str(gid)); + i_close_fd(&fd); + return -1; + } + } + return fd; +} + diff --git a/src/lib/unix-socket-create.h b/src/lib/unix-socket-create.h new file mode 100644 index 0000000..88ffffb --- /dev/null +++ b/src/lib/unix-socket-create.h @@ -0,0 +1,7 @@ +#ifndef UNIX_SOCKET_CREATE_H +#define UNIX_SOCKET_CREATE_H + +int unix_socket_create(const char *path, int mode, + uid_t uid, gid_t gid, int backlog); + +#endif diff --git a/src/lib/unlink-directory.c b/src/lib/unlink-directory.c new file mode 100644 index 0000000..98fddd1 --- /dev/null +++ b/src/lib/unlink-directory.c @@ -0,0 +1,283 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +/* + There's a bit tricky race condition with recursive deletion. + Suppose this happens: + + lstat(dir, ..) -> OK, it's a directory + // attacker deletes dir, replaces it with symlink to / + opendir(dir) -> it actually opens / + + Most portable solution is to lstat() the dir, chdir() there, then check + that "." points to same device/inode as we originally lstat()ed. This + assumes that the device has usable inodes, most should except for some NFS + implementations. + + Filesystems may also reassign a deleted inode to another file + immediately after it's deleted. That in theory makes it possible to exploit + this race to delete the new directory. However, the new inode is quite + unlikely to be any important directory, and attacker is quite unlikely to + find out which directory even got the inode. Maybe with some setuid program + or daemon interaction something could come out of it though. + + Another less portable solution is to fchdir(open(dir, O_NOFOLLOW)). + This should be completely safe. + + The actual file deletion also has to be done relative to current + directory, to make sure that the whole directory structure isn't replaced + with another one while we're deleting it. Going back to parent directory + isn't too easy either - safest (and easiest) way again is to open() the + directory and fchdir() back there. +*/ + +#define _GNU_SOURCE /* for O_NOFOLLOW with Linux */ + +#include "lib.h" +#include "path-util.h" +#include "unlink-directory.h" + +#include <fcntl.h> +#include <unistd.h> +#include <dirent.h> +#include <sys/stat.h> + +#define ERROR_FORMAT "%s(%s) failed: %m" +#define ERROR_FORMAT_DNAME "%s(%s/%s) failed: %m" + +static void ATTR_FORMAT(3,4) +unlink_directory_error(const char **error, + int *first_errno, + const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + const char *err = t_strdup_vprintf(fmt, args); + if (*error == NULL) { + if (first_errno != NULL) + *first_errno = errno; + *error = err; + } else + i_error("%s", err); + va_end(args); +} + +static int +unlink_directory_r(const char *dir, enum unlink_directory_flags flags, + const char **error) +{ + DIR *dirp; + struct dirent *d; + struct stat st; + int dir_fd, old_errno; + +#ifdef O_NOFOLLOW + dir_fd = open(dir, O_RDONLY | O_NOFOLLOW); + if (dir_fd == -1) { + unlink_directory_error(error, NULL, + "open(%s, O_RDONLY | O_NOFOLLOW) failed: %m", + dir); + return -1; + } +#else + struct stat st2; + + if (lstat(dir, &st) < 0) { + unlink_directory_error(error_r, NULL, ERROR_FORMAT, "lstat", dir); + return -1; + } + + if (!S_ISDIR(st.st_mode)) { + if ((st.st_mode & S_IFMT) != S_IFLNK) { + unlink_directory_error(error_r, NULL, "%s is not a directory: %s", dir); + errno = ENOTDIR; + } else { + /* be compatible with O_NOFOLLOW */ + errno = ELOOP; + unlink_directory_error(error_r, NULL, "%s is a symlink, not a directory: %s", dir); + } + return -1; + } + + dir_fd = open(dir, O_RDONLY); + if (dir_fd == -1) { + unlink_directory_error(error_r, NULL, "open(%s, O_RDONLY) failed: %m", dir); + return -1; + } + + if (fstat(dir_fd, &st2) < 0) { + i_close_fd(&dir_fd); + unlink_directory_error(error_r, NULL, ERROR_FORMAT, "fstat", dir); + return -1; + } + + if (st.st_ino != st2.st_ino || + !CMP_DEV_T(st.st_dev, st2.st_dev)) { + /* directory was just replaced with something else. */ + i_close_fd(&dir_fd); + errno = ENOTDIR; + unlink_directory_error(error_r, NULL, "%s race condition: directory was just replaced", dir); + return -1; + } +#endif + if (fchdir(dir_fd) < 0) { + i_close_fd(&dir_fd); + unlink_directory_error(error, NULL, ERROR_FORMAT, "fchdir", dir); + return -1; + } + + dirp = opendir("."); + if (dirp == NULL) { + i_close_fd(&dir_fd); + unlink_directory_error(error, NULL, "opendir(.) (in %s) failed: %m", dir); + return -1; + } + + int first_errno = 0; + for (;;) { + errno = 0; + d = readdir(dirp); + if (d == NULL) { + if (errno != 0) { + unlink_directory_error(error, + &first_errno, + ERROR_FORMAT, + "readdir", + dir); + } + break; + } + if (d->d_name[0] == '.') { + if ((d->d_name[1] == '\0' || + (d->d_name[1] == '.' && d->d_name[2] == '\0'))) { + /* skip . and .. */ + continue; + } + if ((flags & UNLINK_DIRECTORY_FLAG_SKIP_DOTFILES) != 0) + continue; + } + + if (unlink(d->d_name) < 0 && errno != ENOENT) { + old_errno = errno; + + if (lstat(d->d_name, &st) < 0) { + if (errno != ENOENT) { + unlink_directory_error(error, + &first_errno, + ERROR_FORMAT, + "lstat", + dir); + break; + } + } else if (S_ISDIR(st.st_mode) && + (flags & UNLINK_DIRECTORY_FLAG_FILES_ONLY) == 0) { + if (unlink_directory_r(d->d_name, flags, error) < 0) { + if (first_errno == 0) + first_errno = errno; + if (errno != ENOENT) + break; + } + if (fchdir(dir_fd) < 0) { + unlink_directory_error(error, + &first_errno, + ERROR_FORMAT, + "fchdir", + dir); + break; + } + + if (rmdir(d->d_name) < 0 && + errno != ENOENT) { + if (errno == EEXIST) + /* standardize errno */ + errno = ENOTEMPTY; + unlink_directory_error(error, + &first_errno, + ERROR_FORMAT, + "rmdir", + dir); + break; + } + } else if (S_ISDIR(st.st_mode) && + (flags & UNLINK_DIRECTORY_FLAG_FILES_ONLY) != 0) { + /* skip directory */ + } else if (old_errno == EBUSY && + str_begins(d->d_name, ".nfs")) { + /* can't delete NFS files that are still + in use. let the caller decide if this error + is worth logging about */ + break; + } else + /* so it wasn't a directory */ + unlink_directory_error(error, + &first_errno, + ERROR_FORMAT_DNAME, + "unlink", + dir, + d->d_name); + } + } + + i_close_fd(&dir_fd); + if (closedir(dirp) < 0) + unlink_directory_error(error, + &first_errno, + ERROR_FORMAT, + "closedir", + dir); + + if (*error != NULL) { + errno = first_errno; + return -1; + } + return 0; +} + +int unlink_directory(const char *dir, enum unlink_directory_flags flags, + const char **error_r) +{ + const char *orig_dir, *error; + int fd, ret, old_errno; + + if (t_get_working_dir(&orig_dir, &error) < 0) { + i_warning("Could not get working directory in unlink_directory(): %s", + error); + orig_dir = "."; + } + + fd = open(".", O_RDONLY); + if (fd == -1) { + *error_r = t_strdup_printf( + "Can't preserve current directory %s: " + "open(.) failed: %m", orig_dir); + return -1; + } + + /* Cannot set error_r to NULL inside of unlink_directory_r() + because of recursion */ + *error_r = NULL; + ret = unlink_directory_r(dir, flags, error_r); + old_errno = errno; + + if (fchdir(fd) < 0) { + i_fatal("unlink_directory(%s): " + "Can't fchdir() back to our original dir %s: %m", dir, orig_dir); + } + i_close_fd(&fd); + + if (ret < 0) { + errno = old_errno; + return errno == ENOENT ? 0 : -1; + } + + if ((flags & UNLINK_DIRECTORY_FLAG_RMDIR) != 0) { + if (rmdir(dir) < 0 && errno != ENOENT) { + *error_r = t_strdup_printf("rmdir(%s) failed: %m", dir); + if (errno == EEXIST) { + /* standardize errno */ + errno = ENOTEMPTY; + } + return errno == ENOENT ? 0 : 1; + } + } + return 1; +} diff --git a/src/lib/unlink-directory.h b/src/lib/unlink-directory.h new file mode 100644 index 0000000..fbc2a44 --- /dev/null +++ b/src/lib/unlink-directory.h @@ -0,0 +1,21 @@ +#ifndef UNLINK_DIRECTORY_H +#define UNLINK_DIRECTORY_H + +enum unlink_directory_flags { + /* After unlinking all files, rmdir() the directory itself */ + UNLINK_DIRECTORY_FLAG_RMDIR = 0x01, + /* Don't unlink any files beginning with "." */ + UNLINK_DIRECTORY_FLAG_SKIP_DOTFILES = 0x02, + /* Don't recurse into subdirectories */ + UNLINK_DIRECTORY_FLAG_FILES_ONLY = 0x04 +}; + +/* Unlink directory and/or everything under it. + Returns 1 if successful, 0 if error is ENOENT, -1 if other error. + The returned error message contains the exact syscall that failed, + e.g. "open(path) failed: Permission denied" + In case of ENOENT error, error message is also set. */ +int unlink_directory(const char *dir, enum unlink_directory_flags flags, + const char **error_r); + +#endif diff --git a/src/lib/unlink-old-files.c b/src/lib/unlink-old-files.c new file mode 100644 index 0000000..4074327 --- /dev/null +++ b/src/lib/unlink-old-files.c @@ -0,0 +1,73 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "str.h" +#include "unlink-old-files.h" + +#include <signal.h> +#include <unistd.h> +#include <dirent.h> +#include <sys/stat.h> +#include <utime.h> + +static int +unlink_old_files_real(const char *dir, const char *prefix, time_t min_time) +{ + DIR *dirp; + struct dirent *d; + struct stat st; + string_t *path; + size_t prefix_len, dir_len; + + dirp = opendir(dir); + if (dirp == NULL) { + if (errno != ENOENT) + i_error("opendir(%s) failed: %m", dir); + return -1; + } + + /* update atime immediately, so if this scanning is done based on + atime it won't be done by multiple processes if the scan is slow */ + if (utime(dir, NULL) < 0 && errno != ENOENT) + i_error("utime(%s) failed: %m", dir); + + path = t_str_new(256); + str_printfa(path, "%s/", dir); + dir_len = str_len(path); + + prefix_len = strlen(prefix); + while ((d = readdir(dirp)) != NULL) { + if (d->d_name[0] == '.' && + (d->d_name[1] == '\0' || + (d->d_name[1] == '.' && d->d_name[2] == '\0'))) { + /* skip . and .. */ + continue; + } + if (strncmp(d->d_name, prefix, prefix_len) != 0) + continue; + + str_truncate(path, dir_len); + str_append(path, d->d_name); + if (stat(str_c(path), &st) < 0) { + if (errno != ENOENT) + i_error("stat(%s) failed: %m", str_c(path)); + } else if (!S_ISDIR(st.st_mode) && st.st_ctime < min_time) { + i_unlink_if_exists(str_c(path)); + } + } + + if (closedir(dirp) < 0) + i_error("closedir(%s) failed: %m", dir); + return 0; +} + +int unlink_old_files(const char *dir, const char *prefix, time_t min_time) +{ + int ret; + + T_BEGIN { + ret = unlink_old_files_real(dir, prefix, min_time); + } T_END; + return ret; +} diff --git a/src/lib/unlink-old-files.h b/src/lib/unlink-old-files.h new file mode 100644 index 0000000..927adb7 --- /dev/null +++ b/src/lib/unlink-old-files.h @@ -0,0 +1,9 @@ +#ifndef UNLINK_OLD_FILES_H +#define UNLINK_OLD_FILES_H + +/* Unlink all files from directory beginning with given prefix and having + ctime older than min_time. Makes sure that the directory's atime is updated. + Returns -1 if there were some errors. */ +int unlink_old_files(const char *dir, const char *prefix, time_t min_time); + +#endif diff --git a/src/lib/uri-util.c b/src/lib/uri-util.c new file mode 100644 index 0000000..498bc88 --- /dev/null +++ b/src/lib/uri-util.c @@ -0,0 +1,1332 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "net.h" +#include "uri-util.h" + +#include <ctype.h> + +/* [URI-GEN] RFC3986 Appendix A: + + URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] + absolute-URI = scheme ":" hier-part [ "?" query ] + scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + + URI-reference = URI / relative-ref + relative-ref = relative-part [ "?" query ] [ "#" fragment ] + + relative-part = "//" authority path-abempty + / path-absolute + / path-noscheme + / path-empty + hier-part = "//" authority path-abempty + / path-absolute + / path-rootless + / path-empty + + authority = [ userinfo "@" ] host [ ":" port ] + userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) + host = IP-literal / IPv4address / reg-name + port = *DIGIT + + IP-literal = "[" ( IPv6address / IPvFuture ) "]" + IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) + IPv6address = 6( h16 ":" ) ls32 + / "::" 5( h16 ":" ) ls32 + / [ h16 ] "::" 4( h16 ":" ) ls32 + / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + / [ *4( h16 ":" ) h16 ] "::" ls32 + / [ *5( h16 ":" ) h16 ] "::" h16 + / [ *6( h16 ":" ) h16 ] "::" + h16 = 1*4HEXDIG + ls32 = ( h16 ":" h16 ) / IPv4address + IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + dec-octet = DIGIT ; 0-9 + / %x31-39 DIGIT ; 10-99 + / "1" 2DIGIT ; 100-199 + / "2" %x30-34 DIGIT ; 200-249 + / "25" %x30-35 ; 250-255 + reg-name = *( unreserved / pct-encoded / sub-delims ) + + path = path-abempty ; begins with "/" or is empty + / path-absolute ; begins with "/" but not "//" + / path-noscheme ; begins with a non-colon segment + / path-rootless ; begins with a segment + / path-empty ; zero characters + path-abempty = *( "/" segment ) + path-absolute = "/" [ segment-nz *( "/" segment ) ] + path-noscheme = segment-nz-nc *( "/" segment ) + path-rootless = segment-nz *( "/" segment ) + path-empty = 0<pchar> + + segment = *pchar + segment-nz = 1*pchar + segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) + ; non-zero-length segment without any colon ":" + pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + + query = *( pchar / "/" / "?" ) + fragment = *( pchar / "/" / "?" ) + + pct-encoded = "%" HEXDIG HEXDIG + unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + reserved = gen-delims / sub-delims + gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" + sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + / "*" / "+" / "," / ";" / "=" + */ + +#define URI_MAX_SCHEME_NAME_LEN 64 + +/* Character lookup table + * + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" [bit0] + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" [bit1] + * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" [bit2] + * pchar = unreserved / sub-delims / ":" / "@" [bit0|bit1|bit3] + * 'pfchar' = unreserved / sub-delims / ":" / "@" / "/" + * [bit0|bit1|bit3|bit5] + * 'uchar' = unreserved / sub-delims / ":" [bit0|bit1|bit4] + * 'qchar' = pchar / "/" / "?" [bit0|bit1|bit3|bit5|bit6] + * + */ + +#define CHAR_MASK_UNRESERVED (1<<0) +#define CHAR_MASK_SUB_DELIMS (1<<1) +#define CHAR_MASK_PCHAR ((1<<0)|(1<<1)|(1<<3)) +#define CHAR_MASK_PFCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5)) +#define CHAR_MASK_UCHAR ((1<<0)|(1<<1)|(1<<4)) +#define CHAR_MASK_QCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5)|(1<<6)) +#define CHAR_MASK_UNRESERVED_PATH ((1<<0)|(1<<5)) + +static unsigned const char _uri_char_lookup[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10 + 0, 2, 0, 4, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 1, 36, // 20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 2, 0, 2, 0, 68, // 30 + 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0, 4, 0, 1, // 50 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 70 +}; + +static inline int _decode_hex_digit(const unsigned char digit) +{ + switch (digit) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return digit - '0'; + + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + return digit - 'a' + 0x0a; + + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + return digit - 'A' + 0x0A; + } + return -1; +} + +static int +uri_parse_pct_encoded_data(struct uri_parser *parser, + const unsigned char **p, const unsigned char *pend, + unsigned char *ch_r) ATTR_NULL(3) +{ + int value; + + if (**p != '%' || (pend != NULL && *p >= pend)) + return 0; + *p += 1; + + if (**p == 0 || *(*p+1) == 0 || (pend != NULL && *p+1 >= pend)) { + parser->error = "Unexpected URI boundary after '%'"; + return -1; + } + + if ((value = _decode_hex_digit(**p)) < 0) { + parser->error = p_strdup_printf(parser->pool, + "Expecting hex digit after '%%', but found '%c'", **p); + return -1; + } + + *ch_r = (value & 0x0f) << 4; + *p += 1; + + if ((value = _decode_hex_digit(**p)) < 0) { + parser->error = p_strdup_printf(parser->pool, + "Expecting hex digit after '%%%c', but found '%c'", *((*p)-1), **p); + return -1; + } + + *ch_r |= (value & 0x0f); + *p += 1; + + if (!parser->allow_pct_nul && *ch_r == '\0') { + parser->error = + "Percent encoding is not allowed to encode NUL character"; + return -1; + } + return 1; +} + +int uri_parse_pct_encoded(struct uri_parser *parser, + unsigned char *ch_r) +{ + return uri_parse_pct_encoded_data + (parser, &parser->cur, parser->end, ch_r); +} + +static int +uri_parse_unreserved_char(struct uri_parser *parser, unsigned char *ch_r) +{ + if ((*parser->cur & 0x80) != 0) + return 0; + + if ((_uri_char_lookup[*parser->cur] & CHAR_MASK_UNRESERVED) != 0) { + *ch_r = *parser->cur; + parser->cur++; + return 1; + } + return 0; +} + +int uri_parse_unreserved(struct uri_parser *parser, string_t *part) +{ + int len = 0; + + while (parser->cur < parser->end) { + int ret; + unsigned char ch = 0; + + if ((ret = uri_parse_unreserved_char(parser, &ch)) < 0) + return -1; + if (ret == 0) + break; + + if (part != NULL) + str_append_c(part, ch); + len++; + } + + return len > 0 ? 1 : 0; +} + +int uri_parse_unreserved_pct(struct uri_parser *parser, string_t *part) +{ + int len = 0; + + while (parser->cur < parser->end) { + int ret; + unsigned char ch = 0; + + if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) + return -1; + else if (ret == 0 && + (ret=uri_parse_unreserved_char(parser, &ch)) < 0) + return -1; + if (ret == 0) + break; + + if (part != NULL) + str_append_c(part, ch); + len++; + } + + return len > 0 ? 1 : 0; +} + +bool uri_data_decode(struct uri_parser *parser, const char *data, + const char *until, const char **decoded_r) +{ + const unsigned char *p = (const unsigned char *)data; + const unsigned char *pend = (const unsigned char *)until; + string_t *decoded; + int ret; + + if (pend == NULL) { + /* NULL means unlimited; solely rely on '\0' */ + pend = (const unsigned char *)SIZE_MAX; + } + + if (p >= pend || *p == '\0') { + if (decoded_r != NULL) + *decoded_r = ""; + return TRUE; + } + + decoded = uri_parser_get_tmpbuf(parser, 256); + while (p < pend && *p != '\0') { + unsigned char ch; + + if ((ret=uri_parse_pct_encoded_data + (parser, &p, NULL, &ch)) != 0) { + if (ret < 0) + return FALSE; + str_append_c(decoded, ch); + } else { + str_append_c(decoded, *p); + p++; + } + } + + if (decoded_r != NULL) + *decoded_r = p_strdup(parser->pool, str_c(decoded)); + return TRUE; +} + +int uri_parse_scheme(struct uri_parser *parser, const char **scheme_r) +{ + const unsigned char *first = parser->cur; + size_t len = 1; + + /* RFC 3968: + * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + */ + + if (parser->cur >= parser->end || !i_isalpha(*parser->cur)) + return 0; + parser->cur++; + + while (len < URI_MAX_SCHEME_NAME_LEN && + parser->cur < parser->end) { + if (!i_isalnum(*parser->cur) && + *parser->cur != '+' && *parser->cur != '-' && + *parser->cur != '.') + break; + parser->cur++; + len++; + } + + if (parser->cur >= parser->end || *parser->cur != ':') { + parser->error = "Invalid URI scheme"; + return -1; + } + if (scheme_r != NULL) + *scheme_r = t_strndup(first, parser->cur - first); + parser->cur++; + return 1; +} + +int uri_cut_scheme(const char **uri_p, const char **scheme_r) +{ + struct uri_parser parser; + + uri_parser_init(&parser, NULL, *uri_p); + if (uri_parse_scheme(&parser, scheme_r) <= 0) + return -1; + *uri_p = (const char *)parser.cur; + return 0; +} + +static int +uri_parse_dec_octet(struct uri_parser *parser, string_t *literal, + uint8_t *octet_r) ATTR_NULL(2) +{ + unsigned int octet = 0; + int count = 0; + + /* RFC 3986: + * + * dec-octet = DIGIT ; 0-9 + * / %x31-39 DIGIT ; 10-99 + * / "1" 2DIGIT ; 100-199 + * / "2" %x30-34 DIGIT ; 200-249 + * / "25" %x30-35 ; 250-255 + */ + + while (parser->cur < parser->end && i_isdigit(*parser->cur)) { + octet = octet * 10 + (parser->cur[0] - '0'); + if (octet > 255) + return -1; + + if (literal != NULL) + str_append_c(literal, *parser->cur); + + parser->cur++; + count++; + } + + if (count > 0) { + *octet_r = octet; + return 1; + } + return 0; +} + +static int +uri_parse_ipv4address(struct uri_parser *parser, string_t *literal, + struct in_addr *ip4_r) ATTR_NULL(2,3) +{ + uint8_t octet; + uint32_t ip = 0; + int ret; + int i; + + /* RFC 3986: + * + * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + */ + + if ((ret = uri_parse_dec_octet(parser, literal, &octet)) <= 0) + return ret; + ip = octet; + + for (i = 0; i < 3 && parser->cur < parser->end; i++) { + if (*parser->cur != '.') + return -1; + + if (literal != NULL) + str_append_c(literal, '.'); + parser->cur++; + + if (uri_parse_dec_octet(parser, literal, &octet) <= 0) + return -1; + ip = (ip << 8) + octet; + } + + if (ip4_r != NULL) + ip4_r->s_addr = htonl(ip); + return 1; +} + +static int +uri_do_parse_reg_name(struct uri_parser *parser, + string_t *reg_name) ATTR_NULL(2) +{ + /* RFC 3986: + * + * reg-name = *( unreserved / pct-encoded / sub-delims ) + */ + + while (parser->cur < parser->end) { + int ret; + unsigned char c; + + /* unreserved / pct-encoded */ + if ((ret=uri_parse_pct_encoded(parser, &c)) < 0) + return -1; + else if (ret == 0 && + (ret=uri_parse_unreserved_char(parser, &c)) < 0) + return -1; + + if (ret > 0) { + if (reg_name != NULL) + str_append_c(reg_name, c); + continue; + } + + /* sub-delims */ + c = *parser->cur; + if ((c & 0x80) == 0 && (_uri_char_lookup[c] & CHAR_MASK_SUB_DELIMS) != 0) { + if (reg_name != NULL) + str_append_c(reg_name, *parser->cur); + parser->cur++; + continue; + } + break; + } + return 0; +} + +int uri_parse_reg_name(struct uri_parser *parser, + const char **reg_name_r) +{ + string_t *reg_name = NULL; + int ret; + + if (reg_name_r != NULL) + reg_name = uri_parser_get_tmpbuf(parser, 256); + + if ((ret=uri_do_parse_reg_name(parser, reg_name)) <= 0) + return ret; + + if (reg_name_r != NULL) + *reg_name_r = str_c(reg_name); + return 1; +} + +static int uri_do_parse_host_name(struct uri_parser *parser, + string_t *host_name) ATTR_NULL(2) +{ + const unsigned char *first, *part; + int ret; + + /* RFC 3986, Section 3.2.2: + + A registered name intended for lookup in the DNS uses the syntax + defined in Section 3.5 of [RFC1034] and Section 2.1 of [RFC1123]. + Such a name consists of a sequence of domain labels separated by ".", + each domain label starting and ending with an alphanumeric character + and possibly also containing "-" characters. The rightmost domain + label of a fully qualified domain name in DNS may be followed by a + single "." and should be if it is necessary to distinguish between + the complete domain name and some local domain. + + RFC 2396, Section 3.2.2 (old URI specification): + + hostname = *( domainlabel "." ) toplabel [ "." ] + domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum + toplabel = alpha | alpha *( alphanum | "-" ) alphanum + + The description in RFC 3986 is more liberal, so: + + hostname = *( domainlabel "." ) domainlabel [ "." ] + domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum + + We also support percent encoding in spirit of the generic reg-name, + even though this should explicitly not be used according to the RFC. + It is, however, not strictly forbidden (unlike older RFC), so we + support it. + */ + + first = part = parser->cur; + for (;;) { + const unsigned char *offset; + unsigned char ch, pch; + + /* alphanum */ + offset = parser->cur; + ch = pch = *parser->cur; + if (parser->cur >= parser->end) + break; + if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) { + return -1; + } else if (ret > 0) { + if (!i_isalnum(ch)) + return -1; + if (host_name != NULL) + str_append_c(host_name, ch); + part = parser->cur; + } else { + if (!i_isalnum(*parser->cur)) + break; + parser->cur++; + } + + if (parser->cur < parser->end) { + /* *( alphanum | "-" ) alphanum */ + do { + offset = parser->cur; + + if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) { + return -1; + } else if (ret > 0) { + if (!i_isalnum(ch) && ch != '-') + break; + if (host_name != NULL) { + if (offset > part) + str_append_data(host_name, part, offset - part); + str_append_c(host_name, ch); + } + part = parser->cur; + } else { + ch = *parser->cur; + if (!i_isalnum(ch) && ch != '-') + break; + parser->cur++; + } + pch = ch; + } while (parser->cur < parser->end); + + if (!i_isalnum(pch)) { + parser->error = "Invalid domain label in hostname"; + return -1; + } + } + + if (host_name != NULL && parser->cur > part) + str_append_data(host_name, part, parser->cur - part); + + /* "." */ + if (parser->cur >= parser->end || ch != '.') + break; + if (host_name != NULL) + str_append_c(host_name, '.'); + if (parser->cur == offset) + parser->cur++; + part = parser->cur; + } + + if (parser->cur == first) + return 0; + + /* remove trailing '.' */ + if (host_name != NULL) { + const char *name = str_c(host_name); + + i_assert(str_len(host_name) > 0); + if (name[str_len(host_name)-1] == '.') + str_truncate(host_name, str_len(host_name)-1); + } + return 1; +} + +int uri_parse_host_name(struct uri_parser *parser, + const char **host_name_r) +{ + string_t *host_name = NULL; + int ret; + + if (host_name_r != NULL) + host_name = uri_parser_get_tmpbuf(parser, 256); + + if ((ret=uri_do_parse_host_name(parser, host_name)) <= 0) + return ret; + + if (host_name_r != NULL) + *host_name_r = str_c(host_name); + return 1; +} + +static int +uri_parse_ip_literal(struct uri_parser *parser, string_t *literal, + struct in6_addr *ip6_r) ATTR_NULL(2,3) +{ + const unsigned char *p; + const char *address; + struct in6_addr ip6; + + /* IP-literal = "[" ( IPv6address / IPvFuture ) "]" + * IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) + * IPv6address = ; Syntax not relevant: parsed using inet_pton() + */ + + /* "[" already verified */ + + /* Scan for end of address */ + for (p = parser->cur+1; p < parser->end; p++) { + if (*p == ']') + break; + } + + if (p >= parser->end || *p != ']') { + parser->error = "Expecting ']' at end of IP-literal"; + return -1; + } + + if (literal != NULL) + str_append_data(literal, parser->cur, p-parser->cur+1); + address = t_strdup_until(parser->cur+1, p); + parser->cur = p + 1; + + if (*address == '\0') { + parser->error = "Empty IPv6 host address"; + return -1; + } + if (*address == 'v') { + parser->error = p_strdup_printf(parser->pool, + "Future IP host address '%s' not supported", address); + return -1; + } + if (inet_pton(AF_INET6, address, &ip6) <= 0) { + parser->error = p_strdup_printf(parser->pool, + "Invalid IPv6 host address '%s'", address); + return -1; + } + if (ip6_r != NULL) + *ip6_r = ip6; + return 1; +} + +static int +uri_do_parse_host(struct uri_parser *parser, + struct uri_host *host, bool host_name) + ATTR_NULL(2) +{ + const unsigned char *preserve; + struct in_addr ip4; + struct in6_addr ip6; + string_t *literal = NULL; + int ret; + + /* RFC 3986: + * + * host = IP-literal / IPv4address / reg-name + */ + + if (host != NULL) + i_zero(host); + + literal = uri_parser_get_tmpbuf(parser, 256); + + /* IP-literal / */ + if (parser->cur < parser->end && *parser->cur == '[') { + if (uri_parse_ip_literal(parser, literal, &ip6) <= 0) + return -1; + + if (host != NULL) { + host->name = p_strdup(parser->pool, str_c(literal));; + host->ip.family = AF_INET6; + host->ip.u.ip6 = ip6; + } + return 1; + } + + /* IPv4address / + * + * If it fails to parse, we try to parse it as a reg-name + */ + preserve = parser->cur; + if ((ret = uri_parse_ipv4address(parser, literal, &ip4)) > 0) { + if (host != NULL) { + host->name = p_strdup(parser->pool, str_c(literal)); + host->ip.family = AF_INET; + host->ip.u.ip4 = ip4; + } + return ret; + } + parser->cur = preserve; + str_truncate(literal, 0); + + /* reg-name */ + if (host_name) { + if (uri_do_parse_host_name(parser, literal) < 0) + return -1; + } else if (uri_do_parse_reg_name(parser, literal) < 0) + return -1; + if (host != NULL) + host->name = p_strdup(parser->pool, str_c(literal)); + return 0; +} + +int uri_parse_host(struct uri_parser *parser, + struct uri_host *host) +{ + return uri_do_parse_host(parser, host, TRUE); +} + +static int +uri_parse_port(struct uri_parser *parser, + struct uri_authority *auth) ATTR_NULL(2) +{ + const unsigned char *first; + in_port_t port; + + /* RFC 3986: + * + * port = *DIGIT + */ + + first = parser->cur; + while (parser->cur < parser->end && i_isdigit(*parser->cur)) + parser->cur++; + + if (parser->cur == first) + return 0; + if (net_str2port(t_strdup_until(first, parser->cur), &port) < 0) { + parser->error = "Invalid port number"; + return -1; + } + + if (auth != NULL) + auth->port = port; + return 1; +} + +static int +uri_do_parse_authority(struct uri_parser *parser, + struct uri_authority *auth, bool host_name) ATTR_NULL(2) +{ + const unsigned char *p; + int ret; + + /* + * authority = [ userinfo "@" ] host [ ":" port ] + */ + + if (auth != NULL) + i_zero(auth); + + /* Scan ahead to check whether there is a [userinfo "@"] uri component */ + for (p = parser->cur; p < parser->end; p++){ + /* refuse 8bit characters */ + if ((*p & 0x80) != 0) + break; + + /* break at first delimiter */ + if (*p != '%' && (_uri_char_lookup[*p] & CHAR_MASK_UCHAR) == 0) + break; + } + + /* Extract userinfo */ + if (p < parser->end && *p == '@') { + if (auth != NULL) + auth->enc_userinfo = p_strdup_until(parser->pool, parser->cur, p); + parser->cur = p+1; + } + + /* host */ + if (uri_do_parse_host(parser, + (auth == NULL ? NULL : &auth->host), host_name) < 0) + return -1; + if (parser->cur == parser->end) + return 1; + switch (*parser->cur) { + case ':': case '/': case '?': case '#': + break; + default: + parser->error = "Invalid host identifier"; + return -1; + } + + /* [":" port] */ + if (*parser->cur == ':') { + parser->cur++; + + if ((ret = uri_parse_port(parser, auth)) < 0) + return ret; + if (parser->cur == parser->end) + return 1; + switch (*parser->cur) { + case '/': case '?': case '#': + break; + default: + parser->error = "Invalid host port"; + return -1; + } + } + + return 1; +} + +static int +uri_do_parse_slashslash_authority(struct uri_parser *parser, + struct uri_authority *auth, bool host_name) + ATTR_NULL(2) +{ + /* "//" authority */ + + if ((parser->end - parser->cur) <= 2 || parser->cur[0] != '/' || + parser->cur[1] != '/') + return 0; + + parser->cur += 2; + return uri_do_parse_authority(parser, auth, host_name); +} + +int uri_parse_authority(struct uri_parser *parser, + struct uri_authority *auth) +{ + return uri_do_parse_authority(parser, auth, FALSE); +} + +int uri_parse_slashslash_authority(struct uri_parser *parser, + struct uri_authority *auth) +{ + return uri_do_parse_slashslash_authority(parser, auth, FALSE); +} + +int uri_parse_host_authority(struct uri_parser *parser, + struct uri_authority *auth) +{ + return uri_do_parse_authority(parser, auth, TRUE); +} + +int uri_parse_slashslash_host_authority(struct uri_parser *parser, + struct uri_authority *auth) +{ + return uri_do_parse_slashslash_authority(parser, auth, TRUE); +} + +int uri_parse_path_segment(struct uri_parser *parser, const char **segment_r) +{ + const unsigned char *first = parser->cur; + int ret; + + while (parser->cur < parser->end) { + if (*parser->cur == '%') { + unsigned char ch = 0; + if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) + return -1; + if (ret > 0) + continue; + } + + if ((*parser->cur & 0x80) != 0 || + (_uri_char_lookup[*parser->cur] & CHAR_MASK_PCHAR) == 0) + break; + + parser->cur++; + } + + if (parser->cur < parser->end && + *parser->cur != '/' && *parser->cur != '?' && *parser->cur != '#' ) { + parser->error = + "Path component contains invalid character"; + return -1; + } + + if (first == parser->cur) + return 0; + + if (segment_r != NULL) + *segment_r = p_strdup_until(parser->pool, first, parser->cur); + return 1; +} + +int uri_parse_path(struct uri_parser *parser, + int *relative_r, const char *const **path_r) +{ + const unsigned char *pbegin = parser->cur; + ARRAY_TYPE(const_string) segments; + const char *segment = NULL; + unsigned int count; + int relative = 1; + int ret; + + count = 0; + if (path_r != NULL) + p_array_init(&segments, parser->pool, 16); + else + i_zero(&segments); + + /* check for a leading '/' and indicate absolute path + when it is present + */ + if (parser->cur < parser->end && *parser->cur == '/') { + parser->cur++; + relative = 0; + } + + /* parse first segment */ + if ((ret = uri_parse_path_segment(parser, &segment)) < 0) + return -1; + + for (;;) { + if (ret > 0) { + /* strip dot segments */ + if (segment[0] == '.') { + if (segment[1] == '.') { + if (segment[2] == '\0') { + /* '..' -> skip and... */ + segment = NULL; + + /* ... pop last segment (if any) */ + if (count > 0) { + if (path_r != NULL) { + i_assert(count == array_count(&segments)); + array_delete(&segments, count-1, 1); + } + count--; + } else if ( relative > 0 ) { + relative++; + } + } + } else if (segment[1] == '\0') { + /* '.' -> skip */ + segment = NULL; + } + } + } else { + segment = ""; + } + + if (segment != NULL) { + if (path_r != NULL) + array_push_back(&segments, &segment); + count++; + } + + if (parser->cur >= parser->end || *parser->cur != '/') + break; + parser->cur++; + + /* parse next path segment */ + if ((ret = uri_parse_path_segment(parser, &segment)) < 0) + return -1; + } + + if (relative_r != NULL) + *relative_r = relative; + if (path_r != NULL) + *path_r = NULL; + + if (parser->cur == pbegin) { + /* path part of URI is empty */ + return 0; + } + + if (path_r != NULL) { + /* special treatment for a trailing '..' or '.' */ + if (segment == NULL) { + segment = ""; + array_push_back(&segments, &segment); + } + array_append_zero(&segments); + *path_r = array_get(&segments, &count); + } + if (parser->cur < parser->end && + *parser->cur != '?' && *parser->cur != '#') { + parser->error = "Path component contains invalid character"; + return -1; + } + return 1; +} + +int uri_parse_query(struct uri_parser *parser, const char **query_r) +{ + const unsigned char *first = parser->cur; + int ret; + + /* RFC 3986: + * + * URI = { ... } [ "?" query ] { ... } + * query = *( pchar / "/" / "?" ) + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + */ + if (parser->cur >= parser->end || *parser->cur != '?') + return 0; + parser->cur++; + + while (parser->cur < parser->end) { + if (*parser->cur == '%') { + unsigned char ch = 0; + if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) + return -1; + if (ret > 0) + continue; + } + + if ((*parser->cur & 0x80) != 0 || + (_uri_char_lookup[*parser->cur] & CHAR_MASK_QCHAR) == 0) + break; + parser->cur++; + } + + if (parser->cur < parser->end && *parser->cur != '#') { + parser->error = "Query component contains invalid character"; + return -1; + } + + if (query_r != NULL) + *query_r = p_strdup_until(parser->pool, first+1, parser->cur); + return 1; +} + +int uri_parse_fragment(struct uri_parser *parser, const char **fragment_r) +{ + const unsigned char *first = parser->cur; + int ret; + + /* RFC 3986: + * + * URI = { ... } [ "#" fragment ] + * fragment = *( pchar / "/" / "?" ) + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + */ + + if (parser->cur >= parser->end || *parser->cur != '#') + return 0; + parser->cur++; + + while (parser->cur < parser->end) { + if (*parser->cur == '%') { + unsigned char ch = 0; + if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) + return -1; + if (ret > 0) + continue; + } + + if ((*parser->cur & 0x80) != 0 || + (_uri_char_lookup[*parser->cur] & CHAR_MASK_QCHAR) == 0) + break; + parser->cur++; + } + + if (parser->cur < parser->end) { + parser->error = "Fragment component contains invalid character"; + return -1; + } + + if (fragment_r != NULL) + *fragment_r = p_strdup_until(parser->pool, first+1, parser->cur); + return 1; +} + +void uri_parser_init_data(struct uri_parser *parser, + pool_t pool, const unsigned char *data, size_t size) +{ + i_zero(parser); + parser->pool = pool; + parser->begin = parser->cur = data; + parser->end = data + size; +} + +void uri_parser_init(struct uri_parser *parser, + pool_t pool, const char *uri) +{ + uri_parser_init_data + (parser, pool, (const unsigned char *)uri, strlen(uri)); +} + +string_t *uri_parser_get_tmpbuf(struct uri_parser *parser, size_t size) +{ + if (parser->tmpbuf == NULL) + parser->tmpbuf = str_new(parser->pool, size); + else + str_truncate(parser->tmpbuf, 0); + return parser->tmpbuf; +} + +int uri_parse_absolute_generic(struct uri_parser *parser, + enum uri_parse_flags flags) +{ + int relative, aret, ret = 0; + + /* + URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] + + hier-part = "//" authority path-abempty + / path-absolute + / path-rootless + / path-empty + path-abempty = *( "/" segment ) + path-absolute = "/" [ segment-nz *( "/" segment ) ] + path-rootless = segment-nz *( "/" segment ) + path-empty = 0<pchar> + + segment = *pchar + segment-nz = 1*pchar + */ + + /* scheme ":" */ + if ((flags & URI_PARSE_SCHEME_EXTERNAL) == 0 && + (ret=uri_parse_scheme(parser, NULL)) <= 0) { + if (ret == 0) + parser->error = "Missing scheme"; + return -1; + } + + /* "//" authority */ + if ((aret=uri_parse_slashslash_authority + (parser, NULL)) < 0) + return -1; + + /* path-absolute / path-rootless / path-empty */ + if (aret == 0) { + ret = uri_parse_path(parser, &relative, NULL); + /* path-abempty */ + } else if (parser->cur < parser->end && *parser->cur == '/') { + ret = uri_parse_path(parser, &relative, NULL); + i_assert(ret <= 0 || relative == 0); + } + if (ret < 0) + return -1; + + /* [ "?" query ] */ + if (uri_parse_query(parser, NULL) < 0) + return -1; + + /* [ "#" fragment ] */ + if ((ret=uri_parse_fragment(parser, NULL)) < 0) + return ret; + if (ret > 0 && (flags & URI_PARSE_ALLOW_FRAGMENT_PART) == 0) { + parser->error = "Fragment part not allowed"; + return -1; + } + + i_assert(parser->cur == parser->end); + return 0; +} + +/* + * Generic URI manipulation + */ + +void uri_host_copy(pool_t pool, struct uri_host *dest, + const struct uri_host *src) +{ + const char *host_name = src->name; + + /* create host name literal if caller is lazy */ + if (host_name == NULL && src->ip.family != 0) { + host_name = net_ip2addr(&src->ip); + i_assert(*host_name != '\0'); + } + + *dest = *src; + dest->name = p_strdup(pool, host_name); +} + +/* + * Check generic URI + */ + +int uri_check_data(const unsigned char *data, size_t size, + enum uri_parse_flags flags, const char **error_r) +{ + struct uri_parser parser; + int ret; + + i_zero(&parser); + parser.pool = pool_datastack_create(); + parser.begin = parser.cur = data; + parser.end = data + size; + + ret = uri_parse_absolute_generic(&parser, flags); + *error_r = parser.error; + return ret; +} + +int uri_check(const char *uri, enum uri_parse_flags flags, + const char **error_r) +{ + return uri_check_data + ((const unsigned char *)uri, strlen(uri), flags, error_r); +} + +/* + * Generic URI construction + */ + +void uri_data_encode(string_t *out, + const unsigned char esc_table[256], + unsigned char esc_mask, const char *esc_extra, + const char *data) +{ + const unsigned char *pbegin, *p; + + pbegin = p = (const unsigned char *)data; + while (*p != '\0') { + if ((*p & 0x80) != 0 || (esc_table[*p] & esc_mask) == 0 || + (esc_extra != NULL && strchr(esc_extra, (char)*p) != NULL)) { + if ((p - pbegin) > 0) + str_append_data(out, pbegin, p - pbegin); + str_printfa(out, "%%%02x", *p); + p++; + pbegin = p; + } else { + p++; + } + } + if ((p - pbegin) > 0) + str_append_data(out, pbegin, p - pbegin); +} + +void uri_append_scheme(string_t *out, const char *scheme) +{ + str_append(out, scheme); + str_append_c(out, ':'); +} + +void uri_append_user_data(string_t *out, const char *esc, + const char *data) +{ + uri_data_encode(out, _uri_char_lookup, CHAR_MASK_UCHAR, esc, data); +} + +void uri_append_userinfo(string_t *out, const char *userinfo) +{ + uri_append_user_data(out, NULL, userinfo); + str_append_c(out, '@'); +} + +void uri_append_host_name(string_t *out, const char *name) +{ + uri_data_encode(out, _uri_char_lookup, + CHAR_MASK_UNRESERVED | CHAR_MASK_SUB_DELIMS, NULL, name); +} + +void uri_append_host_ip(string_t *out, const struct ip_addr *host_ip) +{ + const char *addr = net_ip2addr(host_ip); + + i_assert(host_ip->family != 0); + + if (host_ip->family == AF_INET) { + str_append(out, addr); + return; + } + + i_assert(host_ip->family == AF_INET6); + str_append_c(out, '['); + str_append(out, addr); + str_append_c(out, ']'); +} + +void uri_append_host(string_t *out, const struct uri_host *host) +{ + if (host->name != NULL) { + /* assume IPv6 literal if starts with '['; avoid encoding */ + if (*host->name == '[') + str_append(out, host->name); + else + uri_append_host_name(out, host->name); + } else + uri_append_host_ip(out, &host->ip); +} + +void uri_append_port(string_t *out, in_port_t port) +{ + if (port != 0) + str_printfa(out, ":%u", port); +} + +void uri_append_path_segment_data(string_t *out, const char *esc, + const char *data) +{ + uri_data_encode(out, _uri_char_lookup, CHAR_MASK_PCHAR, esc, data); +} + +void uri_append_path_segment(string_t *out, const char *segment) +{ + str_append_c(out, '/'); + if (*segment != '\0') + uri_append_path_data(out, NULL, segment); +} + +void uri_append_path_data(string_t *out, const char *esc, + const char *data) +{ + uri_data_encode(out, _uri_char_lookup, CHAR_MASK_PFCHAR, esc, data); +} + +void uri_append_path(string_t *out, const char *path) +{ + str_append_c(out, '/'); + if (*path != '\0') + uri_append_path_data(out, NULL, path); +} + +void uri_append_query_data(string_t *out, const char *esc, + const char *data) +{ + uri_data_encode(out, _uri_char_lookup, CHAR_MASK_QCHAR, esc, data); +} + +void uri_append_query(string_t *out, const char *query) +{ + str_append_c(out, '?'); + if (*query != '\0') + uri_append_query_data(out, NULL, query); +} + +void uri_append_fragment_data(string_t *out, const char *esc, + const char *data) +{ + uri_data_encode(out, _uri_char_lookup, CHAR_MASK_QCHAR, esc, data); +} + +void uri_append_fragment(string_t *out, const char *fragment) +{ + str_append_c(out, '#'); + if (*fragment != '\0') + uri_append_fragment_data(out, NULL, fragment); +} + +void uri_append_unreserved(string_t *out, const char *data) +{ + uri_data_encode(out, _uri_char_lookup, CHAR_MASK_UNRESERVED, + NULL, data); +} + +void uri_append_unreserved_path(string_t *out, const char *data) +{ + uri_data_encode(out, _uri_char_lookup, CHAR_MASK_UNRESERVED_PATH, + NULL, data); +} diff --git a/src/lib/uri-util.h b/src/lib/uri-util.h new file mode 100644 index 0000000..837e54c --- /dev/null +++ b/src/lib/uri-util.h @@ -0,0 +1,298 @@ +#ifndef URI_UTIL_H +#define URI_UTIL_H + +#include "net.h" + +/* + * Generic URI parsing. + */ + +enum uri_parse_flags { + /* Scheme part 'scheme:' is already parsed externally. */ + URI_PARSE_SCHEME_EXTERNAL = BIT(0), + /* Allow '#fragment' part in URI */ + URI_PARSE_ALLOW_FRAGMENT_PART = BIT(1), +}; + +struct uri_host { + const char *name; + struct ip_addr ip; +}; + +struct uri_authority { + /* encoded userinfo part; e.g. "user:pass" */ + const char *enc_userinfo; + + struct uri_host host; + in_port_t port; /* 0 means no port specified */ +}; + +struct uri_parser { + pool_t pool; + const char *error; + + const unsigned char *begin, *cur, *end; + + string_t *tmpbuf; + + bool allow_pct_nul:1; +}; + +/* parse one instance of percent encoding. Returns 1 for success, + 0 if none is preset at the current parser position, and -1 in + case of error. The decoded character is returned in ch_r upon + success */ +int uri_parse_pct_encoded(struct uri_parser *parser, + unsigned char *ch_r); + +/* parse characters as long as these comply with the the 'unreserved' + syntax. Returns 1 if characters were found, 0 if none were found, + and -1 if there was an error */ +int uri_parse_unreserved(struct uri_parser *parser, string_t *part); +/* the same as uri_parse_unreserved(), but the allowed characters are + extended to 'unreserved / pct-encoded', meaning that percent encoding + is allowed */ +int uri_parse_unreserved_pct(struct uri_parser *parser, string_t *part); + +/* decode percent-encoded data from the 'data' parameter, up until the + 'until' parameter. If the latter is NULL, data is decoded up until the + '\0' character. The decoded data is allocated on the parser pool and + returned in decoded_r. Any errors are written to the parser object. */ +bool uri_data_decode(struct uri_parser *parser, const char *data, + const char *until, const char **decoded_r) ATTR_NULL(3); + +/* cut the 'scheme ":"' part from the URI. The uri_p pointer is updated to + point just past the ":". Returns 0 on success and -1 on error. The + result is returned in the scheme_r parameter. This can be NULL to use + this function for merely checking the presence of a valid scheme. */ +int uri_cut_scheme(const char **uri_p, const char **scheme_r) + ATTR_NULL(2); + +/* parse the URI 'scheme ":"' part. Returns 1 if successful, 0 if the first + character is not valid for a scheme, and -1 in case of error. The + result parameter scheme_r can be NULL to use this function for merely + checking the presence of a valid scheme. */ +int uri_parse_scheme(struct uri_parser *parser, const char **scheme_r) + ATTR_NULL(2); + +/* parse the URI 'reg-name' syntax. Returns 1 if successful, 0 if the first + character is not valid for a host name, and -1 in case of error. The + result parameter reg_name_r can be NULL to use this function for merely + checking the presence of a valid host name. The result is allocated from + the data stack. + */ +int uri_parse_reg_name(struct uri_parser *parser, + const char **reg_name_r) ATTR_NULL(2); +/* parse the URI 'reg-name' part as an Internet host name, which is a + sequence of domain name labels separated by '.', as defined in + Section 3.5 of RFC 1034 and Section 2.1 of RFC 1123. Returns 1 if + successful, 0 if the first character is not valid for a host name, + and -1 in case of error. The result parameter host_name_r can be NULL + to use this function for merely checking the presence of a valid host + name. The result is allocated from the data stack. + */ +int uri_parse_host_name(struct uri_parser *parser, + const char **host_name_r) ATTR_NULL(2); +/* parse the URI 'host' syntax, which is either an IP address literal or + a an Internet host name, as defined in Section 3.5 of RFC 1034 and + Section 2.1 of RFC 1123. An IP address literal is always allowed. + Returns 1 if successful, 0 if the first character is not valid for a + host name, and -1 in case of error. The provided host struct is filled + in with the parsed data, all allocated from the parser pool. The host + parameter can be NULL to use this function for merely checking for + valid 'host' syntax. + */ +int uri_parse_host(struct uri_parser *parser, + struct uri_host *host) ATTR_NULL(2); + +/* parse the URI 'authority' syntax. Returns 1 if successful, 0 if the + first character is not valid for the 'authority' syntax and -1 in case + of error. The provided uri_authority struct is filled in with the parsed + data, all allocated from the parser pool. The auth parameter can be + NULL to use this function for merely checking for valid 'authority' + syntax. + */ +int uri_parse_authority(struct uri_parser *parser, + struct uri_authority *auth) ATTR_NULL(2); +/* identical to uri_parse_authority(), except that this function parses + '"//" authority', rather than 'authority'. + */ +int uri_parse_slashslash_authority(struct uri_parser *parser, + struct uri_authority *auth) ATTR_NULL(2); +/* identical to uri_parse_authority(), except that this function parses + the registered name ('reg-name' syntax) as an Internet host name, as + defined in Section 3.5 of RFC 1034 and Section 2.1 of RFC 1123. + */ +int uri_parse_host_authority(struct uri_parser *parser, + struct uri_authority *auth) ATTR_NULL(2); +/* identical to uri_parse_slashslash_authority(), except that this + function parses the registered name ('reg-name' syntax) as an Internet + host name, as defined in Section 3.5 of RFC 1034 and Section 2.1 of + RFC 1123. + */ +int uri_parse_slashslash_host_authority(struct uri_parser *parser, + struct uri_authority *auth) ATTR_NULL(2); + +/* parse the URI 'segment' syntax. Returns 1 if successful, 0 if the first + character is not valid for the 'segment' syntax and -1 in case of + error. The result is allocated from the parser pool. Percent encoding is + not decoded in the result. The result parameter can be NULL to use this + function for merely checking for valid 'segment' syntax. + */ +int uri_parse_path_segment(struct uri_parser *parser, + const char **segment_r) ATTR_NULL(2); +/* parse the URI 'path' syntax. This also resolves '..' and '.' segments in + the path. If the path is relative, the relative_r parameter indicates + how many segments the base path must be moved towards root (as caused by + leading '..' segments). Returns 1 if successful, 0 if the first character + is not valid for the 'segment' syntax and -1 in case of error. The result + is a NULL-terminated string list allocated from the parser pool. Percent + encoding is not decoded in the result. The result parameter can be NULL + to use this function for merely checking for valid 'path' syntax. + */ +int uri_parse_path(struct uri_parser *parser, int *relative_r, + const char *const **path_r) ATTR_NULL(2,3); + +/* parse the URI 'query' syntax. Returns 1 if successful, 0 if the first + character is not valid for the 'query' syntax and -1 in case of + error. The result is allocated from the parser pool. Percent encoding is + not decoded in the result. The result parameter can be NULL to use this + function for merely checking for valid 'query' syntax. + */ +int uri_parse_query(struct uri_parser *parser, + const char **query_r) ATTR_NULL(2); +/* parse the URI 'fragment' syntax. Returns 1 if successful, 0 if the first + character is not valid for the 'fragment' syntax and -1 in case of + error. The result is allocated from the parser pool. Percent encoding is + not decoded in the result. The result parameter can be NULL to use this + function for merely checking for valid 'fragment' syntax. + */ +int uri_parse_fragment(struct uri_parser *parser, + const char **fragment_r) ATTR_NULL(2); + +/* initialize the URI parser with the provided data */ +void uri_parser_init_data(struct uri_parser *parser, + pool_t pool, const unsigned char *data, size_t size); +/* initialize the URI parser with the provided '\0'-terminated string */ +void uri_parser_init(struct uri_parser *parser, + pool_t pool, const char *uri); + +/* returns the temporary buffer associated with this parser. Can be used + for higher-level parsing activities. */ +string_t *uri_parser_get_tmpbuf(struct uri_parser *parser, + size_t size); + +/* Parse a generic (RFC3986) absolute URI for validity. + Returns 0 if valid and -1 otherwise. Note that some URI formats like + "sip", "aix" and "aaa" violate RFC3986 and will currently fail with + this function. + */ +int uri_parse_absolute_generic(struct uri_parser *parser, + enum uri_parse_flags flags); + +/* + * Generic URI manipulation + */ + +/* copy uri_host struct from src to dest and allocate it on pool */ +void uri_host_copy(pool_t pool, struct uri_host *dest, + const struct uri_host *src); + +/* + * Generic URI validation + */ + +/* Check whether the provided data is a valid absolute RFC3986 URI. + Returns 0 if valid and -1 otherwise. */ +int uri_check_data(const unsigned char *data, size_t size, + enum uri_parse_flags flags, const char **error_r); +/* Check whether the provided string is a valid absolute RFC3986 URI. + Returns 0 if valid and -1 otherwise. */ +int uri_check(const char *uri, enum uri_parse_flags, + const char **error_r); + +/* + * Generic URI construction + */ + +/* encodes the '\0'-terminated data using the percent encoding. The + esc_table is a 256 byte lookup table. If none of the esc_mask bits are + set at the character's position in the esc_table, a character needs + to be encoded. Also, when esc_extra contains a character, it needs to + be encoded. All other characters are copied verbatim to the out buffer. + */ +void uri_data_encode(string_t *out, + const unsigned char esc_table[256], + unsigned char esc_mask, const char *esc_extra, + const char *data) ATTR_NULL(4); + +/* append the provided scheme to the out buffer */ +void uri_append_scheme(string_t *out, const char *scheme); + +/* append partial user data (i.e. some part of what comes before '@') to + the out buffer. No '@' is produced. Characters are percent-encoded when + necessary. Characters in esc are always percent-encoded, even when these + are valid 'userinfo' characters. */ +void uri_append_user_data(string_t *out, + const char *esc, const char *data) ATTR_NULL(2); +/* append userinfo and '@' to the out buffer. Characters in userinfo are + percent-encoded when necessary.*/ +void uri_append_userinfo(string_t *out, const char *userinfo); + +/* append the host name to the out buffer. Characters are percent-encoded + when necessary.*/ +void uri_append_host_name(string_t *out, const char *name); +/* append the host IP address to the out buffer. */ +void uri_append_host_ip(string_t *out, const struct ip_addr *host_ip); +/* encode the URI host struct to the out buffer. */ +void uri_append_host(string_t *out, const struct uri_host *host); +/* append the port to the out buffer. */ +void uri_append_port(string_t *out, in_port_t port); + +/* append partial path segment data to the out buffer. No '/' is produced. + Characters are percent-encoded when necessary. Characters in esc are + always percent-encoded, even when these are valid 'segment' characters. + */ +void uri_append_path_segment_data(string_t *out, + const char *esc, const char *data) ATTR_NULL(2); +/* append a full path segment to the out buffer. A leading '/' is + produced. Characters are percent-encoded when necessary. */ +void uri_append_path_segment(string_t *out, const char *segment); +/* append partial path data to the out buffer. The data may include '/', + which is not encoded. Characters are percent-encoded when necessary. + Characters in esc are always percent-encoded, even when these are + valid 'path' characters.*/ +void uri_append_path_data(string_t *out, + const char *esc, const char *data) ATTR_NULL(2); +/* append a full path to the out buffer. A leading '/' is produced. The + data may include more '/', which is not encoded. Characters are + percent-encoded when necessary. + */ +void uri_append_path(string_t *out, const char *path); + +/* append partial query data to the out buffer. No leading '?' is + produced. Characters are percent-encoded when necessary. Characters + in esc are always percent-encoded, even when these are valid 'query' + characters.*/ +void uri_append_query_data(string_t *out, + const char *esc, const char *data) ATTR_NULL(2); +/* append a full URI query part to the out buffer. A leading '?' is + produced. Characters are percent-encoded when necessary. */ +void uri_append_query(string_t *out, const char *query); + +/* append partial fragment data to the out buffer. No leading '#' is + produced. Characters are percent-encoded when necessary. Characters + in esc are always percent-encoded, even when these are valid + 'fragment' characters.*/ +void uri_append_fragment_data(string_t *out, + const char *esc, const char *data) ATTR_NULL(2); +/* append a full URI fragment part to the out buffer. A leading '#' is + produced. Characters are percent-encoded when necessary. */ +void uri_append_fragment(string_t *out, const char *fragment); + +/* append data to the out buffer and escape any reserved character */ +void uri_append_unreserved(string_t *out, const char *data); +/* append data to the out buffer and escape any reserved character except '/' */ +void uri_append_unreserved_path(string_t *out, const char *data); + +#endif diff --git a/src/lib/utc-mktime.c b/src/lib/utc-mktime.c new file mode 100644 index 0000000..e67c687 --- /dev/null +++ b/src/lib/utc-mktime.c @@ -0,0 +1,79 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "utc-mktime.h" + +static int tm_cmp(const struct tm *tm1, const struct tm *tm2) +{ + int diff; + + if ((diff = tm1->tm_year - tm2->tm_year) != 0) + return diff; + if ((diff = tm1->tm_mon - tm2->tm_mon) != 0) + return diff; + if ((diff = tm1->tm_mday - tm2->tm_mday) != 0) + return diff; + if ((diff = tm1->tm_hour - tm2->tm_hour) != 0) + return diff; + if ((diff = tm1->tm_min - tm2->tm_min) != 0) + return diff; + return tm1->tm_sec - tm2->tm_sec; +} + +static inline void adjust_leap_second(struct tm *tm) +{ + if (tm->tm_sec == 60) + tm->tm_sec = 59; +} + +#ifdef HAVE_TIMEGM +/* Normalization done by timegm is considered a failure here, since it means + * the timestamp is not valid as-is. Leap second 60 is adjusted to 59 before + * this though. */ +time_t utc_mktime(const struct tm *tm) +{ + struct tm leap_adj_tm = *tm; + adjust_leap_second(&leap_adj_tm); + struct tm tmp = leap_adj_tm; + time_t t; + + t = timegm(&tmp); + if (tm_cmp(&leap_adj_tm, &tmp) != 0) + return (time_t)-1; + return t; +} +#else +time_t utc_mktime(const struct tm *tm) +{ + struct tm leap_adj_tm = *tm; + adjust_leap_second(&leap_adj_tm); + const struct tm *try_tm; + time_t t; + int bits, dir; + + /* we'll do a binary search across the entire valid time_t range. + when gmtime()'s output matches the tm parameter, we've found the + correct time_t value. this also means that if tm contains invalid + values, -1 is returned. */ +#ifdef TIME_T_SIGNED + t = 0; +#else + t = (time_t)1 << (TIME_T_MAX_BITS - 1); +#endif + for (bits = TIME_T_MAX_BITS - 2;; bits--) { + try_tm = gmtime(&t); + dir = tm_cmp(&leap_adj_tm, try_tm); + if (dir == 0) + return t; + if (bits < 0) + break; + + if (dir < 0) + t -= (time_t)1 << bits; + else + t += (time_t)1 << bits; + } + + return (time_t)-1; +} +#endif diff --git a/src/lib/utc-mktime.h b/src/lib/utc-mktime.h new file mode 100644 index 0000000..9f32f6e --- /dev/null +++ b/src/lib/utc-mktime.h @@ -0,0 +1,11 @@ +#ifndef UTC_MKTIME_H +#define UTC_MKTIME_H + +#include <time.h> + +/* Like mktime(), but assume that tm is in UTC. Unlike mktime(), values in + tm fields must be in valid range. Leap second is accepted any time though + since utc_mktime is often used before applying the time zone offset. */ +time_t utc_mktime(const struct tm *tm); + +#endif diff --git a/src/lib/utc-offset.c b/src/lib/utc-offset.c new file mode 100644 index 0000000..d3b4bd2 --- /dev/null +++ b/src/lib/utc-offset.c @@ -0,0 +1,38 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "utc-offset.h" + +#include <sys/time.h> + +int utc_offset(struct tm *tm, time_t t ATTR_UNUSED) +{ +#ifdef HAVE_TM_GMTOFF + return (int) (tm->tm_gmtoff/60); +#else + struct tm ltm, gtm; + int offset; + + /* gmtime() overwrites tm, so we need to copy it elsewhere */ + ltm = *tm; + tm = gmtime(&t); + gtm = *tm; + + /* max offset of 24 hours */ + if ((ltm.tm_yday < gtm.tm_yday && ltm.tm_year == gtm.tm_year) || + ltm.tm_year < gtm.tm_year) + offset = -24 * 60; + else if ((ltm.tm_yday > gtm.tm_yday && ltm.tm_year == gtm.tm_year) || + ltm.tm_year > gtm.tm_year) + offset = 24 * 60; + else + offset = 0; + + offset += (ltm.tm_hour - gtm.tm_hour) * 60; + offset += (ltm.tm_min - gtm.tm_min); + + /* restore overwritten tm */ + *tm = ltm; + return offset; +#endif +} diff --git a/src/lib/utc-offset.h b/src/lib/utc-offset.h new file mode 100644 index 0000000..a2196f3 --- /dev/null +++ b/src/lib/utc-offset.h @@ -0,0 +1,9 @@ +#ifndef UTC_OFFSET_H +#define UTC_OFFSET_H + +#include <time.h> + +/* Returns given time's offset to UTC in minutes. */ +int utc_offset(struct tm *tm, time_t t); + +#endif diff --git a/src/lib/var-expand-if.c b/src/lib/var-expand-if.c new file mode 100644 index 0000000..aa0a9b3 --- /dev/null +++ b/src/lib/var-expand-if.c @@ -0,0 +1,269 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "var-expand.h" +#include "var-expand-private.h" +#include "wildcard-match.h" + +#include <regex.h> + +enum var_expand_if_op { + OP_UNKNOWN, + OP_NUM_EQ, + OP_NUM_LT, + OP_NUM_LE, + OP_NUM_GT, + OP_NUM_GE, + OP_NUM_NE, +/* put all numeric comparisons before this line */ + OP_STR_EQ, + OP_STR_LT, + OP_STR_LE, + OP_STR_GT, + OP_STR_GE, + OP_STR_NE, + OP_STR_LIKE, + OP_STR_NOT_LIKE, + OP_STR_REGEXP, + OP_STR_NOT_REGEXP, +/* keep this as last */ + OP_COUNT +}; + +static enum var_expand_if_op var_expand_if_str_to_comp(const char *op) +{ + const char *ops[OP_COUNT] = { + NULL, + "==", + "<", + "<=", + ">", + ">=", + "!=", + "eq", + "lt", + "le", + "gt", + "ge", + "ne", + "*", + "!*", + "~", + "!~", + }; + for(enum var_expand_if_op i = 1; i < OP_COUNT; i++) { + i_assert(ops[i] != NULL); + if (strcmp(op, ops[i]) == 0) + return i; + } + return OP_UNKNOWN; +} + +static int var_expand_if_comp(const char *lhs, const char *_op, const char *rhs, + bool *result_r, const char **error_r) +{ + bool neg = FALSE; + enum var_expand_if_op op = var_expand_if_str_to_comp(_op); + + *result_r = FALSE; + if (op == OP_UNKNOWN) { + *error_r = t_strdup_printf("if: Unsupported comparator '%s'", _op); + return -1; + } + + if (op < OP_STR_EQ) { + intmax_t a; + intmax_t b; + if (str_to_intmax(lhs, &a) < 0) { + *error_r = t_strdup_printf("if: %s (lhs) is not a number", lhs); + return -1; + } + if (str_to_intmax(rhs, &b) < 0) { + *error_r = t_strdup_printf("if: %s (rhs) is not a number", rhs); + return -1; + } + switch(op) { + case OP_NUM_EQ: + *result_r = a==b; + return 0; + case OP_NUM_LT: + *result_r = a<b; + return 0; + case OP_NUM_LE: + *result_r = a<=b; + return 0; + case OP_NUM_GT: + *result_r = a>b; + return 0; + case OP_NUM_GE: + *result_r = a>=b; + return 0; + case OP_NUM_NE: + *result_r = a!=b; + return 0; + default: + i_panic("Missing numeric comparator %u", op); + } + } + + switch(op) { + case OP_STR_EQ: + *result_r = strcmp(lhs,rhs)==0; + return 0; + case OP_STR_LT: + *result_r = strcmp(lhs,rhs)<0; + return 0; + case OP_STR_LE: + *result_r = strcmp(lhs,rhs)<=0; + return 0; + case OP_STR_GT: + *result_r = strcmp(lhs,rhs)>0; + return 0; + case OP_STR_GE: + *result_r = strcmp(lhs,rhs)>=0; + return 0; + case OP_STR_NE: + *result_r = strcmp(lhs,rhs)!=0; + return 0; + case OP_STR_LIKE: + *result_r = wildcard_match(lhs, rhs); + return 0; + case OP_STR_NOT_LIKE: + *result_r = !wildcard_match(lhs, rhs); + return 0; + case OP_STR_NOT_REGEXP: + neg = TRUE; + /* fall through */ + case OP_STR_REGEXP: { + int ec; + bool res; + regex_t reg; + if ((ec = regcomp(®, rhs, REG_EXTENDED)) != 0) { + size_t siz; + char *errbuf; + siz = regerror(ec, ®, NULL, 0); + errbuf = t_malloc_no0(siz); + (void)regerror(ec, ®, errbuf, siz); + *error_r = t_strdup_printf("if: regex failed: %s", + errbuf); + return -1; + } + if ((ec = regexec(®, lhs, 0, 0, 0)) != 0) { + i_assert(ec == REG_NOMATCH); + res = FALSE; + } else { + res = TRUE; + } + regfree(®); + /* this should be same as neg. + if NOT_REGEXP, neg == TRUE and res should be FALSE + if REGEXP, ned == FALSE, and res should be TRUE + */ + *result_r = res != neg; + return 0; + } + default: + i_panic("Missing generic comparator %u", op); + } +} + +int var_expand_if(struct var_expand_context *ctx, + const char *key, const char *field, + const char **result_r, const char **error_r) +{ + /* in case the original input had :, we need to fix that + by concatenating the key and field together. */ + const char *input = t_strconcat(key, ":", field, NULL); + const char *p = strchr(input, ';'); + const char *par_end; + string_t *parbuf; + const char *const *parms; + unsigned int depth = 0; + int ret; + bool result, escape = FALSE, maybe_var = FALSE; + + if (p == NULL) { + *error_r = "if: missing parameter(s)"; + return -1; + } + ARRAY_TYPE(const_string) params; + t_array_init(¶ms, 6); + + parbuf = t_str_new(64); + /* we need to skip any %{} parameters here, so we can split the string + correctly from , without breaking any inner expansions */ + for(par_end = p+1; *par_end != '\0'; par_end++) { + if (*par_end == '\\') { + escape = TRUE; + continue; + } else if (escape) { + str_append_c(parbuf, *par_end); + escape = FALSE; + continue; + } + if (*par_end == '%') { + maybe_var = TRUE; + } else if (maybe_var && *par_end == '{') { + depth++; + maybe_var = FALSE; + } else if (depth > 0 && *par_end == '}') { + depth--; + } else if (depth == 0 && *par_end == ';') { + const char *par = str_c(parbuf); + array_push_back(¶ms, &par); + parbuf = t_str_new(64); + continue; + /* if there is a unescaped : at top level it means + that the key + arguments end here. it's probably + a by-product of the t_strconcat at top of function, + which is best handled here. */ + } else if (depth == 0 && *par_end == ':') { + break; + } + str_append_c(parbuf, *par_end); + } + + if (str_len(parbuf) > 0) { + const char *par = str_c(parbuf); + array_push_back(¶ms, &par); + } + + if (array_count(¶ms) != 5) { + if (array_count(¶ms) == 4) { + const char *empty = ""; + array_push_back(¶ms, &empty); + } else { + *error_r = t_strdup_printf("if: requires four or five parameters, got %u", + array_count(¶ms)); + return -1; + } + } + + array_append_zero(¶ms); + parms = array_front(¶ms); + t_array_init(¶ms, 6); + + for(;*parms != NULL; parms++) { + /* expand the parameters */ + string_t *param = t_str_new(64); + if ((ret = var_expand_with_funcs(param, *parms, ctx->table, + ctx->func_table, ctx->context, + error_r)) <= 0) { + return ret; + } + const char *p = str_c(param); + array_push_back(¶ms, &p); + } + + i_assert(array_count(¶ms) == 5); + + /* execute comparison */ + const char *const *args = array_front(¶ms); + if (var_expand_if_comp(args[0], args[1], args[2], &result, error_r)<0) + return -1; + *result_r = result ? args[3] : args[4]; + return 1; +} + diff --git a/src/lib/var-expand-private.h b/src/lib/var-expand-private.h new file mode 100644 index 0000000..f02356a --- /dev/null +++ b/src/lib/var-expand-private.h @@ -0,0 +1,56 @@ +#ifndef VAR_EXPAND_PRIVATE_H +#define VAR_EXPAND_PRIVATE_H 1 + +struct var_expand_context { + /* current variables */ + const struct var_expand_table *table; + /* caller provided function table */ + const struct var_expand_func_table *func_table; + /* caller provided context */ + void *context; + /* last offset, negative counts from end*/ + int offset; + /* last width, negative counts from end */ + int width; + /* last zero padding */ + bool zero_padding:1; +}; + +/* this can be used to register a *global* function that is + prepended to function table. These can be used to register some + special handling for keys. + + you can call var_expand_with_funcs if you need to + expand something inside here. + + return -1 on error, 0 on unknown variable, 1 on success +*/ +typedef int +var_expand_extension_func_t(struct var_expand_context *ctx, + const char *key, const char *field, + const char **result_r, const char **error_r); + +struct var_expand_extension_func_table { + const char *key; + var_expand_extension_func_t *func; +}; + +int var_expand_long(struct var_expand_context *ctx, + const void *key_start, size_t key_len, + const char **var_r, const char **error_r); + +void var_expand_extensions_init(void); +void var_expand_extensions_deinit(void); + +/* Functions registered here are placed before in-built functions, + so you can include your own implementation of something. + Be careful. Use NULL terminated list. +*/ +void var_expand_register_func_array(const struct var_expand_extension_func_table *funcs); +void var_expand_unregister_func_array(const struct var_expand_extension_func_table *funcs); + +int var_expand_if(struct var_expand_context *ctx, + const char *key, const char *field, + const char **result_r, const char **error_r); + +#endif diff --git a/src/lib/var-expand.c b/src/lib/var-expand.c new file mode 100644 index 0000000..7ddaec0 --- /dev/null +++ b/src/lib/var-expand.c @@ -0,0 +1,833 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "md5.h" +#include "hash.h" +#include "hex-binary.h" +#include "base64.h" +#include "hostpid.h" +#include "hmac.h" +#include "pkcs5.h" +#include "hash-method.h" +#include "str.h" +#include "strescape.h" +#include "var-expand.h" +#include "var-expand-private.h" + +#include <unistd.h> +#include <ctype.h> + +#define TABLE_LAST(t) \ + ((t)->key == '\0' && (t)->long_key == NULL) + +struct var_expand_modifier { + char key; + const char *(*func)(const char *, struct var_expand_context *); +}; + +static ARRAY(struct var_expand_extension_func_table) var_expand_extensions; + +static const char * +m_str_lcase(const char *str, struct var_expand_context *ctx ATTR_UNUSED) +{ + return t_str_lcase(str); +} + +static const char * +m_str_ucase(const char *str, struct var_expand_context *ctx ATTR_UNUSED) +{ + return t_str_ucase(str); +} + +static const char * +m_str_escape(const char *str, struct var_expand_context *ctx ATTR_UNUSED) +{ + return str_escape(str); +} + +static const char * +m_str_hex(const char *str, struct var_expand_context *ctx ATTR_UNUSED) +{ + unsigned long long l; + + if (str_to_ullong(str, &l) < 0) + l = 0; + return t_strdup_printf("%llx", l); +} + +static const char * +m_str_reverse(const char *str, struct var_expand_context *ctx ATTR_UNUSED) +{ + size_t len = strlen(str); + char *p, *rev; + + rev = t_malloc_no0(len + 1); + rev[len] = '\0'; + + for (p = rev + len - 1; *str != '\0'; str++) + *p-- = *str; + return rev; +} + +static const char *m_str_hash(const char *str, struct var_expand_context *ctx) +{ + unsigned int value = str_hash(str); + string_t *hash = t_str_new(20); + + if (ctx->width != 0) { + value %= ctx->width; + ctx->width = 0; + } + + str_printfa(hash, "%x", value); + while ((int)str_len(hash) < ctx->offset) + str_insert(hash, 0, "0"); + ctx->offset = 0; + + return str_c(hash); +} + +static const char * +m_str_newhash(const char *str, struct var_expand_context *ctx) +{ + string_t *hash = t_str_new(20); + unsigned char result[MD5_RESULTLEN]; + unsigned int i; + uint64_t value = 0; + + md5_get_digest(str, strlen(str), result); + for (i = 0; i < sizeof(value); i++) { + value <<= 8; + value |= result[i]; + } + + if (ctx->width != 0) { + value %= ctx->width; + ctx->width = 0; + } + + str_printfa(hash, "%x", (unsigned int)value); + while ((int)str_len(hash) < ctx->offset) + str_insert(hash, 0, "0"); + ctx->offset = 0; + + return str_c(hash); +} + +static const char * +m_str_md5(const char *str, struct var_expand_context *ctx ATTR_UNUSED) +{ + unsigned char digest[16]; + + md5_get_digest(str, strlen(str), digest); + + return binary_to_hex(digest, sizeof(digest)); +} + +static const char * +m_str_ldap_dn(const char *str, struct var_expand_context *ctx ATTR_UNUSED) +{ + string_t *ret = t_str_new(256); + + while (*str != '\0') { + if (*str == '.') + str_append(ret, ",dc="); + else + str_append_c(ret, *str); + str++; + } + + return str_free_without_data(&ret); +} + +static const char * +m_str_trim(const char *str, struct var_expand_context *ctx ATTR_UNUSED) +{ + size_t len; + + len = strlen(str); + while (len > 0 && i_isspace(str[len-1])) + len--; + return t_strndup(str, len); +} + +#define MAX_MODIFIER_COUNT 10 +static const struct var_expand_modifier modifiers[] = { + { 'L', m_str_lcase }, + { 'U', m_str_ucase }, + { 'E', m_str_escape }, + { 'X', m_str_hex }, + { 'R', m_str_reverse }, + { 'H', m_str_hash }, + { 'N', m_str_newhash }, + { 'M', m_str_md5 }, + { 'D', m_str_ldap_dn }, + { 'T', m_str_trim }, + { '\0', NULL } +}; + +static int +var_expand_short(const struct var_expand_table *table, char key, + const char **var_r, const char **error_r) +{ + const struct var_expand_table *t; + + if (table != NULL) { + for (t = table; !TABLE_LAST(t); t++) { + if (t->key == key) { + *var_r = t->value != NULL ? t->value : ""; + return 1; + } + } + } + + /* not found */ + if (key == '%') { + *var_r = "%"; + return 1; + } + if (*error_r == NULL) + *error_r = t_strdup_printf("Unknown variable '%%%c'", key); + *var_r = t_strdup_printf("UNSUPPORTED_VARIABLE_%c", key); + return 0; +} + +static int +var_expand_hash(struct var_expand_context *ctx, + const char *key, const char *field, + const char **result_r, const char **error_r) +{ + enum { + FORMAT_HEX, + FORMAT_HEX_UC, + FORMAT_BASE64 + } format = FORMAT_HEX; + + const char *p = strchr(key, ';'); + const char *const *args = NULL; + const char *algo = key; + const char *value; + int ret; + + if (p != NULL) { + algo = t_strcut(key, ';'); + args = t_strsplit(p+1, ","); + } + + const struct hash_method *method; + if (strcmp(algo, "pkcs5") == 0) { + method = hash_method_lookup("sha256"); + } else if ((method = hash_method_lookup(algo)) == NULL) { + return 0; + } + + string_t *field_value = t_str_new(64); + string_t *salt = t_str_new(64); + string_t *tmp = t_str_new(method->digest_size); + + if ((ret = var_expand_long(ctx, field, strlen(field), + &value, error_r)) < 1) { + return ret; + } + + str_append(field_value, value); + + /* default values */ + unsigned int rounds = 1; + unsigned int truncbits = 0; + + if (strcmp(algo, "pkcs5") == 0) { + rounds = 2048; + str_append(salt, field); + } + + while(args != NULL && *args != NULL) { + const char *k = t_strcut(*args, '='); + const char *value = strchr(*args, '='); + if (value == NULL) { + args++; + continue; + } else { + value++; + } + if (strcmp(k, "rounds") == 0) { + if (str_to_uint(value, &rounds)<0) { + *error_r = t_strdup_printf( + "Cannot parse hash arguments:" + "'%s' is not number for rounds", + value); + return -1; + } + if (rounds < 1) { + *error_r = t_strdup_printf( + "Cannot parse hash arguments:" + "rounds must be at least 1"); + return -1; + } + } else if (strcmp(k, "truncate") == 0) { + if (str_to_uint(value, &truncbits)<0) { + *error_r = t_strdup_printf( + "Cannot parse hash arguments:" + "'%s' is not number for truncbits", + value); + return -1; + } + truncbits = I_MIN(truncbits, method->digest_size*8); + } else if (strcmp(k, "salt") == 0) { + str_truncate(salt, 0); + if (var_expand_with_funcs(salt, value, ctx->table, + ctx->func_table, ctx->context, + error_r) < 0) { + return -1; + } + break; + } else if (strcmp(k, "format") == 0) { + if (strcmp(value, "hex") == 0) { + format = FORMAT_HEX; + } else if (strcmp(value, "hexuc") == 0){ + format = FORMAT_HEX_UC; + } else if (strcmp(value, "base64") == 0) { + format = FORMAT_BASE64; + } else { + *error_r = t_strdup_printf( + "Cannot parse hash arguments:" + "'%s' is not supported format", + value); + return -1; + } + } + args++; + } + + str_truncate(tmp, 0); + + if (strcmp(algo, "pkcs5") == 0) { + if (pkcs5_pbkdf(PKCS5_PBKDF2, method, + field_value->data, field_value->used, + salt->data, salt->used, + rounds, HMAC_MAX_CONTEXT_SIZE, tmp) != 0) { + *error_r = "Cannot hash: PKCS5_PBKDF2 failed"; + return -1; + } + } else { + void *context = t_malloc_no0(method->context_size); + + str_append_str(tmp, field_value); + + for(;rounds>0;rounds--) { + method->init(context); + if (salt->used > 0) + method->loop(context, salt->data, salt->used); + method->loop(context, tmp->data, tmp->used); + unsigned char *digest = + buffer_get_modifiable_data(tmp, NULL); + method->result(context, digest); + if (tmp->used != method->digest_size) + buffer_set_used_size(tmp, method->digest_size); + } + } + + if (truncbits > 0) + buffer_truncate_rshift_bits(tmp, truncbits); + + switch(format) { + case FORMAT_HEX: + *result_r = binary_to_hex(tmp->data, tmp->used); + return 1; + case FORMAT_HEX_UC: + *result_r = binary_to_hex(tmp->data, tmp->used); + return 1; + case FORMAT_BASE64: { + string_t *dest = t_str_new(64); + base64_encode(tmp->data, tmp->used, dest); + *result_r = str_c(dest); + return 1; + } + } + + i_unreached(); +} + +static int +var_expand_func(const struct var_expand_func_table *func_table, + const char *key, const char *data, void *context, + const char **var_r, const char **error_r) +{ + const char *value = NULL; + int ret; + + if (strcmp(key, "env") == 0) { + value = getenv(data); + *var_r = value != NULL ? value : ""; + return 1; + } + if (func_table != NULL) { + for (; func_table->key != NULL; func_table++) { + if (strcmp(func_table->key, key) == 0) { + ret = func_table->func(data, context, &value, error_r); + *var_r = value != NULL ? value : ""; + return ret; + } + } + } + if (*error_r == NULL) + *error_r = t_strdup_printf("Unknown variable '%%%s'", key); + *var_r = t_strdup_printf("UNSUPPORTED_VARIABLE_%s", key); + return 0; +} + +static int +var_expand_try_extension(struct var_expand_context *ctx, + const char *key, const char *data, + const char **var_r, const char **error_r) +{ + int ret; + const char *sep = strchr(key, ';'); + + if (sep == NULL) sep = key + strlen(key); + + /* try with extensions */ + const struct var_expand_extension_func_table *f; + array_foreach(&var_expand_extensions, f) { + /* ensure we won't match abbreviations */ + size_t len = sep-key; + if (strncasecmp(key, f->key, len) == 0 && f->key[len] == '\0') + return f->func(ctx, key, data, var_r, error_r); + } + if ((ret = var_expand_func(ctx->func_table, key, data, + ctx->context, var_r, error_r)) == 0) { + *error_r = t_strdup_printf("Unknown variable '%%%s'", key); + } + return ret; +} + + +int +var_expand_long(struct var_expand_context *ctx, + const void *key_start, size_t key_len, + const char **var_r, const char **error_r) +{ + const struct var_expand_table *t; + const char *key, *value = NULL; + int ret = 1; + + if (ctx->table != NULL) { + for (t = ctx->table; !TABLE_LAST(t); t++) { + if (t->long_key != NULL && + strncmp(t->long_key, key_start, key_len) == 0 && + t->long_key[key_len] == '\0') { + *var_r = t->value != NULL ? t->value : ""; + return 1; + } + } + } + key = t_strndup(key_start, key_len); + + /* built-in variables: */ + switch (key_len) { + case 3: + if (strcmp(key, "pid") == 0) + value = my_pid; + else if (strcmp(key, "uid") == 0) + value = dec2str(geteuid()); + else if (strcmp(key, "gid") == 0) + value = dec2str(getegid()); + break; + case 8: + if (strcmp(key, "hostname") == 0) + value = my_hostname; + break; + } + + if (value == NULL) { + const char *data = strchr(key, ':'); + + if (data != NULL) + key = t_strdup_until(key, data++); + else + data = ""; + + ret = var_expand_try_extension(ctx, key, data, &value, error_r); + + if (ret <= 0 && value == NULL) { + value = ""; + } + } + *var_r = value; + return ret; +} + +int var_expand_with_funcs(string_t *dest, const char *str, + const struct var_expand_table *table, + const struct var_expand_func_table *func_table, + void *context, const char **error_r) +{ + const struct var_expand_modifier *m; + const char *var; + struct var_expand_context ctx; + const char *(*modifier[MAX_MODIFIER_COUNT]) + (const char *, struct var_expand_context *); + const char *end; + unsigned int i, modifier_count; + size_t len; + int ret, final_ret = 1; + + *error_r = NULL; + + i_zero(&ctx); + ctx.table = table; + ctx.func_table = func_table; + ctx.context = context; + + for (; *str != '\0'; str++) { + if (*str != '%') + str_append_c(dest, *str); + else { + int sign = 1; + + str++; + + /* reset per-field modifiers */ + ctx.offset = 0; + ctx.width = 0; + ctx.zero_padding = FALSE; + + /* [<offset>.]<width>[<modifiers>]<variable> */ + if (*str == '-') { + sign = -1; + str++; + } + if (*str == '0') { + ctx.zero_padding = TRUE; + str++; + } + while (*str >= '0' && *str <= '9') { + ctx.width = ctx.width*10 + (*str - '0'); + str++; + } + + if (*str == '.') { + ctx.offset = sign * ctx.width; + sign = 1; + ctx.width = 0; + str++; + + /* if offset was prefixed with zero (or it was + plain zero), just ignore that. zero padding + is done with the width. */ + ctx.zero_padding = FALSE; + if (*str == '0') { + ctx.zero_padding = TRUE; + str++; + } + if (*str == '-') { + sign = -1; + str++; + } + + while (*str >= '0' && *str <= '9') { + ctx.width = ctx.width*10 + (*str - '0'); + str++; + } + ctx.width = sign * ctx.width; + } + + modifier_count = 0; + while (modifier_count < MAX_MODIFIER_COUNT) { + modifier[modifier_count] = NULL; + for (m = modifiers; m->key != '\0'; m++) { + if (m->key == *str) { + /* @UNSAFE */ + modifier[modifier_count] = + m->func; + str++; + break; + } + } + if (modifier[modifier_count] == NULL) + break; + modifier_count++; + } + + if (*str == '\0') + break; + + var = NULL; + if (*str == '{' && strchr(str, '}') != NULL) { + /* %{long_key} */ + unsigned int ctr = 1; + bool escape = FALSE; + end = str; + while(*++end != '\0' && ctr > 0) { + if (!escape && *end == '\\') { + escape = TRUE; + continue; + } + if (escape) { + escape = FALSE; + continue; + } + if (*end == '{') ctr++; + if (*end == '}') ctr--; + } + if (ctr == 0) + /* it needs to come back a bit */ + end--; + /* if there is no } it will consume rest of the + string */ + len = end - (str + 1); + ret = var_expand_long(&ctx, str+1, len, + &var, error_r); + str = end; + } else { + ret = var_expand_short(ctx.table, *str, + &var, error_r); + } + i_assert(var != NULL); + + if (final_ret > ret) + final_ret = ret; + + if (ret <= 0) + str_append(dest, var); + else { + for (i = 0; i < modifier_count; i++) + var = modifier[i](var, &ctx); + + if (ctx.offset < 0) { + /* if offset is < 0 then we want to + start at the end */ + size_t len = strlen(var); + size_t offset_from_end = -ctx.offset; + + if (len > offset_from_end) + var += len - offset_from_end; + } else { + while (*var != '\0' && ctx.offset > 0) { + ctx.offset--; + var++; + } + } + if (ctx.width == 0) + str_append(dest, var); + else if (!ctx.zero_padding) { + if (ctx.width < 0) + ctx.width = strlen(var) - (-ctx.width); + str_append_max(dest, var, ctx.width); + } else { + /* %05d -like padding. no truncation. */ + ssize_t len = strlen(var); + while (len < ctx.width) { + str_append_c(dest, '0'); + ctx.width--; + } + str_append(dest, var); + } + } + } + } + return final_ret; +} + +int var_expand(string_t *dest, const char *str, + const struct var_expand_table *table, const char **error_r) +{ + return var_expand_with_funcs(dest, str, table, NULL, NULL, error_r); +} + +static bool +var_get_key_range_full(const char *str, unsigned int *idx_r, + unsigned int *size_r) +{ + const struct var_expand_modifier *m; + unsigned int i = 0; + + /* [<offset>.]<width>[<modifiers>]<variable> */ + while ((str[i] >= '0' && str[i] <= '9') || str[i] == '-') + i++; + + if (str[i] == '.') { + i++; + while ((str[i] >= '0' && str[i] <= '9') || str[i] == '-') + i++; + } + + do { + for (m = modifiers; m->key != '\0'; m++) { + if (m->key == str[i]) { + i++; + break; + } + } + } while (m->key != '\0'); + + if (str[i] != '{') { + /* short key */ + *idx_r = i; + *size_r = str[i] == '\0' ? 0 : 1; + return FALSE; + } else { + unsigned int depth = 1; + bool escape = FALSE; + /* long key */ + *idx_r = ++i; + for (; str[i] != '\0'; i++) { + if (!escape && str[i] == '\\') { + escape = TRUE; + continue; + } + if (escape) { + escape = FALSE; + continue; + } + if (str[i] == '{') + depth++; + if (str[i] == '}') { + if (--depth==0) + break; + } + } + *size_r = i - *idx_r; + return TRUE; + } +} + +char var_get_key(const char *str) +{ + unsigned int idx, size; + + if (var_get_key_range_full(str, &idx, &size)) + return '{'; + return str[idx]; +} + +void var_get_key_range(const char *str, unsigned int *idx_r, + unsigned int *size_r) +{ + (void)var_get_key_range_full(str, idx_r, size_r); +} + +static bool var_has_long_key(const char **str, const char *long_key) +{ + const char *start, *end; + + start = strchr(*str, '{'); + i_assert(start != NULL); + + end = strchr(++start, '}'); + if (end == NULL) + return FALSE; + + if (strncmp(start, long_key, end-start) == 0 && + long_key[end-start] == '\0') + return TRUE; + + *str = end; + return FALSE; +} + +bool var_has_key(const char *str, char key, const char *long_key) +{ + char c; + + for (; *str != '\0'; str++) { + if (*str == '%' && str[1] != '\0') { + str++; + c = var_get_key(str); + if (c == key && key != '\0') + return TRUE; + + if (c == '{' && long_key != NULL) { + if (var_has_long_key(&str, long_key)) + return TRUE; + } + } + } + return FALSE; +} + +void var_expand_extensions_deinit(void) +{ + array_free(&var_expand_extensions); +} + +void var_expand_extensions_init(void) +{ + i_array_init(&var_expand_extensions, 32); + + /* put all hash methods there */ + for(const struct hash_method **meth = hash_methods; + *meth != NULL; + meth++) { + struct var_expand_extension_func_table *func = + array_append_space(&var_expand_extensions); + func->key = (*meth)->name; + func->func = var_expand_hash; + } + + /* pkcs5 */ + struct var_expand_extension_func_table *func = + array_append_space(&var_expand_extensions); + func->key = "pkcs5"; + func->func = var_expand_hash; + + /* if */ + func = array_append_space(&var_expand_extensions); + func->key = "if"; + func->func = var_expand_if; +} + +void +var_expand_register_func_array(const struct var_expand_extension_func_table *funcs) +{ + for(const struct var_expand_extension_func_table *ptr = funcs; + ptr->key != NULL; + ptr++) { + i_assert(*ptr->key != '\0'); + array_push_front(&var_expand_extensions, ptr); + } +} + +void +var_expand_unregister_func_array(const struct var_expand_extension_func_table *funcs) +{ + for(const struct var_expand_extension_func_table *ptr = funcs; + ptr->key != NULL; + ptr++) { + i_assert(ptr->func != NULL); + for(unsigned int i = 0; i < array_count(&var_expand_extensions); i++) { + const struct var_expand_extension_func_table *func = + array_idx(&var_expand_extensions, i); + if (strcasecmp(func->key, ptr->key) == 0) { + array_delete(&var_expand_extensions, i, 1); + } + } + } +} + +struct var_expand_table * +var_expand_merge_tables(pool_t pool, const struct var_expand_table *a, + const struct var_expand_table *b) +{ + ARRAY(struct var_expand_table) table; + size_t a_size = var_expand_table_size(a); + size_t b_size = var_expand_table_size(b); + p_array_init(&table, pool, a_size + b_size + 1); + for(size_t i=0; i<a_size; i++) { + struct var_expand_table *entry = + array_append_space(&table); + entry->key = a[i].key; + entry->value = p_strdup(pool, a[i].value); + entry->long_key = p_strdup(pool, a[i].long_key); + } + for(size_t i=0; i<b_size; i++) { + struct var_expand_table *entry = + array_append_space(&table); + entry->key = b[i].key; + entry->value = p_strdup(pool, b[i].value); + entry->long_key = p_strdup(pool, b[i].long_key); + } + array_append_zero(&table); + return array_front_modifiable(&table); +} diff --git a/src/lib/var-expand.h b/src/lib/var-expand.h new file mode 100644 index 0000000..c8485a4 --- /dev/null +++ b/src/lib/var-expand.h @@ -0,0 +1,60 @@ +#ifndef VAR_EXPAND_H +#define VAR_EXPAND_H + +struct var_expand_table { + char key; + const char *value; + const char *long_key; +}; + +struct var_expand_func_table { + const char *key; + /* %{key:data}, or data is "" with %{key}. + Returns 1 on success, 0 if data is invalid, -1 on temporary error. */ + int (*func)(const char *data, void *context, + const char **value_r, const char **error_r); +}; + +/* Expand % variables in src and append the string in dest. + table must end with key = 0. Returns 1 on success, 0 if the format string + contained invalid/unknown %variables, -1 if one of the functions returned + temporary error. Even in case of errors the dest string is still written as + fully as possible. */ +int var_expand(string_t *dest, const char *str, + const struct var_expand_table *table, + const char **error_r); +/* Like var_expand(), but support also callback functions for + variable expansion. */ +int var_expand_with_funcs(string_t *dest, const char *str, + const struct var_expand_table *table, + const struct var_expand_func_table *func_table, + void *func_context, const char **error_r) ATTR_NULL(3, 4, 5); + +/* Returns the actual key character for given string, ie. skip any modifiers + that are before it. The string should be the data after the '%' character. + For %{long_variable}, '{' is returned. */ +char var_get_key(const char *str) ATTR_PURE; +/* Similar to var_get_key(), but works for long keys as well. For single char + keys size=1, while for e.g. %{key} size=3 and idx points to 'k'. */ +void var_get_key_range(const char *str, unsigned int *idx_r, + unsigned int *size_r); +/* Returns TRUE if key variable is used in the string. + If key is '\0', it's ignored. If long_key is NULL, it's ignored. */ +bool var_has_key(const char *str, char key, const char *long_key) ATTR_PURE; + +static inline size_t ATTR_PURE +var_expand_table_size(const struct var_expand_table *table) +{ + size_t n = 0; + while(table != NULL && (table[n].key != '\0' || + table[n].long_key != NULL)) + n++; + return n; +} + +struct var_expand_table * +var_expand_merge_tables(pool_t pool, const struct var_expand_table *a, + const struct var_expand_table *b); +#define t_var_expand_merge_tables(a, b) \ + (const struct var_expand_table *)var_expand_merge_tables(pool_datastack_create(), (a), (b)) +#endif diff --git a/src/lib/wildcard-match.c b/src/lib/wildcard-match.c new file mode 100644 index 0000000..c1e2b2e --- /dev/null +++ b/src/lib/wildcard-match.c @@ -0,0 +1,105 @@ +/* + * This code would not have been possible without the prior work and + * suggestions of various sourced. Special thanks to Robey for + * all his time/help tracking down bugs and his ever-helpful advice. + * + * 04/09: Fixed the "*\*" against "*a" bug (caused an endless loop) + * + * Chris Fuller (aka Fred1@IRC & Fwitz@IRC) + * crf@cfox.bchs.uh.edu + * + * I hereby release this code into the public domain + * + */ + +#include "lib.h" +#include "wildcard-match.h" + +#include <ctype.h> + +#define WILDS '*' /* matches 0 or more characters (including spaces) */ +#define WILDQ '?' /* matches exactly one character */ + +#define NOMATCH 0 +#define MATCH (match+sofar) + +static int wildcard_match_int(const char *data, const char *mask, bool icase) +{ + const char *ma = mask, *na = data, *lsm = NULL, *lsn = NULL; + int match = 1; + int sofar = 0; + + if (na[0] == '\0') { + /* empty string can match only "*" wildcard(s) */ + while (ma[0] == '*') ma++; + return ma[0] == '\0' ? MATCH : NOMATCH; + } + /* find the end of each string */ + while (*(mask++) != '\0'); + mask-=2; + while (*(data++) != '\0'); + data-=2; + + while (data >= na) { + /* If the mask runs out of chars before the string, fall back on + * a wildcard or fail. */ + if (mask < ma) { + if (lsm != NULL) { + data = --lsn; + mask = lsm; + if (data < na) + lsm = NULL; + sofar = 0; + } + else + return NOMATCH; + } + + switch (*mask) { + case WILDS: /* Matches anything */ + do + mask--; /* Zap redundant wilds */ + while ((mask >= ma) && (*mask == WILDS)); + lsm = mask; + lsn = data; + match += sofar; + sofar = 0; /* Update fallback pos */ + if (mask < ma) + return MATCH; + continue; /* Next char, please */ + case WILDQ: + mask--; + data--; + continue; /* '?' always matches */ + } + if (icase ? (i_toupper(*mask) == i_toupper(*data)) : + (*mask == *data)) { /* If matching char */ + mask--; + data--; + sofar++; /* Tally the match */ + continue; /* Next char, please */ + } + if (lsm != NULL) { /* To to fallback on '*' */ + data = --lsn; + mask = lsm; + if (data < na) + lsm = NULL; /* Rewind to saved pos */ + sofar = 0; + continue; /* Next char, please */ + } + return NOMATCH; /* No fallback=No match */ + } + while ((mask >= ma) && (*mask == WILDS)) + mask--; /* Zap leftover %s & *s */ + return (mask >= ma) ? NOMATCH : MATCH; /* Start of both = match */ +} + +bool wildcard_match(const char *data, const char *mask) +{ + return wildcard_match_int(data, mask, FALSE) != 0; +} + +bool wildcard_match_icase(const char *data, const char *mask) +{ + return wildcard_match_int(data, mask, TRUE) != 0; +} diff --git a/src/lib/wildcard-match.h b/src/lib/wildcard-match.h new file mode 100644 index 0000000..bfe6076 --- /dev/null +++ b/src/lib/wildcard-match.h @@ -0,0 +1,15 @@ +#ifndef WILDCARD_MATCH_H +#define WILDCARD_MATCH_H + +/* Returns TRUE if mask matches data. mask can contain '*' and '?' wildcards. */ +bool wildcard_match(const char *data, const char *mask); +/* Like wildcard_match(), but match ASCII characters case-insensitively. */ +bool wildcard_match_icase(const char *data, const char *mask); + +/* Returns TRUE if mask does *not* contain any '*' or '?' wildcards. */ +static inline bool wildcard_is_literal(const char *mask) +{ + return strpbrk(mask, "*?") == NULL; +} + +#endif diff --git a/src/lib/write-full.c b/src/lib/write-full.c new file mode 100644 index 0000000..ded8d40 --- /dev/null +++ b/src/lib/write-full.c @@ -0,0 +1,54 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "write-full.h" + +#include <unistd.h> + +int write_full(int fd, const void *data, size_t size) +{ + ssize_t ret; + + while (size > 0) { + ret = write(fd, data, size < SSIZE_T_MAX ? size : SSIZE_T_MAX); + if (unlikely(ret < 0)) + return -1; + + if (unlikely(ret == 0)) { + /* nothing was written, only reason for this should + be out of disk space */ + errno = ENOSPC; + return -1; + } + + data = CONST_PTR_OFFSET(data, ret); + size -= ret; + } + + return 0; +} + +int pwrite_full(int fd, const void *data, size_t size, off_t offset) +{ + ssize_t ret; + + while (size > 0) { + ret = pwrite(fd, data, size < SSIZE_T_MAX ? + size : SSIZE_T_MAX, offset); + if (unlikely(ret < 0)) + return -1; + + if (unlikely(ret == 0)) { + /* nothing was written, only reason for this should + be out of disk space */ + errno = ENOSPC; + return -1; + } + + data = CONST_PTR_OFFSET(data, ret); + size -= ret; + offset += ret; + } + + return 0; +} diff --git a/src/lib/write-full.h b/src/lib/write-full.h new file mode 100644 index 0000000..ddef406 --- /dev/null +++ b/src/lib/write-full.h @@ -0,0 +1,10 @@ +#ifndef WRITE_FULL_H +#define WRITE_FULL_H + +/* Write data into file. Returns -1 if error occurred, or 0 if all was ok. + If there's not enough space in device, -1 with ENOSPC is returned, and + it's unspecified how much data was actually written. */ +int write_full(int fd, const void *data, size_t size); +int pwrite_full(int fd, const void *data, size_t size, off_t offset); + +#endif |