summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:36:47 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:36:47 +0000
commit0441d265f2bb9da249c7abf333f0f771fadb4ab5 (patch)
tree3f3789daa2f6db22da6e55e92bee0062a7d613fe /src/lib
parentInitial commit. (diff)
downloaddovecot-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')
-rw-r--r--src/lib/Makefile.am481
-rw-r--r--src/lib/Makefile.in3711
-rw-r--r--src/lib/UnicodeData.txt30592
-rw-r--r--src/lib/aqueue.c124
-rw-r--r--src/lib/aqueue.h41
-rw-r--r--src/lib/array-decl.h26
-rw-r--r--src/lib/array.c166
-rw-r--r--src/lib/array.h420
-rw-r--r--src/lib/askpass.c72
-rw-r--r--src/lib/askpass.h7
-rw-r--r--src/lib/backtrace-string.c156
-rw-r--r--src/lib/backtrace-string.h8
-rw-r--r--src/lib/base32.c324
-rw-r--r--src/lib/base32.h47
-rw-r--r--src/lib/base64.c968
-rw-r--r--src/lib/base64.h403
-rw-r--r--src/lib/bits.c36
-rw-r--r--src/lib/bits.h184
-rw-r--r--src/lib/bsearch-insert-pos.c48
-rw-r--r--src/lib/bsearch-insert-pos.h52
-rw-r--r--src/lib/buffer-istream.c49
-rw-r--r--src/lib/buffer.c495
-rw-r--r--src/lib/buffer.h199
-rw-r--r--src/lib/byteorder.h268
-rw-r--r--src/lib/child-wait.c139
-rw-r--r--src/lib/child-wait.h34
-rw-r--r--src/lib/compat.c268
-rw-r--r--src/lib/compat.h323
-rw-r--r--src/lib/connection.c962
-rw-r--r--src/lib/connection.h260
-rw-r--r--src/lib/cpu-limit.c200
-rw-r--r--src/lib/cpu-limit.h67
-rw-r--r--src/lib/crc32.c91
-rw-r--r--src/lib/crc32.h10
-rw-r--r--src/lib/data-stack.c777
-rw-r--r--src/lib/data-stack.h165
-rw-r--r--src/lib/eacces-error.c310
-rw-r--r--src/lib/eacces-error.h14
-rw-r--r--src/lib/env-util.c140
-rw-r--r--src/lib/env-util.h30
-rw-r--r--src/lib/event-filter-lexer.c2297
-rw-r--r--src/lib/event-filter-lexer.l141
-rw-r--r--src/lib/event-filter-parser.c1852
-rw-r--r--src/lib/event-filter-parser.h96
-rw-r--r--src/lib/event-filter-parser.y195
-rw-r--r--src/lib/event-filter-private.h121
-rw-r--r--src/lib/event-filter.c855
-rw-r--r--src/lib/event-filter.h64
-rw-r--r--src/lib/event-log.c461
-rw-r--r--src/lib/event-log.h151
-rw-r--r--src/lib/execv-const.c33
-rw-r--r--src/lib/execv-const.h9
-rw-r--r--src/lib/failures-private.h26
-rw-r--r--src/lib/failures.c998
-rw-r--r--src/lib/failures.h157
-rw-r--r--src/lib/fd-util.c166
-rw-r--r--src/lib/fd-util.h26
-rw-r--r--src/lib/fdatasync-path.c31
-rw-r--r--src/lib/fdatasync-path.h7
-rw-r--r--src/lib/fdpass.c212
-rw-r--r--src/lib/fdpass.h15
-rw-r--r--src/lib/file-cache.c336
-rw-r--r--src/lib/file-cache.h37
-rw-r--r--src/lib/file-copy.c125
-rw-r--r--src/lib/file-copy.h12
-rw-r--r--src/lib/file-create-locked.c193
-rw-r--r--src/lib/file-create-locked.h47
-rw-r--r--src/lib/file-dotlock.c925
-rw-r--r--src/lib/file-dotlock.h94
-rw-r--r--src/lib/file-lock.c530
-rw-r--r--src/lib/file-lock.h87
-rw-r--r--src/lib/file-set-size.c108
-rw-r--r--src/lib/file-set-size.h13
-rw-r--r--src/lib/fsync-mode.h14
-rw-r--r--src/lib/guid.c174
-rw-r--r--src/lib/guid.h52
-rw-r--r--src/lib/hash-decl.h20
-rw-r--r--src/lib/hash-format.c245
-rw-r--r--src/lib/hash-format.h23
-rw-r--r--src/lib/hash-method.c98
-rw-r--r--src/lib/hash-method.h55
-rw-r--r--src/lib/hash.c593
-rw-r--r--src/lib/hash.h175
-rw-r--r--src/lib/hash2.c242
-rw-r--r--src/lib/hash2.h56
-rw-r--r--src/lib/hex-binary.c85
-rw-r--r--src/lib/hex-binary.h15
-rw-r--r--src/lib/hex-dec.c38
-rw-r--r--src/lib/hex-dec.h12
-rw-r--r--src/lib/hmac-cram-md5.c65
-rw-r--r--src/lib/hmac-cram-md5.h14
-rw-r--r--src/lib/hmac.c152
-rw-r--r--src/lib/hmac.h65
-rw-r--r--src/lib/home-expand.c72
-rw-r--r--src/lib/home-expand.h12
-rw-r--r--src/lib/hook-build.c121
-rw-r--r--src/lib/hook-build.h19
-rw-r--r--src/lib/hostpid.c69
-rw-r--r--src/lib/hostpid.h21
-rw-r--r--src/lib/imem.c78
-rw-r--r--src/lib/imem.h45
-rw-r--r--src/lib/ioloop-epoll.c230
-rw-r--r--src/lib/ioloop-iolist.c56
-rw-r--r--src/lib/ioloop-iolist.h19
-rw-r--r--src/lib/ioloop-kqueue.c175
-rw-r--r--src/lib/ioloop-notify-fd.c55
-rw-r--r--src/lib/ioloop-notify-fd.h28
-rw-r--r--src/lib/ioloop-notify-inotify.c237
-rw-r--r--src/lib/ioloop-notify-kqueue.c224
-rw-r--r--src/lib/ioloop-notify-none.c33
-rw-r--r--src/lib/ioloop-poll.c221
-rw-r--r--src/lib/ioloop-private.h129
-rw-r--r--src/lib/ioloop-select.c148
-rw-r--r--src/lib/ioloop.c1389
-rw-r--r--src/lib/ioloop.h323
-rw-r--r--src/lib/iostream-private.h51
-rw-r--r--src/lib/iostream-proxy.c173
-rw-r--r--src/lib/iostream-proxy.h87
-rw-r--r--src/lib/iostream-pump.c251
-rw-r--r--src/lib/iostream-pump.h69
-rw-r--r--src/lib/iostream-rawlog-private.h25
-rw-r--r--src/lib/iostream-rawlog.c298
-rw-r--r--src/lib/iostream-rawlog.h28
-rw-r--r--src/lib/iostream-temp.c404
-rw-r--r--src/lib/iostream-temp.h31
-rw-r--r--src/lib/iostream.c154
-rw-r--r--src/lib/iostream.h10
-rw-r--r--src/lib/ipwd.c103
-rw-r--r--src/lib/ipwd.h23
-rw-r--r--src/lib/iso8601-date.c309
-rw-r--r--src/lib/iso8601-date.h21
-rw-r--r--src/lib/istream-base64-decoder.c171
-rw-r--r--src/lib/istream-base64-encoder.c226
-rw-r--r--src/lib/istream-base64.h16
-rw-r--r--src/lib/istream-callback.c118
-rw-r--r--src/lib/istream-callback.h39
-rw-r--r--src/lib/istream-chain.c349
-rw-r--r--src/lib/istream-chain.h21
-rw-r--r--src/lib/istream-concat.c391
-rw-r--r--src/lib/istream-concat.h7
-rw-r--r--src/lib/istream-crlf.c208
-rw-r--r--src/lib/istream-crlf.h9
-rw-r--r--src/lib/istream-data.c62
-rw-r--r--src/lib/istream-failure-at.c97
-rw-r--r--src/lib/istream-failure-at.h11
-rw-r--r--src/lib/istream-file-private.h23
-rw-r--r--src/lib/istream-file.c282
-rw-r--r--src/lib/istream-hash.c87
-rw-r--r--src/lib/istream-hash.h12
-rw-r--r--src/lib/istream-jsonstr.c217
-rw-r--r--src/lib/istream-jsonstr.h7
-rw-r--r--src/lib/istream-limit.c144
-rw-r--r--src/lib/istream-multiplex.c298
-rw-r--r--src/lib/istream-multiplex.h8
-rw-r--r--src/lib/istream-private.h140
-rw-r--r--src/lib/istream-rawlog.c119
-rw-r--r--src/lib/istream-rawlog.h14
-rw-r--r--src/lib/istream-seekable.c558
-rw-r--r--src/lib/istream-seekable.h28
-rw-r--r--src/lib/istream-sized.c232
-rw-r--r--src/lib/istream-sized.h41
-rw-r--r--src/lib/istream-tee.c258
-rw-r--r--src/lib/istream-tee.h17
-rw-r--r--src/lib/istream-timeout.c150
-rw-r--r--src/lib/istream-timeout.h9
-rw-r--r--src/lib/istream-try.c165
-rw-r--r--src/lib/istream-try.h25
-rw-r--r--src/lib/istream-unix.c113
-rw-r--r--src/lib/istream-unix.h15
-rw-r--r--src/lib/istream.c1316
-rw-r--r--src/lib/istream.h255
-rw-r--r--src/lib/json-parser.c850
-rw-r--r--src/lib/json-parser.h61
-rw-r--r--src/lib/json-tree.c173
-rw-r--r--src/lib/json-tree.h62
-rw-r--r--src/lib/lib-event-private.h114
-rw-r--r--src/lib/lib-event.c1776
-rw-r--r--src/lib/lib-event.h440
-rw-r--r--src/lib/lib-signals.c702
-rw-r--r--src/lib/lib-signals.h72
-rw-r--r--src/lib/lib.c206
-rw-r--r--src/lib/lib.h115
-rw-r--r--src/lib/llist.h81
-rw-r--r--src/lib/log-throttle.c78
-rw-r--r--src/lib/log-throttle.h32
-rw-r--r--src/lib/macros.h302
-rw-r--r--src/lib/malloc-overflow.h54
-rw-r--r--src/lib/md4.c300
-rw-r--r--src/lib/md4.h33
-rw-r--r--src/lib/md5.c314
-rw-r--r--src/lib/md5.h33
-rw-r--r--src/lib/memarea.c93
-rw-r--r--src/lib/memarea.h31
-rw-r--r--src/lib/mempool-allocfree.c330
-rw-r--r--src/lib/mempool-alloconly.c546
-rw-r--r--src/lib/mempool-datastack.c190
-rw-r--r--src/lib/mempool-system.c163
-rw-r--r--src/lib/mempool-unsafe-datastack.c135
-rw-r--r--src/lib/mempool.c25
-rw-r--r--src/lib/mempool.h179
-rw-r--r--src/lib/mkdir-parents.c177
-rw-r--r--src/lib/mkdir-parents.h33
-rw-r--r--src/lib/mmap-anon.c183
-rw-r--r--src/lib/mmap-util.c63
-rw-r--r--src/lib/mmap-util.h42
-rw-r--r--src/lib/module-context.h116
-rw-r--r--src/lib/module-dir.c697
-rw-r--r--src/lib/module-dir.h77
-rw-r--r--src/lib/mountpoint.c336
-rw-r--r--src/lib/mountpoint.h23
-rw-r--r--src/lib/net.c1237
-rw-r--r--src/lib/net.h199
-rw-r--r--src/lib/nfs-workarounds.c406
-rw-r--r--src/lib/nfs-workarounds.h40
-rw-r--r--src/lib/numpack.c57
-rw-r--r--src/lib/numpack.h11
-rw-r--r--src/lib/ostream-buffer.c88
-rw-r--r--src/lib/ostream-failure-at.c123
-rw-r--r--src/lib/ostream-failure-at.h10
-rw-r--r--src/lib/ostream-file-private.h45
-rw-r--r--src/lib/ostream-file.c1154
-rw-r--r--src/lib/ostream-hash.c56
-rw-r--r--src/lib/ostream-hash.h12
-rw-r--r--src/lib/ostream-multiplex.c367
-rw-r--r--src/lib/ostream-multiplex.h8
-rw-r--r--src/lib/ostream-null.c33
-rw-r--r--src/lib/ostream-null.h7
-rw-r--r--src/lib/ostream-private.h73
-rw-r--r--src/lib/ostream-rawlog.c88
-rw-r--r--src/lib/ostream-rawlog.h14
-rw-r--r--src/lib/ostream-unix.c95
-rw-r--r--src/lib/ostream-unix.h10
-rw-r--r--src/lib/ostream-wrapper.c1259
-rw-r--r--src/lib/ostream-wrapper.h165
-rw-r--r--src/lib/ostream.c804
-rw-r--r--src/lib/ostream.h260
-rw-r--r--src/lib/path-util.c398
-rw-r--r--src/lib/path-util.h69
-rw-r--r--src/lib/pkcs5.c97
-rw-r--r--src/lib/pkcs5.h35
-rw-r--r--src/lib/primes.c48
-rw-r--r--src/lib/primes.h8
-rw-r--r--src/lib/printf-format-fix.c192
-rw-r--r--src/lib/printf-format-fix.h15
-rw-r--r--src/lib/priorityq.c171
-rw-r--r--src/lib/priorityq.h39
-rw-r--r--src/lib/process-stat.c267
-rw-r--r--src/lib/process-stat.h26
-rw-r--r--src/lib/process-title.c194
-rw-r--r--src/lib/process-title.h19
-rw-r--r--src/lib/rand.c101
-rw-r--r--src/lib/randgen.c222
-rw-r--r--src/lib/randgen.h17
-rw-r--r--src/lib/read-full.c40
-rw-r--r--src/lib/read-full.h9
-rw-r--r--src/lib/restrict-access.c531
-rw-r--r--src/lib/restrict-access.h90
-rw-r--r--src/lib/restrict-process-size.c113
-rw-r--r--src/lib/restrict-process-size.h25
-rw-r--r--src/lib/safe-memset.c17
-rw-r--r--src/lib/safe-memset.h8
-rw-r--r--src/lib/safe-mkdir.c78
-rw-r--r--src/lib/safe-mkdir.h10
-rw-r--r--src/lib/safe-mkstemp.c106
-rw-r--r--src/lib/safe-mkstemp.h15
-rw-r--r--src/lib/sendfile-util.c137
-rw-r--r--src/lib/sendfile-util.h16
-rw-r--r--src/lib/seq-range-array.c569
-rw-r--r--src/lib/seq-range-array.h82
-rw-r--r--src/lib/seq-set-builder.c113
-rw-r--r--src/lib/seq-set-builder.h15
-rw-r--r--src/lib/sha-common.h12
-rw-r--r--src/lib/sha1.c288
-rw-r--r--src/lib/sha1.h84
-rw-r--r--src/lib/sha2.c647
-rw-r--r--src/lib/sha2.h89
-rw-r--r--src/lib/sha3.c335
-rw-r--r--src/lib/sha3.h75
-rw-r--r--src/lib/sleep.c74
-rw-r--r--src/lib/sleep.h30
-rw-r--r--src/lib/sort.c17
-rw-r--r--src/lib/sort.h33
-rw-r--r--src/lib/stats-dist.c183
-rw-r--r--src/lib/stats-dist.h40
-rw-r--r--src/lib/str-find.c183
-rw-r--r--src/lib/str-find.h20
-rw-r--r--src/lib/str-sanitize.c165
-rw-r--r--src/lib/str-sanitize.h22
-rw-r--r--src/lib/str-table.c78
-rw-r--r--src/lib/str-table.h19
-rw-r--r--src/lib/str.c158
-rw-r--r--src/lib/str.h100
-rw-r--r--src/lib/strescape.c358
-rw-r--r--src/lib/strescape.h43
-rw-r--r--src/lib/strfuncs.c945
-rw-r--r--src/lib/strfuncs.h170
-rw-r--r--src/lib/strnum.c464
-rw-r--r--src/lib/strnum.h192
-rw-r--r--src/lib/test-aqueue.c73
-rw-r--r--src/lib/test-array.c441
-rw-r--r--src/lib/test-backtrace.c57
-rw-r--r--src/lib/test-base32.c189
-rw-r--r--src/lib/test-base64.c1317
-rw-r--r--src/lib/test-bits.c281
-rw-r--r--src/lib/test-bsearch-insert-pos.c46
-rw-r--r--src/lib/test-buffer-istream.c101
-rw-r--r--src/lib/test-buffer.c367
-rw-r--r--src/lib/test-byteorder.c251
-rw-r--r--src/lib/test-connection.c744
-rw-r--r--src/lib/test-cpu-limit.c145
-rw-r--r--src/lib/test-crc32.c14
-rw-r--r--src/lib/test-data-stack.c455
-rw-r--r--src/lib/test-env-util.c92
-rw-r--r--src/lib/test-event-category-register.c320
-rw-r--r--src/lib/test-event-filter-expr.c250
-rw-r--r--src/lib/test-event-filter-merge.c67
-rw-r--r--src/lib/test-event-filter-parser.c543
-rw-r--r--src/lib/test-event-filter.c598
-rw-r--r--src/lib/test-event-flatten.c391
-rw-r--r--src/lib/test-event-log.c2528
-rw-r--r--src/lib/test-failures.c176
-rw-r--r--src/lib/test-fd-util.c24
-rw-r--r--src/lib/test-file-cache.c293
-rw-r--r--src/lib/test-file-create-locked.c142
-rw-r--r--src/lib/test-guid.c259
-rw-r--r--src/lib/test-hash-format.c54
-rw-r--r--src/lib/test-hash-method.c460
-rw-r--r--src/lib/test-hash.c47
-rw-r--r--src/lib/test-hex-binary.c61
-rw-r--r--src/lib/test-hmac.c302
-rw-r--r--src/lib/test-imem.c61
-rw-r--r--src/lib/test-ioloop.c404
-rw-r--r--src/lib/test-iostream-proxy.c113
-rw-r--r--src/lib/test-iostream-pump.c325
-rw-r--r--src/lib/test-iostream-temp.c150
-rw-r--r--src/lib/test-iso8601-date.c147
-rw-r--r--src/lib/test-istream-base64-decoder.c337
-rw-r--r--src/lib/test-istream-base64-encoder.c222
-rw-r--r--src/lib/test-istream-chain.c199
-rw-r--r--src/lib/test-istream-concat.c254
-rw-r--r--src/lib/test-istream-crlf.c115
-rw-r--r--src/lib/test-istream-failure-at.c49
-rw-r--r--src/lib/test-istream-jsonstr.c139
-rw-r--r--src/lib/test-istream-multiplex.c372
-rw-r--r--src/lib/test-istream-seekable.c290
-rw-r--r--src/lib/test-istream-sized.c111
-rw-r--r--src/lib/test-istream-tee.c139
-rw-r--r--src/lib/test-istream-try.c195
-rw-r--r--src/lib/test-istream-unix.c187
-rw-r--r--src/lib/test-istream.c381
-rw-r--r--src/lib/test-json-parser.c436
-rw-r--r--src/lib/test-json-tree.c113
-rw-r--r--src/lib/test-lib-event.c96
-rw-r--r--src/lib/test-lib-signals.c245
-rw-r--r--src/lib/test-lib.c28
-rw-r--r--src/lib/test-lib.h13
-rw-r--r--src/lib/test-lib.inc112
-rw-r--r--src/lib/test-llist.c138
-rw-r--r--src/lib/test-log-throttle.c57
-rw-r--r--src/lib/test-macros.c63
-rw-r--r--src/lib/test-malloc-overflow.c132
-rw-r--r--src/lib/test-memarea.c42
-rw-r--r--src/lib/test-mempool-allocfree.c128
-rw-r--r--src/lib/test-mempool-alloconly.c94
-rw-r--r--src/lib/test-mempool.c117
-rw-r--r--src/lib/test-multiplex.c167
-rw-r--r--src/lib/test-net.c167
-rw-r--r--src/lib/test-numpack.c64
-rw-r--r--src/lib/test-ostream-buffer.c108
-rw-r--r--src/lib/test-ostream-failure-at.c52
-rw-r--r--src/lib/test-ostream-file.c189
-rw-r--r--src/lib/test-ostream-multiplex.c405
-rw-r--r--src/lib/test-path-util.c278
-rw-r--r--src/lib/test-pkcs5.c49
-rw-r--r--src/lib/test-primes.c24
-rw-r--r--src/lib/test-printf-format-fix.c142
-rw-r--r--src/lib/test-priorityq.c106
-rw-r--r--src/lib/test-random.c67
-rw-r--r--src/lib/test-seq-range-array.c419
-rw-r--r--src/lib/test-seq-set-builder.c132
-rw-r--r--src/lib/test-stats-dist.c132
-rw-r--r--src/lib/test-str-find.c86
-rw-r--r--src/lib/test-str-sanitize.c127
-rw-r--r--src/lib/test-str-table.c33
-rw-r--r--src/lib/test-str.c182
-rw-r--r--src/lib/test-strescape.c197
-rw-r--r--src/lib/test-strfuncs.c640
-rw-r--r--src/lib/test-strnum.c398
-rw-r--r--src/lib/test-time-util.c406
-rw-r--r--src/lib/test-unichar.c185
-rw-r--r--src/lib/test-uri.c807
-rw-r--r--src/lib/test-utc-mktime.c61
-rw-r--r--src/lib/test-var-expand.c474
-rw-r--r--src/lib/test-wildcard-match.c48
-rw-r--r--src/lib/time-util.c169
-rw-r--r--src/lib/time-util.h108
-rw-r--r--src/lib/unichar.c447
-rw-r--r--src/lib/unichar.h145
-rw-r--r--src/lib/unicodemap.c2474
-rwxr-xr-xsrc/lib/unicodemap.pl162
-rw-r--r--src/lib/unix-socket-create.c36
-rw-r--r--src/lib/unix-socket-create.h7
-rw-r--r--src/lib/unlink-directory.c283
-rw-r--r--src/lib/unlink-directory.h21
-rw-r--r--src/lib/unlink-old-files.c73
-rw-r--r--src/lib/unlink-old-files.h9
-rw-r--r--src/lib/uri-util.c1332
-rw-r--r--src/lib/uri-util.h298
-rw-r--r--src/lib/utc-mktime.c79
-rw-r--r--src/lib/utc-mktime.h11
-rw-r--r--src/lib/utc-offset.c38
-rw-r--r--src/lib/utc-offset.h9
-rw-r--r--src/lib/var-expand-if.c269
-rw-r--r--src/lib/var-expand-private.h56
-rw-r--r--src/lib/var-expand.c833
-rw-r--r--src/lib/var-expand.h60
-rw-r--r--src/lib/wildcard-match.c105
-rw-r--r--src/lib/wildcard-match.h15
-rw-r--r--src/lib/write-full.c54
-rw-r--r--src/lib/write-full.h10
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(&copy, 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(&copy, &key);
+ array_push_back(&copy, &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(&copy, &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, &params, 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, &params, 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, &params, 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, &params, 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, &params, 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,
+ &timestamp, 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(&timestamp);
+ 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, &current_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, &params, "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&amp;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(&reg, rhs, REG_EXTENDED)) != 0) {
+ size_t siz;
+ char *errbuf;
+ siz = regerror(ec, &reg, NULL, 0);
+ errbuf = t_malloc_no0(siz);
+ (void)regerror(ec, &reg, errbuf, siz);
+ *error_r = t_strdup_printf("if: regex failed: %s",
+ errbuf);
+ return -1;
+ }
+ if ((ec = regexec(&reg, lhs, 0, 0, 0)) != 0) {
+ i_assert(ec == REG_NOMATCH);
+ res = FALSE;
+ } else {
+ res = TRUE;
+ }
+ regfree(&reg);
+ /* 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(&params, 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(&params, &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(&params, &par);
+ }
+
+ if (array_count(&params) != 5) {
+ if (array_count(&params) == 4) {
+ const char *empty = "";
+ array_push_back(&params, &empty);
+ } else {
+ *error_r = t_strdup_printf("if: requires four or five parameters, got %u",
+ array_count(&params));
+ return -1;
+ }
+ }
+
+ array_append_zero(&params);
+ parms = array_front(&params);
+ t_array_init(&params, 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(&params, &p);
+ }
+
+ i_assert(array_count(&params) == 5);
+
+ /* execute comparison */
+ const char *const *args = array_front(&params);
+ 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